text: /*
   * sh.glob.c: Regular expression expansion
   */
  /*-
   * Copyright (c) 1980, 1991 The Regents of the University of California.
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in the
   *    documentation and/or other materials provided with the distribution.
   * 3. Neither the name of the University nor the names of its contributors
   *    may be used to endorse or promote products derived from this software
   *    without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   */
  #include "sh.h"
  #include "tc.h"
  #include "tw.h"
  
  #include "glob.h"
  
  /*
   * Values for gflag
   */
  #define	G_NONE	0		/* No globbing needed			*/
  #define	G_GLOB	1		/* string contains *?[] characters	*/
  #define	G_CSH	2		/* string contains ~`{ characters	*/
  
  #define	GLOBSPACE	100	/* Alloc increment			*/
  
  
  #define LBRC '{'
  #define RBRC '}'
  #define LBRK '['
  #define RBRK ']'
  #define EOS '\0'
  
  /*
   * globbing is now done in two stages. In the first pass we expand
   * csh globbing idioms ~`{ and then we proceed doing the normal
   * globbing if needed ?*[
   *
   * Csh type globbing is handled in globexpand() and the rest is
   * handled in glob() which is part of the 4.4BSD libc.
   *
   */
  static	Char	 *globtilde	(Char *);
  static	Char     *handleone	(Char *, Char **, int);
  static	Char	**libglob	(Char **);
  static	Char	**globexpand	(Char **, int);
  static	int	  globbrace	(const Char *, Char ***);
  static  void	  expbrace	(Char ***, Char ***, int);
  static	void	  pword		(struct blk_buf *, struct Strbuf *);
  static	void	  backeval	(struct blk_buf *, struct Strbuf *, Char *,
  				 int);
  static Char *
  globtilde(Char *s)
  {
      Char *name, *u, *home, *res;
  
      u = s;
      for (s++; *s && *s != '/' && *s != ':'; s++)
  	continue;
      name = Strnsave(u + 1, s - (u + 1));
      cleanup_push(name, xfree);
      home = gethdir(name);
      if (home == NULL) {
  	if (adrof(STRnonomatch)) {
  	    cleanup_until(name);
  	    return u;
  	}
  	if (*name)
  	    stderror(ERR_UNKUSER, short2str(name));
  	else
  	    stderror(ERR_NOHOME);
      }
      cleanup_until(name);
      if (home[0] == '/' && home[1] == '\0' && s[0] == '/')
  	res = Strsave(s);
      else
  	res = Strspl(home, s);
      xfree(home);
      xfree(u);
      return res;
  }
  
  /* Returns a newly allocated string, old or NULL */
  Char *
  globequal(Char *old)
  {
      int     dig;
      const Char *dir;
      Char    *b;
  
      /*
       * kfk - 17 Jan 1984 - stack hack allows user to get at arbitrary dir names
       * in stack. PWP: let =foobar pass through (for X windows)
       */
      if (old[1] == '-' && (old[2] == '\0' || old[2] == '/')) {
  	/* =- */
  	const Char *olddir = varval (STRowd);
  
  	if (olddir && *olddir &&
  	    !dcwd->di_next->di_name && !dcwd->di_prev->di_name)
  	    return Strspl(olddir, &old[2]);
  	dig = -1;
  	b = &old[2];
      }
      else if (Isdigit(old[1])) {
  	/* =<number> */
  	dig = old[1] - '0';
  	for (b = &old[2]; Isdigit(*b); b++)
  	    dig = dig * 10 + (*b - '0');
  	if (*b != '\0' && *b != '/')
  	    /* =<number>foobar */
  	    return old;
      }
      else
  	/* =foobar */
  	return old;
  
      dir = getstakd(dig);
      if (dir == NULL)
  	return NULL;
      return Strspl(dir, b);
  }
  
  static int
  globbrace(const Char *s, Char ***bl)
  {
      struct Strbuf gbuf = Strbuf_INIT;
      struct blk_buf bb = BLK_BUF_INIT;
      int     i;
      const Char *p, *pm, *pe, *pl;
      size_t prefix_len;
  
      /* copy part up to the brace */
      for (p = s; *p != LBRC; p++)
  	;
      prefix_len = p - s;
  
      /* check for balanced braces */
      for (i = 0, pe = ++p; *pe; pe++)
  	if (*pe == LBRK) {
  	    /* Ignore everything between [] */
  	    for (++pe; *pe != RBRK && *pe != EOS; pe++)
  		continue;
  	    if (*pe == EOS)
  		return (-RBRK);
  	}
  	else if (*pe == LBRC)
  	    i++;
  	else if (*pe == RBRC) {
  	    if (i == 0)
  		break;
  	    i--;
  	}
  
      if (i != 0 || *pe == '\0')
  	return (-RBRC);
  
      Strbuf_appendn(&gbuf, s, prefix_len);
  
      for (i = 0, pl = pm = p; pm <= pe; pm++)
  	switch (*pm) {
  	case LBRK:
  	    for (++pm; *pm != RBRK && *pm != EOS; pm++)
  		continue;
  	    if (*pm == EOS) {
  		bb_cleanup(&bb);
  		xfree(gbuf.s);
  		return (-RBRK);
  	    }
  	    break;
  	case LBRC:
  	    i++;
  	    break;
  	case RBRC:
  	    if (i) {
  		i--;
  		break;
  	    }
  	    /* FALLTHROUGH */
  	case ',':
  	    if (i && *pm == ',')
  		break;
  	    else {
  		gbuf.len = prefix_len;
  		Strbuf_appendn(&gbuf, pl, pm - pl);
  		Strbuf_append(&gbuf, pe + 1);
  		Strbuf_terminate(&gbuf);
  		bb_append(&bb, Strsave(gbuf.s));
  		pl = pm + 1;
  	    }
  	    break;
  	default:
  	    break;
  	}
      *bl = bb_finish(&bb);
      xfree(gbuf.s);
      return bb.len;
  }
  
  
  static void
  expbrace(Char ***nvp, Char ***elp, int size)
  {
      Char **vl, **el, **nv, *s;
  
      vl = nv = *nvp;
      if (elp != NULL)
  	el = *elp;
      else
  	el = vl + blklen(vl);
  
      for (s = *vl; s; s = *++vl) {
  	Char  **vp, **bp;
  
  	/* leave {} untouched for find */
  	if (s[0] == '{' && (s[1] == '\0' || (s[1] == '}' && s[2] == '\0')))
  	    continue;
  	if (Strchr(s, '{') != NULL) {
  	    Char  **bl = NULL;
  	    int     len;
  
  	    if ((len = globbrace(s, &bl)) < 0)
  		stderror(ERR_MISSING, -len);
  	    xfree(s);
  	    if (len == 1) {
  		*vl-- = *bl;
  		xfree(bl);
  		continue;
  	    }
  	    if (&el[len] >= &nv[size]) {
  		size_t l, e;
  		l = &el[len] - &nv[size];
  		size += GLOBSPACE > l ? GLOBSPACE : l;
  		l = vl - nv;
  		e = el - nv;
  		nv = xrealloc(nv, size * sizeof(Char *));
  		*nvp = nv; /* To keep cleanups working */
  		vl = nv + l;
  		el = nv + e;
  	    }
  	    /* nv vl   el     bl
  	     * |  |    |      |
  	     * -.--..--	      x--
  	     *   |            len
  	     *   vp
  	     */
  	    vp = vl--;
  	    *vp = *bl;
  	    len--;
  	    for (bp = el; bp != vp; bp--)
  		bp[len] = *bp;
  	    el += len;
  	    /* nv vl    el bl
  	     * |  |     |  |
  	     * -.-x  ---    --
  	     *   |len
  	     *   vp
  	     */
  	    vp++;
  	    for (bp = bl + 1; *bp; *vp++ = *bp++)
  		continue;
  	    xfree(bl);
  	}
  
      }
      if (elp != NULL)
  	*elp = el;
  }
  
  static Char **
  globexpand(Char **v, int noglob)
  {
      Char   *s;
      Char  ***fnv, **vl, **el;
      int     size = GLOBSPACE;
  
  
      fnv = xmalloc(sizeof(Char ***));
      *fnv = vl = xmalloc(sizeof(Char *) * size);
      *vl = NULL;
      cleanup_push(fnv, blk_indirect_cleanup);
  
      /*
       * Step 1: expand backquotes.
       */
      while ((s = *v++) != NULL) {
  	if (Strchr(s, '`')) {
  	    int     i;
  	    Char **expanded;
  
  	    expanded = dobackp(s, 0);
  	    for (i = 0; expanded[i] != NULL; i++) {
  		*vl++ = expanded[i];
  		if (vl == &(*fnv)[size]) {
  		    size += GLOBSPACE;
  		    *fnv = xrealloc(*fnv, size * sizeof(Char *));
  		    vl = &(*fnv)[size - GLOBSPACE];
  		}
  	    }
  	    xfree(expanded);
  	}
  	else {
  	    *vl++ = Strsave(s);
  	    if (vl == &(*fnv)[size]) {
  		size += GLOBSPACE;
  		*fnv = xrealloc(*fnv, size * sizeof(Char *));
  		vl = &(*fnv)[size - GLOBSPACE];
  	    }
  	}
  	*vl = NULL;
      }
  
      if (noglob)
  	goto done;
  
      /*
       * Step 2: expand braces
       */
      el = vl;
      expbrace(fnv, &el, size);
  
  
      /*
       * Step 3: expand ~ =
       */
      vl = *fnv;
      for (s = *vl; s; s = *++vl)
  	switch (*s) {
  	    Char *ns;
  	case '~':
  	    *vl = globtilde(s);
  	    break;
  	case '=':
  	    if ((ns = globequal(s)) == NULL) {
  		if (!adrof(STRnonomatch))
  		    stderror(ERR_DEEP); /* Error */
  	    }
  	    if (ns && ns != s) {
  		/* Expansion succeeded */
  		xfree(s);
  		*vl = ns;
  	    }
  	    break;
  	default:
  	    break;
  	}
      vl = *fnv;
  
      /*
       * Step 4: expand .. if the variable symlinks==expand is set
       */
      if (symlinks == SYM_EXPAND) {
  	for (s = *vl; s; s = *++vl) {
  	    *vl = dnormalize(s, 1);
  	    xfree(s);
  	}
      }
  
   done:
      cleanup_ignore(fnv);
      cleanup_until(fnv);
      vl = *fnv;
      xfree(fnv);
      return vl;
  }
  
  static Char *
  handleone(Char *str, Char **vl, int action)
  {
      size_t chars;
      Char **t, *p, *strp;
  
      switch (action) {
      case G_ERROR:
  	setname(short2str(str));
  	blkfree(vl);
  	stderror(ERR_NAME | ERR_AMBIG);
  	break;
      case G_APPEND:
  	chars = 0;
  	for (t = vl; (p = *t++) != NULL; chars++)
  	    chars += Strlen(p);
  	str = xmalloc(chars * sizeof(Char));
  	for (t = vl, strp = str; (p = *t++) != NULL; chars++) {
  	    while (*p)
  		 *strp++ = *p++ & TRIM;
  	    *strp++ = ' ';
  	}
  	*--strp = '\0';
  	blkfree(vl);
  	break;
      case G_IGNORE:
  	str = Strsave(strip(*vl));
  	blkfree(vl);
  	break;
      default:
  	break;
      }
      return (str);
  }
  
  static Char **
  libglob(Char **vl)
  {
      int     gflgs = GLOB_QUOTE | GLOB_NOMAGIC | GLOB_ALTNOT;
      glob_t  globv;
      char   *ptr;
      int     nonomatch = adrof(STRnonomatch) != 0, magic = 0, match = 0;
  
      if (adrof(STRglobdot))
         gflgs |= GLOB_DOT;
  
      if (adrof(STRglobstar))
         gflgs |= GLOB_STAR;
  
      if (!vl || !vl[0])
  	return(vl);
  
      globv.gl_offs = 0;
      globv.gl_pathv = 0;
      globv.gl_pathc = 0;
  
      if (nonomatch)
  	gflgs |= GLOB_NOCHECK;
  
      do {
  	ptr = short2qstr(*vl);
  	switch (glob(ptr, gflgs, 0, &globv)) {
  	case GLOB_ABEND:
  	    globfree(&globv);
  	    setname(ptr);
  	    stderror(ERR_NAME | ERR_GLOB);
  	    /* NOTREACHED */
  	case GLOB_NOSPACE:
  	    globfree(&globv);
  	    stderror(ERR_NOMEM);
  	    /* NOTREACHED */
  	default:
  	    break;
  	}
  	if (globv.gl_flags & GLOB_MAGCHAR) {
  	    match |= (globv.gl_matchc != 0);
  	    magic = 1;
  	}
  	gflgs |= GLOB_APPEND;
      }
      while (*++vl);
      vl = (globv.gl_pathc == 0 || (magic && !match && !nonomatch)) ? 
  	NULL : blk2short(globv.gl_pathv);
      globfree(&globv);
      return (vl);
  }
  
  Char   *
  globone(Char *str, int action)
  {
      Char   *v[2], **vl, **vo;
      int gflg, noglob;
  
      noglob = adrof(STRnoglob) != 0;
      v[0] = str;
      v[1] = 0;
      gflg = tglob(v);
      if (gflg == G_NONE)
  	return (strip(Strsave(str)));
  
      if (gflg & G_CSH) {
  	/*
  	 * Expand back-quote, tilde and brace
  	 */
  	vo = globexpand(v, noglob);
  	if (noglob || (gflg & G_GLOB) == 0) {
  	    vl = vo;
  	    goto result;
  	}
  	cleanup_push(vo, blk_cleanup);
      }
      else if (noglob || (gflg & G_GLOB) == 0)
  	return (strip(Strsave(str)));
      else
  	vo = v;
  
      vl = libglob(vo);
      if (gflg & G_CSH) {
      	if (vl != vo)
  	    cleanup_until(vo);
  	else
  	    cleanup_ignore(vo);
      }
      if (vl == NULL) {
  	setname(short2str(str));
  	stderror(ERR_NAME | ERR_NOMATCH);
      }
   result:
      if (vl && vl[0] == NULL) {
  	xfree(vl);
  	return (Strsave(STRNULL));
      }
      if (vl && vl[1]) 
  	return (handleone(str, vl, action));
      else {
  	str = strip(*vl);
  	xfree(vl);
  	return (str);
      }
  }
  
  Char  **
  globall(Char **v, int gflg)
  {
      Char  **vl, **vo;
      int noglob;
  
      if (!v || !v[0])
  	return saveblk(v);
  
      noglob = adrof(STRnoglob) != 0;
  
      if (gflg & G_CSH)
  	/*
  	 * Expand back-quote, tilde and brace
  	 */
  	vl = vo = globexpand(v, noglob);
      else
  	vl = vo = saveblk(v);
  
      if (!noglob && (gflg & G_GLOB)) {
  	cleanup_push(vo, blk_cleanup);
  	vl = libglob(vo);
  	if (vl == vo)
  	    cleanup_ignore(vo);
  	cleanup_until(vo);
      }
      else
  	trim(vl);
  
      return vl;
  }
  
  Char **
  glob_all_or_error(Char **v)
  {
      int gflag;
  
      gflag = tglob(v);
      if (gflag) {
  	v = globall(v, gflag);
  	if (v == NULL)
  	    stderror(ERR_NAME | ERR_NOMATCH);
      } else {
  	v = saveblk(v);
  	trim(v);
      }
      return v;
  }
  
  void
  rscan(Char **t, void (*f) (Char))
  {
      Char *p;
  
      while ((p = *t++) != NULL)
  	while (*p)
  	    (*f) (*p++);
  }
  
  void
  trim(Char **t)
  {
      Char *p;
  
      while ((p = *t++) != NULL)
  	while (*p) {
  #if INVALID_BYTE != 0
  	    if ((*p & INVALID_BYTE) != INVALID_BYTE)	/* *p < INVALID_BYTE */
  #endif
  		*p &= TRIM;
  	    p++;
  	}
  }
  
  int
  tglob(Char **t)
  {
      int gflag;
      const Char *p;
  
      gflag = 0;
      while ((p = *t++) != NULL) {
  	if (*p == '~' || *p == '=')
  	    gflag |= G_CSH;
  	else if (*p == '{' &&
  		 (p[1] == '\0' || (p[1] == '}' && p[2] == '\0')))
  	    continue;
  	while (*p != '\0') {
  	    if (*p == '`') {
  		gflag |= G_CSH;
  #ifdef notdef
  		/*
  		 * We do want to expand echo `echo '*'`, so we don't\
  		 * use this piece of code anymore.
  		 */
  		p++;
  		while (*p && *p != '`') 
  		    if (*p++ == '\\') {
  			if (*p)		/* Quoted chars */
  			    p++;
  			else
  			    break;
  		    }
  		if (!*p)		/* The matching ` */
  		    break;
  #endif
  	    }
  	    else if (*p == '{')
  		gflag |= G_CSH;
  	    else if (isglob(*p))
  		gflag |= G_GLOB;
  	    else if (symlinks == SYM_EXPAND && 
  		p[1] && ISDOTDOT(p) && (p == *(t-1) || *(p-1) == '/') )
  	    	gflag |= G_CSH;
  	    p++;
  	}
      }
      return gflag;
  }
  
  /*
   * Command substitute cp.  If literal, then this is a substitution from a
   * << redirection, and so we should not crunch blanks and tabs, separating
   * words only at newlines.
   */
  Char  **
  dobackp(Char *cp, int literal)
  {
      struct Strbuf word = Strbuf_INIT;
      struct blk_buf bb = BLK_BUF_INIT;
      Char *lp, *rp, *ep;
  
      cleanup_push(&bb, bb_cleanup);
      cleanup_push(&word, Strbuf_cleanup);
      for (;;) {
  	for (lp = cp; *lp != '\0' && *lp != '`'; lp++)
  	    ;
  	Strbuf_appendn(&word, cp, lp - cp);
  	if (*lp == 0)
  	    break;
  	lp++;
  	for (rp = lp; *rp && *rp != '`'; rp++)
  	    if (*rp == '\\') {
  		rp++;
  		if (!*rp)
  		    goto oops;
  	    }
  	if (!*rp) {
  	oops:
  	    cleanup_until(&bb);
  	    stderror(ERR_UNMATCHED, '`');
  	}
  	ep = Strnsave(lp, rp - lp);
  	cleanup_push(ep, xfree);
  	backeval(&bb, &word, ep, literal);
  	cleanup_until(ep);
  	cp = rp + 1;
      }
      if (word.len != 0)
  	pword(&bb, &word);
      cleanup_ignore(&bb);
      cleanup_until(&bb);
      return bb_finish(&bb);
  }
  
  
  static void
  backeval(struct blk_buf *bb, struct Strbuf *word, Char *cp, int literal)
  {
      ssize_t icnt;
      Char c, *ip;
      struct command faket;
      int    hadnl;
      int     pvec[2], quoted;
      Char   *fakecom[2], ibuf[BUFSIZE];
  
      hadnl = 0;
      icnt = 0;
      if (!literal) {
  	for (ip = cp; (*ip & QUOTE) != 0; ip++)
  		continue;
  	quoted = *ip == '\0';
      } else
  	quoted = literal;
      faket.t_dtyp = NODE_COMMAND;
      faket.t_dflg = F_BACKQ;
      faket.t_dlef = 0;
      faket.t_drit = 0;
      faket.t_dspr = 0;
      faket.t_dcom = fakecom;
      fakecom[0] = STRfakecom1;
      fakecom[1] = 0;
  
      /*
       * We do the psave job to temporarily change the current job so that the
       * following fork is considered a separate job.  This is so that when
       * backquotes are used in a builtin function that calls glob the "current
       * job" is not corrupted.  We only need one level of pushed jobs as long as
       * we are sure to fork here.
       */
      psavejob();
      cleanup_push(&faket, psavejob_cleanup); /* faket is only a marker */
  
      /*
       * It would be nicer if we could integrate this redirection more with the
       * routines in sh.sem.c by doing a fake execute on a builtin function that
       * was piped out.
       */
      mypipe(pvec);
      cleanup_push(&pvec[0], open_cleanup);
      cleanup_push(&pvec[1], open_cleanup);
      if (pfork(&faket, -1) == 0) {
  	jmp_buf_t osetexit;
  	struct command *t;
  	size_t omark;
  
  	xclose(pvec[0]);
  	(void) dmove(pvec[1], 1);
  	(void) dmove(SHDIAG,  2);
  	initdesc();
  	closem();
  	arginp = cp;
  	for (arginp = cp; *cp; cp++) {
  	    *cp &= TRIM;
  	    if (is_set(STRcsubstnonl) && (*cp == '\n' || *cp == '\r'))
  		*cp = ' ';
  	}
  
          /*
  	 * In the child ``forget'' everything about current aliases or
  	 * eval vectors.
  	 */
  	alvec = NULL;
  	evalvec = NULL;
  	alvecp = NULL;
  	evalp = NULL;
  
  	omark = cleanup_push_mark();
  	getexit(osetexit);
  	for (;;) {
  	    struct wordent paraml1;
  	    initlex(&paraml1);
  
  	    (void) setexit();
  	    justpr = 0;
  	    
  	    if (haderr) {
  		/* unwind */
  		doneinp = 0;
  		cleanup_pop_mark(omark);
  		resexit(osetexit);
  		reset();
  	    }
  	    if (seterr) {
  		xfree(seterr);
  		seterr = NULL;
  	    }
  
  	    freelex(&paraml1);
  	    (void) lex(&paraml1);
  	    cleanup_push(&paraml1, lex_cleanup);
  	    if (seterr)
  		stderror(ERR_OLD);
  	    alias(&paraml1);
  	    t = syntax(paraml1.next, &paraml1, 0);
  	    cleanup_push(t, syntax_cleanup);
  	    /* The F_BACKQ flag must set so the job output is correct if
  	     * printexitvalue is set.  If it's not set, the job output
  	     * will have "Exit N" appended where N is the exit status. */
  	    if (t)
  		    t->t_dflg = F_BACKQ|F_NOFORK;
  	    if (seterr)
  		stderror(ERR_OLD);
  #ifdef SIGTSTP
  	    signal(SIGTSTP, SIG_IGN);
  #endif
  #ifdef SIGTTIN
  	    signal(SIGTTIN, SIG_IGN);
  #endif
  #ifdef SIGTTOU
  	    signal(SIGTTOU, SIG_IGN);
  #endif
  	    execute(t, -1, NULL, NULL, TRUE);
  
  	    cleanup_until(&paraml1);
  	}
      }
      cleanup_until(&pvec[1]);
      c = 0;
      ip = NULL;
      do {
  	ssize_t     cnt = 0;
  
  	for (;;) {
  	    if (icnt == 0) {
  		ip = ibuf;
  		icnt = wide_read(pvec[0], ibuf, BUFSIZE, 0);
  		if (icnt <= 0)
  		    goto eof;
  	    }
  	    if (hadnl)
  		break;
  	    --icnt;
  	    c = (*ip++ & TRIM);
  	    if (c == 0)
  		break;
  #if defined(WINNT_NATIVE) || defined(__CYGWIN__)
  	    if (c == '\r')
  	    	c = ' ';
  #endif /* WINNT_NATIVE || __CYGWIN__ */
  	    if (c == '\n') {
  		/*
  		 * Continue around the loop one more time, so that we can eat
  		 * the last newline without terminating this word.
  		 */
  		hadnl = 1;
  		continue;
  	    }
  	    if (!quoted && (c == ' ' || c == '\t'))
  		break;
  	    cnt++;
  	    if (c == '\\' || quoted)
  		c |= QUOTE;
  	    Strbuf_append1(word, c);
  	}
  	/*
  	 * Unless at end-of-file, we will form a new word here if there were
  	 * characters in the word, or in any case when we take text literally.
  	 * If we didn't make empty words here when literal was set then we
  	 * would lose blank lines.
  	 */
  	if (c != 0 && (cnt || literal))
  	    pword(bb, word);
  	hadnl = 0;
      } while (c > 0);
   eof:
      cleanup_until(&pvec[0]);
      pwait();
      cleanup_until(&faket); /* psavejob_cleanup(); */
  }
  
  static void
  pword(struct blk_buf *bb, struct Strbuf *word)
  {
      Char *s;
  
      s = Strbuf_finish(word);
      bb_append(bb, s);
      *word = Strbuf_init;
  }
  
  int
  Gmatch(const Char *string, const Char *pattern)
  {
      return Gnmatch(string, pattern, NULL);
  }
  
  int
  Gnmatch(const Char *string, const Char *pattern, const Char **endstr)
  {
      Char ***fblk, **p;
      const Char *tstring = string;
      int	   gpol = 1, gres = 0;
  
      if (*pattern == '^') {
  	gpol = 0;
  	pattern++;
      }
  
      fblk = xmalloc(sizeof(Char ***));
      *fblk = xmalloc(GLOBSPACE * sizeof(Char *));
      (*fblk)[0] = Strsave(pattern);
      (*fblk)[1] = NULL;
  
      cleanup_push(fblk, blk_indirect_cleanup);
      expbrace(fblk, NULL, GLOBSPACE);
  
      if (endstr == NULL)
  	/* Exact matches only */
  	for (p = *fblk; *p; p++) 
  	    gres |= t_pmatch(string, *p, &tstring, 1) == 2 ? 1 : 0;
      else {
  	const Char *end;
  
  	/* partial matches */
          end = Strend(string);
  	for (p = *fblk; *p; p++)
  	    if (t_pmatch(string, *p, &tstring, 1) != 0) {
  		gres |= 1;
  		if (end > tstring)
  		    end = tstring;
  	    }
  	*endstr = end;
      }
  
      cleanup_until(fblk);
      return(gres == gpol);
  } 
  
  /* t_pmatch():
   *	Return 2 on exact match, 	
   *	Return 1 on substring match.
   *	Return 0 on no match.
   *	*estr will point to the end of the longest exact or substring match.
   */
  int
  t_pmatch(const Char *string, const Char *pattern, const Char **estr, int cs)
  {
      Char stringc, patternc, rangec;
      int     match, negate_range;
      const Char *pestr, *nstring;
  
      for (nstring = string;; string = nstring) {
  	stringc = *nstring++ & TRIM;
  	patternc = *pattern++ & TRIM;
  	switch (patternc) {
  	case '\0':
  	    *estr = string;
  	    return (stringc == '\0' ? 2 : 1);
  	case '?':
  	    if (stringc == 0)
  		return (0);
  	    break;
  	case '*':
  	    if (!*pattern) {
  		*estr = Strend(string);
  		return (2);
  	    }
  	    pestr = NULL;
  
  	    for (;;) {
  		switch(t_pmatch(string, pattern, estr, cs)) {
  		case 0:
  		    break;
  		case 1:
  		    pestr = *estr;/*FIXME: does not guarantee longest match */
  		    break;
  		case 2:
  		    return 2;
  		default:
  		    abort();	/* Cannot happen */
  		}
  		stringc = *string++ & TRIM;
  		if (!stringc)
  		    break;
  	    }
  
  	    if (pestr) {
  		*estr = pestr;
  		return 1;
  	    }
  	    else
  		return 0;
  
  	case '[':
  	    match = 0;
  	    if ((negate_range = (*pattern == '^')) != 0)
  		pattern++;
  	    while ((rangec = *pattern++ & TRIM) != '\0') {
  		if (rangec == ']')
  		    break;
  		if (match)
  		    continue;
  		if (*pattern == '-' && pattern[1] != ']') {
  		    Char rangec2;
  		    pattern++;
  		    rangec2 = *pattern++ & TRIM;
  		    match = (globcharcoll(stringc, rangec2, 0) <= 0 &&
  			globcharcoll(rangec, stringc, 0) <= 0);
  		}
  		else 
  		    match = (stringc == rangec);
  	    }
  	    if (rangec == '\0')
  		stderror(ERR_NAME | ERR_MISSING, ']');
  	    if ((!match) && (stringc == '\0'))
  		return (0);
  	    if (match == negate_range)
  		return (0);
  	    break;
  	default:
  	    if (cs ? patternc  != stringc
  		: Tolower(patternc) != Tolower(stringc))
  		return (0);
  	    break;
  	}
      }
  }
  