Subversion Repositories DevTools

Rev

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