Beispiel #1
0
static void
fixDolMod(void)
{
    eChar c;

    c = DgetC(0);
    if (c == ':') {
	do {
	    c = DgetC(0), dolmcnt = 1, dol_flag_a = 0;
	    if (c == 'g' || c == 'a') {
		if (c == 'g')
		    dolmcnt = INT_MAX;
		else
		    dol_flag_a = 1;
		c = DgetC(0);
	    }
	    if ((c == 'g' && dolmcnt != INT_MAX) || 
		(c == 'a' && dol_flag_a == 0)) {
		if (c == 'g')
		    dolmcnt = INT_MAX;
		else
		    dol_flag_a = 1;
		c = DgetC(0);
	    }

	    if (c == 's') {	/* [eichin:19910926.0755EST] */
		int delimcnt = 2;
		eChar delim = DgetC(0);
		Strbuf_append1(&dolmod, (Char) c);
		Strbuf_append1(&dolmod, (Char) delim);

		if (delim == DEOF || !delim || letter(delim)
		    || Isdigit(delim) || any(" \t\n", delim)) {
		    seterror(ERR_BADSUBST);
		    break;
		}	
		while ((c = DgetC(0)) != DEOF) {
		    Strbuf_append1(&dolmod, (Char) c);
		    if(c == delim) delimcnt--;
		    if(!delimcnt) break;
		}
		if(delimcnt) {
		    seterror(ERR_BADSUBST);
		    break;
		}
		continue;
	    }
	    if (!any("luhtrqxes", c))
		stderror(ERR_BADMOD, (int)c);
	    Strbuf_append1(&dolmod, (Char) c);
	    if (c == 'q')
		dolmcnt = INT_MAX;
	}
	while ((c = DgetC(0)) == ':');
	unDredc(c);
    }
    else
	unDredc(c);
}
Beispiel #2
0
const Char *
quote_meta(struct Strbuf *buf, const Char *s)
{
    buf->len = 0;
    while (*s != '\0') {
	if (cmap(*s, _META | _DOL | _QF | _QB | _ESC | _GLOB))
	    Strbuf_append1(buf, '\\');
	Strbuf_append1(buf, *s++);
    }
    Strbuf_terminate(buf);
    return buf->s;
}
Beispiel #3
0
/*
 * Pack up more characters in this word
 */
static int
Dpack(struct Strbuf *wbuf)
{
    eChar c;

    for (;;) {
	c = DgetC(DODOL);
	if (c == '\\') {
	    c = DgetC(0);
	    if (c == DEOF) {
		unDredc(c);
		return 1;
	    }
	    if (c == '\n')
		c = ' ';
	    else
		c |= QUOTE;
	}
	if (c == DEOF) {
	    unDredc(c);
	    return 1;
	}
	if (cmap(c, _SP | _NL | _QF | _QB)) {	/* sp \t\n'"` */
	    unDgetC(c);
	    if (cmap(c, QUOTES))
		return 0;
	    return 1;
	}
	Strbuf_append1(wbuf, (Char) c);
    }
}
Beispiel #4
0
/* PrintXKey():
 *	Print the binding associated with Xkey key.
 *	Print entire Xmap if null
 */
void
PrintXkey(const CStr *key)
{
    struct Strbuf buf = Strbuf_INIT;
    CStr cs;

    if (key) {
	cs.buf = key->buf;
	cs.len = key->len;
    }
    else {
	cs.buf = STRNULL;
	cs.len = 0;
    }
    /* do nothing if Xmap is empty and null key specified */
    if (Xmap == NULL && cs.len == 0)
	return;

    Strbuf_append1(&buf, '"');
    cleanup_push(&buf, Strbuf_cleanup);
    if (Lookup(&buf, &cs, Xmap) <= -1)
	/* key is not bound */
	xprintf(CGETS(9, 4, "Unbound extended key \"%S\"\n"), cs.buf);
    cleanup_until(&buf);
}
Beispiel #5
0
static void
Enumerate(struct Strbuf *buf, const XmapNode *ptr)
{
    size_t old_len;

    if (ptr == NULL) {
#ifdef DEBUG_EDIT
	xprintf(CGETS(9, 6, "Enumerate: BUG!! Null ptr passed\n!"));
#endif
	return;
    }

    old_len = buf->len;
    unparsech(buf, ptr->ch); /* put this char at end of string */
    if (ptr->next == NULL) {
	/* print this Xkey and function */
	Strbuf_append1(buf, '"');
	Strbuf_terminate(buf);
	printOne(buf->s, &ptr->val, ptr->type);
    }
    else
	Enumerate(buf, ptr->next);

    /* go to sibling if there is one */
    if (ptr->sibling) {
	buf->len = old_len;
	Enumerate(buf, ptr->sibling);
    }
}
Beispiel #6
0
Char   *
expand_lex(const struct wordent *sp0, int from, int to)
{
    struct Strbuf buf = Strbuf_INIT;
    const struct wordent *sp;
    Char *s;
    Char prev_c;
    int i;

    prev_c = '\0';

    if (!sp0 || (sp = sp0->next) == sp0 || sp == (sp0 = sp0->prev))
	return Strbuf_finish(&buf); /* null lex */

    for (i = 0; ; i++) {
	if ((i >= from) && (i <= to)) {	/* if in range */
	    for (s = sp->word; *s; s++) {
		/*
		 * bugfix by Michael Bloom: anything but the current history
		 * character {(PWP) and backslash} seem to be dealt with
		 * elsewhere.
		 */
		if ((*s & QUOTE)
		    && (((*s & TRIM) == HIST && HIST != '\0') ||
			(((*s & TRIM) == '\'') && (prev_c != '\\')) ||
			(((*s & TRIM) == '\"') && (prev_c != '\\')) ||
			(((*s & TRIM) == '\\') && (prev_c != '\\')))) {
		    Strbuf_append1(&buf, '\\');
		}
		Strbuf_append1(&buf, *s & TRIM);
		prev_c = *s;
	    }
	    Strbuf_append1(&buf, ' ');
	}
	sp = sp->next;
	if (sp == sp0)
	    break;
    }
    if (buf.len != 0)
	buf.len--;		/* get rid of trailing space */

    return Strbuf_finish(&buf);
}
Beispiel #7
0
Char *
blkexpand(Char *const *av)
{
    struct Strbuf buf = Strbuf_INIT;

    for (; *av; av++) {
	Strbuf_append(&buf, *av);
	if (av[1])
	    Strbuf_append1(&buf, ' ');
    }
    return Strbuf_finish(&buf);
}
Beispiel #8
0
static void
exportpath(Char **val)
{
    struct Strbuf buf = Strbuf_INIT;
    Char    	*exppath;

    if (val)
	while (*val) {
	    Strbuf_append(&buf, *val++);
	    if (*val == 0 || eq(*val, STRRparen))
		break;
	    Strbuf_append1(&buf, PATHSEP);
	}
    exppath = Strbuf_finish(&buf);
    cleanup_push(exppath, xfree);
    tsetenv(STRKPATH, exppath);
    cleanup_until(exppath);
}
Beispiel #9
0
/* Lookup():
 *	look for the string starting at node ptr.
 *	Print if last node
 */
