Subversion Repositories DevTools

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
/*
2
 * MS-DOS SHELL - 'word' Interpretator
3
 *
4
 * MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited and Charles Forsyth
5
 *
6
 * This code is based on (in part) the shell program written by Charles
7
 * Forsyth and the subsequence modifications made by Simon J. Gerraty (for
8
 * his Public Domain Korn Shell) and is subject to the following copyright
9
 * restrictions:
10
 *
11
 * 1.  Redistribution and use in source and binary forms are permitted
12
 *     provided that the above copyright notice is duplicated in the
13
 *     source form and the copyright notice in file sh6.c is displayed
14
 *     on entry to the program.
15
 *
16
 * 2.  The sources (or parts thereof) or objects generated from the sources
17
 *     (or parts of sources) cannot be sold under any circumstances.
18
 *
19
 *    $Header: /cvsroot/device/DEVL/UTILS/SH/SH4.C,v 1.1 2002/08/02 06:49:32 adamy Exp $
20
 *
21
 *    $Log: SH4.C,v $
22
 *    Revision 1.1  2002/08/02 06:49:32  adamy
23
 *    imported (reference only)
24
 *
25
 *    Revision 1.1  2001/07/20 05:55:42  ayoung
26
 *    WIN32 support
27
 *
28
 *    Revision 1.2  2000/09/27 08:33:35  adamy
29
 *    Added EXTENDED_LINE cache and use __systeml_mode during DOS builds.
30
 *
31
 *    Revision 1.1.1.1  1999/12/02 01:11:12  gordonh
32
 *    UTIL
33
 *
34
 *	Revision 2.13  1994/08/25  20:49:11  istewart
35
 *	MS Shell 2.3 Release
36
 *
37
 *	Revision 2.12  1994/02/01  10:25:20  istewart
38
 *	Release 2.3 Beta 2, including first NT port
39
 *
40
 *	Revision 2.11  1994/01/11  17:55:25  istewart
41
 *	Release 2.3 Beta 0 patches
42
 *
43
 *	Revision 2.10  1993/08/25  16:03:57  istewart
44
 *	Beta 225 - see Notes file
45
 *
46
 *	Revision 2.9  1993/07/02  10:21:35  istewart
47
 *	224 Beta fixes
48
 *
49
 *	Revision 2.8  1993/06/14  11:00:12  istewart
50
 *	More changes for 223 beta
51
 *
52
 *	Revision 2.7  1993/06/02  09:52:35  istewart
53
 *	Beta 223 Updates - see Notes file
54
 *
55
 *	Revision 2.6  1993/02/16  16:03:15  istewart
56
 *	Beta 2.22 Release
57
 *
58
 *	Revision 2.5  1993/01/26  18:35:09  istewart
59
 *	Release 2.2 beta 0
60
 *
61
 *	Revision 2.4  1992/12/14  10:54:56  istewart
62
 *	BETA 215 Fixes and 2.1 Release
63
 *
64
 *	Revision 2.3  1992/11/06  10:03:44  istewart
65
 *	214 Beta test updates
66
 *
67
 *	Revision 2.2  1992/09/03  18:54:45  istewart
68
 *	Beta 213 Updates
69
 *
70
 *	Revision 2.1  1992/07/10  10:52:48  istewart
71
 *	211 Beta updates
72
 *
73
 *	Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
74
 *	MS-Shell 2.0 Baseline release
75
 *
76
 */
77
 
78
#include <sys/types.h>
79
#include <sys/stat.h>
80
#include <stdio.h>
81
#include <limits.h>			/* String library functions     */
82
#include <signal.h>
83
#include <errno.h>
84
#include <setjmp.h>
85
#include <dirent.h>
86
#include <string.h>
87
#include <stdlib.h>
88
#include <unistd.h>
89
#include <ctype.h>
90
#include <fcntl.h>
91
#include "sh.h"
92
 
93
/*
94
 * string expansion
95
 *
96
 * first pass: quoting, IFS separation, ${} and $() substitution.
97
 * second pass: filename expansion (*?[]~).
98
 */
99
 
100
/*
101
 * expansion generator state
102
 */
103
 
104
typedef struct Expand {
105
    /* int  type; */			/* see ExpandAWord()			*/
106
    char	*str;			/* string			*/
107
    union {
108
	char	**strv;			/* string[]			*/
109
	FILE	*file;			/* file				*/
110
    }		u;			/* source			*/
111
 
112
    bool	split;			/* split "$@"			*/
113
} Expand;
114
 
115
#define	XBASE	0		/* scanning original			*/
116
#define	XSUB	1		/* expanding ${} string			*/
117
#define	XARGSEP	2		/* ifs0 between "$@"			*/
118
#define	XARG	3		/* expanding $*, $@			*/
119
#define	XCOM	4		/* expanding $()			*/
120
 
121
/*
122
 * Quote processing
123
 */
124
 
125
#define QUOTE_NONE	0	/* None					*/
126
#define QUOTE_INSIDE	1	/* Inside quotes			*/
127
#define QUOTE_TEMP	2	/* Mark a temporary quote		*/
128
 
129
/*
130
 * for nested substitution: ${var:=$var2}
131
 */
132
 
133
typedef struct SubType {
134
    short	type;		/* [=+-?%#] action after expanded word	*/
135
    short	base;		/* begin position of expanded word	*/
136
    char	*name;		/* name for ${var=word}			*/
137
    int		index;		/* index ${var[index]=word}		*/
138
} SubType;
139
 
140
static void F_LOCAL	ExpandAWord (char *, Word_B **, int);
141
static int F_LOCAL	VariableSubstitute (Expand *, char *, int, int);
142
static int F_LOCAL	CommandSubstitute (Expand *, char *);
143
static char * F_LOCAL	TrimSubstitute (SubType *, char *);
144
static int F_LOCAL	MathsSubstitute (Expand *, char *);
145
static void F_LOCAL	ExpandGlobCharacters (char *, Word_B **);
146
static void F_LOCAL	GlobAWord (char *, char *, char *, Word_B **, bool);
147
static char * F_LOCAL	RemoveMagicMarkers (unsigned char *);
148
static char * F_LOCAL	TildeSubstitution (unsigned char *);
149
static unsigned char * F_LOCAL	CheckForMultipleDrives (unsigned char *);
150
static bool F_LOCAL	ProcessCommandTree (C_Op *, int);
151
static char * F_LOCAL	WordScan (char *, int);
152
static int F_LOCAL	HandleArrayValue (char *, char **, int);
153
static void F_LOCAL	CheckForUnset (char *, int);
154
static void F_LOCAL	AlternationExpand (char *, Word_B **, int);
155
static int F_LOCAL	AlternationScan (char **, char **, char, int);
156
static void		BuildVariableEntryList (const void *, VISIT, int);
157
static int F_LOCAL 	GetNumberofFloppyDrives (void);
158
 
159
static char		*PNullNSet = "%s: parameter null or not set";
160
static char		*GVAV_Name;		/* Name for building a	*/
161
						/* list of Variable	*/
162
						/* Array values		*/
163
static Word_B		*GVAV_WordList;		/* Word block for list	*/
164
 
165
 
166
/*
167
 * compile and expand word
168
 */
169
 
170
char	*substitute (char *cp, int ExpandMode)
171
{
172
    struct source	*sold = source;
173
    char		*res;
174
 
175
    source = pushs (SWSTR);
176
    source->str = (char *) cp;
177
 
178
    if (ScanNextToken (ONEWORD) != PARSE_WORD)
179
	PrintErrorMessage ("eval: substitute error");
180
 
181
    res = ExpandAString (yylval.cp, ExpandMode);
182
    source = sold;
183
    return res;
184
}
185
 
186
/*
187
 * expand arg-list
188
 */
189
 
