Subversion Repositories DevTools

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
/*
2
 * MS-DOS SHELL - Lexical Scanner
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/SH5.C,v 1.2 2004/05/10 09:30:06 ayoung Exp $
20
 *
21
 *    $Log: SH5.C,v $
22
 *    Revision 1.2  2004/05/10 09:30:06  ayoung
23
 *    improved Path/PATH handling
24
 *    Quote CreateProcess arg0 if embedded spaces are  encountered
25
 *    Native NT exec dont need to check command line length
26
 *    Warning when '@' within redirect list
27
 *    DEBUG_EXEC option, split from PRINT_EXEC option and improved
28
 *
29
 *    Revision 1.1  2002/08/02 06:49:33  adamy
30
 *    imported (reference only)
31
 *
32
 *    Revision 1.1  2001/07/20 05:55:42  ayoung
33
 *    WIN32 support
34
 *
35
 *    Revision 1.1.1.1  1999/12/02 01:11:12  gordonh
36
 *    UTIL
37
 *
38
 *	Revision 2.13  1994/08/25  20:49:11  istewart
39
 *	MS Shell 2.3 Release
40
 *
41
 *	Revision 2.12  1994/02/01  10:25:20  istewart
42
 *	Release 2.3 Beta 2, including first NT port
43
 *
44
 *	Revision 2.11  1994/01/11  17:55:25  istewart
45
 *	Release 2.3 Beta 0 patches
46
 *
47
 *	Revision 2.10  1993/11/09  10:39:49  istewart
48
 *	Beta 226 checking
49
 *
50
 *	Revision 2.9  1993/08/25  16:03:57  istewart
51
 *	Beta 225 - see Notes file
52
 *
53
 *	Revision 2.8  1993/07/02  10:21:35  istewart
54
 *	224 Beta fixes
55
 *
56
 *	Revision 2.7  1993/06/14  11:00:12  istewart
57
 *	More changes for 223 beta
58
 *
59
 *	Revision 2.6  1993/06/02  09:52:35  istewart
60
 *	Beta 223 Updates - see Notes file
61
 *
62
 *	Revision 2.5  1993/02/16  16:03:15  istewart
63
 *	Beta 2.22 Release
64
 *
65
 *	Revision 2.4  1993/01/26  18:35:09  istewart
66
 *	Release 2.2 beta 0
67
 *
68
 *	Revision 2.3  1992/11/06  10:03:44  istewart
69
 *	214 Beta test updates
70
 *
71
 *	Revision 2.2  1992/09/03  18:54:45  istewart
72
 *	Beta 213 Updates
73
 *
74
 *	Revision 2.1  1992/07/10  10:52:48  istewart
75
 *	211 Beta updates
76
 *
77
 *	Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
78
 *	MS-Shell 2.0 Baseline release
79
 *
80
 *
81
 */
82
 
83
#include <sys/types.h>
84
#include <sys/stat.h>
85
#include <stdio.h>
86
#include <signal.h>
87
#include <errno.h>
88
#include <setjmp.h>
89
#include <stdlib.h>
90
#include <string.h>
91
#include <fcntl.h>
92
#include <limits.h>
93
#include <dirent.h>
94
#include <unistd.h>
95
#include "sh.h"
96
 
97
/*
98
 * lexical analysis and source input
99
 */
100
 
101
/* we set s->str to NULLSTR instead of "", so that ungetsc() works */
102
 
103
static	char	nullstr [] = {0, 0};
104
#define	NULLSTR	(nullstr + 1)
105
 
106
static bool		expanding_alias = FALSE;
107
static bool		AllowMultipleAliases = FALSE;
108
 
109
/*
110
 * Here document processing
111
 */
112
 
113
typedef struct here {
114
    struct here		*h_next;	/* Link to next			*/
115
    IO_Actions		*h_iop;		/* I/O options			*/
116
} Here_D;
117
 
118
/*
119
 * The full list contains all the documents created during a parse.  The
120
 * active list, contains the non-processed ones.
121
 */
122
 
123
					/* Full list header		*/
124
static Here_D		*HereListHead = (Here_D *)NULL;
125
					/* Active list Header		*/
126
static Here_D		*ActiveListHead = (Here_D *)NULL;
127
 
128
 
129
static void F_LOCAL	ReadHereDocument (IO_Actions *iop);
130
static void F_LOCAL	GetHereDocuments (void);
131
 
132
static char		*LIT_Unclosed = "here document `%s' unclosed";
133
 
134
/*
135
 * Get next character functions
136
 */
137
 
138
static int F_LOCAL	getsc_ (void);
139
 
140
/* optimized getsc_() */
141
 
142
#define	getsc()		((*source->str != 0) ? (int)*(source->str++) : getsc_())
143
#define	ungetsc()	(source->str--)
144
 
145
/*
146
 * states while lexing word
147
 */
148
 
149
#define	SBASE		0	/* outside any lexical constructs	*/
150
#define	SSQUOTE		1	/* inside ''				*/
151
#define	SDQUOTE		2	/* inside ""				*/
152
#define	SBRACE		3	/* inside ${}				*/
153
#define	SPAREN		4	/* inside $()				*/
154
#define	SBQUOTE		5	/* inside ``				*/
155
#define	SWORD		6	/* implicit quoting for substitute()	*/
156
#define	SDPAREN		7	/* inside (( )), implicit quoting	*/
157
#define	SDDPAREN	8	/* inside $(( )), implicit quoting	*/
158
#define	SVSUBST		9	/* inside ${ }, following the V name	*/
159
#define	SVARRAY		10	/* inside ${name [..].. } 		*/
160
 
161
/*
162
 * Lexical analyzer
163
 *
164
 * tokens are not regular expressions, they are LL(1).
165
 * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
166
 * hence the state stack.
167
 */
168
 
