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