190
char	**ExpandWordList (char **ap, int ExpandMode, ExeMode *PMode)
191
{
192
    Word_B	*w = (Word_B *)NULL;
193
    bool	FoundProgram = FALSE;
194
    int		i;
195
    int		InitialCount;
196
    char	*CurrentAp;
197
 
198
    if ((ap == NOWORDS) || (*ap == NOWORD))
199
	return ap;
200
 
201
/* Expand the arguments */
202
 
203
    while (*ap != NOWORD)
204
    {
205
	InitialCount = WordBlockSize (w);
206
	ExpandAWord (CurrentAp = *(ap++), &w, ExpandMode);
207
 
208
/*
209
 * Get the program mode for expansion of words and globs.  Update the
210
 * current mode to reflect it.
211
 */
212
	if ((!FoundProgram) && 
213
                (PMode != (ExeMode *)NULL) && WordBlockSize (w))
214
	{
215
	    CheckProgramMode (w->w_words[0], PMode);
216
            FoundProgram = TRUE;                /* One shot config check */
217
 
218
	    if (PMode->Flags & EP_NOEXPAND)
219
		ExpandMode &= ~EXPAND_GLOBBING;
220
 
221
	    if (PMode->Flags & EP_NOWORDS)
222
		ExpandMode &= ~EXPAND_SPLITIFS;
223
 
224
	    if (PMode->Flags & EP_CONVERT)
225
		ExpandMode |= EXPAND_CONVERT;
226
	}
227
 
228
/* Convert UNIX directories to DOS and - to / ?, except for first
229
 * argument
230
 */
231
 
232
	if (ExpandMode & EXPAND_CONVERT)
233
	{
234
	    for (i = max (InitialCount, 1); i < WordBlockSize (w); i++)
235
	    {
236
		PATH_TO_DOS (w->w_words[i]);
237
 
238
/* Convert - to /, if the string is not quoted */
239
 
240
		if ((*(w->w_words[i]) == CHAR_SWITCH) &&
241
		    ((*CurrentAp != WORD_OQUOTE) ||
242
		     (*CurrentAp != WORD_QTCHAR)))
243
		    *(w->w_words[i]) = '/';
244
	    }
245
	}
246
    }
247
 
248
/* Return the word list */
249
 
250
    return GetWordList (AddWordToBlock (NOWORD, w));
251
}
252
 
253
/*
254
 * expand string
255
 */
256
 
257
char	*ExpandAString (char *cp, int ExpandMode)
258
{
259
    Word_B		*w = (Word_B *)NULL;
260
 
261
    ExpandAWord (cp, &w, ExpandMode);
262
 
263
    return (WordBlockSize (w) == 0) ? null : w->w_words[0];
264
}
265
 
266
/*
267
 * expand string - return only one component
268
 * used from iosetup to expand redirection files
269
 */
270
 
271
char	*ExpandOneStringFirstComponent (char *cp, int ExpandMode)
272
{
273
    Word_B		*w = (Word_B *)NULL;
274
 
275
    ExpandAWord (cp, &w, ExpandMode);
276
 
277
    switch (WordBlockSize (w))
278
    {
279
	case 0:
280
	    return null;
281
 
282
	case 1:
283
	    return w->w_words[0];
284
    }
285
 
286
    return ExpandAString (cp, ExpandMode & ~EXPAND_GLOBBING);
287
}
288
 
289
/*
290
 * Expand a word or two!
291
 */
292
 
293
static void F_LOCAL ExpandAWord (char   *OriginalWord,	/* input word	*/
294
				 Word_B **WordList,  /* output word list*/
295
				 int    ExpandMode)  /* Expand Flags	*/
296
{
297
    int			c;
298
    int			type = XBASE;	/* expansion type		*/
299
    int			QuoteStatus = 0;	/* quoted		*/
300
    int			quotestack[11];	/* Keep this bigger than the	*/
301
					/* subtype stack		*/
302
    int			*qst = quotestack + 11;
303
    XString		ds;		/* Expandable destination string*/
304
    unsigned char	*dp;		/* Pointer into Destination S.	*/
305
    char		*sp;		/* source			*/
306
    int			fdo;		/* second pass flags; have word */
307
    int			word;
308
    int			combase;
309
    int			ArrayIndex;
310
    Expand		x;		/* expansion variables */
311
    SubType		subtype [10];	/* substitution type stack */
312
    SubType		*st = subtype + 10;
313
    int			newlines;	/* For trailing newlines in COMSUB */
314
    int			trimming = 0;	/* flag if expanding ${var#pat}	*/
315
					/* or ${var%pat}		*/
316
    char		ifs0 = *GetVariableAsString (IFS, FALSE);
317
 
318
    if (OriginalWord == NULL)
319
	PrintErrorMessage ("eval: expanding a NULL");
320
 
321
    if (FL_TEST (FLAG_DISABLE_GLOB))
322
	ExpandMode &= (~EXPAND_GLOBBING);
323
 
324
/*
325
 * Look for '{' in the input word
326
 */
327
 
328
    if ((ShellGlobalFlags & FLAGS_ALTERNATION) &&
329
	(!(ExpandMode & EXPAND_NOALTS)) &&
330
	(ExpandMode & EXPAND_GLOBBING) &&
331
	((sp = strchr (OriginalWord, CHAR_OPEN_BRACES)) != (char *)NULL) &&
332
	(sp[-1] == WORD_CHAR) &&
333
	(!(sp[1] == WORD_CHAR && sp[2] == CHAR_CLOSE_BRACES)))
334
    {
335
	AlternationExpand (OriginalWord, WordList, ExpandMode);
336
	return;
337
    }
338
 
339
    ExpandMode &= ~EXPAND_NOALTS;
340
 
341
/* Initialise */
342
 
343
    dp = (unsigned char *)XCreate (&ds, 128);	/* destination string	 */
344
    type = XBASE;
345
    sp = OriginalWord;
346
    fdo = 0;
347
    word = !(ExpandMode & EXPAND_SPLITIFS);
348
 
349
/* The Main loop!! */
350
 
351
    while (1)
352
    {
353
	XCheck (&ds, (unsigned char **)(&dp));
354
 
355
	switch (type)
356
	{
357
	    case XBASE:			/* original prefixed string	*/
358
 
359
		switch ((c = *(sp++)))
360
		{
361
		    case WORD_EOS:		/* End - Hurray		*/
362
			c = 0;
363
			break;
364
 
365
		    case WORD_CHAR:		/* Normal char		*/
366
			c = *(sp++);
367
			break;
368
 
369
		    case WORD_QCHAR:		/* Escaped char		*/
370
		    case WORD_QTCHAR:
371
			QuoteStatus |= QUOTE_TEMP;/* temporary quote	*/
372
			c = *(sp++);
373
			break;
374
 
375
		    case WORD_OQUOTE:		/* Start quoted		*/
376
		    case WORD_ODQUOTE:
377
			word = 1;
378
			QuoteStatus = QUOTE_INSIDE;
379
			continue;
380
 
381
		    case WORD_CQUOTE:		/* End quoted		*/
382
		    case WORD_CDQUOTE:
383
			QuoteStatus = QUOTE_NONE;
384
			continue;
385
 
386
		    case WORD_COMSUB:		/* $(....)		*/
387
			type = CommandSubstitute (&x, sp);
388
			sp = strchr (sp, 0) + 1;
389
			combase = XCurrentOffset (ds, dp);
390
			newlines = 0;
391
			continue;
392
 
393
		    case WORD_OMATHS:		/* $((....))		*/
394
			type = MathsSubstitute (&x, sp);
395
			sp = strchr (sp, 0) + 1;
396
			continue;
397
 
398
		    case WORD_OSUBST:	/* ${var{:}[=+-?]word}		*/
399
			OriginalWord = sp; 		/* variable	*/
400
			sp = strchr (sp, 0) + 1;	/* skip variable */
401
 
402
/* Check for Array Variable */
403
 
404
			ArrayIndex = 0;
405
			if (*sp == WORD_OARRAY)
406
			    ArrayIndex = HandleArrayValue (OriginalWord, &sp,
407
							   ExpandMode);
408
 
409
			c = (*sp == WORD_CSUBST) ? 0 : *(sp++);
410
 
411
/* Check for match option */
412
 
413
			if (((c & 0x7f) == CHAR_MATCH_START) ||
414
			    ((c & 0x7f) == CHAR_MATCH_END))
415
			{
416
			    CheckForUnset (OriginalWord, ArrayIndex);
417
			    trimming++;
418
			    type = XBASE;
419
			    *--qst = QuoteStatus;
420
			    QuoteStatus = QUOTE_NONE;
421
			}
422
 
423
			else
424
			    type = VariableSubstitute (&x, OriginalWord,
425
						       c, ArrayIndex);
426
 
427
/* expand? */
428
 
429
			if (type == XBASE)
430
			{
431
			    if (st == subtype)
432
				ShellErrorMessage ("ridiculous ${} nesting");
433
 
434
			    --st;
435
			    st->type = c;
436
			    st->base = XCurrentOffset (ds, dp);
437
			    st->name = OriginalWord;
438
			    st->index = ArrayIndex;
439
			}
440
 
441
			else
442
			    sp = WordScan (sp, WORD_CSUBST); /* skip word */
443
 
444
			continue;
445
 
446
		    case WORD_CSUBST: /* only get here if expanding word */
447
			*dp = 0;
448
 
449
			if (ExpandMode & EXPAND_GLOBBING)
450
			    ExpandMode &= (~EXPAND_PATTERN);
451
 
452
/*
453
 *  Check that full functionality is here!
454
 */
455
			switch (st->type & 0x7f)
456
			{
457
			    case CHAR_MATCH_START:
458
			    case CHAR_MATCH_END:
459
				*dp = 0;
460
				dp = XResetOffset (ds, st->base);
461
 
462
				QuoteStatus = *(qst++);
463
				x.str = TrimSubstitute (st, (char *)dp);
464
				type = XSUB;
465
				trimming--;
466
				continue;
467
 
468
			    case CHAR_ASSIGN:
469
				SetVariableArrayFromString
470
					(st->name, st->index,
471
					 (char *)XResetOffset (ds, st->base));
472
				break;
473
 
474
			    case '?':
475
				if (dp == XResetOffset (ds, st->base))
476
				    ShellErrorMessage (PNullNSet, OriginalWord);
477
 
478
				else
479
				    ShellErrorMessage ("%s",
480
						       XResetOffset (ds,
481
								     st->base));
482
			}
483
 
484
			st++;
485
			type = XBASE;
486
			continue;
487
		}
488
 
489
		break;
490
 
491
	    case XSUB:
492
		if ((c = *(x.str++)) == 0)
493
		{
494
		    type = XBASE;
495
		    continue;
496
		}
497
 
498
		break;
499
 
500
	    case XARGSEP:
501
		type = XARG;
502
		QuoteStatus = QUOTE_INSIDE;
503
 
504
	    case XARG:
505
		if ((c = *(x.str++)) == 0)
506
		{
507
		    if ((x.str = *(x.u.strv++)) == NULL)
508
		    {
509
			type = XBASE;
510
			continue;
511
		    }
512
 
513
		    else if (QuoteStatus && x.split)
514
		    {
515
			type = XARGSEP;		/* terminate word for "$@" */
516
			QuoteStatus = QUOTE_NONE;
517
		    }
518
 
519
		    c = ifs0;
520
		}
521
 
522
		break;
523
 
524
	    case XCOM:
525
		if (newlines)			/* Spit out saved nl's	*/
526
		{
527
		    c = CHAR_NEW_LINE;
528
		    --newlines;
529
		}
530
 
531
		else
532
		{
533
		    while ((c = getc (x.u.file)) == CHAR_NEW_LINE)
534
			newlines++;		/* Save newlines	*/
535
 
536
		    if (newlines && (c != EOF))
537
		    {
538
			ungetc (c, x.u.file);
539
			c = CHAR_NEW_LINE;
540
			--newlines;
541
		    }
542
		}
543
 
544
		if (c == EOF)
545
		{
546
		    OriginalWord = (char *)XResetOffset (ds, combase);
547
		    newlines = 0;
548
		    S_fclose (x.u.file, TRUE);
549
		    type = XBASE;
550
		    continue;
551
		}
552
 
553
		break;
554
	}
555
 
556
/* check for end of word or IFS separation */
557
 
558
	if ((c == 0) || (!QuoteStatus && (ExpandMode & EXPAND_SPLITIFS) &&
559
			 IS_IFS (c)))
560
	{
561
	    if (word)
562
	    {
563
		*(dp++) = 0;
564
		OriginalWord = XClose (&ds, dp);
565
 
566
		if (fdo & EXPAND_TILDE)
567
		    OriginalWord =
568
			TildeSubstitution ((unsigned char *)OriginalWord);
569
 
570
		if (fdo & EXPAND_GLOBBING)
571
		    ExpandGlobCharacters (OriginalWord, WordList);
572
 
573
		else
574
		    *WordList = AddWordToBlock (OriginalWord, *WordList);
575
 
576
/* Re-set */
577
		fdo = 0;
578
		word = 0;
579
 
580
		if (c != 0)
581
		    dp = (unsigned char *)XCreate (&ds, 128);
582
	    }
583
 
584
	    if (c == 0)
585
		return;
586
	}
587
 
588
/*
589
 * Mark any special second pass chars
590
 */
591
	else
592
	{
593
	    if (!QuoteStatus)
594
	    {
595
		switch (c)
596
		{
597
		    case CHAR_MATCH_ALL:
598
		    case CHAR_MATCH_ANY:
599
		    case CHAR_OPEN_BRACKETS:
600
			if ((ExpandMode & (EXPAND_PATTERN | EXPAND_GLOBBING)) ||
601
			    trimming)
602
			{
603
			    fdo |= (ExpandMode & EXPAND_GLOBBING);
604
			    *dp++ = CHAR_MAGIC;
605
			}
606
 
607
			break;
608
 
609
/*
610
 * Check for [^...
611
 */
612
 
613
		    case CHAR_NOT:
614
			if (((ExpandMode & (EXPAND_PATTERN | EXPAND_GLOBBING))
615
			    || trimming) &&
616
			    ((dp[-1] == CHAR_OPEN_BRACKETS) &&
617
			     (dp[-2] == CHAR_MAGIC)))
618
			    *dp++ = CHAR_MAGIC;
619
			break;
620
 
621
		    case CHAR_TILDE:
622
			if (((ExpandMode & EXPAND_TILDE) &&
623
			     (dp == XStart (ds))) ||
624
			    (!(ExpandMode & EXPAND_SPLITIFS) &&
625
			     (dp[-1] == '=' || dp[-1] == ':')))
626
			{
627
			    fdo |= EXPAND_TILDE;
628
			    *dp++ = CHAR_MAGIC;
629
			}
630
 
631
			break;
632
		}
633
	    }
634
 
635
	    else
636
		QuoteStatus &= ~QUOTE_TEMP;	/* undo temporary	*/
637
 
638
	    word = 1;
639
	    *dp++ = (char)c;		/* save output char	*/
640
	}
641
    }
642
}
643
 
644
/*
645
 * Prepare to generate the string returned by ${} substitution.
646
 */
647
 
648
static int F_LOCAL VariableSubstitute (Expand	*xp,
649
				       char	*name,
650
				       int	stype,
651
				       int	Index)
652
{
653
    int		c;
654
    int		type;
655
 
656
/* Handle ${#*|@}
657
 *        ${#name[*]}
658
 *        ${#name[value]}
659
 *
660
 * String length or argc
661
 */
662
 
663
    if ((*name == '#') && ((c = name[1]) != 0))
664
    {
665
	if ((c == '*') || (c == '@'))
666
	    c = ParameterCount - 1;
667
 
668
	else if (Index < 0)
669
	    c = CountVariableArraySize (name + 1);
670
 
671
	else
672
	    c = strlen (GetVariableArrayAsString (name + 1, Index, FALSE));
673
 
674
	xp->str = StringCopy (IntegerToString (c));
675
	return XSUB;
676
    }
677
 
678
    c = *name;
679
 
680
/* Handle ${*|@}
681
 *        ${*|@[*|@]}
682
 *
683
 * Use Parameter list
684
 */
685
 
686
    if (c == '*' || c == '@')
687
    {
688
	if (ParameterCount == 0)
689
	{
690
	    xp->str = null;
691
	    type = XSUB;
692
	}
693
 
694
	else
695
	{
696
	    xp->u.strv = ParameterArray + 1 + ((Index >= 0) ? Index : 0);
697
	    xp->str = *(xp->u.strv++);
698
	    xp->split = C2bool (c == '@');		/* $@ */
699
	    type = XARG;
700
	}
701
    }
702
 
703
/* ${name[*|@]} */
704
 
705
    else if (Index < 0)
706
    {
707
 
708
/* Build list of values */
709
 
710
	if (isdigit (*name))
711
	{
712
	    for (c = 0; isdigit (*name) && (c < 1000); name++)
713
		c = c * 10 + *name - '0';
714
 
715
	    xp->u.strv = (c <= ParameterCount) ? ParameterArray + c
716
					       : NOWORDS;
717
	}
718
 
719
	else
720
	{
721
	    GVAV_Name = name;
722
	    GVAV_WordList = (Word_B *)NULL;
723
	    twalk (VariableTree, BuildVariableEntryList);
724
	    xp->u.strv = WordBlockSize (GVAV_WordList)
725
				? GetWordList (AddWordToBlock (NOWORD,
726
							       GVAV_WordList))
727
				: NOWORDS;
728
	}
729
 
730
/* Set up list.  Check to see if there any any entries */
731
 
732
	if (xp->u.strv == NOWORDS)
733
	{
734
	    xp->str = null;
735
	    type = XSUB;
736
	}
737
 
738
	else
739
	{
740
	    xp->str = *(xp->u.strv++);
741
	    xp->split = C2bool (Index == -2); 		/* ${name[@]} */
742
	    type = XARG;
743
	}
744
    }
745
 
746
/* ${name[num]} */
747
 
748
    else
749
    {
750
	xp->str = GetVariableArrayAsString (name, Index, TRUE);
751
	type = XSUB;
752
    }
753
 
754
    c = stype & 0x7F;
755
 
756
/* test the compiler's code generator */
757
 
758
    if ((c == CHAR_MATCH_END) || (c == CHAR_MATCH_START) ||
759
	(((stype & CHAR_MAGIC) ? (*xp->str == 0)
760
			       : (xp->str == null))
761
		? (c == '=') || (c == '-') || (c == '?')
762
		: (c == '+')))
763
	type = XBASE;	/* expand word instead of variable value */
764
 
765
/* Check for unset value */
766
 
767
    if ((type != XBASE) && FL_TEST (FLAG_UNSET_ERROR) &&
768
    	(xp->str == null) && (c != '+'))
769
	ShellErrorMessage ("unset variable %s", name);
770
 
771
    return type;
772
}
773
 
774
/*
775
 * Run the command in $(...) and read its output.
776
 */
777
 
778
static int F_LOCAL CommandSubstitute (Expand *xp, char *cp)
779
{
780
    Source	*s;
781
    C_Op	*t;
782
    FILE	*fi;
783
    jmp_buf	ReturnPoint;
784
    int		localpipe;
785
 
786
    if ((localpipe = OpenAPipe ()) < 0)
787
	return XBASE;
788
 
789
/* Create a new environment */
790
 
791
    CreateNewEnvironment ();
792
    MemoryAreaLevel++;
793
 
794
    if (SetErrorPoint (ReturnPoint))
795
    {
796
        QuitCurrentEnvironment ();
797
        ReleaseMemoryArea (MemoryAreaLevel--);	/* free old space */
798
	ClearExtendedLineFile ();
799
	S_close (localpipe, TRUE);
800
	return XBASE;
801
    }
802
 
803
/* Create line buffer */
804
 
805
    e.line = GetAllocatedSpace (LINE_MAX);
806
 
807
/* Parse the command */
808
 
809
    s = pushs (SSTRING);
810
    s->str = cp;
811
 
812
/* Check for $(<file) */
813
 
814
    if (((t = BuildParseTree (s)) != (C_Op *)NULL) &&
815
	(t->type == TCOM)     && (*t->args == NOWORD) &&
816
	(*t->vars == NOWORD)  && (t->ioact != (IO_Actions **)NULL))
817
    {
818
	IO_Actions	*io = *t->ioact;
819
	char		*name;
820
 
821
/* We don't need the pipe - so get rid of it */
822
 
823
	S_close (localpipe, TRUE);
824
 
825
/* OK - terminate the created environment */
826
 
827
	QuitCurrentEnvironment ();
828
 
829
	if ((io->io_flag & IOTYPE) != IOREAD)
830
	    ShellErrorMessage ("funny $() command");
831
 
832
	if ((localpipe = S_open (FALSE, name = ExpandAString (io->io_name,
833
							      EXPAND_TILDE),
834
				 O_RMASK)) < 0)
835
	    ShellErrorMessage ("cannot open %s", name);
836
    }
837
 
838
/* Execute the command */
839
 
840
    else
841
    {
842
	if (!ProcessCommandTree (t, localpipe))
843
	    longjmp (ReturnPoint, 1);
844
 
845
	QuitCurrentEnvironment ();
846
    }
847
 
848
/* Open the IO Stream */
849
 
850
    if ((fi = ReOpenFile (ReMapIOHandler (localpipe),
851
    			  sOpenReadMode)) == (FILE *)NULL)
852
	ShellErrorMessage ("cannot open $() input");
853
 
854
/*
855
 * Free old memory area
856
 */
857
 
858
    ReleaseMemoryArea (MemoryAreaLevel--);
859
    xp->u.file = fi;
860
    return XCOM;
861
}
862
 
863
/*
864
 * perform #pattern and %pattern substitution in ${}
865
 */
866
 
867
static char * F_LOCAL TrimSubstitute (SubType *st, char *pat)
868
{
869
    int			mode = GM_SHORTEST;
870
    char		*pos;
871
    char		*tsp;
872
    char		*str = GetVariableArrayAsString (st->name, st->index,
873
							 TRUE);
874
 
875
/*
876
 * Switch on the match type
877
 */
878
 
879
    switch (st->type & 0xff)
880
    {
881
	case CHAR_MATCH_START | CHAR_MAGIC:/* longest match at begin	*/
882
	    mode = GM_LONGEST;
883
 
884
	case CHAR_MATCH_START:		/* shortest at begin		*/
885
	    if (GeneralPatternMatch (str, (unsigned char *)pat, FALSE, &pos, mode))
886
		return pos;
887
 
888
	    break;
889
 
890
	case CHAR_MATCH_END | CHAR_MAGIC:/* longest match at end	*/
891
	    mode = GM_LONGEST;
892
 
893
	case CHAR_MATCH_END:		/* shortest match at end	*/
894
	    if (SuffixPatternMatch (str, pat, &pos, mode))
895
	    {
896
		tsp = StringCopy (str);
897
		tsp[pos - str] = 0;
898
		return tsp;
899
	    }
900
 
901
	    break;
902
 
903
    }
904
 
905
    return str;		/* no match, return string */
906
}
907
 
908
/*
909
 * glob
910
 * Name derived from V6's /etc/glob, the program that expanded filenames.
911
 */
912
 
