/* This file is part of Ping903
Copyright (C) 2020 Sergey Poznyakoff
Ping903 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
Ping903 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Ping903. If not, see .
*/
#include
#include
#include
#include "defs.h"
void
argcv_free(int ac, char **av)
{
int i;
for (i = 0; i < ac; i++)
free(av[i]);
free(av);
}
int
strsplit(char const *str, int max, int *ret_ac, char ***ret_av, char **endp)
{
char **av = NULL;
int ac;
char const *p;
enum { Stop, Error, Inspace, Inquote, Inword } state;
av = calloc(max+1, sizeof(*av));
if (!av)
return STRSPLIT_NOMEM;
ac = 0;
#define ISWS(c) ((c)==' '||(c)=='\t')
#define UNESCAPE(p) ((p)[0] == '\\' && ((p)[1] == '\\' || (p)[1] == '"'))
if (ISWS(*str))
state = Inspace;
else if (*str == '"') {
state = Inquote;
str++;
} else
state = Inword;
p = str;
while (state != Stop && state != Error) {
switch (state) {
case Inquote:
if (*p == 0) {
p = str;
state = Error;
} else if (UNESCAPE(p))
p += 2;
else if (*p == '"') {
char *q;
size_t len = p - str;
av[ac] = malloc(len + 1);
if (!av[ac])
goto err;
q = av[ac];
while (str < p) {
if (UNESCAPE(str))
str++;
*q++ = *str++;
}
*q = 0;
ac++;
str++;
p = str;
if (*p == 0)
state = Stop;
else if (ISWS(*str))
state = Inspace;
else
state = Error;
} else
p++;
break;
case Inword:
if (*p == 0 || ISWS(*p)) {
size_t len = p - str;
av[ac] = malloc(len + 1);
if (!av[ac])
goto err;
memcpy(av[ac], str, len);
av[ac][len] = 0;
ac++;
state = *p ? Inspace : Stop;
} else
p++;
break;
case Inspace:
if (*p == 0) {
state = Stop;
break;
} else if (*p == '"') {
state = Inquote;
p++;
str = p;
} else if (!ISWS(*p)) {
state = Inword;
str = p;
} else
p++;
if (ac == max && state != Inspace) {
state = Stop;
}
break;
default:
abort();
}
}
av[ac] = NULL;
*ret_av = av;
*ret_ac = ac;
*endp = (char*) p;
return state == Stop ? STRSPLIT_OK : STRSPLIT_ERR;
err:
argcv_free(ac, av);
return STRSPLIT_NOMEM;
}