169
int	ScanNextToken (int LexicalControlFlags)
170
{
171
    int			c;
172
    char		states [64], *statep = states;
173
    XString		ws;		/* expandable output word	*/
174
    unsigned char	*wp;		/* output word pointer		*/
175
    char		*sp, *dp;
176
    char		istate;
177
    char		state;
178
    int			c2 = 0;
179
    static int		RecursiveAliasCount = 0;
180
    static AliasList	*RecursiveAlias[MAX_RECURSIVEALIASES];
181
    int			SDD_Count = 0;	/* Parathensis counter		*/
182
    char		LexSeparator;
183
 
184
    if (expanding_alias)
185
    {
186
	expanding_alias = FALSE;
187
 
188
	while (RecursiveAliasCount-- > 0)
189
	    RecursiveAlias[RecursiveAliasCount]->AFlags &= ~ALIAS_EXPANDING;
190
 
191
	RecursiveAliasCount = 0;
192
    }
193
 
194
/*
195
 * Loop round again!
196
 */
197
 
198
Again:
199
 
200
    wp = (unsigned char *)XCreate (&ws, 64);
201
 
202
/*
203
 * Single word ?
204
 */
205
 
206
    if (LexicalControlFlags & ONEWORD)
207
	istate = SWORD;
208
 
209
/*
210
 * Maths expression?
211
 */
212
 
213
    else if (LexicalControlFlags & MATHS_EXPRESSION)
214
    {
215
	istate = SDPAREN;
216
	*(wp)++ = WORD_ODQUOTE;
217
    }
218
 
219
/*
220
 * Normal scanning
221
 */
222
 
223
    else
224
    {
225
	istate = SBASE;
226
 
227
/* Ignore white space */
228
 
229
	while ((c = getsc ()) == CHAR_SPACE || c == CHAR_TAB)
230
	    continue;
231
 
232
/* Comment? */
233
 
234
	if (c == CHAR_COMMENT)
235
	{
236
	    while (((c = getsc ()) != 0) && (c != CHAR_NEW_LINE))
237
		continue;
238
	}
239
 
240
	ungetsc ();
241
    }
242
 
243
/*
244
 * Which separator test to use?
245
 */
246
 
247
    LexSeparator = (char)((LexicalControlFlags & TEST_EXPRESSION)
248
			? (C_IFS | C_SEMICOLON)
249
			: C_LEX1);
250
 
251
/*
252
 * trailing ' ' in alias definition - Yes, allow more aliases
253
 */
254
 
255
    if (AllowMultipleAliases)
256
    {
257
	AllowMultipleAliases = FALSE;
258
	LexicalControlFlags |= ALLOW_ALIAS;
259
    }
260
 
261
/*
262
 * collect non-special or quoted characters to form word
263
 */
264
 
265
    for (*statep = state = istate;
266
	 !(((c = getsc ()) == 0) ||
267
	   ((state == SBASE) && (CharTypes[c] & LexSeparator))); )
268
    {
269
	XCheck (&ws, &wp);
270
 
271
	switch (state)
272
	{
273
	    case SBASE:
274
BasicCharacterProcessing:
275
		switch (c)
276
		{
277
		    case CHAR_META:
278
			c = getsc ();
279
 
280
			if (c != CHAR_NEW_LINE)
281
			{
282
			    *(wp)++ = WORD_QTCHAR;
283
			    *(wp)++ = (unsigned char)c;
284
			}
285
 
286
			else if (wp == XStart (ws))
287
			    goto Again;
288
 
289
			break;
290
 
291
		    case CHAR_SINGLE_QUOTE:
292
			*++statep = state = SSQUOTE;
293
			*(wp)++ = WORD_OQUOTE;
294
			break;
295
 
296
		    case CHAR_DOUBLE_QUOTE:
297
			*++statep = state = SDQUOTE;
298
			*(wp)++ = WORD_ODQUOTE;
299
			break;
300
 
301
		    default:
302
			goto Subst;
303
		}
304
 
305
		break;
306
 
307
/* Non-special character processing */
308
 
309
Subst:
310
		switch (c)
311
		{
312
 
313
/*
314
 * Escaped character
315
 */
316
		    case CHAR_META:
317
			switch (c = getsc ())
318
			{
319
			    case CHAR_NEW_LINE:
320
				break;
321
 
322
			    case CHAR_DOUBLE_QUOTE:
323
			    case CHAR_META:
324
			    case CHAR_VARIABLE:
325
			    case CHAR_BACKQUOTE:
326
				*(wp)++ = WORD_QTCHAR;
327
				*(wp)++ = (unsigned char)c;
328
				break;
329
 
330
			    default:
331
				XCheck (&ws, &wp);
332
				*(wp)++ = WORD_CHAR;
333
				*(wp)++ = CHAR_META;
334
				*(wp)++ = WORD_CHAR;
335
				*(wp)++ = (unsigned char)c;
336
				break;
337
			}
338
 
339
			break;
340
 
341
/*
342
 * Handler $(), $(()), ${}, $var, $num, $#, $*, $@
343
 */
344
 
345
		    case CHAR_VARIABLE:
346
			c = getsc ();
347
 
348
/* Check for $() & $(()) */
349
 
350
			if (c == CHAR_OPEN_PARATHENSIS)
351
			{
352
			    if (getsc () == CHAR_OPEN_PARATHENSIS)
353
			    {
354
				*++statep = state = SDDPAREN;
355
				*(wp)++ = WORD_OMATHS;
356
				SDD_Count = 0;
357
			    }
358
 
359
			    else
360
			    {
361
				ungetsc ();
362
				*++statep = state = SPAREN;
363
				*(wp)++ = WORD_COMSUB;
364
			    }
365
			}
366
 
367
/* Check for ${} */
368
 
369
			else if (c == CHAR_OPEN_BRACES)
370
			{
371
			    *++statep = state = SVSUBST;
372
			    *(wp)++ = WORD_OSUBST;
373
			    c = getsc ();
374
 
375
			    if (!IS_VariableFC (c) && !IS_VarNumeric (c))
376
				ShellErrorMessage ("invalid variable name");
377
 
378
			    do
379
			    {
380
				XCheck (&ws, &wp);
381
				*(wp)++ = (unsigned char)c;
382
				c = getsc ();
383
			    } while (IS_VariableSC (c));
384
 
385
			    *(wp)++ = 0;
386
			    ungetsc ();
387
			}
388
 
389
/* Check for $var */
390
 
391
			else if (IS_VariableFC (c))
392
			{
393
			    *(wp)++ = WORD_OSUBST;
394
 
395
			    do
396
			    {
397
				XCheck (&ws, &wp);
398
				*(wp)++ = (unsigned char)c;
399
				c = getsc ();
400
			    } while (IS_VariableSC(c));
401
 
402
			    *(wp)++ = 0;
403
			    *(wp)++ = WORD_CSUBST;
404
			    ungetsc ();
405
 
406
			}
407
 
408
/* Check for $number, $*, $@, $#, $! $$ $- $? */
409
 
410
			else if (IS_VarNumeric (c))
411
			{
412
			    XCheck (&ws, &wp);
413
			    *(wp)++ = WORD_OSUBST;
414
			    *(wp)++ = (unsigned char)c;
415
			    *(wp)++ = 0;
416
			    *(wp)++ = WORD_CSUBST;
417
			}
418
 
419
/* $??? - no mapping */
420
 
421
			else
422
			{
423
			    *(wp)++ = WORD_CHAR;
424
			    *(wp)++ = CHAR_VARIABLE;
425
			    ungetsc ();
426
			}
427
 
428
			break;
429
 
430
/*
431
 *  Handler ` ` (old style $())
432
 */
433
		    case CHAR_BACKQUOTE:
434
			*++statep = state = SBQUOTE;
435
			*(wp)++ = WORD_COMSUB;
436
			break;
437
 
438
/*
439
 * Normal character
440
 */
441
 
442
		    default:
443
			*(wp)++ = WORD_CHAR;
444
			*(wp)++ = (unsigned char)c;
445
		}
446
 
447
		break;
448
 
449
	    case SSQUOTE:			/* Inside '...'		*/
450
		if (c == CHAR_SINGLE_QUOTE)
451
		{
452
		    state = *--statep;
453
		    *(wp)++ = WORD_CQUOTE;
454
		}
455
 
456
/* Check for an escaped '.  None of the UNIX versions seem to do this, so
457
 * it may be wrong, so I've #if it out
458
 */
459
 
460
		else
461
		{
462
		    *(wp)++ = WORD_QCHAR;
463
		    *(wp)++ = (unsigned char)c;
464
		}
465
 
466
#if 0
467
		else if (c != CHAR_META)
468
		{
469
		    *(wp)++ = WORD_QCHAR;
470
		    *(wp)++ = (unsigned char)c;
471
		}
472
 
473
/* Yes - insert a quoted ' */
474
		else if ((c = getsc ()) == CHAR_SINGLE_QUOTE)
475
		{
476
		    *(wp)++ = WORD_QTCHAR;
477
		    *(wp)++ = CHAR_SINGLE_QUOTE;
478
		}
479
 
480
/* No - unget the character and insert the meta */
481
 
482
		else
483
		{
484
		    ungetsc ();
485
		    *(wp)++ = WORD_QCHAR;
486
		    *(wp)++ = CHAR_META;
487
		}
488
#endif
489
 
490
		break;
491
 
492
	    case SDQUOTE:			/* Inside "..."		*/
493
		if (c == CHAR_DOUBLE_QUOTE)
494
		{
495
		    state = *--statep;
496
		    *(wp)++ = WORD_CDQUOTE;
497
		}
498
 
499
		else
500
		    goto Subst;
501
 
502
		break;
503
 
504
	    case SPAREN:			/* Inside $(...)	*/
505
		if (c == CHAR_OPEN_PARATHENSIS)
506
		    *++statep = state;
507
 
508
		else if (c == CHAR_CLOSE_PARATHENSIS)
509
		    state = *--statep;
510
 
511
		if (state == SPAREN)
512
		    *(wp)++ = (unsigned char)c;
513
 
514
		else
515
		    *(wp)++ = 0; /* end of WORD_COMSUB */
516
 
517
		break;
518
 
519
	    case SBRACE:			/* Inside ${...}	*/
520
		if (c != CHAR_CLOSE_BRACES)
521
		    goto BasicCharacterProcessing;
522
 
523
		state = *--statep;
524
		*(wp)++ = WORD_CSUBST;
525
		break;
526
 
527
	    case SVARRAY:			/* Inside ${name [...].}*/
528
		if (c != CHAR_CLOSE_BRACKETS)
529
		    goto BasicCharacterProcessing;
530
 
531
		state = *--statep;
532
		*(wp)++ = WORD_CARRAY;
533
		break;
534
 
535
 
536
	    case SVSUBST:		/* Inside ${ }, following the 	*/						/* Variable name		*/
537
 
538
/* Set state to SBRACE to handle closing brace. */
539
 
540
		*statep = state = SBRACE;
541
 
542
/* Simple Name
543
 *
544
 * Possibilities: ${name}
545
 */
546
 
547
		if (c == CHAR_CLOSE_BRACES)
548
		    ungetsc ();
549
 
550
/*
551
 * Array Name.  Reset to this state for closing ] post processing
552
 *
553
 * Possibilities: ${name[index].....}
554
 */
555
 
556
		else if (c == CHAR_OPEN_BRACKETS)
557
		{
558
		    *statep = SVSUBST;
559
		    *++statep = state = SVARRAY;
560
		    *(wp)++ = WORD_OARRAY;
561
		}
562
 
563
/* Korn pattern trimming
564
 *
565
 * Possibilities: ${name#string}
566
 *		  ${name##string}
567
 *		  ${name%string}
568
 */
569
 
570
		else if ((c == CHAR_MATCH_START) || (c == CHAR_MATCH_END))
571
		{
572
		    if (getsc () == c)
573
			c |= CHAR_MAGIC;
574
 
575
		    else
576
			ungetsc ();
577
 
578
		    *(wp)++ = (unsigned char)c;
579
		}
580
 
581
/* Subsitution
582
 *
583
 * Possibilities: ${name:?value}
584
 * 		  ${name:-value}
585
 * 		  ${name:=value}
586
 * 		  ${name:+value}
587
 *		  ${name?value}
588
 * 		  ${name-value}
589
 * 		  ${name=value}
590
 * 		  ${name+value}
591
 */
592
 
593
		else
594
		{
595
		    c2 = (c == ':') ? CHAR_MAGIC : 0;
596
 
597
		    if (c == ':')
598
			c = getsc();
599
 
600
		    if (!IS_VarOp (c))
601
		    {
602
			CompilingError ();
603
			ShellErrorMessage ("Unknown substitution operator '%c'",
604
					   c);
605
		    }
606
 
607
		    *(wp)++ = (unsigned char)(c | c2);
608
		}
609
 
610
		break;
611
 
612
	    case SBQUOTE:			/* Inside `...`		*/
613
		if (c == CHAR_BACKQUOTE)
614
		{
615
		    *(wp)++ = 0;
616
		    state = *--statep;
617
		}
618
 
619
		else if (c == CHAR_META)
620
		{
621
		    switch (c = getsc())
622
		    {
623
			case CHAR_NEW_LINE:
624
			    break;
625
 
626
			case CHAR_META:
627
			case CHAR_VARIABLE:
628
			case CHAR_BACKQUOTE:
629
			    *(wp)++ = (unsigned char)c;
630
			    break;
631
 
632
			default:
633
			    *(wp)++ = CHAR_META;
634
			    *(wp)++ = (unsigned char)c;
635
			    break;
636
		    }
637
		}
638
 
639
		else
640
		    *(wp)++ = (unsigned char)c;
641
 
642
		break;
643
 
644
	    case SWORD:				/* ONEWORD		*/
645
		goto Subst;
646
 
647
	    case SDPAREN:			/* Include ((....))	*/
648
		if (c == CHAR_CLOSE_PARATHENSIS)
649
		{
650
		    if (getsc () == CHAR_CLOSE_PARATHENSIS)
651
		    {
652
			*(wp)++ = WORD_ODQUOTE;
653
			c = 0;
654
			goto Done;
655
		    }
656
 
657
		    else
658
			ungetsc ();
659
		}
660
 
661
		goto Subst;
662
 
663
	    case SDDPAREN:			/* Include $((....))	*/
664
 
665
/* Check for sub-expressions */
666
 
667
		if (c == CHAR_OPEN_PARATHENSIS)
668
		    SDD_Count++;
669
 
670
/* Check for end of sub-expression or $((..)) */
671
 
672
		else if (c == CHAR_CLOSE_PARATHENSIS)
673
		{
674
		    if (SDD_Count)
675
			SDD_Count--;
676
 
677
		    else if (getsc () == CHAR_CLOSE_PARATHENSIS)
678
		    {
679
			state = *--statep;
680
			*(wp)++ = 0;
681
			break;
682
		    }
683
 
684
		    else
685
		    {
686
			CompilingError ();
687
			ShellErrorMessage ("maths sub-expression not closed");
688
		    }
689
		}
690
 
691
/* Normal character? */
692
 
693
		*(wp)++ = (char)c;
694
		break;
695
	}
696
    }
697
 
698
/* Finally, the end! */
699
 
700
Done:
701
    XCheck (&ws, &wp);
702
 
703
    if (state != istate)
704
    {
705
	CompilingError ();
706
	ShellErrorMessage ("no closing quote");
707
    }
708
 
709
    if (!(LexicalControlFlags & TEST_EXPRESSION) &&
710
	 ((c == CHAR_INPUT) || (c == CHAR_OUTPUT)))
711
    {
712
	unsigned char *cp = XStart (ws);
713
 
714
/* Set c2 to the io unit value */
715
 
716
	if ((wp > cp) && (cp[0] == WORD_CHAR) && (IS_Numeric (cp[1])))
717
	{
718
	    wp = cp; /* throw away word */
719
	    c2 = cp[1] - '0';
720
	}
721
 
722
	else
723
	    c2 = (c == CHAR_OUTPUT) ? 1 : 0;	/* 0 for <, 1 for > */
724
    }
725
 
726
/* no word, process LEX1 character */
727
 
728
    if ((wp == XStart (ws)) && (state == SBASE))
729
    {
730
	XFree (ws);	/* free word */
731
 
732
	switch (c)
733
	{
734
	    default:
735
		return c;
736
 
737
    /* Check for double character thingys - ||, &&, ;; */
738
 
739
	    case CHAR_PIPE:
740
	    case CHAR_ASYNC:
741
	    case CHAR_SEPARATOR:
742
		if ((c2 = getsc ()) == c)
743
		{
744
		    switch (c)
745
		    {
746
			case CHAR_PIPE:
747
			    c = PARSE_LOGICAL_OR;
748
			    break;
749
 
750
			case CHAR_ASYNC:
751
			    c = PARSE_LOGICAL_AND;
752
			    break;
753
 
754
			case CHAR_SEPARATOR:
755
			    c = PARSE_BREAK;
756
			    break;
757
 
758
			default:
759
			    c = YYERRCODE;
760
			    break;
761
		    }
762
		}
763
 
764
/* Check for co-process |& */
765
 
766
		else if ((c == CHAR_PIPE) && (c2 == CHAR_ASYNC))
767
		    c = PARSE_COPROCESS;
768
 
769
		else
770
		    ungetsc ();
771
 
772
		return c;
773
 
774
    /* Re-direction */
775
 
776
	    case CHAR_INPUT:
777
	    case CHAR_OUTPUT:
778
	    {
779
		IO_Actions	*iop;
780
 
781
		iop = (IO_Actions *) GetAllocatedSpace (sizeof (IO_Actions));
782
		iop->io_unit = c2/*unit*/;
783
 
784
    /* Look at the next character */
785
 
786
		c2 = getsc ();
787
 
788
    /* Check for >>, << & <> */
789
 
790
		if ((c2 == CHAR_OUTPUT) || (c2 == CHAR_INPUT) )
791
		{
792
		    iop->io_flag = (c != c2) ? IORDWR
793
					     : (c == CHAR_OUTPUT) ? IOCAT
794
								  : IOHERE;
795
		    c2 = getsc ();
796
		}
797
 
798
		else
799
		    iop->io_flag = (c == CHAR_OUTPUT) ? IOWRITE : IOREAD;
800
 
801
    /* Check for <<- - strip tabs */
802
 
803
		if (iop->io_flag == IOHERE)
804
		{
805
		    if (c2 == '-')
806
			iop->io_flag |= IOSKIP;
807
 
808
		    else
809
			ungetsc ();
810
		}
811
 
812
    /* Check for <& or >& */
813
 
814
		else if (c2 == '&')
815
		    iop->io_flag = IODUP;
816
 
817
    /* Check for >! */
818
 
819
		else if ((c2 == CHAR_PIPE) && (iop->io_flag == IOWRITE))
820
		    iop->io_flag |= IOCLOBBER;
821
 
822
		else
823
		    ungetsc ();
824
 
825
		yylval.iop = iop;
826
		return PARSE_REDIR;
827
	    }
828
 
829
	    case CHAR_NEW_LINE:
830
		GetHereDocuments ();
831
 
832
		if (LexicalControlFlags & ALLOW_CONTINUATION)
833
		    goto Again;
834
 
835
		return c;
836
 
837
	    case CHAR_OPEN_PARATHENSIS:
838
		c2 = getsc();
839
 
840
		if (c2 == CHAR_CLOSE_PARATHENSIS)
841
		    c = PARSE_MPAREN;
842
 
843
		else if (c2 == CHAR_OPEN_PARATHENSIS)
844
		    c = PARSE_MDPAREN;
845
 
846
		else
847
		    ungetsc();
848
 
849
	    case CHAR_CLOSE_PARATHENSIS:
850
		return c;
851
	}
852
    }
853
 
854
/* Must be a word !!! */
855
 
856
    *(wp)++ = WORD_EOS;		/* terminate word */
857
    yylval.cp = XClose (&ws, wp);
858
 
859
    if (state == SWORD || state == SDPAREN)	/* ONEWORD? */
860
	return PARSE_WORD;
861
 
862
    ungetsc ();		/* unget terminator */
863
 
864
/* Make sure the CurrentLexIdentifier array stays '\0' padded */
865
 
866
    memset (CurrentLexIdentifier, 0, IDENT);
867
 
868
/* copy word to unprefixed string CurrentLexIdentifier */
869
 
870
    for (sp = yylval.cp, dp = CurrentLexIdentifier;
871
	 (dp < CurrentLexIdentifier + IDENT) && ((c = *sp++) == WORD_CHAR); )
872
	    *dp++ = *sp++;
873
 
874
    if (c != WORD_EOS)
875
	*CurrentLexIdentifier = 0;	/* word is not unquoted */
876
 
877
/* Are we allowing Keywords and Aliases ? */
878
 
879
    if (*CurrentLexIdentifier != 0 &&
880
	(LexicalControlFlags & (ALLOW_KEYWORD | ALLOW_ALIAS)))
881
    {
882
	AliasList	*p;
883
	int		yval;
884
	Source		*s;
885
 
886
/* Check keyword */
887
 
888
	if ((LexicalControlFlags & ALLOW_KEYWORD) &&
889
	    (yval = LookUpSymbol (CurrentLexIdentifier)))
890
	{
891
	    ReleaseMemoryCell (yylval.cp);
892
	    return yval;
893
	}
894
 
895
/* Check Alias */
896
 
897
	if ((LexicalControlFlags & ALLOW_ALIAS) &&
898
	    ((p = LookUpAlias (CurrentLexIdentifier, TRUE)) !=
899
		 (AliasList *)NULL) &&
900
	    (!(p->AFlags & ALIAS_EXPANDING)))
901
	{
902
	    if (RecursiveAliasCount == MAX_RECURSIVEALIASES)
903
	    {
904
		CompilingError ();
905
		ShellErrorMessage ("excessive recusrsive aliasing");
906
	    }
907
 
908
	    else
909
		RecursiveAlias[RecursiveAliasCount++] = p;
910
 
911
	    p->AFlags |= ALIAS_EXPANDING;
912
 
913
/* check for recursive aliasing */
914
 
915
	    for (s = source; s->type == SALIAS; s = s->next)
916
	    {
917
		if (s->u.Calias == p)
918
		    return PARSE_WORD;
919
	    }
920
 
921
	    ReleaseMemoryCell ((void *)yylval.cp);
922
 
923
/* push alias expansion */
924
 
925
	    s = pushs (SALIAS);
926
	    s->str = p->value;
927
	    s->u.Calias = p;
928
	    s->next = source;
929
	    source = s;
930
	    goto Again;
931
	}
932
    }
933
 
934
    return PARSE_WORD;
935
}
936
 