static int
Lookup(struct Strbuf *buf, const CStr *str, const XmapNode *ptr)
{
    if (ptr == NULL)
	return (-1);		/* cannot have null ptr */

    if (str->len == 0) {
	/* no more chars in string.  Enumerate from here. */
	Enumerate(buf, ptr);
	return (0);
    }
    else {
	/* If match put this char into buf.  Recurse */
	if (ptr->ch == *(str->buf)) {
	    /* match found */
	    unparsech(buf, ptr->ch);
	    if (ptr->next != NULL) {
		/* not yet at leaf */
		CStr tstr;
		tstr.buf = str->buf + 1;
		tstr.len = str->len - 1;
		return (Lookup(buf, &tstr, ptr->next));
	    }
	    else {
		/* next node is null so key should be complete */
		if (str->len == 1) {
		    Strbuf_append1(buf, '"');
		    Strbuf_terminate(buf);
		    printOne(buf->s, &ptr->val, ptr->type);
		    return (0);
		}
		else
		    return (-1);/* mismatch -- string still has chars */
	    }
	}
	else {
	    /* no match found try sibling */
	    if (ptr->sibling)
		return (Lookup(buf, str, ptr->sibling));
	    else
		return (-1);
	}
    }
}
Beispiel #10
0
/* tw_dollar():
 *	Expand $<n> args in buffer
 */
static const Char *
tw_dollar(const Char *str, Char **wl, size_t nwl, Char **result, Char sep,
	  const char *msg)
{
    struct Strbuf buf = Strbuf_INIT;
    Char *res;
    const Char *sp;

    for (sp = str; *sp && *sp != sep;)
	if (sp[0] == '$' && sp[1] == ':' && Isdigit(sp[sp[2] == '-' ? 3 : 2])) {
	    int num, neg = 0;
	    sp += 2;
	    if (*sp == '-') {
		neg = 1;
		sp++;
	    }
	    for (num = *sp++ - '0'; Isdigit(*sp); num += 10 * num + *sp++ - '0')
		continue;
	    if (neg)
		num = nwl - num - 1;
	    if (num >= 0 && (size_t)num < nwl)
		Strbuf_append(&buf, wl[num]);
	}
	else
	    Strbuf_append1(&buf, *sp++);

    res = Strbuf_finish(&buf);

    if (*sp++ == sep) {
	*result = res;
	return sp;
    }

    xfree(res);
    /* Truncates data if WIDE_STRINGS */
    stderror(ERR_COMPMIS, (int)sep, msg, short2str(str));
    return --sp;
} /* end tw_dollar */
Beispiel #11
0
/* dnormalize():
 *	The path will be normalized if it
 *	1) is "..",
 *	2) or starts with "../",
 *	3) or ends with "/..",
 *	4) or contains the string "/../",
 *	then it will be normalized, unless those strings are quoted. 
 *	Otherwise, a copy is made and sent back.
 */
Char   *
dnormalize(const Char *cp, int expnd)
{

/* return true if dp is of the form "../xxx" or "/../xxx" */
#define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
#define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))

#ifdef S_IFLNK
    if (expnd) {
	struct Strbuf buf = Strbuf_INIT;
 	int     dotdot = 0;
	Char   *dp, *cwd;
	const Char *start = cp;
# ifdef HAVE_SLASHSLASH
	int slashslash;
# endif /* HAVE_SLASHSLASH */

	/*
	 * count the number of "../xxx" or "xxx/../xxx" in the path
	 */
	for ( ; *cp && *(cp + 1); cp++)
	    if (IS_DOTDOT(start, cp))
	        dotdot++;

	/*
	 * if none, we are done.
	 */
        if (dotdot == 0)
	    return (Strsave(start));
	
# ifdef notdef
	struct stat sb;
	/*
	 * We disable this test because:
	 * cd /tmp; mkdir dir1 dir2; cd dir2; ln -s /tmp/dir1; cd dir1;
	 * echo ../../dir1 does not expand. We had enabled this before
	 * because it was bothering people with expansions in compilation
	 * lines like -I../../foo. Maybe we need some kind of finer grain
	 * control?
	 *
	 * If the path doesn't exist, we are done too.
	 */
	if (lstat(short2str(start), &sb) != 0 && errno == ENOENT)
	    return (Strsave(start));
# endif

	cwd = xmalloc((Strlen(dcwd->di_name) + 3) * sizeof(Char));
	(void) Strcpy(cwd, dcwd->di_name);

	/*
	 * If the path starts with a slash, we are not relative to
	 * the current working directory.
	 */
	if (ABSOLUTEP(start))
	    *cwd = '\0';
# ifdef HAVE_SLASHSLASH
	slashslash = cwd[0] == '/' && cwd[1] == '/';
# endif /* HAVE_SLASHSLASH */

	/*
	 * Ignore . and count ..'s
	 */
	cp = start;
	do {
	    dotdot = 0;
	    buf.len = 0;
	    while (*cp) 
	        if (IS_DOT(start, cp)) {
	            if (*++cp)
	                cp++;
	        }
	        else if (IS_DOTDOT(start, cp)) {
		    if (buf.len != 0)
		        break; /* finish analyzing .././../xxx/[..] */
		    dotdot++;
		    cp += 2;
		    if (*cp)
		        cp++;
	        }
	        else 
		    Strbuf_append1(&buf, *cp++);

	    Strbuf_terminate(&buf);
	    while (dotdot > 0) 
	        if ((dp = Strrchr(cwd, '/')) != NULL) {
# ifdef HAVE_SLASHSLASH
		    if (dp == &cwd[1]) 
		        slashslash = 1;
# endif /* HAVE_SLASHSLASH */
		        *dp = '\0';
		        dotdot--;
	        }
	        else
		    break;

	    if (!*cwd) {	/* too many ..'s, starts with "/" */
	        cwd[0] = '/';
# ifdef HAVE_SLASHSLASH
		/*
		 * Only append another slash, if already the former cwd
		 * was in a double-slash path.
		 */
		cwd[1] = slashslash ? '/' : '\0';
		cwd[2] = '\0';
# else /* !HAVE_SLASHSLASH */
		cwd[1] = '\0';
# endif /* HAVE_SLASHSLASH */
	    }
# ifdef HAVE_SLASHSLASH
	    else if (slashslash && cwd[1] == '\0') {
		cwd[1] = '/';
		cwd[2] = '\0';
	    }
# endif /* HAVE_SLASHSLASH */

	    if (buf.len != 0) {
		size_t i;

		i = Strlen(cwd);
		if (TRM(cwd[i - 1]) != '/') {
		    cwd[i++] = '/';
		    cwd[i] = '\0';
		}
	        dp = Strspl(cwd, TRM(buf.s[0]) == '/' ? &buf.s[1] : buf.s);
	        xfree(cwd);
	        cwd = dp;
		i = Strlen(cwd) - 1;
	        if (TRM(cwd[i]) == '/')
		    cwd[i] = '\0';
	    }
	    /* Reduction of ".." following the stuff we collected in buf
	     * only makes sense if the directory item in buf really exists.
	     * Avoid reduction of "-I../.." (typical compiler call) to ""
	     * or "/usr/nonexistant/../bin" to "/usr/bin":
	     */
	    if (cwd[0]) {
	        struct stat exists;
		if (0 != stat(short2str(cwd), &exists)) {
		    xfree(buf.s);
		    xfree(cwd);
		    return Strsave(start);
		}
	    }
	} while (*cp != '\0');
	xfree(buf.s);
	return cwd;
    }
#endif /* S_IFLNK */
    return Strsave(cp);
}
Beispiel #12
0
/*
 * Get a word.  This routine is analogous to the routine
 * word() in sh.lex.c for the main lexical input.  One difference
 * here is that we don't get a newline to terminate our expansion.
 * Rather, DgetC will return a DEOF when we hit the end-of-input.
 */
static int
Dword(struct blk_buf *bb)
{
    eChar c, c1;
    struct Strbuf *wbuf = Strbuf_alloc();
    int dolflg;
    int    sofar = 0;
    Char *str;

    cleanup_push(wbuf, Strbuf_free);
    for (;;) {
	c = DgetC(DODOL);
	switch (c) {

	case DEOF:
	    if (sofar == 0) {
		cleanup_until(wbuf);
		return (0);
	    }
	    /* finish this word and catch the code above the next time */
	    unDredc(c);
	    /*FALLTHROUGH*/

	case '\n':
	    goto end;

	case ' ':
	case '\t':
	    continue;

	case '`':
	    /* We preserve ` quotations which are done yet later */
	    Strbuf_append1(wbuf, (Char) c);
	    /*FALLTHROUGH*/
	case '\'':
	case '"':
	    /*
	     * Note that DgetC never returns a QUOTES character from an
	     * expansion, so only true input quotes will get us here or out.
	     */
	    c1 = c;
	    dolflg = c1 == '"' ? DODOL : 0;
	    for (;;) {
		c = DgetC(dolflg);
		if (c == c1)
		    break;
		if (c == '\n' || c == DEOF) {
		    cleanup_until(bb);
		    stderror(ERR_UNMATCHED, (int)c1);
		}
		if ((c & (QUOTE | TRIM)) == ('\n' | QUOTE)) {
		    if (wbuf->len != 0 && (wbuf->s[wbuf->len - 1] & TRIM) == '\\')
			wbuf->len--;
		}
		switch (c1) {

		case '"':
		    /*
		     * Leave any `s alone for later. Other chars are all
		     * quoted, thus `...` can tell it was within "...".
		     */
		    Strbuf_append1(wbuf, c == '`' ? '`' : c | QUOTE);
		    break;

		case '\'':
		    /* Prevent all further interpretation */
		    Strbuf_append1(wbuf, c | QUOTE);
		    break;

		case '`':
		    /* Leave all text alone for later */
		    Strbuf_append1(wbuf, (Char) c);
		    break;

		default:
		    break;
		}
	    }
	    if (c1 == '`')
		Strbuf_append1(wbuf, '`');
	    sofar = 1;
	    if (Dpack(wbuf) != 0)
		goto end;
	    continue;

	case '\\':
	    c = DgetC(0);	/* No $ subst! */
	    if (c == '\n' || c == DEOF)
		continue;
	    c |= QUOTE;
	    break;

	default:
	    break;
	}
	unDgetC(c);
	sofar = 1;
	if (Dpack(wbuf) != 0)
	    goto end;
    }

 end:
    cleanup_ignore(wbuf);
    cleanup_until(wbuf);
    str = Strbuf_finish(wbuf);
    bb_append(bb, str);
    xfree(wbuf);
    return 1;
}
Beispiel #13
0
/*ARGSUSED*/
void
dolist(Char **v, struct command *c)
{
    Char **globbed;
    int     i, k, ret = 0;
    struct stat st;

    USE(c);
    if (*++v == NULL) {
	struct Strbuf word = Strbuf_INIT;

	Strbuf_terminate(&word);
	cleanup_push(&word, Strbuf_cleanup);
	(void) t_search(&word, LIST, TW_ZERO, 0, STRNULL, 0);
	cleanup_until(&word);
	return;
    }
    v = glob_all_or_error(v);
    globbed = v;
    cleanup_push(globbed, blk_cleanup);
    for (k = 0; v[k] != NULL && v[k][0] != '-'; k++)
	continue;
    if (v[k]) {
	/*
	 * We cannot process a flag therefore we let ls do it right.
	 */
	Char *lspath;
	struct command *t;
	struct wordent cmd, *nextword, *lastword;
	Char   *cp;
	struct varent *vp;

	if (setintr) {
	    pintr_disabled++;
	    cleanup_push(&pintr_disabled, disabled_cleanup);
	}
	if (seterr) {
	    xfree(seterr);
	    seterr = NULL;
	}

	lspath = STRls;
	STRmCF[1] = 'C';
	STRmCF[3] = '\0';
	/* Look at listflags, to add -A to the flags, to get a path
	   of ls if necessary */
	if ((vp = adrof(STRlistflags)) != NULL && vp->vec != NULL &&
	    vp->vec[0] != STRNULL) {
	    if (vp->vec[1] != NULL && vp->vec[1][0] != '\0')
		lspath = vp->vec[1];
	    for (cp = vp->vec[0]; *cp; cp++)
		switch (*cp) {
		case 'x':
		    STRmCF[1] = 'x';
		    break;
		case 'a':
		    STRmCF[3] = 'a';
		    break;
		case 'A':
		    STRmCF[3] = 'A';
		    break;
		default:
		    break;
		}
	}

	cmd.word = STRNULL;
	lastword = &cmd;
	nextword = xcalloc(1, sizeof cmd);
	nextword->word = Strsave(lspath);
	lastword->next = nextword;
	nextword->prev = lastword;
	lastword = nextword;
	nextword = xcalloc(1, sizeof cmd);
	nextword->word = Strsave(STRmCF);
	lastword->next = nextword;
	nextword->prev = lastword;
#if defined(KANJI) && defined(SHORT_STRINGS) && defined(DSPMBYTE)
	if (dspmbyte_ls) {
	    lastword = nextword;
	    nextword = xcalloc(1, sizeof cmd);
	    nextword->word = Strsave(STRmmliteral);
	    lastword->next = nextword;
	    nextword->prev = lastword;
	}
#endif
#ifdef COLOR_LS_F
	if (color_context_ls) {
	    lastword = nextword;
	    nextword = xcalloc(1, sizeof cmd);
	    nextword->word = Strsave(STRmmcolormauto);
	    lastword->next = nextword;
	    nextword->prev = lastword;
	}
#endif /* COLOR_LS_F */
	lastword = nextword;
	for (cp = *v; cp; cp = *++v) {
	    nextword = xcalloc(1, sizeof cmd);
	    nextword->word = quote(Strsave(cp));
	    lastword->next = nextword;
	    nextword->prev = lastword;
	    lastword = nextword;
	}
	lastword->next = &cmd;
	cmd.prev = lastword;
	cleanup_push(&cmd, lex_cleanup);

	/* build a syntax tree for the command. */
	t = syntax(cmd.next, &cmd, 0);
	cleanup_push(t, syntax_cleanup);
	if (seterr)
	    stderror(ERR_OLD);
	/* expand aliases like process() does */
	/* alias(&cmd); */
	/* execute the parse tree. */
	execute(t, tpgrp > 0 ? tpgrp : -1, NULL, NULL, FALSE);
	/* done. free the lex list and parse tree. */
	cleanup_until(&cmd);
	if (setintr)
	    cleanup_until(&pintr_disabled);
    }
    else {
	Char   *dp, *tmp;
	struct Strbuf buf = Strbuf_INIT;

	cleanup_push(&buf, Strbuf_cleanup);
	for (k = 0, i = 0; v[k] != NULL; k++) {
	    tmp = dnormalize(v[k], symlinks == SYM_IGNORE);
	    cleanup_push(tmp, xfree);
	    dp = Strend(tmp) - 1;
	    if (*dp == '/' && dp != tmp)
#ifdef apollo
		if (dp != &tmp[1])
#endif /* apollo */
		*dp = '\0';
	    if (stat(short2str(tmp), &st) == -1) {
		int err;

		err = errno;
		if (k != i) {
		    if (i != 0)
			xputchar('\n');
		    print_by_column(STRNULL, &v[i], k - i, FALSE);
		}
		haderr = 1;
		xprintf("%S: %s.\n", tmp, strerror(err));
		haderr = 0;
		i = k + 1;
		ret = 1;
	    }
	    else if (S_ISDIR(st.st_mode)) {
		Char   *cp;

		if (k != i) {
		    if (i != 0)
			xputchar('\n');
		    print_by_column(STRNULL, &v[i], k - i, FALSE);
		}
		if (k != 0 && v[1] != NULL)
		    xputchar('\n');
		xprintf("%S:\n", tmp);
		buf.len = 0;
		for (cp = tmp; *cp; cp++)
		    Strbuf_append1(&buf, (*cp | QUOTE));
		Strbuf_terminate(&buf);
		dp = &buf.s[buf.len - 1];
		if (
#ifdef WINNT_NATIVE
		    (*dp != (Char) (':' | QUOTE)) &&
#endif /* WINNT_NATIVE */
		    (*dp != (Char) ('/' | QUOTE))) {
		    Strbuf_append1(&buf, '/');
		    Strbuf_terminate(&buf);
		} else 
		    *dp &= TRIM;
		(void) t_search(&buf, LIST, TW_ZERO, 0, STRNULL, 0);
		i = k + 1;
	    }
	    cleanup_until(tmp);
	}
	cleanup_until(&buf);
	if (k != i) {
	    if (i != 0)
		xputchar('\n');
	    print_by_column(STRNULL, &v[i], k - i, FALSE);
	}
	if (ret)
	    stderror(ERR_SILENT);
    }

    cleanup_until(globbed);
}
Beispiel #14
0
/*
 * Handle the multitudinous $ expansion forms.
 * Ugh.
 */
