11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1991, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 4207944Sjilles * Copyright (c) 1997-2005 5207944Sjilles * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. 61556Srgrimes * 71556Srgrimes * This code is derived from software contributed to Berkeley by 81556Srgrimes * Kenneth Almquist. 91556Srgrimes * 101556Srgrimes * Redistribution and use in source and binary forms, with or without 111556Srgrimes * modification, are permitted provided that the following conditions 121556Srgrimes * are met: 131556Srgrimes * 1. Redistributions of source code must retain the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer. 151556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 161556Srgrimes * notice, this list of conditions and the following disclaimer in the 171556Srgrimes * documentation and/or other materials provided with the distribution. 181556Srgrimes * 4. Neither the name of the University nor the names of its contributors 191556Srgrimes * may be used to endorse or promote products derived from this software 201556Srgrimes * without specific prior written permission. 211556Srgrimes * 221556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 231556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 241556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 251556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 261556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 271556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 281556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 291556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 301556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 311556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 321556Srgrimes * SUCH DAMAGE. 331556Srgrimes */ 341556Srgrimes 351556Srgrimes#ifndef lint 3636150Scharnier#if 0 3736150Scharnierstatic char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; 3836150Scharnier#endif 391556Srgrimes#endif /* not lint */ 4099110Sobrien#include <sys/cdefs.h> 4199110Sobrien__FBSDID("$FreeBSD: stable/10/bin/sh/expand.c 320510 2017-06-30 21:32:48Z jilles $"); 421556Srgrimes 4317987Speter#include <sys/types.h> 4417987Speter#include <sys/time.h> 4517987Speter#include <sys/stat.h> 46213775Sjhb#include <dirent.h> 4717987Speter#include <errno.h> 48213775Sjhb#include <inttypes.h> 49213775Sjhb#include <limits.h> 5017987Speter#include <pwd.h> 51213775Sjhb#include <stdio.h> 5217987Speter#include <stdlib.h> 53108286Stjr#include <string.h> 54213775Sjhb#include <unistd.h> 55221646Sjilles#include <wchar.h> 56223120Sjilles#include <wctype.h> 5717987Speter 581556Srgrimes/* 591556Srgrimes * Routines to expand arguments to commands. We have to deal with 601556Srgrimes * backquotes, shell variables, and file metacharacters. 611556Srgrimes */ 621556Srgrimes 631556Srgrimes#include "shell.h" 641556Srgrimes#include "main.h" 651556Srgrimes#include "nodes.h" 661556Srgrimes#include "eval.h" 671556Srgrimes#include "expand.h" 681556Srgrimes#include "syntax.h" 691556Srgrimes#include "parser.h" 701556Srgrimes#include "jobs.h" 711556Srgrimes#include "options.h" 721556Srgrimes#include "var.h" 731556Srgrimes#include "input.h" 741556Srgrimes#include "output.h" 751556Srgrimes#include "memalloc.h" 761556Srgrimes#include "error.h" 771556Srgrimes#include "mystring.h" 7817987Speter#include "arith.h" 7917987Speter#include "show.h" 80223060Sjilles#include "builtins.h" 811556Srgrimes 821556Srgrimes/* 831556Srgrimes * Structure specifying which parts of the string should be searched 841556Srgrimes * for IFS characters. 851556Srgrimes */ 861556Srgrimes 871556Srgrimesstruct ifsregion { 881556Srgrimes struct ifsregion *next; /* next region in list */ 891556Srgrimes int begoff; /* offset of start of region */ 901556Srgrimes int endoff; /* offset of end of region */ 91194975Sjilles int inquotes; /* search for nul bytes only */ 921556Srgrimes}; 931556Srgrimes 941556Srgrimes 95213760Sobrienstatic char *expdest; /* output of current string */ 96213760Sobrienstatic struct nodelist *argbackq; /* list of back quote expressions */ 97213760Sobrienstatic struct ifsregion ifsfirst; /* first struct in list of ifs regions */ 98213760Sobrienstatic struct ifsregion *ifslastp; /* last struct in list */ 99213760Sobrienstatic struct arglist exparg; /* holds expanded arg list */ 1001556Srgrimes 101264166Sjillesstatic char *argstr(char *, int); 102213811Sobrienstatic char *exptilde(char *, int); 103262951Sjmmvstatic char *expari(char *); 104213811Sobrienstatic void expbackq(union node *, int, int); 105214524Sjillesstatic int subevalvar(char *, char *, int, int, int, int, int); 106213811Sobrienstatic char *evalvar(char *, int); 107264168Sjillesstatic int varisset(const char *, int); 108287751Sjillesstatic void strtodest(const char *, int, int, int); 109264168Sjillesstatic void varvalue(const char *, int, int, int); 110213811Sobrienstatic void recordregion(int, int, int); 111213811Sobrienstatic void removerecordregions(int); 112213811Sobrienstatic void ifsbreakup(char *, struct arglist *); 113287751Sjillesstatic void expandmeta(struct strlist *); 114213811Sobrienstatic void expmeta(char *, char *); 115213811Sobrienstatic void addfname(char *); 116213811Sobrienstatic struct strlist *expsort(struct strlist *); 117213811Sobrienstatic struct strlist *msort(struct strlist *, int); 118229220Sjillesstatic int patmatch(const char *, const char *, int); 119213811Sobrienstatic char *cvtnum(int, char *); 120221646Sjillesstatic int collate_range_cmp(wchar_t, wchar_t); 1211556Srgrimes 122213811Sobrienstatic int 123221646Sjillescollate_range_cmp(wchar_t c1, wchar_t c2) 12419281Sache{ 125221646Sjilles static wchar_t s1[2], s2[2]; 12619281Sache 12719281Sache s1[0] = c1; 12819281Sache s2[0] = c2; 129221646Sjilles return (wcscoll(s1, s2)); 13019281Sache} 13119281Sache 132216384Sjillesstatic char * 133216384Sjillesstputs_quotes(const char *data, const char *syntax, char *p) 134216384Sjilles{ 135216384Sjilles while (*data) { 136216384Sjilles CHECKSTRSPACE(2, p); 137216384Sjilles if (syntax[(int)*data] == CCTL) 138216384Sjilles USTPUTC(CTLESC, p); 139216384Sjilles USTPUTC(*data++, p); 140216384Sjilles } 141216384Sjilles return (p); 142216384Sjilles} 143216384Sjilles#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p) 1441556Srgrimes 1451556Srgrimes/* 146212243Sjilles * Perform expansions on an argument, placing the resulting list of arguments 147212243Sjilles * in arglist. Parameter expansion, command substitution and arithmetic 148212243Sjilles * expansion are always performed; additional expansions can be requested 149212243Sjilles * via flag (EXP_*). 150212243Sjilles * The result is left in the stack string. 151218203Sjilles * When arglist is NULL, perform here document expansion. 152212243Sjilles * 153212243Sjilles * Caution: this function uses global state and is not reentrant. 154212243Sjilles * However, a new invocation after an interrupted invocation is safe 155212243Sjilles * and will reset the global state for the new call. 1561556Srgrimes */ 1571556Srgrimesvoid 15890111Simpexpandarg(union node *arg, struct arglist *arglist, int flag) 15917987Speter{ 1601556Srgrimes struct strlist *sp; 1611556Srgrimes char *p; 1621556Srgrimes 1631556Srgrimes argbackq = arg->narg.backquote; 1641556Srgrimes STARTSTACKSTR(expdest); 1651556Srgrimes ifsfirst.next = NULL; 1661556Srgrimes ifslastp = NULL; 1671556Srgrimes argstr(arg->narg.text, flag); 1681556Srgrimes if (arglist == NULL) { 169222907Sjilles STACKSTRNUL(expdest); 1701556Srgrimes return; /* here document expanded */ 1711556Srgrimes } 1721556Srgrimes STPUTC('\0', expdest); 1731556Srgrimes p = grabstackstr(expdest); 1741556Srgrimes exparg.lastp = &exparg.list; 1751556Srgrimes if (flag & EXP_FULL) { 1761556Srgrimes ifsbreakup(p, &exparg); 1771556Srgrimes *exparg.lastp = NULL; 1781556Srgrimes exparg.lastp = &exparg.list; 179287751Sjilles expandmeta(exparg.list); 1801556Srgrimes } else { 1811556Srgrimes sp = (struct strlist *)stalloc(sizeof (struct strlist)); 1821556Srgrimes sp->text = p; 1831556Srgrimes *exparg.lastp = sp; 1841556Srgrimes exparg.lastp = &sp->next; 1851556Srgrimes } 1861556Srgrimes while (ifsfirst.next != NULL) { 1871556Srgrimes struct ifsregion *ifsp; 1881556Srgrimes INTOFF; 1891556Srgrimes ifsp = ifsfirst.next->next; 1901556Srgrimes ckfree(ifsfirst.next); 1911556Srgrimes ifsfirst.next = ifsp; 1921556Srgrimes INTON; 1931556Srgrimes } 1941556Srgrimes *exparg.lastp = NULL; 1951556Srgrimes if (exparg.list) { 1961556Srgrimes *arglist->lastp = exparg.list; 1971556Srgrimes arglist->lastp = exparg.lastp; 1981556Srgrimes } 1991556Srgrimes} 2001556Srgrimes 2011556Srgrimes 2021556Srgrimes 2031556Srgrimes/* 204212243Sjilles * Perform parameter expansion, command substitution and arithmetic 205212243Sjilles * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE. 206262951Sjmmv * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'. 207212243Sjilles * This is used to expand word in ${var+word} etc. 208276365Sjilles * If EXP_FULL or EXP_CASE are set, keep and/or generate CTLESC 209212243Sjilles * characters to allow for further processing. 210212243Sjilles * If EXP_FULL is set, also preserve CTLQUOTEMARK characters. 2111556Srgrimes */ 212264166Sjillesstatic char * 21390111Simpargstr(char *p, int flag) 21417987Speter{ 21525233Ssteve char c; 216276365Sjilles int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */ 2171556Srgrimes int firsteq = 1; 218214512Sjilles int split_lit; 219214512Sjilles int lit_quoted; 2201556Srgrimes 221214512Sjilles split_lit = flag & EXP_SPLIT_LIT; 222214512Sjilles lit_quoted = flag & EXP_LIT_QUOTED; 223214512Sjilles flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); 2241556Srgrimes if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) 2251556Srgrimes p = exptilde(p, flag); 2261556Srgrimes for (;;) { 227215783Sjilles CHECKSTRSPACE(2, expdest); 2281556Srgrimes switch (c = *p++) { 2291556Srgrimes case '\0': 230264166Sjilles return (p - 1); 231212243Sjilles case CTLENDVAR: 232262951Sjmmv case CTLENDARI: 233264166Sjilles return (p); 23438887Stegge case CTLQUOTEMARK: 235214512Sjilles lit_quoted = 1; 23638887Stegge /* "$@" syntax adherence hack */ 23738887Stegge if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=') 23838887Stegge break; 23939137Stegge if ((flag & EXP_FULL) != 0) 240215783Sjilles USTPUTC(c, expdest); 24138887Stegge break; 242214512Sjilles case CTLQUOTEEND: 243214512Sjilles lit_quoted = 0; 244214512Sjilles break; 2451556Srgrimes case CTLESC: 2461556Srgrimes if (quotes) 247215783Sjilles USTPUTC(c, expdest); 2481556Srgrimes c = *p++; 249215783Sjilles USTPUTC(c, expdest); 250214512Sjilles if (split_lit && !lit_quoted) 251214512Sjilles recordregion(expdest - stackblock() - 252214512Sjilles (quotes ? 2 : 1), 253214512Sjilles expdest - stackblock(), 0); 2541556Srgrimes break; 2551556Srgrimes case CTLVAR: 2561556Srgrimes p = evalvar(p, flag); 2571556Srgrimes break; 2581556Srgrimes case CTLBACKQ: 2591556Srgrimes case CTLBACKQ|CTLQUOTE: 2601556Srgrimes expbackq(argbackq->n, c & CTLQUOTE, flag); 2611556Srgrimes argbackq = argbackq->next; 2621556Srgrimes break; 263262951Sjmmv case CTLARI: 264262951Sjmmv p = expari(p); 2651556Srgrimes break; 2661556Srgrimes case ':': 2671556Srgrimes case '=': 2681556Srgrimes /* 2691556Srgrimes * sort of a hack - expand tildes in variable 2701556Srgrimes * assignments (after the first '=' and after ':'s). 2711556Srgrimes */ 272215783Sjilles USTPUTC(c, expdest); 273214512Sjilles if (split_lit && !lit_quoted) 274214512Sjilles recordregion(expdest - stackblock() - 1, 275214512Sjilles expdest - stackblock(), 0); 276214512Sjilles if (flag & EXP_VARTILDE && *p == '~' && 277214512Sjilles (c != '=' || firsteq)) { 278214512Sjilles if (c == '=') 279214512Sjilles firsteq = 0; 2801556Srgrimes p = exptilde(p, flag); 2811556Srgrimes } 2821556Srgrimes break; 2831556Srgrimes default: 284215783Sjilles USTPUTC(c, expdest); 285214512Sjilles if (split_lit && !lit_quoted) 286214512Sjilles recordregion(expdest - stackblock() - 1, 287214512Sjilles expdest - stackblock(), 0); 2881556Srgrimes } 2891556Srgrimes } 2901556Srgrimes} 2911556Srgrimes 292212243Sjilles/* 293212243Sjilles * Perform tilde expansion, placing the result in the stack string and 294212243Sjilles * returning the next position in the input string to process. 295212243Sjilles */ 296213811Sobrienstatic char * 29790111Simpexptilde(char *p, int flag) 29817987Speter{ 2991556Srgrimes char c, *startp = p; 3001556Srgrimes struct passwd *pw; 3011556Srgrimes char *home; 3021556Srgrimes 303287751Sjilles for (;;) { 304287751Sjilles c = *p; 3051556Srgrimes switch(c) { 306200988Sjilles case CTLESC: /* This means CTL* are always considered quoted. */ 307200988Sjilles case CTLVAR: 308200988Sjilles case CTLBACKQ: 309200988Sjilles case CTLBACKQ | CTLQUOTE: 310200988Sjilles case CTLARI: 311200988Sjilles case CTLENDARI: 31239137Stegge case CTLQUOTEMARK: 31339137Stegge return (startp); 3141556Srgrimes case ':': 315287751Sjilles if ((flag & EXP_VARTILDE) == 0) 316287751Sjilles break; 317287751Sjilles /* FALLTHROUGH */ 318287751Sjilles case '\0': 3191556Srgrimes case '/': 320206150Sjilles case CTLENDVAR: 321287751Sjilles *p = '\0'; 322287751Sjilles if (*(startp+1) == '\0') { 323287751Sjilles home = lookupvar("HOME"); 324287751Sjilles } else { 325287751Sjilles pw = getpwnam(startp+1); 326287751Sjilles home = pw != NULL ? pw->pw_dir : NULL; 327287751Sjilles } 328287751Sjilles *p = c; 329287751Sjilles if (home == NULL || *home == '\0') 330287751Sjilles return (startp); 331287751Sjilles strtodest(home, flag, VSNORMAL, 1); 332287751Sjilles return (p); 3331556Srgrimes } 3341556Srgrimes p++; 3351556Srgrimes } 3361556Srgrimes} 3371556Srgrimes 3381556Srgrimes 339213811Sobrienstatic void 34090111Simpremoverecordregions(int endoff) 34138887Stegge{ 34238887Stegge if (ifslastp == NULL) 34338887Stegge return; 34438887Stegge 34538887Stegge if (ifsfirst.endoff > endoff) { 34638887Stegge while (ifsfirst.next != NULL) { 34738887Stegge struct ifsregion *ifsp; 34838887Stegge INTOFF; 34938887Stegge ifsp = ifsfirst.next->next; 35038887Stegge ckfree(ifsfirst.next); 35138887Stegge ifsfirst.next = ifsp; 35238887Stegge INTON; 35338887Stegge } 35438887Stegge if (ifsfirst.begoff > endoff) 35538887Stegge ifslastp = NULL; 35638887Stegge else { 35738887Stegge ifslastp = &ifsfirst; 35838887Stegge ifsfirst.endoff = endoff; 35938887Stegge } 36038887Stegge return; 36138887Stegge } 362155301Sschweikh 36338887Stegge ifslastp = &ifsfirst; 36438887Stegge while (ifslastp->next && ifslastp->next->begoff < endoff) 36538887Stegge ifslastp=ifslastp->next; 36638887Stegge while (ifslastp->next != NULL) { 36738887Stegge struct ifsregion *ifsp; 36838887Stegge INTOFF; 36938887Stegge ifsp = ifslastp->next->next; 37038887Stegge ckfree(ifslastp->next); 37138887Stegge ifslastp->next = ifsp; 37238887Stegge INTON; 37338887Stegge } 37438887Stegge if (ifslastp->endoff > endoff) 37538887Stegge ifslastp->endoff = endoff; 37638887Stegge} 37738887Stegge 3781556Srgrimes/* 379262951Sjmmv * Expand arithmetic expression. 380262951Sjmmv * Note that flag is not required as digits never require CTLESC characters. 3811556Srgrimes */ 382262951Sjmmvstatic char * 383262951Sjmmvexpari(char *p) 38417987Speter{ 385262951Sjmmv char *q, *start; 386178631Sstefanf arith_t result; 38738887Stegge int begoff; 38838887Stegge int quoted; 389262951Sjmmv int adj; 3901556Srgrimes 391262951Sjmmv quoted = *p++ == '"'; 392262951Sjmmv begoff = expdest - stackblock(); 393264166Sjilles p = argstr(p, 0); 394262951Sjmmv removerecordregions(begoff); 395262951Sjmmv STPUTC('\0', expdest); 396262951Sjmmv start = stackblock() + begoff; 39738887Stegge 398207206Sjilles q = grabstackstr(expdest); 399262951Sjmmv result = arith(start); 400207206Sjilles ungrabstackstr(q, expdest); 401262951Sjmmv 402262951Sjmmv start = stackblock() + begoff; 403262951Sjmmv adj = start - expdest; 404262951Sjmmv STADJUST(adj, expdest); 405262951Sjmmv 406262951Sjmmv CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest); 407262951Sjmmv fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result); 408262951Sjmmv adj = strlen(expdest); 409262951Sjmmv STADJUST(adj, expdest); 410262951Sjmmv if (!quoted) 411262951Sjmmv recordregion(begoff, expdest - stackblock(), 0); 412262951Sjmmv return p; 4131556Srgrimes} 4141556Srgrimes 4151556Srgrimes 4161556Srgrimes/* 417212243Sjilles * Perform command substitution. 4181556Srgrimes */ 419213811Sobrienstatic void 42090111Simpexpbackq(union node *cmd, int quoted, int flag) 42117987Speter{ 4221556Srgrimes struct backcmd in; 4231556Srgrimes int i; 4241556Srgrimes char buf[128]; 4251556Srgrimes char *p; 4261556Srgrimes char *dest = expdest; 4271556Srgrimes struct ifsregion saveifs, *savelastp; 4281556Srgrimes struct nodelist *saveargbackq; 4291556Srgrimes char lastc; 4301556Srgrimes int startloc = dest - stackblock(); 4311556Srgrimes char const *syntax = quoted? DQSYNTAX : BASESYNTAX; 432276365Sjilles int quotes = flag & (EXP_FULL | EXP_CASE); 433248980Sjilles size_t nnl; 4341556Srgrimes 4351556Srgrimes INTOFF; 4361556Srgrimes saveifs = ifsfirst; 4371556Srgrimes savelastp = ifslastp; 4381556Srgrimes saveargbackq = argbackq; 4391556Srgrimes p = grabstackstr(dest); 4401556Srgrimes evalbackcmd(cmd, &in); 4411556Srgrimes ungrabstackstr(p, dest); 4421556Srgrimes 4431556Srgrimes p = in.buf; 4441556Srgrimes lastc = '\0'; 445115424Sfenner nnl = 0; 446115424Sfenner /* Don't copy trailing newlines */ 4471556Srgrimes for (;;) { 4481556Srgrimes if (--in.nleft < 0) { 4491556Srgrimes if (in.fd < 0) 4501556Srgrimes break; 4511556Srgrimes while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); 4521556Srgrimes TRACE(("expbackq: read returns %d\n", i)); 4531556Srgrimes if (i <= 0) 4541556Srgrimes break; 4551556Srgrimes p = buf; 4561556Srgrimes in.nleft = i - 1; 4571556Srgrimes } 4581556Srgrimes lastc = *p++; 4591556Srgrimes if (lastc != '\0') { 460115424Sfenner if (lastc == '\n') { 461115424Sfenner nnl++; 462115424Sfenner } else { 463216706Sjilles CHECKSTRSPACE(nnl + 2, dest); 464115424Sfenner while (nnl > 0) { 465115424Sfenner nnl--; 466216706Sjilles USTPUTC('\n', dest); 467115424Sfenner } 468216496Sjilles if (quotes && syntax[(int)lastc] == CCTL) 469216706Sjilles USTPUTC(CTLESC, dest); 470216706Sjilles USTPUTC(lastc, dest); 471115424Sfenner } 4721556Srgrimes } 4731556Srgrimes } 47417987Speter 4751556Srgrimes if (in.fd >= 0) 4761556Srgrimes close(in.fd); 4771556Srgrimes if (in.buf) 4781556Srgrimes ckfree(in.buf); 479316942Sjilles if (in.jp) { 480316942Sjilles p = grabstackstr(dest); 48145916Scracauer exitstatus = waitforjob(in.jp, (int *)NULL); 482316942Sjilles ungrabstackstr(p, dest); 483316942Sjilles } 484213775Sjhb TRACE(("expbackq: size=%td: \"%.*s\"\n", 485213775Sjhb ((dest - stackblock()) - startloc), 486213775Sjhb (int)((dest - stackblock()) - startloc), 4871556Srgrimes stackblock() + startloc)); 488316942Sjilles ifsfirst = saveifs; 489316942Sjilles ifslastp = savelastp; 490316942Sjilles if (quoted == 0) 491316942Sjilles recordregion(startloc, dest - stackblock(), 0); 492316942Sjilles argbackq = saveargbackq; 4931556Srgrimes expdest = dest; 4941556Srgrimes INTON; 4951556Srgrimes} 4961556Srgrimes 4971556Srgrimes 4981556Srgrimes 499287751Sjillesstatic void 500287751Sjillesrecordleft(const char *str, const char *loc, char *startp) 501287751Sjilles{ 502287751Sjilles int amount; 503287751Sjilles 504287751Sjilles amount = ((str - 1) - (loc - startp)) - expdest; 505287751Sjilles STADJUST(amount, expdest); 506287751Sjilles while (loc != str - 1) 507287751Sjilles *startp++ = *loc++; 508287751Sjilles} 509287751Sjilles 510213811Sobrienstatic int 51190111Simpsubevalvar(char *p, char *str, int strloc, int subtype, int startloc, 512214524Sjilles int varflags, int quotes) 51317987Speter{ 51417987Speter char *startp; 51517987Speter char *loc = NULL; 51645514Stegge char *q; 51717987Speter int c = 0; 51817987Speter struct nodelist *saveargbackq = argbackq; 51920425Ssteve int amount; 52020425Ssteve 521206150Sjilles argstr(p, (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX || 522206147Sjilles subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX ? 523206150Sjilles EXP_CASE : 0) | EXP_TILDE); 52417987Speter STACKSTRNUL(expdest); 52517987Speter argbackq = saveargbackq; 52617987Speter startp = stackblock() + startloc; 52720425Ssteve if (str == NULL) 52820425Ssteve str = stackblock() + strloc; 52917987Speter 53017987Speter switch (subtype) { 53117987Speter case VSASSIGN: 53217987Speter setvar(str, startp, 0); 53320425Ssteve amount = startp - expdest; 53420425Ssteve STADJUST(amount, expdest); 53517987Speter varflags &= ~VSNUL; 53617987Speter return 1; 53717987Speter 53817987Speter case VSQUESTION: 53917987Speter if (*p != CTLENDVAR) { 540201366Sjilles outfmt(out2, "%s\n", startp); 54117987Speter error((char *)NULL); 54217987Speter } 543112254Sru error("%.*s: parameter %snot set", (int)(p - str - 1), 54417987Speter str, (varflags & VSNUL) ? "null or " 54517987Speter : nullstr); 54617987Speter return 0; 54717987Speter 54817987Speter case VSTRIMLEFT: 54925233Ssteve for (loc = startp; loc < str; loc++) { 55017987Speter c = *loc; 55117987Speter *loc = '\0'; 552214524Sjilles if (patmatch(str, startp, quotes)) { 55317987Speter *loc = c; 554287751Sjilles recordleft(str, loc, startp); 555287751Sjilles return 1; 55617987Speter } 55717987Speter *loc = c; 558214524Sjilles if (quotes && *loc == CTLESC) 55945514Stegge loc++; 56017987Speter } 56117987Speter return 0; 56217987Speter 56317987Speter case VSTRIMLEFTMAX: 56445514Stegge for (loc = str - 1; loc >= startp;) { 56517987Speter c = *loc; 56617987Speter *loc = '\0'; 567214524Sjilles if (patmatch(str, startp, quotes)) { 56817987Speter *loc = c; 569287751Sjilles recordleft(str, loc, startp); 570287751Sjilles return 1; 57117987Speter } 57217987Speter *loc = c; 57345514Stegge loc--; 574214524Sjilles if (quotes && loc > startp && *(loc - 1) == CTLESC) { 57545514Stegge for (q = startp; q < loc; q++) 57645514Stegge if (*q == CTLESC) 57745514Stegge q++; 57845514Stegge if (q > loc) 57945514Stegge loc--; 58045514Stegge } 58117987Speter } 58217987Speter return 0; 58317987Speter 58417987Speter case VSTRIMRIGHT: 58545514Stegge for (loc = str - 1; loc >= startp;) { 586214524Sjilles if (patmatch(str, loc, quotes)) { 58720425Ssteve amount = loc - expdest; 58820425Ssteve STADJUST(amount, expdest); 58917987Speter return 1; 59017987Speter } 59145514Stegge loc--; 592214524Sjilles if (quotes && loc > startp && *(loc - 1) == CTLESC) { 59345514Stegge for (q = startp; q < loc; q++) 59445514Stegge if (*q == CTLESC) 59545514Stegge q++; 59645514Stegge if (q > loc) 59745514Stegge loc--; 59845514Stegge } 59917987Speter } 60017987Speter return 0; 60117987Speter 60217987Speter case VSTRIMRIGHTMAX: 60317987Speter for (loc = startp; loc < str - 1; loc++) { 604214524Sjilles if (patmatch(str, loc, quotes)) { 60520425Ssteve amount = loc - expdest; 60620425Ssteve STADJUST(amount, expdest); 60717987Speter return 1; 60817987Speter } 609214524Sjilles if (quotes && *loc == CTLESC) 61045514Stegge loc++; 61117987Speter } 61217987Speter return 0; 61317987Speter 61417987Speter 61517987Speter default: 61617987Speter abort(); 61717987Speter } 61817987Speter} 61917987Speter 62017987Speter 6211556Srgrimes/* 6221556Srgrimes * Expand a variable, and return a pointer to the next character in the 6231556Srgrimes * input string. 6241556Srgrimes */ 6251556Srgrimes 626213811Sobrienstatic char * 62790111Simpevalvar(char *p, int flag) 62817987Speter{ 6291556Srgrimes int subtype; 6301556Srgrimes int varflags; 6311556Srgrimes char *var; 632264168Sjilles const char *val; 63345644Stegge int patloc; 6341556Srgrimes int c; 6351556Srgrimes int set; 6361556Srgrimes int special; 6371556Srgrimes int startloc; 63817987Speter int varlen; 639221602Sjilles int varlenb; 64017987Speter int easy; 641276365Sjilles int quotes = flag & (EXP_FULL | EXP_CASE); 642287751Sjilles int record = 0; 6431556Srgrimes 644164081Sstefanf varflags = (unsigned char)*p++; 6451556Srgrimes subtype = varflags & VSTYPE; 6461556Srgrimes var = p; 6471556Srgrimes special = 0; 6481556Srgrimes if (! is_name(*p)) 6491556Srgrimes special = 1; 6501556Srgrimes p = strchr(p, '=') + 1; 6511556Srgrimesagain: /* jump here after setting a variable with ${var=text} */ 652179022Sstefanf if (varflags & VSLINENO) { 653179022Sstefanf set = 1; 654262951Sjmmv special = 1; 655262951Sjmmv val = NULL; 656179022Sstefanf } else if (special) { 65725233Ssteve set = varisset(var, varflags & VSNUL); 6581556Srgrimes val = NULL; 6591556Srgrimes } else { 66060592Scracauer val = bltinlookup(var, 1); 66117987Speter if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { 6621556Srgrimes val = NULL; 6631556Srgrimes set = 0; 6641556Srgrimes } else 6651556Srgrimes set = 1; 6661556Srgrimes } 66717987Speter varlen = 0; 6681556Srgrimes startloc = expdest - stackblock(); 669198454Sjilles if (!set && uflag && *var != '@' && *var != '*') { 67096939Stjr switch (subtype) { 67196939Stjr case VSNORMAL: 67296939Stjr case VSTRIMLEFT: 67396939Stjr case VSTRIMLEFTMAX: 67496939Stjr case VSTRIMRIGHT: 67596939Stjr case VSTRIMRIGHTMAX: 67696939Stjr case VSLENGTH: 677112254Sru error("%.*s: parameter not set", (int)(p - var - 1), 678112254Sru var); 67996939Stjr } 68096939Stjr } 6811556Srgrimes if (set && subtype != VSPLUS) { 6821556Srgrimes /* insert the value of the variable */ 6831556Srgrimes if (special) { 684262951Sjmmv if (varflags & VSLINENO) 685262951Sjmmv STPUTBIN(var, p - var - 1, expdest); 686262951Sjmmv else 687262951Sjmmv varvalue(var, varflags & VSQUOTE, subtype, flag); 68817987Speter if (subtype == VSLENGTH) { 689221602Sjilles varlenb = expdest - stackblock() - startloc; 690221602Sjilles varlen = varlenb; 691221602Sjilles if (localeisutf8) { 692221602Sjilles val = stackblock() + startloc; 693221602Sjilles for (;val != expdest; val++) 694221602Sjilles if ((*val & 0xC0) == 0x80) 695221602Sjilles varlen--; 696221602Sjilles } 697221602Sjilles STADJUST(-varlenb, expdest); 69817987Speter } 6991556Srgrimes } else { 70017987Speter if (subtype == VSLENGTH) { 70117987Speter for (;*val; val++) 702221602Sjilles if (!localeisutf8 || 703221602Sjilles (*val & 0xC0) != 0x80) 704221602Sjilles varlen++; 7051556Srgrimes } 706287751Sjilles else 707287751Sjilles strtodest(val, flag, subtype, 708287751Sjilles varflags & VSQUOTE); 7091556Srgrimes } 7101556Srgrimes } 71120425Ssteve 7121556Srgrimes if (subtype == VSPLUS) 7131556Srgrimes set = ! set; 71417987Speter 71520425Ssteve easy = ((varflags & VSQUOTE) == 0 || 71617987Speter (*var == '@' && shellparam.nparam != 1)); 71717987Speter 71817987Speter 71917987Speter switch (subtype) { 72017987Speter case VSLENGTH: 72117987Speter expdest = cvtnum(varlen, expdest); 722287751Sjilles record = 1; 723287751Sjilles break; 72417987Speter 72517987Speter case VSNORMAL: 726287751Sjilles record = easy; 72717987Speter break; 72817987Speter 72917987Speter case VSPLUS: 73017987Speter case VSMINUS: 73117987Speter if (!set) { 732214512Sjilles argstr(p, flag | (flag & EXP_FULL ? EXP_SPLIT_LIT : 0) | 733214512Sjilles (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0)); 73417987Speter break; 73517987Speter } 736287751Sjilles record = easy; 73717987Speter break; 73817987Speter 73917987Speter case VSTRIMLEFT: 74017987Speter case VSTRIMLEFTMAX: 74117987Speter case VSTRIMRIGHT: 74217987Speter case VSTRIMRIGHTMAX: 743320510Sjilles if (!set) { 744320510Sjilles set = 1; 74517987Speter break; 746320510Sjilles } 74717987Speter /* 74817987Speter * Terminate the string and start recording the pattern 74917987Speter * right after it 75017987Speter */ 75117987Speter STPUTC('\0', expdest); 75245644Stegge patloc = expdest - stackblock(); 75345644Stegge if (subevalvar(p, NULL, patloc, subtype, 754214524Sjilles startloc, varflags, quotes) == 0) { 75545644Stegge int amount = (expdest - stackblock() - patloc) + 1; 75625233Ssteve STADJUST(-amount, expdest); 75725233Ssteve } 75838887Stegge /* Remove any recorded regions beyond start of variable */ 75938887Stegge removerecordregions(startloc); 760287751Sjilles record = 1; 761287751Sjilles break; 76217987Speter 76317987Speter case VSASSIGN: 76417987Speter case VSQUESTION: 76517987Speter if (!set) { 766214524Sjilles if (subevalvar(p, var, 0, subtype, startloc, varflags, 767214524Sjilles quotes)) { 76820425Ssteve varflags &= ~VSNUL; 769155301Sschweikh /* 770155301Sschweikh * Remove any recorded regions beyond 771155301Sschweikh * start of variable 77238887Stegge */ 77338887Stegge removerecordregions(startloc); 7741556Srgrimes goto again; 77520425Ssteve } 77617987Speter break; 7771556Srgrimes } 778287751Sjilles record = easy; 77917987Speter break; 78017987Speter 781164003Sstefanf case VSERROR: 782164003Sstefanf c = p - var - 1; 783164003Sstefanf error("${%.*s%s}: Bad substitution", c, var, 784164003Sstefanf (c > 0 && *p != CTLENDVAR) ? "..." : ""); 785164003Sstefanf 78617987Speter default: 78717987Speter abort(); 7881556Srgrimes } 78917987Speter 790287751Sjilles if (record) 791287751Sjilles recordregion(startloc, expdest - stackblock(), 792287751Sjilles varflags & VSQUOTE || (ifsset() && ifsval()[0] == '\0' && 793287751Sjilles (*var == '@' || *var == '*'))); 794287751Sjilles 7951556Srgrimes if (subtype != VSNORMAL) { /* skip to end of alternative */ 7961556Srgrimes int nesting = 1; 7971556Srgrimes for (;;) { 7981556Srgrimes if ((c = *p++) == CTLESC) 7991556Srgrimes p++; 8001556Srgrimes else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { 8011556Srgrimes if (set) 8021556Srgrimes argbackq = argbackq->next; 8031556Srgrimes } else if (c == CTLVAR) { 8041556Srgrimes if ((*p++ & VSTYPE) != VSNORMAL) 8051556Srgrimes nesting++; 8061556Srgrimes } else if (c == CTLENDVAR) { 8071556Srgrimes if (--nesting == 0) 8081556Srgrimes break; 8091556Srgrimes } 8101556Srgrimes } 8111556Srgrimes } 8121556Srgrimes return p; 8131556Srgrimes} 8141556Srgrimes 8151556Srgrimes 8161556Srgrimes 8171556Srgrimes/* 8181556Srgrimes * Test whether a specialized variable is set. 8191556Srgrimes */ 8201556Srgrimes 821213811Sobrienstatic int 822264168Sjillesvarisset(const char *name, int nulok) 82325233Ssteve{ 8241556Srgrimes 82525233Ssteve if (*name == '!') 826209600Sjilles return backgndpidset(); 82725233Ssteve else if (*name == '@' || *name == '*') { 8281556Srgrimes if (*shellparam.p == NULL) 8291556Srgrimes return 0; 83025233Ssteve 83125233Ssteve if (nulok) { 83225233Ssteve char **av; 83325233Ssteve 83425233Ssteve for (av = shellparam.p; *av; av++) 83525233Ssteve if (**av != '\0') 83625233Ssteve return 1; 83725233Ssteve return 0; 83825233Ssteve } 83918202Speter } else if (is_digit(*name)) { 84025233Ssteve char *ap; 841275777Sjilles long num; 84225233Ssteve 843275777Sjilles errno = 0; 844275777Sjilles num = strtol(name, NULL, 10); 845275777Sjilles if (errno != 0 || num > shellparam.nparam) 84625233Ssteve return 0; 84725233Ssteve 84825233Ssteve if (num == 0) 84925233Ssteve ap = arg0; 85025233Ssteve else 85125233Ssteve ap = shellparam.p[num - 1]; 85225233Ssteve 85325233Ssteve if (nulok && (ap == NULL || *ap == '\0')) 85425233Ssteve return 0; 8551556Srgrimes } 8561556Srgrimes return 1; 8571556Srgrimes} 8581556Srgrimes 859216384Sjillesstatic void 860216384Sjillesstrtodest(const char *p, int flag, int subtype, int quoted) 861216384Sjilles{ 862276365Sjilles if (flag & (EXP_FULL | EXP_CASE) && subtype != VSLENGTH) 863216384Sjilles STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest); 864216384Sjilles else 865216384Sjilles STPUTS(p, expdest); 866216384Sjilles} 8671556Srgrimes 8681556Srgrimes/* 8691556Srgrimes * Add the value of a specialized variable to the stack string. 8701556Srgrimes */ 8711556Srgrimes 872213811Sobrienstatic void 873264168Sjillesvarvalue(const char *name, int quoted, int subtype, int flag) 87417987Speter{ 8751556Srgrimes int num; 8761556Srgrimes char *p; 8771556Srgrimes int i; 8781556Srgrimes char sep; 8791556Srgrimes char **ap; 8801556Srgrimes 88118202Speter switch (*name) { 8821556Srgrimes case '$': 8831556Srgrimes num = rootpid; 8841556Srgrimes goto numvar; 8851556Srgrimes case '?': 88617987Speter num = oexitstatus; 8871556Srgrimes goto numvar; 8881556Srgrimes case '#': 8891556Srgrimes num = shellparam.nparam; 8901556Srgrimes goto numvar; 8911556Srgrimes case '!': 892209600Sjilles num = backgndpidval(); 8931556Srgrimesnumvar: 89417987Speter expdest = cvtnum(num, expdest); 8951556Srgrimes break; 8961556Srgrimes case '-': 8971556Srgrimes for (i = 0 ; i < NOPTS ; i++) { 8981556Srgrimes if (optlist[i].val) 8991556Srgrimes STPUTC(optlist[i].letter, expdest); 9001556Srgrimes } 9011556Srgrimes break; 9021556Srgrimes case '@': 903164081Sstefanf if (flag & EXP_FULL && quoted) { 90438887Stegge for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 905216384Sjilles strtodest(p, flag, subtype, quoted); 90638887Stegge if (*ap) 90738887Stegge STPUTC('\0', expdest); 90838887Stegge } 90938887Stegge break; 9101556Srgrimes } 911102410Scharnier /* FALLTHROUGH */ 9121556Srgrimes case '*': 913149825Srse if (ifsset()) 91438887Stegge sep = ifsval()[0]; 91538887Stegge else 91638887Stegge sep = ' '; 9171556Srgrimes for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { 918216384Sjilles strtodest(p, flag, subtype, quoted); 919222361Sjilles if (!*ap) 920222361Sjilles break; 921222361Sjilles if (sep || (flag & EXP_FULL && !quoted && **ap != '\0')) 9221556Srgrimes STPUTC(sep, expdest); 9231556Srgrimes } 9241556Srgrimes break; 9251556Srgrimes case '0': 9261556Srgrimes p = arg0; 927216384Sjilles strtodest(p, flag, subtype, quoted); 9281556Srgrimes break; 9291556Srgrimes default: 93018202Speter if (is_digit(*name)) { 93118202Speter num = atoi(name); 93218202Speter if (num > 0 && num <= shellparam.nparam) { 93318202Speter p = shellparam.p[num - 1]; 934216384Sjilles strtodest(p, flag, subtype, quoted); 93518202Speter } 9361556Srgrimes } 9371556Srgrimes break; 9381556Srgrimes } 9391556Srgrimes} 9401556Srgrimes 9411556Srgrimes 9421556Srgrimes 9431556Srgrimes/* 944218909Sbrucec * Record the fact that we have to scan this region of the 9451556Srgrimes * string for IFS characters. 9461556Srgrimes */ 9471556Srgrimes 948213811Sobrienstatic void 949194975Sjillesrecordregion(int start, int end, int inquotes) 95017987Speter{ 95125233Ssteve struct ifsregion *ifsp; 9521556Srgrimes 953264478Sjilles INTOFF; 9541556Srgrimes if (ifslastp == NULL) { 9551556Srgrimes ifsp = &ifsfirst; 9561556Srgrimes } else { 957194975Sjilles if (ifslastp->endoff == start 958194975Sjilles && ifslastp->inquotes == inquotes) { 959194975Sjilles /* extend previous area */ 960194975Sjilles ifslastp->endoff = end; 961264478Sjilles INTON; 962194975Sjilles return; 963194975Sjilles } 9641556Srgrimes ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); 9651556Srgrimes ifslastp->next = ifsp; 9661556Srgrimes } 9671556Srgrimes ifslastp = ifsp; 9681556Srgrimes ifslastp->next = NULL; 9691556Srgrimes ifslastp->begoff = start; 9701556Srgrimes ifslastp->endoff = end; 971194975Sjilles ifslastp->inquotes = inquotes; 972264478Sjilles INTON; 9731556Srgrimes} 9741556Srgrimes 9751556Srgrimes 9761556Srgrimes 9771556Srgrimes/* 9781556Srgrimes * Break the argument string into pieces based upon IFS and add the 9791556Srgrimes * strings to the argument list. The regions of the string to be 9801556Srgrimes * searched for IFS characters have been stored by recordregion. 981212243Sjilles * CTLESC characters are preserved but have little effect in this pass 982212243Sjilles * other than escaping CTL* characters. In particular, they do not escape 983212243Sjilles * IFS characters: that should be done with the ifsregion mechanism. 984212243Sjilles * CTLQUOTEMARK characters are used to preserve empty quoted strings. 985212243Sjilles * This pass treats them as a regular character, making the string non-empty. 986212243Sjilles * Later, they are removed along with the other CTL* characters. 9871556Srgrimes */ 988213811Sobrienstatic void 98990111Simpifsbreakup(char *string, struct arglist *arglist) 99090111Simp{ 9911556Srgrimes struct ifsregion *ifsp; 9921556Srgrimes struct strlist *sp; 9931556Srgrimes char *start; 99425233Ssteve char *p; 9951556Srgrimes char *q; 996201053Sjilles const char *ifs; 997194975Sjilles const char *ifsspc; 998194975Sjilles int had_param_ch = 0; 9991556Srgrimes 1000194975Sjilles start = string; 100117987Speter 1002194975Sjilles if (ifslastp == NULL) { 1003194975Sjilles /* Return entire argument, IFS doesn't apply to any of it */ 1004194975Sjilles sp = (struct strlist *)stalloc(sizeof *sp); 1005194975Sjilles sp->text = start; 1006194975Sjilles *arglist->lastp = sp; 1007194975Sjilles arglist->lastp = &sp->next; 1008194975Sjilles return; 1009194975Sjilles } 1010194975Sjilles 1011194975Sjilles ifs = ifsset() ? ifsval() : " \t\n"; 1012194975Sjilles 1013194975Sjilles for (ifsp = &ifsfirst; ifsp != NULL; ifsp = ifsp->next) { 1014194975Sjilles p = string + ifsp->begoff; 1015194975Sjilles while (p < string + ifsp->endoff) { 1016194975Sjilles q = p; 1017194975Sjilles if (*p == CTLESC) 1018194975Sjilles p++; 1019194975Sjilles if (ifsp->inquotes) { 1020194975Sjilles /* Only NULs (should be from "$@") end args */ 1021194977Sjilles had_param_ch = 1; 1022194975Sjilles if (*p != 0) { 10231556Srgrimes p++; 1024194975Sjilles continue; 1025194975Sjilles } 1026194975Sjilles ifsspc = NULL; 1027194975Sjilles } else { 1028194975Sjilles if (!strchr(ifs, *p)) { 1029194977Sjilles had_param_ch = 1; 103038887Stegge p++; 1031194975Sjilles continue; 1032194975Sjilles } 1033194975Sjilles ifsspc = strchr(" \t\n", *p); 1034194975Sjilles 1035194975Sjilles /* Ignore IFS whitespace at start */ 1036194975Sjilles if (q == start && ifsspc != NULL) { 1037194975Sjilles p++; 10381556Srgrimes start = p; 1039194975Sjilles continue; 1040194975Sjilles } 1041194977Sjilles had_param_ch = 0; 10421556Srgrimes } 1043194975Sjilles 1044194975Sjilles /* Save this argument... */ 1045194975Sjilles *q = '\0'; 10461556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 10471556Srgrimes sp->text = start; 10481556Srgrimes *arglist->lastp = sp; 10491556Srgrimes arglist->lastp = &sp->next; 1050194975Sjilles p++; 1051194975Sjilles 1052194975Sjilles if (ifsspc != NULL) { 1053194975Sjilles /* Ignore further trailing IFS whitespace */ 1054194975Sjilles for (; p < string + ifsp->endoff; p++) { 1055194975Sjilles q = p; 1056194975Sjilles if (*p == CTLESC) 1057194975Sjilles p++; 1058194975Sjilles if (strchr(ifs, *p) == NULL) { 1059194975Sjilles p = q; 1060194975Sjilles break; 1061194975Sjilles } 1062194975Sjilles if (strchr(" \t\n", *p) == NULL) { 1063194975Sjilles p++; 1064194975Sjilles break; 1065194975Sjilles } 1066194975Sjilles } 1067194975Sjilles } 1068194975Sjilles start = p; 10691556Srgrimes } 1070194975Sjilles } 1071194975Sjilles 1072194975Sjilles /* 1073194975Sjilles * Save anything left as an argument. 1074194975Sjilles * Traditionally we have treated 'IFS=':'; set -- x$IFS' as 1075194975Sjilles * generating 2 arguments, the second of which is empty. 1076194975Sjilles * Some recent clarification of the Posix spec say that it 1077194975Sjilles * should only generate one.... 1078194975Sjilles */ 1079194975Sjilles if (had_param_ch || *start != 0) { 10801556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 10811556Srgrimes sp->text = start; 10821556Srgrimes *arglist->lastp = sp; 10831556Srgrimes arglist->lastp = &sp->next; 10841556Srgrimes } 10851556Srgrimes} 10861556Srgrimes 10871556Srgrimes 1088213760Sobrienstatic char expdir[PATH_MAX]; 1089212243Sjilles#define expdir_end (expdir + sizeof(expdir)) 10901556Srgrimes 10911556Srgrimes/* 1092212243Sjilles * Perform pathname generation and remove control characters. 1093212243Sjilles * At this point, the only control characters should be CTLESC and CTLQUOTEMARK. 1094212243Sjilles * The results are stored in the list exparg. 10951556Srgrimes */ 1096213811Sobrienstatic void 1097287751Sjillesexpandmeta(struct strlist *str) 109817987Speter{ 10991556Srgrimes char *p; 11001556Srgrimes struct strlist **savelastp; 11011556Srgrimes struct strlist *sp; 11021556Srgrimes char c; 11031556Srgrimes 11041556Srgrimes while (str) { 11051556Srgrimes if (fflag) 11061556Srgrimes goto nometa; 11071556Srgrimes p = str->text; 11081556Srgrimes for (;;) { /* fast check for meta chars */ 11091556Srgrimes if ((c = *p++) == '\0') 11101556Srgrimes goto nometa; 1111211646Sjilles if (c == '*' || c == '?' || c == '[') 11121556Srgrimes break; 11131556Srgrimes } 11141556Srgrimes savelastp = exparg.lastp; 11151556Srgrimes INTOFF; 11161556Srgrimes expmeta(expdir, str->text); 11171556Srgrimes INTON; 11181556Srgrimes if (exparg.lastp == savelastp) { 11198855Srgrimes /* 11208855Srgrimes * no matches 11211556Srgrimes */ 11221556Srgrimesnometa: 11231556Srgrimes *exparg.lastp = str; 11241556Srgrimes rmescapes(str->text); 11251556Srgrimes exparg.lastp = &str->next; 11261556Srgrimes } else { 11271556Srgrimes *exparg.lastp = NULL; 11281556Srgrimes *savelastp = sp = expsort(*savelastp); 11291556Srgrimes while (sp->next != NULL) 11301556Srgrimes sp = sp->next; 11311556Srgrimes exparg.lastp = &sp->next; 11321556Srgrimes } 11331556Srgrimes str = str->next; 11341556Srgrimes } 11351556Srgrimes} 11361556Srgrimes 11371556Srgrimes 11381556Srgrimes/* 11391556Srgrimes * Do metacharacter (i.e. *, ?, [...]) expansion. 11401556Srgrimes */ 11411556Srgrimes 1142213811Sobrienstatic void 114390111Simpexpmeta(char *enddir, char *name) 114490111Simp{ 1145248980Sjilles const char *p; 1146248980Sjilles const char *q; 1147248980Sjilles const char *start; 11481556Srgrimes char *endname; 11491556Srgrimes int metaflag; 11501556Srgrimes struct stat statb; 11511556Srgrimes DIR *dirp; 11521556Srgrimes struct dirent *dp; 11531556Srgrimes int atend; 11541556Srgrimes int matchdot; 1155207944Sjilles int esc; 1156228941Sjilles int namlen; 11571556Srgrimes 11581556Srgrimes metaflag = 0; 11591556Srgrimes start = name; 1160207944Sjilles for (p = name; esc = 0, *p; p += esc + 1) { 11611556Srgrimes if (*p == '*' || *p == '?') 11621556Srgrimes metaflag = 1; 11631556Srgrimes else if (*p == '[') { 11641556Srgrimes q = p + 1; 116526488Sache if (*q == '!' || *q == '^') 11661556Srgrimes q++; 11671556Srgrimes for (;;) { 116838887Stegge while (*q == CTLQUOTEMARK) 116938887Stegge q++; 11701556Srgrimes if (*q == CTLESC) 11711556Srgrimes q++; 11721556Srgrimes if (*q == '/' || *q == '\0') 11731556Srgrimes break; 11741556Srgrimes if (*++q == ']') { 11751556Srgrimes metaflag = 1; 11761556Srgrimes break; 11771556Srgrimes } 11781556Srgrimes } 11791556Srgrimes } else if (*p == '\0') 11801556Srgrimes break; 118138887Stegge else if (*p == CTLQUOTEMARK) 118238887Stegge continue; 1183207944Sjilles else { 1184207944Sjilles if (*p == CTLESC) 1185207944Sjilles esc++; 1186207944Sjilles if (p[esc] == '/') { 1187207944Sjilles if (metaflag) 1188207944Sjilles break; 1189207944Sjilles start = p + esc + 1; 1190207944Sjilles } 11911556Srgrimes } 11921556Srgrimes } 11931556Srgrimes if (metaflag == 0) { /* we've reached the end of the file name */ 11941556Srgrimes if (enddir != expdir) 11951556Srgrimes metaflag++; 11961556Srgrimes for (p = name ; ; p++) { 119738887Stegge if (*p == CTLQUOTEMARK) 119838887Stegge continue; 11991556Srgrimes if (*p == CTLESC) 12001556Srgrimes p++; 12011556Srgrimes *enddir++ = *p; 12021556Srgrimes if (*p == '\0') 12031556Srgrimes break; 1204211155Sjilles if (enddir == expdir_end) 1205211155Sjilles return; 12061556Srgrimes } 1207147812Sdelphij if (metaflag == 0 || lstat(expdir, &statb) >= 0) 12081556Srgrimes addfname(expdir); 12091556Srgrimes return; 12101556Srgrimes } 1211248980Sjilles endname = name + (p - name); 12121556Srgrimes if (start != name) { 12131556Srgrimes p = name; 12141556Srgrimes while (p < start) { 121538887Stegge while (*p == CTLQUOTEMARK) 121638887Stegge p++; 12171556Srgrimes if (*p == CTLESC) 12181556Srgrimes p++; 12191556Srgrimes *enddir++ = *p++; 1220211155Sjilles if (enddir == expdir_end) 1221211155Sjilles return; 12221556Srgrimes } 12231556Srgrimes } 12241556Srgrimes if (enddir == expdir) { 12251556Srgrimes p = "."; 12261556Srgrimes } else if (enddir == expdir + 1 && *expdir == '/') { 12271556Srgrimes p = "/"; 12281556Srgrimes } else { 12291556Srgrimes p = expdir; 12301556Srgrimes enddir[-1] = '\0'; 12311556Srgrimes } 12321556Srgrimes if ((dirp = opendir(p)) == NULL) 12331556Srgrimes return; 12341556Srgrimes if (enddir != expdir) 12351556Srgrimes enddir[-1] = '/'; 12361556Srgrimes if (*endname == 0) { 12371556Srgrimes atend = 1; 12381556Srgrimes } else { 12391556Srgrimes atend = 0; 1240207944Sjilles *endname = '\0'; 1241207944Sjilles endname += esc + 1; 12421556Srgrimes } 12431556Srgrimes matchdot = 0; 124438887Stegge p = start; 124538887Stegge while (*p == CTLQUOTEMARK) 124638887Stegge p++; 124738887Stegge if (*p == CTLESC) 124838887Stegge p++; 124938887Stegge if (*p == '.') 12501556Srgrimes matchdot++; 12511556Srgrimes while (! int_pending() && (dp = readdir(dirp)) != NULL) { 12521556Srgrimes if (dp->d_name[0] == '.' && ! matchdot) 12531556Srgrimes continue; 125445514Stegge if (patmatch(start, dp->d_name, 0)) { 1255228941Sjilles namlen = dp->d_namlen; 1256228941Sjilles if (enddir + namlen + 1 > expdir_end) 1257211155Sjilles continue; 1258228941Sjilles memcpy(enddir, dp->d_name, namlen + 1); 1259211155Sjilles if (atend) 12601556Srgrimes addfname(expdir); 1261211155Sjilles else { 1262228942Sjilles if (dp->d_type != DT_UNKNOWN && 1263228942Sjilles dp->d_type != DT_DIR && 1264228942Sjilles dp->d_type != DT_LNK) 1265228942Sjilles continue; 1266228941Sjilles if (enddir + namlen + 2 > expdir_end) 126717987Speter continue; 1268228941Sjilles enddir[namlen] = '/'; 1269228941Sjilles enddir[namlen + 1] = '\0'; 1270228941Sjilles expmeta(enddir + namlen + 1, endname); 12711556Srgrimes } 12721556Srgrimes } 12731556Srgrimes } 12741556Srgrimes closedir(dirp); 12751556Srgrimes if (! atend) 1276207944Sjilles endname[-esc - 1] = esc ? CTLESC : '/'; 12771556Srgrimes} 12781556Srgrimes 12791556Srgrimes 12801556Srgrimes/* 12811556Srgrimes * Add a file name to the list. 12821556Srgrimes */ 12831556Srgrimes 1284213811Sobrienstatic void 128590111Simpaddfname(char *name) 128690111Simp{ 12871556Srgrimes char *p; 12881556Srgrimes struct strlist *sp; 12891556Srgrimes 1290297749Sjilles p = stsavestr(name); 12911556Srgrimes sp = (struct strlist *)stalloc(sizeof *sp); 12921556Srgrimes sp->text = p; 12931556Srgrimes *exparg.lastp = sp; 12941556Srgrimes exparg.lastp = &sp->next; 12951556Srgrimes} 12961556Srgrimes 12971556Srgrimes 12981556Srgrimes/* 12991556Srgrimes * Sort the results of file name expansion. It calculates the number of 13001556Srgrimes * strings to sort and then calls msort (short for merge sort) to do the 13011556Srgrimes * work. 13021556Srgrimes */ 13031556Srgrimes 1304213811Sobrienstatic struct strlist * 130590111Simpexpsort(struct strlist *str) 130690111Simp{ 13071556Srgrimes int len; 13081556Srgrimes struct strlist *sp; 13091556Srgrimes 13101556Srgrimes len = 0; 13111556Srgrimes for (sp = str ; sp ; sp = sp->next) 13121556Srgrimes len++; 13131556Srgrimes return msort(str, len); 13141556Srgrimes} 13151556Srgrimes 13161556Srgrimes 1317213811Sobrienstatic struct strlist * 131890111Simpmsort(struct strlist *list, int len) 131917987Speter{ 132017987Speter struct strlist *p, *q = NULL; 13211556Srgrimes struct strlist **lpp; 13221556Srgrimes int half; 13231556Srgrimes int n; 13241556Srgrimes 13251556Srgrimes if (len <= 1) 13261556Srgrimes return list; 13278855Srgrimes half = len >> 1; 13281556Srgrimes p = list; 13291556Srgrimes for (n = half ; --n >= 0 ; ) { 13301556Srgrimes q = p; 13311556Srgrimes p = p->next; 13321556Srgrimes } 13331556Srgrimes q->next = NULL; /* terminate first half of list */ 13341556Srgrimes q = msort(list, half); /* sort first half of list */ 13351556Srgrimes p = msort(p, len - half); /* sort second half */ 13361556Srgrimes lpp = &list; 13371556Srgrimes for (;;) { 13381556Srgrimes if (strcmp(p->text, q->text) < 0) { 13391556Srgrimes *lpp = p; 13401556Srgrimes lpp = &p->next; 13411556Srgrimes if ((p = *lpp) == NULL) { 13421556Srgrimes *lpp = q; 13431556Srgrimes break; 13441556Srgrimes } 13451556Srgrimes } else { 13461556Srgrimes *lpp = q; 13471556Srgrimes lpp = &q->next; 13481556Srgrimes if ((q = *lpp) == NULL) { 13491556Srgrimes *lpp = p; 13501556Srgrimes break; 13511556Srgrimes } 13521556Srgrimes } 13531556Srgrimes } 13541556Srgrimes return list; 13551556Srgrimes} 13561556Srgrimes 13571556Srgrimes 13581556Srgrimes 1359221646Sjillesstatic wchar_t 1360221646Sjillesget_wc(const char **p) 1361221646Sjilles{ 1362221646Sjilles wchar_t c; 1363221646Sjilles int chrlen; 1364221646Sjilles 1365221646Sjilles chrlen = mbtowc(&c, *p, 4); 1366221646Sjilles if (chrlen == 0) 1367221646Sjilles return 0; 1368221646Sjilles else if (chrlen == -1) 1369221646Sjilles c = 0; 1370221646Sjilles else 1371221646Sjilles *p += chrlen; 1372221646Sjilles return c; 1373221646Sjilles} 1374221646Sjilles 1375221646Sjilles 13761556Srgrimes/* 1377223120Sjilles * See if a character matches a character class, starting at the first colon 1378223120Sjilles * of "[:class:]". 1379223120Sjilles * If a valid character class is recognized, a pointer to the next character 1380223120Sjilles * after the final closing bracket is stored into *end, otherwise a null 1381223120Sjilles * pointer is stored into *end. 1382223120Sjilles */ 1383223120Sjillesstatic int 1384223120Sjillesmatch_charclass(const char *p, wchar_t chr, const char **end) 1385223120Sjilles{ 1386223120Sjilles char name[20]; 1387223120Sjilles const char *nameend; 1388223120Sjilles wctype_t cclass; 1389223120Sjilles 1390223120Sjilles *end = NULL; 1391223120Sjilles p++; 1392223120Sjilles nameend = strstr(p, ":]"); 1393248980Sjilles if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) || 1394248980Sjilles nameend == p) 1395223120Sjilles return 0; 1396223120Sjilles memcpy(name, p, nameend - p); 1397223120Sjilles name[nameend - p] = '\0'; 1398223120Sjilles *end = nameend + 2; 1399223120Sjilles cclass = wctype(name); 1400223120Sjilles /* An unknown class matches nothing but is valid nevertheless. */ 1401223120Sjilles if (cclass == 0) 1402223120Sjilles return 0; 1403223120Sjilles return iswctype(chr, cclass); 1404223120Sjilles} 1405223120Sjilles 1406223120Sjilles 1407223120Sjilles/* 14081556Srgrimes * Returns true if the pattern matches the string. 14091556Srgrimes */ 14101556Srgrimes 1411229220Sjillesstatic int 1412200956Sjillespatmatch(const char *pattern, const char *string, int squoted) 141390111Simp{ 1414223120Sjilles const char *p, *q, *end; 1415229201Sjilles const char *bt_p, *bt_q; 141625233Ssteve char c; 1417221646Sjilles wchar_t wc, wc2; 14181556Srgrimes 14191556Srgrimes p = pattern; 14201556Srgrimes q = string; 1421229201Sjilles bt_p = NULL; 1422229201Sjilles bt_q = NULL; 14231556Srgrimes for (;;) { 14241556Srgrimes switch (c = *p++) { 14251556Srgrimes case '\0': 1426229201Sjilles if (*q != '\0') 1427229201Sjilles goto backtrack; 1428229201Sjilles return 1; 14291556Srgrimes case CTLESC: 143045514Stegge if (squoted && *q == CTLESC) 143145514Stegge q++; 14321556Srgrimes if (*q++ != *p++) 1433229201Sjilles goto backtrack; 14341556Srgrimes break; 143538887Stegge case CTLQUOTEMARK: 143638887Stegge continue; 14371556Srgrimes case '?': 143845514Stegge if (squoted && *q == CTLESC) 143945514Stegge q++; 1440229201Sjilles if (*q == '\0') 1441229201Sjilles return 0; 1442229201Sjilles if (localeisutf8) { 1443221646Sjilles wc = get_wc(&q); 1444229201Sjilles /* 1445229201Sjilles * A '?' does not match invalid UTF-8 but a 1446229201Sjilles * '*' does, so backtrack. 1447229201Sjilles */ 1448229201Sjilles if (wc == 0) 1449229201Sjilles goto backtrack; 1450229201Sjilles } else 1451223010Sjilles wc = (unsigned char)*q++; 14521556Srgrimes break; 14531556Srgrimes case '*': 14541556Srgrimes c = *p; 145538887Stegge while (c == CTLQUOTEMARK || c == '*') 145638887Stegge c = *++p; 1457229201Sjilles /* 1458229201Sjilles * If the pattern ends here, we know the string 1459229201Sjilles * matches without needing to look at the rest of it. 1460229201Sjilles */ 1461229201Sjilles if (c == '\0') 1462229201Sjilles return 1; 1463229201Sjilles /* 1464229201Sjilles * First try the shortest match for the '*' that 1465229201Sjilles * could work. We can forget any earlier '*' since 1466229201Sjilles * there is no way having it match more characters 1467229201Sjilles * can help us, given that we are already here. 1468229201Sjilles */ 1469229201Sjilles bt_p = p; 1470229201Sjilles bt_q = q; 1471229201Sjilles break; 14721556Srgrimes case '[': { 1473287752Sjilles const char *savep, *saveq; 14741556Srgrimes int invert, found; 1475221646Sjilles wchar_t chr; 14761556Srgrimes 1477287752Sjilles savep = p, saveq = q; 14781556Srgrimes invert = 0; 147926488Sache if (*p == '!' || *p == '^') { 14801556Srgrimes invert++; 14811556Srgrimes p++; 14821556Srgrimes } 14831556Srgrimes found = 0; 1484221646Sjilles if (squoted && *q == CTLESC) 1485221646Sjilles q++; 1486229201Sjilles if (*q == '\0') 1487229201Sjilles return 0; 1488229201Sjilles if (localeisutf8) { 1489221646Sjilles chr = get_wc(&q); 1490229201Sjilles if (chr == 0) 1491229201Sjilles goto backtrack; 1492229201Sjilles } else 1493223010Sjilles chr = (unsigned char)*q++; 14941556Srgrimes c = *p++; 14951556Srgrimes do { 1496287752Sjilles if (c == '\0') { 1497287752Sjilles p = savep, q = saveq; 1498287752Sjilles c = '['; 1499287752Sjilles goto dft; 1500287752Sjilles } 150138887Stegge if (c == CTLQUOTEMARK) 150238887Stegge continue; 1503223120Sjilles if (c == '[' && *p == ':') { 1504223120Sjilles found |= match_charclass(p, chr, &end); 1505223120Sjilles if (end != NULL) 1506223120Sjilles p = end; 1507223120Sjilles } 15081556Srgrimes if (c == CTLESC) 15091556Srgrimes c = *p++; 1510221646Sjilles if (localeisutf8 && c & 0x80) { 1511221646Sjilles p--; 1512221646Sjilles wc = get_wc(&p); 1513221646Sjilles if (wc == 0) /* bad utf-8 */ 1514221646Sjilles return 0; 1515221646Sjilles } else 1516223010Sjilles wc = (unsigned char)c; 15171556Srgrimes if (*p == '-' && p[1] != ']') { 15181556Srgrimes p++; 151938887Stegge while (*p == CTLQUOTEMARK) 152038887Stegge p++; 15211556Srgrimes if (*p == CTLESC) 15221556Srgrimes p++; 1523221646Sjilles if (localeisutf8) { 1524221646Sjilles wc2 = get_wc(&p); 1525221646Sjilles if (wc2 == 0) /* bad utf-8 */ 1526221646Sjilles return 0; 1527221646Sjilles } else 1528223010Sjilles wc2 = (unsigned char)*p++; 1529221646Sjilles if ( collate_range_cmp(chr, wc) >= 0 1530221646Sjilles && collate_range_cmp(chr, wc2) <= 0 153117525Sache ) 15321556Srgrimes found = 1; 15331556Srgrimes } else { 1534221646Sjilles if (chr == wc) 15351556Srgrimes found = 1; 15361556Srgrimes } 15371556Srgrimes } while ((c = *p++) != ']'); 15381556Srgrimes if (found == invert) 1539229201Sjilles goto backtrack; 15401556Srgrimes break; 15411556Srgrimes } 15421556Srgrimesdft: default: 154345514Stegge if (squoted && *q == CTLESC) 154445514Stegge q++; 1545229201Sjilles if (*q == '\0') 15461556Srgrimes return 0; 1547229201Sjilles if (*q++ == c) 1548229201Sjilles break; 1549229201Sjillesbacktrack: 1550229201Sjilles /* 1551229201Sjilles * If we have a mismatch (other than hitting the end 1552229201Sjilles * of the string), go back to the last '*' seen and 1553229201Sjilles * have it match one additional character. 1554229201Sjilles */ 1555229201Sjilles if (bt_p == NULL) 1556229201Sjilles return 0; 1557229201Sjilles if (squoted && *bt_q == CTLESC) 1558229201Sjilles bt_q++; 1559229201Sjilles if (*bt_q == '\0') 1560229201Sjilles return 0; 1561229201Sjilles bt_q++; 1562229201Sjilles p = bt_p; 1563229201Sjilles q = bt_q; 15641556Srgrimes break; 15651556Srgrimes } 15661556Srgrimes } 15671556Srgrimes} 15681556Srgrimes 15691556Srgrimes 15701556Srgrimes 15711556Srgrimes/* 1572212243Sjilles * Remove any CTLESC and CTLQUOTEMARK characters from a string. 15731556Srgrimes */ 15741556Srgrimes 15751556Srgrimesvoid 157690111Simprmescapes(char *str) 157738887Stegge{ 157825233Ssteve char *p, *q; 15791556Srgrimes 15801556Srgrimes p = str; 1581214512Sjilles while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) { 15821556Srgrimes if (*p++ == '\0') 15831556Srgrimes return; 15841556Srgrimes } 15851556Srgrimes q = p; 15861556Srgrimes while (*p) { 1587214512Sjilles if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { 158838887Stegge p++; 158938887Stegge continue; 159038887Stegge } 15911556Srgrimes if (*p == CTLESC) 15921556Srgrimes p++; 15931556Srgrimes *q++ = *p++; 15941556Srgrimes } 15951556Srgrimes *q = '\0'; 15961556Srgrimes} 15971556Srgrimes 15981556Srgrimes 15991556Srgrimes 16001556Srgrimes/* 16011556Srgrimes * See if a pattern matches in a case statement. 16021556Srgrimes */ 16031556Srgrimes 16041556Srgrimesint 1605200956Sjillescasematch(union node *pattern, const char *val) 160690111Simp{ 16071556Srgrimes struct stackmark smark; 16081556Srgrimes int result; 16091556Srgrimes char *p; 16101556Srgrimes 16111556Srgrimes setstackmark(&smark); 16121556Srgrimes argbackq = pattern->narg.backquote; 16131556Srgrimes STARTSTACKSTR(expdest); 16141556Srgrimes ifslastp = NULL; 16151556Srgrimes argstr(pattern->narg.text, EXP_TILDE | EXP_CASE); 16161556Srgrimes STPUTC('\0', expdest); 16171556Srgrimes p = grabstackstr(expdest); 161845514Stegge result = patmatch(p, val, 0); 16191556Srgrimes popstackmark(&smark); 16201556Srgrimes return result; 16211556Srgrimes} 162217987Speter 162317987Speter/* 162417987Speter * Our own itoa(). 162517987Speter */ 162617987Speter 1627213811Sobrienstatic char * 162890111Simpcvtnum(int num, char *buf) 162990111Simp{ 163017987Speter char temp[32]; 163117987Speter int neg = num < 0; 163217987Speter char *p = temp + 31; 163317987Speter 163417987Speter temp[31] = '\0'; 163517987Speter 163617987Speter do { 163717987Speter *--p = num % 10 + '0'; 163817987Speter } while ((num /= 10) != 0); 163917987Speter 164017987Speter if (neg) 164117987Speter *--p = '-'; 164217987Speter 1643215783Sjilles STPUTS(p, buf); 164417987Speter return buf; 164517987Speter} 1646108286Stjr 1647108286Stjr/* 1648108286Stjr * Do most of the work for wordexp(3). 1649108286Stjr */ 1650108286Stjr 1651108286Stjrint 1652108286Stjrwordexpcmd(int argc, char **argv) 1653108286Stjr{ 1654108286Stjr size_t len; 1655108286Stjr int i; 1656108286Stjr 1657108286Stjr out1fmt("%08x", argc - 1); 1658108286Stjr for (i = 1, len = 0; i < argc; i++) 1659108286Stjr len += strlen(argv[i]); 1660108286Stjr out1fmt("%08x", (int)len); 1661215567Sjilles for (i = 1; i < argc; i++) 1662215567Sjilles outbin(argv[i], strlen(argv[i]) + 1, out1); 1663108286Stjr return (0); 1664108286Stjr} 1665289938Sjilles 1666289938Sjilles/* 1667289938Sjilles * Do most of the work for wordexp(3), new version. 1668289938Sjilles */ 1669289938Sjilles 1670289938Sjillesint 1671289938Sjillesfreebsd_wordexpcmd(int argc __unused, char **argv __unused) 1672289938Sjilles{ 1673289938Sjilles struct arglist arglist; 1674289938Sjilles union node *args, *n; 1675289938Sjilles struct strlist *sp; 1676289938Sjilles size_t count, len; 1677289938Sjilles int ch; 1678289938Sjilles int protected = 0; 1679289938Sjilles int fd = -1; 1680289938Sjilles 1681289938Sjilles while ((ch = nextopt("f:p")) != '\0') { 1682289938Sjilles switch (ch) { 1683289938Sjilles case 'f': 1684289938Sjilles fd = number(shoptarg); 1685289938Sjilles break; 1686289938Sjilles case 'p': 1687289938Sjilles protected = 1; 1688289938Sjilles break; 1689289938Sjilles } 1690289938Sjilles } 1691289938Sjilles if (*argptr != NULL) 1692289938Sjilles error("wrong number of arguments"); 1693289938Sjilles if (fd < 0) 1694289938Sjilles error("missing fd"); 1695289938Sjilles INTOFF; 1696289938Sjilles setinputfd(fd, 1); 1697289938Sjilles INTON; 1698289938Sjilles args = parsewordexp(); 1699289938Sjilles popfile(); /* will also close fd */ 1700289938Sjilles if (protected) 1701289938Sjilles for (n = args; n != NULL; n = n->narg.next) { 1702289938Sjilles if (n->narg.backquote != NULL) { 1703289938Sjilles outcslow('C', out1); 1704289938Sjilles error("command substitution disabled"); 1705289938Sjilles } 1706289938Sjilles } 1707289938Sjilles outcslow(' ', out1); 1708289938Sjilles arglist.lastp = &arglist.list; 1709289938Sjilles for (n = args; n != NULL; n = n->narg.next) 1710289938Sjilles expandarg(n, &arglist, EXP_FULL | EXP_TILDE); 1711289938Sjilles *arglist.lastp = NULL; 1712289938Sjilles for (sp = arglist.list, count = len = 0; sp; sp = sp->next) 1713289938Sjilles count++, len += strlen(sp->text); 1714289938Sjilles out1fmt("%016zx %016zx", count, len); 1715289938Sjilles for (sp = arglist.list; sp; sp = sp->next) 1716289938Sjilles outbin(sp->text, strlen(sp->text) + 1, out1); 1717289938Sjilles return (0); 1718289938Sjilles} 1719