Пример #1
0
static Namval_t *make_time(Namval_t* np)
{
	int offset = stktell(stkstd);
	char *name = nv_name(np);
	struct dctime *dp = newof(NULL,struct dctime,1,0); 
	if(!dp)
		return((Namval_t*)0);
	sfprintf(stkstd,"%s.format\0",name);
	sfputc(stkstd,0);
	dp->format = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD);
	dp->fun.disc = &timedisc;
	nv_stack(np,&dp->fun);
	return(np);
}
Пример #2
0
static Namval_t *sh_newnode(register Shfield_t *fp, Namval_t *np)
{
	char *val = np->nvalue + fp->offset;
	char *name = nv_name(np);
	register Namval_t *nq;
	int offset = stktell(stkstd);
	sfprintf(stkstd,"%s.%s\0",name,fp->name);
	sfputc(stkstd,0);
	nq = nv_search(stkptr(stkstd,offset),sh.var_tree,NV_ADD);
	if(fp->size<0)
		val = *(char**)val;
	nv_putval(nq,val,fp->flags|NV_NOFREE);
	if(fp->make)
		(*fp->make)(nq);
	return(nq);
}
Пример #3
0
static char *walk_class(register Namval_t *np, int dlete, struct dcclass *dcp)
{
	static Sfio_t *out;
	Sfio_t *outfile;
	int savtop = stktell(stkstd);
	char *savptr =  stkfreeze(stkstd,0);
	if(dlete)
		outfile = 0;
	else if(!(outfile=out))
                outfile = out =  sfnew((Sfio_t*)0,(char*)0,-1,-1,SF_WRITE|SF_STRING);
	else
		sfseek(outfile,0L,SEEK_SET);
	genvalue(outfile,&dcp->sclass,0,np);
	stkset(stkstd,savptr,savtop);
	if(!outfile)
		return((char*)0);
	sfputc(out,0);
	return((char*)out->_data);
}
Пример #4
0
static char *fmtx(Shell_t *shp, const char *string) {
    const char *cp = string;
    int n, c;
    const unsigned char *norm_state = (const unsigned char *)sh_lexstates[ST_NORM];
    int offset = stktell(shp->stk);
    if (*cp == '#' || *cp == '~') sfputc(shp->stk, '\\');
    while ((c = mb1char((char **)&cp)),
           (c > UCHAR_MAX) || (n = norm_state[c]) == 0 || n == S_EPAT) {
        ;  // empty loop
    }
    if (n == S_EOF && *string != '#') return (char *)string;
    sfwrite(shp->stk, string, --cp - string);
    for (string = cp; (c = mb1char((char **)&cp)); string = cp) {
        if ((n = cp - string) == 1) {
            n = norm_state[c];
            if (n && n != S_EPAT) sfputc(shp->stk, '\\');
            sfputc(shp->stk, c);
        } else {
            sfwrite(shp->stk, string, n);
        }
    }
    sfputc(shp->stk, 0);
    return stkptr(shp->stk, offset);
}
Пример #5
0
Файл: history.c Проект: att/ast
//
// Open the history file. If HISTNAME is not given and userid==0 then no history file. If login_sh
// and HISTFILE is longer than HIST_MAX bytes then it is cleaned up.
//
// hist_open() returns 1, if history file is opened.
//
int sh_histinit(void *sh_context) {
    Shell_t *shp = sh_context;
    int fd;
    History_t *hp;
    char *histname;
    char *fname = NULL;
    int histmask, maxlines, hist_start = 0;
    char *cp;
    off_t hsize = 0;

    shgd->hist_ptr = hist_ptr;
    if (shgd->hist_ptr) return 1;
    if (!(histname = nv_getval(HISTFILE))) {
        int offset = stktell(shp->stk);
        cp = nv_getval(HOME);
        if (cp) sfputr(shp->stk, cp, -1);
        sfputr(shp->stk, hist_fname, 0);
        stkseek(shp->stk, offset);
        histname = stkptr(shp->stk, offset);
    }

#if 0
// TODO: Figure out if this should be enabled. Originally excluded via `#ifdef future`.
    if (hp = wasopen) {
        // Reuse history file if same name.
        wasopen = 0;
        shgd->hist_ptr = hist_ptr = hp;
        if (strcmp(histname, hp->histname) == 0) {
            return 1;
        } else {
            hist_free();
        }
    }
#endif  // future

retry:
    cp = path_relative(shp, histname);
    if (!histinit) histmode = S_IRUSR | S_IWUSR;
    if ((fd = open(cp, O_BINARY | O_APPEND | O_RDWR | O_CREAT | O_CLOEXEC, histmode)) >= 0) {
        hsize = lseek(fd, (off_t)0, SEEK_END);
    }
    if ((unsigned)fd < 10) {
        int n;
        if ((n = sh_fcntl(fd, F_DUPFD_CLOEXEC, 10)) >= 0) {
            sh_close(fd);
            fd = n;
        }
    }
    // Make sure that file has history file format.
    if (hsize && hist_check(fd)) {
        sh_close(fd);
        hsize = 0;
        if (unlink(cp) >= 0) goto retry;
        fd = -1;
    }

    // Don't allow root a history_file in /tmp.
    if (fd < 0 && shgd->userid) {
        fname = ast_temp_file(NULL, NULL, &fd, O_APPEND | O_CLOEXEC);
        if (!fname) return 0;
    }

    if (fd < 0) return 0;
    // Set the file to close-on-exec.
    (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
    cp = nv_getval(HISTSIZE);
    if (cp) {
        maxlines = (unsigned)strtol(cp, NULL, 10);
    } else {
        maxlines = HIST_DFLT;
    }
    for (histmask = 16; histmask <= maxlines; histmask <<= 1) {
        ;  // empty loop
    }
    histmask -= 1;
    hp = calloc(1, sizeof(History_t) + histmask * sizeof(off_t));
    if (!hp) {
        sh_close(fd);
        return 0;
    }

    shgd->hist_ptr = hist_ptr = hp;
    hp->histshell = shp;
    hp->histsize = maxlines;
    hp->histmask = histmask;
    hp->histfp = sfnew(NULL, NULL, HIST_BSIZE, fd, SF_READ | SF_WRITE | SF_APPENDWR | SF_SHARE);
    hp->histind = 1;
    hp->histcmds[1] = 2;
    hp->histcnt = 2;
    hp->histname = strdup(histname);
    hp->histdisc = hist_disc;
    if (hsize == 0) {
        // Put special characters at front of file.
        sfwrite(hp->histfp, (char *)hist_stamp, 2);
        sfsync(hp->histfp);
    } else {
        // Initialize history list.
        int first, last;
        off_t mark, size = (HIST_MAX / 4) + maxlines * HIST_LINE;
        hp->histind = first = hist_nearend(hp, hp->histfp, hsize - size);
        histinit = 1;
        hist_eof(hp);  // this sets histind to last command
        if ((hist_start = (last = (int)hp->histind) - maxlines) <= 0) hist_start = 1;
        mark = hp->histmarker;
        while (first > hist_start) {
            size += size;
            first = hist_nearend(hp, hp->histfp, hsize - size);
            hp->histind = first;
        }
        histinit = hist_start;
        hist_eof(hp);
        if (!histinit) {
            sfseek(hp->histfp, hp->histcnt = hsize, SEEK_SET);
            hp->histind = last;
            hp->histmarker = mark;
        }
        histinit = 0;
    }
    if (fname) {
        unlink(fname);
        free(fname);
    }
    if (hist_clean(fd) && hist_start > 1 && hsize > HIST_MAX) {
#ifdef DEBUG
        sfprintf(sfstderr, "%d: hist_trim hsize=%d\n", getpid(), hsize);
        sfsync(sfstderr);
#endif  // DEBUG
        hp = hist_trim(hp, (int)hp->histind - maxlines);
    }
    sfdisc(hp->histfp, &hp->histdisc);
    STORE_VT((HISTCUR)->nvalue, i32p, &hp->histind);
    sh_timeradd(1000L * (HIST_RECENT - 30), 1, hist_touch, hp->histname);
    hp->auditfp = NULL;

    char buff[SF_BUFSIZE];
    if (!sh_isstate(shp, SH_INTERACTIVE)) return 1;
    hp->auditmask = sh_checkaudit(hp, AUDIT_FILE, buff, sizeof(buff));
    if (!hp->auditmask) return 1;

    if ((fd = sh_open(buff, O_BINARY | O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC,
                      S_IRUSR | S_IWUSR)) >= 0 &&
        fd < 10) {
        int n;
        if ((n = sh_fcntl(fd, F_DUPFD_CLOEXEC, 10)) >= 0) {
            sh_close(fd);
            fd = n;
        }
    }
    if (fd >= 0) {
        (void)fcntl(fd, F_SETFD, FD_CLOEXEC);
        hp->tty = strdup(isatty(2) ? ttyname(2) : "notty");
        hp->auditfp = sfnew(NULL, NULL, -1, fd, SF_WRITE);
    }

    return 1;
}
Пример #6
0
void
errorv(const char* id, int level, va_list ap)
{
	register int	n;
	int		fd;
	int		flags;
	char*		s;
	char*		t;
	char*		format;
	char*		library;
	const char*	catalog;

	int		line;
	char*		file;

#if !_PACKAGE_astsa
	unsigned long	d;
	struct tms	us;
#endif

	if (!error_info.init)
	{
		error_info.init = 1;
		stropt(getenv("ERROR_OPTIONS"), options, sizeof(*options), setopt, NiL);
	}
	if (level > 0)
	{
		flags = level & ~ERROR_LEVEL;
		level &= ERROR_LEVEL;
	}
	else
		flags = 0;
	if ((flags & (ERROR_USAGE|ERROR_NOID)) == ERROR_NOID)
	{
		format = (char*)id;
		id = 0;
	}
	else
		format = 0;
	if (id)
	{
		catalog = (char*)id;
		if (!*catalog || *catalog == ':')
		{
			catalog = 0;
			library = 0;
		}
		else if ((library = strchr(catalog, ':')) && !*++library)
			library = 0;
	}
	else
	{
		catalog = 0;
		library = 0;
	}
	if (catalog)
		id = 0;
	else
	{
		id = (const char*)error_info.id;
		catalog = error_info.catalog;
	}
	if (level < error_info.trace || (flags & ERROR_LIBRARY) && !(((error_info.set | error_info.flags) ^ error_info.clear) & ERROR_LIBRARY) || level < 0 && error_info.mask && !(error_info.mask & (1<<(-level - 1))))
	{
		if (level >= ERROR_FATAL)
			(*error_info.exit)(level - 1);
		return;
	}
	if (error_info.trace < 0)
		flags |= ERROR_LIBRARY|ERROR_SYSTEM;
	flags |= error_info.set | error_info.flags;
	flags &= ~error_info.clear;
	if (!library)
		flags &= ~ERROR_LIBRARY;
	fd = (flags & ERROR_OUTPUT) ? va_arg(ap, int) : error_info.fd;
	if (error_info.write)
	{
		long	off;
		char*	bas;

		bas = stkptr(stkstd, 0);
		if (off = stktell(stkstd))
			stkfreeze(stkstd, 0);
		file = error_info.id;
		if (error_state.prefix)
			sfprintf(stkstd, "%s: ", error_state.prefix);
		if (flags & ERROR_USAGE)
		{
			if (flags & ERROR_NOID)
				sfprintf(stkstd, "       ");
			else
				sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "Usage"));
			if (file || opt_info.argv && (file = opt_info.argv[0]))
				print(stkstd, file, " ");
		}
		else
		{
			if (level && !(flags & ERROR_NOID))
			{
				if (error_info.context && level > 0)
					context(stkstd, CONTEXT(error_info.flags, error_info.context));
				if (file)
					print(stkstd, file, (flags & ERROR_LIBRARY) ? " " : ": ");
				if (flags & (ERROR_CATALOG|ERROR_LIBRARY))
				{
					sfprintf(stkstd, "[");
					if (flags & ERROR_CATALOG)
						sfprintf(stkstd, "%s %s%s",
							catalog ? catalog : ERROR_translate(NiL, NiL, ast.id, "DEFAULT"),
							ERROR_translate(NiL, NiL, ast.id, "catalog"),
							(flags & ERROR_LIBRARY) ? ", " : "");
					if (flags & ERROR_LIBRARY)
						sfprintf(stkstd, "%s %s",
							library,
							ERROR_translate(NiL, NiL, ast.id, "library"));
					sfprintf(stkstd, "]: ");
				}
			}
			if (level > 0 && error_info.line > ((flags & ERROR_INTERACTIVE) != 0))
			{
				if (error_info.file && *error_info.file)
					sfprintf(stkstd, "\"%s\", ", error_info.file);
				sfprintf(stkstd, "%s %d: ", ERROR_translate(NiL, NiL, ast.id, "line"), error_info.line);
			}
		}
#if !_PACKAGE_astsa
		if (error_info.time)
		{
			if ((d = times(&us)) < error_info.time || error_info.time == 1)
				error_info.time = d;
			sfprintf(stkstd, " %05lu.%05lu.%05lu ", d - error_info.time, (unsigned long)us.tms_utime, (unsigned long)us.tms_stime);
		}
#endif
		switch (level)
		{
		case 0:
			flags &= ~ERROR_SYSTEM;
			break;
		case ERROR_WARNING:
			sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "warning"));
			break;
		case ERROR_PANIC:
			sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "panic"));
			break;
		default:
			if (level < 0)
			{
				s = ERROR_translate(NiL, NiL, ast.id, "debug");
				if (error_info.trace < -1)
					sfprintf(stkstd, "%s%d:%s", s, level, level > -10 ? " " : "");
				else
					sfprintf(stkstd, "%s: ", s);
				for (n = 0; n < error_info.indent; n++)
				{
					sfputc(stkstd, ' ');
					sfputc(stkstd, ' ');
				}
			}
			break;
		}
		if (flags & ERROR_SOURCE)
		{
			/*
			 * source ([version], file, line) message
			 */

			file = va_arg(ap, char*);
			line = va_arg(ap, int);
			s = ERROR_translate(NiL, NiL, ast.id, "line");
			if (error_info.version)
				sfprintf(stkstd, "(%s: \"%s\", %s %d) ", error_info.version, file, s, line);
			else
				sfprintf(stkstd, "(\"%s\", %s %d) ", file, s, line);
		}
Пример #7
0
Файл: enum.c Проект: att/ast
int b_enum(int argc, char **argv, Shbltin_t *context) {
    bool pflag = false, iflag = false;
    int i, n;
    ssize_t sz = -1;
    Namval_t *np, *tp, *mp;
    Namarr_t *ap;
    char *cp;
    const char *sp;
    struct Enum *ep;
    Shell_t *shp = context->shp;
    struct {
        Optdisc_t opt;
        Namval_t *np;
    } optdisc;

    if (cmdinit(argc, argv, context, ERROR_NOTIFY)) return -1;
    while ((n = optget(argv, enum_usage))) {
        switch (n) {
            case 'p': {
                pflag = true;
                break;
            }
            case 'i': {
                iflag = true;
                break;
            }
            case ':': {
                errormsg(SH_DICT, 2, "%s", opt_info.arg);
                break;
            }
            case '?': {
                errormsg(SH_DICT, ERROR_usage(2), "%s", opt_info.arg);
                __builtin_unreachable();
            }
            default: { break; }
        }
    }

    argv += opt_info.index;
    argc -= opt_info.index;
    if (error_info.errors || argc != 1) {
        error(ERROR_USAGE | 2, "%s", optusage(NULL));
        return 1;
    }

    while ((cp = *argv++)) {
        np = nv_open(cp, shp->var_tree, NV_VARNAME | NV_NOADD);
        if (!np || !(ap = nv_arrayptr(np)) || ap->fun || ap->nelem < 2) {
            error(ERROR_exit(1), "%s must name an array containing at least two elements", cp);
        }
        n = stktell(shp->stk);
        sfprintf(shp->stk, "%s.%s%c", NV_CLASS, np->nvname, 0);
        tp = nv_open(stkptr(shp->stk, n), shp->var_tree, NV_VARNAME);
        if (pflag) {
            sh_outenum(shp, sfstdout, tp);
            continue;
        }
        stkseek(shp->stk, n);
        n = ap->nelem;
        i = 0;
        nv_onattr(tp, NV_UINT16);
        nv_putval(tp, (char *)&i, NV_INTEGER);
        nv_putsub(np, NULL, 0L, ARRAY_SCAN);
        do {
            sz += strlen(nv_getval(np));
        } while (nv_nextsub(np));
        ep = calloc(1, sizeof(struct Enum));
        if (!ep) {
            error(ERROR_system(1), "out of space");
            __builtin_unreachable();
        }
        ep->nelem = n;
        mp = nv_namptr(ep->node, 0);
        mp->nvshell = shp;
        nv_setsize(mp, 10);
        nv_onattr(mp, NV_UINT16);
        ep->iflag = iflag;

        ep->values = malloc(n * sizeof(*ep->values));
        nv_putsub(np, NULL, 0L, ARRAY_SCAN);
        i = 0;
        do {
            sp = nv_getval(np);
            ep->values[i++] = strdup(sp);
        } while (nv_nextsub(np));
        assert(n == i);

        ep->namfun.dsize = sizeof(struct Enum);
        ep->namfun.disc = &ENUM_disc;
        ep->namfun.type = tp;
        nv_onattr(tp, NV_RDONLY);
        nv_disc(tp, &ep->namfun, DISC_OP_FIRST);
        memset(&optdisc, 0, sizeof(optdisc));
        optdisc.opt.infof = enuminfo;
        optdisc.np = tp;
        nv_addtype(tp, enum_type, &optdisc, sizeof(optdisc));
        nv_onattr(np, NV_LTOU | NV_UTOL);
    }
    nv_open(0, shp->var_tree, 0);
    return error_info.errors != 0;
}
Пример #8
0
int b_enum(int argc, char** argv, Shbltin_t *context)
#endif
{
	bool			pflag=false, iflag=false;
	int			i,n;
	ssize_t			sz = -1;
	Namval_t		*np, *tp, *mp;
	Namarr_t		*ap;
	char			*cp,*sp;
	struct Enum		*ep;
	Shell_t			*shp = context->shp;
	struct {
	    Optdisc_t	opt;
	    Namval_t	*np;
	}			optdisc;

	cmdinit(argc, argv, context, ERROR_CATALOG, ERROR_NOTIFY);
	for (;;)
	{
		switch (optget(argv, enum_usage))
		{
		case 'p':
			pflag = true;
			continue;
		case 'i':
			iflag = true;
			continue;
		case '?':
			error(ERROR_USAGE|4, "%s", opt_info.arg);
			break;
		case ':':
			error(2, "%s", opt_info.arg);
			break;
		}
		break;
	}
	argv += opt_info.index;
	if (error_info.errors)
	{
		error(ERROR_USAGE|2, "%s", optusage(NiL));
		return 1;
	}
	if(!*argv)
		sh_outenum(shp,sfstdout,(Namval_t*)0);
	while(cp = *argv++)
	{
		if(!(np = nv_open(cp, shp->var_tree, NV_VARNAME|NV_NOADD))  || !(ap=nv_arrayptr(np)) || ap->fun || (sz=ap->nelem) < 2)
			error(ERROR_exit(1), "%s must name an array  containing at least two elements",cp);
		n = stktell(shp->stk);
		sfprintf(shp->stk,"%s.%s%c",NV_CLASS,np->nvname,0);
		tp = nv_open(stkptr(shp->stk,n), shp->var_tree, NV_VARNAME);
		if(pflag)
		{
			sh_outenum(shp,sfstdout,tp);
			continue;
		}
		stkseek(shp->stk,n);
		n = sz;
		i = 0;
		nv_onattr(tp, NV_UINT16);
		nv_putval(tp, (char*)&i, NV_INTEGER);
		nv_putsub(np, (char*)0, 0L, ARRAY_SCAN);
		do
		{
			sz += strlen(nv_getval(np));
		}
		while(nv_nextsub(np));
		sz += n*sizeof(char*);
		if(!(ep = newof(0,struct Enum,1,sz)))
			error(ERROR_system(1), "out of space");
		mp = nv_namptr(ep->node,0);
		mp->nvshell = shp;
		nv_setsize(mp,10);
		nv_onattr(mp, NV_UINT16);
		ep->iflag = iflag;
		ep->nelem = n;
		cp = (char*)&ep->values[n+1];
		nv_putsub(np, (char*)0, 0L, ARRAY_SCAN);
		ep->values[n] = 0;
		i = 0;
		do
		{
			ep->values[i++] = cp;
			sp =  nv_getval(np);
			n = strlen(sp);
			memcpy(cp,sp,n+1);
			cp += n+1;
		}
		while(nv_nextsub(np));
		ep->hdr.dsize = sizeof(struct Enum)+sz;
		ep->hdr.disc = &ENUM_disc;
		ep->hdr.type = tp;
		nv_onattr(tp, NV_RDONLY);
		nv_disc(tp, &ep->hdr,NV_FIRST);
		memset(&optdisc,0,sizeof(optdisc));
		optdisc.opt.infof = enuminfo;
		optdisc.np = tp;
		nv_addtype(tp, enum_type, &optdisc.opt, sizeof(optdisc)); 
		nv_onattr(np,NV_LTOU|NV_UTOL);
	}
	nv_open(0,shp->var_tree,0);
	return error_info.errors != 0;
}
Пример #9
0
Файл: expand.c Проект: att/ast
int path_expand(Shell_t *shp, const char *pattern, struct argnod **arghead) {
    glob_t gdata;
    struct argnod *ap;
    glob_t *gp = &gdata;
    int flags, extra = 0;
#if SHOPT_BASH
    int off;
    char *sp, *cp, *cp2;
#endif

    sh_stats(STAT_GLOBS);
    memset(gp, 0, sizeof(gdata));
    flags = GLOB_GROUP | GLOB_AUGMENTED | GLOB_NOCHECK | GLOB_NOSORT | GLOB_STACK | GLOB_LIST |
            GLOB_DISC;
    if (sh_isoption(shp, SH_MARKDIRS)) flags |= GLOB_MARK;
    if (sh_isoption(shp, SH_GLOBSTARS)) flags |= GLOB_STARSTAR;
#if SHOPT_BASH
#if 0
        if(sh_isoption(shp,SH_BASH) && !sh_isoption(shp,SH_EXTGLOB))
                flags &= ~GLOB_AUGMENTED;
#endif
    if (sh_isoption(shp, SH_NULLGLOB)) flags &= ~GLOB_NOCHECK;
    if (sh_isoption(shp, SH_NOCASEGLOB)) flags |= GLOB_ICASE;
#endif
    if (sh_isstate(shp, SH_COMPLETE)) {
        extra += scantree(shp, shp->alias_tree, pattern, arghead);
        extra += scantree(shp, shp->fun_tree, pattern, arghead);
        gp->gl_nextdir = nextdir;
        flags |= GLOB_COMPLETE;
        flags &= ~GLOB_NOCHECK;
    }
#if SHOPT_BASH
    off = stktell(shp->stk);
    if (off) sp = stkfreeze(shp->stk, 0);
    if (sh_isoption(shp, SH_BASH)) {
        // For bash, FIGNORE is a colon separated list of suffixes to ignore
        // when doing filename/command completion. GLOBIGNORE is similar to ksh
        // FIGNORE, but colon separated instead of being an augmented shell
        // pattern. Generate shell patterns out of those here.
        if (sh_isstate(shp, SH_FCOMPLETE)) {
            cp = nv_getval(sh_scoped(shp, FIGNORENOD));
        } else {
            static Namval_t *GLOBIGNORENOD;
            if (!GLOBIGNORENOD) GLOBIGNORENOD = nv_open("GLOBIGNORE", shp->var_tree, 0);
            cp = nv_getval(sh_scoped(shp, GLOBIGNORENOD));
        }
        if (cp) {
            flags |= GLOB_AUGMENTED;
            sfputr(shp->stk, "@(", -1);
            if (!sh_isstate(shp, SH_FCOMPLETE)) {
                sfputr(shp->stk, cp, -1);
                for (cp = stkptr(shp->stk, off); *cp; cp++) {
                    if (*cp == ':') *cp = '|';
                }
            } else {
                cp2 = strtok(cp, ":");
                if (!cp2) cp2 = cp;
                do {
                    sfputc(shp->stk, '*');
                    sfputr(shp->stk, cp2, -1);
                    cp2 = strtok(NULL, ":");
                    if (cp2) {
                        *(cp2 - 1) = ':';
                        sfputc(shp->stk, '|');
                    }
                } while (cp2);
            }
            sfputc(shp->stk, ')');
            gp->gl_fignore = stkfreeze(shp->stk, 1);
        } else if (!sh_isstate(shp, SH_FCOMPLETE) && sh_isoption(shp, SH_DOTGLOB)) {
            gp->gl_fignore = "";
        }
    } else
#endif
        gp->gl_fignore = nv_getval(sh_scoped(shp, FIGNORENOD));
    if (suflen) gp->gl_suffix = sufstr;
    gp->gl_intr = &shp->trapnote;
    suflen = 0;
    if (strncmp(pattern, "~(N", 3) == 0) flags &= ~GLOB_NOCHECK;
    ast_glob(pattern, flags, 0, gp);
#if SHOPT_BASH
    if (off) {
        stkset(shp->stk, sp, off);
    } else {
        stkseek(shp->stk, 0);
    }
#endif
    sh_sigcheck(shp);
    for (ap = (struct argnod *)gp->gl_list; ap; ap = ap->argnxt.ap) {
        ap->argchn.ap = ap->argnxt.ap;
        if (!ap->argnxt.ap) ap->argchn.ap = *arghead;
    }
    if (gp->gl_list) *arghead = (struct argnod *)gp->gl_list;
    return gp->gl_pathc + extra;
}