static void
Dgetdol(void)
{
    Char *np;
    struct varent *vp = NULL;
    struct Strbuf *name = Strbuf_alloc();
    eChar   c, sc;
    int     subscr = 0, lwb = 1, upb = 0;
    int    dimen = 0, bitset = 0, length = 0;
    static Char *dolbang = NULL;

    cleanup_push(name, Strbuf_free);
    dolmod.len = dolmcnt = dol_flag_a = 0;
    c = sc = DgetC(0);
    if (c == DEOF) {
      stderror(ERR_SYNTAX);
      return;
    }
    if (c == '{')
	c = DgetC(0);		/* sc is { to take } later */
    if ((c & TRIM) == '#')
	dimen++, c = DgetC(0);	/* $# takes dimension */
    else if (c == '?')
	bitset++, c = DgetC(0);	/* $? tests existence */
    else if (c == '%')
	length++, c = DgetC(0); /* $% returns length in chars */
    switch (c) {

    case '!':
	if (dimen || bitset || length)
	    stderror(ERR_SYNTAX);
	if (backpid != 0) {
	    xfree(dolbang);
	    setDolp(dolbang = putn((tcsh_number_t)backpid));
	}
	cleanup_until(name);
	goto eatbrac;

    case '$':
	if (dimen || bitset || length)
	    stderror(ERR_SYNTAX);
	setDolp(doldol);
	cleanup_until(name);
	goto eatbrac;

    case '<'|QUOTE: {
	static struct Strbuf wbuf; /* = Strbuf_INIT; */

	if (bitset)
	    stderror(ERR_NOTALLOWED, "$?<");
	if (dimen)
	    stderror(ERR_NOTALLOWED, "$#<");
	if (length)
	    stderror(ERR_NOTALLOWED, "$%<");
	wbuf.len = 0;
	{
	    char cbuf[MB_LEN_MAX];
	    size_t cbp = 0;
	    int old_pintr_disabled;

	    for (;;) {
	        int len;
		ssize_t res;
		Char wc;

		pintr_push_enable(&old_pintr_disabled);
		res = force_read(OLDSTD, cbuf + cbp, 1);
		cleanup_until(&old_pintr_disabled);
		if (res != 1)
		    break;
		cbp++;
		len = normal_mbtowc(&wc, cbuf, cbp);
		if (len == -1) {
		    reset_mbtowc();
		    if (cbp < MB_LEN_MAX)
		        continue; /* Maybe a partial character */
		    wc = (unsigned char)*cbuf | INVALID_BYTE;
		}
		if (len <= 0)
		    len = 1;
		if (cbp != (size_t)len)
		    memmove(cbuf, cbuf + len, cbp - len);
		cbp -= len;
		if (wc == '\n')
		    break;
		Strbuf_append1(&wbuf, wc);
	    }
	    while (cbp != 0) {
		int len;
		Char wc;

		len = normal_mbtowc(&wc, cbuf, cbp);
		if (len == -1) {
		    reset_mbtowc();
		    wc = (unsigned char)*cbuf | INVALID_BYTE;
		}
		if (len <= 0)
		    len = 1;
		if (cbp != (size_t)len)
		    memmove(cbuf, cbuf + len, cbp - len);
		cbp -= len;
		if (wc == '\n')
		    break;
		Strbuf_append1(&wbuf, wc);
	    }
	    Strbuf_terminate(&wbuf);
	}

	fixDolMod();
	setDolp(wbuf.s); /* Kept allocated until next $< expansion */
	cleanup_until(name);
	goto eatbrac;
    }

    case '*':
	Strbuf_append(name, STRargv);
	Strbuf_terminate(name);
	vp = adrof(STRargv);
	subscr = -1;		/* Prevent eating [...] */
	break;

    case DEOF:
    case '\n':
	np = dimen ? STRargv : (bitset ? STRstatus : NULL);
	if (np) {
	    bitset = 0;
	    Strbuf_append(name, np);
	    Strbuf_terminate(name);
	    vp = adrof(np);
	    subscr = -1;		/* Prevent eating [...] */
	    unDredc(c);
	    break;
	}
	else
	    stderror(ERR_SYNTAX);
	/*NOTREACHED*/

    default:
	if (Isdigit(c)) {
	    if (dimen)
		stderror(ERR_NOTALLOWED, "$#<num>");
	    subscr = 0;
	    do {
		subscr = subscr * 10 + c - '0';
		c = DgetC(0);
	    } while (c != DEOF && Isdigit(c));
	    unDredc(c);
	    if (subscr < 0)
		stderror(ERR_RANGE);
	    if (subscr == 0) {
		if (bitset) {
		    dolp = dolzero ? STR1 : STR0;
		    cleanup_until(name);
		    goto eatbrac;
		}
		if (ffile == 0)
		    stderror(ERR_DOLZERO);
		if (length) {
		    length = Strlen(ffile);
		    addla(putn((tcsh_number_t)length));
		}
		else {
		    fixDolMod();
		    setDolp(ffile);
		}
		cleanup_until(name);
		goto eatbrac;
	    }
#if 0
	    if (bitset)
		stderror(ERR_NOTALLOWED, "$?<num>");
	    if (length)
		stderror(ERR_NOTALLOWED, "$%<num>");
#endif
	    vp = adrof(STRargv);
	    if (vp == 0) {
		vp = &nulargv;
		cleanup_until(name);
		goto eatmod;
	    }
	    break;
	}
	if (c == DEOF || !alnum(c)) {
	    np = dimen ? STRargv : (bitset ? STRstatus : NULL);
	    if (np) {
		bitset = 0;
		Strbuf_append(name, np);
		Strbuf_terminate(name);
		vp = adrof(np);
		subscr = -1;		/* Prevent eating [...] */
		unDredc(c);
		break;
	    }
	    else
		stderror(ERR_VARALNUM);
	}
	for (;;) {
	    Strbuf_append1(name, (Char) c);
	    c = DgetC(0);
	    if (c == DEOF || !alnum(c))
		break;
	}
	Strbuf_terminate(name);
	unDredc(c);
	vp = adrof(name->s);
    }
    if (bitset) {
	dolp = (vp || getenv(short2str(name->s))) ? STR1 : STR0;
	cleanup_until(name);
	goto eatbrac;
    }
    if (vp == NULL || vp->vec == NULL) {
	np = str2short(getenv(short2str(name->s)));
	if (np) {
	    static Char *env_val; /* = NULL; */

	    cleanup_until(name);
	    fixDolMod();
	    if (length) {
		    addla(putn((tcsh_number_t)Strlen(np)));
	    } else {
		    xfree(env_val);
		    env_val = Strsave(np);
		    setDolp(env_val);
	    }
	    goto eatbrac;
	}
	udvar(name->s);
	/* NOTREACHED */
    }
    cleanup_until(name);
    c = DgetC(0);
    upb = blklen(vp->vec);
    if (dimen == 0 && subscr == 0 && c == '[') {
	name = Strbuf_alloc();
	cleanup_push(name, Strbuf_free);
	np = name->s;
	for (;;) {
	    c = DgetC(DODOL);	/* Allow $ expand within [ ] */
	    if (c == ']')
		break;
	    if (c == '\n' || c == DEOF)
		stderror(ERR_INCBR);
	    Strbuf_append1(name, (Char) c);
	}
	Strbuf_terminate(name);
	np = name->s;
	if (dolp || dolcnt)	/* $ exp must end before ] */
	    stderror(ERR_EXPORD);
	if (!*np)
	    stderror(ERR_SYNTAX);
	if (Isdigit(*np)) {
	    int     i;

	    for (i = 0; Isdigit(*np); i = i * 10 + *np++ - '0')
		continue;
	    if (i < 0 || (i > upb && !any("-*", *np))) {
		cleanup_until(name);
		dolerror(vp->v_name);
		return;
	    }
	    lwb = i;
	    if (!*np)
		upb = lwb, np = STRstar;
	}
	if (*np == '*')
	    np++;
	else if (*np != '-')
	    stderror(ERR_MISSING, '-');
	else {
	    int i = upb;

	    np++;
	    if (Isdigit(*np)) {
		i = 0;
		while (Isdigit(*np))
		    i = i * 10 + *np++ - '0';
		if (i < 0 || i > upb) {
		    cleanup_until(name);
		    dolerror(vp->v_name);
		    return;
		}
	    }
	    if (i < lwb)
		upb = lwb - 1;
	    else
		upb = i;
	}
	if (lwb == 0) {
	    if (upb != 0) {
		cleanup_until(name);
		dolerror(vp->v_name);
		return;
	    }
	    upb = -1;
	}
	if (*np)
	    stderror(ERR_SYNTAX);
	cleanup_until(name);
    }
    else {
	if (subscr > 0) {
	    if (subscr > upb)
		lwb = 1, upb = 0;
	    else
		lwb = upb = subscr;
	}
	unDredc(c);
    }
    if (dimen) {
	/* this is a kludge. It prevents Dgetdol() from */
	/* pushing erroneous ${#<error> values into the labuf. */
	if (sc == '{') {
	    c = Dredc();
	    if (c != '}')
		stderror(ERR_MISSING, '}');
	    unDredc(c);
	}
	addla(putn((tcsh_number_t)(upb - lwb + 1)));
    }
    else if (length) {
	int i;

	for (i = lwb - 1, length = 0; i < upb; i++)
	    length += Strlen(vp->vec[i]);
#ifdef notdef
	/* We don't want that, since we can always compute it by adding $#xxx */
	length += i - 1;	/* Add the number of spaces in */
#endif
	addla(putn((tcsh_number_t)length));
    }
    else {
eatmod:
	fixDolMod();
	dolnxt = &vp->vec[lwb - 1];
	dolcnt = upb - lwb + 1;
    }
eatbrac:
    if (sc == '{') {
	c = Dredc();
	if (c != '}')
	    stderror(ERR_MISSING, '}');
    }
}
Beispiel #15
0
/*
 * Form a shell temporary file (in unit 0) from the words
 * of the shell input up to EOF or a line the same as "term".
 * Unit 0 should have been closed before this call.
 */
