Skip to content
Snippets Groups Projects
Commit 0e4c9599 authored by Herbert Xu's avatar Herbert Xu
Browse files

builtin: Fix handling of trailing IFS white spaces


The read built-in does not handle trailing IFS white spaces in
the right way, when there are more fields than variables.  Part
of the problem is that this case is handled outside of ifsbreakup.

Harald van Dijk wrote a patch to fix this by moving the magic
into ifsbreakup itself.

This patch further reorganises the ifsbreakup loop by having only
one loop over the whole string.

Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Tested-by: default avatarHarald van Dijk <harald@gigawatt.nl>
parent da534b74
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -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
Loading
Loading
@@ -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);
Loading
Loading
@@ -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;
Loading
Loading
@@ -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';
Loading
Loading
@@ -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;
 
Loading
Loading
Loading
Loading
@@ -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 */
Loading
Loading
Loading
Loading
@@ -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();
 
Loading
Loading
@@ -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);
Loading
Loading
@@ -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;
}
 
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment