/* Variable expansion */ static int node_split_prefix (struct wordsplit *wsp, struct wordsplit_node **ptail, struct wordsplit_node *node, size_t beg, size_t len, int flg) { struct wordsplit_node *newnode = NULL; if (len == 0) return 0; if (wsnode_new (wsp, &newnode)) return 1; wsnode_insert (wsp, newnode, *ptail, 0); if (node->flags & _WSNF_WORD) { const char *str = wsnode_ptr (wsp, node); char *newstr = malloc (len + 1); if (!newstr) return _wsplt_nomem (wsp); memcpy (newstr, str + beg, len); newstr[len] = 0; newnode->flags = _WSNF_WORD; newnode->v.word = newstr; } else { newnode->v.segm.beg = node->v.segm.beg + beg; newnode->v.segm.end = newnode->v.segm.beg + len; } newnode->flags |= flg; *ptail = newnode; return 0; }
static int wordsplit_finish (struct wordsplit *wsp) { struct wordsplit_node *p; size_t n; n = 0; for (p = wsp->ws_head; p; p = p->next) n++; if (alloc_space (wsp, n + 1)) return 1; for (p = wsp->ws_head; p; p = p->next) { const char *str = wsnode_ptr (wsp, p); size_t slen = wsnode_len (p); char *newstr = malloc (slen + 1); /* Assign newstr first, even if it is NULL. This way wordsplit_free will work even if we return nomem later. */ wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = newstr; if (!newstr) return _wsplt_nomem (wsp); memcpy (newstr, str, slen); newstr[slen] = 0; wsp->ws_wordc++; } wsp->ws_wordv[wsp->ws_offs + wsp->ws_wordc] = NULL; return 0; }
static int node_expand (struct wordsplit *wsp, struct wordsplit_node *node, int (*beg_p) (int), int (*ws_exp_fn) (struct wordsplit *wsp, const char *str, size_t len, struct wordsplit_node **ptail, const char **pend, int flg)) { const char *str = wsnode_ptr (wsp, node); size_t slen = wsnode_len (node); const char *end = str + slen; const char *p; size_t off = 0; struct wordsplit_node *tail = node; for (p = str; p < end; p++) { if (*p == '\\') { p++; continue; } if (*p == '$' && beg_p (p[1])) { size_t n = p - str; if (tail != node) tail->flags |= _WSNF_JOIN; if (node_split_prefix (wsp, &tail, node, off, n, _WSNF_JOIN)) return 1; p++; if (ws_exp_fn (wsp, p, slen - n, &tail, &p, node->flags & (_WSNF_JOIN | _WSNF_QUOTE))) return 1; off += p - str + 1; str = p + 1; } } if (p > str) { if (tail != node) tail->flags |= _WSNF_JOIN; if (node_split_prefix (wsp, &tail, node, off, p - str, node->flags & (_WSNF_JOIN|_WSNF_QUOTE))) return 1; } if (tail != node) { wsnode_remove (wsp, node); wsnode_free (node); } return 0; }
static int coalesce_segment (struct wordsplit *wsp, struct wordsplit_node *node) { struct wordsplit_node *p, *end; size_t len = 0; char *buf, *cur; int stop; for (p = node; p && (p->flags & _WSNF_JOIN); p = p->next) { len += wsnode_len (p); } if (p) len += wsnode_len (p); end = p; buf = malloc (len + 1); if (!buf) return _wsplt_nomem (wsp); cur = buf; p = node; for (stop = 0; !stop;) { struct wordsplit_node *next = p->next; const char *str = wsnode_ptr (wsp, p); size_t slen = wsnode_len (p); memcpy (cur, str, slen); cur += slen; if (p != node) { node->flags |= p->flags & _WSNF_QUOTE; wsnode_remove (wsp, p); stop = p == end; wsnode_free (p); } p = next; } *cur = 0; node->flags &= ~_WSNF_JOIN; if (node->flags & _WSNF_WORD) free (node->v.word); else node->flags |= _WSNF_WORD; node->v.word = buf; return 0; }
static int node_expand_vars (struct wordsplit *wsp, struct wordsplit_node *node) { const char *str = wsnode_ptr (wsp, node); size_t slen = wsnode_len (node); const char *end = str + slen; const char *p; size_t off = 0; struct wordsplit_node *tail = node; for (p = str; p < end; p++) { if (*p == '\\') { p++; continue; } if (*p == '$') { size_t n = p - str; if (tail != node) tail->flags |= _WSNF_JOIN; if (node_split_prefix (wsp, &tail, node, off, n, _WSNF_JOIN)) return 1; p++; if (expvar (wsp, p, slen - n, &tail, &p, node->flags & (_WSNF_JOIN | _WSNF_QUOTE))) return 1; off += p - str + 1; str = p + 1; } } if (p > str) { if (tail != node) tail->flags |= _WSNF_JOIN; if (node_split_prefix (wsp, &tail, node, off, p - str, node->flags & _WSNF_JOIN)) return 1; } if (tail != node) { wsnode_remove (wsp, node); wsnode_free (node); } return 0; }
static int wsnode_quoteremoval (struct wordsplit *wsp) { struct wordsplit_node *p; void (*uqfn) (char *, const char *, size_t) = (wsp->ws_flags & WRDSF_CESCAPES) ? wordsplit_c_unquote_copy : wordsplit_sh_unquote_copy; for (p = wsp->ws_head; p; p = p->next) { const char *str = wsnode_ptr (wsp, p); size_t slen = wsnode_len (p); int unquote; if (wsp->ws_flags & WRDSF_QUOTE) { unquote = !(p->flags & _WSNF_NOEXPAND); } else unquote = 0; if (unquote) { if (!(p->flags & _WSNF_WORD)) { char *newstr = malloc (slen + 1); if (!newstr) return _wsplt_nomem (wsp); memcpy (newstr, str, slen); newstr[slen] = 0; p->v.word = newstr; p->flags |= _WSNF_WORD; } if (wsp->ws_flags & WRDSF_ESCAPE) wordsplit_general_unquote_copy (p->v.word, str, slen, wsp->ws_escape); else uqfn (p->v.word, str, slen); } } return 0; }
static int wsnode_quoteremoval (struct wordsplit *wsp) { struct wordsplit_node *p; for (p = wsp->ws_head; p; p = p->next) { const char *str = wsnode_ptr (wsp, p); size_t slen = wsnode_len (p); int unquote; if (wsp->ws_flags & WRDSF_QUOTE) { unquote = !(p->flags & _WSNF_NOEXPAND); } else unquote = 0; if (unquote) { if (!(p->flags & _WSNF_WORD)) { char *newstr = malloc (slen + 1); if (!newstr) return _wsplt_nomem (wsp); memcpy (newstr, str, slen); newstr[slen] = 0; p->v.word = newstr; p->flags |= _WSNF_WORD; } wordsplit_string_unquote_copy (wsp, p->flags & _WSNF_QUOTE, p->v.word, str, slen); } } return 0; }
static int wordsplit_pathexpand (struct wordsplit *wsp) { struct wordsplit_node *p, *next; char *pattern = NULL; size_t patsize = 0; size_t slen; 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); return 0; }
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); 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); return 0; }