void
heredoc(Char *term)
{
    eChar  c;
    Char   *Dv[2];
    struct Strbuf lbuf = Strbuf_INIT, mbuf = Strbuf_INIT;
    Char    obuf[BUFSIZE + 1];
#define OBUF_END (obuf + sizeof(obuf) / sizeof (*obuf) - 1)
    Char *lbp, *obp, *mbp;
    Char  **vp;
    int    quoted;
#ifdef HAVE_MKSTEMP
    char   *tmp = short2str(shtemp);
    char   *dot = strrchr(tmp, '.');

    if (!dot)
	stderror(ERR_NAME | ERR_NOMATCH);
    strcpy(dot, TMP_TEMPLATE);

    xclose(0);
    if (mkstemp(tmp) == -1)
	stderror(ERR_SYSTEM, tmp, strerror(errno));
#else /* !HAVE_MKSTEMP */
    char   *tmp;
# ifndef WINNT_NATIVE

again:
# endif /* WINNT_NATIVE */
    tmp = short2str(shtemp);
# if O_CREAT == 0
    if (xcreat(tmp, 0600) < 0)
	stderror(ERR_SYSTEM, tmp, strerror(errno));
# endif
    xclose(0);
    if (xopen(tmp, O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY|O_LARGEFILE, 0600) ==
	-1) {
	int oerrno = errno;
# ifndef WINNT_NATIVE
	if (errno == EEXIST) {
	    if (unlink(tmp) == -1) {
		xfree(shtemp);
		mbp = randsuf();
		shtemp = Strspl(STRtmpsh, mbp);
		xfree(mbp);
	    }
	    goto again;
	}
# endif /* WINNT_NATIVE */
	(void) unlink(tmp);
	errno = oerrno;
 	stderror(ERR_SYSTEM, tmp, strerror(errno));
    }
#endif /* HAVE_MKSTEMP */
    (void) unlink(tmp);		/* 0 0 inode! */
    Dv[0] = term;
    Dv[1] = NULL;
    gflag = 0;
    trim(Dv);
    rscan(Dv, Dtestq);
    quoted = gflag;
    obp = obuf;
    obuf[BUFSIZE] = 0;
    inheredoc = 1;
    cleanup_push(&inheredoc, inheredoc_cleanup);
#ifdef WINNT_NATIVE
    __dup_stdin = 1;
#endif /* WINNT_NATIVE */
    cleanup_push(&lbuf, Strbuf_cleanup);
    cleanup_push(&mbuf, Strbuf_cleanup);
    for (;;) {
	Char **words;

	/*
	 * Read up a line
	 */
	lbuf.len = 0;
	for (;;) {
	    c = readc(1);	/* 1 -> Want EOF returns */
	    if (c == CHAR_ERR || c == '\n')
		break;
	    if ((c &= TRIM) != 0)
		Strbuf_append1(&lbuf, (Char) c);
	}
	Strbuf_terminate(&lbuf);

	/* Catch EOF in the middle of a line. */
	if (c == CHAR_ERR && lbuf.len != 0)
	    c = '\n';

	/*
	 * Check for EOF or compare to terminator -- before expansion
	 */
	if (c == CHAR_ERR || eq(lbuf.s, term))
	    break;

	/*
	 * If term was quoted or -n just pass it on
	 */
	if (quoted || noexec) {
	    Strbuf_append1(&lbuf, '\n');
	    Strbuf_terminate(&lbuf);
	    for (lbp = lbuf.s; (c = *lbp++) != 0;) {
		*obp++ = (Char) c;
		if (obp == OBUF_END) {
		    tmp = short2str(obuf);
		    (void) xwrite(0, tmp, strlen (tmp));
		    obp = obuf;
		}
	    }
	    continue;
	}

	/*
	 * Term wasn't quoted so variable and then command expand the input
	 * line
	 */
	Dcp = lbuf.s;
	Dvp = Dv + 1;
	mbuf.len = 0;
	for (;;) {
	    c = DgetC(DODOL);
	    if (c == DEOF)
		break;
	    if ((c &= TRIM) == 0)
		continue;
	    /* \ quotes \ $ ` here */
	    if (c == '\\') {
		c = DgetC(0);
		if (!any("$\\`", c))
		    unDgetC(c | QUOTE), c = '\\';
		else
		    c |= QUOTE;
	    }
	    Strbuf_append1(&mbuf, (Char) c);
	}
	Strbuf_terminate(&mbuf);

	/*
	 * If any ` in line do command substitution
	 */
	mbp = mbuf.s;
	if (Strchr(mbp, '`') != NULL) {
	    /*
	     * 1 arg to dobackp causes substitution to be literal. Words are
	     * broken only at newlines so that all blanks and tabs are
	     * preserved.  Blank lines (null words) are not discarded.
	     */
	    words = dobackp(mbp, 1);
	}
	else
	    /* Setup trivial vector similar to return of dobackp */
	    Dv[0] = mbp, Dv[1] = NULL, words = Dv;

	/*
	 * Resurrect the words from the command substitution each separated by
	 * a newline.  Note that the last newline of a command substitution
	 * will have been discarded, but we put a newline after the last word
	 * because this represents the newline after the last input line!
	 */
	for (vp= words; *vp; vp++) {
	    for (mbp = *vp; *mbp; mbp++) {
		*obp++ = *mbp & TRIM;
		if (obp == OBUF_END) {
		    tmp = short2str(obuf);
		    (void) xwrite(0, tmp, strlen (tmp));
		    obp = obuf;
		}
	    }
	    *obp++ = '\n';
	    if (obp == OBUF_END) {
	        tmp = short2str(obuf);
		(void) xwrite(0, tmp, strlen (tmp));
		obp = obuf;
	    }
	}
	if (words != Dv)
	    blkfree(words);
    }
    *obp = 0;
    tmp = short2str(obuf);
    (void) xwrite(0, tmp, strlen (tmp));
    (void) lseek(0, (off_t) 0, L_SET);
    cleanup_until(&inheredoc);
}
Beispiel #16
0
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];
    char    tibuf[BUFSIZE];

    hadnl = 0;
    icnt = 0;
    quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0;
    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 (;;) {
	    (void) setexit();
	    justpr = 0;
	    
	    if (haderr) {
		/* unwind */
		doneinp = 0;
		cleanup_pop_mark(omark);
		resexit(osetexit);
		reset();
	    }
	    if (seterr) {
		xfree(seterr);
		seterr = NULL;
	    }

	    (void) lex(&paraml);
	    cleanup_push(&paraml, lex_cleanup);
	    if (seterr)
		stderror(ERR_OLD);
	    alias(&paraml);
	    t = syntax(paraml.next, &paraml, 0);
	    if (t == NULL)
		return;
	    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. */
	    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(&paraml);
	}
    }
    cleanup_until(&pvec[1]);
    c = 0;
    ip = NULL;
    do {
	ssize_t     cnt = 0;
	char   *tmp;

	tmp = tibuf;
	for (;;) {
	    while (icnt == 0) {
		int     i, eof;

		ip = ibuf;
		icnt = xread(pvec[0], tmp, tibuf + BUFSIZE - tmp);
		eof = 0;
		if (icnt <= 0) {
		    if (tmp == tibuf)
			goto eof;
		    icnt = 0;
		    eof = 1;
		}
		icnt += tmp - tibuf;
		i = 0;
		tmp = tibuf;
		while (tmp < tibuf + icnt) {
		    int len;

		    len = normal_mbtowc(&ip[i], tmp, tibuf + icnt - tmp);
		    if (len == -1) {
		        reset_mbtowc();
		        if (!eof && (size_t)(tibuf + icnt - tmp) < MB_CUR_MAX) {
			    break; /* Maybe a partial character */
			}
			ip[i] = (unsigned char) *tmp | INVALID_BYTE; /* Error */
		    }
		    if (len <= 0)
		        len = 1;
		    i++;
		    tmp += len;
		}
		if (tmp != tibuf)
		    memmove (tibuf, tmp, tibuf + icnt - tmp);
		tmp = tibuf + (tibuf + icnt - tmp);
		icnt = i;
	    }
	    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++;
	    Strbuf_append1(word, c | quoted);
	}
	/*
	 * 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(); */
}
Beispiel #17
0
static void
unparsech(struct Strbuf *buf, Char ch)
{
    if (ch == 0) {
	Strbuf_append1(buf, '^');
	Strbuf_append1(buf, '@');
    }
    else if (Iscntrl(ch)) {
	Strbuf_append1(buf, '^');
	if (ch == CTL_ESC('\177'))
	    Strbuf_append1(buf, '?');
	else
#ifdef IS_ASCII
	    Strbuf_append1(buf, ch | 0100);
#else
	    Strbuf_append1(buf, _toebcdic[_toascii[ch]|0100]);
#endif
    }
    else if (ch == '^') {
	Strbuf_append1(buf, '\\');
	Strbuf_append1(buf, '^');
    } else if (ch == '\\') {
	Strbuf_append1(buf, '\\');
	Strbuf_append1(buf, '\\');
    } else if (ch == ' ' || (Isprint(ch) && !Isspace(ch))) {
	Strbuf_append1(buf, ch);
    }
    else {
	Strbuf_append1(buf, '\\');
	Strbuf_append1(buf, ((ch >> 6) & 7) + '0');
	Strbuf_append1(buf, ((ch >> 3) & 7) + '0');
	Strbuf_append1(buf, (ch & 7) + '0');
    }
}
Beispiel #18
0
/*
 * dfollow - change to arg directory; fall back on cdpath if not valid
 */
