On Sun, Jul 08, 2018 at 02:54:32PM +0200, Jeremie Courreges-Anglas wrote:
> On Sun, Jul 08 2018, Anton Lindqvist <[email protected]> wrote:
> > On Sat, Jul 07, 2018 at 10:06:41AM +0200, Andreas Kusalananda Kähäri wrote:
> >> Hi,
> >> 
> >> I'm not sure this is a bug or a misunderstanding by me, but it sure
> >> surprised me:
> >> 
> >>     $ set -- file.txt txt
> >> 
> >>     $ printf 'Two values: %s %s\n' "$1" "$2"
> >>     Two values: file.txt txt
> >> 
> >>     $ printf 'First value with suffix removed: %s\n' "${1%.*}"
> >>     First value with suffix removed: file
> >> 
> >>     $ printf 'First value with second value removed from end: %s\n' 
> >> "${1%.$2}"
> >>     First value with second value removed from end: txt
> >> 
> >> I would have expected the last two parameter expansion to generate the
> >> same result ("file").  Also, removing the dot ("${1%$2}") generates an
> >> empty result.
> >> 
> >> Note that using variables, this works as expected:
> >> 
> >>     $ filename=file.txt
> >>     $ suffix=txt
> >>     $ printf 'First value with second value removed from end: %s\n' 
> >> "${filename%.$suffix}"
> >>     First value with second value removed from end: file
> >> 
> >> This is on a current system using either /bin/sh or /bin/ksh and the
> >> effect of trying to remove from the start of the string is similar.
> >
> > That's indeed a bug. Lookup of positional arguments ($1 and $2) is done
> > using global() which in turn returns a pointer to static storage for
> > read-only variables (positional arguments are read-only). So the second
> > call to global() overwrites the previous one. Here's a first stab in
> > which read-only variables are copied during expansion. I ended up adding
> > a new function in order to avoid deep indenting. Also, zero st_head in
> > order to simplify the free loop; otherwise st_head.var would contain
> > stack garbage.
> >
> > Comments? OK?
> 
> s/has/have/ in the comment you're adding, I think, except for this

yep

> ok jca@ for the diff and regress parts

ok tb for these as well

> 
> > Index: eval.c
> > ===================================================================
> > RCS file: /cvs/src/bin/ksh/eval.c,v
> > retrieving revision 1.60
> > diff -u -p -r1.60 eval.c
> > --- eval.c  9 Apr 2018 17:53:36 -0000       1.60
> > +++ eval.c  8 Jul 2018 08:17:05 -0000
> > @@ -58,6 +58,8 @@ static    char   *tilde(char *);
> >  static     char   *homedir(char *);
> >  static void        alt_expand(XPtrV *, char *, char *, char *, int);
> >  
> > +static struct tbl *varcpy(struct tbl *);
> > +
> >  /* compile and expand word */
> >  char *
> >  substitute(const char *cp, int f)
> > @@ -190,7 +192,8 @@ expand(char *cp,        /* input word */
> >     doblank = 0;
> >     make_magic = 0;
> >     word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
> > -   st_head.next = NULL;
> > +
> > +   memset(&st_head, 0, sizeof(st_head));
> >     st = &st_head;
> >  
> >     while (1) {
> > @@ -305,7 +308,7 @@ expand(char *cp,        /* input word */
> >                                     st->stype = stype;
> >                                     st->base = Xsavepos(ds, dp);
> >                                     st->f = f;
> > -                                   st->var = x.var;
> > +                                   st->var = varcpy(x.var);
> >                                     st->quote = quote;
> >                                     /* skip qualifier(s) */
> >                                     if (stype)
> > @@ -577,7 +580,7 @@ expand(char *cp,        /* input word */
> >                                     Xinit(ds, dp, 128, ATEMP);
> >                     }
> >                     if (c == 0)
> > -                           return;
> > +                           goto done;
> >                     if (word != IFS_NWS)
> >                             word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
> >             } else {
> > @@ -682,6 +685,14 @@ expand(char *cp,       /* input word */
> >                     word = IFS_WORD;
> >             }
> >     }
> > +
> > +done:
> > +   for (st = &st_head; st != NULL; st = st->next) {
> > +           if (st->var == NULL || (st->var->flag & RDONLY) == 0)
> > +                   continue;
> > +
> > +           afree(st->var, ATEMP);
> > +   }
> >  }
> >  
> >  /*
> > @@ -1285,4 +1296,24 @@ alt_expand(XPtrV *wp, char *start, char 
> >             }
> >     }
> >     return;
> > +}
> > +
> > +/*
> > + * Copy the given variable if it's flagged as read-only.
> > + * Such variables has static storage and only one can therefore be 
> > referenced at
> > + * a time.
> > + * This is necessary in order to allow variable expansion expressions to 
> > refer
> > + * to multiple read-only variables.
> > + */
> > +static struct tbl *
> > +varcpy(struct tbl *vp)
> > +{
> > +   struct tbl *cpy;
> > +
> > +   if ((vp->flag & RDONLY) == 0)
> > +           return vp;
> > +
> > +   cpy = alloc(sizeof(struct tbl), ATEMP);
> > +   memcpy(cpy, vp, sizeof(struct tbl));
> > +   return cpy;
> >  }
> >
> 
> -- 
> jca | PGP : 0x1524E7EE / 5135 92C1 AD36 5293 2BDF  DDCC 0DFA 74AE 1524 E7EE

Reply via email to