913
static void F_LOCAL ExpandGlobCharacters (char *Pattern, Word_B **WordList)
914
{
915
    char		path [FFNAME_MAX];
916
    int			oldsize = WordBlockSize (*WordList);
917
    int			newsize;
918
 
919
#if (OS_TYPE != OS_UNIX)
920
    char		*NewPattern;		/* Search file name	*/
921
    int			CurrentDrive;		/* Current drive	*/
922
    int			MaxDrives;		/* Max drive		*/
923
    int			SelectedDrive;		/* Selected drive	*/
924
    int			y_drive;		/* Dummies		*/
925
    unsigned char	*DriveCharacter;	/* Multi-drive flag	*/
926
    char		SDriveString[2];
927
    char		*EndPattern;
928
 
929
/* Search all drives ? */
930
 
931
    if ((DriveCharacter = CheckForMultipleDrives (Pattern))
932
    			!= (unsigned char *)NULL)
933
    {
934
	CurrentDrive = GetCurrentDrive ();
935
	MaxDrives = SetCurrentDrive (CurrentDrive);
936
	SDriveString[1] = 0;
937
	EndPattern = WordScan (Pattern, 0);
938
	NewPattern = GetAllocatedSpace ((EndPattern - Pattern) + 1);
939
 
940
/* Scan the available drives */
941
 
942
	for (SelectedDrive = 1; SelectedDrive <= MaxDrives; ++SelectedDrive)
943
	{
944
	    if (SetCurrentDrive (SelectedDrive) != -1)
945
	    {
946
		y_drive = GetCurrentDrive ();
947
		SetCurrentDrive (CurrentDrive);
948
	    }
949
 
950
	    else
951
	        y_drive = -1;
952
 
953
/* Check to see if the second diskette drive is really there */
954
 
955
	    if ((GetNumberofFloppyDrives () < 2) && (SelectedDrive == 2))
956
		continue;
957
 
958
/* If the drive exists and is in our list - process it */
959
 
960
	    *DriveCharacter = 0;
961
	    *SDriveString = GetDriveLetter (SelectedDrive);
962
	    strlwr (Pattern);
963
 
964
	    if ((y_drive == SelectedDrive) &&
965
		GeneralPatternMatch (SDriveString, Pattern, TRUE, (char **)NULL,
966
				     GM_ALL))
967
	    {
968
		*DriveCharacter = CHAR_DRIVE;
969
		*NewPattern = *SDriveString;
970
		memcpy (NewPattern + 1, DriveCharacter,
971
			((unsigned char *)EndPattern - DriveCharacter) + 1);
972
 
973
		GlobAWord (path, path, NewPattern, WordList, FALSE);
974
	    }
975
 
976
	    *DriveCharacter = CHAR_DRIVE;
977
	}
978
 
979
	ReleaseMemoryCell (NewPattern);
980
    }
981
 
982
/*
983
 * No special processing for drives
984
 */
985
 
986
    else
987
	GlobAWord (path, path, Pattern, WordList, FALSE);
988
#else
989
 
990
/* UNIX has not drives. Goodie! */
991
 
992
    GlobAWord (path, path, Pattern, WordList, FALSE);
993
#endif
994
 
995
/*
996
 * Sort or something
997
 */
998
 
999
    if ((newsize = WordBlockSize (*WordList)) == oldsize)
1000
	*WordList = AddWordToBlock (RemoveMagicMarkers ((unsigned char *)Pattern),
1001
				    *WordList);
1002
 
1003
    else
1004
	qsort (&(*WordList)->w_words[oldsize], (size_t)(newsize - oldsize),
1005
	       sizeof (char *), SortCompare);
1006
}
1007
 
1008
/*
1009
 * Recursive bit
1010
 */
1011
 
1012
static void F_LOCAL GlobAWord (char   *ds,	/* dest path		*/
1013
			       char   *dp,	/* dest end		*/
1014
      			       char   *sp,	/* source path		*/
1015
			       Word_B **WordList,/* output list		*/
1016
			       bool   check)	/* check dest existence */
1017
{
1018
    char		*EndFileName;		/* next source component */
1019
    char		EndChar;
1020
    char		*CFileName;
1021
    char		*tdp;
1022
    DIR			*dirp;
1023
    struct dirent	*d;
1024
    bool		IgnoreCase = TRUE;
1025
 
1026
/* End of source path ? */
1027
 
1028
    if (sp == (char *)NULL)
1029
    {
1030
	if (check && (!S_access (ds, F_OK)))
1031
	    return;
1032
 
1033
	*WordList = AddWordToBlock (StringCopy (ds), *WordList);
1034
	return;
1035
    }
1036
 
1037
    if ((dp > ds) && (!IsDriveCharacter (*(dp - 1))))
1038
	*dp++ = CHAR_UNIX_DIRECTORY;
1039
 
1040
    while (IsPathCharacter (*sp))
1041
	*(dp++) = *(sp++);
1042
 
1043
/* Find end of current file name */
1044
 
1045
    if ((EndFileName = FindPathCharacter (sp)) != (char *)NULL)
1046
    {
1047
	*(EndFileName++) = 0;
1048
	EndChar = CHAR_UNIX_DIRECTORY;
1049
    }
1050
 
1051
#if (OS_TYPE != OS_UNIX)
1052
    if ((tdp = strchr (sp, CHAR_DRIVE)) != (char *)NULL)
1053
    {
1054
	if (EndFileName != (char *)NULL)
1055
	    *(--EndFileName) = CHAR_UNIX_DIRECTORY;
1056
 
1057
	EndFileName = tdp;
1058
	*(EndFileName++) = 0;
1059
	EndChar = CHAR_DRIVE;
1060
    }
1061
#endif
1062
 
1063
    *dp = 0;
1064
 
1065
 /* contains no pattern? */
1066
 
1067
    if (strchr (sp, CHAR_MAGIC) == NULL)
1068
    {
1069
	tdp = dp;
1070
	CFileName = sp;
1071
 
1072
	while ((*(tdp++) = *(CFileName++)) != 0)
1073
	    continue;
1074
 
1075
	if (IsDriveCharacter (EndChar))
1076
	{
1077
	    *(tdp - 1) = CHAR_DRIVE;
1078
	    *tdp = 0;
1079
	}
1080
 
1081
	else
1082
	    --tdp;
1083
 
1084
	GlobAWord (ds, tdp, EndFileName, WordList, check);
1085
    }
1086
 
1087
    else
1088
    {
1089
 
1090
/* Check for drive letter and append a . to get the current directory */
1091
 
1092
	if ((strlen (ds) == 2) && IsDriveCharacter (*(ds + 1)))
1093
	{
1094
	    *(ds + 2) = CHAR_PERIOD;
1095
	    *(ds + 3) = 0;
1096
	}
1097
 
1098
/* Scan the directory */
1099
 
1100
	if ((dirp = opendir ((*ds == 0) ? CurrentDirLiteral
1101
					: ds)) != (DIR *)NULL)
1102
	{
1103
	    if ((IsHPFSFileSystem ((*ds == 0) ? CurrentDirLiteral : ds)) &&
1104
		(!(ShellGlobalFlags & FLAGS_NOCASE)))
1105
		IgnoreCase = FALSE;
1106
 
1107
	    while ((d = readdir (dirp)) != (struct dirent *)NULL)
1108
	    {
1109
		CFileName = d->d_name;
1110
 
1111
/*
1112
 * Ignore . * ..
1113
 */
1114
 
1115
		if ((*CFileName == CHAR_PERIOD) &&
1116
		    ((*(CFileName + 1) == 0) ||
1117
		     ((*(CFileName + 1) == CHAR_PERIOD) &&
1118
		      (*(CFileName + 2) == 0))))
1119
		    continue;
1120
 
1121
/*
1122
 * Ignore . files unless match starts with a dot.
1123
 */
1124
 
1125
		if ((*CFileName == CHAR_PERIOD && *sp != CHAR_PERIOD) ||
1126
		    !GeneralPatternMatch (CFileName, (unsigned char *)sp,
1127
					  IgnoreCase, (char **)NULL, GM_ALL))
1128
		    continue;
1129
 
1130
		tdp = dp;
1131
		while ((*tdp++ = *CFileName++) != 0)
1132
		    continue;
1133
 
1134
		--tdp;
1135
 
1136
		GlobAWord (ds, tdp, EndFileName, WordList,
1137
			   C2bool (EndFileName != NULL));
1138
	    }
1139
 
1140
	    closedir (dirp);
1141
	}
1142
    }
1143
 
1144
    if (EndFileName != NULL)
1145
	*(--EndFileName) = EndChar;
1146
}
1147
 