937
/*
938
 * read "<<word" text into temp file
939
 */
940
 
941
static void F_LOCAL ReadHereDocument (struct ioword *iop)
942
{
943
    FILE	*FP = NULL;
944
    int		tf;
945
    int		c;
946
    char	*EoFString;
947
    char	*cp;
948
    char	*Line;
949
    size_t	LineLen;
950
 
951
/* Expand the terminator */
952
 
953
    LineLen = strlen (EoFString = ExpandAString (iop->io_name, 0));
954
 
955
/* Save the tempoary file information */
956
 
957
    iop->io_name = StringCopy (GenerateTemporaryFileName ());
958
 
959
/* Create the tempoary file */
960
 
961
    if (((tf = S_open (FALSE, iop->io_name, O_CMASK | O_NOINHERIT)) < 0)
962
	|| ((FP = ReOpenFile (tf, sOpenWriteBinaryMode)) == (FILE *)NULL))
963
	ShellErrorMessage ("Cannot create temporary file");
964
 
965
/* Get some space */
966
 
967
    Line = GetAllocatedSpace (LineLen + 3);
968
 
969
/* Read the file */
970
 
971
    while (TRUE)
972
    {
973
	cp = Line;
974
 
975
/* Read the first n characters to check for terminator */
976
 
977
	while ((c = getsc ()) != CHAR_NEW_LINE)
978
	{
979
	    if (c == 0)
980
		ShellErrorMessage (LIT_Unclosed, EoFString);
981
 
982
	    if (cp > Line + LineLen)
983
		break;
984
 
985
/* Ignore leading tabs if appropriate */
986
 
987
	    if (!((iop->io_flag & IOSKIP) && (cp == Line) && (c == CHAR_TAB)))
988
		*(cp++) = (char)c;
989
	}
990
 
991
	*cp = 0;
992
	ungetsc ();
993
 
994
/* Check for end of string */
995
 
996
	if (strcmp (EoFString, Line) == 0 || c == 0)
997
	    break;
998
 
999
/* Dump the line */
1000
 
1001
        if (FP) fputs (Line, FP);
1002
 
1003
	while ((c = getsc ()) != CHAR_NEW_LINE)
1004
	{
1005
	    if (c == 0)
1006
		ShellErrorMessage (LIT_Unclosed, EoFString);
1007
 
1008
            if (FP) putc (c, FP);
1009
	}
1010
 
1011
        if (FP) putc (c, FP);
1012
    }
1013
 
1014
    if (FP) S_fclose (FP, TRUE);
1015
}
1016
 
