diff options
-rw-r--r-- | src/expand.c | 111 | ||||
-rw-r--r-- | src/expand.h | 2 | ||||
-rw-r--r-- | src/miscbltin.c | 30 |
3 files changed, 83 insertions, 60 deletions
diff --git a/src/expand.c b/src/expand.c index b2d710d..36bea76 100644 --- a/src/expand.c +++ b/src/expand.c @@ -50,6 +50,7 @@ #include <glob.h> #endif #include <ctype.h> +#include <stdbool.h> /* * Routines to expand arguments to commands. We have to deal with @@ -203,7 +204,7 @@ expandarg(union node *arg, struct arglist *arglist, int flag) * TODO - EXP_REDIR */ if (flag & EXP_FULL) { - ifsbreakup(p, &exparg); + ifsbreakup(p, -1, &exparg); *exparg.lastp = NULL; exparg.lastp = &exparg.list; expandmeta(exparg.list, flag); @@ -1016,15 +1017,18 @@ recordregion(int start, int end, int nulonly) * Break the argument string into pieces based upon IFS and add the * strings to the argument list. The regions of the string to be * searched for IFS characters have been stored by recordregion. + * If maxargs is non-negative, at most maxargs arguments will be created, by + * joining together the last arguments. */ void -ifsbreakup(char *string, struct arglist *arglist) +ifsbreakup(char *string, int maxargs, struct arglist *arglist) { struct ifsregion *ifsp; struct strlist *sp; char *start; char *p; char *q; + char *r = NULL; const char *ifs, *realifs; int ifsspc; int nulonly; @@ -1042,16 +1046,76 @@ ifsbreakup(char *string, struct arglist *arglist) ifs = nulonly ? nullstr : realifs; ifsspc = 0; while (p < string + ifsp->endoff) { + int c; + bool isifs; + bool isdefifs; + q = p; - if (*p == (char)CTLESC) - p++; - if (strchr(ifs, *p)) { + c = *p++; + if (c == (char)CTLESC) + c = *p++; + + isifs = strchr(ifs, c); + isdefifs = false; + if (isifs) + isdefifs = strchr(defifs, c); + + /* If only reading one more argument: + * If we have exactly one field, + * read that field without its terminator. + * If we have more than one field, + * read all fields including their terminators, + * except for trailing IFS whitespace. + * + * This means that if we have only IFS + * characters left, and at most one + * of them is non-whitespace, we stop + * reading here. + * Otherwise, we read all the remaining + * characters except for trailing + * IFS whitespace. + * + * In any case, r indicates the start + * of the characters to remove, or NULL + * if no characters should be removed. + */ + if (!maxargs) { + if (isdefifs) { + if (!r) + r = q; + continue; + } + + if (!(isifs && ifsspc)) + r = NULL; + + ifsspc = 0; + continue; + } + + if (ifsspc) { + if (isifs) + q = p; + + start = q; + + if (isdefifs) + continue; + + isifs = false; + } + + if (isifs) { if (!nulonly) - ifsspc = (strchr(defifs, *p) != NULL); + ifsspc = isdefifs; /* Ignore IFS whitespace at start */ if (q == start && ifsspc) { - p++; start = p; + ifsspc = 0; + continue; + } + if (maxargs > 0 && !--maxargs) { + r = q; continue; } *q = '\0'; @@ -1059,39 +1123,20 @@ ifsbreakup(char *string, struct arglist *arglist) sp->text = start; *arglist->lastp = sp; arglist->lastp = &sp->next; - p++; - if (!nulonly) { - for (;;) { - if (p >= string + ifsp->endoff) { - break; - } - q = p; - if (*p == (char)CTLESC) - p++; - if (strchr(ifs, *p) == NULL ) { - p = q; - break; - } else if (strchr(defifs, *p) == NULL) { - if (ifsspc) { - p++; - ifsspc = 0; - } else { - p = q; - break; - } - } else - p++; - } - } start = p; - } else - p++; + continue; + } + + ifsspc = 0; } } while ((ifsp = ifsp->next) != NULL); if (nulonly) goto add; } + if (r) + *r = '\0'; + if (!*start) return; diff --git a/src/expand.h b/src/expand.h index 6a90f67..26dc5b4 100644 --- a/src/expand.h +++ b/src/expand.h @@ -69,7 +69,7 @@ char *_rmescapes(char *, int); int casematch(union node *, char *); void recordregion(int, int, int); void removerecordregions(int); -void ifsbreakup(char *, struct arglist *); +void ifsbreakup(char *, int, struct arglist *); void ifsfree(void); /* From arith.y */ diff --git a/src/miscbltin.c b/src/miscbltin.c index b596fd2..39b9c47 100644 --- a/src/miscbltin.c +++ b/src/miscbltin.c @@ -67,28 +67,21 @@ * less fields than variables -> remaining variables unset. * * @param line complete line of input + * @param ac argument count * @param ap argument (variable) list * @param len length of line including trailing '\0' */ static void -readcmd_handle_line(char *s, char **ap) +readcmd_handle_line(char *s, int ac, char **ap) { struct arglist arglist; struct strlist *sl; - char *backup; - char *line; - /* ifsbreakup will fiddle with stack region... */ - line = stackblock(); s = grabstackstr(s); - /* need a copy, so that delimiters aren't lost - * in case there are more fields than variables */ - backup = sstrdup(line); - arglist.lastp = &arglist.list; - ifsbreakup(s, &arglist); + ifsbreakup(s, ac, &arglist); *arglist.lastp = NULL; ifsfree(); @@ -104,21 +97,6 @@ readcmd_handle_line(char *s, char **ap) return; } - /* remaining fields present, but no variables left. */ - if (!ap[1] && sl->next) { - size_t offset; - char *remainder; - - /* FIXME little bit hacky, assuming that ifsbreakup - * will not modify the length of the string */ - offset = sl->text - s; - remainder = backup + offset; - rmescapes(remainder); - setvar(*ap, remainder, 0); - - return; - } - /* set variable to field */ rmescapes(sl->text); setvar(*ap, sl->text, 0); @@ -211,7 +189,7 @@ start: out: recordregion(startloc, p - (char *)stackblock(), 0); STACKSTRNUL(p); - readcmd_handle_line(p + 1, ap); + readcmd_handle_line(p + 1, argc - (ap - argv), ap); return status; } |