1148
/*
1149
 * remove MAGIC from string
1150
 */
1151
 
1152
static char * F_LOCAL RemoveMagicMarkers (unsigned char *Word)
1153
{
1154
    unsigned char	*dp, *sp;
1155
 
1156
    for (dp = sp = Word; *sp != 0; sp++)
1157
    {
1158
	if (*sp != CHAR_MAGIC)
1159
	    *dp++ = *sp;
1160
    }
1161
 
1162
    *dp = 0;
1163
    return (char *)Word;
1164
}
1165
 
1166
/*
1167
 * tilde expansion
1168
 *
1169
 * based on a version by Arnold Robbins
1170
 *
1171
 * Think this needs Expanable strings!!
1172
 */
1173
 
1174
static char * F_LOCAL TildeSubstitution (unsigned char *acp)
1175
{
1176
    unsigned		c;
1177
    unsigned char	path[FFNAME_MAX];
1178
    unsigned char	*cp = acp;
1179
    unsigned char	*wp = path;
1180
    unsigned char	*dp;
1181
 
1182
    while (TRUE)
1183
    {
1184
	while (TRUE)
1185
	{
1186
	    if ((c = *cp++) == 0)
1187
	    {
1188
		*wp = 0;
1189
		ReleaseMemoryCell ((void *)acp);
1190
		return StringCopy ((char *)path);
1191
	    }
1192
 
1193
	    else if ((c == CHAR_MAGIC) && (*cp == CHAR_TILDE))
1194
		break;
1195
 
1196
	    else
1197
		*wp++ = (char)c;
1198
	}
1199
 
1200
	dp = NULL;	/* no output substitution */
1201
 
1202
 /*
1203
  * ~ or ~/
1204
  */
1205
 
1206
	if ((cp[1] == 0) || IsPathCharacter (cp[1]) || (IsDriveCharacter (cp[1])))
1207
	{
1208
	    dp = (unsigned char *)GetVariableAsString (HomeVariableName, FALSE);
1209
	    cp += 1;
1210
	}
1211
 
1212
	else if ((cp[1] == '+') && (IsPathCharacter (cp[2]) ||
1213
				    IsDriveCharacter (cp[2]) || (cp[2] == 0)))
1214
	{
1215
	    dp = (unsigned char *)GetVariableAsString (PWDVariable, FALSE);
1216
	    cp += 2;
1217
	}
1218
 
1219
	else if ((cp[1] == '-') && (IsPathCharacter (cp[2]) ||
1220
				    IsDriveCharacter (cp[2]) || (cp[2] == 0)))
1221
	{
1222
	    dp = (unsigned char *)GetVariableAsString (OldPWDVariable, FALSE);
1223
	    cp += 2;
1224
	}
1225
 
1226
/* substitute */
1227
 
1228
	if (dp != NULL)
1229
	{
1230
	    while (*dp != 0)
1231
		*wp++ = *dp++;
1232
 
1233
/* Remove double //'s on directories */
1234
 
1235
	    if (IsPathCharacter (*(wp - 1)) && IsPathCharacter (*cp))
1236
		cp++;
1237
	}
1238
    }
1239
}
1240
 
1241
/*
1242
 * Sort Compare
1243
 */
1244
 
1245
extern int _CDECL SortCompare (const void *a1, const void *a2)
1246
{
1247
    return strcmp (*((char **)a1), *((char **)a2));
1248
}
1249
 
1250
/*
1251
 * Return the position of Prefix StopWord in the quoted string
1252
 */
1253
 
1254
static char * F_LOCAL WordScan (char *QString, int StopWord)
1255
{
1256
    int		VarSubNest = 0;
1257
 
1258
    while (TRUE)
1259
    {
1260
	switch (*(QString++))
1261
	{
1262
	    case WORD_EOS:
1263
		return QString;
1264
 
1265
	    case WORD_CHAR:
1266
	    case WORD_QCHAR:
1267
	    case WORD_QTCHAR:
1268
		QString++;
1269
		break;
1270
 
1271
	    case WORD_OQUOTE:
1272
	    case WORD_ODQUOTE:
1273
	    case WORD_CQUOTE:
1274
	    case WORD_CDQUOTE:
1275
		break;
1276
 
1277
	    case WORD_OARRAY:
1278
		VarSubNest++;
1279
		break;
1280
 
1281
	    case WORD_CARRAY:
1282
		if ((StopWord == WORD_CARRAY) && (VarSubNest == 0))
1283
		    return QString;
1284
 
1285
		VarSubNest--;
1286
		break;
1287
 
1288
	    case WORD_OSUBST:
1289
		VarSubNest++;
1290
 
1291
		while (*(QString++) != 0)
1292
		    continue;
1293
 
1294
		if (*QString != WORD_CSUBST)
1295
		    QString++;
1296
 
1297
		break;
1298
 
1299
	    case WORD_CSUBST:
1300
		if ((StopWord == WORD_CSUBST) && (VarSubNest == 0))
1301
		    return QString;
1302
 
1303
		VarSubNest--;
1304
		break;
1305
 
1306
	    case WORD_COMSUB:
1307
	    case WORD_OMATHS:
1308
		while (*(QString++) != 0)
1309
		    continue;
1310
 
1311
		break;
1312
	}
1313
    }
1314
}
1315
 
1316
/*
1317
 * Maths substitute - convert $((....)) to a number.
1318
 */
1319
 
1320
static int F_LOCAL MathsSubstitute (Expand *xp, char *sp)
1321
{
1322
    char	DecimalString[12];
1323
    char	*esp;
1324
 
1325
    esp = substitute (sp, 0);
1326
    sprintf (DecimalString, "%lu", EvaluateMathsExpression (esp));
1327
    xp->str = StringCopy (DecimalString);
1328
    return XSUB;
1329
}
1330
 
1331
 
1332
/*
1333
 * Check for multi_drive prefix
1334
 */
1335
 
1336
#if (OS_TYPE != OS_UNIX)
1337
static unsigned char * F_LOCAL CheckForMultipleDrives (unsigned char *prefix)
1338
{
1339
    if ((*(prefix++) != CHAR_MAGIC) || (!IS_WildCard (*prefix)))
1340
	return (unsigned char *)NULL;
1341
 
1342
    if (*prefix != CHAR_OPEN_BRACKETS)
1343
	return *(prefix + 1) == CHAR_DRIVE ? prefix + 1 : (unsigned char *)NULL;
1344
 
1345
    while (*prefix && (*prefix != CHAR_CLOSE_BRACKETS))
1346
    {
1347
	if ((*prefix == CHAR_MATCH_RANGE) && (*(prefix + 1)))
1348
	    ++prefix;
1349
 
1350
	++prefix;
1351
    }
1352
 
1353
    return (*prefix && (*(prefix + 1) == CHAR_DRIVE))
1354
		? prefix + 1 : (unsigned char *)NULL;
1355
}
1356
#endif
1357
 
1358
/*
1359
 * A command tree is to be expanded for stdin
1360
 */
1361
 
1362
static bool F_LOCAL ProcessCommandTree (C_Op *outtree, int localpipe)
1363
{
1364
    long		s_flags = flags;
1365
    Break_C		*S_RList = Return_List;	/* Save loval links	*/
1366
    Break_C		*S_BList = Break_List;
1367
    Break_C		*S_SList = SShell_List;
1368
    bool		s_ProcessingEXECCommand = ProcessingEXECCommand;
1369
    int			Local_depth = Execute_stack_depth++;
1370
    jmp_buf		ReturnPoint;
1371
    int			ReturnValue;
1372
    FunctionList	*s_CurrentFunction = CurrentFunction;
1373
    Break_C		bc;
1374
 
1375
/* Create the pipe to read the output from the command string */
1376
 
1377
    S_dup2 (localpipe, 1);
1378
 
1379
    FL_CLEAR (FLAG_EXIT_ON_ERROR);
1380
    FL_CLEAR (FLAG_ECHO_INPUT);
1381
    FL_CLEAR (FLAG_NO_EXECUTE);
1382
 
1383
/* Set up new environment */
1384
 
1385
    ReturnValue = CreateGlobalVariableList (FLAGS_NONE);
1386
 
1387
    if ((ReturnValue != -1) && (!SetErrorPoint (ReturnPoint)))
1388
    {
1389
	Return_List = (Break_C *)NULL;
1390
	Break_List  = (Break_C *)NULL;
1391
 
1392
/* Clear execute flags.  */
1393
 
1394
	ProcessingEXECCommand = TRUE;
1395
	CurrentFunction = (FunctionList *)NULL;
1396
 
1397
/* Parse the line and execute it */
1398
 
1399
	if (setjmp (bc.CurrentReturnPoint) == 0)
1400
	{
1401
	    bc.NextExitLevel = SShell_List;
1402
	    SShell_List = &bc;
1403
	    ReturnValue = ExecuteParseTree (outtree, NOPIPE, NOPIPE, 0);
1404
	}
1405
 
1406
/* Parse error */
1407
 
1408
	else
1409
	    ReturnValue = -1;
1410
 
1411
/* Clean up any files around we nolonger need */
1412
 
1413
	ClearExtendedLineFile ();
1414
    }
1415
 
1416
    else
1417
	ReturnValue = -1;
1418
 
1419
/* Restore environment */
1420
 
1421
    RestoreEnvironment (ReturnValue, Local_depth);
1422
 
1423
/* Free old space */
1424
 
1425
    FreeAllHereDocuments (MemoryAreaLevel);
1426
 
1427
/* Ok - completed processing - restore environment and read the pipe */
1428
 
1429
    ProcessingEXECCommand = s_ProcessingEXECCommand;
1430
    flags = s_flags;
1431
    Return_List = S_RList;
1432
    Break_List	= S_BList;
1433
    SShell_List = S_SList;
1434
    CurrentFunction = s_CurrentFunction;
1435
 
1436
/* Move pipe to start so we can read it */
1437
 
1438
    lseek (localpipe, 0L, SEEK_SET);
1439
    return C2bool (ReturnValue != -1);
1440
}
1441
 
1442
/*	(pc@hillside.co.uk)
1443
 * I have decided to `fudge' alternations by picking up the compiled command
1444
 * tree and working with it recursively to generate the set of arguments.
1445
 * This has the advantage of making a single discrete change to the code
1446
 *
1447
 * This routine calls itself recursively
1448
 *
1449
 *	a) Scan forward looking for { building the output string if none found
1450
 *	   then call expand - and exit
1451
 *	b) When { found, scan forward finding the end }
1452
 *	c) Add first alternate to output string
1453
 *	d) Scan for the end of the string copying into output
1454
 *	e) Call routine with new string
1455
 *
1456
 * Major complication is quoting
1457
 */
1458
 
1459
static void F_LOCAL AlternationExpand (char   *cp,	/* input word	*/
1460
				       Word_B **WordList,/* output words	*/
1461
				       int    ExpandMode)/* DO* flags	*/
1462
{
1463
    char	*srcp = cp;
1464
    char	*left;		/* destination string of left hand side	*/
1465
    char	*leftend;	/* end of left hand side		*/
1466
    char	*alt;		/* start of alterate section		*/
1467
    char	*altend;	/* end of alternate section		*/
1468
    char	*ap;		/* working pointer			*/
1469
    char	*right;		/* right hand side			*/
1470
    char	*rp;		/* used to copy right-hand side		*/
1471
    size_t	maxlen;		/* max string length			*/
1472
 
1473
    maxlen  = WordScan (cp, 0) - cp;
1474
    left    = GetAllocatedSpace (maxlen);
1475
    leftend = left;
1476
 
1477
    if (AlternationScan (&srcp, &leftend, CHAR_OPEN_BRACES, 0) == 0)
1478
    {
1479
	ExpandAWord (cp, WordList, ExpandMode & EXPAND_NOALTS);
1480
	ReleaseMemoryCell (left);
1481
	return;
1482
    }
1483
 
1484
/* We have a alternation section */
1485
 
1486
    alt = GetAllocatedSpace (maxlen);
1487
    altend = alt;
1488
    srcp += 2;
1489
 
1490
    if (AlternationScan (&srcp, &altend, CHAR_CLOSE_BRACES, 1) == 0)
1491
    {
1492
	ReleaseMemoryCell (left);
1493
	ReleaseMemoryCell (alt);
1494
	PrintErrorMessage ("Mis-matched {}.");
1495
    }
1496
 
1497
    *(altend++) = WORD_CHAR;
1498
    *(altend++) = ',';
1499
    *altend = WORD_EOS;
1500
 
1501
/* finally we may have a right-hand side */
1502
 
1503
    right = srcp + 2;
1504
 
1505
/* glue the bits together making a new string */
1506
 
1507
    for (srcp = alt; *srcp != WORD_EOS;)
1508
    {
1509
	ap = leftend;
1510
 
1511
	if (AlternationScan (&srcp, &ap, ',', -1) == 0)
1512
	{
1513
	    ReleaseMemoryCell (left);
1514
	    ReleaseMemoryCell (alt);
1515
	    PrintErrorMessage ("Missing comma.");
1516
	}
1517
 
1518
	srcp += 2;
1519
	rp = right;
1520
	AlternationScan (&rp, &ap, WORD_EOS, 0);
1521
	AlternationExpand (left, WordList, ExpandMode);
1522
    }
1523
 
1524
    ReleaseMemoryCell (left);
1525
    ReleaseMemoryCell (alt);
1526
    return;
1527
}
1528
 
1529
/*
1530
 * Scan the tree
1531
 */
1532
 
1533
static int F_LOCAL AlternationScan (char **cpp,/* source pointer      */
1534
			            char **dpp,/* destination pointer */
1535
			            char endc, /* last character look for  */
1536
			            int  bal)
