source: trunk/ash/expand.c@ 2764

Last change on this file since 2764 was 2460, checked in by bird, 20 years ago

NetBSD sh 2005-07-03.

File size: 32.1 KB
Line 
1/* $NetBSD: expand.c,v 1.71 2005/06/01 15:41:19 lukem Exp $ */
2
3/*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <sys/cdefs.h>
36#ifndef lint
37#if 0
38static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95";
39#else
40__RCSID("$NetBSD: expand.c,v 1.71 2005/06/01 15:41:19 lukem Exp $");
41#endif
42#endif /* not lint */
43
44#include <sys/types.h>
45#include <sys/time.h>
46#include <sys/stat.h>
47#include <errno.h>
48#include <dirent.h>
49#include <unistd.h>
50#include <pwd.h>
51#include <stdlib.h>
52#include <stdio.h>
53
54/*
55 * Routines to expand arguments to commands. We have to deal with
56 * backquotes, shell variables, and file metacharacters.
57 */
58
59#include "shell.h"
60#include "main.h"
61#include "nodes.h"
62#include "eval.h"
63#include "expand.h"
64#include "syntax.h"
65#include "parser.h"
66#include "jobs.h"
67#include "options.h"
68#include "var.h"
69#include "input.h"
70#include "output.h"
71#include "memalloc.h"
72#include "error.h"
73#include "mystring.h"
74#include "show.h"
75
76/*
77 * Structure specifying which parts of the string should be searched
78 * for IFS characters.
79 */
80
81struct ifsregion {
82 struct ifsregion *next; /* next region in list */
83 int begoff; /* offset of start of region */
84 int endoff; /* offset of end of region */
85 int inquotes; /* search for nul bytes only */
86};
87
88
89char *expdest; /* output of current string */
90struct nodelist *argbackq; /* list of back quote expressions */
91struct ifsregion ifsfirst; /* first struct in list of ifs regions */
92struct ifsregion *ifslastp; /* last struct in list */
93struct arglist exparg; /* holds expanded arg list */
94
95STATIC void argstr(char *, int);
96STATIC char *exptilde(char *, int);
97STATIC void expbackq(union node *, int, int);
98STATIC int subevalvar(char *, char *, int, int, int, int);
99STATIC char *evalvar(char *, int);
100STATIC int varisset(char *, int);
101STATIC void varvalue(char *, int, int, int);
102STATIC void recordregion(int, int, int);
103STATIC void removerecordregions(int);
104STATIC void ifsbreakup(char *, struct arglist *);
105STATIC void ifsfree(void);
106STATIC void expandmeta(struct strlist *, int);
107STATIC void expmeta(char *, char *);
108STATIC void addfname(char *);
109STATIC struct strlist *expsort(struct strlist *);
110STATIC struct strlist *msort(struct strlist *, int);
111STATIC int pmatch(char *, char *, int);
112STATIC char *cvtnum(int, char *);
113
114/*
115 * Expand shell variables and backquotes inside a here document.
116 */
117
118void
119expandhere(union node *arg, int fd)
120{
121 herefd = fd;
122 expandarg(arg, (struct arglist *)NULL, 0);
123 xwrite(fd, stackblock(), expdest - stackblock());
124}
125
126
127/*
128 * Perform variable substitution and command substitution on an argument,
129 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
130 * perform splitting and file name expansion. When arglist is NULL, perform
131 * here document expansion.
132 */
133
134void
135expandarg(union node *arg, struct arglist *arglist, int flag)
136{
137 struct strlist *sp;
138 char *p;
139
140 argbackq = arg->narg.backquote;
141 STARTSTACKSTR(expdest);
142 ifsfirst.next = NULL;
143 ifslastp = NULL;
144 argstr(arg->narg.text, flag);
145 if (arglist == NULL) {
146 return; /* here document expanded */
147 }
148 STPUTC('\0', expdest);
149 p = grabstackstr(expdest);
150 exparg.lastp = &exparg.list;
151 /*
152 * TODO - EXP_REDIR
153 */
154 if (flag & EXP_FULL) {
155 ifsbreakup(p, &exparg);
156 *exparg.lastp = NULL;
157 exparg.lastp = &exparg.list;
158 expandmeta(exparg.list, flag);
159 } else {
160 if (flag & EXP_REDIR) /*XXX - for now, just remove escapes */
161 rmescapes(p);
162 sp = (struct strlist *)stalloc(sizeof (struct strlist));
163 sp->text = p;
164 *exparg.lastp = sp;
165 exparg.lastp = &sp->next;
166 }
167 ifsfree();
168 *exparg.lastp = NULL;
169 if (exparg.list) {
170 *arglist->lastp = exparg.list;
171 arglist->lastp = exparg.lastp;
172 }
173}
174
175
176
177/*
178 * Perform variable and command substitution.
179 * If EXP_FULL is set, output CTLESC characters to allow for further processing.
180 * Otherwise treat $@ like $* since no splitting will be performed.
181 */
182
183STATIC void
184argstr(char *p, int flag)
185{
186 char c;
187 int quotes = flag & (EXP_FULL | EXP_CASE); /* do CTLESC */
188 int firsteq = 1;
189 const char *ifs = NULL;
190 int ifs_split = EXP_IFS_SPLIT;
191
192 if (flag & EXP_IFS_SPLIT)
193 ifs = ifsset() ? ifsval() : " \t\n";
194
195 if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE)))
196 p = exptilde(p, flag);
197 for (;;) {
198 switch (c = *p++) {
199 case '\0':
200 case CTLENDVAR: /* end of expanding yyy in ${xxx-yyy} */
201 return;
202 case CTLQUOTEMARK:
203 /* "$@" syntax adherence hack */
204 if (p[0] == CTLVAR && p[2] == '@' && p[3] == '=')
205 break;
206 if ((flag & EXP_FULL) != 0)
207 STPUTC(c, expdest);
208 ifs_split = 0;
209 break;
210 case CTLQUOTEEND:
211 ifs_split = EXP_IFS_SPLIT;
212 break;
213 case CTLESC:
214 if (quotes)
215 STPUTC(c, expdest);
216 c = *p++;
217 STPUTC(c, expdest);
218 break;
219 case CTLVAR:
220 p = evalvar(p, (flag & ~EXP_IFS_SPLIT) | (flag & ifs_split));
221 break;
222 case CTLBACKQ:
223 case CTLBACKQ|CTLQUOTE:
224 expbackq(argbackq->n, c & CTLQUOTE, flag);
225 argbackq = argbackq->next;
226 break;
227 case CTLENDARI:
228 expari(flag);
229 break;
230 case ':':
231 case '=':
232 /*
233 * sort of a hack - expand tildes in variable
234 * assignments (after the first '=' and after ':'s).
235 */
236 STPUTC(c, expdest);
237 if (flag & EXP_VARTILDE && *p == '~') {
238 if (c == '=') {
239 if (firsteq)
240 firsteq = 0;
241 else
242 break;
243 }
244 p = exptilde(p, flag);
245 }
246 break;
247 default:
248 STPUTC(c, expdest);
249 if (flag & EXP_IFS_SPLIT & ifs_split && strchr(ifs, c) != NULL) {
250 /* We need to get the output split here... */
251 recordregion(expdest - stackblock() - 1,
252 expdest - stackblock(), 0);
253 }
254 break;
255 }
256 }
257}
258
259STATIC char *
260exptilde(char *p, int flag)
261{
262 char c, *startp = p;
263 struct passwd *pw;
264 const char *home;
265 int quotes = flag & (EXP_FULL | EXP_CASE);
266
267 while ((c = *p) != '\0') {
268 switch(c) {
269 case CTLESC:
270 return (startp);
271 case CTLQUOTEMARK:
272 return (startp);
273 case ':':
274 if (flag & EXP_VARTILDE)
275 goto done;
276 break;
277 case '/':
278 goto done;
279 }
280 p++;
281 }
282done:
283 *p = '\0';
284 if (*(startp+1) == '\0') {
285 if ((home = lookupvar("HOME")) == NULL)
286 goto lose;
287 } else {
288 if ((pw = getpwnam(startp+1)) == NULL)
289 goto lose;
290 home = pw->pw_dir;
291 }
292 if (*home == '\0')
293 goto lose;
294 *p = c;
295 while ((c = *home++) != '\0') {
296 if (quotes && SQSYNTAX[(int)c] == CCTL)
297 STPUTC(CTLESC, expdest);
298 STPUTC(c, expdest);
299 }
300 return (p);
301lose:
302 *p = c;
303 return (startp);
304}
305
306
307STATIC void
308removerecordregions(int endoff)
309{
310 if (ifslastp == NULL)
311 return;
312
313 if (ifsfirst.endoff > endoff) {
314 while (ifsfirst.next != NULL) {
315 struct ifsregion *ifsp;
316 INTOFF;
317 ifsp = ifsfirst.next->next;
318 ckfree(ifsfirst.next);
319 ifsfirst.next = ifsp;
320 INTON;
321 }
322 if (ifsfirst.begoff > endoff)
323 ifslastp = NULL;
324 else {
325 ifslastp = &ifsfirst;
326 ifsfirst.endoff = endoff;
327 }
328 return;
329 }
330
331 ifslastp = &ifsfirst;
332 while (ifslastp->next && ifslastp->next->begoff < endoff)
333 ifslastp=ifslastp->next;
334 while (ifslastp->next != NULL) {
335 struct ifsregion *ifsp;
336 INTOFF;
337 ifsp = ifslastp->next->next;
338 ckfree(ifslastp->next);
339 ifslastp->next = ifsp;
340 INTON;
341 }
342 if (ifslastp->endoff > endoff)
343 ifslastp->endoff = endoff;
344}
345
346
347/*
348 * Expand arithmetic expression. Backup to start of expression,
349 * evaluate, place result in (backed up) result, adjust string position.
350 */
351void
352expari(int flag)
353{
354 char *p, *start;
355 int result;
356 int begoff;
357 int quotes = flag & (EXP_FULL | EXP_CASE);
358 int quoted;
359
360 /* ifsfree(); */
361
362 /*
363 * This routine is slightly over-complicated for
364 * efficiency. First we make sure there is
365 * enough space for the result, which may be bigger
366 * than the expression if we add exponentation. Next we
367 * scan backwards looking for the start of arithmetic. If the
368 * next previous character is a CTLESC character, then we
369 * have to rescan starting from the beginning since CTLESC
370 * characters have to be processed left to right.
371 */
372#if INT_MAX / 1000000000 >= 10 || INT_MIN / 1000000000 <= -10
373#error "integers with more than 10 digits are not supported"
374#endif
375 CHECKSTRSPACE(12 - 2, expdest);
376 USTPUTC('\0', expdest);
377 start = stackblock();
378 p = expdest - 1;
379 while (*p != CTLARI && p >= start)
380 --p;
381 if (*p != CTLARI)
382 error("missing CTLARI (shouldn't happen)");
383 if (p > start && *(p-1) == CTLESC)
384 for (p = start; *p != CTLARI; p++)
385 if (*p == CTLESC)
386 p++;
387
388 if (p[1] == '"')
389 quoted=1;
390 else
391 quoted=0;
392 begoff = p - start;
393 removerecordregions(begoff);
394 if (quotes)
395 rmescapes(p+2);
396 result = arith(p+2);
397 fmtstr(p, 12, "%d", result);
398
399 while (*p++)
400 ;
401
402 if (quoted == 0)
403 recordregion(begoff, p - 1 - start, 0);
404 result = expdest - p + 1;
405 STADJUST(-result, expdest);
406}
407
408