📄 expand.c
字号:
/* $NetBSD: expand.c,v 1.68.2.2 2005/04/07 11:37:39 tron Exp $ *//*- * Copyright (c) 1991, 1993 * 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. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#include <sys/cdefs.h>#ifndef lint#if 0static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95";#else__RCSID("$NetBSD: expand.c,v 1.68.2.2 2005/04/07 11:37:39 tron Exp $");#endif#endif /* not lint */#include <sys/types.h>#include <sys/time.h>#include <sys/stat.h>#include <errno.h>#include <dirent.h>#include <unistd.h>#include <stdlib.h>#include <stdio.h>/* * 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 "show.h"/* * 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 inquotes; /* 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 */STATIC void argstr(char *, int);STATIC char *exptilde(char *, int);STATIC void expbackq(union node *, int, int);STATIC int subevalvar(char *, char *, int, int, int, int);STATIC char *evalvar(char *, int);STATIC int varisset(char *, int);STATIC void varvalue(char *, int, int, int);STATIC void recordregion(int, int, int);STATIC void removerecordregions(int); STATIC void ifsbreakup(char *, struct arglist *);STATIC void ifsfree(void);STATIC void expandmeta(struct strlist *, int);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 *, int);STATIC char *cvtnum(int, char *);/* * Expand shell variables and backquotes inside a here document. */voidexpandhere(union node *arg, int fd){ 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 EXP_FULL is true, * perform splitting and file name expansion. When arglist is NULL, perform * here document expansion. */voidexpandarg(union node *arg, struct arglist *arglist, int flag){ struct strlist *sp; char *p; argbackq = arg->narg.backquote; STARTSTACKSTR(expdest); ifsfirst.next = NULL; ifslastp = NULL; argstr(arg->narg.text, flag); if (arglist == NULL) { return; /* here document expanded */ } STPUTC('\0', expdest); p = grabstackstr(expdest); exparg.lastp = &exparg.list; /* * TODO - EXP_REDIR */ if (flag & EXP_FULL) { ifsbreakup(p, &exparg); *exparg.lastp = NULL; exparg.lastp = &exparg.list; expandmeta(exparg.list, flag); } else { if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ rmescapes(p); sp = (struct strlist *)stalloc(sizeof (struct strlist)); sp->text = p; *exparg.lastp = sp; exparg.lastp = &sp->next; } ifsfree(); *exparg.lastp = NULL; if (exparg.list) { *arglist->lastp = exparg.list; arglist->lastp = exparg.lastp; }}/* * Perform variable and command substitution. * If EXP_FULL is set, output CTLESC characters to allow for further processing. * Otherwise treat $@ like $* since no splitting will be performed. */STATIC voidargstr(char *p, int flag){ char c; int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ int firsteq = 1; const char *ifs = 0; int ifs_split = EXP_IFS_SPLIT; if (flag & EXP_IFS_SPLIT) ifs = ifsset() ? ifsval() : " \t\n"; if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) p = exptilde(p, flag); for (;;) { switch (c = *p++) { case '\0': case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */ return; case CTLQUOTEMARK: /* "$@" syntax adherence hack */ if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') break; if ((flag & EXP_FULL) != 0) STPUTC(c, expdest); ifs_split = 0; break; case CTLQUOTEEND: ifs_split = EXP_IFS_SPLIT; break; case CTLESC: if (quotes) STPUTC(c, expdest); c = *p++; STPUTC(c, expdest); break; case CTLVAR: p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split)); break; case CTLBACKQ: case CTLBACKQ|CTLQUOTE: expbackq(argbackq->n, c & CTLQUOTE, flag); argbackq = argbackq->next; break; case CTLENDARI: expari(flag); break; case ':': case '=': /* * sort of a hack - expand tildes in variable * assignments (after the first '=' and after ':'s). */ STPUTC(c, expdest); if (flag & EXP_VARTILDE && *p == '~') { if (c == '=') { if (firsteq) firsteq = 0; else break; } p = exptilde(p, flag); } break; default: STPUTC(c, expdest); if (flag & EXP_IFS_SPLIT & ifs_split && strchr(ifs, c) != NULL) { /* We need to get the output split here... */ recordregion(expdest - stackblock() - 1, expdest - stackblock(), 0); } break; } }}STATIC char *exptilde(char *p, int flag){ char c, *startp = p; const char *home; int quotes = flag & (EXP_FULL | EXP_CASE); while ((c = *p) != '\0') { switch(c) { case CTLESC: return (startp); case CTLQUOTEMARK: return (startp); case ':': if (flag & EXP_VARTILDE) goto done; break; case '/': goto done; } p++; }done: *p = '\0'; if (*(startp+1) == '\0') { if ((home = lookupvar("HOME")) == NULL) goto lose; } else goto lose; if (*home == '\0') goto lose; *p = c; while ((c = *home++) != '\0') { if (quotes && SQSYNTAX[(int)c] == CCTL) STPUTC(CTLESC, expdest); STPUTC(c, expdest); } return (p);lose: *p = c; return (startp);}STATIC void removerecordregions(int endoff){ if (ifslastp == NULL) return; if (ifsfirst.endoff > endoff) { while (ifsfirst.next != NULL) { struct ifsregion *ifsp; INTOFF; ifsp = ifsfirst.next->next; ckfree(ifsfirst.next); ifsfirst.next = ifsp; INTON; } if (ifsfirst.begoff > endoff) ifslastp = NULL; else { ifslastp = &ifsfirst; ifsfirst.endoff = endoff; } return; } ifslastp = &ifsfirst; while (ifslastp->next && ifslastp->next->begoff < endoff) ifslastp=ifslastp->next; while (ifslastp->next != NULL) { struct ifsregion *ifsp; INTOFF; ifsp = ifslastp->next->next; ckfree(ifslastp->next); ifslastp->next = ifsp; INTON; } if (ifslastp->endoff > endoff) ifslastp->endoff = endoff;}/* * Expand arithmetic expression. Backup to start of expression, * evaluate, place result in (backed up) result, adjust string position. */voidexpari(int flag){ char *p, *start; int result; int begoff; int quotes = flag & (EXP_FULL | EXP_CASE); int quoted; /* ifsfree(); */ /* * This routine is slightly over-complicated for * efficiency. First we make sure there is * enough space for the result, which may be bigger * than the expression if we add exponentation. Next we * scan backwards looking for the start of arithmetic. If the * next previous character is a CTLESC character, then we * have to rescan starting from the beginning since CTLESC * characters have to be processed left to right. */#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10#error "integers with more than 10 digits are not supported"#endif CHECKSTRSPACE(12 - 2, expdest); USTPUTC('\0', expdest); start = stackblock(); p = expdest - 1; while (*p != CTLARI && p >= start) --p; if (*p != CTLARI) error("missing CTLARI (shouldn't happen)"); if (p > start && *(p-1) == CTLESC) for (p = start; *p != CTLARI; p++) if (*p == CTLESC) p++; if (p[1] == '"') quoted=1; else quoted=0; begoff = p - start; removerecordregions(begoff); if (quotes) rmescapes(p+2); result = arith(p+2); fmtstr(p, 12, "%d", result); while (*p++) ; if (quoted == 0) recordregion(begoff, p - 1 - start, 0); result = expdest - p + 1; STADJUST(-result, expdest);}/* * Expand stuff in backwards quotes. */STATIC voidexpbackq(union node *cmd, int quoted, int flag){ 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; int quotes = flag & (EXP_FULL | EXP_CASE); 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 (quotes && syntax[(int)lastc] == CCTL) STPUTC(CTLESC, dest); STPUTC(lastc, dest); } } /* Eat all trailing newlines */ p = stackblock() + startloc; while (dest > p && dest[-1] == '\n') STUNPUTC(dest); if (in.fd >= 0) close(in.fd); if (in.buf) ckfree(in.buf); if (in.jp) back_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;}STATIC intsubevalvar(char *p, char *str, int strloc, int subtype, int startloc, int varflags){ char *startp; char *loc = NULL; char *q; int c = 0; int saveherefd = herefd; struct nodelist *saveargbackq = argbackq; int amount; herefd = -1; argstr(p, 0); STACKSTRNUL(expdest); herefd = saveherefd; argbackq = saveargbackq; startp = stackblock() + startloc; if (str == NULL) str = stackblock() + strloc; switch (subtype) { case VSASSIGN: setvar(str, startp, 0); amount = startp - expdest; STADJUST(amount, expdest); varflags &= ~VSNUL; if (c != 0) *loc = c; return 1; case VSQUESTION: if (*p != CTLENDVAR) { outfmt(&errout, "%s\n", startp); error((char *)NULL); } error("%.*s: parameter %snot set", p - str - 1, str, (varflags & VSNUL) ? "null or " : nullstr); /* NOTREACHED */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -