diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/wordsplit.c | 367 | ||||
-rw-r--r-- | src/wordsplit.h | 37 |
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 |