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); } }
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; }
/* 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); } } }
/*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); }
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; }
/* * 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); }
/* * 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, '}'); } }
/* * 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); }
/* 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); }