summaryrefslogtreecommitdiffabout
path: root/src
authorSergey Poznyakoff <gray@gnu.org.ua>2014-10-28 13:40:20 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2015-12-17 13:26:28 (GMT)
commit56a02e741cd8d8b9dce27a79ae9bbcaf1713c4f7 (patch) (side-by-side diff)
tree1153a7dd5c15ab80f1ffa7da4eb9091685222445 /src
parent8383ec3a522a944969b3fc44069a3ff056da554a (diff)
downloadgrecs-56a02e741cd8d8b9dce27a79ae9bbcaf1713c4f7.tar.gz
grecs-56a02e741cd8d8b9dce27a79ae9bbcaf1713c4f7.tar.bz2
Improve wordsplit
* src/wordsplit.c: Implement default assignment, word expansion in variable defaults, distinction between ${variable:-word} and ${variable-word}. * doc/wordsplit.3: New file. * src/wordsplit.h (wordsplit)<ws_envbuf,ws_envidx> <ws_envsiz>: New members. (WRDSF_ARGV): Remove. (WRDSF_OPTIONS): New flag. (WRDSO_ARGV): New option bit. * tests/wordsplit.at: Add new tests. * tests/wsp.c: Set WRDSF_OPTIONS flag if one of the options is requested.
Diffstat (limited to 'src') (more/less context) (ignore whitespace changes)
-rw-r--r--src/wordsplit.c367
-rw-r--r--src/wordsplit.h37
2 files changed, 310 insertions, 94 deletions
diff --git a/src/wordsplit.c b/src/wordsplit.c
index 4a69725..c726239 100644
--- a/src/wordsplit.c
+++ b/src/wordsplit.c
@@ -114,11 +114,30 @@ _wsplt_subsplit (struct wordsplit *wsp, struct wordsplit *wss,
wss->ws_error = wsp->ws_error;
wss->ws_alloc_die = wsp->ws_alloc_die;
+ if (!(flags & WRDSF_NOVAR))
+ {
+ wss->ws_env = wsp->ws_env;
+ wss->ws_getvar = wsp->ws_getvar;
+ flags |= wsp->ws_flags & (WRDSF_ENV | WRDSF_ENV_KV | WRDSF_GETVAR);
+ }
+ if (!(flags & WRDSF_NOCMD))
+ {
+ wss->ws_command = wsp->ws_command;
+ }
+
+ if ((flags & (WRDSF_NOVAR|WRDSF_NOCMD)) != (WRDSF_NOVAR|WRDSF_NOCMD))
+ {
+ wss->ws_closure = wsp->ws_closure;
+ flags |= wsp->ws_flags & WRDSF_CLOSURE;
+ }
+
+ wss->ws_options = wsp->ws_options;
+
flags |= WRDSF_DELIM
| WRDSF_ALLOC_DIE
| WRDSF_ERROR
| WRDSF_DEBUG
- | (wsp->ws_flags & (WRDSF_SHOWDBG | WRDSF_SHOWERR));
+ | (wsp->ws_flags & (WRDSF_SHOWDBG | WRDSF_SHOWERR | WRDSF_OPTIONS));
return wordsplit_run (str, len, wss, flags, wsp->ws_lvl + 1);
}
@@ -168,12 +187,11 @@ wordsplit_init (struct wordsplit *wsp, const char *input, size_t len,
if (!(wsp->ws_flags & WRDSF_ERROR))
wsp->ws_error = _wsplt_error;
- if (!(wsp->ws_flags & WRDSF_NOVAR)
- && !(wsp->ws_flags & (WRDSF_ENV | WRDSF_GETVAR)))
+ if (!(wsp->ws_flags & WRDSF_NOVAR))
{
- _wsplt_seterr (wsp, WRDSE_USAGE);
- errno = EINVAL;
- return wsp->ws_errno;
+ /* These will be initialized on first variable assignment */
+ wsp->ws_envidx = wsp->ws_envsiz = 0;
+ wsp->ws_envbuf = NULL;
}
if (!(wsp->ws_flags & WRDSF_NOCMD))
@@ -214,6 +232,9 @@ wordsplit_init (struct wordsplit *wsp, const char *input, size_t len,
if (!(wsp->ws_flags & WRDSF_CLOSURE))
wsp->ws_closure = NULL;
+ if (!(wsp->ws_flags & WRDSF_OPTIONS))
+ wsp->ws_options = 0;
+
wsp->ws_endp = 0;
wordsplit_init0 (wsp);
@@ -717,13 +738,14 @@ find_closing_paren (const char *str, size_t i, size_t len, size_t *poff,
return 1;
}
-static const char *
-wordsplit_find_env (struct wordsplit *wsp, const char *name, size_t len)
+static int
+wordsplit_find_env (struct wordsplit *wsp, const char *name, size_t len,
+ char const **ret)
{
size_t i;
if (!(wsp->ws_flags & WRDSF_ENV))
- return NULL;
+ return WRDSE_UNDEF;
if (wsp->ws_flags & WRDSF_ENV_KV)
{
@@ -732,14 +754,17 @@ wordsplit_find_env (struct wordsplit *wsp, const char *name, size_t len)
{
size_t elen = strlen (wsp->ws_env[i]);
if (elen == len && memcmp (wsp->ws_env[i], name, elen) == 0)
- return wsp->ws_env[i + 1];
+ {
+ *ret = wsp->ws_env[i + 1];
+ return WRDSE_OK;
+ }
/* Skip the value. Break the loop if it is NULL. */
i++;
if (wsp->ws_env[i] == NULL)
break;
}
}
- else
+ else if (wsp->ws_env)
{
/* Usual (A=B) environment. */
for (i = 0; wsp->ws_env[i]; i++)
@@ -751,10 +776,117 @@ wordsplit_find_env (struct wordsplit *wsp, const char *name, size_t len)
if (name[j] != var[j])
break;
if (j == len && var[j] == '=')
- return var + j + 1;
+ {
+ *ret = var + j + 1;
+ return WRDSE_OK;
+ }
+ }
+ }
+ return WRDSE_UNDEF;
+}
+
+static int
+wsplt_assign_var (struct wordsplit *wsp, const char *name, size_t namelen,
+ char *value)
+{
+ int n = (wsp->ws_flags & WRDSF_ENV_KV) ? 2 : 1;
+ char *v;
+
+ if (wsp->ws_envidx + n >= wsp->ws_envsiz)
+ {
+ size_t sz;
+ char **newenv;
+
+ if (!wsp->ws_envbuf)
+ {
+ if (wsp->ws_flags & WRDSF_ENV)
+ {
+ size_t i = 0, j;
+
+ if (wsp->ws_env)
+ {
+ for (; wsp->ws_env[i]; i++)
+ ;
+ }
+
+ sz = i + n + 1;
+
+ newenv = calloc (sz, sizeof(newenv[0]));
+ if (!newenv)
+ return _wsplt_nomem (wsp);
+
+ for (j = 0; j < i; j++)
+ {
+ newenv[j] = strdup (wsp->ws_env[j]);
+ if (!newenv[j])
+ {
+ for (; j > 1; j--)
+ free (newenv[j-1]);
+ free (newenv[j-1]);
+ return _wsplt_nomem (wsp);
+ }
+ }
+ newenv[j] = NULL;
+
+ wsp->ws_envbuf = newenv;
+ wsp->ws_envidx = i;
+ wsp->ws_envsiz = sz;
+ wsp->ws_env = (const char**) wsp->ws_envbuf;
+ }
+ else
+ {
+ newenv = calloc (WORDSPLIT_ENV_INIT, sizeof(newenv[0]));
+ if (!newenv)
+ return _wsplt_nomem (wsp);
+ wsp->ws_envbuf = newenv;
+ wsp->ws_envidx = 0;
+ wsp->ws_envsiz = WORDSPLIT_ENV_INIT;
+ wsp->ws_env = (const char**) wsp->ws_envbuf;
+ wsp->ws_flags |= WRDSF_ENV;
+ }
+ }
+ else
+ {
+ wsp->ws_envsiz *= 2;
+ newenv = realloc (wsp->ws_envbuf,
+ wsp->ws_envsiz * sizeof (wsp->ws_envbuf[0]));
+ if (!newenv)
+ return _wsplt_nomem (wsp);
+ wsp->ws_envbuf = newenv;
+ wsp->ws_env = (const char**) wsp->ws_envbuf;
}
}
- return NULL;
+
+ if (wsp->ws_flags & WRDSF_ENV_KV)
+ {
+ /* A key-value pair environment */
+ char *p = malloc (namelen + 1);
+ if (!p)
+ return _wsplt_nomem (wsp);
+ memcpy (p, name, namelen);
+ p[namelen] = 0;
+
+ v = strdup (value);
+ if (!v)
+ {
+ free (p);
+ return _wsplt_nomem (wsp);
+ }
+ wsp->ws_env[wsp->ws_envidx++] = p;
+ wsp->ws_env[wsp->ws_envidx++] = v;
+ }
+ else
+ {
+ v = malloc (namelen + strlen(value) + 2);
+ if (!v)
+ return _wsplt_nomem (wsp);
+ memcpy (v, name, namelen);
+ v[namelen++] = '=';
+ strcpy(v + namelen, value);
+ wsp->ws_env[wsp->ws_envidx++] = v;
+ }
+ wsp->ws_env[wsp->ws_envidx++] = NULL;
+ return WRDSE_OK;
}
static int
@@ -767,7 +899,9 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
const char *vptr;
struct wordsplit_node *newnode;
const char *start = str - 1;
-
+ int rc;
+ struct wordsplit ws;
+
if (ISVARBEG (str[0]))
{
for (i = 1; i < len; i++)
@@ -780,22 +914,35 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
str++;
len--;
for (i = 1; i < len; i++)
- if (str[i] == '}' || str[i] == ':')
- break;
- if (str[i] == ':')
- {
- size_t j;
-
- defstr = str + i + 1;
- if (find_closing_paren (str, i + 1, len, &j, "{}"))
- return _wsplt_seterr (wsp, WRDSE_CBRACE);
- *pend = str + j;
- }
- else if (str[i] == '}')
{
- *pend = str + i;
+ if (str[i] == ':')
+ {
+ size_t j;
+
+ defstr = str + i + 1;
+ if (find_closing_paren (str, i + 1, len, &j, "{}"))
+ return _wsplt_seterr (wsp, WRDSE_CBRACE);
+ *pend = str + j;
+ break;
+ }
+ else if (str[i] == '}')
+ {
+ defstr = NULL;
+ *pend = str + i;
+ break;
+ }
+ else if (strchr ("-+?=", str[i]))
+ {
+ size_t j;
+
+ defstr = str + i;
+ if (find_closing_paren (str, i, len, &j, "{}"))
+ return _wsplt_seterr (wsp, WRDSE_CBRACE);
+ *pend = str + j;
+ break;
+ }
}
- else
+ if (i == len)
return _wsplt_seterr (wsp, WRDSE_CBRACE);
}
else
@@ -820,64 +967,99 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
i - its length
defstr - default replacement str */
- vptr = wordsplit_find_env (wsp, str, i);
- if (vptr)
+ if (defstr && strchr("-+?=", defstr[0]) == 0)
{
- value = strdup (vptr);
- if (!value)
- return _wsplt_nomem (wsp);
+ rc = WRDSE_UNDEF;
+ defstr = NULL;
}
- else if (wsp->ws_flags & WRDSF_GETVAR)
+ else
{
- int rc = wsp->ws_getvar (&value, str, i, wsp->ws_closure);
- switch (rc)
+ rc = wordsplit_find_env (wsp, str, i, &vptr);
+ if (rc == WRDSE_OK)
{
- case WRDSE_OK:
- break;
-
- case WRDSE_NOSPACE:
- return _wsplt_nomem (wsp);
-
- case WRDSE_UNDEF:
- value = NULL;
- break;
+ value = strdup (vptr);
+ if (!value)
+ rc = WRDSE_NOSPACE;
+ }
+ else if (wsp->ws_flags & WRDSF_GETVAR)
+ rc = wsp->ws_getvar (&value, str, i, wsp->ws_closure);
+ else
+ rc = WRDSE_UNDEF;
- case WRDSE_USERERR:
- if (wsp->ws_errno == WRDSE_USERERR)
- free (wsp->ws_usererr);
- wsp->ws_usererr = value;
- /* fall through */
- default:
- _wsplt_seterr (wsp, rc);
- return 1;
+ if (rc == WRDSE_OK && value[0] == 0 && defstr && defstr[-1] == ':')
+ {
+ free (value);
+ rc = WRDSE_UNDEF;
}
}
- else
- value = NULL;
- if (!value)
+ switch (rc)
{
+ case WRDSE_OK:
+ if (defstr && *defstr == '+')
+ {
+ size_t size = *pend - ++defstr;
+
+ rc = _wsplt_subsplit (wsp, &ws, defstr, size,
+ WRDSF_NOSPLIT | WRDSF_WS | WRDSF_QUOTE |
+ (wsp->ws_flags &
+ (WRDSF_NOVAR | WRDSF_NOCMD)));
+ if (rc)
+ return rc;
+ free (value);
+ value = ws.ws_wordv[0];
+ ws.ws_wordv[0] = NULL;
+ wordsplit_free (&ws);
+ }
+ break;
+
+ case WRDSE_UNDEF:
if (defstr)
{
size_t size;
- if (*defstr == '-')
+ if (*defstr == '-' || *defstr == '=')
{
size = *pend - ++defstr;
- value = malloc (size + 1);
- if (!value)
- return _wsplt_nomem (wsp);
- memcpy (value, defstr, size);
- value[size] = 0;
+
+ rc = _wsplt_subsplit (wsp, &ws, defstr, size,
+ WRDSF_NOSPLIT | WRDSF_WS | WRDSF_QUOTE |
+ (wsp->ws_flags &
+ (WRDSF_NOVAR | WRDSF_NOCMD)));
+ if (rc)
+ return rc;
+
+ value = ws.ws_wordv[0];
+ ws.ws_wordv[0] = NULL;
+ wordsplit_free (&ws);
+
+ if (defstr[-1] == '=')
+ wsplt_assign_var (wsp, str, i, value);
}
- else if (*defstr == '?')
+ else
{
- size = *pend - ++defstr;
- if (size == 0)
- wsp->ws_error (_("%.*s: variable null or not set"),
- (int) i, str);
- else
- wsp->ws_error ("%.*s: %.*s",
- (int) i, str, (int) size, defstr);
+ if (*defstr == '?')
+ {
+ size = *pend - ++defstr;
+ if (size == 0)
+ wsp->ws_error (_("%.*s: variable null or not set"),
+ (int) i, str);
+ else
+ {
+ rc = _wsplt_subsplit (wsp, &ws, defstr, size,
+ WRDSF_NOSPLIT | WRDSF_WS |
+ WRDSF_QUOTE |
+ (wsp->ws_flags &
+ (WRDSF_NOVAR | WRDSF_NOCMD)));
+ if (rc == 0)
+ wsp->ws_error ("%.*s: %s",
+ (int) i, str, ws.ws_wordv[0]);
+ else
+ wsp->ws_error (_("%.*s: %.*s"),
+ (int) i, str, (int) size, defstr);
+ wordsplit_free (&ws);
+ }
+ }
+ value = NULL;
}
}
else if (wsp->ws_flags & WRDSF_UNDEF)
@@ -899,18 +1081,21 @@ expvar (struct wordsplit *wsp, const char *str, size_t len,
return _wsplt_nomem (wsp);
}
}
+ break;
+
+ case WRDSE_NOSPACE:
+ return _wsplt_nomem (wsp);
+
+ case WRDSE_USERERR:
+ if (wsp->ws_errno == WRDSE_USERERR)
+ free (wsp->ws_usererr);
+ wsp->ws_usererr = value;
+ /* fall through */
+ default:
+ _wsplt_seterr (wsp, rc);
+ return 1;
}
- else if (defstr && *defstr == '+')
- {
- size_t size = *pend - ++defstr;
- free (value);
- value = malloc (size + 1);
- if (!value)
- return _wsplt_nomem (wsp);
- memcpy (value, defstr, size);
- value[size] = 0;
- }
-
+
if (value)
{
if (flg & _WSNF_QUOTE)
@@ -1111,7 +1296,7 @@ expcmd (struct wordsplit *wsp, const char *str, size_t len,
}
*pend = str + j;
- if (wsp->ws_flags & WRDSF_ARGV)
+ if (wsp->ws_options & WRDSO_ARGV)
{
struct wordsplit ws;
@@ -2034,6 +2219,23 @@ wordsplit_free_words (struct wordsplit *ws)
}
void
+wordsplit_free_envbuf (struct wordsplit *ws)
+{
+ if (ws->ws_flags & WRDSF_NOCMD)
+ return;
+ if (ws->ws_envbuf)
+ {
+ size_t i;
+
+ for (i = 0; ws->ws_envbuf[i]; i++)
+ free (ws->ws_envbuf[i]);
+ free (ws->ws_envbuf);
+ ws->ws_envidx = ws->ws_envsiz = 0;
+ ws->ws_envbuf = NULL;
+ }
+}
+
+void
wordsplit_clearerr (struct wordsplit *ws)
{
if (ws->ws_errno == WRDSE_USERERR)
@@ -2048,6 +2250,7 @@ wordsplit_free (struct wordsplit *ws)
wordsplit_free_words (ws);
free (ws->ws_wordv);
ws->ws_wordv = NULL;
+ wordsplit_free_envbuf (ws);
}
const char *_wordsplit_errstr[] = {
diff --git a/src/wordsplit.h b/src/wordsplit.h
index 6a78c48..3c1d533 100644
--- a/src/wordsplit.h
+++ b/src/wordsplit.h
@@ -19,6 +19,8 @@
#include <stddef.h>
+typedef struct wordsplit wordsplit_t;
+
/* Structure used to direct the splitting. Members marked with [Input]
can be defined before calling wordsplit(), those marked with [Output]
provide return values when the function returns. If neither mark is
@@ -43,7 +45,7 @@ struct wordsplit
const char *ws_comment; /* [Input] (WRDSF_COMMENT) Comment characters. */
const char *ws_escape; /* [Input] (WRDSF_ESCAPE) Characters to be escaped
with backslash. */
- void (*ws_alloc_die) (struct wordsplit *wsp);
+ void (*ws_alloc_die) (wordsplit_t *wsp);
/* [Input] (WRDSF_ALLOC_DIE) Function called when
out of memory. Must not return. */
void (*ws_error) (const char *, ...)
@@ -56,6 +58,11 @@ struct wordsplit
output. */
const char **ws_env; /* [Input] (WRDSF_ENV, !WRDSF_NOVAR) Array of
environment variables. */
+
+ char **ws_envbuf;
+ size_t ws_envidx;
+ size_t ws_envsiz;
+
int (*ws_getvar) (char **ret, const char *var, size_t len, void *clos);
/* [Input] (WRDSF_GETVAR, !WRDSF_NOVAR) Looks up
the name VAR (LEN bytes long) in the table of
@@ -86,7 +93,7 @@ struct wordsplit
size_t ws_endp; /* Points past the last processed byte in
ws_input. */
int ws_errno; /* [Output] Error code, if an error occurred. */
- char *ws_usererr; /* [Input/Output] Points to textual description of
+ char *ws_usererr; /* Points to textual description of
the error, if ws_errno is WRDSE_USERERR. Must
be allocated with malloc(3). */
struct wordsplit_node *ws_head, *ws_tail;
@@ -94,6 +101,9 @@ struct wordsplit
int ws_lvl; /* Invocation nesting level. */
};
+/* Initial size for ws_env, if allocated automatically */
+#define WORDSPLIT_ENV_INIT 16
+
/* Wordsplit flags. */
/* Append the words found to the array resulting from a previous
call. */
@@ -164,10 +174,10 @@ struct wordsplit
#define WRDSF_ESCAPE 0x10000000
/* Incremental mode */
#define WRDSF_INCREMENTAL 0x20000000
-/* ws_command needs argv parameter */
-#define WRDSF_ARGV 0x40000000
/* Perform pathname and tilde expansion */
-#define WRDSF_PATHEXPAND 0x80000000
+#define WRDSF_PATHEXPAND 0x40000000
+/* ws_options is initialized */
+#define WRDSF_OPTIONS 0x80000000
#define WRDSF_DEFFLAGS \
(WRDSF_NOVAR | WRDSF_NOCMD | \
@@ -179,6 +189,8 @@ struct wordsplit
#define WRDSO_FAILGLOB 0x02
/* Allow a leading period to be matched by metacharacters. */
#define WRDSO_DOTGLOB 0x04
+/* ws_command needs argv parameter */
+#define WRDSO_ARGV 0x08
#define WRDSE_OK 0
#define WRDSE_EOF WRDSE_OK
@@ -192,11 +204,12 @@ struct wordsplit
#define WRDSE_GLOBERR 8
#define WRDSE_USERERR 9
-int wordsplit (const char *s, struct wordsplit *p, int flags);
+int wordsplit (const char *s, wordsplit_t *p, int flags);
int wordsplit_len (const char *s, size_t len,
- struct wordsplit *p, int flags);
-void wordsplit_free (struct wordsplit *p);
-void wordsplit_free_words (struct wordsplit *ws);
+ wordsplit_t *p, int flags);
+void wordsplit_free (wordsplit_t *p);
+void wordsplit_free_words (wordsplit_t *ws);
+void wordsplit_free_envbuf (struct wordsplit *ws);
int wordsplit_c_unquote_char (int c);
int wordsplit_c_quote_char (int c);
@@ -208,9 +221,9 @@ void wordsplit_sh_unquote_copy (char *dst, const char *src, size_t n);
void wordsplit_c_unquote_copy (char *dst, const char *src, size_t n);
void wordsplit_c_quote_copy (char *dst, const char *src, int quote_hex);
-void wordsplit_perror (struct wordsplit *ws);
-const char *wordsplit_strerror (struct wordsplit *ws);
+void wordsplit_perror (wordsplit_t *ws);
+const char *wordsplit_strerror (wordsplit_t *ws);
-void wordsplit_clearerr (struct wordsplit *ws);
+void wordsplit_clearerr (wordsplit_t *ws);
#endif

Return to:

Send suggestions and report system problems to the System administrator.