diff --git a/src/wordsplit.c b/src/wordsplit.c index f0170e1..6b01887 100644 --- a/src/wordsplit.c +++ b/src/wordsplit.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #if ENABLE_NLS # include @@ -1210,6 +1212,195 @@ wordsplit_trimws (struct wordsplit *wsp) } static int +wordsplit_tildexpand (struct wordsplit *wsp) +{ + struct wordsplit_node *p; + char *uname = NULL; + size_t usize = 0; + + for (p = wsp->ws_head; p; p = p->next) + { + const char *str; + + if (p->flags & _WSNF_QUOTE) + continue; + + str = wsnode_ptr (wsp, p); + if (str[0] == '~') + { + size_t i, size, dlen; + size_t slen = wsnode_len (p); + char *dir; + struct passwd *pw; + char *newstr; + + for (i = 1; i < slen && str[i] != '/'; i++) + ; + if (i == slen) + continue; + if (i > 1) + { + if (i > usize) + { + char *p = realloc (uname, i); + if (!p) + { + free (uname); + return _wsplt_nomem (wsp); + } + uname = p; + usize = i; + } + --i; + memcpy (uname, str + 1, i); + uname[i] = 0; + pw = getpwnam (uname); + } + else + pw = getpwuid (getuid ()); + + if (!pw) + continue; + + dlen = strlen (pw->pw_dir); + size = slen - i + dlen; + newstr = malloc (size); + if (!newstr) + { + free (uname); + return _wsplt_nomem (wsp); + } + --size; + + memcpy (newstr, pw->pw_dir, dlen); + memcpy (newstr + dlen, str + i + 1, slen - i - 1); + newstr[size] = 0; + if (p->flags & _WSNF_WORD) + free (p->v.word); + p->v.word = newstr; + p->flags |= _WSNF_WORD; + } + } + free (uname); +} + +static int +isglob (const char *s, int l) +{ + while (l--) + { + if (strchr ("*?[", *s++)) + return 1; + } + return 0; +} + +static int +wordsplit_pathexpand (struct wordsplit *wsp) +{ + struct wordsplit_node *p, *next; + char *pattern = NULL; + size_t patsize = 0; + size_t slen; + char *str; + int flags = 0; + +#ifdef GLOB_PERIOD + if (wsp->ws_options & WRDSO_DOTGLOB) + flags = GLOB_PERIOD; +#endif + + for (p = wsp->ws_head; p; p = next) + { + const char *str; + + next = p->next; + + if (p->flags & _WSNF_QUOTE) + continue; + + str = wsnode_ptr (wsp, p); + slen = wsnode_len (p); + + if (isglob (str, slen)) + { + int i; + glob_t g; + struct wordsplit_node *prev; + + if (slen + 1 > patsize) + { + char *p = realloc (pattern, slen + 1); + if (!p) + return _wsplt_nomem (wsp); + pattern = p; + patsize = slen + 1; + } + memcpy (pattern, str, slen); + pattern[slen] = 0; + + switch (glob (pattern, flags, NULL, &g)) + { + case 0: + break; + + case GLOB_NOSPACE: + free (pattern); + return _wsplt_nomem (wsp); + + case GLOB_NOMATCH: + if (wsp->ws_options & WRDSO_NULLGLOB) + { + wsnode_remove (wsp, p); + wsnode_free (p); + } + else if (wsp->ws_options & WRDSO_FAILGLOB) + { + char buf[128]; + if (wsp->ws_errno == WRDSE_USERERR) + free (wsp->ws_usererr); + snprintf (buf, sizeof (buf), _("no files match pattern %s"), + pattern); + free (pattern); + wsp->ws_usererr = strdup (buf); + if (!wsp->ws_usererr) + return _wsplt_nomem (wsp); + else + return _wsplt_seterr (wsp, WRDSE_USERERR); + } + continue; + + default: + free (pattern); + return _wsplt_seterr (wsp, WRDSE_GLOBERR); + } + + prev = p; + for (i = 0; i < g.gl_pathc; i++) + { + struct wordsplit_node *newnode; + char *newstr; + + if (wsnode_new (wsp, &newnode)) + return 1; + newstr = strdup (g.gl_pathv[i]); + if (!newstr) + return _wsplt_nomem (wsp); + newnode->v.word = newstr; + newnode->flags |= _WSNF_WORD|_WSNF_QUOTE; + wsnode_insert (wsp, newnode, prev, 0); + prev = newnode; + } + globfree (&g); + + wsnode_remove (wsp, p); + wsnode_free (p); + } + } + free (pattern); +} + +static int skip_sed_expr (const char *command, size_t i, size_t len) { int state; @@ -1653,6 +1844,16 @@ wordsplit_process_list (struct wordsplit *wsp, size_t start) } } + if (wsp->ws_flags & WRDSF_PATHEXPAND) + { + wordsplit_tildexpand (wsp); + if (wsp->ws_flags & WRDSF_SHOWDBG) + { + wsp->ws_debug ("After tilde expansion:"); + wordsplit_dump_nodes (wsp); + } + } + /* Expand variables */ if (!(wsp->ws_flags & WRDSF_NOVAR)) { @@ -1709,6 +1910,16 @@ wordsplit_process_list (struct wordsplit *wsp, size_t start) wordsplit_dump_nodes (wsp); } + if (wsp->ws_flags & WRDSF_PATHEXPAND) + { + wordsplit_pathexpand (wsp); + if (wsp->ws_flags & WRDSF_SHOWDBG) + { + wsp->ws_debug ("After path expansion:"); + wordsplit_dump_nodes (wsp); + } + } + return wsp->ws_errno; } @@ -1823,7 +2034,8 @@ const char *_wordsplit_errstr[] = { N_("unbalanced curly brace"), N_("undefined variable"), N_("input exhausted"), - N_("unbalanced parenthesis") + N_("unbalanced parenthesis"), + N_("globbing error") }; int _wordsplit_nerrs = sizeof (_wordsplit_errstr) / sizeof (_wordsplit_errstr[0]); diff --git a/src/wordsplit.h b/src/wordsplit.h index eee262d..a531ac0 100644 --- a/src/wordsplit.h +++ b/src/wordsplit.h @@ -26,6 +26,7 @@ struct wordsplit size_t ws_offs; size_t ws_wordn; int ws_flags; + int ws_options; const char *ws_delim; const char *ws_comment; const char *ws_escape; @@ -122,11 +123,20 @@ struct wordsplit #define WRDSF_INCREMENTAL 0x20000000 /* ws_command needs argv parameter */ #define WRDSF_ARGV 0x40000000 +/* Perform pathname and tilde expansion */ +#define WRDSF_PATHEXPAND 0x80000000 #define WRDSF_DEFFLAGS \ (WRDSF_NOVAR | WRDSF_NOCMD | \ WRDSF_QUOTE | WRDSF_SQUEEZE_DELIMS | WRDSF_CESCAPES) +/* Remove the word that produces empty string after path expansion */ +#define WRDSO_NULLGLOB 0x01 +/* Print error message if path expansion produces empty string */ +#define WRDSO_FAILGLOB 0x02 +/* Allow a leading period to be matched by metacharacters. */ +#define WRDSO_DOTGLOB 0x04 + #define WRDSE_OK 0 #define WRDSE_EOF WRDSE_OK #define WRDSE_QUOTE 1 @@ -136,7 +146,8 @@ struct wordsplit #define WRDSE_UNDEF 5 #define WRDSE_NOINPUT 6 #define WRDSE_PAREN 7 -#define WRDSE_USERERR 8 +#define WRDSE_GLOBERR 8 +#define WRDSE_USERERR 9 int wordsplit (const char *s, struct wordsplit *p, int flags); int wordsplit_len (const char *s, size_t len, diff --git a/tests/wsp.c b/tests/wsp.c index f01f6b6..2733c44 100644 --- a/tests/wsp.c +++ b/tests/wsp.c @@ -56,6 +56,14 @@ struct kwd bool_keytab[] = { { "default", WRDSF_DEFFLAGS }, { "env_kv", WRDSF_ENV_KV }, { "incremental", WRDSF_INCREMENTAL }, + { "pathexpand", WRDSF_PATHEXPAND }, + { NULL, 0 } +}; + +struct kwd opt_keytab[] = { + { "nullglob", WRDSO_NULLGLOB }, + { "failglob", WRDSO_FAILGLOB }, + { "dotglob", WRDSO_DOTGLOB }, { NULL, 0 } }; @@ -93,8 +101,13 @@ help () putchar ('\n'); for (i = 0; string_keytab[i].name; i++) { - printf (" -%s\n", bool_keytab[i].name); - printf (" %s ARG\n", bool_keytab[i].name); + printf (" -%s\n", string_keytab[i].name); + printf (" %s ARG\n", string_keytab[i].name); + } + putchar ('\n'); + for (i = 0; opt_keytab[i].name; i++) + { + printf (" [-]%s\n", opt_keytab[i].name); } putchar ('\n'); printf (" -dooffs\n"); @@ -269,7 +282,8 @@ main (int argc, char **argv) size_t fenvmax = sizeof (fenvbase) / sizeof (fenvbase[0]); progname = argv[0]; - + + ws.ws_options = 0; for (i = 1; i < argc; i++) { char *opt = argv[i]; @@ -389,6 +403,15 @@ main (int argc, char **argv) continue; } + if (kwxlat (opt_keytab, opt, &flag) == 0) + { + if (negate) + ws.ws_options &= ~flag; + else + ws.ws_options |= flag; + continue; + } + if (strchr (opt, '=')) { assert (fenvidx < fenvmax - 1);