1017
/*
1018
 * Handle scanning error
1019
 */
1020
 
1021
void	CompilingError (void)
1022
{
1023
    yynerrs++;
1024
 
1025
    while (source->type == SALIAS) /* pop aliases */
1026
	source = source->next;
1027
 
1028
    source->str = NULLSTR;	/* zap pending input */
1029
}
1030
 
1031
/*
1032
 * input for ScanNextToken with alias expansion
1033
 */
1034
 
1035
Source *pushs (int type)
1036
{
1037
    Source *s = (Source *) GetAllocatedSpace (sizeof (Source));
1038
 
1039
    s->type = type;
1040
    s->str = NULLSTR;
1041
    return s;
1042
}
1043
 
1044
 
1045
/*
1046
 * Get the next string from input source
1047
 */
1048
 
1049
static int F_LOCAL getsc_ (void)
1050
{
1051
    Source	*s = source;
1052
    int		c;
1053
 
1054
    while ((c = *s->str++) == 0)
1055
    {
1056
	s->str = NULL;		/* return 0 for EOF by default */
1057
 
1058
	switch (s->type)
1059
	{
1060
	    case SEOF:
1061
		s->str = NULLSTR;
1062
		return 0;
1063
 
1064
	    case STTY:
1065
		s->line++;
1066
		s->str = ConsoleLineBuffer;
1067
		s->str[c = GetConsoleInput ()] = 0;
1068
 
1069
		FlushStreams ();
1070
 
1071
/* EOF? */
1072
		if (c == 0)
1073
		{
1074
		    s->str = NULL;
1075
		    s->line--;
1076
		}
1077
 
1078
/* Ignore pre-white space */
1079
 
1080
		else
1081
		{
1082
		    c = 0;
1083
 
1084
		    while (s->str[c] && IS_IFS ((int)s->str[c]))
1085
			c++;
1086
 
1087
/* Blank line?? */
1088
 
1089
		    if (!s->str[c])
1090
		    {
1091
			s->str = &s->str[c - 1];
1092
			s->line--;
1093
		    }
1094
 
1095
		    else
1096
			s->str = &s->str[c];
1097
		}
1098
 
1099
		break;
1100
 
1101
	    case SFILE:
1102
		s->line++;
1103
		s->str = fgets (e.line, LINE_MAX, s->u.file);
1104
 
1105
		DPRINT (1, ("getsc_: File line = <%s>", s->str));
1106
 
1107
		if (s->str == NULL)
1108
		{
1109
		    if (s->u.file != stdin)
1110
			S_fclose (s->u.file, TRUE);
1111
		}
1112
 
1113
		break;
1114
 
1115
	    case SWSTR:
1116
		break;
1117
 
1118
	    case SSTRING:
1119
		s->str = LIT_NewLine;
1120
		s->type = SEOF;
1121
		break;
1122
 
1123
	    case SWORDS:
1124
		s->str = *s->u.strv++;
1125
		s->type = SWORDSEP;
1126
		break;
1127
 
1128
	    case SWORDSEP:
1129
		if (*s->u.strv == NULL)
1130
		{
1131
		    s->str = LIT_NewLine;
1132
		    s->type = SEOF;
1133
		}
1134
 
1135
		else
1136
		{
1137
		    s->str = " ";
1138
		    s->type = SWORDS;
1139
		}
1140
 
1141
		break;
1142
 
1143
	    case SALIAS:
1144
		s->str = s->u.Calias->value;
1145
 
1146
/*
1147
 * If there is a trailing ' ', allow multiple aliases
1148
 */
1149
 
1150
		if ((*(s->str) != 0) &&
1151
		    (((c = s->str[strlen (s->str) - 1]) == CHAR_SPACE) ||
1152
		     (c == CHAR_TAB)))
1153
		    AllowMultipleAliases = TRUE;
1154
 
1155
		source = s = s->next;
1156
		expanding_alias = TRUE;
1157
		continue;
1158
	}
1159
 
1160
	if (s->str == (char *)NULL)
1161
	{
1162
	    s->type = SEOF;
1163
	    s->str = NULLSTR;
1164
	    return 0;
1165
	}
1166
 
1167
	if (s->echo)
1168
	    foputs (s->str);
1169
    }
1170
 
1171
    return c;
1172
}
1173
 
1174
/*
1175
 * Close all file handlers
1176
 */
1177
 
1178
void CloseAllHandlers (void)
1179
{
1180
    int		u;
1181
 
1182
/*
1183
 * Close any stream IO blocks
1184
 */
1185
 
1186
    for (u = 0; u < MaxNumberofFDs; u++)
1187
    {
1188
#if  defined (__TURBOC__)
1189
	if ((_streams[u].flags & _F_RDWR) && (_streams[u].fd >= NUFILE))
1190
	    fclose (&_streams[u]);
1191
#elif  defined (__WATCOMC__)
1192
	if ((u < _NFILES) && (__iob[u]._flag & (_READ | _WRITE)) &&
1193
	    (__iob[u]._handle >= NUFILE))
1194
	    fclose (&__iob[u]);
1195
#elif (OS_TYPE == OS_UNIX)
1196
#  ifdef __STDC__
1197
	if ((__iob[u]._flag & (_IOREAD | _IORW | _IOWRT)) &&
1198
	    (__iob[u]._file >= NUFILE))
1199
	    fclose (&__iob[u]);
1200
#  else
1201
	if ((_iob[u]._flag & (_IOREAD | _IORW | _IOWRT)) &&
1202
	    (_iob[u]._file >= NUFILE))
1203
	    fclose (&_iob[u]);
1204
#  endif
1205
#elif defined (__EMX__)
1206
	if ((_streamv[u].flags & (_IOREAD | _IORW | _IOWRT)) &&
1207
	    (_streamv[u].handle >= NUFILE))
1208
	    fclose (&_streamv[u]);
1209
#elif !defined (__OS2__)
1210
	if ((_iob[u]._flag & (_IOREAD | _IORW | _IOWRT)) &&
1211
	    (_iob[u]._file >= NUFILE))
1212
	    fclose (&_iob[u]);
1213
#endif
1214
    }
1215
 
1216
/* Close any open files */
1217
 
1218
    for (u = NUFILE; u < MaxNumberofFDs;)
1219
	S_close (u++, TRUE);
1220
}
1221
 
