Hi, Am Sunday 28 June 2009 06:19:02 schrieb Herbert Xu: > Awesome! The patch looks pretty good in general. There are just > a few minor issues that we need to fix.
Thanks for your thorough review! Revised patch is attached.
>
> > +static void
> > +readcmd_handle_line(char *line, char **ap)
> > +{
> > + struct arglist arglist;
> > + struct strlist *sl;
> > + char *s, *backup;
> > +
> > + /* ifsbreakup will fiddle stack region, need a copy */
> > + s = savestr(line);
>
> savestr calls strdup so it cannot be used safely without disabling
> SIGINT in dash. sstrdup is the safe alternative. In fact, if
> you do a grabstackstr first then you only need to dup once.
thanks, should be fixed now (though I admit that I'm still not 100% confident
if I fully understood dash's memory management).
>
> > + /* need yet another copy, so that delimiters aren't lost
> > + * in case there are more fields than variables */
> > + backup = savestr(line);
> > +
> > + arglist.lastp = &arglist.list;
> > + recordregion(0, strlen(line), 0);
>
> You can avoid doing strlen if you save the stacknxt and use that
> to compute the length.
Done, thanks!
[..]
>
> > + /* preceeding backslash */
> > + if (backslash != 0) {
> > + backslash = 0;
> > + if (c != '\n') {
> > + STPUTC(c, p);
>
> Need to output CTLESC for ifsbreakup and check for raw CTLESC
> and escape those. Also need to rmescapes after ifsbreakup for
> each argument.
Thanks, that was a logic error from my side.
I hope I got this right with the patch, but I must admit that this is pretty
much a shot in the dark with the new patch, as I don't think I've completely
understood the logic behind escape character processing in dash yet :/.
Cheers,
Stefan.
diff --git a/src/expand.c b/src/expand.c
index 7995d40..48c45e5 100644
--- a/src/expand.c
+++ b/src/expand.c
@@ -117,9 +117,6 @@ STATIC char *evalvar(char *, int);
STATIC size_t strtodest(const char *, const char *, int);
STATIC void memtodest(const char *, size_t, const char *, int);
STATIC ssize_t varvalue(char *, int, int);
-STATIC void recordregion(int, int, int);
-STATIC void removerecordregions(int);
-STATIC void ifsbreakup(char *, struct arglist *);
STATIC void ifsfree(void);
STATIC void expandmeta(struct strlist *, int);
#ifdef HAVE_GLOB
@@ -412,7 +409,7 @@ lose:
}
-STATIC void
+void
removerecordregions(int endoff)
{
if (ifslastp == NULL)
@@ -1001,7 +998,7 @@ value:
* string for IFS characters.
*/
-STATIC void
+void
recordregion(int start, int end, int nulonly)
{
struct ifsregion *ifsp;
@@ -1028,7 +1025,7 @@ recordregion(int start, int end, int nulonly)
* strings to the argument list. The regions of the string to be
* searched for IFS characters have been stored by recordregion.
*/
-STATIC void
+void
ifsbreakup(char *string, struct arglist *arglist)
{
struct ifsregion *ifsp;
diff --git a/src/expand.h b/src/expand.h
index 1862aea..405af0b 100644
--- a/src/expand.h
+++ b/src/expand.h
@@ -67,6 +67,9 @@ void expari(int);
#define rmescapes(p) _rmescapes((p), 0)
char *_rmescapes(char *, int);
int casematch(union node *, char *);
+void recordregion(int, int, int);
+void removerecordregions(int);
+void ifsbreakup(char *, struct arglist *);
/* From arith.y */
intmax_t arith(const char *);
diff --git a/src/miscbltin.c b/src/miscbltin.c
index 3f91bc3..8f2576b 100644
--- a/src/miscbltin.c
+++ b/src/miscbltin.c
@@ -55,18 +55,93 @@
#include "miscbltin.h"
#include "mystring.h"
#include "main.h"
+#include "expand.h"
+#include "parser.h"
#undef rflag
+/** handle one line of the read command.
+ * more fields than variables -> remainder shall be part of last variable.
+ * less fields than variables -> remaining variables unset.
+ *
+ * @param line complete line of input
+ * @param ap argument (variable) list
+ * @param len length of line including trailing '\0'
+ */
+static void
+readcmd_handle_line(char *line, char **ap, size_t len)
+{
+ struct arglist arglist;
+ struct strlist *sl;
+ char *s, *backup;
+
+ /* ifsbreakup will fiddle with stack region... */
+ s = grabstackstr(line + len);
+ /* need a copy, so that delimiters aren't lost
+ * in case there are more fields than variables */
+ if (len > 0) {
+ backup = sstrdup(line);
+ } else {
+ /* len==0, so just nullify all arguments...
+ * otherwise memcpy (from sstrdup) would be called
+ * with equal memory regions.
+ */
+ backup = NULL;
+ goto nullify;
+ }
+
+ arglist.lastp = &arglist.list;
+ recordregion(0, len, 0);
+
+ ifsbreakup(s, &arglist);
+ *arglist.lastp = NULL;
+ removerecordregions(0);
+
+ for (sl = arglist.list; sl != NULL; sl = sl->next) {
+
+ /* remaining fields present, but no variables left. */
+ if ((*(ap + 1) == NULL) && (sl->next != NULL)) {
+ 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);
+
+ ungrabstackstr(backup, 0);
+ ungrabstackstr(s, 0);
+ return;
+ }
+
+ /* set variable to field */
+ rmescapes(sl->text);
+ setvar(*ap, sl->text, 0);
+ ap++;
+ }
+
+nullify:
+ /* nullify remaining arguments */
+ for (; *ap != NULL; ap++) {
+ setvar(*ap, nullstr, 0);
+ }
+
+ if (backup != NULL) {
+ ungrabstackstr(backup, 0);
+ }
+ ungrabstackstr(s, 0);
+}
/*
* The read builtin. The -e option causes backslashes to escape the
- * following character.
+ * following character. The -p option followed by an argument prompts
+ * with the argument.
*
* This uses unbuffered input, which may be avoidable in some cases.
*/
-
int
readcmd(int argc, char **argv)
{
@@ -75,14 +150,14 @@ readcmd(int argc, char **argv)
char c;
int rflag;
char *prompt;
- const char *ifs;
char *p;
- int startword;
- int status;
int i;
+ int res;
+ int status;
rflag = 0;
prompt = NULL;
+ status = 0;
while ((i = nextopt("p:r")) != '\0') {
if (i == 'p')
prompt = optionarg;
@@ -95,60 +170,54 @@ readcmd(int argc, char **argv)
flushall();
#endif
}
- if (*(ap = argptr) == NULL)
+ if (*(ap = argptr) == NULL) {
sh_error("arg count");
- if ((ifs = bltinlookup("IFS")) == NULL)
- ifs = defifs;
- status = 0;
- startword = 1;
+ }
+
backslash = 0;
STARTSTACKSTR(p);
for (;;) {
- if (read(0, &c, 1) != 1) {
+ /* read character by character until eol */
+ res = read(0, &c, 1);
+ if (res != 1) {
status = 1;
break;
}
- if (c == '\0')
- continue;
- if (backslash) {
+
+ if (c == '\0') {
backslash = 0;
- if (c != '\n')
- goto put;
- continue;
- }
- if (!rflag && c == '\\') {
- backslash++;
continue;
}
- if (c == '\n')
+
+ /* eol reached, end loop */
+ if ((backslash == 0) && (c == '\n')) {
break;
- if (startword && *ifs == ' ' && strchr(ifs, c)) {
+ }
+
+ /* preceeding backslash */
+ if (backslash != 0) {
+ backslash = 0;
+ if (c != '\n') {
+ STPUTC(CTLESC, p);
+ STPUTC(c, p);
+ }
continue;
}
- startword = 0;
- if (ap[1] != NULL && strchr(ifs, c) != NULL) {
- STACKSTRNUL(p);
- setvar(*ap, stackblock(), 0);
- ap++;
- startword = 1;
- STARTSTACKSTR(p);
- } else {
-put:
- STPUTC(c, p);
+
+ if ((! rflag) && (c == '\\')) {
+ backslash++;
+ continue;
}
+
+ backslash = 0;
+ STPUTC(c, p);
}
+
STACKSTRNUL(p);
- /* Remove trailing blanks */
- while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
- *p = '\0';
- setvar(*ap, stackblock(), 0);
- while (*++ap != NULL)
- setvar(*ap, nullstr, 0);
+ readcmd_handle_line(stackblock(), ap, p - (char *)stackblock());
return status;
}
-
-
/*
* umask builtin
*
signature.asc
Description: This is a digitally signed message part.
