Subversion Repositories DevTools

Rev

Rev 255 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
/*
2
 * MS-DOS SHELL - Main program, memory and variable management
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 is subject to the following copyright restrictions:
8
 *
9
 * 1.  Redistribution and use in source and binary forms are permitted
10
 *     provided that the above copyright notice is duplicated in the
11
 *     source form and the copyright notice in file sh6.c is displayed
12
 *     on entry to the program.
13
 *
14
 * 2.  The sources (or parts thereof) or objects generated from the sources
15
 *     (or parts of sources) cannot be sold under any circumstances.
16
 *
17
 * When parts of the original 2.1 shell were replaced by the Lexical
18
 * Analsyer written by Simon J. Gerraty (for his Public Domain Korn Shell,
19
 * which is also based on Charles Forsyth original idea), a number of changes
20
 * were made to reflect the changes Simon made to the Parse output tree.  Some
21
 * parts of this code in this module are based on the algorithms/ideas that
22
 * he incorporated into his shell, in particular interfaces to the new Lexical
23
 * Scanner.
24
 *
25
 *    $Header: /cvsroot/device/DEVL/UTILS/SH/Sh1.c,v 1.3 2004/11/04 02:47:59 ayoung Exp $
26
 *
27
 *    $Log: Sh1.c,v $
28
 *    Revision 1.3  2004/11/04 02:47:59  ayoung
29
 *    handle upto 8 PATH definitions
30
 *
31
 *    Revision 1.2  2004/05/10 09:30:06  ayoung
32
 *    improved Path/PATH handling
33
 *    Quote CreateProcess arg0 if embedded spaces are  encountered
34
 *    Native NT exec dont need to check command line length
35
 *    Warning when '@' within redirect list
36
 *    DEBUG_EXEC option, split from PRINT_EXEC option and improved
37
 *
38
 *    Revision 1.1  2002/08/02 06:49:33  adamy
39
 *    imported (reference only)
40
 *
41
 *    Revision 1.1  2001/07/20 05:55:43  ayoung
42
 *    WIN32 support
43
 *
44
 *    Revision 1.1.1.1  1999/12/02 01:11:12  gordonh
45
 *    UTIL
46
 *
47
 *	Revision 2.19  1994/08/25  20:49:11  istewart
48
 *	MS Shell 2.3 Release
49
 *
50
 *	Revision 2.18  1994/02/23  09:23:38  istewart
51
 *	Beta 233 updates
52
 *
53
 *	Revision 2.17  1994/02/01  10:25:20  istewart
54
 *	Release 2.3 Beta 2, including first NT port
55
 *
56
 *	Revision 2.16  1994/01/20  14:51:43  istewart
57
 *	Release 2.3 Beta 1
58
 *
59
 *	Revision 2.15  1994/01/11  17:55:25  istewart
60
 *	Release 2.3 Beta 0 patches
61
 *
62
 *	Revision 2.14  1993/12/01  11:58:34  istewart
63
 *	Release 226 beta
64
 *
65
 *	Revision 2.13  1993/11/09  10:39:49  istewart
66
 *	Beta 226 checking
67
 *
68
 *	Revision 2.12  1993/08/25  16:03:57  istewart
69
 *	Beta 225 - see Notes file
70
 *
71
 *	Revision 2.11  1993/07/02  10:21:35  istewart
72
 *	224 Beta fixes
73
 *
74
 *	Revision 2.10  1993/06/16  12:55:49  istewart
75
 *	Fix the -s and -t options so they work
76
 *
77
 *	Revision 2.9  1993/06/14  11:00:12  istewart
78
 *	More changes for 223 beta
79
 *
80
 *	Revision 2.8  1993/06/02  09:52:35  istewart
81
 *	Beta 223 Updates - see Notes file
82
 *
83
 *	Revision 2.7  1993/02/16  16:03:15  istewart
84
 *	Beta 2.22 Release
85
 *
86
 *	Revision 2.6  1993/01/26  18:35:09  istewart
87
 *	Release 2.2 beta 0
88
 *
89
 *	Revision 2.5  1992/12/14  10:54:56  istewart
90
 *	BETA 215 Fixes and 2.1 Release
91
 *
92
 *	Revision 2.4  1992/11/06  10:03:44  istewart
93
 *	214 Beta test updates
94
 *
95
 *	Revision 2.3  1992/09/03  18:54:45  istewart
96
 *	Beta 213 Updates
97
 *
98
 *	Revision 2.2  1992/07/16  14:33:34  istewart
99
 *	Beta 212 Baseline
100
 *
101
 *	Revision 2.1  1992/07/10  10:52:48  istewart
102
 *	211 Beta updates
103
 *
104
 *	Revision 2.0  1992/04/13  17:39:09  Ian_Stewartson
105
 *     MS-Shell 2.0 Baseline release
106
 */
107
 
108
#include <sys/types.h>
109
#include <sys/stat.h>
110
#include <stdio.h>
111
#include <stdlib.h>
112
#include <signal.h>
113
#include <errno.h>
114
#include <setjmp.h>
115
#include <stdarg.h>
116
#include <string.h>
117
#include <unistd.h>
118
#include <ctype.h>
119
#include <fcntl.h>
120
#include <limits.h>
121
#include <dirent.h>
122
#include <time.h>
123
#include "sh.h"
124
 
125
/* OS2 or DOS, DEBUG MEMORY or normal malloc */
126
 
127
#ifdef OS2_DOSALLOC
128
#  ifdef DEBUG_MEMORY
129
#    define RELEASE_MEMORY(x)	if (DosFreeSeg (SELECTOROF ((x))))	\
130
				    fprintf(stderr, "Memory Release error\n");
131
#  else
132
#    define RELEASE_MEMORY(x)	DosFreeSeg (SELECTOROF ((x)))
133
#  endif
134
 
135
#else
136
#  define RELEASE_MEMORY(x)     free ((x))
137
#endif
138
 
139
/*
140
 * Structure of Malloced space to allow release of space nolonger required
141
 * without having to know about it.
142
 */
143
 
144
typedef struct region {
145
    struct region	*next;
146
    size_t		nbytes;
147
    int			area;
148
} s_region;
149
 
150
static struct region	*MemoryAreaHeader = (s_region *)NULL;
151
 
152
/*
153
 * default shell, search rules
154
 */
155
 
156
#if (OS_TYPE == OS_UNIX)
157
static char		*shellname = "/bin/pksh";
158
static char		search[] = ":/bin:/usr/bin";
159
 
160
extern char		**environ;
161
#else
162
static char		*shellname = "c:/bin/sh.exe";
163
static char		search[] = ";c:/bin;c:/usr/bin";
164
#endif
165
static char		*ymail = "You have mail";
166
static char		*ShellNameLiteral = "sh";
167
static char		*ShellOptions = "D:MPRX:abc:defghijklmnopqrtsuvwxyz0";
168
 
169
#if (OS_TYPE != OS_DOS)
170
static char		DefaultWinTitle[] = "MS Shell";
171
#endif
172
 
173
#if (OS_TYPE == OS_OS2)
174
#  if (OS_SIZE == OS_32)
175
static HEV		SessionQueueSema = 0;
176
static HQUEUE		SessionQueueHandler = 0;
177
#  else
178
static HSEM		SessionQueueSema = 0;
179
static HQUEUE		SessionQueueHandler = 0;
180
#  endif
181
#endif
182
 
183
static char		*tilde = "~";
184
static char		LIT_OSmode[] = "OSMODE";
185
static char		LIT_SHmode[] = "SHMODE";
186
static char		LIT_Dollar[] = "$";
187
#if (OS_TYPE == OS_DOS)
188
static char            *NOExit = "sh: ignoring attempt to leave lowest level shell";
189
#endif
190
static bool		ProcessingDEBUGTrap = FALSE;
191
static bool		ProcessingERRORTrap = FALSE;
192
static unsigned int	ATOE_GFlags;	/* Twalk GLOBAL flags		*/
193
static unsigned int	ATNE_Function;	/* Twalk GLOBAL flags		*/
194
 
195
/*
196
 * Count value for counting the number entries in an array
197
 */
198
 
199
static int		Count_Array;	/* Array size			*/
200
static char		*Count_Name;	/* Array name			*/
201
 
202
/* Integer Variables */
203
 
204
static struct ShellVariablesInit {
205
    char       *Name;                   /* Name of variable             */
206
    int		Status;			/* Initial status		*/
207
    char       *CValue;
208
} InitialiseShellVariables[] = {
209
    { LIT_COLUMNS,		STATUS_INTEGER,		"80"		},
210
    { HistorySizeVariable,	STATUS_INTEGER,		"100"		},
211
    { LineCountVariable,	STATUS_INTEGER,		"1"		},
212
    { LIT_LINES,		STATUS_INTEGER,		"25"		},
213
    { OptIndVariable,		STATUS_INTEGER,		"1"		},
214
    { StatusVariable,		STATUS_INTEGER,		"0"		},
215
    { RandomVariable,		(STATUS_EXPORT | STATUS_INTEGER),
216
							null		},
217
    { SecondsVariable,		(STATUS_EXPORT | STATUS_INTEGER),
218
							null		},
219
    { LIT_OSmode,		(STATUS_EXPORT | STATUS_CANNOT_UNSET |
220
				 STATUS_INTEGER),	null		},
221
    { LIT_SHmode,		(STATUS_EXPORT | STATUS_CANNOT_UNSET |
222
				 STATUS_INTEGER),	null		},
223
    { LIT_Dollar,		(STATUS_CANNOT_UNSET | STATUS_INTEGER),
224
							null		},
225
 
226
    { LastWordVariable,		STATUS_EXPORT,		null		},
303 dpurdie 227
    { PathLiteral,		(STATUS_EXPORT | STATUS_CANNOT_UNSET | STATUS_CONVERT_MSDOS),
227 dpurdie 228
							search		},
229
    { IFS,			(STATUS_EXPORT | STATUS_CANNOT_UNSET),
230
							" \t\n"		},
231
    { PS1,			(STATUS_EXPORT | STATUS_CANNOT_UNSET),
232
							"%e$ "		},
233
    { PS2,			(STATUS_EXPORT | STATUS_CANNOT_UNSET),
234
							"> "		},
235
    { PS3,			STATUS_EXPORT,		"#? "		},
236
    { PS4,			STATUS_EXPORT,		"+ "		},
237
    { HomeVariableName,		STATUS_EXPORT,		null		},
238
    { ShellVariableName,	STATUS_EXPORT,		null		},
239
 
240
#if (OS_TYPE != OS_DOS)
241
    { WinTitleVariable, 	0,			DefaultWinTitle	},
242
#endif
243
 
244
    { (char *)NULL,		0 }
245
};
246
 
247
/*
248
 * List of Editor variables
249
 */
250
 
251
#if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)
252
static char		*EditorVariables[] = {
253
    FCEditVariable, EditorVariable, VisualVariable, (char *)NULL
254
};
255
#endif
256
 
257
					/* Entry directory		*/
258
static char	Start_directory[PATH_MAX + 6] = { 0 };
259
static time_t	ShellStartTime = 0;	/* Load time of shell		*/
260
					/* Original Interrupt 24 address */
261
#if (OS_TYPE == OS_DOS) && !defined (__EMX__)
262
#if defined (__WATCOMC__)
263
#    define INTERRUPT_TYPE		__interrupt __far
264
#  else
265
#    define INTERRUPT_TYPE		interrupt far
266
#  endif
267
static void	(INTERRUPT_TYPE *Orig_I24_V) (void);
268
#endif
269
 
270
#ifdef SIGQUIT
271
static void     (_SIGDECL *qflag)(int) = SIG_IGN;
272
#endif
273
 
274
/* Functions */
275
 
276
static int F_LOCAL	RunCommands (Source *);
277
static unsigned char * F_LOCAL  CheckClassExpression (unsigned char *, int, bool);
278
static bool F_LOCAL	Initialise (int, char **);
279
static void F_LOCAL	CheckForMailArriving (void);
280
static void F_LOCAL	LoadGlobalVariableList (void);
281
static void F_LOCAL	LoadTheProfileFiles (void);
282
static void F_LOCAL	ConvertUnixPathToMSDOS (char *);
283
static void F_LOCAL	ClearUserPrompts (void);
284
static void F_LOCAL	SecondAndRandomEV (char *, long);
285
static void F_LOCAL	SetUpParameterEV (int, char **, char *);
286
static bool F_LOCAL	AllowedToSetVariable (VariableList *);
287
static void F_LOCAL	SetUpANumericValue (VariableList *, long, int);
288
static void F_LOCAL	ProcessErrorExit (char *, va_list);
289
static char * F_LOCAL	SuppressSpacesZeros (VariableList *, char *);
290
static void		AddToNewEnvironment (const void *, VISIT, int);
291
static void		AddToOldEnvironment (const void *, VISIT, int);
292
static void		DeleteEnvironment (const void *, VISIT, int);
293
static void F_LOCAL	CheckOPTIND (char *, long);
294
static void F_LOCAL	CreateIntegerVariables (void);
295
static bool F_LOCAL	ExecuteShellScript (char *);
296
static void 		CountEnvironment (const void *, VISIT, int);
297
 
298
/*
299
 * Process termination
300
 */
301
 
302
#if (OS_TYPE != OS_DOS)
303
static void F_LOCAL	CheckForTerminatedProcess (void);
304
 
305
#  if (OS_TYPE == OS_NT)
306
static void		LookUpJobs (const void *, VISIT, int);
307
#  endif
308
 
309
#else
310
#  define CheckForTerminatedProcess()
311
#endif
312
 
313
/*
314
 * No real argv interface
315
 */
316
 
317
#if (OS_TYPE == OS_UNIX)
318
#define Pre_Process_Argv(a,b)
319
#else
320
static void F_LOCAL	Pre_Process_Argv (char **, int *);
321
#endif
322
 
323
/*
324
 * OS/2 Session Queues
325
 */
326
 
327
#if (OS_TYPE == OS_OS2)
328
static void F_LOCAL	CheckForSessionEnd (void);
329
static void F_LOCAL	CreateTerminationQueues (void);
330
#else
331
#  define CheckForSessionEnd()
332
#  define CreateTerminationQueues()
333
#endif
334
 
335
#if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)
336
static void F_LOCAL	SetEditorMode (char *);
337
#endif
338
 
339
/*
340
 * The Program Name and Command Line, set up by stdargv.c
341
 */
342
 
343
#if (OS_TYPE != OS_UNIX)
344
extern char 		*_APgmName;
345
extern char		*_ACmdLine;
346
#endif
347
 
255 dpurdie 348
/*
349
**  Debug level
350
*/
351
int debug_level = 0;
227 dpurdie 352
 
353
/*
354
 * The main program starts here
355
 */