1537
{
1538
    char	*cp, *dp;
1539
    bool	QuoteStatus = FALSE;
1540
    int		balance = 0;
1541
    bool	UseBalance = FALSE;
1542
    int		VarSubNest = 0;
1543
 
1544
    if (bal)
1545
    {
1546
	UseBalance = TRUE;
1547
	balance = (bal < 1) ? 0 : 1;
1548
    }
1549
 
1550
    cp = *cpp;
1551
    dp = *dpp;
1552
 
1553
    while (*cp != WORD_EOS)
1554
    {
1555
	switch (*cp)
1556
	{
1557
	    case WORD_CHAR:
1558
		if (QuoteStatus)
1559
		{
1560
		    if (cp[1] == CHAR_CLOSE_BRACKETS)
1561
			QuoteStatus = FALSE;
1562
		}
1563
 
1564
		else if (!QuoteStatus)
1565
		{
1566
		    if (cp[1] == CHAR_OPEN_BRACKETS)
1567
			QuoteStatus = TRUE;
1568
 
1569
		    else
1570
		    {
1571
			if (UseBalance)
1572
			{
1573
			    if (cp[1] == CHAR_OPEN_BRACES)
1574
				balance++;
1575
 
1576
			    if (cp[1] == CHAR_CLOSE_BRACES)
1577
				balance--;
1578
			}
1579
 
1580
			if ((cp[1] == endc) && (balance == 0))
1581
			{
1582
			    *dp = WORD_EOS;
1583
			    *dpp = dp;
1584
			    *cpp = cp;
1585
			    return 1;
1586
			}
1587
		    }
1588
		}
1589
 
1590
	    case WORD_QCHAR:
1591
	    case WORD_QTCHAR:
1592
		*(dp++) = *(cp++);
1593
		*(dp++) = *(cp++);
1594
		break;
1595
 
1596
	    case WORD_OQUOTE:
1597
	    case WORD_ODQUOTE:
1598
		QuoteStatus = TRUE;
1599
		*(dp++) = *(cp++);
1600
		break;
1601
 
1602
	    case WORD_CQUOTE:
1603
	    case WORD_CDQUOTE:
1604
		QuoteStatus = FALSE;
1605
		*(dp++) = *(cp++);
1606
		break;
1607
 
1608
	    case WORD_OARRAY:
1609
		VarSubNest++;
1610
		*(dp++) = *(cp++);
1611
		break;
1612
 
1613
	    case WORD_OSUBST:
1614
		VarSubNest++;
1615
 
1616
		while ((*(dp++) = *(cp++)))
1617
		    continue;
1618
 
1619
		if (*cp != WORD_CSUBST)
1620
		    *(dp++) = *(cp++);
1621
 
1622
		break;
1623
 
1624
	    case WORD_CSUBST:
1625
	    case WORD_CARRAY:
1626
		*(dp++) = *(cp++);
1627
		VarSubNest--;
1628
		break;
1629
 
1630
	    case WORD_COMSUB:
1631
	    case WORD_OMATHS:
1632
		while ((*(dp++) = *(cp++)))
1633
		    continue;
1634
 
1635
		break;
1636
	}
1637
    }
1638
 
1639
    *dp = WORD_EOS;
1640
    *cpp = cp;
1641
    *dpp = dp;
1642
 
1643
    return 0;
1644
}
1645
 
1646
/*
1647
 * Handle Array Value between WORD_OARRAY & WORD_CARRAY
1648
 * Return the array index (-1 == [*]).
1649
 *			  (-2 == [@]).
1650
 */
1651
 
1652
static int F_LOCAL HandleArrayValue (char *name,
1653
				     char **InputString,
1654
				     int  ExpandMode)
1655
{
1656
				/* Start after the Open Array		*/
1657
    char	*End = WordScan ((*InputString) + 1, WORD_CARRAY);
1658
    size_t	Length = (End - *InputString) - 1;
1659
    char	*ExpWord;
1660
    Word_B	*WordList = (Word_B *)NULL;
1661
    long	value;
1662
 
1663
/* Build a copy of the substring to expand */
1664
 
1665
    ExpWord = memcpy (GetAllocatedSpace (Length), *InputString + 1, Length);
1666
    ExpWord[Length - 1] = WORD_EOS;
1667
 
1668
    ExpandMode &= ~(EXPAND_GLOBBING | EXPAND_CONVERT | EXPAND_NOALTS);
1669
    ExpandAWord (ExpWord, &WordList, ExpandMode);
1670
 
1671
/* Check for valid value */
1672
 
1673
    if (WordBlockSize (WordList) != 1)
1674
	ShellErrorMessage (LIT_BadArray, "too many words");
1675
 
1676
/*
1677
 * There are a couple of special cases:
1678
 *
1679
 * ${#name[*]}
1680
 * ${name[*]}
1681
 * ${name[@]}
1682
 */
1683
 
1684
    if (!strcmp (WordList->w_words[0], "*"))
1685
	value = -1L;
1686
 
1687
    else if (!strcmp (WordList->w_words[0], "@") &&
1688
	     ((*name != '#') || (*(name + 1) == 0)))
1689
	value = -2L;
1690
 
1691
/*
1692
 * Otherwise, get the array value
1693
 */
1694
 
1695
    else
1696
    {
1697
	value = EvaluateMathsExpression (WordList->w_words[0]);
1698
 
1699
	if ((value < 0) || (value > INT_MAX))
1700
	    ShellErrorMessage (LIT_ArrayRange, name);
1701
    }
1702
 
1703
    *InputString = End;
1704
    return (int)value;
1705
}
1706
 
1707
/*
1708
 * Check for Unset Variable
1709
 */
1710
 
1711
static void F_LOCAL CheckForUnset (char *name, int Index)
1712
{
1713
    if ((FL_TEST (FLAG_UNSET_ERROR)) &&
1714
	(GetVariableArrayAsString (name, Index, FALSE) == null))
1715
	ShellErrorMessage ("%s: unset variable", name);
1716
}
1717
 
1718
 
1719
/*
1720
 * TWALK - Build list of the values of an Environment Variable
1721
 */
1722
 
1723
static void BuildVariableEntryList (const void *key, VISIT visit, int level)
1724
{
1725
    VariableList	*vp = (*(VariableList **)key);
1726
 
1727
    if (((visit == postorder) || (visit == leaf)) &&
1728
       (strcmp (GVAV_Name, vp->name) == 0))
1729
	GVAV_WordList = AddWordToBlock (GetVariableArrayAsString (vp->name,
1730
								  vp->index,
1731
								  TRUE),
1732
					GVAV_WordList);
1733
}
1734
 
1735
/*
1736
 * Return the number of floppy disks
1737
 */
1738
 
1739
#if (OS_TYPE == OS_OS2)
1740
static int F_LOCAL GetNumberofFloppyDrives (void)
1741
{
1742
    BYTE	nflop = 1;
1743
 
1744
#  if (OS_SIZE == OS_16)
1745
    DosDevConfig (&nflop, DEVINFO_FLOPPY, 0);
1746
#  else
1747
    DosDevConfig (&nflop, DEVINFO_FLOPPY);
1748
#  endif
1749
 
1750
    return nflop;
1751
}
1752
#endif
1753
 
1754
/* DOS Version */
1755
 
1756
#if (OS_TYPE == OS_DOS)
1757
static int F_LOCAL GetNumberofFloppyDrives (void)
1758
{
1759
#  if defined (__TURBOC__)
1760
 
1761
    return ((biosequip () & 0x00c0) >> 6) + 1;
1762
 
1763
#  elif defined (__EMX__)
1764
 
1765
    union REGS		r;
1766
 
1767
    SystemInterrupt (0x11, &r, &r);
1768
    return ((r.x.REG_AX & 0x00c0) >> 6) + 1;
1769
 
1770
#  else
1771
 
1772
    return ((_bios_equiplist () & 0x00c0) >> 6) + 1;
1773
 
1774
#  endif
1775
}
1776
#endif
1777
 
1778
/* NT Version */
1779
 
1780
#if (OS_TYPE == OS_NT)
1781
static int F_LOCAL GetNumberofFloppyDrives (void)
1782
{
1783
    char	szNewDrive[4];
1784
    DWORD	dwLogicalDrives = GetLogicalDrives();
1785
    int		LastTest = 0;
1786
    int		i;
1787
 
1788
    strcpy (szNewDrive, "x:\\");
1789
 
1790
/* Look at each drive until we find a non-floppy which exists */
1791
 
1792
    for (i = 0; i < 25; i++)
1793
    {
1794
	if (dwLogicalDrives & (1L << i))
1795
	{
1796
	    szNewDrive[0] = i + 'A';
1797
 
1798
	    if (GetDriveType (szNewDrive) != DRIVE_REMOVABLE)
1799
		break;
1800
 
1801
	    LastTest = i + 1;
1802
	}
1803
    }
1804
 
1805
    return LastTest;
1806
}
1807
#endif