expand.c revision 223060
190792Sgshapiro/*- 290792Sgshapiro * Copyright (c) 1991, 1993 390792Sgshapiro * The Regents of the University of California. All rights reserved. 490792Sgshapiro * Copyright (c) 1997-2005 590792Sgshapiro * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. 690792Sgshapiro * 790792Sgshapiro * This code is derived from software contributed to Berkeley by 890792Sgshapiro * Kenneth Almquist. 990792Sgshapiro * 1090792Sgshapiro * Redistribution and use in source and binary forms, with or without 1194334Sgshapiro * modification, are permitted provided that the following conditions 1290792Sgshapiro * are met: 1390792Sgshapiro * 1. Redistributions of source code must retain the above copyright 1490792Sgshapiro * notice, this list of conditions and the following disclaimer. 1590792Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright 1690792Sgshapiro * notice, this list of conditions and the following disclaimer in the 1790792Sgshapiro * documentation and/or other materials provided with the distribution. 1890792Sgshapiro * 4. Neither the name of the University nor the names of its contributors 1990792Sgshapiro * may be used to endorse or promote products derived from this software 2090792Sgshapiro * without specific prior written permission. 2190792Sgshapiro * 2290792Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2390792Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2490792Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2590792Sgshapiro * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2690792Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2794334Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2894334Sgshapiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2994334Sgshapiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3090792Sgshapiro * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3190792Sgshapiro * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3290792Sgshapiro * SUCH DAMAGE. 3390792Sgshapiro */ 3490792Sgshapiro 3590792Sgshapiro#ifndef lint 3690792Sgshapiro#if 0 3790792Sgshapirostatic char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; 3890792Sgshapiro#endif 3990792Sgshapiro#endif /* not lint */ 4090792Sgshapiro#include <sys/cdefs.h> 4190792Sgshapiro__FBSDID("$FreeBSD: head/bin/sh/expand.c 223060 2011-06-13 21:03:27Z jilles $"); 4290792Sgshapiro 4390792Sgshapiro#include <sys/types.h> 4490792Sgshapiro#include <sys/time.h> 4590792Sgshapiro#include <sys/stat.h> 4690792Sgshapiro#include <dirent.h> 4790792Sgshapiro#include <errno.h> 4890792Sgshapiro#include <inttypes.h> 4990792Sgshapiro#include <limits.h> 5090792Sgshapiro#include <pwd.h> 5190792Sgshapiro#include <stdio.h> 5290792Sgshapiro#include <stdlib.h> 5390792Sgshapiro#include <string.h> 5490792Sgshapiro#include <unistd.h> 5594334Sgshapiro#include <wchar.h> 5690792Sgshapiro 5794334Sgshapiro/* 5894334Sgshapiro * Routines to expand arguments to commands. We have to deal with 5994334Sgshapiro * backquotes, shell variables, and file metacharacters. 6094334Sgshapiro */ 6194334Sgshapiro 6294334Sgshapiro#include "shell.h" 6390792Sgshapiro#include "main.h" 6490792Sgshapiro#include "nodes.h" 6590792Sgshapiro#include "eval.h" 6690792Sgshapiro#include "expand.h" 6790792Sgshapiro#include "syntax.h" 6890792Sgshapiro#include "parser.h" 6990792Sgshapiro#include "jobs.h" 7090792Sgshapiro#include "options.h" 7190792Sgshapiro#include "var.h" 7290792Sgshapiro#include "input.h" 7390792Sgshapiro#include "output.h" 7490792Sgshapiro#include "memalloc.h" 7590792Sgshapiro#include "error.h" 7690792Sgshapiro#include "mystring.h" 7790792Sgshapiro#include "arith.h" 7890792Sgshapiro#include "show.h" 7990792Sgshapiro#include "builtins.h" 8090792Sgshapiro 8190792Sgshapiro/* 8290792Sgshapiro * Structure specifying which parts of the string should be searched 8390792Sgshapiro * for IFS characters. 8494334Sgshapiro */ 8594334Sgshapiro 8690792Sgshapirostruct ifsregion { 8790792Sgshapiro struct ifsregion *next; /* next region in list */ 8890792Sgshapiro int begoff; /* offset of start of region */ 8990792Sgshapiro int endoff; /* offset of end of region */ 9090792Sgshapiro int inquotes; /* search for nul bytes only */ 9190792Sgshapiro}; 9290792Sgshapiro 9390792Sgshapiro 9490792Sgshapirostatic char *expdest; /* output of current string */ 9590792Sgshapirostatic struct nodelist *argbackq; /* list of back quote expressions */ 9690792Sgshapirostatic struct ifsregion ifsfirst; /* first struct in list of ifs regions */ 9790792Sgshapirostatic struct ifsregion *ifslastp; /* last struct in list */ 9890792Sgshapirostatic struct arglist exparg; /* holds expanded arg list */ 9990792Sgshapiro 10090792Sgshapirostatic void argstr(char *, int); 10190792Sgshapirostatic char *exptilde(char *, int); 10290792Sgshapirostatic void expbackq(union node *, int, int); 10390792Sgshapirostatic int subevalvar(char *, char *, int, int, int, int, int); 10490792Sgshapirostatic char *evalvar(char *, int); 10590792Sgshapirostatic int varisset(char *, int); 10690792Sgshapirostatic void varvalue(char *, int, int, int); 10790792Sgshapirostatic void recordregion(int, int, int); 10890792Sgshapirostatic void removerecordregions(int); 10990792Sgshapirostatic void ifsbreakup(char *, struct arglist *); 11090792Sgshapirostatic void expandmeta(struct strlist *, int); 11190792Sgshapirostatic void expmeta(char *, char *); 11290792Sgshapirostatic void addfname(char *); 11390792Sgshapirostatic struct strlist *expsort(struct strlist *); 11490792Sgshapirostatic struct strlist *msort(struct strlist *, int); 11590792Sgshapirostatic char *cvtnum(int, char *); 11690792Sgshapirostatic int collate_range_cmp(wchar_t, wchar_t); 11790792Sgshapiro 11890792Sgshapirostatic int 11990792Sgshapirocollate_range_cmp(wchar_t c1, wchar_t c2) 12090792Sgshapiro{ 12190792Sgshapiro static wchar_t s1[2], s2[2]; 12290792Sgshapiro 12390792Sgshapiro s1[0] = c1; 12490792Sgshapiro s2[0] = c2; 12590792Sgshapiro return (wcscoll(s1, s2)); 12690792Sgshapiro} 12790792Sgshapiro 12890792Sgshapiro/* 12990792Sgshapiro * Expand shell variables and backquotes inside a here document. 13090792Sgshapiro * union node *arg the document 13190792Sgshapiro * int fd; where to write the expanded version 13290792Sgshapiro */ 13390792Sgshapiro 13490792Sgshapirovoid 13590792Sgshapiroexpandhere(union node *arg, int fd) 13690792Sgshapiro{ 13790792Sgshapiro expandarg(arg, (struct arglist *)NULL, 0); 13890792Sgshapiro xwrite(fd, stackblock(), expdest - stackblock()); 13990792Sgshapiro} 14090792Sgshapiro 14190792Sgshapirostatic char * 14290792Sgshapirostputs_quotes(const char *data, const char *syntax, char *p) 14390792Sgshapiro{ 14494334Sgshapiro while (*data) { 14590792Sgshapiro CHECKSTRSPACE(2, p); 14690792Sgshapiro if (syntax[(int)*data] == CCTL) 14790792Sgshapiro USTPUTC(CTLESC, p); 14894334Sgshapiro USTPUTC(*data++, p); 14994334Sgshapiro } 15094334Sgshapiro return (p); 15194334Sgshapiro} 15294334Sgshapiro#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p) 15394334Sgshapiro 15490792Sgshapiro/* 15590792Sgshapiro * Perform expansions on an argument, placing the resulting list of arguments 15690792Sgshapiro * in arglist. Parameter expansion, command substitution and arithmetic 15790792Sgshapiro * expansion are always performed; additional expansions can be requested 15890792Sgshapiro * via flag (EXP_*). 15990792Sgshapiro * The result is left in the stack string. 16090792Sgshapiro * When arglist is NULL, perform here document expansion. 16190792Sgshapiro * 16290792Sgshapiro * Caution: this function uses global state and is not reentrant. 16394334Sgshapiro * However, a new invocation after an interrupted invocation is safe 16490792Sgshapiro * and will reset the global state for the new call. 16590792Sgshapiro */ 16690792Sgshapirovoid 16790792Sgshapiroexpandarg(union node *arg, struct arglist *arglist, int flag) 16890792Sgshapiro{ 16990792Sgshapiro struct strlist *sp; 17090792Sgshapiro char *p; 17190792Sgshapiro 17290792Sgshapiro argbackq = arg->narg.backquote; 17390792Sgshapiro STARTSTACKSTR(expdest); 17490792Sgshapiro ifsfirst.next = NULL; 17590792Sgshapiro ifslastp = NULL; 17690792Sgshapiro argstr(arg->narg.text, flag); 17790792Sgshapiro if (arglist == NULL) { 17890792Sgshapiro STACKSTRNUL(expdest); 17990792Sgshapiro return; /* here document expanded */ 18090792Sgshapiro } 18190792Sgshapiro STPUTC('\0', expdest); 18290792Sgshapiro p = grabstackstr(expdest); 18390792Sgshapiro exparg.lastp = &exparg.list; 18490792Sgshapiro /* 18590792Sgshapiro * TODO - EXP_REDIR 18690792Sgshapiro */ 18790792Sgshapiro if (flag & EXP_FULL) { 18890792Sgshapiro ifsbreakup(p, &exparg); 18990792Sgshapiro *exparg.lastp = NULL; 19090792Sgshapiro exparg.lastp = &exparg.list; 19190792Sgshapiro expandmeta(exparg.list, flag); 19290792Sgshapiro } else { 19390792Sgshapiro if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */ 19490792Sgshapiro rmescapes(p); 19590792Sgshapiro sp = (struct strlist *)stalloc(sizeof (struct strlist)); 19690792Sgshapiro sp->text = p; 19790792Sgshapiro *exparg.lastp = sp; 19890792Sgshapiro exparg.lastp = &sp->next; 19990792Sgshapiro } 20090792Sgshapiro while (ifsfirst.next != NULL) { 20190792Sgshapiro struct ifsregion *ifsp; 20290792Sgshapiro INTOFF; 20390792Sgshapiro ifsp = ifsfirst.next->next; 20490792Sgshapiro ckfree(ifsfirst.next); 20590792Sgshapiro ifsfirst.next = ifsp; 20690792Sgshapiro INTON; 20790792Sgshapiro } 20890792Sgshapiro *exparg.lastp = NULL; 20990792Sgshapiro if (exparg.list) { 21090792Sgshapiro *arglist->lastp = exparg.list; 21190792Sgshapiro arglist->lastp = exparg.lastp; 21290792Sgshapiro } 21390792Sgshapiro} 21490792Sgshapiro 21590792Sgshapiro 21690792Sgshapiro 21790792Sgshapiro/* 21890792Sgshapiro * Perform parameter expansion, command substitution and arithmetic 21990792Sgshapiro * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE. 22090792Sgshapiro * Processing ends at a CTLENDVAR character as well as '\0'. 22190792Sgshapiro * This is used to expand word in ${var+word} etc. 22290792Sgshapiro * If EXP_FULL, EXP_CASE or EXP_REDIR are set, keep and/or generate CTLESC 22390792Sgshapiro * characters to allow for further processing. 22490792Sgshapiro * If EXP_FULL is set, also preserve CTLQUOTEMARK characters. 22590792Sgshapiro */ 22690792Sgshapirostatic void 22790792Sgshapiroargstr(char *p, int flag) 22890792Sgshapiro{ 22990792Sgshapiro char c; 23090792Sgshapiro int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); /* do CTLESC */ 23190792Sgshapiro int firsteq = 1; 23290792Sgshapiro int split_lit; 23390792Sgshapiro int lit_quoted; 23490792Sgshapiro 23590792Sgshapiro split_lit = flag & EXP_SPLIT_LIT; 23690792Sgshapiro lit_quoted = flag & EXP_LIT_QUOTED; 23790792Sgshapiro flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); 23890792Sgshapiro if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 23990792Sgshapiro p = exptilde(p, flag); 24090792Sgshapiro for (;;) { 24190792Sgshapiro CHECKSTRSPACE(2, expdest); 24290792Sgshapiro switch (c = *p++) { 24390792Sgshapiro case '\0': 24490792Sgshapiro case CTLENDVAR: 24590792Sgshapiro goto breakloop; 24690792Sgshapiro case CTLQUOTEMARK: 24790792Sgshapiro lit_quoted = 1; 24890792Sgshapiro /* "$@" syntax adherence hack */ 24990792Sgshapiro if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') 25090792Sgshapiro break; 25190792Sgshapiro if ((flag & EXP_FULL) != 0) 25290792Sgshapiro USTPUTC(c, expdest); 25390792Sgshapiro break; 25490792Sgshapiro case CTLQUOTEEND: 25590792Sgshapiro lit_quoted = 0; 25690792Sgshapiro break; 25790792Sgshapiro case CTLESC: 25890792Sgshapiro if (quotes) 25990792Sgshapiro USTPUTC(c, expdest); 26090792Sgshapiro c = *p++; 26190792Sgshapiro USTPUTC(c, expdest); 26290792Sgshapiro if (split_lit && !lit_quoted) 26390792Sgshapiro recordregion(expdest - stackblock() - 26490792Sgshapiro (quotes ? 2 : 1), 26590792Sgshapiro expdest - stackblock(), 0); 26690792Sgshapiro break; 26790792Sgshapiro case CTLVAR: 26890792Sgshapiro p = evalvar(p, flag); 26990792Sgshapiro break; 27090792Sgshapiro case CTLBACKQ: 27190792Sgshapiro case CTLBACKQ|CTLQUOTE: 27290792Sgshapiro expbackq(argbackq->n, c & CTLQUOTE, flag); 27390792Sgshapiro argbackq = argbackq->next; 27490792Sgshapiro break; 27590792Sgshapiro case CTLENDARI: 27690792Sgshapiro expari(flag); 27790792Sgshapiro break; 27890792Sgshapiro case ':': 27990792Sgshapiro case '=': 28090792Sgshapiro /* 28190792Sgshapiro * sort of a hack - expand tildes in variable 28290792Sgshapiro * assignments (after the first '=' and after ':'s). 28390792Sgshapiro */ 28490792Sgshapiro USTPUTC(c, expdest); 28590792Sgshapiro if (split_lit && !lit_quoted) 28690792Sgshapiro recordregion(expdest - stackblock() - 1, 28790792Sgshapiro expdest - stackblock(), 0); 28890792Sgshapiro if (flag & EXP_VARTILDE && *p == '~' && 28990792Sgshapiro (c != '=' || firsteq)) { 29090792Sgshapiro if (c == '=') 29190792Sgshapiro firsteq = 0; 29290792Sgshapiro p = exptilde(p, flag); 29390792Sgshapiro } 29490792Sgshapiro break; 29590792Sgshapiro default: 29690792Sgshapiro USTPUTC(c, expdest); 29790792Sgshapiro if (split_lit && !lit_quoted) 29890792Sgshapiro recordregion(expdest - stackblock() - 1, 29990792Sgshapiro expdest - stackblock(), 0); 30090792Sgshapiro } 30190792Sgshapiro } 30290792Sgshapirobreakloop:; 30390792Sgshapiro} 30490792Sgshapiro 30590792Sgshapiro/* 30690792Sgshapiro * Perform tilde expansion, placing the result in the stack string and 30790792Sgshapiro * returning the next position in the input string to process. 30890792Sgshapiro */ 30990792Sgshapirostatic char * 31090792Sgshapiroexptilde(char *p, int flag) 31190792Sgshapiro{ 31290792Sgshapiro char c, *startp = p; 31390792Sgshapiro struct passwd *pw; 31494334Sgshapiro char *home; 31594334Sgshapiro int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 31690792Sgshapiro 31790792Sgshapiro while ((c = *p) != '\0') { 31890792Sgshapiro switch(c) { 31990792Sgshapiro case CTLESC: /* This means CTL* are always considered quoted. */ 32090792Sgshapiro case CTLVAR: 32190792Sgshapiro case CTLBACKQ: 32290792Sgshapiro case CTLBACKQ | CTLQUOTE: 32390792Sgshapiro case CTLARI: 32494334Sgshapiro case CTLENDARI: 32594334Sgshapiro case CTLQUOTEMARK: 32694334Sgshapiro return (startp); 32794334Sgshapiro case ':': 32894334Sgshapiro if (flag & EXP_VARTILDE) 32994334Sgshapiro goto done; 33094334Sgshapiro break; 33194334Sgshapiro case '/': 33294334Sgshapiro case CTLENDVAR: 33394334Sgshapiro goto done; 33494334Sgshapiro } 33594334Sgshapiro p++; 33694334Sgshapiro } 33794334Sgshapirodone: 33894334Sgshapiro *p = '\0'; 33994334Sgshapiro if (*(startp+1) == '\0') { 34094334Sgshapiro if ((home = lookupvar("HOME")) == NULL) 34194334Sgshapiro goto lose; 34294334Sgshapiro } else { 34394334Sgshapiro if ((pw = getpwnam(startp+1)) == NULL) 34494334Sgshapiro goto lose; 34594334Sgshapiro home = pw->pw_dir; 34694334Sgshapiro } 34794334Sgshapiro if (*home == '\0') 34894334Sgshapiro goto lose; 34994334Sgshapiro *p = c; 35094334Sgshapiro if (quotes) 35194334Sgshapiro STPUTS_QUOTES(home, SQSYNTAX, expdest); 35294334Sgshapiro else 35394334Sgshapiro STPUTS(home, expdest); 35494334Sgshapiro return (p); 35594334Sgshapirolose: 35694334Sgshapiro *p = c; 35794334Sgshapiro return (startp); 35894334Sgshapiro} 35994334Sgshapiro 36094334Sgshapiro 36194334Sgshapirostatic void 36294334Sgshapiroremoverecordregions(int endoff) 36394334Sgshapiro{ 36494334Sgshapiro if (ifslastp == NULL) 36594334Sgshapiro return; 36694334Sgshapiro 36794334Sgshapiro if (ifsfirst.endoff > endoff) { 36894334Sgshapiro while (ifsfirst.next != NULL) { 36994334Sgshapiro struct ifsregion *ifsp; 37094334Sgshapiro INTOFF; 37194334Sgshapiro ifsp = ifsfirst.next->next; 37294334Sgshapiro ckfree(ifsfirst.next); 37394334Sgshapiro ifsfirst.next = ifsp; 37494334Sgshapiro INTON; 37594334Sgshapiro } 37694334Sgshapiro if (ifsfirst.begoff > endoff) 37794334Sgshapiro ifslastp = NULL; 37894334Sgshapiro else { 37994334Sgshapiro ifslastp = &ifsfirst; 38094334Sgshapiro ifsfirst.endoff = endoff; 38194334Sgshapiro } 38294334Sgshapiro return; 38394334Sgshapiro } 38490792Sgshapiro 38590792Sgshapiro ifslastp = &ifsfirst; 38690792Sgshapiro while (ifslastp->next && ifslastp->next->begoff < endoff) 38790792Sgshapiro ifslastp=ifslastp->next; 38890792Sgshapiro while (ifslastp->next != NULL) { 38990792Sgshapiro struct ifsregion *ifsp; 39090792Sgshapiro INTOFF; 39190792Sgshapiro ifsp = ifslastp->next->next; 39290792Sgshapiro ckfree(ifslastp->next); 39390792Sgshapiro ifslastp->next = ifsp; 39490792Sgshapiro INTON; 39590792Sgshapiro } 39690792Sgshapiro if (ifslastp->endoff > endoff) 39790792Sgshapiro ifslastp->endoff = endoff; 39890792Sgshapiro} 39994334Sgshapiro 40090792Sgshapiro/* 40190792Sgshapiro * Expand arithmetic expression. Backup to start of expression, 40290792Sgshapiro * evaluate, place result in (backed up) result, adjust string position. 40390792Sgshapiro */ 40490792Sgshapirovoid 40590792Sgshapiroexpari(int flag) 40690792Sgshapiro{ 40790792Sgshapiro char *p, *q, *start; 40890792Sgshapiro arith_t result; 40994334Sgshapiro int begoff; 41094334Sgshapiro int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 41190792Sgshapiro int quoted; 41290792Sgshapiro 41390792Sgshapiro /* 41490792Sgshapiro * This routine is slightly over-complicated for 41590792Sgshapiro * efficiency. First we make sure there is 41694334Sgshapiro * enough space for the result, which may be bigger 41794334Sgshapiro * than the expression. Next we 41894334Sgshapiro * scan backwards looking for the start of arithmetic. If the 41994334Sgshapiro * next previous character is a CTLESC character, then we 42094334Sgshapiro * have to rescan starting from the beginning since CTLESC 42194334Sgshapiro * characters have to be processed left to right. 42294334Sgshapiro */ 42394334Sgshapiro CHECKSTRSPACE(DIGITS(result) - 2, expdest); 42494334Sgshapiro USTPUTC('\0', expdest); 42590792Sgshapiro start = stackblock(); 42694334Sgshapiro p = expdest - 2; 42794334Sgshapiro while (p >= start && *p != CTLARI) 42894334Sgshapiro --p; 42994334Sgshapiro if (p < start || *p != CTLARI) 43094334Sgshapiro error("missing CTLARI (shouldn't happen)"); 43194334Sgshapiro if (p > start && *(p - 1) == CTLESC) 43294334Sgshapiro for (p = start; *p != CTLARI; p++) 43390792Sgshapiro if (*p == CTLESC) 43494334Sgshapiro p++; 43594334Sgshapiro 43694334Sgshapiro if (p[1] == '"') 43794334Sgshapiro quoted=1; 43894334Sgshapiro else 43994334Sgshapiro quoted=0; 44094334Sgshapiro begoff = p - start; 44194334Sgshapiro removerecordregions(begoff); 44294334Sgshapiro if (quotes) 44394334Sgshapiro rmescapes(p+2); 44494334Sgshapiro q = grabstackstr(expdest); 44594334Sgshapiro result = arith(p+2); 44690792Sgshapiro ungrabstackstr(q, expdest); 44794334Sgshapiro fmtstr(p, DIGITS(result), ARITH_FORMAT_STR, result); 44894334Sgshapiro while (*p++) 44990792Sgshapiro ; 45094334Sgshapiro if (quoted == 0) 45194334Sgshapiro recordregion(begoff, p - 1 - start, 0); 45294334Sgshapiro result = expdest - p + 1; 45394334Sgshapiro STADJUST(-result, expdest); 45494334Sgshapiro} 45594334Sgshapiro 45694334Sgshapiro 45794334Sgshapiro/* 45894334Sgshapiro * Perform command substitution. 45990792Sgshapiro */ 46090792Sgshapirostatic void 46194334Sgshapiroexpbackq(union node *cmd, int quoted, int flag) 46294334Sgshapiro{ 46394334Sgshapiro struct backcmd in; 46494334Sgshapiro int i; 46594334Sgshapiro char buf[128]; 46694334Sgshapiro char *p; 46794334Sgshapiro char *dest = expdest; 46894334Sgshapiro struct ifsregion saveifs, *savelastp; 46994334Sgshapiro struct nodelist *saveargbackq; 47090792Sgshapiro char lastc; 47194334Sgshapiro int startloc = dest - stackblock(); 47294334Sgshapiro char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 47394334Sgshapiro int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 47494334Sgshapiro int nnl; 47594334Sgshapiro 47694334Sgshapiro INTOFF; 47794334Sgshapiro saveifs = ifsfirst; 47894334Sgshapiro savelastp = ifslastp; 47994334Sgshapiro saveargbackq = argbackq; 48094334Sgshapiro p = grabstackstr(dest); 48194334Sgshapiro evalbackcmd(cmd, &in); 48294334Sgshapiro ungrabstackstr(p, dest); 48394334Sgshapiro ifsfirst = saveifs; 48494334Sgshapiro ifslastp = savelastp; 48594334Sgshapiro argbackq = saveargbackq; 48694334Sgshapiro 48794334Sgshapiro p = in.buf; 48894334Sgshapiro lastc = '\0'; 48994334Sgshapiro nnl = 0; 49094334Sgshapiro /* Don't copy trailing newlines */ 49194334Sgshapiro for (;;) { 49294334Sgshapiro if (--in.nleft < 0) { 49394334Sgshapiro if (in.fd < 0) 49494334Sgshapiro break; 49594334Sgshapiro while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 49694334Sgshapiro TRACE(("expbackq: read returns %d\n", i)); 49794334Sgshapiro if (i <= 0) 49894334Sgshapiro break; 49994334Sgshapiro p = buf; 50094334Sgshapiro in.nleft = i - 1; 50194334Sgshapiro } 50294334Sgshapiro lastc = *p++; 50394334Sgshapiro if (lastc != '\0') { 50494334Sgshapiro if (lastc == '\n') { 50594334Sgshapiro nnl++; 50694334Sgshapiro } else { 50794334Sgshapiro CHECKSTRSPACE(nnl + 2, dest); 50894334Sgshapiro while (nnl > 0) { 50994334Sgshapiro nnl--; 51094334Sgshapiro USTPUTC('\n', dest); 51194334Sgshapiro } 51294334Sgshapiro if (quotes && syntax[(int)lastc] == CCTL) 51394334Sgshapiro USTPUTC(CTLESC, dest); 51494334Sgshapiro USTPUTC(lastc, dest); 51594334Sgshapiro } 51694334Sgshapiro } 51794334Sgshapiro } 51890792Sgshapiro 51990792Sgshapiro if (in.fd >= 0) 52090792Sgshapiro close(in.fd); 52194334Sgshapiro if (in.buf) 52294334Sgshapiro ckfree(in.buf); 52390792Sgshapiro if (in.jp) 52490792Sgshapiro exitstatus = waitforjob(in.jp, (int *)NULL); 52590792Sgshapiro if (quoted == 0) 52694334Sgshapiro recordregion(startloc, dest - stackblock(), 0); 52790792Sgshapiro TRACE(("expbackq: size=%td: \"%.*s\"\n", 52890792Sgshapiro ((dest - stackblock()) - startloc), 52994334Sgshapiro (int)((dest - stackblock()) - startloc), 53094334Sgshapiro stackblock() + startloc)); 53190792Sgshapiro expdest = dest; 53290792Sgshapiro INTON; 53390792Sgshapiro} 53490792Sgshapiro 53590792Sgshapiro 53690792Sgshapiro 53790792Sgshapirostatic int 53890792Sgshapirosubevalvar(char *p, char *str, int strloc, int subtype, int startloc, 53990792Sgshapiro int varflags, int quotes) 54094334Sgshapiro{ 54190792Sgshapiro char *startp; 54290792Sgshapiro char *loc = NULL; 54390792Sgshapiro char *q; 54490792Sgshapiro int c = 0; 54590792Sgshapiro struct nodelist *saveargbackq = argbackq; 54690792Sgshapiro int amount; 54790792Sgshapiro 54890792Sgshapiro argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX || 54990792Sgshapiro subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ? 55090792Sgshapiro EXP_CASE : 0) | EXP_TILDE); 55190792Sgshapiro STACKSTRNUL(expdest); 55290792Sgshapiro argbackq = saveargbackq; 55390792Sgshapiro startp = stackblock() + startloc; 55490792Sgshapiro if (str == NULL) 55594334Sgshapiro str = stackblock() + strloc; 55690792Sgshapiro 55790792Sgshapiro switch (subtype) { 55890792Sgshapiro case VSASSIGN: 55990792Sgshapiro setvar(str, startp, 0); 56090792Sgshapiro amount = startp - expdest; 56190792Sgshapiro STADJUST(amount, expdest); 56290792Sgshapiro varflags &= ~VSNUL; 56390792Sgshapiro return 1; 56490792Sgshapiro 56590792Sgshapiro case VSQUESTION: 56690792Sgshapiro if (*p != CTLENDVAR) { 56790792Sgshapiro outfmt(out2, "%s\n", startp); 56890792Sgshapiro error((char *)NULL); 56990792Sgshapiro } 57090792Sgshapiro error("%.*s: parameter %snot set", (int)(p - str - 1), 57190792Sgshapiro str, (varflags & VSNUL) ? "null or " 57290792Sgshapiro : nullstr); 57390792Sgshapiro return 0; 57490792Sgshapiro 57590792Sgshapiro case VSTRIMLEFT: 57690792Sgshapiro for (loc = startp; loc < str; loc++) { 57790792Sgshapiro c = *loc; 57890792Sgshapiro *loc = '\0'; 57990792Sgshapiro if (patmatch(str, startp, quotes)) { 58090792Sgshapiro *loc = c; 58190792Sgshapiro goto recordleft; 58290792Sgshapiro } 58394334Sgshapiro *loc = c; 58490792Sgshapiro if (quotes && *loc == CTLESC) 58590792Sgshapiro loc++; 58690792Sgshapiro } 58790792Sgshapiro return 0; 58894334Sgshapiro 58994334Sgshapiro case VSTRIMLEFTMAX: 59094334Sgshapiro for (loc = str - 1; loc >= startp;) { 59194334Sgshapiro c = *loc; 59294334Sgshapiro *loc = '\0'; 59390792Sgshapiro if (patmatch(str, startp, quotes)) { 59490792Sgshapiro *loc = c; 59594334Sgshapiro goto recordleft; 59690792Sgshapiro } 59790792Sgshapiro *loc = c; 59894334Sgshapiro loc--; 59994334Sgshapiro if (quotes && loc > startp && *(loc - 1) == CTLESC) { 60094334Sgshapiro for (q = startp; q < loc; q++) 60190792Sgshapiro if (*q == CTLESC) 60290792Sgshapiro q++; 60390792Sgshapiro if (q > loc) 60490792Sgshapiro loc--; 60590792Sgshapiro } 60690792Sgshapiro } 60790792Sgshapiro return 0; 60890792Sgshapiro 60990792Sgshapiro case VSTRIMRIGHT: 61090792Sgshapiro for (loc = str - 1; loc >= startp;) { 61190792Sgshapiro if (patmatch(str, loc, quotes)) { 61290792Sgshapiro amount = loc - expdest; 61390792Sgshapiro STADJUST(amount, expdest); 61490792Sgshapiro return 1; 61590792Sgshapiro } 61690792Sgshapiro loc--; 61790792Sgshapiro if (quotes && loc > startp && *(loc - 1) == CTLESC) { 61890792Sgshapiro for (q = startp; q < loc; q++) 61990792Sgshapiro if (*q == CTLESC) 62090792Sgshapiro q++; 62190792Sgshapiro if (q > loc) 62290792Sgshapiro loc--; 62390792Sgshapiro } 62490792Sgshapiro } 62594334Sgshapiro return 0; 62690792Sgshapiro 62794334Sgshapiro case VSTRIMRIGHTMAX: 62890792Sgshapiro for (loc = startp; loc < str - 1; loc++) { 62990792Sgshapiro if (patmatch(str, loc, quotes)) { 63090792Sgshapiro amount = loc - expdest; 63190792Sgshapiro STADJUST(amount, expdest); 63290792Sgshapiro return 1; 63390792Sgshapiro } 63494334Sgshapiro if (quotes && *loc == CTLESC) 63590792Sgshapiro loc++; 63690792Sgshapiro } 63790792Sgshapiro return 0; 63894334Sgshapiro 63994334Sgshapiro 64094334Sgshapiro default: 64190792Sgshapiro abort(); 64294334Sgshapiro } 64394334Sgshapiro 64494334Sgshapirorecordleft: 64594334Sgshapiro amount = ((str - 1) - (loc - startp)) - expdest; 64694334Sgshapiro STADJUST(amount, expdest); 64794334Sgshapiro while (loc != str - 1) 64894334Sgshapiro *startp++ = *loc++; 64990792Sgshapiro return 1; 65090792Sgshapiro} 65194334Sgshapiro 65290792Sgshapiro 65390792Sgshapiro/* 65490792Sgshapiro * Expand a variable, and return a pointer to the next character in the 65590792Sgshapiro * input string. 65694334Sgshapiro */ 65794334Sgshapiro 65894334Sgshapirostatic char * 65994334Sgshapiroevalvar(char *p, int flag) 66094334Sgshapiro{ 66194334Sgshapiro int subtype; 66294334Sgshapiro int varflags; 66394334Sgshapiro char *var; 66494334Sgshapiro char *val; 66594334Sgshapiro int patloc; 66694334Sgshapiro int c; 66794334Sgshapiro int set; 66894334Sgshapiro int special; 66994334Sgshapiro int startloc; 67094334Sgshapiro int varlen; 67194334Sgshapiro int varlenb; 67294334Sgshapiro int easy; 67390792Sgshapiro int quotes = flag & (EXP_FULL | EXP_CASE | EXP_REDIR); 67490792Sgshapiro 67590792Sgshapiro varflags = (unsigned char)*p++; 67690792Sgshapiro subtype = varflags & VSTYPE; 67790792Sgshapiro var = p; 67890792Sgshapiro special = 0; 67990792Sgshapiro if (! is_name(*p)) 68090792Sgshapiro special = 1; 68190792Sgshapiro p = strchr(p, '=') + 1; 68290792Sgshapiroagain: /* jump here after setting a variable with ${var=text} */ 68390792Sgshapiro if (varflags & VSLINENO) { 68490792Sgshapiro set = 1; 68590792Sgshapiro special = 0; 68690792Sgshapiro val = var; 68790792Sgshapiro p[-1] = '\0'; /* temporarily overwrite '=' to have \0 68890792Sgshapiro terminated string */ 68990792Sgshapiro } else if (special) { 69094334Sgshapiro set = varisset(var, varflags & VSNUL); 69190792Sgshapiro val = NULL; 69290792Sgshapiro } else { 69390792Sgshapiro val = bltinlookup(var, 1); 69490792Sgshapiro if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 69590792Sgshapiro val = NULL; 69690792Sgshapiro set = 0; 69790792Sgshapiro } else 69890792Sgshapiro set = 1; 69990792Sgshapiro } 70090792Sgshapiro varlen = 0; 70190792Sgshapiro startloc = expdest - stackblock(); 70290792Sgshapiro if (!set && uflag && *var != '@' && *var != '*') { 70390792Sgshapiro switch (subtype) { 70490792Sgshapiro case VSNORMAL: 70590792Sgshapiro case VSTRIMLEFT: 70690792Sgshapiro case VSTRIMLEFTMAX: 70790792Sgshapiro case VSTRIMRIGHT: 70890792Sgshapiro case VSTRIMRIGHTMAX: 70990792Sgshapiro case VSLENGTH: 71090792Sgshapiro error("%.*s: parameter not set", (int)(p - var - 1), 71190792Sgshapiro var); 71290792Sgshapiro } 71390792Sgshapiro } 71490792Sgshapiro if (set && subtype != VSPLUS) { 71590792Sgshapiro /* insert the value of the variable */ 71690792Sgshapiro if (special) { 71790792Sgshapiro varvalue(var, varflags & VSQUOTE, subtype, flag); 71890792Sgshapiro if (subtype == VSLENGTH) { 71990792Sgshapiro varlenb = expdest - stackblock() - startloc; 72090792Sgshapiro varlen = varlenb; 72190792Sgshapiro if (localeisutf8) { 72290792Sgshapiro val = stackblock() + startloc; 72390792Sgshapiro for (;val != expdest; val++) 72490792Sgshapiro if ((*val & 0xC0) == 0x80) 72590792Sgshapiro varlen--; 72694334Sgshapiro } 72790792Sgshapiro STADJUST(-varlenb, expdest); 72894334Sgshapiro } 72994334Sgshapiro } else { 73094334Sgshapiro char const *syntax = (varflags & VSQUOTE) ? DQSYNTAX 73194334Sgshapiro : BASESYNTAX; 73294334Sgshapiro 73394334Sgshapiro if (subtype == VSLENGTH) { 73494334Sgshapiro for (;*val; val++) 73594334Sgshapiro if (!localeisutf8 || 73694334Sgshapiro (*val & 0xC0) != 0x80) 73794334Sgshapiro varlen++; 73894334Sgshapiro } 73994334Sgshapiro else { 74094334Sgshapiro if (quotes) 74194334Sgshapiro STPUTS_QUOTES(val, syntax, expdest); 74294334Sgshapiro else 74394334Sgshapiro STPUTS(val, expdest); 74490792Sgshapiro 74590792Sgshapiro } 74690792Sgshapiro } 74790792Sgshapiro } 74890792Sgshapiro 74990792Sgshapiro if (subtype == VSPLUS) 75090792Sgshapiro set = ! set; 75190792Sgshapiro 75290792Sgshapiro easy = ((varflags & VSQUOTE) == 0 || 75390792Sgshapiro (*var == '@' && shellparam.nparam != 1)); 75490792Sgshapiro 75590792Sgshapiro 75690792Sgshapiro switch (subtype) { 75790792Sgshapiro case VSLENGTH: 75890792Sgshapiro expdest = cvtnum(varlen, expdest); 75990792Sgshapiro goto record; 76090792Sgshapiro 76190792Sgshapiro case VSNORMAL: 76290792Sgshapiro if (!easy) 76390792Sgshapiro break; 76490792Sgshapirorecord: 76590792Sgshapiro recordregion(startloc, expdest - stackblock(), 76690792Sgshapiro varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' && 76790792Sgshapiro (*var == '@' || *var == '*'))); 76890792Sgshapiro break; 76990792Sgshapiro 77090792Sgshapiro case VSPLUS: 77190792Sgshapiro case VSMINUS: 77290792Sgshapiro if (!set) { 77390792Sgshapiro argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) | 77490792Sgshapiro (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0)); 77590792Sgshapiro break; 77690792Sgshapiro } 77790792Sgshapiro if (easy) 77890792Sgshapiro goto record; 77990792Sgshapiro break; 78090792Sgshapiro 78190792Sgshapiro case VSTRIMLEFT: 78290792Sgshapiro case VSTRIMLEFTMAX: 78390792Sgshapiro case VSTRIMRIGHT: 78490792Sgshapiro case VSTRIMRIGHTMAX: 78590792Sgshapiro if (!set) 78694334Sgshapiro break; 78794334Sgshapiro /* 78894334Sgshapiro * Terminate the string and start recording the pattern 78994334Sgshapiro * right after it 79094334Sgshapiro */ 79194334Sgshapiro STPUTC('\0', expdest); 79294334Sgshapiro patloc = expdest - stackblock(); 79394334Sgshapiro if (subevalvar(p, NULL, patloc, subtype, 79494334Sgshapiro startloc, varflags, quotes) == 0) { 79594334Sgshapiro int amount = (expdest - stackblock() - patloc) + 1; 79690792Sgshapiro STADJUST(-amount, expdest); 79790792Sgshapiro } 79890792Sgshapiro /* Remove any recorded regions beyond start of variable */ 79990792Sgshapiro removerecordregions(startloc); 80090792Sgshapiro goto record; 80190792Sgshapiro 80294334Sgshapiro case VSASSIGN: 80390792Sgshapiro case VSQUESTION: 80490792Sgshapiro if (!set) { 80590792Sgshapiro if (subevalvar(p, var, 0, subtype, startloc, varflags, 80690792Sgshapiro quotes)) { 80790792Sgshapiro varflags &= ~VSNUL; 80890792Sgshapiro /* 80990792Sgshapiro * Remove any recorded regions beyond 81090792Sgshapiro * start of variable 81194334Sgshapiro */ 81294334Sgshapiro removerecordregions(startloc); 81394334Sgshapiro goto again; 81494334Sgshapiro } 81594334Sgshapiro break; 81694334Sgshapiro } 81790792Sgshapiro if (easy) 81890792Sgshapiro goto record; 81994334Sgshapiro break; 82094334Sgshapiro 82194334Sgshapiro case VSERROR: 82294334Sgshapiro c = p - var - 1; 82394334Sgshapiro error("${%.*s%s}: Bad substitution", c, var, 82494334Sgshapiro (c > 0 && *p != CTLENDVAR) ? "..." : ""); 82594334Sgshapiro 82694334Sgshapiro default: 82794334Sgshapiro abort(); 82894334Sgshapiro } 82990792Sgshapiro p[-1] = '='; /* recover overwritten '=' */ 83090792Sgshapiro 83190792Sgshapiro if (subtype != VSNORMAL) { /* skip to end of alternative */ 83294334Sgshapiro int nesting = 1; 83394334Sgshapiro for (;;) { 83494334Sgshapiro if ((c = *p++) == CTLESC) 83590792Sgshapiro p++; 83694334Sgshapiro else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 83794334Sgshapiro if (set) 83890792Sgshapiro argbackq = argbackq->next; 83994334Sgshapiro } else if (c == CTLVAR) { 84094334Sgshapiro if ((*p++ & VSTYPE) != VSNORMAL) 84190792Sgshapiro nesting++; 84294334Sgshapiro } else if (c == CTLENDVAR) { 84390792Sgshapiro if (--nesting == 0) 84490792Sgshapiro break; 84590792Sgshapiro } 84690792Sgshapiro } 84790792Sgshapiro } 84894334Sgshapiro return p; 84990792Sgshapiro} 85090792Sgshapiro 85190792Sgshapiro 85290792Sgshapiro 85390792Sgshapiro/* 85494334Sgshapiro * Test whether a specialized variable is set. 85594334Sgshapiro */ 85694334Sgshapiro 85794334Sgshapirostatic int 85894334Sgshapirovarisset(char *name, int nulok) 85994334Sgshapiro{ 86094334Sgshapiro 86194334Sgshapiro if (*name == '!') 86290792Sgshapiro return backgndpidset(); 86394334Sgshapiro else if (*name == '@' || *name == '*') { 86494334Sgshapiro if (*shellparam.p == NULL) 86594334Sgshapiro return 0; 86694334Sgshapiro 86790792Sgshapiro if (nulok) { 86894334Sgshapiro char **av; 86994334Sgshapiro 87094334Sgshapiro for (av = shellparam.p; *av; av++) 87194334Sgshapiro if (**av != '\0') 87294334Sgshapiro return 1; 87394334Sgshapiro return 0; 87494334Sgshapiro } 87594334Sgshapiro } else if (is_digit(*name)) { 87694334Sgshapiro char *ap; 87794334Sgshapiro int num = atoi(name); 87894334Sgshapiro 87994334Sgshapiro if (num > shellparam.nparam) 88094334Sgshapiro return 0; 88194334Sgshapiro 88294334Sgshapiro if (num == 0) 88394334Sgshapiro ap = arg0; 88490792Sgshapiro else 88594334Sgshapiro ap = shellparam.p[num - 1]; 88694334Sgshapiro 88794334Sgshapiro if (nulok && (ap == NULL || *ap == '\0')) 88894334Sgshapiro return 0; 88994334Sgshapiro } 89090792Sgshapiro return 1; 89194334Sgshapiro} 89294334Sgshapiro 89394334Sgshapirostatic void 89494334Sgshapirostrtodest(const char *p, int flag, int subtype, int quoted) 89594334Sgshapiro{ 89694334Sgshapiro if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) 89794334Sgshapiro STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest); 89894334Sgshapiro else 89994334Sgshapiro STPUTS(p, expdest); 90094334Sgshapiro} 90194334Sgshapiro 90294334Sgshapiro/* 90394334Sgshapiro * Add the value of a specialized variable to the stack string. 90494334Sgshapiro */ 90594334Sgshapiro 90694334Sgshapirostatic void 90794334Sgshapirovarvalue(char *name, int quoted, int subtype, int flag) 90894334Sgshapiro{ 90994334Sgshapiro int num; 91094334Sgshapiro char *p; 91194334Sgshapiro int i; 91290792Sgshapiro char sep; 91390792Sgshapiro char **ap; 91490792Sgshapiro 91590792Sgshapiro switch (*name) { 91690792Sgshapiro case '$': 91790792Sgshapiro num = rootpid; 91890792Sgshapiro goto numvar; 91990792Sgshapiro case '?': 92090792Sgshapiro num = oexitstatus; 92190792Sgshapiro goto numvar; 92290792Sgshapiro case '#': 92390792Sgshapiro num = shellparam.nparam; 92490792Sgshapiro goto numvar; 92590792Sgshapiro case '!': 92690792Sgshapiro num = backgndpidval(); 92790792Sgshapironumvar: 92890792Sgshapiro expdest = cvtnum(num, expdest); 92990792Sgshapiro break; 93090792Sgshapiro case '-': 93190792Sgshapiro for (i = 0 ; i < NOPTS ; i++) { 93290792Sgshapiro if (optlist[i].val) 93390792Sgshapiro STPUTC(optlist[i].letter, expdest); 93494334Sgshapiro } 93590792Sgshapiro break; 93690792Sgshapiro case '@': 93790792Sgshapiro if (flag & EXP_FULL && quoted) { 93890792Sgshapiro for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 93994334Sgshapiro strtodest(p, flag, subtype, quoted); 94094334Sgshapiro if (*ap) 94194334Sgshapiro STPUTC('\0', expdest); 94290792Sgshapiro } 94394334Sgshapiro break; 94490792Sgshapiro } 94590792Sgshapiro /* FALLTHROUGH */ 94690792Sgshapiro case '*': 94790792Sgshapiro if (ifsset()) 94890792Sgshapiro sep = ifsval()[0]; 94990792Sgshapiro else 95090792Sgshapiro sep = ' '; 95190792Sgshapiro for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 95294334Sgshapiro strtodest(p, flag, subtype, quoted); 95390792Sgshapiro if (!*ap) 95490792Sgshapiro break; 95590792Sgshapiro if (sep || (flag & EXP_FULL && !quoted && **ap != '\0')) 95690792Sgshapiro STPUTC(sep, expdest); 95790792Sgshapiro } 95890792Sgshapiro break; 95990792Sgshapiro case '0': 96090792Sgshapiro p = arg0; 96190792Sgshapiro strtodest(p, flag, subtype, quoted); 96290792Sgshapiro break; 96390792Sgshapiro default: 96490792Sgshapiro if (is_digit(*name)) { 96590792Sgshapiro num = atoi(name); 96690792Sgshapiro if (num > 0 && num <= shellparam.nparam) { 96790792Sgshapiro p = shellparam.p[num - 1]; 96890792Sgshapiro strtodest(p, flag, subtype, quoted); 96990792Sgshapiro } 97090792Sgshapiro } 97190792Sgshapiro break; 97290792Sgshapiro } 97390792Sgshapiro} 97490792Sgshapiro 97590792Sgshapiro 97694334Sgshapiro 97794334Sgshapiro/* 97894334Sgshapiro * Record the fact that we have to scan this region of the 97994334Sgshapiro * string for IFS characters. 98094334Sgshapiro */ 98194334Sgshapiro 98290792Sgshapirostatic void 98390792Sgshapirorecordregion(int start, int end, int inquotes) 98490792Sgshapiro{ 98590792Sgshapiro struct ifsregion *ifsp; 98690792Sgshapiro 98794334Sgshapiro if (ifslastp == NULL) { 98890792Sgshapiro ifsp = &ifsfirst; 98990792Sgshapiro } else { 99090792Sgshapiro if (ifslastp->endoff == start 99190792Sgshapiro && ifslastp->inquotes == inquotes) { 99290792Sgshapiro /* extend previous area */ 99390792Sgshapiro ifslastp->endoff = end; 99490792Sgshapiro return; 99590792Sgshapiro } 99690792Sgshapiro ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 99790792Sgshapiro ifslastp->next = ifsp; 99890792Sgshapiro } 99990792Sgshapiro ifslastp = ifsp; 100094334Sgshapiro ifslastp->next = NULL; 100190792Sgshapiro ifslastp->begoff = start; 100290792Sgshapiro ifslastp->endoff = end; 100390792Sgshapiro ifslastp->inquotes = inquotes; 100490792Sgshapiro} 100590792Sgshapiro 100690792Sgshapiro 100790792Sgshapiro 100890792Sgshapiro/* 100990792Sgshapiro * Break the argument string into pieces based upon IFS and add the 101090792Sgshapiro * strings to the argument list. The regions of the string to be 101194334Sgshapiro * searched for IFS characters have been stored by recordregion. 101290792Sgshapiro * CTLESC characters are preserved but have little effect in this pass 101394334Sgshapiro * other than escaping CTL* characters. In particular, they do not escape 101490792Sgshapiro * IFS characters: that should be done with the ifsregion mechanism. 101590792Sgshapiro * CTLQUOTEMARK characters are used to preserve empty quoted strings. 101690792Sgshapiro * This pass treats them as a regular character, making the string non-empty. 101794334Sgshapiro * Later, they are removed along with the other CTL* characters. 101894334Sgshapiro */ 101994334Sgshapirostatic void 102094334Sgshapiroifsbreakup(char *string, struct arglist *arglist) 102190792Sgshapiro{ 102290792Sgshapiro struct ifsregion *ifsp; 102390792Sgshapiro struct strlist *sp; 102490792Sgshapiro char *start; 102594334Sgshapiro char *p; 102694334Sgshapiro char *q; 102790792Sgshapiro const char *ifs; 102890792Sgshapiro const char *ifsspc; 102990792Sgshapiro int had_param_ch = 0; 103090792Sgshapiro 103190792Sgshapiro start = string; 103290792Sgshapiro 103394334Sgshapiro if (ifslastp == NULL) { 103494334Sgshapiro /* Return entire argument, IFS doesn't apply to any of it */ 103590792Sgshapiro sp = (struct strlist *)stalloc(sizeof *sp); 103690792Sgshapiro sp->text = start; 103794334Sgshapiro *arglist->lastp = sp; 103890792Sgshapiro arglist->lastp = &sp->next; 103990792Sgshapiro return; 104090792Sgshapiro } 104190792Sgshapiro 104290792Sgshapiro ifs = ifsset() ? ifsval() : " \t\n"; 104390792Sgshapiro 104494334Sgshapiro for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { 104594334Sgshapiro p = string + ifsp->begoff; 104690792Sgshapiro while (p < string + ifsp->endoff) { 104790792Sgshapiro q = p; 104894334Sgshapiro if (*p == CTLESC) 104990792Sgshapiro p++; 105090792Sgshapiro if (ifsp->inquotes) { 105190792Sgshapiro /* Only NULs (should be from "$@") end args */ 105290792Sgshapiro had_param_ch = 1; 105390792Sgshapiro if (*p != 0) { 105494334Sgshapiro p++; 105590792Sgshapiro continue; 105690792Sgshapiro } 105790792Sgshapiro ifsspc = NULL; 105890792Sgshapiro } else { 105990792Sgshapiro if (!strchr(ifs, *p)) { 106090792Sgshapiro had_param_ch = 1; 106190792Sgshapiro p++; 106290792Sgshapiro continue; 106390792Sgshapiro } 106490792Sgshapiro ifsspc = strchr(" \t\n", *p); 106590792Sgshapiro 106690792Sgshapiro /* Ignore IFS whitespace at start */ 106790792Sgshapiro if (q == start && ifsspc != NULL) { 106890792Sgshapiro p++; 106990792Sgshapiro start = p; 107090792Sgshapiro continue; 107190792Sgshapiro } 107290792Sgshapiro had_param_ch = 0; 107390792Sgshapiro } 107490792Sgshapiro 107594334Sgshapiro /* Save this argument... */ 107694334Sgshapiro *q = '\0'; 107794334Sgshapiro sp = (struct strlist *)stalloc(sizeof *sp); 107894334Sgshapiro sp->text = start; 107994334Sgshapiro *arglist->lastp = sp; 108094334Sgshapiro arglist->lastp = &sp->next; 108190792Sgshapiro p++; 108290792Sgshapiro 108390792Sgshapiro if (ifsspc != NULL) { 108490792Sgshapiro /* Ignore further trailing IFS whitespace */ 108590792Sgshapiro for (; p < string + ifsp->endoff; p++) { 108690792Sgshapiro q = p; 108790792Sgshapiro if (*p == CTLESC) 108894334Sgshapiro p++; 108994334Sgshapiro if (strchr(ifs, *p) == NULL) { 109094334Sgshapiro p = q; 109190792Sgshapiro break; 109290792Sgshapiro } 109390792Sgshapiro if (strchr(" \t\n", *p) == NULL) { 109490792Sgshapiro p++; 109590792Sgshapiro break; 109690792Sgshapiro } 109790792Sgshapiro } 109890792Sgshapiro } 109994334Sgshapiro start = p; 110094334Sgshapiro } 110194334Sgshapiro } 110294334Sgshapiro 110390792Sgshapiro /* 110490792Sgshapiro * Save anything left as an argument. 110590792Sgshapiro * Traditionally we have treated 'IFS=':'; set -- x$IFS' as 110690792Sgshapiro * generating 2 arguments, the second of which is empty. 110790792Sgshapiro * Some recent clarification of the Posix spec say that it 110890792Sgshapiro * should only generate one.... 110990792Sgshapiro */ 111090792Sgshapiro if (had_param_ch || *start != 0) { 111190792Sgshapiro sp = (struct strlist *)stalloc(sizeof *sp); 111290792Sgshapiro sp->text = start; 111390792Sgshapiro *arglist->lastp = sp; 111490792Sgshapiro arglist->lastp = &sp->next; 111590792Sgshapiro } 111690792Sgshapiro} 111790792Sgshapiro 111890792Sgshapiro 111990792Sgshapirostatic char expdir[PATH_MAX]; 112090792Sgshapiro#define expdir_end (expdir + sizeof(expdir)) 112190792Sgshapiro 112290792Sgshapiro/* 112390792Sgshapiro * Perform pathname generation and remove control characters. 112490792Sgshapiro * At this point, the only control characters should be CTLESC and CTLQUOTEMARK. 112590792Sgshapiro * The results are stored in the list exparg. 112690792Sgshapiro */ 112790792Sgshapirostatic void 112890792Sgshapiroexpandmeta(struct strlist *str, int flag __unused) 112990792Sgshapiro{ 113090792Sgshapiro char *p; 113190792Sgshapiro struct strlist **savelastp; 113290792Sgshapiro struct strlist *sp; 113390792Sgshapiro char c; 113490792Sgshapiro /* TODO - EXP_REDIR */ 113590792Sgshapiro 113690792Sgshapiro while (str) { 113790792Sgshapiro if (fflag) 113890792Sgshapiro goto nometa; 113990792Sgshapiro p = str->text; 114090792Sgshapiro for (;;) { /* fast check for meta chars */ 114190792Sgshapiro if ((c = *p++) == '\0') 114290792Sgshapiro goto nometa; 114390792Sgshapiro if (c == '*' || c == '?' || c == '[') 114490792Sgshapiro break; 114590792Sgshapiro } 114690792Sgshapiro savelastp = exparg.lastp; 114790792Sgshapiro INTOFF; 114890792Sgshapiro expmeta(expdir, str->text); 114990792Sgshapiro INTON; 115090792Sgshapiro if (exparg.lastp == savelastp) { 115194334Sgshapiro /* 115294334Sgshapiro * no matches 115394334Sgshapiro */ 115494334Sgshapironometa: 115594334Sgshapiro *exparg.lastp = str; 115694334Sgshapiro rmescapes(str->text); 115794334Sgshapiro exparg.lastp = &str->next; 115890792Sgshapiro } else { 115990792Sgshapiro *exparg.lastp = NULL; 116090792Sgshapiro *savelastp = sp = expsort(*savelastp); 116190792Sgshapiro while (sp->next != NULL) 116290792Sgshapiro sp = sp->next; 116390792Sgshapiro exparg.lastp = &sp->next; 116490792Sgshapiro } 116590792Sgshapiro str = str->next; 116690792Sgshapiro } 116790792Sgshapiro} 116890792Sgshapiro 116990792Sgshapiro 117090792Sgshapiro/* 117190792Sgshapiro * Do metacharacter (i.e. *, ?, [...]) expansion. 117290792Sgshapiro */ 117390792Sgshapiro 117490792Sgshapirostatic void 117590792Sgshapiroexpmeta(char *enddir, char *name) 117690792Sgshapiro{ 117790792Sgshapiro char *p; 117890792Sgshapiro char *q; 117990792Sgshapiro char *start; 118090792Sgshapiro char *endname; 118190792Sgshapiro int metaflag; 118290792Sgshapiro struct stat statb; 118390792Sgshapiro DIR *dirp; 118490792Sgshapiro struct dirent *dp; 118590792Sgshapiro int atend; 118690792Sgshapiro int matchdot; 118790792Sgshapiro int esc; 118890792Sgshapiro 118990792Sgshapiro metaflag = 0; 119090792Sgshapiro start = name; 119190792Sgshapiro for (p = name; esc = 0, *p; p += esc + 1) { 119290792Sgshapiro if (*p == '*' || *p == '?') 119390792Sgshapiro metaflag = 1; 119490792Sgshapiro else if (*p == '[') { 119590792Sgshapiro q = p + 1; 119690792Sgshapiro if (*q == '!' || *q == '^') 119790792Sgshapiro q++; 119890792Sgshapiro for (;;) { 119990792Sgshapiro while (*q == CTLQUOTEMARK) 120090792Sgshapiro q++; 120190792Sgshapiro if (*q == CTLESC) 120290792Sgshapiro q++; 120390792Sgshapiro if (*q == '/' || *q == '\0') 120490792Sgshapiro break; 120590792Sgshapiro if (*++q == ']') { 120690792Sgshapiro metaflag = 1; 120790792Sgshapiro break; 120890792Sgshapiro } 120990792Sgshapiro } 1210 } else if (*p == '\0') 1211 break; 1212 else if (*p == CTLQUOTEMARK) 1213 continue; 1214 else { 1215 if (*p == CTLESC) 1216 esc++; 1217 if (p[esc] == '/') { 1218 if (metaflag) 1219 break; 1220 start = p + esc + 1; 1221 } 1222 } 1223 } 1224 if (metaflag == 0) { /* we've reached the end of the file name */ 1225 if (enddir != expdir) 1226 metaflag++; 1227 for (p = name ; ; p++) { 1228 if (*p == CTLQUOTEMARK) 1229 continue; 1230 if (*p == CTLESC) 1231 p++; 1232 *enddir++ = *p; 1233 if (*p == '\0') 1234 break; 1235 if (enddir == expdir_end) 1236 return; 1237 } 1238 if (metaflag == 0 || lstat(expdir, &statb) >= 0) 1239 addfname(expdir); 1240 return; 1241 } 1242 endname = p; 1243 if (start != name) { 1244 p = name; 1245 while (p < start) { 1246 while (*p == CTLQUOTEMARK) 1247 p++; 1248 if (*p == CTLESC) 1249 p++; 1250 *enddir++ = *p++; 1251 if (enddir == expdir_end) 1252 return; 1253 } 1254 } 1255 if (enddir == expdir) { 1256 p = "."; 1257 } else if (enddir == expdir + 1 && *expdir == '/') { 1258 p = "/"; 1259 } else { 1260 p = expdir; 1261 enddir[-1] = '\0'; 1262 } 1263 if ((dirp = opendir(p)) == NULL) 1264 return; 1265 if (enddir != expdir) 1266 enddir[-1] = '/'; 1267 if (*endname == 0) { 1268 atend = 1; 1269 } else { 1270 atend = 0; 1271 *endname = '\0'; 1272 endname += esc + 1; 1273 } 1274 matchdot = 0; 1275 p = start; 1276 while (*p == CTLQUOTEMARK) 1277 p++; 1278 if (*p == CTLESC) 1279 p++; 1280 if (*p == '.') 1281 matchdot++; 1282 while (! int_pending() && (dp = readdir(dirp)) != NULL) { 1283 if (dp->d_name[0] == '.' && ! matchdot) 1284 continue; 1285 if (patmatch(start, dp->d_name, 0)) { 1286 if (enddir + dp->d_namlen + 1 > expdir_end) 1287 continue; 1288 memcpy(enddir, dp->d_name, dp->d_namlen + 1); 1289 if (atend) 1290 addfname(expdir); 1291 else { 1292 if (enddir + dp->d_namlen + 2 > expdir_end) 1293 continue; 1294 enddir[dp->d_namlen] = '/'; 1295 enddir[dp->d_namlen + 1] = '\0'; 1296 expmeta(enddir + dp->d_namlen + 1, endname); 1297 } 1298 } 1299 } 1300 closedir(dirp); 1301 if (! atend) 1302 endname[-esc - 1] = esc ? CTLESC : '/'; 1303} 1304 1305 1306/* 1307 * Add a file name to the list. 1308 */ 1309 1310static void 1311addfname(char *name) 1312{ 1313 char *p; 1314 struct strlist *sp; 1315 1316 p = stalloc(strlen(name) + 1); 1317 scopy(name, p); 1318 sp = (struct strlist *)stalloc(sizeof *sp); 1319 sp->text = p; 1320 *exparg.lastp = sp; 1321 exparg.lastp = &sp->next; 1322} 1323 1324 1325/* 1326 * Sort the results of file name expansion. It calculates the number of 1327 * strings to sort and then calls msort (short for merge sort) to do the 1328 * work. 1329 */ 1330 1331static struct strlist * 1332expsort(struct strlist *str) 1333{ 1334 int len; 1335 struct strlist *sp; 1336 1337 len = 0; 1338 for (sp = str ; sp ; sp = sp->next) 1339 len++; 1340 return msort(str, len); 1341} 1342 1343 1344static struct strlist * 1345msort(struct strlist *list, int len) 1346{ 1347 struct strlist *p, *q = NULL; 1348 struct strlist **lpp; 1349 int half; 1350 int n; 1351 1352 if (len <= 1) 1353 return list; 1354 half = len >> 1; 1355 p = list; 1356 for (n = half ; --n >= 0 ; ) { 1357 q = p; 1358 p = p->next; 1359 } 1360 q->next = NULL; /* terminate first half of list */ 1361 q = msort(list, half); /* sort first half of list */ 1362 p = msort(p, len - half); /* sort second half */ 1363 lpp = &list; 1364 for (;;) { 1365 if (strcmp(p->text, q->text) < 0) { 1366 *lpp = p; 1367 lpp = &p->next; 1368 if ((p = *lpp) == NULL) { 1369 *lpp = q; 1370 break; 1371 } 1372 } else { 1373 *lpp = q; 1374 lpp = &q->next; 1375 if ((q = *lpp) == NULL) { 1376 *lpp = p; 1377 break; 1378 } 1379 } 1380 } 1381 return list; 1382} 1383 1384 1385 1386static wchar_t 1387get_wc(const char **p) 1388{ 1389 wchar_t c; 1390 int chrlen; 1391 1392 chrlen = mbtowc(&c, *p, 4); 1393 if (chrlen == 0) 1394 return 0; 1395 else if (chrlen == -1) 1396 c = 0; 1397 else 1398 *p += chrlen; 1399 return c; 1400} 1401 1402 1403/* 1404 * Returns true if the pattern matches the string. 1405 */ 1406 1407int 1408patmatch(const char *pattern, const char *string, int squoted) 1409{ 1410 const char *p, *q; 1411 char c; 1412 wchar_t wc, wc2; 1413 1414 p = pattern; 1415 q = string; 1416 for (;;) { 1417 switch (c = *p++) { 1418 case '\0': 1419 goto breakloop; 1420 case CTLESC: 1421 if (squoted && *q == CTLESC) 1422 q++; 1423 if (*q++ != *p++) 1424 return 0; 1425 break; 1426 case CTLQUOTEMARK: 1427 continue; 1428 case '?': 1429 if (squoted && *q == CTLESC) 1430 q++; 1431 if (localeisutf8) 1432 wc = get_wc(&q); 1433 else 1434 wc = (unsigned char)*q++; 1435 if (wc == '\0') 1436 return 0; 1437 break; 1438 case '*': 1439 c = *p; 1440 while (c == CTLQUOTEMARK || c == '*') 1441 c = *++p; 1442 if (c != CTLESC && c != CTLQUOTEMARK && 1443 c != '?' && c != '*' && c != '[') { 1444 while (*q != c) { 1445 if (squoted && *q == CTLESC && 1446 q[1] == c) 1447 break; 1448 if (*q == '\0') 1449 return 0; 1450 if (squoted && *q == CTLESC) 1451 q++; 1452 q++; 1453 } 1454 } 1455 do { 1456 if (patmatch(p, q, squoted)) 1457 return 1; 1458 if (squoted && *q == CTLESC) 1459 q++; 1460 } while (*q++ != '\0'); 1461 return 0; 1462 case '[': { 1463 const char *endp; 1464 int invert, found; 1465 wchar_t chr; 1466 1467 endp = p; 1468 if (*endp == '!' || *endp == '^') 1469 endp++; 1470 for (;;) { 1471 while (*endp == CTLQUOTEMARK) 1472 endp++; 1473 if (*endp == '\0') 1474 goto dft; /* no matching ] */ 1475 if (*endp == CTLESC) 1476 endp++; 1477 if (*++endp == ']') 1478 break; 1479 } 1480 invert = 0; 1481 if (*p == '!' || *p == '^') { 1482 invert++; 1483 p++; 1484 } 1485 found = 0; 1486 if (squoted && *q == CTLESC) 1487 q++; 1488 if (localeisutf8) 1489 chr = get_wc(&q); 1490 else 1491 chr = (unsigned char)*q++; 1492 if (chr == '\0') 1493 return 0; 1494 c = *p++; 1495 do { 1496 if (c == CTLQUOTEMARK) 1497 continue; 1498 if (c == CTLESC) 1499 c = *p++; 1500 if (localeisutf8 && c & 0x80) { 1501 p--; 1502 wc = get_wc(&p); 1503 if (wc == 0) /* bad utf-8 */ 1504 return 0; 1505 } else 1506 wc = (unsigned char)c; 1507 if (*p == '-' && p[1] != ']') { 1508 p++; 1509 while (*p == CTLQUOTEMARK) 1510 p++; 1511 if (*p == CTLESC) 1512 p++; 1513 if (localeisutf8) { 1514 wc2 = get_wc(&p); 1515 if (wc2 == 0) /* bad utf-8 */ 1516 return 0; 1517 } else 1518 wc2 = (unsigned char)*p++; 1519 if ( collate_range_cmp(chr, wc) >= 0 1520 && collate_range_cmp(chr, wc2) <= 0 1521 ) 1522 found = 1; 1523 } else { 1524 if (chr == wc) 1525 found = 1; 1526 } 1527 } while ((c = *p++) != ']'); 1528 if (found == invert) 1529 return 0; 1530 break; 1531 } 1532dft: default: 1533 if (squoted && *q == CTLESC) 1534 q++; 1535 if (*q++ != c) 1536 return 0; 1537 break; 1538 } 1539 } 1540breakloop: 1541 if (*q != '\0') 1542 return 0; 1543 return 1; 1544} 1545 1546 1547 1548/* 1549 * Remove any CTLESC and CTLQUOTEMARK characters from a string. 1550 */ 1551 1552void 1553rmescapes(char *str) 1554{ 1555 char *p, *q; 1556 1557 p = str; 1558 while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) { 1559 if (*p++ == '\0') 1560 return; 1561 } 1562 q = p; 1563 while (*p) { 1564 if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { 1565 p++; 1566 continue; 1567 } 1568 if (*p == CTLESC) 1569 p++; 1570 *q++ = *p++; 1571 } 1572 *q = '\0'; 1573} 1574 1575 1576 1577/* 1578 * See if a pattern matches in a case statement. 1579 */ 1580 1581int 1582casematch(union node *pattern, const char *val) 1583{ 1584 struct stackmark smark; 1585 int result; 1586 char *p; 1587 1588 setstackmark(&smark); 1589 argbackq = pattern->narg.backquote; 1590 STARTSTACKSTR(expdest); 1591 ifslastp = NULL; 1592 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 1593 STPUTC('\0', expdest); 1594 p = grabstackstr(expdest); 1595 result = patmatch(p, val, 0); 1596 popstackmark(&smark); 1597 return result; 1598} 1599 1600/* 1601 * Our own itoa(). 1602 */ 1603 1604static char * 1605cvtnum(int num, char *buf) 1606{ 1607 char temp[32]; 1608 int neg = num < 0; 1609 char *p = temp + 31; 1610 1611 temp[31] = '\0'; 1612 1613 do { 1614 *--p = num % 10 + '0'; 1615 } while ((num /= 10) != 0); 1616 1617 if (neg) 1618 *--p = '-'; 1619 1620 STPUTS(p, buf); 1621 return buf; 1622} 1623 1624/* 1625 * Do most of the work for wordexp(3). 1626 */ 1627 1628int 1629wordexpcmd(int argc, char **argv) 1630{ 1631 size_t len; 1632 int i; 1633 1634 out1fmt("%08x", argc - 1); 1635 for (i = 1, len = 0; i < argc; i++) 1636 len += strlen(argv[i]); 1637 out1fmt("%08x", (int)len); 1638 for (i = 1; i < argc; i++) 1639 outbin(argv[i], strlen(argv[i]) + 1, out1); 1640 return (0); 1641} 1642