1222
 
1223
/*
1224
 * remap fd into Shell's fd space
1225
 */
1226
 
1227
int ReMapIOHandler (int fd)
1228
{
1229
    int		i;
1230
    int		n_io = 0;
1231
    int		map [NUFILE];
1232
    int		o_fd = fd;
1233
 
1234
    if (fd < FDBASE)
1235
    {
1236
	do
1237
	{
1238
	    map[n_io++] = fd;
1239
	    fd = dup (fd);
1240
 
1241
	} while ((fd >= 0) && (fd < FDBASE));
1242
 
1243
	for (i = 0; i < n_io; i++)
1244
	    close (map[i]);
1245
 
1246
/* Check we can map it */
1247
 
1248
	if (fd >= (32 + FDBASE))
1249
	{
1250
	    close (fd);
1251
	    fd = -1;
1252
	}
1253
 
1254
	else
1255
	{
1256
	    S_Remap (o_fd, fd);
1257
	    S_close (o_fd, TRUE);
1258
	}
1259
 
1260
	if (fd < 0)
1261
	{
1262
#ifdef DEBUG
1263
	    struct stat		st;
1264
 
1265
	    fprintf (stderr, "\nOld Fd=%d, New=%d, Map=0x%.8lx\n", o_fd, fd,
1266
		     e.IOMap);
1267
 
1268
	    feputs ("Ch. Device Inode  Mode  Links RDev   Size\n");
1269
 
1270
	    for (i = 0; i < MaxNumberofFDs; i++)
1271
	    {
1272
		if (fstat (i, &st) < 0)
1273
		    fprintf (stderr, "%2d: cannot get status (%s)\n", i,
1274
			     strerror (errno));
1275
 
1276
		else
1277
		    fprintf (stderr, "%2d: 0x%.4x %5u %6o %5d 0x%.4x %ld\n", i,
1278
			     st.st_dev, st.st_ino, st.st_mode, st.st_nlink,
1279
			     st.st_rdev, st.st_size);
1280
	    }
1281
#endif
1282
	    ShellErrorMessage ("too many files open");
1283
	}
1284
    }
1285
 
1286
    return fd;
1287
}
1288
 