356
#if defined(WIN32)
357
void __cdecl main(int argc, char **argv, char **envp)
358
#else
359
void main(int argc, char **argv)
360
#endif
361
{
362
    int			cflag = 0;
363
    int			sc;
364
    char                *name;
365
					/* Load up various parts of the	*/
366
					/* system			*/
367
    bool                OptionsRflag;
368
    bool		OptionsXflag = FALSE;	/* -x option from	*/
369
						/* command line		*/
370
    bool		Level0Shell = FALSE;	/* Level 0 (read profile)*/
371
    jmp_buf		ReturnPoint;
372
    char		*cp;
373
    Source		*s;
374
    int			fid;
375
    bool		TTYInput;
376
    bool		PIPEInput;
377
    bool		RootShell = FALSE;
378
 
379
    (void) envp;
380
 
381
    name = *argv;
382
    OptionsRflag = Initialise(argc, argv);
383
 
384
    SetWindowName ((char *)NULL);
385
 
386
/* Create Parse input */
387
 
388
    s = pushs (SFILE);
389
    s->u.file = stdin;
390
 
391
/* Set up start time */
392
 
393
    ShellStartTime = time ((time_t *)NULL);
394
 
395
/* Preprocess options to convert two character options of the form /x to
396
 * -x.  Some programs!!
397
 */
398
 
399
    if (argc > 1)
400
        Pre_Process_Argv (argv, &argc);
401
 
402
/* Save the start directory for when we exit */
403
 
404
    S_getcwd (Start_directory, 0);
405
 
303 dpurdie 406
/* Force Shell Globals as DOS */
407
#if (OS_TYPE == OS_NT) ||(OS_TYPE == OS_DOS)
408
	ShellGlobalFlags |= FLAGS_MSDOS_FORMAT;
409
#endif
410
 
227 dpurdie 411
/* Process the options */
412
 
413
    while ((sc = GetOptions (argc, argv, ShellOptions, GETOPT_MESSAGE)) != EOF)
414
    {
415
	switch (sc)
416
	{
417
	    case '?':
418
		FinalExitCleanUp (1);
419
 
420
	    case '0':				/* Level 0 flag for DOS	*/
421
		Level0Shell = TRUE;
422
		break;
423
 
424
	    case 'r':				/* Restricted		*/
425
		OptionsRflag = TRUE;
426
		break;
427
 
428
	    case 'c':				/* Command on line	*/
429
		ClearUserPrompts ();
430
		cflag = 1;
431
		s->type = SSTRING;
432
		s->str = OptionArgument;
433
		SetVariableFromString ("_cString", OptionArgument);
434
		break;
435
 
436
	    case 'q':				/* No quit ints		*/
437
#ifdef SIGQUIT
438
		qflag = SIG_DFL;
439
#endif
440
		break;
441
 
442
 
443
	    case 'X':
444
		if (!GotoDirectory (OptionArgument, GetCurrentDrive ()))
445
		{
446
		    PrintErrorMessage ("%s: bad directory", OptionArgument);
447
		    FinalExitCleanUp (1);
448
		}
449
 
450
		break;
451
 
452
	    case 'x':
453
		OptionsXflag = TRUE;
454
		break;
455
 
456
	    case 'M':
457
		ShellGlobalFlags |= FLAGS_MSDOS_FORMAT;
458
	        break;
459
 
460
	    case 'D':
461
		AssignVariableFromString (OptionArgument, (int *)NULL);
462
	        break;
463
 
464
	    case 'R':
465
		RootShell = TRUE;
466
		ChangeInitLoad = TRUE;	/* Change load .ini pt.		*/
467
		break;
468
 
469
#if (OS_TYPE != OS_DOS)
470
	    case 'P':				/* Use real pipes	*/
471
    		ShellGlobalFlags |= FLAGS_REALPIPES;
472
    		break;
473
#endif
474
 
475
	    case 's':				/* standard input	*/
476
	    	if (cflag)
477
		    PrintErrorMessage ("cannot use -s and -c together");
478
 
479
	    case 'i':				/* Set interactive	*/
480
		InteractiveFlag = TRUE;
481
 
482
	    default:
483
		if (islower (sc))
484
		    FL_SET (sc);
485
	}
486
 
487
/* If -s, set the argv to point to -s so that the rest of the parameters
488
 * get used as parameters ($digit) values.
489
 */
490
 
491
        if (FL_TEST (FLAG_POSITION))
492
	{
493
	    OptionIndex--;
494
	    break;
495
	}
496
    }
497
 
498
/* Under UNIX, check for login shell */
499
 
500
#if (OS_TYPE == OS_UNIX)
501
    if (*argv[0] == '-')
502
	Level0Shell = TRUE;
503
#endif
504
 
505
    argv += OptionIndex;
506
    argc -= OptionIndex;
507
 
508
/* Get configuration info */
509
 
510
    Configure_Keys ();
511
 
512
/*
513
 * Check for terminal input.  A special case for OS/2.  If pipe input and
514
 * output and no arguments, set interactive flag.
515
 *
516
 * Unset the variable after!
517
 */
518
 
519
    TTYInput  = C2bool ((IS_TTY (0) && IS_TTY (1)));
520
    PIPEInput = C2bool ((IS_Pipe (0) && IS_Pipe (1) &&
521
                         (GetVariableAsString (LIT_AllowTTY, FALSE) != null)));
522
 
523
    UnSetVariable (LIT_AllowTTY, -1, TRUE);
524
 
525
    if ((s->type == SFILE) &&
526
        ((FL_TEST (FLAG_INTERACTIVE)) || TTYInput || PIPEInput) &&
527
        !cflag &&
528
        ((argc == 0) || FL_TEST (FLAG_POSITION)))
529
    {
530
        s->type = STTY;
531
        FL_SET (FLAG_POSITION);
532
        FL_SET (FLAG_INTERACTIVE);
533
        InteractiveFlag = TRUE;
534
 
535
        if  (TTYInput)
536
            PrintVersionNumber (stderr);
537
    }
538
 
539
/* Root shell - check only tty devices */
540
 
541
    if (RootShell)
542
    {
543
       if ((s->type != STTY) || !TTYInput)
544
            PrintErrorMessage ("-R not valid on non-interactive shells");
545
 
546
#if (OS_TYPE == OS_DOS) && !defined (__EMX__)
547
        Orig_I24_V = (void (INTERRUPT_TYPE *)())NULL;
548
#endif
549
    }
550
 
551
/*
552
 * Execute commands from a file? - disable prompts
553
 */
554
 
555
    if ((s->type == SFILE) && (argc > 0) && !InteractiveFlag)
556
    {
557
        ClearUserPrompts ();
558
	name = *argv;
559
 
560
	if (((fid = S_open (FALSE, s->file = name, O_RMASK)) < 0) ||
561
	    ((s->u.file = ReOpenFile (ReMapIOHandler (fid), sOpenReadMode))
562
	                == (FILE *)NULL))
563
	{
564
	    PrintErrorMessage (LIT_Emsg, "cannot open script", name,
565
			       strerror (errno));
566
	    FinalExitCleanUp (1);
567
	}
568
 
569
/* Un-map this file descriptor from the current environment so it does not
570
 * get closed at the wrong times
571
 */
572
        ChangeFileDescriptorStatus (fileno (s->u.file), FALSE);
573
 
574
#if (OS_TYPE != OS_DOS)
575
        SetVariableFromString (WinTitleVariable, name);
576
#endif
577
    }
578
 
579
/* Setup stderr, stdout buffering */
580
 
581
    setvbuf (stderr, (char *)NULL, _IONBF, 0);
582
 
583
    if (InteractiveFlag && !IS_TTY (1))
584
        setvbuf (stdout, (char *)NULL, _IOLBF, BUFSIZ);
585
 
586
/* Set up the $- variable */
587
 
588
    SetShellSwitches ();
589
 
590
#ifdef SIGQUIT
591
    signal (SIGQUIT, qflag);
592
#endif
593
 
594
/* Set up signals */
595
 
596
    if (InteractiveFlag)
597
        signal (SIGTERM, TerminateSignalled);
598
 
599
/* Load any parameters */
600
 
601
   SetUpParameterEV (argc, argv, name);
602
 
603
/* Return point */
604
 
605
    if (SetErrorPoint (ReturnPoint))
606
        ExitTheShell (FALSE);
607
 
608
    signal (SIGINT, InterruptSignalled);
609
 
610
/* Read profile ?.  Init EMAC first for binding keys */
611
 
612
    if (((name != (char *)NULL) && (*name == CHAR_HYPHEN)) || Level0Shell)
613
    {
614
#if defined (FLAGS_EMACS) || defined (FLAGS_GMACS)
615
        EMACS_Initialisation ();
616
#endif
617
        LoadTheProfileFiles ();
618
    }
619
 
620
/*
621
 * Load history and configure
622
 */
623
 
624
    if (s->type == STTY)
625
    {
626
        HistoryEnabled = TRUE;
627
        LoadHistory ();
628
    }
629
 
630
/*
631
 * If the x or r flag was set on the command line, set it now after the
632
 * profiles have been executed.
633
 */
634
 
255 dpurdie 635
    if ( debug_level & DEBUG_EXEC_ALL )
227 dpurdie 636
    {
255 dpurdie 637
    	FL_SET (FLAG_DEBUG_EXECUTE);
227 dpurdie 638
     	FL_CLEAR (FLAG_WARNING);
639
    }
640
    if (OptionsXflag)
641
    {
642
        FL_SET (FLAG_PRINT_EXECUTE);
643
    }
644
 
645
    if (OptionsRflag)
646
    {
647
	FL_SET (FLAG_READONLY_SHELL);
648
	RestrictedShellFlag = TRUE;
649
    }
650
 
651
/*
652
 * Execute $ENV
653
 *
654
 * TOCHECK - substitute (cp, DOTILDE);
655
 */
656
 
657
    if ((cp = GetVariableAsString (ENVVariable, FALSE)) != null)
658
        ExecuteShellScript (cp);
659
 
660
/* If interactive, set up Editor modes */
661
#if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)
662
    if (InteractiveFlag)
663
    {
664
        char    **rep = EditorVariables;
665
 
666
	while (*rep != (char *)NULL)
667
	{
668
	    if ((cp = GetVariableAsString (*(rep++), FALSE)) != null)
669
	    {
670
                SetEditorMode (cp);
671
		break;
672
	    }
673
	}
674
    }
675
#endif
676
 
677
/*
678
 * Execute what ever we have to do!!
679
 */
680
 
681
//  while (TRUE)
682
    for (;;)
683
    {
684
	switch (SetErrorPoint (ReturnPoint))
685
	{
686
	    case TERMINATE_POINT_SET:
687
		RunCommands (s);		/* Drop to exit		*/
688
 
689
	    case TERMINATE_COMMAND:
690
	    default:
691
		ExitTheShell (FALSE);
692
 
693
/* Re-set TTY input.  If we reach this point, the shell is a root shell and
694
 * the no exit message has been displayed.  Reset the shell for input from the
695
 * TTY.
696
 */
697
 
698
	    case TERMINATE_SHELL:
699
		s->type = STTY;
700
		break;
701
	}
702
    }
703
}
704
 
705
 
706
/*
707
 * Process a script file
708
 */
709
 
710
static bool F_LOCAL ExecuteShellScript (char *name)
711
{
712
    FILE	*f = stdin;
713
    int		fp;
714
    Source	*s;
715
 
716
    if (strcmp (name, "-") != 0)
717
    {
718
	if ((fp = OpenForExecution (name, (char **)NULL, (int *)NULL)) < 0)
719
	    return FALSE;
720
 
721
	if ((f = ReOpenFile (fp = ReMapIOHandler (fp),
722
			     sOpenReadMode)) == (FILE *)NULL)
723
	    return FALSE;
724
    }
725
 
726
    (s = pushs (SFILE))->u.file = f;
727
    s->file = name;
728
 
729
    RunCommands (s);
730
 
731
    if (f != stdin)
732
	CloseFile (f);
733
 
734
    return TRUE;
735
}
736
 
737
 
738
/*
739
 * run the commands from the input source, returning status.
740
 */
741
 
742
static int F_LOCAL RunCommands (Source *src)
743
{
744
    int		i;
745
    jmp_buf	ReturnPoint;
746
    C_Op	*t = (C_Op *)NULL;
747
    bool	wastty;
748
    int		EntryMemoryLevel = MemoryAreaLevel + 1;
749
 
750
    CreateNewEnvironment ();
751
    e.ErrorReturnPoint = (ErrorPoint)NULL;
752
    e.line = GetAllocatedSpace (LINE_MAX);
753
 
754
/*
755
 * Run until the end
756
 */
757
 
758
//  while (TRUE)
759
    for (;;)
760
    {
761
 
762
/* Initialise space */
763
 
764
	MemoryAreaLevel = EntryMemoryLevel;
765
	ReleaseMemoryArea (MemoryAreaLevel);
766
	SW_intr = 0;
767
	ProcessingEXECCommand = FALSE;
768
 
769
	if (src->next == NULL)
770
	    src->echo = C2bool (FL_TEST (FLAG_ECHO_INPUT));
771
 
772
/*
773
 * Set up a few things for console input - cursor, mail prompt etc
774
 */
775
 
776
	if ((wastty = C2bool (src->type == STTY)) != FALSE)
777
	{
778
	    PositionCursorInColumnZero ();
779
	    CheckForMailArriving ();
780
	    CheckForTerminatedProcess ();
781
	    CloseAllHandlers ();	/* Clean up any open shell files */
782
	}
783
 
784
	LastUserPrompt = PS1;
785
	FlushStreams ();			/* Clear output */
786
 
787
/* Set execute function recursive level and the SubShell count to zero */
788
 
789
	Execute_stack_depth = 0;
790
 
791
/* Set up Redirection IO (Saved) array and SubShell Environment information */
792
 
793
	NSave_IO_E = 0;		/* Number of entries		*/
794
	MSave_IO_E = 0;		/* Max Number of entries	*/
795
	NSubShells = 0;		/* Number of entries		*/
796
	MSubShells = 0;		/* Max Number of entries	*/
797
	CurrentFunction = (FunctionList *)NULL;
798
	CurrentFunction = (FunctionList *)NULL;
799
	ProcessingDEBUGTrap = FALSE;
800
	ProcessingERRORTrap = FALSE;
801
	Break_List = (Break_C *)NULL;
802
	Return_List = (Break_C *)NULL;
803
	SShell_List = (Break_C *)NULL;
804
	ProcessingEXECCommand = FALSE;
805
 
806
/* Get the line and process it */
807
 
808
	if (SetErrorPoint (ReturnPoint) ||
809
	    ((t = BuildParseTree (src)) == (C_Op *)NULL) || SW_intr)
810
	{
811
	    ScrapHereList ();
812
 
813
	    if ((!InteractiveFlag && SW_intr) || FL_TEST (FLAG_ONE_COMMAND))
814
		ExitTheShell (FALSE);
815
 
816
/* Go round again */
817
 
818
	    src->str = null;
819
	    SW_intr = 0;
820
	    continue;
821
	}
822
 
823
/* Ok - reset some variables and then execute the command tree */
824
 
825
	SW_intr = 0;
826
	ProcessingEXECCommand = FALSE;
827
	FlushHistoryBuffer ();		/* Save history			*/
828
 
829
/* Check for exit */
830
 
831
	if ((t != NULL) && (t->type == TEOF))
832
	{
833
	    if (wastty && (ShellGlobalFlags & FLAGS_IGNOREEOF))
834
	    {
835
		PrintWarningMessage ("Use `exit'");
836
		src->type = STTY;
837
		continue;
838
	    }
839
 
840
	    else
841
		break;
842
	}
843
 
844
/* Execute the parse tree */
845
 
846
	if ((SetErrorPoint (ReturnPoint) == 0) &&
847
	    ((!FL_TEST (FLAG_NO_EXECUTE)) || (src->type == STTY)))
848
	    ExecuteParseTree (t, NOPIPE, NOPIPE, 0);
849
 
850
/* Make sure the I/O and environment are back at level 0 and then clear them */
851
 
852
	e.ErrorReturnPoint = (ErrorPoint)NULL;
853
	Execute_stack_depth = 0;
854
	ClearExtendedLineFile ();
855
 
856
	if (NSubShells != 0)
857
	    DeleteGlobalVariableList ();
858
 
859
	if (NSave_IO_E)
860
	    RestoreStandardIO (0, TRUE);
861
 
862
	if (MSubShells)
863
	    ReleaseMemoryCell ((void *)SubShells);
864
 
865
	if (MSave_IO_E)
866
	    ReleaseMemoryCell ((void *)SSave_IO);
867
 
868
    /* Check for interrupts */
869
 
870
	if ((!InteractiveFlag && SW_intr) || FL_TEST (FLAG_ONE_COMMAND))
871
	{
872
	    ProcessingEXECCommand = FALSE;
873
	    ExitTheShell (FALSE);
874
	}
875
 
876
/* Run any traps that are required */
877
 
878
	if ((i = InterruptTrapPending) != 0)
879
	{
880
	    InterruptTrapPending = 0;
881
	    RunTrapCommand (i);
882
	}
883
    }
884
 
885
/*
886
 * Terminate the current environment
887
 */
888
 
889
    QuitCurrentEnvironment ();
890
    return ExitStatus;
891
}
892
 
893
 
894
/*
895
 * Set up the value of $-
896
 */
897
 
898
void SetShellSwitches (void)
899
{
900
    char	*cp, c;
901
    char	m['z' - 'a' + 2];
902
 
903
    for (cp = m, c = 'a'; c <= 'z'; ++c)
904
    {
905
	if (FL_TEST (c))
906
	    *(cp++) = c;
907
    }
908
 
909
    if (ShellGlobalFlags & FLAGS_MSDOS_FORMAT)
910
	*(cp++) = 'M';
911
 
912
    *cp = 0;
913
    SetVariableFromString (ShellOptionsVariable, m);
914
}
915
 
916
 
917
/*
918
 * Terminate current environment with an error
919
 */
920
 
921
void TerminateCurrentEnvironment (int TValue)
922
{
923
    FlushStreams ();			/* Clear output */
924
 
925
    if (e.ErrorReturnPoint != (ErrorPoint)NULL)
926
	ExitErrorPoint (TValue);
927
 
928
    /* NOTREACHED */
929
}
930
 
931
 
932
/*
933
 * Exit the shell
934
 */
935
 
936
void ExitTheShell (bool ReturnRequired)
937
{
938
    FlushStreams ();			/* Clear output */
939
 
940
    if (ProcessingEXECCommand)
941
	TerminateCurrentEnvironment (TERMINATE_COMMAND);
942
 
943
#if (OS_TYPE == OS_DOS) && !defined (__EMX__)
944
    if (Orig_I24_V == (void (INTERRUPT_TYPE *)())NULL)
945
    {
946
	feputs (NOExit);
947
 
948
	if (!ReturnRequired)
949
	    TerminateCurrentEnvironment (TERMINATE_SHELL);
950
    }
951
#else
952
    (void) ReturnRequired;
953
#endif
954
 
955
/* Clean up */
956
 
957
    ScrapHereList ();
958
    FreeAllHereDocuments (1);
959
 
960
/* Trap zero on exit */
961
 
962
    RunTrapCommand (0);
963
 
964
/* Dump history on exit */
965
 
966
    DumpHistory ();
967
 
968
    CloseAllHandlers ();
969
 
970
/* If this is a command only - restore the directory because DOS doesn't
971
 * and the user might expect it
972
 */
973
 
974
    if (*Start_directory)
975
	RestoreCurrentDirectory (Start_directory);
976
 
977
/* If this happens during startup - we restart */
978
 
979
#if (OS_TYPE == OS_DOS) && !defined (__EMX__)
980
    if (Orig_I24_V == (void (INTERRUPT_TYPE *)())NULL)
981
	return;
982
#endif
983
 
984
/*
985
 * Clean up any Here Document files left in the function tree
986
 */
987
 
988
    DeleteAllFunctions ();
989
 
990
/* Exit - hurray */
991
 
992
    FinalExitCleanUp (ExitStatus);
993
 
994
/* NOTREACHED */
995
}
996
 
997
 
998
/*
999
 * Output warning message
1000
 */
1001
 
1002
int PrintWarningMessage (char *fmt, ...)
1003
{
1004
    va_list	ap;
1005
 
1006
    va_start (ap, fmt);
1007
    vfprintf (stderr, fmt, ap);
1008
    feputc (CHAR_NEW_LINE);
1009
    ExitStatus = -1;
1010
 
1011
/* If leave on error - exit */
1012
 
1013
    if (FL_TEST (FLAG_EXIT_ON_ERROR))
1014
	ExitTheShell (FALSE);
1015
 
1016
    va_end (ap);
1017
    return 1;
1018
}
1019
 
1020
 
1021
/*
1022
 * Shell error message
1023
 */
1024
 
1025
void ShellErrorMessage (char *fmt, ...)
1026
{
1027
    va_list	ap;
1028
 
1029
/* Error message processing */
1030
 
1031
    if (source->file == (char *)NULL)
1032
	feputs ("sh: ");
1033
 
1034
    else
1035
	fprintf (stderr, "%s: at line %d, ", source->file, source->line);
1036
 
1037
    va_start (ap, fmt);
1038
    ProcessErrorExit (fmt, ap);
1039
    va_end (ap);
1040
}
1041
 
1042
 
1043
/*
1044
 * Output error message
1045
 */
1046
 
1047
void PrintErrorMessage (char *fmt, ...)
1048
{
1049
    va_list	ap;
1050
 
1051
/* Error message processing */
1052
 
1053
    va_start (ap, fmt);
1054
    ProcessErrorExit (fmt, ap);
1055
    va_end (ap);
1056
}
1057
 
1058
/*
1059
 * Common processing for PrintErrorMessage and ShellError Message
1060
 */
1061
 
1062
static void F_LOCAL ProcessErrorExit (char *fmt, va_list ap)
1063
{
1064
    vfprintf (stderr, fmt, ap);
1065
    feputc (CHAR_NEW_LINE);
1066
 
1067
    ExitStatus = -1;
1068
 
1069
    if (FL_TEST (FLAG_EXIT_ON_ERROR))
1070
	ExitTheShell (FALSE);
1071
 
1072
/* Error processing */
1073
 
1074
    if (FL_TEST (FLAG_NO_EXECUTE))
1075
	return;
1076
 
1077
/* If not interactive - exit */
1078
 
1079
    if (!InteractiveFlag)
1080
	ExitTheShell (FALSE);
1081
 
1082
    if (e.ErrorReturnPoint != (ErrorPoint)NULL)
1083
	ExitErrorPoint (TERMINATE_COMMAND);
1084
 
1085
/* CloseAllHandlers (); Removed - caused problems.  There may be problems
1086
 * remaining with files left open?
1087
 */
1088
}
1089
 
1090
 
1091
/*
1092
 * Create or delete a new environment.  If f is set, delete the environment
1093
 */
1094
 
1095
void CreateNewEnvironment (void)
1096
{
1097
    ShellFileEnvironment	*ep;
1098
 
1099
/* Create a new environment */
1100
 
1101
    if ((ep = (ShellFileEnvironment *)
1102
		GetAllocatedSpace (sizeof (ShellFileEnvironment)))
1103
		== (ShellFileEnvironment *)NULL)
1104
    {
1105
	while (e.PreviousEnvironment)
1106
	    QuitCurrentEnvironment ();
1107
 
1108
	TerminateCurrentEnvironment (TERMINATE_COMMAND);
1109
    }
1110
 
1111
    *ep = e;
1112
    e.PreviousEnvironment = ep;
1113
    e.IOMap = 0L;
1114
    e.OpenStreams = (Word_B *)NULL;
1115
}
1116
 
1117
 
1118
/*
1119
 * Exit the current environment successfully
1120
 */
1121
 
1122
void QuitCurrentEnvironment (void)
1123
{
1124
    ShellFileEnvironment	*ep;
1125
    unsigned long		FdMap;
1126
    Word_B			*wb;
1127
    int				NEntries;
1128
    int				i;
1129
 
1130
/* Restore old environment, delete the space and close any files opened in
1131
 * this environment
1132
 */
1133
 
1134
    if ((ep = e.PreviousEnvironment) != (ShellFileEnvironment *)NULL)
1135
    {
1136
 
1137
/* Close opened streams */
1138
 
1139
	wb = e.OpenStreams;
1140
	NEntries = WordBlockSize (wb);
1141
 
1142
	for (i = 0; i < NEntries; i++)
1143
	{
1144
	    if (wb->w_words[i] != (char *)NULL)
1145
		fclose ((FILE *)wb->w_words[i]);
1146
	}
1147
 
1148
/* Get the files used in this environment to close */
1149
 
1150
	FdMap = e.IOMap;
1151
	e = *ep;
1152
 
1153
	ReleaseMemoryCell ((void *)ep);
1154
 
1155
	for (i = 0; i < 32; i++)
1156
	{
1157
	    if (FdMap & (1L << i))
1158
		S_close (i + FDBASE, TRUE);
1159
	}
1160
    }
1161
}
1162
 
1163
/*
1164
 * Convert binary to ascii
1165
 */
1166
 
1167
char *IntegerToString (int n)
1168
{
1169
    static char		nt[10];
1170
 
1171
    sprintf (nt, "%u", n);
1172
    return nt;
1173
}
1174
 
1175
/*
1176
 * SIGINT interrupt processing
1177
 */
1178
void _SIGDECL InterruptSignalled (int signo)
1179
{
1180
/* Restore signal processing and set SIGINT detected flag */
1181
 
1182
    signal (signo, InterruptSignalled);
1183
 
1184
#ifdef SIGNALDEBUG
1185
    fprintf (stderr, "Interrupt %d detected\n", signo); fflush (stderr);
1186
#endif
1187
 
1188
/* Under OS/2, if the Ignore Interrupts flag is set, ignore them.  To do
1189
 * with starting OS/2 programs in sh3.c
1190
 */
1191
 
1192
#if (OS_TYPE != OS_DOS)
1193
    if (IgnoreInterrupts)
1194
        return;
1195
#endif
1196
 
1197
/* Set interrupt detected */
1198
 
1199
    SW_intr = 1;
1200
 
1201
/* Are we talking to the user?  Yes - Abandon processing */
1202
 
1203
    if (InteractiveFlag)
1204
	TerminateCurrentEnvironment (TERMINATE_COMMAND);
1205
 
1206
/* No - exit */
1207
 
1208
    else
1209
    {
1210
	ProcessingEXECCommand = FALSE;
1211
	ExitStatus = 1;
1212
	ExitTheShell (FALSE);
1213
    }
1214
}
1215
 
1216
/*
1217
 * Grap some space and check for an error
1218
 */
1219
 
1220
void *GetAllocatedSpace (size_t n)
1221
{
1222
    void	*cp;
1223
 
1224
    if ((cp = AllocateMemoryCell (n)) == (void *)NULL)
1225
	PrintErrorMessage (BasicErrorMessage, ShellNameLiteral, Outofmemory1);
1226
 
1227
    return cp;
1228
}
1229
 
1230
/*
1231
 * Re-allocate some space
1232
 */
1233
 
1234
void *ReAllocateSpace (void *OldSpace, size_t NewSize)
1235
{
1236
    void	*NewSpace;
1237
 
1238
    if ((NewSpace = GetAllocatedSpace (NewSize)) == (void *)NULL)
1239
        return NewSpace;
1240
 
1241
    if (OldSpace != (void *)NULL)
1242
    {
1243
	size_t	OldSize = ((s_region *)((char *)OldSpace -
1244
					sizeof (s_region)))->nbytes;
1245
 
1246
	SetMemoryAreaNumber (NewSpace, GetMemoryAreaNumber (OldSpace));
1247
	memcpy (NewSpace, OldSpace, OldSize);
1248
	ReleaseMemoryCell (OldSpace);
1249
    }
1250
 
1251
    return NewSpace;
1252
}
1253
 
1254
 
1255
/*
1256
 * Duplicate a memory Area
1257
 */
1258
 
1259
void *DuplicateMemoryCell (void *cell)
1260
{
1261
    void	*new;
1262
    size_t	len = ((s_region *)((char *)cell - sizeof (s_region)))->nbytes;
1263
 
1264
    if ((new = AllocateMemoryCell (len)) == (void *)NULL)
1265
	PrintErrorMessage (BasicErrorMessage, ShellNameLiteral, Outofmemory1);
1266
 
1267
    else
1268
	memcpy (new, cell, len);
1269
 
1270
    return new;
1271
}
1272
 
1273
 
1274
/*
1275
 * Get memory Area size
1276
 */
1277
 
1278
size_t GetMemoryCellSize (void *cell)
1279
{
1280
    return ((s_region *)((char *)cell - sizeof (s_region)))->nbytes;
1281
}
1282
 
1283
/*
1284
 * Save a string in a given area
1285
 */
1286
 
1287
char *StringSave (char *s)
1288
{
1289
    char	*cp;
1290
 
1291
    if ((cp = GetAllocatedSpace (strlen (s) + 1)) != (char *)NULL)
1292
    {
1293
	SetMemoryAreaNumber ((void *)cp, 0);
1294
	return strcpy (cp, s);
1295
    }
1296
 
1297
    return null;
1298
}
1299
 
1300
/*
1301
 * Duplicate at current Memory level
1302
 */
1303
 
1304
char *StringCopy (char *s)
1305
{
1306
    char	*cp;
1307
 
1308
    if ((cp = GetAllocatedSpace (strlen (s) + 1)) != (char *)NULL)
1309
	return strcpy (cp, s);
1310
 
1311
    return null;
1312
}
1313
 
1314
/*
1315
 * trap handling - Save signal number and restore signal processing
1316
 */
1317
 
1318
void _SIGDECL TerminateSignalled (int i)
1319
{
1320
    if (i == SIGINT)		/* Need this because swapper sets it	*/
1321
    {
1322
	SW_intr = 0;
1323
    }
1324
 
1325
    InterruptTrapPending = i;
1326
    signal (i, TerminateSignalled);
1327
}
1328
 
1329
/*
1330
 * Execute a trap command
1331
 *
1332
 *  0 - exit trap
1333
 * -1 - Debug Trap
1334
 * -2 - Error Trap
1335
 */
1336
 
1337
void RunTrapCommand (int i)
1338
{
1339
    Source	*s;
1340
    char	*trapstr;
1341
    char	tval[10];
1342
    char	*tvalp = tval;
1343
 
1344
/* Check for special values and recursion */
1345
 
1346
    if (i == -1)
1347
    {
1348
	tvalp = Trap_DEBUG;
1349
 
1350
	if (ProcessingDEBUGTrap)
1351
	    return;
1352
 
1353
	ProcessingDEBUGTrap = TRUE;
1354
    }
1355
 
1356
/* Error trap */
1357
 
1358
    else if (i == -2)
1359
    {
1360
	tvalp = Trap_ERR;
1361
 
1362
	if (ProcessingERRORTrap)
1363
	    return;
1364
 
1365
	ProcessingERRORTrap = TRUE;
1366
    }
1367
 
1368
    else
1369
	sprintf (tval, "~%d", i);
1370
 
1371
    if ((trapstr = GetVariableAsString (tvalp, FALSE)) == null)
1372
	return;
1373
 
1374
/* If signal zero, save a copy of the trap value and then delete the trap */
1375
 
1376
    if (i == 0)
1377
    {
1378
	trapstr = StringCopy (trapstr);
1379
	UnSetVariable (tval, -1, TRUE);
1380
    }
1381
 
1382
    (s = pushs (SSTRING))->str = trapstr;
1383
    RunACommand (s, (char **)NULL);
1384
 
1385
    ProcessingDEBUGTrap = FALSE;
1386
    ProcessingERRORTrap = FALSE;
1387
}
1388
 
1389
/*
1390
 * Find the given name in the dictionary and return its value.  If the name was
1391
 * not previously there, enter it now and return a null value.
1392
 */
1393
 
1394
VariableList    *LookUpVariable (char *name,    /* Variable name        */
1395
				 int  Index,	/* Array Index		*/
1396
				 bool cflag)	/* Create flag		*/
1397
{
1398
    VariableList		*vp;
1399
    VariableList		**vpp;
1400
    int				c;
1401
    static VariableList		dummy;
1402
    void                        (_SIGDECL *save_signal)(int);
1403
 
1404
/* Set up the dummy variable */
1405
 
1406
    memset (&dummy, 0, sizeof (VariableList));
1407
    dummy.name = name;
1408
    dummy.status = STATUS_READONLY;
1409
    dummy.value = null;
1410
 
1411
/* If digit string - use the dummy to return the value */
1412
 
1413
    if (isdigit (*name))
1414
    {
1415
	for (c = 0; isdigit (*name) && (c < 1000); name++)
1416
	    c = c * 10 + *name - '0';
1417
 
1418
	c += Index;
1419
	dummy.value = (c <= ParameterCount) ? ParameterArray[c] : null;
1420
	return &dummy;
1421
    }
1422
 
1423
/* Look up in list */
1424
 
1425
    dummy.index = Index;
1426
    vpp = (VariableList **)tfind (&dummy, &VariableTree, SearchVariable);
1427
 
1428
/* If we found it, return it */
1429
 
1430
    if (vpp != (VariableList **)NULL)
1431
    {
1432
        vp = *vpp;
1433
 
1434
/* Special processing for SECONDS and RANDOM */
1435
 
1436
	if (!strcmp (name, SecondsVariable) &&
1437
	    !(DisabledVariables & DISABLE_SECONDS))
1438
	    SetUpANumericValue (vp, time ((time_t *)NULL) - ShellStartTime, 10);
1439
 
1440
	else if (!strcmp (name, RandomVariable) &&
1441
		 !(DisabledVariables & DISABLE_RANDOM))
1442
	    SetUpANumericValue (vp, (long)rand(), 10);
1443
 
1444
	return vp;
1445
    }
1446
 
1447
/* If we don't want to create it, return a dummy */
1448
 
1449
    dummy.status |= STATUS_NOEXISTANT;
1450
 
1451
    if (!cflag)
1452
	return &dummy;
1453
 
1454
/* Create a new variable.  If no memory, use the dummy */
1455
 
1456
    dummy.name = null;
1457
 
1458
    if ((vp = (VariableList *)GetAllocatedSpace (sizeof (VariableList)))
1459
		== (VariableList *)NULL)
1460
	return &dummy;
1461
 
1462
    if ((vp->name = StringCopy (name)) == null)
1463
    {
1464
	ReleaseMemoryCell ((void *)vp);
1465
	return &dummy;
1466
    }
1467
 
1468
/* Set values */
1469
 
1470
    vp->value = null;
1471
    vp->index = Index;
1472
    vp->status = NSubShells ? 0 : STATUS_GLOBAL;
1473
 
1474
/* Save signals */
1475
 
1476
    save_signal = signal (SIGINT, SIG_IGN);
1477
 
1478
/* Add to the tree */
1479
 
1480
    if (tsearch (vp, &VariableTree, SearchVariable) == (void *)NULL)
1481
    {
1482
	ReleaseMemoryCell ((void *)vp->name);
1483
	ReleaseMemoryCell ((void *)vp);
1484
        vp = &dummy;
1485
    }
1486
 
1487
/* OK Added OK - set up memory */
1488
 
1489
    else
1490
    {
1491
	SetMemoryAreaNumber ((void *)vp, 0);
1492
	SetMemoryAreaNumber ((void *)vp->name, 0);
1493
    }
1494
 
1495
/* Restore signals */
1496
 
1497
    signal (SIGINT, save_signal);
1498
 
1499
    return vp;
1500
}
1501
 
1502
/*
1503
 * Count the number of entries in an Array Variable
1504
 */
1505
 
1506
int     CountVariableArraySize (char *name)
1507
{
1508
    if (isdigit (*name))
1509
	return ParameterCount;
1510
 
1511
    Count_Array = 0;
1512
    Count_Name = name;
1513
    twalk (VariableTree, CountEnvironment);
1514
    return Count_Array;
1515
}
1516
 
1517
/*
1518
 * TWALK - Count the Environment Variables in an array
1519
 */
1520
 
1521
static void CountEnvironment (const void *key, VISIT visit, int level)
1522
{
1523
    (void) level;
1524
 
1525
    if (((visit == postorder) || (visit == leaf)) &&
1526
       (strcmp (Count_Name, (*(VariableList **)key)->name) == 0))
1527
	Count_Array++;
1528
}
1529
 
1530
/*
1531
 * TFIND & TSEARCH - Search the VARIABLE TREE for an entry
1532
 */
1533
 
1534
int	SearchVariable (const void *key1, const void *key2)
1535
{
1536
    int			diff;
1537
 
1538
    if ((diff = strcmp (((VariableList *)key1)->name,
1539
			((VariableList *)key2)->name)) != 0)
1540
	return diff;
1541
 
1542
    return ((VariableList *)key1)->index - ((VariableList *)key2)->index;
1543
}
1544
 
1545
/*
1546
 * Execute an assignment.  If a valid assignment, load it into the variable
1547
 * list.
1548
 *
1549
 * If value is not NULL, assign it.  Otherwise don't
1550
 */
1551
 
1552
bool    AssignVariableFromString (char *String, /* assignment string       */
1553
                                  int  *Index)  /* Index value returned    */
1554
{
1555
    char	*cp;
1556
    long	value = 0;
1557
 
1558
/* Ignore if not valid environment variable - check alpha and equals */
1559
 
1560
    if (!GetVariableName (String, &value, &cp, (bool*)NULL) ||
1561
	(*cp != CHAR_ASSIGN))
1562
    {
1563
	if (value == -1)
1564
	    PrintErrorMessage (LIT_BadArray, String);
1565
 
1566
	return FALSE;
1567
    }
1568
 
1569
/* Change the = to a end of string */
1570
 
1571
    *(cp++) = 0;
1572
 
1573
/* Assign the value */
1574
 
1575
    SetVariableArrayFromString (String, (int)value, cp);
1576
 
1577
/* Return the index */
1578
 
1579
    if (Index != (int *)NULL)
1580
	*Index = (int)value;
1581
 
1582
    return TRUE;
1583
}
1584
 
1585
/*
1586
 * Get variable name and index
1587
 *
1588
 * String either ends in a null or assignment
1589
 */
1590
 
1591
bool    GetVariableName (char *String,  /* The original string          */
1592
			 long *Index,	/* Array index value found	*/
1593
			 char **Value,	/* Pointer to the value		*/
1594
			 bool *Array)	/* Array detected flag		*/
1595
{
1596
    char        *cp, *sp;
1597
    char	EndName;
1598
 
1599
    *Index = 0;
1600
 
1601
/* Ignore if not valid environment variable - check alpha and equals */
1602
 
1603
    if (((EndName = IsValidVariableName (String)) != CHAR_ASSIGN) &&
1604
	(EndName != CHAR_OPEN_BRACKETS) && EndName)
1605
	return FALSE;
1606
 
1607
    if ((cp = strchr (String, CHAR_ASSIGN)) == (char *)NULL)
1608
	cp = &String[strlen (String)];
1609
 
1610
    if (Array != (bool *)NULL)
1611
	*Array = C2bool (EndName == CHAR_OPEN_BRACKETS);
1612
 
1613
/* Check for valid array */
1614
 
1615
    if (EndName == CHAR_OPEN_BRACKETS)
1616
    {
1617
	if ((Index == (long *)NULL) || (*(cp - 1) != CHAR_CLOSE_BRACKETS))
1618
	    return FALSE;
1619
 
1620
/* Terminate the name and remove the trailing bracket */
1621
 
1622
	*(sp = strchr (String, CHAR_OPEN_BRACKETS)) = 0;
1623
	*(cp - 1) = 0;
1624
 
1625
	if ((!ConvertNumericValue (sp + 1, Index, 10)) ||
1626
	    (*Index < 0) || (*Index > INT_MAX))
1627
	{
1628
	    *Index = -1;
1629
	    return FALSE;
1630
	}
1631
    }
1632
 
1633
/* Return pointer to null or assignment */
1634
 
1635
    *Value = cp;
1636
    return TRUE;
1637
}
1638
 
1639
/*
1640
 * Duplicate the Variable List for a Subshell
1641
 *
1642
 * Create a new Var_list environment for a Sub Shell
1643
 */
1644
 
1645
int CreateGlobalVariableList (unsigned int Function)
1646
{
1647
    int			i;
1648
    S_SubShell		*sp;
1649
 
1650
    for (sp = SubShells, i = 0; (i < NSubShells) &&
1651
			       (SubShells[i].depth < Execute_stack_depth);
1652
	 i++);
1653
 
1654
/* If depth is greater or equal to the Execute_stack_depth - we should panic
1655
 * as this should not happen.  However, for the moment, I'll ignore it
1656
 */
1657
 
1658
    if (NSubShells == MSubShells)
1659
    {
1660
	sp = (S_SubShell *)ReAllocateSpace ((MSubShells == 0) ? (void *)NULL
1661
							      : SubShells,
1662
					    (MSubShells + SSAVE_IO_SIZE) *
1663
						sizeof (S_SubShell));
1664
/* Check for error */
1665
 
1666
	if (sp == (S_SubShell *)NULL)
1667
	    return -1;
1668
 
1669
	SetMemoryAreaNumber ((void *)sp, 0);
1670
	SubShells = sp;
1671
	MSubShells += SSAVE_IO_SIZE;
1672
    }
1673
 
1674
/* Save the depth and the old Variable Tree value */
1675
 
1676
    sp = &SubShells[NSubShells++];
1677
    sp->OldVariableTree = VariableTree;
1678
    sp->depth  = Execute_stack_depth;
1679
    sp->GFlags = ShellGlobalFlags | Function;
1680
    sp->Eflags = flags;
1681
    VariableTree = (void *)NULL;
1682
 
1683
/* Duplicate the old Variable list */
1684
 
1685
    ATNE_Function = Function;
1686
    twalk (sp->OldVariableTree, AddToNewEnvironment);
1687
 
1688
/* Reset global values */
1689
 
1690
    LoadGlobalVariableList ();
1691
    return 0;
1692
}
1693
 
1694
/*
1695
 * TWALK - add to new environment
1696
 */
1697
 
1698
static void AddToNewEnvironment (const void *key, VISIT visit, int level)
1699
{
1700
    VariableList	*vp = *(VariableList **)key;
1701
    VariableList	*vp1;
1702
 
1703
    (void) level;
1704
 
1705
    if ((visit == postorder) || (visit == leaf))
1706
    {
1707
 
1708
/* For functions, do not copy the traps */
1709
 
1710
	if (ATNE_Function && (*vp->name == CHAR_TILDE) && vp->name[1])
1711
	    return;
1712
 
1713
/* Create a new entry */
1714
 
1715
	vp1 = LookUpVariable (vp->name, vp->index, TRUE);
1716
 
1717
	if ((!(vp->status & STATUS_INTEGER)) && (vp->value != null))
1718
	    vp1->value = StringSave (vp->value);
1719
 
1720
/* Copy some flags */
1721
 
1722
	vp1->status = vp->status;
1723
	vp1->nvalue = vp->nvalue;
1724
	vp1->base = vp->base;
1725
	vp1->width = vp->width;
1726
    }
1727
}
1728
 
1729
/*
1730
 * Delete a SubShell environment and restore the original
1731
 */
1732
 
1733
void DeleteGlobalVariableList (void)
1734
{
1735
    int			j;
1736
    S_SubShell		*sp;
1737
    VariableList	*vp;
1738
    void                (_SIGDECL *save_signal)(int);
1739
 
1740
    for (j = NSubShells; j > 0; j--)
1741
    {
1742
       sp = &SubShells[j - 1];
1743
 
1744
       if (sp->depth < Execute_stack_depth)
1745
	   break;
1746
 
1747
/* Reduce number of entries */
1748
 
1749
	--NSubShells;
1750
 
1751
/* Disable signals */
1752
 
1753
	save_signal = signal (SIGINT, SIG_IGN);
1754
 
1755
/* Restore the previous level information */
1756
 
1757
	vp = VariableTree;
1758
	VariableTree = sp->OldVariableTree;
1759
	ShellGlobalFlags = (unsigned int)(sp->GFlags & ~FLAGS_FUNCTION);
1760
	flags = sp->Eflags;
1761
 
1762
/* Release the space */
1763
 
1764
	ATOE_GFlags = sp->GFlags;
1765
 
1766
	twalk (vp, AddToOldEnvironment);
1767
	twalk (vp, DeleteEnvironment);
1768
 
1769
/* Restore signals */
1770
 
1771
	signal (SIGINT, save_signal);
1772
 
1773
	LoadGlobalVariableList ();
1774
    }
1775
}
1776
 
1777
/*
1778
 * TWALK - delete old environment tree
1779
 */
1780
 
1781
static void DeleteEnvironment (const void *key, VISIT visit, int level)
1782
{
1783
    VariableList	*vp = *(VariableList **)key;
1784
 
1785
    (void) level;
1786
 
1787
    if ((visit == endorder) || (visit == leaf))
1788
    {
1789
        if (vp->value == null)
1790
            ReleaseMemoryCell ((void *)vp->value);
1791
 
1792
        ReleaseMemoryCell ((void *)vp->name);
1793
        ReleaseMemoryCell ((void *)vp);
1794
    }
1795
}
1796
 
1797
/*
1798
 * TWALK - Transfer Current Environment to the Old one
1799
 */
1800
 
1801
static void AddToOldEnvironment (const void *key, VISIT visit, int level)
1802
{
1803
    VariableList	*vp = *(VariableList **)key;
1804
    VariableList	*vp1;
1805
 
1806
    (void) level;
1807
 
1808
    if ((visit == postorder) || (visit == leaf))
1809
    {
1810
 
1811
/* Skip local variables and traps */
1812
 
1813
	if ((ATOE_GFlags & FLAGS_FUNCTION) && (!(vp->status & STATUS_LOCAL)) &&
1814
	    (((*vp->name != CHAR_TILDE) || !vp->name[1])))
1815
	{
1816
 
1817
/* Get the entry in the old variable list and update it with the new
1818
 * parameters
1819
 */
1820
	    vp1 = LookUpVariable (vp->name, vp->index, TRUE);
1821
 
1822
	    if (vp1->value != null)
1823
		ReleaseMemoryCell ((void *)vp1->value);
1824
 
1825
	    vp1->value = vp->value;
1826
	    vp->value = null;		/* Stop releaseing this as its tx */
1827
 
1828
	    vp1->status = vp->status;
1829
	    vp1->nvalue = vp->nvalue;
1830
	    vp1->base   = vp->base;
1831
	    vp1->width  = vp->width;
1832
	}
1833
    }
1834
}
1835
 
1836
/*
1837
 * Load GLobal Var List values
1838
 */
1839
 
1840
static void F_LOCAL LoadGlobalVariableList (void)
1841
{
1842
    VariableList	*cifs = LookUpVariable (IFS, 0, TRUE);
1843
 
1844
    CurrentDirectory = LookUpVariable (tilde, 0, TRUE);
1845
    RestoreCurrentDirectory (CurrentDirectory->value);
1846
    SetCharacterTypes (cifs->value, C_IFS);
1847
}
1848
 
1849
/*
1850
 * Match a pattern as in sh(1).  Enhancement to handle prefix processing
1851
 *
1852
 * IgnoreCase - ignore case on comparisions.
1853
 * end - end of match in 'string'.
1854
 * mode - mode for match processing - see GM_ flags in sh.h
1855
 *
1856
 * pattern character are prefixed with MAGIC by expand.
1857
 */
1858
 
1859
bool GeneralPatternMatch (char		*string,	/* String	*/
1860
			  unsigned char *pattern,	/* Pattern	*/
1861
			  bool		IgnoreCase,	/* Ignorecase	*/
1862
			  char		**end,		/* End of match	*/
1863
			  int		mode)		/* Mode		*/
1864
{
1865
    int		string_c, pattern_c;
1866
    char	*save_end;
1867
 
1868
    if ((string == (char *)NULL) || (pattern == (unsigned char *)NULL))
1869
	return FALSE;
1870
 
1871
    while ((pattern_c = *(pattern++)) != 0)
1872
    {
1873
	string_c = *(string++);
1874
 
1875
	if (pattern_c != CHAR_MAGIC)
1876
	{
1877
	    if (IgnoreCase)
1878
	    {
1879
		string_c = tolower (string_c);
1880
		pattern_c = tolower (pattern_c);
1881
	    }
1882
 
1883
	    if (string_c != pattern_c)
1884
		return FALSE;
1885
 
1886
	    continue;
1887
	}
1888
 
1889
/* Magic characters */
1890
 
1891
	switch (*(pattern++))
1892
	{
1893
	    case CHAR_OPEN_BRACKETS:	/* Class expression		*/
1894
		if ((!string_c) ||
1895
		    ((pattern = CheckClassExpression (pattern, string_c,
1896
						     IgnoreCase)) ==
1897
				(unsigned char *)NULL))
1898
		    return FALSE;
1899
 
1900
		break;
1901
 
1902
	    case CHAR_MATCH_ANY:	/* Match any character		*/
1903
		if (string_c == 0)
1904
		    return FALSE;
1905
 
1906
		break;
1907
 
1908
	    case CHAR_MATCH_ALL:	/* Match as many as possible	*/
1909
		--string;
1910
		save_end = (char *)NULL;
1911
 
1912
		do
1913
		{
1914
		    if (!*pattern ||
1915
			GeneralPatternMatch (string, pattern, IgnoreCase, end,
1916
					     mode))
1917
		    {
1918
			if (mode == GM_LONGEST)
1919
			    save_end = *end;
1920
 
1921
			else
1922
			    return TRUE;
1923
		    }
1924
 
1925
		} while (*(string++));
1926
 
1927
		if (end != (char **)NULL)
1928
		    *end = save_end;
1929
 
1930
		return C2bool (save_end != (char *)NULL);
1931
 
1932
	    default:		/* Match				*/
1933
		if ((unsigned)string_c != pattern[-1])
1934
		    return FALSE;
1935
 
1936
		break;
1937
	}
1938
    }
1939
 
1940
    if (end != (char **)NULL)
1941
    {
1942
	*end = string;
1943
	return TRUE;
1944
    }
1945
 
1946
    return C2bool (*string == 0);
1947
}
1948
 
1949
/*
1950
 * Process a class expression - []
1951
 */
1952
 
1953
static unsigned char * F_LOCAL CheckClassExpression (
1954
				unsigned char	*pattern,
1955
				int		string_c, /* Match char*/
1956
				bool		IgnoreCase)/* Ic flag	*/
1957
{
1958
    int		llimit_c, ulimit_c;
1959
    bool	not = FALSE;
1960
    bool	found;
1961
 
1962
/* Exclusive or inclusive class */
1963
 
1964
    if ((*pattern == CHAR_MAGIC) &&
1965
	((*(pattern + 1) == CHAR_NOT) || (*(pattern + 1) == '!')))
1966
    {
1967
	pattern += 2;
1968
	not = TRUE;
1969
    }
1970
 
1971
    found = not;
1972
 
1973
/* Process the pattern */
1974
 
1975
    do
1976
    {
1977
	if (*pattern == CHAR_MAGIC)
1978
	    pattern++;
1979
 
1980
	if (!*pattern)
1981
	    return (unsigned char *)NULL;
1982
 
1983
/* Get the next character in class, converting to lower case if necessary */
1984
 
1985
	llimit_c = IgnoreCase ? tolower (*pattern) : *pattern;
1986
 
1987
/* If this is a range, get the end of range character */
1988
 
1989
	if ((*(pattern + 1) == CHAR_MATCH_RANGE) &&
1990
	    (*(pattern + 2) != CHAR_CLOSE_BRACKETS))
1991
	{
1992
	    ulimit_c = IgnoreCase ? tolower (*(pattern + 2)) : *(pattern + 2);
1993
	    pattern++;
1994
	}
1995
 
1996
	else
1997
	    ulimit_c = llimit_c;
1998
 
1999
/* Is the current character in the class? */
2000
 
2001
	if ((llimit_c <= string_c) && (string_c <= ulimit_c))
2002
	    found = C2bool (!not);
2003
 
2004
    } while (*(++pattern) != CHAR_CLOSE_BRACKETS);
2005
 
2006
    return found ? pattern + 1 : (unsigned char *)NULL;
2007
}
2008
 
2009
/*
2010
 * Suffix processing - find the longest/shortest suffix.
2011
 */
2012
 
2013
bool SuffixPatternMatch (char *string,	/* String to match		*/
2014
			 char *pattern, /* Pattern to match against	*/
2015
			 char **start,	/* Start position		*/
2016
			 int  mode)	/* Match mode			*/
2017
{
2018
    char	*save_start = (char *)NULL;
2019
 
2020
/* Scan the string, looking for a match to the end */
2021
 
2022
    while (*string)
2023
    {
2024
	if (GeneralPatternMatch (string, (unsigned char *)pattern, FALSE,
2025
				 (char **)NULL, GM_ALL))
2026
	{
2027
 
2028
/* If longest, stop here */
2029
 
2030
	    if (mode == GM_LONGEST)
2031
	    {
2032
		*start = string;
2033
		return TRUE;
2034
	    }
2035
 
2036
/* Save the start of the shortest string so far and continue */
2037
 
2038
	    save_start = string;
2039
	}
2040
 
2041
	++string;
2042
    }
2043
 
2044
    return C2bool ((*start = save_start) != (char *)NULL);
2045
}
2046
 
2047
/*
2048
 * Get a string in a malloced area
2049
 */
2050
 
2051
char *AllocateMemoryCell (size_t nbytes)
2052
{
2053
    s_region		*np;
2054
    void                (_SIGDECL *save_signal)(int);
2055
#ifdef OS2_DOSALLOC
2056
    SEL			sel;
2057
#endif
2058
 
2059
    if (nbytes == 0)
2060
	abort ();	/* silly and defeats the algorithm */
2061
 
2062
/* Grab some space */
2063
 
2064
#ifdef OS2_DOSALLOC
2065
    if (DosAllocSeg (nbytes + sizeof (s_region), &sel, SEG_NONSHARED))
2066
    {
2067
	errno = ENOMEM;
2068
	return (char *)NULL;
2069
    }
2070
 
2071
    np = (s_region *)MAKEP (sel, 0);
2072
    memset (np, 0, nbytes + sizeof (s_region));
2073
 
2074
#else
2075
    if ((np = (s_region *)calloc (nbytes + sizeof (s_region), 1))
2076
		== (s_region *)NULL)
2077
    {
2078
	errno = ENOMEM;
2079
        return (char *)NULL;
2080
    }
2081
#endif
2082
 
2083
/* Disable signals */
2084
 
2085
    save_signal = signal (SIGINT, SIG_IGN);
2086
 
2087
/* Link into chain */
2088
 
2089
    np->next = MemoryAreaHeader;
2090
    np->area = MemoryAreaLevel;
2091
    np->nbytes = nbytes;
2092
 
2093
    MemoryAreaHeader = np;
2094
 
2095
/* Restore signals */
2096
 
2097
    signal (SIGINT, save_signal);
2098
 
2099
    return ((char *)np) + sizeof (s_region);
2100
}
2101
 
2102
/*
2103
 * Release a array of strings
2104
 */
2105
 
2106
void	ReleaseAList (char **list)
2107
{
2108
    char	**ap = list;
2109
 
2110
    while (*ap != (char *)NULL)
2111
	ReleaseMemoryCell (*(ap++));
2112
 
2113
    ReleaseMemoryCell (list);
2114
 
2115
}
2116
 
2117
/*
2118
 * Free a string in a malloced area
2119
 */
2120
 
2121
void ReleaseMemoryCell (void *s)
2122
{
2123
    s_region		*cp = MemoryAreaHeader;
2124
    s_region		*lp = (s_region *)NULL;
2125
    s_region		*sp = (s_region *)((char *)s - sizeof (s_region));
2126
    void                (_SIGDECL *save_signal)(int);
2127
 
2128
/* Disable signals */
2129
 
2130
    save_signal = signal (SIGINT, SIG_IGN);
2131
 
2132
/* Find the string in the chain */
2133
 
2134
    if (s != (char *)NULL)
2135
    {
2136
	while (cp != (s_region *)NULL)
2137
	{
2138
	    if (cp != sp)
2139
	    {
2140
		lp = cp;
2141
		cp = cp->next;
2142
		continue;
2143
	    }
2144
 
2145
/* First in chain ? */
2146
 
2147
	    else if (lp == (s_region *)NULL)
2148
		MemoryAreaHeader = cp->next;
2149
 
2150
/* Delete the current entry and relink */
2151
 
2152
	    else
2153
		lp->next = cp->next;
2154
 
2155
	    RELEASE_MEMORY (cp);
2156
	    break;
2157
	}
2158
    }
2159
 
2160
/* Restore signals */
2161
 
2162
    signal (SIGINT, save_signal);
2163
}
2164
 
2165
/*
2166
 * Check for memory leaks with a dump
2167
 */
2168
 
2169
#ifdef DEBUG_MEMORY
2170
void DumpMemoryCells (int status)
2171
{
2172
    s_region		*cp = MemoryAreaHeader;
2173
    size_t		i;
2174
    char		buffer[17];
2175
    char		*sp;
2176
 
2177
/* Find the string in the chain */
2178
 
2179
    while (cp != (s_region *)NULL)
2180
    {
2181
	fprintf (stderr, "Segment 0x%.8lx Area %5d Length %5d Link 0x%.8lx\n",
2182
		 cp, cp->area, cp->nbytes, cp->next);
2183
 
2184
	memset (buffer, CHAR_SPACE, 17);
2185
	buffer[16] = 0;
2186
 
2187
	sp = ((char *)cp) + sizeof (s_region);
2188
 
2189
	for (i = 0; i < (((cp->nbytes - 1)/16) + 1) * 16; i++)
2190
	{
2191
	    if (i >= cp->nbytes)
2192
	    {
2193
		feputs ("   ");
2194
		buffer [i % 16] = CHAR_SPACE;
2195
	    }
2196
 
2197
	    else
2198
	    {
2199
		fprintf (stderr, "%.2x ", *sp & 0x0ff);
2200
		buffer [i % 16] = (char)(isprint (*sp) ? *sp : CHAR_PERIOD);
2201
	    }
2202
 
2203
	    if (i % 16 == 15)
2204
		fprintf (stderr, "    [%s]\n", buffer);
2205
 
2206
	    sp++;
2207
	}
2208
 
2209
	feputc (CHAR_NEW_LINE);
2210
	cp = cp->next;
2211
    }
2212
#undef exit
2213
    exit (status);
2214
#define exit(x)		DumpMemoryCells (x)
2215
}
2216
#endif
2217
 
2218
/*
2219
 * Autodelete space nolonger required.  Ie. Free all the strings in a malloced
2220
 * area
2221
 */
2222
 
2223
void ReleaseMemoryArea (int a)
2224
{
2225
    s_region		*cp = MemoryAreaHeader;
2226
    s_region		*lp = (s_region *)NULL;
2227
    void                (_SIGDECL *save_signal)(int);
2228
 
2229
/* Release the Here documents first */
2230
 
2231
    FreeAllHereDocuments (a);
2232
 
2233
/* Disable signals */
2234
 
2235
    save_signal = signal (SIGINT, SIG_IGN);
2236
 
2237
    while (cp != (s_region *)NULL)
2238
    {
2239
 
2240
/* Is the area number less than that specified - yes, continue */
2241
 
2242
	if (cp->area < a)
2243
	{
2244
	    lp = cp;
2245
	    cp = cp->next;
2246
	}
2247
 
2248
/* OK - delete the area.  Is it the first in chain ?  Yes, delete, relink
2249
 * and update start location
2250
 */
2251
 
2252
	else if (lp == (s_region *)NULL)
2253
	{
2254
	    lp = cp;
2255
	    cp = cp->next;
2256
	    MemoryAreaHeader = cp;
2257
 
2258
	    RELEASE_MEMORY (lp);
2259
	    lp = (s_region *)NULL;
2260
	}
2261
 
2262
/* Not first, delete the current entry and relink */
2263
 
2264
	else
2265
	{
2266
	    lp->next = cp->next;
2267
	    RELEASE_MEMORY (cp);
2268
	    cp = lp->next;
2269
	}
2270
    }
2271
 
2272
/* Restore signals */
2273
 
2274
    signal (SIGINT, save_signal);
2275
}
2276
 
2277
/*
2278
 * Set the area number for a malloced string.  This allows autodeletion of
2279
 * space that is nolonger required.
2280
 */
2281
 
2282
void SetMemoryAreaNumber (void *cp, int a)
2283
{
2284
    s_region	*sp = (s_region *)((char *)cp - sizeof (s_region));
2285
 
2286
    if (cp != (void *)NULL)
2287
	sp->area = a;
2288
}
2289
 
2290
/*
2291
 * Get the area number for a malloced string
2292
 */
2293
 
2294
int GetMemoryAreaNumber (void *cp)
2295
{
2296
    s_region	*sp = (s_region *)((char *)cp - sizeof (s_region));
2297
 
2298
    return sp->area;
2299
}
2300
 
2301
/* Output one of the Prompt.  We save the prompt for the history part of
2302
 * the program
2303
 */
2304
 
2305
void OutputUserPrompt (char *s)
2306
{
2307
    struct tm		*tm;
2308
    time_t		xtime = time ((time_t *)NULL);
2309
    int			i;
2310
    char		buf[PATH_MAX + 4];
2311
 
2312
    if (LastUserPrompt != (char *)NULL)
2313
    {
2314
	LastUserPrompt = s;		/* Save the Last prompt id	*/
2315
	s = GetVariableAsString (s, TRUE); /* Get the string value	*/
2316
 
2317
	if (LastUserPrompt == PS1)
2318
	    s = substitute (s, 0);
2319
    }
2320
 
2321
    else
2322
	s = LastUserPrompt1;
2323
 
2324
    tm = localtime (&xtime);
2325
 
2326
    while (*s)
2327
    {
2328
 
2329
/* If a format character, process it */
2330
 
2331
	if (*s == CHAR_FORMAT)
2332
	{
2333
	    s++;
2334
	    *s = (char)tolower(*s);
2335
 
2336
	    if (*s == CHAR_FORMAT)
2337
		fputchar (CHAR_FORMAT);
2338
 
2339
	    else
2340
	    {
2341
		*buf = 0;
2342
 
2343
		switch (*(s++))
2344
		{
2345
		    case 'e':		    /* Current event number */
2346
			if (HistoryEnabled)
2347
			    sprintf (buf, "%d", Current_Event + 1);
2348
 
2349
			break;
2350
 
2351
		    case 't':		    /* time	    */
2352
			sprintf (buf,"%.2d:%.2d", tm->tm_hour, tm->tm_min);
2353
			break;
2354
 
2355
		    case 'd':		    /* date	    */
2356
			sprintf (buf, "%.3s %.2d-%.2d-%.2d",
2357
				 &"SunMonTueWedThuFriSat"[tm->tm_wday * 3],
2358
				 tm->tm_mday, tm->tm_mon + 1,
2359
				 tm->tm_year % 100);
2360
			break;
2361
 
2362
		    case 'p':		    /* directory    */
2363
		    case 'n':		    /* default drive */
2364
			strcpy (buf, CurrentDirectory->value);
2365
 
2366
			if (*(s - 1) == 'n')
2367
			    buf[1] = 0;
2368
 
2369
			break;
2370
 
2371
#if (OS_TYPE != OS_UNIX)
2372
		    case 'v':		    /* version	    */
2373
			sprintf (buf, "%s %.2d:%.2d", LIT_OSname,
2374
                                OS_VERS_N, OS_VERS_M);
2375
			break;
2376
#endif
2377
		}
2378
 
2379
/* Output the string */
2380
 
2381
		foputs (buf);
2382
	    }
2383
	}
2384
 
2385
/* Escaped character ? */
2386
 
2387
	else if (*s == CHAR_META)
2388
	{
2389
	    ++s;
2390
	    if ((i = ProcessOutputMetaCharacters (&s)) == -1)
2391
		i = 0;
2392
 
2393
	    fputchar ((char)i);
2394
	}
2395
 
2396
	else
2397
	    fputchar (*(s++));
2398
    }
2399
 
2400
    FlushStreams ();			/* Clear output */
2401
}
2402
 
2403
/*
2404
 * Get the current path in UNIX format and save it in the environment
2405
 * variable $~
2406
 */
2407
 
2408
void GetCurrentDirectoryPath (void)
2409
{
2410
    char	ldir[PATH_MAX + 6];
2411
    char	*CurrentPWDValue;		/* Current directory	*/
2412
 
2413
    S_getcwd (ldir, 0);
2414
 
2415
/* Save in environment */
2416
 
2417
    SetVariableFromString (tilde, ldir);
2418
    CurrentDirectory = LookUpVariable (tilde, 0, TRUE);
2419
 
2420
/* If we have changed directory, set PWD and OLDPWD */
2421
 
2422
    if (strcmp (CurrentPWDValue = GetVariableAsString (PWDVariable, FALSE),
2423
		ldir))
2424
    {
2425
	SetVariableFromString (OldPWDVariable, CurrentPWDValue);
2426
	SetVariableFromString (PWDVariable, ldir);
2427
    }
2428
}
2429
 
2430
/*
2431
 * Initialise the shell and Patch up various parts of the system for the
2432
 * shell.  At the moment, we modify the ctype table so that _ is an upper
2433
 * case character.
2434
 */
2435
 
2436
/* Is the path 'a' subset of 'p' ? */
2437
 
2438
#if (OS_TYPE == OS_NT)
2439
#define PATHSMAX    8
2440
#define PATHOFF     5
2441
 
2442
static int
2443
SubPath( const char *p, const char *a )
2444
{
2445
    int         pl, al, i;
2446
 
2447
    pl = strlen(p += PATHOFF);
2448
    al = strlen(a += PATHOFF);
2449
    for (i = 0; pl-- >= al; i++)
2450
        if (strnicmp( p++, a, al ) == 0)
2451
        {
2452
            if (i == 0 && (pl == al))
2453
                return (0);
2454
            return (i+1);
2455
        }
2456
    return (-1);
2457
}
2458
#endif
2459
 
2460
static bool F_LOCAL Initialise (int argc, char **argv)
2461
{
2462
    char	*s, *s1;
2463
    char	**ap;
2464
    char	*name = *argv;
2465
    bool	OptionsRflag = FALSE;
2466
#if (OS_TYPE == OS_NT)
2467
    bool	Level0 = FALSE;
2468
    int		sc;
2469
#endif
2470
 
2471
/*
2472
 * Determine the base OS if we can!
2473
 */
2474
 
2475
#if (OS_TYPE == OS_NT)
2476
    BaseOS = BASE_OS_NT;	/* Default to NT - no other */
2477
#elif (OS_TYPE == OS_UNIX)
2478
    BaseOS = BASE_OS_UNIX;	/* Default to UNIX - no other */
2479
#elif (OS_TYPE == OS_OS2)
2480
    BaseOS = BASE_OS_OS2;	/* Default to OS2 - no other */
2481
#elif (OS_TYPE == OS_DOS)
2482
 
2483
    BaseOS = BASE_OS_DOS;	/* Default to DOS */
2484
 
2485
    if (_osmajor > 10)          /* Rather crude check */
2486
	BaseOS = BASE_OS_OS2;
2487
 
2488
/* Know to stop NT ? */
2489
 
2490
    else
2491
    {
2492
        union REGS	r;
2493
	r.h.ah = 0x16;
2494
	r.h.al = 0;
2495
 
2496
	SystemInterrupt (0x2f, &r, &r);
2497
 
2498
	if (r.h.al > 0)
2499
	    BaseOS = BASE_OS_WIN;
2500
 
2501
	else
2502
	{
2503
	    r.x.REG_AX = 0x3306;
2504
	    SystemInterrupt (0x21, &r, &r);
2505
 
2506
	    if ((r.x.REG_AX == 0x3306) && (r.x.REG_BX == 0x3205))
2507
		BaseOS = BASE_OS_NT;
2508
	}
2509
    }
2510
#endif
2511
 
2512
/*
2513
 * For MSDOS: Get original interrupt 24 address and set up our new interrupt
2514
 *	      24 address.
2515
 * For OS/2 : Set noignore interrupts.
2516
 */
2517
 
2518
#if (OS_TYPE == OS_DOS) && !defined (__EMX__)
2519
    Orig_I24_V = GetInterruptVector (0x24);
2520
#  if (OS_SIZE == OS_16)
2521
#   if (XXX)
2522
    SetInterruptVector (0x24, SW_Int24);
2523
#   endif
2524
#  else
2525
    _harderr (HardErrorHandler);
2526
#  endif
2527
#elif (OS_TYPE != OS_DOS)
2528
    IgnoreInterrupts = FALSE;
2529
#endif
2530
 
2531
/* Create terminations queues - OS/2 only */
2532
 
2533
    CreateTerminationQueues ();
2534
 
2535
/* Set up character maps */
2536
 
2537
    InitialiseCharacterTypes ();
2538
 
2539
/* Create the integer variables, in case they are loaded from the
2540
 * environment
2541
 */
2542
    CreateIntegerVariables ();
2543
 
2544
/* Initialise Path Extension lists */
2545
 
2546
    BuildExtensionLists ();
2547
 
2548
/* For NT, we need to know now if this is a level 0 shell because NT has a
2549
 * nasty habit of being case in-sensitive!  Not only for file names, but
2550
 * also environment variables.  So scan the command line for the -0 option!
2551
 */
2552
 
2553
#if (OS_TYPE == OS_NT)
2554
    while ((sc = GetOptions (argc, argv, ShellOptions, 0)) != EOF)
2555
    {
2556
	if (sc == '0')
2557
	{
2558
	    Level0 = TRUE;
2559
	    break;
2560
	}
2561
    }
2562
 
2563
    ResetGetOptions ();
2564
#endif
2565
 
2566
/* Load the environment into our structures */
2567
 
2568
    if ((ap = environ) != NOWORDS)
2569
    {
2570
#if (OS_TYPE == OS_NT)
2571
        const char *paths[PATHSMAX];
2572
        int pathcnt = 0;
2573
#endif
2574
 
2575
	for (ap = environ; *ap != (char *)NULL; ap++)
2576
	{
2577
 
2578
/* Set up any variables.  Note there is an assumption that
2579
 * AssignVariableFromString sets the equals sign to 0, hiding the value;
2580
 */
2581
	    if (!strncmp ("SECONDS=", *ap, 8))
2582
		continue;
2583
 
2584
/* NT is case in-sensitive - what a PAIN! */
2585
 
2586
#if (OS_TYPE == OS_NT)
2587
                                                /* special PATH handling */
2588
            if (strnicmp ("PATH=", *ap, 5) == 0)
2589
            {
2590
                if (pathcnt < PATHSMAX)
2591
                    paths[ pathcnt++ ] = *ap;
2592
                continue;                       /* next -- process on exit */
2593
            }
2594
 
2595
	    if (Level0)
2596
            {
2597
		s = *ap;
2598
 
2599
		while (*s && (*s != CHAR_ASSIGN))
2600
		{                               /* convert to upper case */
2601
		    *s = (char)toupper (*s);
2602
		    s++;
2603
		}
2604
	    }
2605
#endif
2606
	    if (AssignVariableFromString (*ap, (int *)NULL))
2607
		SetVariableStatus (*ap, STATUS_EXPORT);
2608
	}
2609
 
255 dpurdie 2610
    /*
2611
    **  Can not access varibales from the environment
2612
    **  Set up debug level
2613
    */
2614
    debug_level = GetVariableAsNumeric ("SHDEBUGEXEC" );
2615
 
227 dpurdie 2616
#if (OS_TYPE == OS_NT)
2617
        if (pathcnt)
2618
        {                                       /* import */
2619
            const char   *p;                    /* path and alternative */
255 dpurdie 2620
            int dbg = (debug_level & DEBUG_MULTIPATH);
227 dpurdie 2621
            p = paths[0];
2622
            if (pathcnt > 1)
2623
            {
255 dpurdie 2624
                int      err, i;
227 dpurdie 2625
                int      pl, al;
2626
 
2627
                pl = -1;
2628
                for (i=0; i<pathcnt; i++) {     /* select greater */
2629
                    if ((al = strlen(paths[i])) > pl) {
2630
                         p = paths[i];
2631
                         pl = al;
2632
                    }
2633
                }
2634
 
2635
                if (dbg) {
255 dpurdie 2636
                    fprintf ( stderr, "Encountered %d path definitions ...\n", pathcnt );
227 dpurdie 2637
                }
2638
 
2639
                for (err=0, i=0; i<pathcnt; i++) {
2640
                                                /* diff alternatives */
2641
                    if (dbg) {
2642
                        fprintf ( stderr, "[%d] %s", i, paths[i] );
2643
                    }
2644
 
2645
                    if (p == paths[i]) {
2646
                        if (dbg) {              /* .. selection */
2647
                            fprintf ( stderr, " .. using.\n" );
2648
                        }
2649
                    } else {
2650
                        int     ret;
2651
 
2652
                        if ((ret = SubPath(p, paths[i])) == -1)
2653
                        {                       /* .. different */
2654
                            if (dbg) {
2655
                                fprintf ( stderr, " .. different!\n" );
2656
                            }
2657
 
2658
	                    if (!FL_TEST (FLAG_WARNING)) {
2659
                                if (err++ == 0)
255 dpurdie 2660
                                    PrintWarningMessage ( "sh: %d path definitions encountered.", pathcnt );
2661
                                PrintWarningMessage ( "sh: ignoring differing alternative '%.4s'\n %s", paths[i], paths[i] );
227 dpurdie 2662
                            }
2663
 
2664
                        } else if (ret == 0) {
2665
                            if (dbg) {          /* .. same value */
2666
                                fprintf ( stderr, " .. same.\n" );
2667
                            }
2668
 
2669
                        } else {                /* .. substr */
2670
                            if (dbg) {
2671
                                fprintf ( stderr, " .. substr.\n" );
2672
                            }
2673
                        }
2674
                    }
2675
                }
2676
 
2677
                if (err) {
2678
                    PrintWarningMessage ( "sh: using '%.4s'\n %s.", p, p );
2679
                }
2680
            }
255 dpurdie 2681
            else if ( dbg )
2682
            {
2683
                    fprintf ( stderr, "Only one PATH definition seen\n", pathcnt );
2684
            }
227 dpurdie 2685
 
2686
            SetVariableFromString (PathLiteral, (char *)(p + PATHOFF));
2687
            SetVariableStatus (PathLiteral, STATUS_EXPORT);
2688
        }
2689
 
2690
        if (GetVariableAsString (UserLiteral, FALSE) == null)
2691
        {                                       /* set user */
2692
            char name[200];
2693
            u_long size = sizeof(name);
2694
 
2695
            if (GetUserName( name, &size )) 
2696
            {
2697
               SetVariableFromString (UserLiteral, name);
2698
               SetVariableStatus (UserLiteral, STATUS_EXPORT);
2699
            }
2700
        }
2701
 
2702
#endif  /*OS_NT*/
2703
    }
2704
 
2705
/* Change COMSPEC to unix format for execution */
2706
 
2707
    PATH_TO_UNIX (GetVariableAsString (ComspecVariable, FALSE));
2708
    SetVariableStatus (ComspecVariable, STATUS_CONVERT_MSDOS);
2709
 
2710
/* Zap all files */
2711
 
2712
    CloseAllHandlers ();
2713
    MemoryAreaLevel = 1;
2714
 
2715
/* Get the current directory */
2716
 
2717
    GetCurrentDirectoryPath ();
2718
 
2719
/* Initialise the getopts command */
2720
 
2721
    ResetGetoptsValues (TRUE);
2722
 
2723
/* Set up SHELL variable.  First check for a restricted shell.  Check the
2724
 * restricted shell
2725
 */
2726
 
2727
    SetVariableFromString (LastWordVariable, name);
2728
 
2729
/* For OS/2, we prefer the full path name and not that in argv[0].  Save a
2730
 * copy of the string
2731
 */
2732
#if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_NT)
2733
    if (_APgmName)
2734
    {
2735
        PATH_TO_UNIX ((name = GetAllocatedSpace (strlen (_APgmName) + 4)));
2736
        SetMemoryAreaNumber ((void *)name, 0);
2737
        strcpy (name, _APgmName);
2738
    }
2739
#endif
2740
 
2741
 
2742
/* Has the program name got a .exe extension - Yes probably DOS 3+.  So
2743
 * save it as the Shell name.  Under OS/2, make sure we use .exe
2744
 */
2745
    if (GetVariableAsString (ShellVariableName, FALSE) == null)
2746
    {
2747
#if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_NT)
2748
	if ((s1 = strrchr (name, CHAR_PERIOD)) == (char *)NULL)
2749
	    strcat (name, EXEExtension);
2750
 
2751
	SetVariableFromString (ShellVariableName, name);
2752
 
2753
	ReleaseMemoryCell (name);
2754
#elif (OS_TYPE == OS_DOS)
2755
	if (((s1 = strrchr (name, CHAR_PERIOD)) != (char *)NULL) &&
2756
	    (stricmp (s1 + 1, EXEExtension + 1) == 0))
2757
	    SetVariableFromString (ShellVariableName, PATH_TO_UNIX (name));
2758
#elif (OS_TYPE == OS_UNIX)
2759
	SetVariableFromString (ShellVariableName,
2760
				(*name == '-') ? name + 1 : name);
2761
#endif
2762
    }
2763
 
2764
/* Default if necessary */
2765
 
2766
    if (GetVariableAsString (ShellVariableName, FALSE) == null)
2767
	SetVariableFromString (ShellVariableName, shellname);
2768
 
2769
    PATH_TO_UNIX (s1 = GetVariableAsString (ShellVariableName, FALSE));
2770
 
2771
/* Check for restricted shell */
2772
 
2773
    if ((s = FindLastPathCharacter (s1)) == (char *)NULL)
2774
	s = s1;
2775
 
2776
    else
2777
	s++;
2778
 
2779
    if (*s == 'r')
2780
	OptionsRflag = TRUE;
2781
 
2782
/* Set up home directory */
2783
 
2784
    if (GetVariableAsString (HomeVariableName, FALSE) == null)
2785
    {
2786
        if ((s = GetVariableAsString ("INIT", FALSE)) == null)
2787
            s = CurrentDirectory->value;
2788
 
2789
	SetVariableFromString (HomeVariableName, s);
2790
    }
2791
 
2792
/* Set up OS Mode */
2793
 
2794
#if (OS_TYPE == OS_UNIX) || defined(WIN32)
2795
    SetVariableFromNumeric (LIT_OSmode, 3L);
2796
#elif defined (__TURBOC__)
2797
    SetVariableFromNumeric (LIT_OSmode, 0L);
2798
#elif (__WATCOMC__)
2799
    SetVariableFromNumeric (LIT_OSmode, (long)OS_TYPE - 1);
2800
#else
2801
    SetVariableFromNumeric (LIT_OSmode, (long)_osmode);
2802
#endif
2803
 
2804
#if (OS_SIZE == OS_32)
2805
    SetVariableFromNumeric (LIT_SHmode, 32L);
2806
#else
2807
    SetVariableFromNumeric (LIT_SHmode, 16L);
2808
#endif
2809
 
2810
/* Set up history file location */
2811
 
2812
    SetVariableFromNumeric (LIT_Dollar, (long)getpid ());
2813
 
2814
    LoadGlobalVariableList ();
2815
    PATH_TO_UNIX (GetVariableAsString (PathLiteral, FALSE));
2816
    PATH_TO_UNIX (GetVariableAsString (CDPathLiteral, FALSE));
2817
 
2818
    return OptionsRflag;
2819
}
2820
 
2821
/*
2822
 * Mail Check processing.  Every $MAILCHECK seconds, we check either $MAIL
2823
 * or $MAILPATH to see if any file has changed its modification time since
2824
 * we last looked.  In $MAILCHECK, the files are separated by semi-colon (;).
2825
 * If the filename contains a %, the string following the % is the message
2826
 * to display if the file has changed.
2827
 */
2828
 
2829
static void F_LOCAL CheckForMailArriving (void)
2830
{
2831
    int			delay = (int)GetVariableAsNumeric (MailCheckVariable);
2832
    char		*mail = GetVariableAsString ("MAIL", FALSE);
2833
    char		*mailp = GetVariableAsString ("MAILPATH", FALSE);
2834
    static time_t	last = 0L;
2835
    time_t		current = time ((time_t *)NULL);
2836
    struct stat		st;
2837
    char		*cp, *sp, *ap;
2838
 
2839
/* Have we waited long enough */
2840
 
2841
    if (((current - last) < delay) || (DisabledVariables & DISABLE_MAILCHECK))
2842
	return;
2843
 
2844
/* Yes - Check $MAILPATH.  If it is defined, process it.  Otherwise, use
2845
 * $MAIL
2846
 */
2847
 
2848
    if (mailp != null)
2849
    {
2850
 
2851
/* Check MAILPATH */
2852
 
2853
	sp = mailp;
2854
 
2855
/* Look for the next separator */
2856
 
2857
	while ((cp = strchr (sp, CHAR_PATH_SEPARATOR)) != (char *)NULL)
2858
	{
2859
	    *cp = 0;
2860
 
2861
/* % in string ? */
2862
 
2863
	    if ((ap = strchr (sp, CHAR_FORMAT)) != (char *)NULL)
2864
		*ap = 0;
2865
 
2866
/* Check the file name */
2867
 
2868
	    if ((S_stat (sp, &st)) && (st.st_mtime > last) && st.st_size)
2869
	    {
2870
		feputs ((ap != (char *)NULL) ? ap + 1 : ymail);
2871
		feputc (CHAR_NEW_LINE);
2872
	    }
2873
 
2874
/* Restore the % */
2875
 
2876
	    if (ap != (char *)NULL)
2877
		*ap = CHAR_FORMAT;
2878
 
2879
/* Restore the semi-colon and find the next one */
2880
 
2881
	    *cp = CHAR_PATH_SEPARATOR;
2882
	    sp = cp + 1;
2883
	}
2884
    }
2885
 
2886
/* Just check MAIL */
2887
 
2888
    else if ((mail != null) && (S_stat (mail, &st)) &&
2889
	     (st.st_mtime > last) && st.st_size)
2890
    {
2891
	feputs (ymail);
2892
	feputc (CHAR_NEW_LINE);
2893
    }
2894
 
2895
/* Save the last check time */
2896
 
2897
    last = current;
2898
}
2899
 
2900
/*
2901
 * Preprocess Argv to get handle of options in the format /x
2902
 *
2903
 * Some programs invoke the shell using / instead of - to mark the options.
2904
 * We need to convert to -.  Also /c is a special case.  The rest of the
2905
 * command line is the command to execute.  So, we get the command line
2906
 * from the original buffer instead of argv array.
2907
 */
2908
 
2909
#if (OS_TYPE != OS_UNIX)
2910
static void F_LOCAL Pre_Process_Argv (char **argv, int *argc1)
2911
{
2912
    int		argc = 1;
2913
    char	*ocl = _ACmdLine;
2914
    int		i;
2915
 
2916
/* Check for these options */
2917
 
2918
    while ((*++argv != (char *)NULL) && (strlen (*argv) == 2) &&
2919
	   (**argv == '/'))
2920
    {
2921
	argc++;
2922
	*strlwr (*argv) = CHAR_SWITCH;
2923
 
2924
/* Get the original information from the command line */
2925
 
2926
	if ((*argv)[1] == 'c')
2927
	{
2928
 
2929
/*
2930
 * In some case under OS/2, we get /c, but with EMX style.  So all we need
2931
 * to do is change the / to a - and return.
2932
 */
2933
 
2934
#  if (OS_TYPE == OS_OS2)
2935
	    if (EMXStyleParameters)
2936
	        return;
2937
#  endif
2938
 
2939
/*
2940
 * Otherwise, parse the command line again, looking for the /c
2941
 */
2942
 
2943
	    while ((*ocl != '/') && (*(ocl + 1) != 'c') &&
2944
		   (*ocl) && (*ocl != CHAR_RETURN))
2945
		++ocl;
2946
 
2947
	    if (*ocl != '/')
2948
		continue;
2949
 
2950
/* Find the start of the string */
2951
 
2952
	    ocl += 2;
2953
 
2954
	    while (isspace (*ocl) && (*ocl != CHAR_RETURN))
2955
		++ocl;
2956
 
2957
	    if (*ocl == CHAR_RETURN)
2958
		continue;
2959
 
2960
/* Found the start.  Set up next parameter and ignore the rest */
2961
 
2962
	    if (*(argv + 1) == (char *)NULL)
2963
		continue;
2964
 
2965
	    argc++;
2966
 
2967
/* Remove quotes from string, if they are there */
2968
 
2969
	    if ((*ocl == CHAR_DOUBLE_QUOTE) &&
2970
		(ocl[i = (strlen (ocl) - 1)] == CHAR_DOUBLE_QUOTE))
2971
	    {
2972
		ocl[i] = 0;
2973
		ocl++;
2974
	    }
2975
 
2976
/* Set up new argument array */
2977
 
2978
	    *(argv + 1) = ocl;
2979
	    *(argv + 2) = (char *)NULL;
2980
	    *argc1 = argc;
2981
 
2982
	    if ((ocl = strchr (ocl, CHAR_RETURN)) != (char *)NULL)
2983
		*ocl = 0;
2984
 
2985
	    return;
2986
	}
2987
    }
2988
}
2989
#endif
2990
 
2991
/*
2992
 * Convert path format to/from UNIX
2993
 */
2994
 
2995
char *ConvertPathToFormat (char *path, char in, char out)
2996
{
2997
#if (OS_TYPE == OS_UNIX)
2998
    return path;
2999
#else
3000
    char	*s = path;
3001
 
3002
    while ((path = strchr (path, in)) != (char *)NULL)
3003
	*path = out;
3004
 
3005
    return s;
3006
#endif
3007
}
3008
 
3009
/* Load profiles onto I/O Stack */
3010
 
3011
static void F_LOCAL LoadTheProfileFiles (void)
3012
{
3013
    char	*name;
3014
    char	*Pname;
3015
 
3016
    if ((Pname = GetVariableAsString ("ETCPROFILE", FALSE)) == null)
3017
    {
3018
	Pname = "x:/etc/profile";
3019
	*Pname = GetDriveLetter (GetRootDiskDrive ());
3020
    }
3021
 
3022
    InteractiveFlag = TRUE;
3023
    ExecuteShellScript (Pname);
3024
 
3025
/*
3026
 * Check $HOME format.  If in DOS format, mark and convert to UNIX
3027
 */
3028
 
3029
    if (strchr (name = GetVariableAsString (HomeVariableName, FALSE),
3030
		CHAR_DOS_PATH) != (char *)NULL)
3031
    {
3032
	PATH_TO_UNIX (name);
3033
	SetVariableStatus (HomeVariableName, STATUS_CONVERT_MSDOS);
3034
    }
3035
 
3036
    name = BuildFileName ("profile"); 	/* Set up home profile */
3037
    ExecuteShellScript (name);
3038
    ReleaseMemoryCell ((void *)name);
3039
}
3040
 
3041
/*
3042
 * Convert Unix PATH to MSDOS PATH
3043
 */
3044
 
3045
static void F_LOCAL ConvertUnixPathToMSDOS (char *cp)
3046
{
3047
    char	*scp = cp;
3048
    int		colon = 0;
3049
 
3050
/* If there is a semi-colon or a backslash, we assume this is a DOS format
3051
 * path
3052
 */
3053
 
3054
    if ((strchr (cp, CHAR_PATH_SEPARATOR) != (char *)NULL) ||
3055
	(strchr (cp, CHAR_DOS_PATH) != (char *)NULL))
3056
	return;
3057
 
3058
/* Count the number of colons */
3059
 
3060
    while ((cp = strchr (cp, CHAR_COLON)) != (char *)NULL)
3061
    {
3062
	++colon;
3063
	++cp;
3064
    }
3065
 
3066
/* If there are no colons or there is one colon as the second character, it
3067
 * is probably an MSDOS path
3068
 */
3069
 
3070
    cp = scp;
3071
    if ((colon == 0) || ((colon == 1) && (*(cp + 1) == CHAR_COLON)))
3072
	return;
3073
 
3074
/* Otherwise, convert all colons to semis */
3075
 
3076
    while ((cp = strchr (cp, CHAR_COLON)) != (char *)NULL)
3077
	*(cp++) = CHAR_PATH_SEPARATOR;
3078
}
3079
 
3080
/* Generate a file name from a directory and name.  Return null if an error
3081
 * occurs or some malloced space containing the file name otherwise
3082
 */
3083
 
3084
char *BuildFileName (char *name)
3085
{
3086
    char	*dir = GetVariableAsString (HomeVariableName, FALSE);
3087
    char	*cp;
3088
 
3089
/* Get some space */
3090
 
3091
    if ((cp = AllocateMemoryCell (strlen (dir) + strlen (name) + 2))
3092
		== (char *)NULL)
3093
	return null;
3094
 
3095
/* Addend the directory and a / if the directory does not end in one */
3096
 
3097
    strcpy (cp, dir);
3098
 
3099
    if (!IsPathCharacter (cp[strlen (cp) - 1]))
3100
	strcat (cp, DirectorySeparator);
3101
 
3102
/* Append the file name */
3103
 
3104
    return strcat (cp, name);
3105
}
3106
 
3107
/* Clear prompts */
3108
 
3109
static void F_LOCAL ClearUserPrompts (void)
3110
{
3111
    ClearVariableStatus (PS1, STATUS_EXPORT);
3112
    ClearVariableStatus (PS2, STATUS_EXPORT);
3113
    ClearVariableStatus (PS3, STATUS_EXPORT);
3114
    SetVariableFromString (PS1, null);
3115
    SetVariableFromString (PS2, null);
3116
    SetVariableFromString (PS3, null);
3117
}
3118
 
3119
/* Process setting of SECONDS and RANDOM environment variables */
3120
 
3121
static void F_LOCAL SecondAndRandomEV (char *name, long val)
3122
{
3123
    if (!strcmp (name, SecondsVariable) &&
3124
	!(DisabledVariables & DISABLE_SECONDS))
3125
	ShellStartTime = time ((time_t *)NULL) - val;
3126
 
3127
    else if (!strcmp (name, RandomVariable) &&
3128
	     !(DisabledVariables & DISABLE_RANDOM))
3129
	srand ((int)val);
3130
}
3131
 
3132
/*
3133
 * Set up the Window name.  Give up if it does not work.
3134
 */
3135
 
3136
#if (OS_TYPE == OS_OS2)
3137
void	SetWindowName (char *title)
3138
{
3139
    HSWITCH		hswitch;
3140
    SWCNTRL		swctl;
3141
    char		*cp;
3142
 
3143
    if ((!(hswitch = WinQuerySwitchHandle (0, getpid ()))) ||
3144
	(WinQuerySwitchEntry (hswitch, &swctl)))
3145
	return;
3146
 
3147
    if (title != (char *)NULL)
3148
        cp = title;
3149
 
3150
    else if ((DisabledVariables & DISABLE_WINTITLE) ||
3151
	     ((cp = GetVariableAsString (WinTitleVariable, FALSE)) == null))
3152
	cp = DefaultWinTitle;
3153
 
3154
    strncpy (swctl.szSwtitle, cp, sizeof (swctl.szSwtitle));
3155
    swctl.szSwtitle[sizeof (swctl.szSwtitle) - 1] = 0;
3156
    WinChangeSwitchEntry (hswitch, &swctl);
3157
}
3158
#endif
3159
 
3160
/* NT Version */
3161
 
3162
#if (OS_TYPE == OS_NT)
3163
void	SetWindowName (char *title)
3164
{
3165
    char		*cp;
3166
 
3167
    if (title != (char *)NULL)
3168
        cp = title;
3169
 
3170
    else if ((DisabledVariables & DISABLE_WINTITLE) ||
3171
	     ((cp = GetVariableAsString (WinTitleVariable, FALSE)) == null))
3172
	cp = DefaultWinTitle;
3173
 
3174
    SetConsoleTitle (cp);
3175
}
3176
#endif
3177
 
3178
/*
3179
 * In OS/2, check for terminated processes
3180
 */
3181
 
3182
#if (OS_TYPE == OS_OS2)
3183
static void F_LOCAL CheckForTerminatedProcess (void)
3184
{
3185
    RESULTCODES		rescResults;
3186
    PID			pidProcess;
3187
    char		*s;
3188
    OSCALL_RET		rc;
3189
 
3190
#  if (OS_TYPE == OS_OS2)
3191
   CheckForSessionEnd ();
3192
#  endif
3193
 
3194
/* Check for tasks terminating */
3195
 
3196
    while (TRUE)
3197
    {
3198
	while ((rc = DosCwait (DCWA_PROCESSTREE, DCWW_NOWAIT, &rescResults,
3199
			       &pidProcess, 0)) == ERROR_INTERRUPT)
3200
	    continue;
3201
 
3202
/* Ignore errors */
3203
 
3204
	if (rc)
3205
	    return;
3206
 
3207
/* Remove the job */
3208
 
3209
	DeleteJob (pidProcess);
3210
 
3211
	switch (rescResults.codeTerminate)
3212
	{
3213
	    case TC_EXIT:
3214
		s = "Normal Exit";
3215
		break;
3216
 
3217
	    case TC_HARDERROR:
3218
		s = "Hard error";
3219
		break;
3220
 
3221
	    case TC_TRAP:
3222
		s = "Trapped";
3223
		break;
3224
 
3225
	    case TC_KILLPROCESS:
3226
		s = "Killed";
3227
		break;
3228
 
3229
	    default:
3230
		s = "Unknown";
3231
		break;
3232
 
3233
	}
3234
 
3235
	fprintf (stderr, "Process %d terminated - %s (%d)\n", pidProcess, s,
3236
		 rescResults.codeTerminate);
3237
    }
3238
}
3239
#endif
3240
 
3241
 
3242
/* NT Version */
3243
 
3244
#if (OS_TYPE == OS_NT)
3245
static void F_LOCAL CheckForTerminatedProcess (void)
3246
{
3247
    twalk (JobTree, LookUpJobs);
3248
}
3249
 
3250
 
3251
/*
3252
 * Walk the NT job tree looking for terminated processes
3253
 */
3254
 
3255
static void LookUpJobs (const void *key, VISIT visit, int level)
3256
{
3257
    JobList	*job = *(JobList **)key;
3258
    HANDLE	hp;
3259
    DWORD	res;
3260
    DWORD	ExitCode;
3261
 
3262
    (void) level;
3263
 
3264
    if ((visit == postorder) || (visit == leaf))
3265
    {
3266
	if ((hp = OpenProcess (PROCESS_ALL_ACCESS, (BOOL)TRUE,
3267
			       job->pid)) == NULL)
3268
	{
3269
	    PrintWarningMessage ("sh: Cannot open process %d\n%s", job->pid,
3270
				 GetOSSystemErrorMessage (GetLastError ()));
3271
	    DeleteJob (job->pid);
3272
	}
3273
 
3274
/* Wait for the object to exit */
3275
 
3276
	else if ((res = WaitForSingleObject (hp, 0)) == WAIT_OBJECT_0)
3277
	{
3278
	    DeleteJob (job->pid);
3279
 
3280
	    if (!GetExitCodeProcess (hp, &ExitCode))
3281
		PrintWarningMessage (
3282
			"sh: Cannot get termination code for process %d\n%s",
3283
			job->pid, GetOSSystemErrorMessage (GetLastError ()));
3284
	    else
3285
		fprintf (stderr, "Process %d terminated (%ld)\n", job->pid,
3286
			 ExitCode);
3287
	}
3288
 
3289
/* Failed?  - error */
3290
 
3291
	else if (res == WAIT_FAILED)
3292
	{
3293
	    PrintWarningMessage ("sh: Cannot wait for process %d\n%s",
3294
				 job->pid,
3295
				 GetOSSystemErrorMessage (GetLastError ()));
3296
 
3297
	    DeleteJob (job->pid);
3298
	}
3299
 
3300
	CloseHandle (hp);
3301
    }
3302
}
3303
#endif
3304
 
3305
 
3306
/* UNIX version */
3307
 
3308
#if (OS_TYPE == OS_UNIX)
3309
static void F_LOCAL CheckForTerminatedProcess (void)
3310
{
3311
    fputs ("UNIX: CheckForTerminatedProcess NI\n", stderr);
3312
}
3313
#endif
3314
 
3315
 
3316
/*
3317
 * Check for end of a Session
3318
 */
3319
 
3320
#if (OS_TYPE == OS_OS2)
3321
static void F_LOCAL	CheckForSessionEnd (void)
3322
{
3323
    OSCALL_PARAM	DataLength;
3324
    OSCALL_RET		rc;
3325
    BYTE		bElemPriority;
3326
    struct SessionEnd
3327
    {
3328
        unsigned short	SessionId;
3329
	unsigned short	ExitCode;
3330
    }			*DataAddress;
3331
 
3332
#  if (OS_SIZE == OS_32)
3333
    REQUESTDATA		Request;
3334
#  else
3335
    QUEUERESULT		Request;
3336
#  endif
3337
 
3338
/* Check for sessions terminating */
3339
 
3340
    while ((rc = DosReadQueue (SessionQueueHandler, &Request, &DataLength,
3341
    		       	       (PVOID *)&DataAddress,
3342
    		       	       0, DCWW_NOWAIT, &bElemPriority,
3343
			       SessionQueueSema)) == NO_ERROR)
3344
    {
3345
	DeleteJobBySession (DataAddress->SessionId);
3346
 
3347
	fprintf (stderr, "Session %d terminated - Normal Exit (%d)\n",
3348
		 DataAddress->SessionId, DataAddress->ExitCode);
3349
 
3350
#  if (OS_SIZE == OS_32)
3351
	DosFreeMem (DataAddress);
3352
#  else
3353
	DosFreeSeg (SELECTOROF ((DataAddress)));
3354
#  endif
3355
    }
3356
}
3357
#endif
3358
 
3359
/*
3360
 * Set up the Parameter Environment variables
3361
 */
3362
 
3363
static void F_LOCAL SetUpParameterEV (int argc, char **argv, char *name)
3364
{
3365
    Word_B	*wb = (Word_B *)NULL;
3366
    char	*Value;
3367
    int		i;
3368
 
3369
    if ((Value = StringSave (name)) == null)
3370
    {
3371
	fprintf (stderr, BasicErrorMessage, ShellNameLiteral, Outofmemory1);
3372
	return;
3373
    }
3374
 
3375
    wb = AddParameter (Value, wb, ShellNameLiteral);
3376
 
3377
    for (i = 1; i < argc; ++i)
3378
    {
3379
	if ((!AssignVariableFromString (argv[i], (int *)NULL)) &&
3380
	    (wb != (Word_B *)NULL))
3381
	{
3382
	    if ((Value = StringSave (argv[i])) != null)
3383
		wb = AddParameter (Value, wb, ShellNameLiteral);
3384
 
3385
	    else
3386
	    {
3387
		fprintf (stderr, BasicErrorMessage, ShellNameLiteral,
3388
			 Outofmemory1);
3389
		return;
3390
	    }
3391
	}
3392
    }
3393
 
3394
    if (wb != (Word_B *)NULL)
3395
	wb = AddParameter ((char *)NULL, wb, ShellNameLiteral);
3396
}
3397
 
3398
/*
3399
 * Update the Seconds and Random variables
3400
 */
3401
 
3402
void HandleSECONDandRANDOM (void)
3403
{
3404
    if (!(DisabledVariables & DISABLE_SECONDS))
3405
	LookUpVariable (SecondsVariable, 0, TRUE);
3406
 
3407
    if (!(DisabledVariables & DISABLE_RANDOM))
3408
	LookUpVariable (RandomVariable, 0, TRUE);
3409
}
3410
 
3411
/*
3412
 * Set the status of an environment variable
3413
 */
3414
 
3415
void SetVariableStatus (char *name,             /* Variable name        */
3416
			int  flag)		/* New status		*/
3417
{
3418
    VariableList	*vp = LookUpVariable (name, 0, TRUE);
3419
 
3420
    if (IS_VariableFC ((int)*vp->name))	/* not an internal symbol ($# etc) */
3421
	vp->status |= flag;
3422
}
3423
 
3424
/*
3425
 * Array version - only 0 is exported
3426
 */
3427
 
3428
void SetVariableArrayStatus (char *name,	/* Variable name	*/
3429
			     int  index,	/* Array index		*/
3430
			     int  flag)		/* New status		*/
3431
{
3432
    VariableList	*vp = LookUpVariable (name, index, TRUE);
3433
 
3434
    if (IS_VariableFC ((int)*vp->name))	/* not an internal symbol ($# etc) */
3435
    {
3436
	vp->status |= flag;
3437
 
3438
	if (index)
3439
	    vp->status &= ~STATUS_EXPORT;
3440
    }
3441
}
3442
 
3443
/*
3444
 * Set the status of an environment variable
3445
 */
3446
 
3447
void ClearVariableStatus (char *name, int flag)
3448
{
3449
    VariableList	*vp = LookUpVariable (name, 0, TRUE);
3450
 
3451
    if (IS_VariableFC ((int)*vp->name))	/* not an internal symbol ($# etc) */
3452
	vp->status &= ~flag;
3453
}
3454
 
3455
/*
3456
 * Check allowed to set variable
3457
 */
3458
 
3459
static bool F_LOCAL AllowedToSetVariable (VariableList *vp)
3460
{
3461
    if (vp->status & STATUS_READONLY)
3462
    {
3463
	ShellErrorMessage (LIT_2Strings, vp->name, LIT_IsReadonly);
3464
	return FALSE;
3465
    }
3466
 
3467
/* Check for $PATH, $SHELL or $ENV reset in restricted shell */
3468
 
3469
    if ((!strcmp (vp->name, PathLiteral) || !strcmp (vp->name, ENVVariable) ||
3470
	 !strcmp (vp->name, ShellVariableName)) &&
3471
	CheckForRestrictedShell (PathLiteral))
3472
	return FALSE;
3473
 
3474
    return TRUE;
3475
}
3476
 
3477
/*
3478
 * Set up a variable from a string
3479
 */
3480
 
3481
void SetVariableFromString (char *name, char *val)
3482
{
3483
    SetVariableArrayFromString (name, 0, val);
3484
}
3485
 
3486
/*
3487
 * Array index version
3488
 */
3489
 
3490
void SetVariableArrayFromString (char *name, int Index, char *val)
3491
{
3492
    VariableList	*vp = LookUpVariable (name, Index, TRUE);
3493
    char		*xp = null;
3494
    long		nval;
3495
 
3496
/* Check if allowed to set variable */
3497
 
3498
    if (!AllowedToSetVariable (vp))
3499
	return;
3500
 
3501
/* If we change the PATH to a new value, we need to untrack some aliases */
3502
 
3503
    if (!strcmp (name, PathLiteral) && strcmp (vp->value, val))
3504
	UnTrackAllAliases ();
3505
 
3506
    CheckOPTIND (name, atol (val));
3507
 
3508
/* Save the new value */
3509
 
3510
    if ((!(vp->status & STATUS_INTEGER)) && (val != null) && strlen (val) &&
3511
	((xp = StringSave (val = SuppressSpacesZeros (vp, val))) == null))
3512
	    return;
3513
 
3514
/* Free the old value if appropriate */
3515
 
3516
    if (vp->value != null)
3517
	ReleaseMemoryCell ((void *)vp->value);
3518
 
3519
    vp->value = null;
3520
 
3521
/* String value? */
3522
 
3523
    if (!(vp->status & STATUS_INTEGER))
3524
    {
3525
	vp->value = xp;
3526
 
3527
	if (!vp->width)
3528
	    vp->width = strlen (val);
3529
    }
3530
 
3531
/* No - Number value */
3532
 
3533
    else if (!ValidMathsExpression (val, &nval))
3534
    {
3535
	SecondAndRandomEV (name, nval);
3536
	SetUpANumericValue (vp, nval, -1);
3537
    }
3538
 
3539
 
3540
/* Check to see if it should be exported */
3541
 
3542
    if (FL_TEST (FLAG_ALL_EXPORT))
3543
	vp->status |= STATUS_EXPORT;
3544
 
3545
/* Convert UNIX to DOS for PATH variable */
3546
 
3547
    if (!strcmp (name, PathLiteral))
3548
	ConvertUnixPathToMSDOS (GetVariableAsString (PathLiteral, FALSE));
3549
 
3550
    else if (!strcmp (name, CDPathLiteral))
3551
	ConvertUnixPathToMSDOS (GetVariableAsString (CDPathLiteral, FALSE));
3552
 
3553
    else if (!strcmp (name, PathExtsLiteral))
3554
	BuildExtensionLists ();
3555
 
3556
/* Set up IFS characters */
3557
 
3558
    else if (!strcmp (name, IFS))
3559
	SetCharacterTypes (val, C_IFS);
3560
 
3561
/* Check for title change */
3562
 
3563
#if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)
3564
    else if (!strcmp (name, FCEditVariable))
3565
	SetEditorMode (val);
3566
 
3567
    else if (!strcmp (name, EditorVariable))
3568
	SetEditorMode (val);
3569
 
3570
    else if (!strcmp (name, VisualVariable))
3571
	SetEditorMode (val);
3572
#endif
3573
 
3574
#if (OS_TYPE != OS_DOS)
3575
    else if ((!(DisabledVariables & DISABLE_WINTITLE)) &&
3576
	     (!strcmp (name, WinTitleVariable)))
3577
	SetWindowName ((char *)NULL);
3578
#endif
3579
}
3580
 
3581
 
3582
/*
3583
 * Set a variable from a numeric
3584
 */
3585
 
3586
void SetVariableFromNumeric (char *name, long value)
3587
{
3588
    SetVariableArrayFromNumeric (name, 0, value);
3589
}
3590
 
3591
 
3592
/* Array version */
3593
 
3594
void SetVariableArrayFromNumeric (char *name, int Index, long value)
3595
{
3596
    VariableList	*vp = LookUpVariable (name, Index, TRUE);
3597
    char		NumericBuffer[20];
3598
 
3599
/* Check if allowed to set variable */
3600
 
3601
    if (!AllowedToSetVariable (vp))
3602
	return;
3603
 
3604
    CheckOPTIND (name, value);
3605
 
3606
    if (!(vp->status & STATUS_INTEGER))
3607
    {
3608
	sprintf (NumericBuffer, "%ld", value);
3609
	SetVariableFromString (name, NumericBuffer);
3610
    }
3611
 
3612
/* Save the integer value */
3613
 
3614
    else
3615
	SetUpANumericValue (vp, value, -1);
3616
 
3617
    SecondAndRandomEV (name, value);
3618
}
3619
 
3620
 
3621
/*
3622
 * Get variable as a numeric
3623
 */
3624
 
3625
long GetVariableAsNumeric (char *name)
3626
{
3627
    return GetVariableArrayAsNumeric (name, 0);
3628
}
3629
 
3630
 
3631
/* Array version */
3632
 
3633
long GetVariableArrayAsNumeric (char *name, int Index)
3634
{
3635
    VariableList	*vp = LookUpVariable (name, Index, FALSE);
3636
 
3637
    if (vp->status & STATUS_INTEGER)
3638
	return vp->nvalue;
3639
 
3640
    else
3641
	return atol (vp->value);
3642
}
3643
 
3644
 
3645
/*
3646
 * Get variable as a formatted string
3647
 */
3648
 
3649
char *GetVariableAsString (char *name, bool Format)
3650
{
3651
    return GetVariableArrayAsString (name, 0, Format);
3652
}
3653
 
3654
 
3655
/*
3656
 * Indexed version
3657
 */
3658
 
3659
char *GetVariableArrayAsString (char *name, int Index, bool Format)
3660
{
3661
    VariableList	*vp = LookUpVariable (name, Index, FALSE);
3662
    char		*Value = vp->value;
3663
    char		*xp;
3664
    size_t		len;
3665
    char		*NumericBuffer;
3666
 
3667
    if (vp->status & STATUS_INTEGER)
3668
    {
3669
	if ((NumericBuffer = GetAllocatedSpace (40)) == (char *)NULL)
3670
	    return null;
3671
 
3672
	if (vp->base != 10)
3673
	{
3674
	    sprintf (NumericBuffer, LIT_BNumber, vp->base);
3675
	    xp = NumericBuffer + strlen (NumericBuffer);
3676
	}
3677
 
3678
	else
3679
	    xp = NumericBuffer;
3680
 
3681
        ltoa (vp->nvalue, xp, vp->base);
3682
	return NumericBuffer;
3683
    }
3684
 
3685
/* Handle a string variable, if no formating required, return it */
3686
 
3687
    if (!Format)
3688
	return vp->value;
3689
 
3690
/* Left justify ? */
3691
 
3692
    if (vp->status & STATUS_LEFT_JUSTIFY)
3693
    {
3694
	xp = SuppressSpacesZeros (vp, Value);
3695
 
3696
	if ((Value = GetAllocatedSpace (vp->width + 1)) == (char *)NULL)
3697
	    return null;
3698
 
3699
	memset (Value, CHAR_SPACE, vp->width);
3700
	Value[vp->width] = 0;
3701
 
3702
	if ((len = strlen (xp)) > vp->width)
3703
	    len = vp->width;
3704
 
3705
	memcpy (Value, xp, len);
3706
    }
3707
 
3708
/* Right justify ? */
3709
 
3710
    else if (vp->status & (STATUS_RIGHT_JUSTIFY | STATUS_ZERO_FILL))
3711
    {
3712
	if ((xp = GetAllocatedSpace (vp->width + 1)) == (char *)NULL)
3713
	    return null;
3714
 
3715
	if ((len = strlen (Value)) < vp->width)
3716
	{
3717
	    memset (xp, ((vp->status & STATUS_ZERO_FILL) &&
3718
			 (isdigit (*Value))) ? '0' : CHAR_SPACE, vp->width);
3719
 
3720
	    memcpy (xp + (vp->width - len), Value, len);
3721
	}
3722
 
3723
	else
3724
	    memcpy (xp, Value + vp->width - len, vp->width);
3725
 
3726
	*(xp + vp->width) = 0;
3727
	Value = xp;
3728
    }
3729
 
3730
/* Handle upper and lower case conversions */
3731
 
3732
    if (vp->status & STATUS_LOWER_CASE)
3733
	Value = strlwr (StringCopy (Value));
3734
 
3735
    if (vp->status & STATUS_UPPER_CASE)
3736
	Value = strupr (StringCopy (Value));
3737
 
3738
    return Value;
3739
}
3740
 
3741
 
3742
/*
3743
 * Set up a numeric value
3744
 */
3745
 
3746
static void F_LOCAL SetUpANumericValue (VariableList *vp, long value, int base)
3747
{
3748
    vp->nvalue = value;
3749
    vp->status |= STATUS_INTEGER;
3750
 
3751
    if (vp->base == 0)
3752
	vp->base = (base > 1) ? base
3753
			      : ((LastNumberBase != -1) ? LastNumberBase
3754
							: 10);
3755
 
3756
    if (vp->value != null)
3757
	ReleaseMemoryCell ((void *)vp->value);
3758
 
3759
    vp->value = null;
3760
}
3761
 
3762
 
3763
/*
3764
 * Suppress leading spaces and zeros
3765
 */
3766
 
3767
static char * F_LOCAL SuppressSpacesZeros (VariableList *vp, char *value)
3768
{
3769
    if (vp->status & STATUS_LEFT_JUSTIFY)
3770
    {
3771
	while (*value == CHAR_SPACE)
3772
	    value++;
3773
 
3774
	if (vp->status & STATUS_ZERO_FILL)
3775
	{
3776
	    while (*value == '0')
3777
		value++;
3778
	}
3779
    }
3780
 
3781
    return value;
3782
}
3783
 
3784
 
3785
/*
3786
 * Check to see if a reset of CheckOPTIND has occured
3787
 */
3788
 
3789
static void F_LOCAL CheckOPTIND (char *name, long value)
3790
{
3791
    if ((value == 1) && (!(DisabledVariables & DISABLE_OPTIND)) &&
3792
	(strcmp (OptIndVariable, name) == 0))
3793
	ResetGetoptsValues (FALSE);
3794
}
3795
 
3796
 
3797
/*
3798
 * Initialise the Integer variables by creating them
3799
 */
3800
 
3801
static void F_LOCAL CreateIntegerVariables (void)
3802
{
3803
    struct ShellVariablesInit	*wp = InitialiseShellVariables;
3804
 
3805
    while  (wp->Name != (char *)NULL)
3806
    {
3807
	SetVariableStatus (wp->Name, wp->Status);
3808
 
3809
	if (wp->CValue != null)
3810
	    SetVariableFromString (wp->Name, wp->CValue);
3811
 
3812
	wp++;
3813
    }
3814
}
3815
 
3816
 
3817
/*
3818
 * Close up and exit
3819
 */
3820
 
3821
void FinalExitCleanUp (int status)
3822
{
3823
#if (OS_TYPE == OS_OS)
3824
#  if (OS_SIZE == OS_32)
3825
    DosCloseEventSem (SessionQueueSema);
3826
    DosCloseQueue (SessionQueueHandler);
3827
#  else
3828
    DosCloseSem (SessionQueueSema);
3829
    DosCloseQueue (SessionQueueHandler);
3830
#  endif
3831
#endif
3832
 
3833
    exit (status);
3834
}
3835
 
3836
 
3837
/*
3838
 * Create Session termination semaphores and queues.  Also get the number
3839
 * file handlers.
3840
 */
3841
 
3842
#if (OS_TYPE == OS_OS2)
3843
static void F_LOCAL CreateTerminationQueues (void)
3844
{
3845
    int			count = 0;
3846
    static char		Name[25];
3847
    OSCALL_RET		rc;
3848
#  if (OS_SIZE == OS_32)
3849
    LONG		ReqCount = 0;		/* Increment		*/
3850
    ULONG		CurMaxFH;		/* Available File handlers */
3851
 
3852
    DosSetRelMaxFH (&ReqCount, &CurMaxFH);
3853
    MaxNumberofFDs = min (CurMaxFH, 32 + FDBASE);
3854
#  endif
3855
 
3856
/* Create semaphore for queue */
3857
 
3858
    while (TRUE)
3859
    {
3860
#  if (OS_SIZE == OS_32)
3861
        sprintf (Name, "\\SEM32\\SHELL\\%.5d", count++);
3862
#  else
3863
        sprintf (Name, "\\SEM\\SHELL\\%.5d", count++);
3864
#  endif
3865
 
3866
#  if (OS_SIZE == OS_32)
3867
	if ((rc = DosCreateEventSem (Name, &SessionQueueSema,
3868
				     DC_SEM_SHARED, TRUE)) == NO_ERROR)
3869
	    break;
3870
#  else
3871
	if ((rc = DosCreateSem (CSEM_PUBLIC, &SessionQueueSema,
3872
				Name)) == NO_ERROR)
3873
	{
3874
	    DosSemClear (SessionQueueSema);
3875
	    break;
3876
	}
3877
#  endif
3878
 
3879
/* Check for error */
3880
 
3881
#  if (OS_SIZE == OS_32)
3882
	if (rc != ERROR_DUPLICATE_NAME)
3883
#  else
3884
	if (rc != ERROR_ALREADY_EXISTS)
3885
#  endif
3886
	{
3887
	    SessionQueueSema = 0;
3888
	    PrintErrorMessage ("DosCreateSem: Cannot create semaphore\n%s",
3889
	    		       GetOSSystemErrorMessage (rc));
3890
	}
3891
    }
3892
 
3893
/* Create the queue */
3894
 
3895
    count = 0;
3896
 
3897
    while (TRUE)
3898
    {
3899
#  if (OS_SIZE == OS_32)
3900
        sprintf (Name, "\\QUEUES\\SHELL\\%.5d", count++);
3901
#  else
3902
        sprintf (Name, "\\QUEUES\\SHQ%.5d", count++);
3903
#  endif
3904
 
3905
	if ((rc = DosCreateQueue (&SessionQueueHandler,
3906
#ifdef QUE_CONVERT_ADDRESS
3907
				  QUE_FIFO | QUE_CONVERT_ADDRESS,
3908
#else
3909
				  QUE_FIFO,
3910
#endif
3911
 
3912
				  Name)) == NO_ERROR)
3913
	    break;
3914
 
3915
/* Check for error */
3916
 
3917
	if (rc != ERROR_QUE_DUPLICATE)
3918
	{
3919
	    SessionQueueHandler = 0;
3920
	    PrintErrorMessage ("DosCreateQueue: Cannot create queue\n%s",
3921
	    		       GetOSSystemErrorMessage (rc));
3922
	}
3923
    }
3924
 
3925
    SessionEndQName = Name;
3926
}
3927
#endif
3928
 
3929
/*
3930
 * Set up Edit mode
3931
 */
3932
 
3933
#if defined (FLAGS_EMACS) || defined (FLAGS_VI) || defined (FLAGS_GMACS)
3934
static void F_LOCAL SetEditorMode (char *ed)
3935
{
3936
    char	*rcp;
3937
 
3938
    if ((rcp = strrchr (ed, '/')) != (char *)NULL)
3939
	ed = rcp + 1;
3940
 
3941
#  ifdef FLAGS_EMACS
3942
    if (strstr (ed, "emacs"))
3943
    {
3944
	ShellGlobalFlags &= ~FLAGS_EDITORS;
3945
	ShellGlobalFlags |= FLAGS_EMACS;
3946
    }
3947
#  endif
3948
 
3949
#  ifdef FLAGS_VI
3950
    if (strstr (ed, "vi"))
3951
    {
3952
	ShellGlobalFlags &= ~FLAGS_EDITORS;
3953
	ShellGlobalFlags |= FLAGS_VI;
3954
    }
3955
#  endif
3956
 
3957
#  ifdef FLAGS_GMACS
3958
    if (strstr (ed, "gmacs"))
3959
    {
3960
	ShellGlobalFlags &= ~FLAGS_EDITORS;
3961
	ShellGlobalFlags |= FLAGS_GMACS;
3962
    }
3963
#  endif
3964
}
3965
#endif