static Char *
dfollow(Char *cp, int old)
{
    Char *dp;
    struct varent *c;
    int serrno;

    cp = old ? Strsave(cp) : globone(cp, G_ERROR);
    cleanup_push(cp, xfree);
#ifdef apollo
    if (Strchr(cp, '`')) {
	char *dptr;
	if (chdir(dptr = short2str(cp)) < 0) 
	    stderror(ERR_SYSTEM, dptr, strerror(errno));
	dp = agetcwd();
	cleanup_push(dp, xfree);
	if (dp != NULL) {
	    cleanup_until(cp);
	    return dgoto(dp);
	}
	else
	    stderror(ERR_SYSTEM, dptr, strerror(errno));
    }
#endif /* apollo */

    /*
     * if we are ignoring symlinks, try to fix relatives now.
     * if we are expading symlinks, it should be done by now.
     */ 
    dp = dnormalize(cp, symlinks == SYM_IGNORE);
    if (chdir(short2str(dp)) >= 0) {
        cleanup_until(cp);
        return dgoto(dp);
    }
    else {
        xfree(dp);
        if (chdir(short2str(cp)) >= 0) {
	    cleanup_ignore(cp);
	    cleanup_until(cp);
	    return dgoto(cp);
	}
	else if (errno != ENOENT && errno != ENOTDIR) {
	    int err;

	    err = errno;
	    stderror(ERR_SYSTEM, short2str(cp), strerror(err));
	}
	serrno = errno;
    }

    if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
	&& (c = adrof(STRcdpath)) && c->vec != NULL) {
	struct Strbuf buf = Strbuf_INIT;
	Char  **cdp;

	for (cdp = c->vec; *cdp; cdp++) {
	    size_t len = Strlen(*cdp);
	    buf.len = 0;
	    if (len > 0) {
		Strbuf_append(&buf, *cdp);
		if ((*cdp)[len - 1] != '/')
		    Strbuf_append1(&buf, '/');
	    }
	    Strbuf_append(&buf, cp);
	    Strbuf_terminate(&buf);
	    /*
	     * We always want to fix the directory here
	     * If we are normalizing symlinks
	     */
	    dp = dnormalize(buf.s, symlinks == SYM_IGNORE || 
				   symlinks == SYM_EXPAND);
	    if (chdir(short2str(dp)) >= 0) {
		printd = 1;
		xfree(buf.s);
		cleanup_until(cp);
		return dgoto(dp);
	    }
	    else if (chdir(short2str(cp)) >= 0) {
		printd = 1;
		xfree(dp);
		xfree(buf.s);
		cleanup_ignore(cp);
		cleanup_until(cp);
		return dgoto(cp);
	    }
	}
	xfree(buf.s);
    }
    dp = varval(cp);
    if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
	cleanup_until(cp);
	cp = Strsave(dp);
	printd = 1;
	return dgoto(cp);
    }
    /*
     * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
     * directories we could get to.
     */
    if (!bequiet)
	stderror(ERR_SYSTEM, short2str(cp), strerror(serrno));
    cleanup_until(cp);
    return (NULL);
}