1289
 
1290
/*
1291
 * Generate a temporary filename
1292
 */
1293
 
1294
char *GenerateTemporaryFileName (void)
1295
{
1296
    static char	tmpfile[FFNAME_MAX];
1297
    char	*tmpdir;	/* Points to directory prefix of pipe	*/
1298
    static int	temp_count = 0;
1299
    char	*sep = DirectorySeparator;
1300
 
1301
/* Find out where we should put temporary files */
1302
 
1303
    if (((tmpdir = GetVariableAsString ("TMP", FALSE)) == null) &&
1304
	((tmpdir = GetVariableAsString ( HomeVariableName, FALSE )) == null) &&
1305
	((tmpdir = GetVariableAsString ("TMPDIR", FALSE)) == null)) 
1306
    {
1307
	static int first = 0;
1308
 
1309
	if (!first) {
1310
            PrintWarningMessage (
1311
               "sh: neither TMP nor TMPDIR are set, using current directory." );
1312
	    first = 1;	
1313
	}
1314
	tmpdir = CurrentDirLiteral;
1315
    }
1316
 
1317
    if (strchr ("/\\", tmpdir[strlen (tmpdir) - 1]) != (char *)NULL)
1318
	sep = null;
1319
 
1320
/* Get a unique temporary file name */
1321
 
1322
    while (1)
1323
    {
1324
	sprintf (tmpfile, "%s%ssht%.5u.tmp", tmpdir, sep, temp_count++);
1325
 
1326
	if (!S_access (tmpfile, F_OK))
1327
	    break;
1328
    }
1329
 
1330
    return tmpfile;
1331
}
1332
 
1333
/*
1334
 * XString functions
1335
 */
1336
 
1337
/*
1338
 * Check an expandable string for overflow
1339
 */
1340
 
1341
void	XCheck (XString *xs, unsigned char **xp)
1342
{
1343
    if (*xp >= xs->SEnd)
1344
    {
1345
	int	OldOffset = *xp - xs->SStart;
1346
 
1347
	xs->SStart = ReAllocateSpace (xs->SStart, (xs->SLength *= 2) + 8);
1348
	xs->SEnd = xs->SStart + xs->SLength;
1349
	*xp = xs->SStart + OldOffset;
1350
    }
1351
}
1352
 
1353
/*
1354
 * Close an Expanded String
1355
 */
1356
 
1357
char	*XClose (XString *xs, unsigned char *End)
1358
{
1359
    size_t	len = End - xs->SStart;
1360
    char	*s = memcpy (GetAllocatedSpace (len), xs->SStart, len);
1361
 
1362
    ReleaseMemoryCell (xs->SStart);
1363
    return s;
1364
}
1365
 
1366
/*
1367
 * Create an Expanded String
1368
 */
1369
 
1370
char	*XCreate (XString *xs, size_t len)
1371
{
1372
    xs->SLength = len;
1373
    xs->SStart = GetAllocatedSpace (len + 8);
1374
    xs->SEnd = xs->SStart + len;
1375
    return (char *)xs->SStart;
1376
}
1377
 
1378
/*
1379
 * here documents
1380
 *
1381
 * Save a here file's IOP for later processing (ie delete of the temp file
1382
 * name)
1383
 */
1384
 
1385
void SaveHereDocumentInfo (IO_Actions *iop)
1386
{
1387
    Here_D	*h, *lh;
1388
 
1389
    if ((h = (Here_D *) GetAllocatedSpace (sizeof(Here_D))) == (Here_D *)NULL)
1390
	return;
1391
 
1392
    h->h_iop     = iop;
1393
    h->h_next    = (Here_D *)NULL;
1394
 
1395
    if (HereListHead == (Here_D *)NULL)
1396
	HereListHead = h;
1397
 
1398
    else
1399
    {
1400
	for (lh = HereListHead; lh != (Here_D *)NULL; lh = lh->h_next)
1401
	{
1402
	    if (lh->h_next == (Here_D *)NULL)
1403
	    {
1404
		lh->h_next = h;
1405
		break;
1406
	    }
1407
	}
1408
    }
1409
}
1410
 
1411
/*
1412
 * Read all the active here documents
1413
 */
1414
 
1415
static void F_LOCAL GetHereDocuments (void)
1416
{
1417
    Here_D	*h, *hp;
1418
 
1419
/* Scan here files first leaving HereListHead list in place */
1420
 
1421
    for (hp = h = HereListHead; h != (Here_D *)NULL; hp = h, h = h->h_next)
1422
	ReadHereDocument (h->h_iop);
1423
 
1424
/* Make HereListHead list active - keep list intact for scraphere */
1425
 
1426
    if (hp != (Here_D *)NULL)
1427
    {
1428
	hp->h_next	 = ActiveListHead;
1429
	ActiveListHead	 = HereListHead;
1430
	HereListHead     = (Here_D *)NULL;
1431
    }
1432
}
1433
 
1434
/*
1435
 * Zap all the here documents, unless they are currently in use by a
1436
 * function.
1437
 */
1438
 
1439
void ScrapHereList (void)
1440
{
1441
    Here_D	*h;
1442
 
1443
    for (h = HereListHead; h != (Here_D *)NULL; h = h->h_next)
1444
    {
1445
	if ((h->h_iop != (IO_Actions *)NULL) &&
1446
	    (h->h_iop->io_name != (char *)NULL) &&
1447
	    (!(h->h_iop->io_flag & IOFUNCTION)))
1448
	    unlink (h->h_iop->io_name);
1449
    }
1450
 
1451
    HereListHead = (Here_D *)NULL;
1452
}
1453
 
1454
/*
1455
 * unlink here temp files before a ReleaseMemoryArea (area)
1456
 */
1457
 
1458
void FreeAllHereDocuments (int area)
1459
{
1460
    Here_D	*current = ActiveListHead;
1461
    Here_D	*previous = (Here_D *)NULL;
1462
 
1463
    while (current != (Here_D *)NULL)
1464
    {
1465
	if (GetMemoryAreaNumber ((void *)current) >= area)
1466
	{
1467
	    if ((current->h_iop->io_name != (char *)NULL) &&
1468
		(!(current->h_iop->io_flag & IOFUNCTION)))
1469
		unlink (current->h_iop->io_name);
1470
 
1471
	    if (ActiveListHead == current)
1472
		ActiveListHead = current->h_next;
1473
 
1474
	    else
1475
		previous->h_next = current->h_next;
1476
	}
1477
 
1478
	previous = current;
1479
	current = current->h_next;
1480
    }
1481
}
1482
 
1483
/*
1484
 * Open here document temp file.
1485
 * If unquoted here, expand here temp file into second temp file.
1486
 */
1487
 
1488
int	OpenHereFile (char *hname, bool sub)
1489
{
1490
    FILE	*FP;
1491
    char	*cp;
1492
    Source	*s;
1493
    char	*tname = (char *)NULL;
1494
    jmp_buf	ReturnPoint;
1495
 
1496
/* Check input file */
1497
 
1498
    if (hname == (char *)NULL)
1499
	return -1;
1500
 
1501
/*
1502
 * If processing for $, ` and ' is required, do it
1503
 */
1504
 
1505
    if (sub)
1506
    {
1507
	CreateNewEnvironment ();
1508
 
1509
	if (SetErrorPoint (ReturnPoint) == 0)
1510
	{
1511
	    if ((FP = FOpenFile (hname, sOpenReadMode)) == (FILE *)NULL)
1512
	    {
1513
		PrintErrorMessage ("Here Document (%s) lost", hname);
1514
		return -1;
1515
	    }
1516
 
1517
/* set up ScanNextToken input from here file */
1518
 
1519
	    s = pushs (SFILE);
1520
	    s->u.file = FP;
1521
	    source = s;
1522
 
1523
	    if (ScanNextToken (ONEWORD) != PARSE_WORD)
1524
		PrintErrorMessage ("Here Document error");
1525
 
1526
	    cp = ExpandAString (yylval.cp, 0);
1527
 
1528
    /* write expanded input to another temp file */
1529
 
1530
	    tname = StringCopy (GenerateTemporaryFileName ());
1531
 
1532
	    if ((FP = FOpenFile (tname, sOpenAppendMode)) == (FILE *)NULL)
1533
	    {
1534
		ShellErrorMessage (Outofmemory1);
1535
		TerminateCurrentEnvironment (TERMINATE_COMMAND);
1536
	    }
1537
 
1538
	    fputs (cp, FP);
1539
	    CloseFile (FP);
1540
 
1541
	    QuitCurrentEnvironment ();
1542
 
1543
	    return S_open (TRUE, tname, O_RDONLY);
1544
	}
1545
 
1546
/* Error - terminate */
1547
 
1548
	else
1549
	{
1550
	    QuitCurrentEnvironment ();
1551
	    CloseFile (FP);
1552
 
1553
	    if (tname != (char *)NULL)
1554
		unlink (tname);
1555
 
1556
	    return -1;
1557
	}
1558
    }
1559
 
1560
/* Otherwise, just open the document */
1561
 
1562
    return S_open (FALSE, hname, O_RDONLY);
1563
}