📄 expand.c
字号:
/*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * 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. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. 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. */#ifndef lintstatic char sccsid[] = "@(#)expand.c 5.1 (Berkeley) 3/7/91";#endif /* not lint *//* * Routines to expand arguments to commands. We have to deal with * backquotes, shell variables, and file metacharacters. */#include "shell.h"#include "main.h"#include "nodes.h"#include "eval.h"#include "expand.h"#include "syntax.h"#include "parser.h"#include "jobs.h"#include "options.h"#include "var.h"#include "input.h"#include "output.h"#include "memalloc.h"#include "error.h"#include "mystring.h"#include <sys/types.h>#include <sys/stat.h>#include <errno.h>#include <dirent.h>#if USEGETPW#include <pwd.h>#endif/* * Structure specifying which parts of the string should be searched * for IFS characters. */struct ifsregion { struct ifsregion *next; /* next region in list */ int begoff; /* offset of start of region */ int endoff; /* offset of end of region */ int nulonly; /* search for nul bytes only */};char *expdest; /* output of current string */struct nodelist *argbackq; /* list of back quote expressions */struct ifsregion ifsfirst; /* first struct in list of ifs regions */struct ifsregion *ifslastp; /* last struct in list */struct arglist exparg; /* holds expanded arg list */#if UDIR || TILDE/* * Set if the last argument processed had /u/logname or ~logname expanded. * This variable is read by the cd command. */int didudir;#endif#ifdef __STDC__STATIC void argstr(char *, int);STATIC void expbackq(union node *, int, int);STATIC char *evalvar(char *, int);STATIC int varisset(int);STATIC void varvalue(int, int, int);STATIC void recordregion(int, int, int);STATIC void ifsbreakup(char *, struct arglist *);STATIC void expandmeta(struct strlist *);STATIC void expmeta(char *, char *);STATIC void addfname(char *);STATIC struct strlist *expsort(struct strlist *);STATIC struct strlist *msort(struct strlist *, int);STATIC int pmatch(char *, char *);#elseSTATIC void argstr();STATIC void expbackq();STATIC char *evalvar();STATIC int varisset();STATIC void varvalue();STATIC void recordregion();STATIC void ifsbreakup();STATIC void expandmeta();STATIC void expmeta();STATIC void addfname();STATIC struct strlist *expsort();STATIC struct strlist *msort();STATIC int pmatch();#endif#if UDIR || TILDE#ifdef __STDC__STATIC char *expudir(char *);#elseSTATIC char *expudir();#endif#endif /* UDIR || TILDE *//* * Expand shell variables and backquotes inside a here document. */voidexpandhere(arg, fd) union node *arg; /* the document */ int fd; /* where to write the expanded version */ { herefd = fd; expandarg(arg, (struct arglist *)NULL, 0); xwrite(fd, stackblock(), expdest - stackblock());}/* * Perform variable substitution and command substitution on an argument, * placing the resulting list of arguments in arglist. If full is true, * perform splitting and file name expansion. When arglist is NULL, perform * here document expansion. */voidexpandarg(arg, arglist, full) union node *arg; struct arglist *arglist; { struct strlist *sp; char *p;#if UDIR || TILDE didudir = 0;#endif argbackq = arg->narg.backquote; STARTSTACKSTR(expdest); ifsfirst.next = NULL; ifslastp = NULL; argstr(arg->narg.text, full); if (arglist == NULL) return; /* here document expanded */ STPUTC('\0', expdest); p = grabstackstr(expdest); exparg.lastp = &exparg.list; if (full) { ifsbreakup(p, &exparg); *exparg.lastp = NULL; exparg.lastp = &exparg.list; expandmeta(exparg.list); } else { sp = (struct strlist *)stalloc(sizeof (struct strlist)); sp->text = p; *exparg.lastp = sp; exparg.lastp = &sp->next; } while (ifsfirst.next != NULL) { struct ifsregion *ifsp; INTOFF; ifsp = ifsfirst.next->next; ckfree(ifsfirst.next); ifsfirst.next = ifsp; INTON; } *exparg.lastp = NULL; if (exparg.list) { *arglist->lastp = exparg.list; arglist->lastp = exparg.lastp; }}/* * Perform variable and command substitution. If full is set, output CTLESC * characters to allow for further processing. If full is not set, treat * $@ like $* since no splitting will be performed. */STATIC voidargstr(p, full) register char *p; { char c; for (;;) { switch (c = *p++) { case '\0': case CTLENDVAR: goto breakloop; case CTLESC: if (full) STPUTC(c, expdest); c = *p++; STPUTC(c, expdest); break; case CTLVAR: p = evalvar(p, full); break; case CTLBACKQ: case CTLBACKQ|CTLQUOTE: expbackq(argbackq->n, c & CTLQUOTE, full); argbackq = argbackq->next; break; default: STPUTC(c, expdest); } }breakloop:;}/* * Expand stuff in backwards quotes. */STATIC voidexpbackq(cmd, quoted, full) union node *cmd; { struct backcmd in; int i; char buf[128]; char *p; char *dest = expdest; struct ifsregion saveifs, *savelastp; struct nodelist *saveargbackq; char lastc; int startloc = dest - stackblock(); char const *syntax = quoted? DQSYNTAX : BASESYNTAX; int saveherefd; INTOFF; saveifs = ifsfirst; savelastp = ifslastp; saveargbackq = argbackq; saveherefd = herefd; herefd = -1; p = grabstackstr(dest); evalbackcmd(cmd, &in); ungrabstackstr(p, dest); ifsfirst = saveifs; ifslastp = savelastp; argbackq = saveargbackq; herefd = saveherefd; p = in.buf; lastc = '\0'; for (;;) { if (--in.nleft < 0) { if (in.fd < 0) break; while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); TRACE(("expbackq: read returns %d\n", i)); if (i <= 0) break; p = buf; in.nleft = i - 1; } lastc = *p++; if (lastc != '\0') { if (full && syntax[lastc] == CCTL) STPUTC(CTLESC, dest); STPUTC(lastc, dest); } } if (lastc == '\n') { STUNPUTC(dest); } if (in.fd >= 0) close(in.fd); if (in.buf) ckfree(in.buf); if (in.jp) exitstatus = waitforjob(in.jp); if (quoted == 0) recordregion(startloc, dest - stackblock(), 0); TRACE(("evalbackq: size=%d: \"%.*s\"\n", (dest - stackblock()) - startloc, (dest - stackblock()) - startloc, stackblock() + startloc)); expdest = dest; INTON;}/* * Expand a variable, and return a pointer to the next character in the * input string. */STATIC char *evalvar(p, full) char *p; { int subtype; int flags; char *var; char *val; int c; int set; int special; int startloc; flags = *p++; subtype = flags & VSTYPE; var = p; special = 0; if (! is_name(*p)) special = 1; p = strchr(p, '=') + 1;again: /* jump here after setting a variable with ${var=text} */ if (special) { set = varisset(*var); val = NULL; } else { val = lookupvar(var); if (val == NULL || (flags & VSNUL) && val[0] == '\0') { val = NULL; set = 0; } else set = 1; } startloc = expdest - stackblock(); if (set && subtype != VSPLUS) { /* insert the value of the variable */ if (special) { varvalue(*var, flags & VSQUOTE, full); } else { char const *syntax = (flags & VSQUOTE)? DQSYNTAX : BASESYNTAX; while (*val) { if (full && syntax[*val] == CCTL) STPUTC(CTLESC, expdest); STPUTC(*val++, expdest); } } } if (subtype == VSPLUS) set = ! set; if (((flags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)) && (set || subtype == VSNORMAL)) recordregion(startloc, expdest - stackblock(), flags & VSQUOTE); if (! set && subtype != VSNORMAL) { if (subtype == VSPLUS || subtype == VSMINUS) { argstr(p, full); } else { char *startp; int saveherefd = herefd; herefd = -1; argstr(p, 0); STACKSTRNUL(expdest); herefd = saveherefd; startp = stackblock() + startloc; if (subtype == VSASSIGN) { setvar(var, startp, 0); STADJUST(startp - expdest, expdest); flags &=~ VSNUL; goto again; } /* subtype == VSQUESTION */ if (*p != CTLENDVAR) { outfmt(&errout, "%s\n", startp); error((char *)NULL); } error("%.*s: parameter %snot set", p - var - 1, var, (flags & VSNUL)? "null or " : nullstr); } } if (subtype != VSNORMAL) { /* skip to end of alternative */ int nesting = 1; for (;;) { if ((c = *p++) == CTLESC) p++; else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { if (set) { if (argbackq != NULL) argbackq = argbackq->next; } } else if (c == CTLVAR) { if ((*p++ & VSTYPE) != VSNORMAL) nesting++; } else if (c == CTLENDVAR) { if (--nesting == 0) break; } } } return p;}/* * Test whether a specialized variable is set. */STATIC intvarisset(name) char name; { char **ap; if (name == '!') { if (backgndpid == -1) return 0; } else if (name == '@' || name == '*') { if (*shellparam.p == NULL) return 0; } else if ((unsigned)(name -= '1') <= '9' - '1') { ap = shellparam.p; do { if (*ap++ == NULL) return 0; } while (--name >= 0); } return 1;}/* * Add the value of a specialized variable to the stack string. */STATIC voidvarvalue(name, quoted, allow_split) char name; { int num; char temp[32]; char *p; int i; extern int exitstatus; char sep; char **ap; char const *syntax; switch (name) { case '$': num = rootpid; goto numvar; case '?': num = exitstatus; goto numvar; case '#': num = shellparam.nparam; goto numvar; case '!': num = backgndpid;numvar: p = temp + 31; temp[31] = '\0'; do { *--p = num % 10 + '0'; } while ((num /= 10) != 0); while (*p) STPUTC(*p++, expdest); break; case '-': for (i = 0 ; optchar[i] ; i++) { if (optval[i]) STPUTC(optchar[i], expdest); } break; case '@': if (allow_split) { sep = '\0'; goto allargs; } /* fall through */ case '*': sep = ' ';allargs: /* Only emit CTLESC if we will do further processing, i.e. if allow_split is set. */ syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX; for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { /* should insert CTLESC characters */ while (*p) { if (syntax[*p] == CCTL) STPUTC(CTLESC, expdest); STPUTC(*p++, expdest); } if (*ap) STPUTC(sep, expdest); } break; case '0': p = arg0;string: /* Only emit CTLESC if we will do further processing, i.e. if allow_split is set. */ syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX; while (*p) { if (syntax[*p] == CCTL) STPUTC(CTLESC, expdest); STPUTC(*p++, expdest); } break; default: if ((unsigned)(name -= '1') <= '9' - '1') { p = shellparam.p[name]; goto string; } break; }}/* * Record the the fact that we have to scan this region of the * string for IFS characters. */STATIC voidrecordregion(start, end, nulonly) { register struct ifsregion *ifsp; if (ifslastp == NULL) { ifsp = &ifsfirst; } else { ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); ifslastp->next = ifsp; } ifslastp = ifsp; ifslastp->next = NULL; ifslastp->begoff = start; ifslastp->endoff = end; ifslastp->nulonly = nulonly;}/* * Break the argument string into pieces based upon IFS and add the * strings to the argument list. The regions of the string to be * searched for IFS characters have been stored by recordregion. */STATIC voidifsbreakup(string, arglist) char *string; struct arglist *arglist; { struct ifsregion *ifsp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -