w32tex
About: TeX Live provides a comprehensive TeX system including all the major TeX-related programs, macro packages, and fonts that are free software. Windows sources.
  Fossies Dox: w32tex-src.tar.xz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

sh7.c
Go to the documentation of this file.
1 /*
2  * MS-DOS SHELL - Internal Command Processing
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. The
8  * code for the test (dotest) command was based on code written by
9  * Erik Baalbergen. The following copyright conditions apply:
10  *
11  * 1. Redistribution and use in source and binary forms are permitted
12  * provided that the above copyright notice is duplicated in the
13  * source form and the copyright notice in file sh6.c is displayed
14  * on entry to the program.
15  *
16  * 2. The sources (or parts thereof) or objects generated from the sources
17  * (or parts of sources) cannot be sold under any circumstances.
18  *
19  * $Header: /usr/users/istewart/shell/sh2.3/Release/RCS/sh7.c,v 2.18 1994/08/25 20:49:11 istewart Exp $
20  *
21  * $Log: sh7.c,v $
22  * Revision 2.18 1994/08/25 20:49:11 istewart
23  * MS Shell 2.3 Release
24  *
25  * Revision 2.17 1994/02/23 09:23:38 istewart
26  * Beta 233 updates
27  *
28  * Revision 2.16 1994/02/01 10:25:20 istewart
29  * Release 2.3 Beta 2, including first NT port
30  *
31  * Revision 2.15 1994/01/20 14:51:43 istewart
32  * Release 2.3 Beta 1
33  *
34  * Revision 2.14 1994/01/11 17:55:25 istewart
35  * Release 2.3 Beta 0 patches
36  *
37  * Revision 2.13 1993/12/01 11:58:34 istewart
38  * Release 226 beta
39  *
40  * Revision 2.12 1993/11/09 10:39:49 istewart
41  * Beta 226 checking
42  *
43  * Revision 2.11 1993/08/25 16:03:57 istewart
44  * Beta 225 - see Notes file
45  *
46  * Revision 2.10 1993/07/02 10:21:35 istewart
47  * 224 Beta fixes
48  *
49  * Revision 2.9 1993/06/14 11:00:12 istewart
50  * More changes for 223 beta
51  *
52  * Revision 2.8 1993/06/02 09:52:35 istewart
53  * Beta 223 Updates - see Notes file
54  *
55  * Revision 2.7 1993/02/16 16:03:15 istewart
56  * Beta 2.22 Release
57  *
58  * Revision 2.6 1993/01/26 18:35:09 istewart
59  * Release 2.2 beta 0
60  *
61  * Revision 2.5 1992/12/14 10:54:56 istewart
62  * BETA 215 Fixes and 2.1 Release
63  *
64  * Revision 2.4 1992/11/06 10:03:44 istewart
65  * 214 Beta test updates
66  *
67  * Revision 2.3 1992/09/03 18:54:45 istewart
68  * Beta 213 Updates
69  *
70  * Revision 2.2 1992/07/16 14:33:34 istewart
71  * Beta 212 Baseline
72  *
73  * Revision 2.1 1992/07/10 10:52:48 istewart
74  * 211 Beta updates
75  *
76  * Revision 2.0 1992/05/07 21:33:35 Ian_Stewartson
77  * MS-Shell 2.0 Baseline release
78  *
79  */
80 
81 #include <sys/types.h>
82 #include <sys/stat.h>
83 #if defined (__EMX__)
84 # include <sys/wait.h>
85 #endif
86 #include <stdio.h>
87 #include <signal.h>
88 #include <errno.h>
89 #include <setjmp.h>
90 #include <ctype.h>
91 #include <string.h>
92 #include <unistd.h>
93 #include <stdlib.h>
94 #include <fcntl.h>
95 #include <limits.h>
96 #include <dirent.h>
97 #include <stdarg.h>
98 #include <time.h>
99 #include "sh.h"
100 
101 #define SECS 60L
102 #define MINS 3600L
103 
104 #if (OS_TYPE == OS_OS2)
105 # if defined (__EMX__)
106 # define PGM_TITLE_TYPE unsigned char
107 # else
108 # define PGM_TITLE_TYPE char
109 # endif
110 #endif
111 /* Definitions for echo and print */
112 
113 #define ECHO_ESCAPE 0x01
114 #define ECHO_NO_EOL 0x02
115 #define ECHO_HISTORY 0x04
116 
117 /* Definitions for test */
118 
119 #define END_OF_INPUT 0
120 #define FILE_READABLE 1
121 #define FILE_WRITABLE 2
122 #define FILE_REGULAR 3
123 #define FILE_DIRECTRY 4
124 #define FILE_NONZERO 5
125 #define FILE_TERMINAL 6
126 #define STRING_ZERO 7
127 #define STRING_NONZERO 8
128 #define STRING_EQUAL 9
129 #define STRING_NOTEQUAL 10
130 #define NUMBER_EQUAL 11
131 #define NUMBER_NOTEQUAL 12
132 #define NUMBER_EQ_GREAT 13
133 #define NUMBER_GREATER 14
134 #define NUMBER_EQ_LESS 15
135 #define NUMBER_LESS 16
136 #define UNARY_NOT 17
137 #define BINARY_AND 18
138 #define BINARY_OR 19
139 #define LPAREN 20
140 #define RPAREN 21
141 #define OPERAND 22
142 #define FILE_EXECUTABLE 23
143 #define FILE_USER 24
144 #define FILE_GROUP 25
145 #define FILE_TEXT 26
146 #define FILE_BLOCK 27
147 #define FILE_CHARACTER 28
148 #define FILE_FIFO 29
149 #define FILE_NEWER 30
150 #define FILE_OLDER 31
151 #define STRING_LESS 32
152 #define STRING_GREATER 33
153 #define FILE_EXISTS 34
154 #define TEST_OPTION 35
155 #define FILE_SYMBOLIC 36
156 #define FILE_OWNER 37
157 #define FILE_GROUPER 38
158 #define FILE_SOCKET 39
159 #define FILE_EQUAL 40
160 
161 #define UNARY_OP 1
162 #define BINARY_OP 2
163 #define B_UNARY_OP 3
164 #define B_BINARY_OP 4
165 #define PAREN 5
166 
167 /* This is the list of operators and the conversion values */
168 
169 static struct TestOperator {
171  short OperatorID;
173 } ListOfTestOperators[] = {
174 
175 /* These two entries are modified depending on the test program. The
176  * alternative values are shown in the following comment.
177  */
178 
179  {"-a", FILE_EXISTS, UNARY_OP},
180  /* {"-a", BINARY_AND, B_BINARY_OP}, */
181  {"-o", TEST_OPTION, UNARY_OP},
182  /* {"-o", BINARY_OR, B_BINARY_OP}, */
183 
184 /* Add new entries after here */
185 
186  {"-r", FILE_READABLE, UNARY_OP},
187  {"-w", FILE_WRITABLE, UNARY_OP},
188  {"-x", FILE_EXECUTABLE, UNARY_OP},
189  {"-f", FILE_REGULAR, UNARY_OP},
190  {"-d", FILE_DIRECTRY, UNARY_OP},
191  {"-s", FILE_NONZERO, UNARY_OP},
192  {"-t", FILE_TERMINAL, UNARY_OP},
193  {"-z", STRING_ZERO, UNARY_OP},
194  {"-n", STRING_NONZERO, UNARY_OP},
195  {"=", STRING_EQUAL, BINARY_OP},
196  {"!=", STRING_NOTEQUAL, BINARY_OP},
197  {"<", STRING_LESS, BINARY_OP},
198  {">", STRING_GREATER, BINARY_OP},
199  {"-eq", NUMBER_EQUAL, BINARY_OP},
200  {"-ne", NUMBER_NOTEQUAL, BINARY_OP},
201  {"-ge", NUMBER_EQ_GREAT, BINARY_OP},
202  {"-gt", NUMBER_GREATER, BINARY_OP},
203  {"-le", NUMBER_EQ_LESS, BINARY_OP},
204  {"-lt", NUMBER_LESS, BINARY_OP},
205  {"!", UNARY_NOT, B_UNARY_OP},
206  {"(", LPAREN, PAREN},
207  {")", RPAREN, PAREN},
208  {"&&", BINARY_AND, B_BINARY_OP},
209  {"||", BINARY_OR, B_BINARY_OP},
210  {"-c", FILE_CHARACTER, UNARY_OP},
211  {"-b", FILE_BLOCK, UNARY_OP},
212  {"-u", FILE_USER, UNARY_OP},
213  {"-g", FILE_GROUP, UNARY_OP},
214  {"-k", FILE_TEXT, UNARY_OP},
215  {"-p", FILE_FIFO, UNARY_OP},
216  {"-h", FILE_SYMBOLIC, UNARY_OP},
217  {"-L", FILE_SYMBOLIC, UNARY_OP},
218  {"-O", FILE_OWNER, UNARY_OP},
219  {"-G", FILE_GROUPER, UNARY_OP},
220  {"-S", FILE_SOCKET, UNARY_OP},
221  {"-nt", FILE_NEWER, BINARY_OP},
222  {"-ot", FILE_OLDER, BINARY_OP},
223  {"-ef", FILE_EQUAL, BINARY_OP},
224  {(char *)NULL, 0, 0}
225 };
226 
227 /*
228  * -o values for set
229  */
230 
231 static struct SetOptions {
232  char *OptionName; /* Option name */
233  unsigned int FlagValue; /* Option flag */
234  bool HasOptionValue; /* Has -x value */
235 } SetOptions[] = {
236  { "alternation", FLAGS_ALTERNATION, FALSE },
237  { "allexport", 'a', TRUE },
238 
239 #ifdef FLAGS_BREAK_SWITCH
240  { "break", FLAGS_BREAK_SWITCH, FALSE },
241 #endif
242 
243 #ifdef FLAGS_EMACS
244  { "emacs", FLAGS_EMACS, FALSE },
245 #endif
246 
247  { "errexit", 'e', TRUE },
248 
249 #ifdef FLAGS_GMACS
250  { "gmacs", FLAGS_GMACS, FALSE },
251 #endif
252 
253 #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_OS2)
254  { "ignorecase", FLAGS_NOCASE, FALSE },
255 #endif
256 
257  { "ignoreeof", FLAGS_IGNOREEOF, FALSE },
258  { "keyword", 'k', TRUE },
259  { "markdirs", FLAGS_MARKDIRECTORY, FALSE },
260  { "msdos", FLAGS_MSDOS_FORMAT, FALSE },
261  { "noclobber", FLAGS_NOCLOBER, FALSE },
262  { "noexec", 'n', TRUE },
263  { "noglob", 'f', TRUE },
264  { "nounset", 'u', TRUE },
265 
266 #ifdef FLAGS_SET_OS2
267  { "os2", FLAGS_SET_OS2, FALSE },
268 #endif
269 
270  { "privileged", 'p', TRUE },
271 
272 #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_OS2)
273  { "realpipes", FLAGS_REALPIPES, FALSE },
274 #endif
275 
276  { "trackall", 'h', TRUE },
277 
278 #ifdef FLAGS_VI
279  { "vi", FLAGS_VI, FALSE },
280 #endif
281 
282  { "verbose", 'v', TRUE },
283  { "verify", FLAGS_VERIFY_SWITCH, FALSE },
284 
285 #ifdef FLAGS_SET_NT
286  { "winnt", FLAGS_SET_NT, FALSE },
287 #endif
288 
289  { "xtrace", 'x', TRUE },
290  { (char *)NULL, 0, FALSE }
291 };
292 
293 /*
294  * Getopts values
295  */
296 
297 static GetoptsIndex GetOptsIndex = { 1, 1 };
298 
299 /*
300  * Signal Name structure
301  *
302  * Note that the first two entries are constructed such that the character
303  * before the name is a ~.
304  */
305 
306 #define MAX_TRAP_SIGNALS ARRAY_SIZE (TrapSignalList)
307 #define SIG_SPECIAL -1 /* Error or trap */
308 #define SIG_NO_MAP -2 /* No DOS mapping */
309 
310 #if (OS_TYPE == OS_UNIX)
311 #define MAX_SIG_MAP NSIG
312 #else
313 #define MAX_SIG_MAP ARRAY_SIZE (UnixToDosSignals)
314 #endif
315 
316 /*
317  * Signal name to number mapping
318  */
319 
320 static struct TrapSignalList {
321  char *name;
322  int signo;
323 } TrapSignalList [] = {
324  { Trap_DEBUG + 1, SIG_SPECIAL },
325  { Trap_ERR + 1, SIG_SPECIAL },
326  { LIT_exit, 0 },
327  { "SIGINT", SIGINT },
328  { "SIGFPE", SIGFPE },
329  { "SIGILL", SIGILL },
330  { "SIGSEGV", SIGSEGV },
331  { "SIGABRT", SIGABRT },
332 
333 #ifdef SIGTERM
334  { "SIGTERM", SIGTERM },
335 #endif
336 
337 #ifdef SIGBREAK
338  { "SIGBREAK", SIGBREAK },
339 #endif
340 
341 #ifdef SIGUSR1
342  { "SIGUSR1", SIGUSR1 },
343 #endif
344 
345 #ifdef SIGUSR2
346  { "SIGUSR2", SIGUSR2 },
347 #endif
348 
349 #ifdef SIGUSR3
350  { "SIGUSR3", SIGUSR3 },
351 #endif
352 
353 #ifdef SIGIDIVZ
354  { "SIGIDIVZ", SIGIDIVZ },
355 #endif
356 
357 #ifdef SIGIOVFL
358  { "SIGIOVFL", SIGIOVFL },
359 #endif
360 
361 #if (OS_TYPE == OS_UNIX)
362  { "SIGHUP", SIGHUP },
363  { "SIGQUIT", SIGQUIT },
364  { "SIGTRAP", SIGTRAP },
365  { "SIGIOT", SIGIOT },
366  { "SIGEMT", SIGEMT },
367  { "SIGKILL", SIGKILL },
368  { "SIGBUS", SIGBUS },
369  { "SIGSYS", SIGSYS },
370  { "SIGPIPE", SIGPIPE },
371  { "SIGALRM", SIGALRM },
372  { "SIGTERM", SIGTERM },
373  { "SIGUSR1", SIGUSR1 },
374  { "SIGUSR2", SIGUSR2 },
375  { "SIGCLD", SIGCLD },
376  { "SIGPWR", SIGPWR },
377  { "SIGWINCH", SIGWINCH },
378  { "SIGURG", SIGURG },
379  { "SIGPOLL", SIGPOLL },
380  { "SIGIO", SIGIO },
381  { "SIGSTOP", SIGSTOP },
382  { "SIGTSTP", SIGTSTP },
383  { "SIGCONT", SIGCONT },
384  { "SIGTTIN", SIGTTIN },
385  { "SIGTTOU", SIGTTOU },
386  { "SIGVTALRM", SIGVTALRM },
387  { "SIGPROF", SIGPROF },
388  { "SIGXCPU", SIGXCPU },
389  { "SIGXFSZ", SIGXFSZ },
390 #endif
391 };
392 
393 /*
394  * UNIX to DOS signal number mapping. We only have 15 mappings because
395  * only the fdirst 15 signal numbers are common
396  */
397 
398 #if (OS_TYPE != OS_UNIX)
399 static int UnixToDosSignals [] = {
400  0, /* 0 - On exit */
401  SIG_NO_MAP, /* 1 - hangup */
402  SIGINT, /* 2 - interrupt (DEL) */
403  SIG_NO_MAP, /* 3 - quit (ASCII FS) */
404  SIGILL, /* 4 - illegal instruction */
405  SIG_NO_MAP, /* 5 - trace trap */
406  SIG_NO_MAP, /* 6 - IOT instruction */
407  SIG_NO_MAP, /* 7 - EMT instruction */
408  SIGFPE, /* 8 - floating point exception */
409  SIG_NO_MAP, /* 9 - kill */
410  SIG_NO_MAP, /* 10 - bus error */
411  SIGSEGV, /* 11 - segmentation violation */
412  SIG_NO_MAP, /* 12 - bad argument to system call */
413  SIG_NO_MAP, /* 13 - write on a pipe with no reader */
414  SIG_NO_MAP, /* 14 - alarm clock */
415  SIGTERM /* 15 - software termination signal */
416 };
417 #endif
418 
419 
420 /*
421  * General Functions
422  */
423 
424 static int DeleteAllVariables (const void *, const void *);
425 static int F_LOCAL doOutofMemory (char *);
426 static int F_LOCAL TestProcessNextExpression (int);
427 static int F_LOCAL TestANDExpression (int);
428 static int F_LOCAL TestPrimaryExpression (int);
429 static int F_LOCAL TestUnaryOperand (int);
430 static int F_LOCAL TestBinaryOperand (void);
431 static int F_LOCAL TestLexicalAnalysis (void);
432 static struct TestOperator *
433  F_LOCAL TestLookupOperator (char *);
434 static long F_LOCAL GetNumberForTest (char *);
435 static void F_LOCAL TestSyntaxError (void);
436 static void F_LOCAL SetVerifyStatus (bool);
437 static void F_LOCAL WhenceLocation (bool, char *, char *);
438 static void F_LOCAL WhenceType (char *);
439 static int F_LOCAL PrintOptionSettings (void);
440 static int F_LOCAL CheckFAccess (char *, int);
441 static int F_LOCAL CheckFType (char *, mode_t);
442 static int F_LOCAL CheckFMode (char *, mode_t);
443 static int F_LOCAL CheckFSize (char *);
444 static int F_LOCAL CheckForFD (char *);
445 
446 #if (OS_TYPE != OS_UNIX)
448 #endif
449 
450 #if (OS_TYPE == OS_DOS)
451 static void F_LOCAL SetBreakStatus (bool);
452 #else
453 # define SetBreakStatus(a)
454 #endif
455 
456 static bool F_LOCAL CheckPhysLogical (char *, bool *);
457 static char * F_LOCAL GetPhysicalPath (char *, bool);
458 static bool F_LOCAL ReadALine (int, char *, bool, bool, int *);
459 static bool F_LOCAL WriteOutLine (int);
460 static bool F_LOCAL ChangeOptionValue (char *, bool);
461 static void F_LOCAL SetClearFlag (int, bool);
462 static void F_LOCAL RemoveVariable (char *, int);
463 static int F_LOCAL BreakContinueProcessing (char *, int);
464 static int F_LOCAL SetUpNewParameterVariables (char **, int, int, char *);
465 static int F_LOCAL UsageError (char *);
466 static void F_LOCAL PrintEntry (VariableList *, bool, unsigned int);
467 static int F_LOCAL UpdateVariableStatus (char **, unsigned int);
468 static int F_LOCAL TypesetVariables (char **);
469 static int F_LOCAL ListAllVariables (unsigned int, bool);
470 static int F_LOCAL HandleFunction (char *);
471 static int F_LOCAL TestOptionValue (char *, bool);
472 static int F_LOCAL GetUnitNumber (char *);
473 static struct SetOptions * F_LOCAL LookUpOptionValue (char *);
474 static void DisplayVariableEntry (const void *, VISIT, int);
475 static struct TrapSignalList * F_LOCAL LookupSignalName (char *);
476 
477 #if (OS_TYPE != OS_DOS)
478 static bool F_LOCAL ConvertJobToPid (char *, PID *);
479 
480 # if (OS_TYPE == OS_OS2)
481 static void F_LOCAL DisplayStartData (STARTDATA *);
482 # endif
483 
484 # if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32)
485 APIRET DosQFileMode (PSZ, PULONG);
486 
487 # if !defined (__EMX__)
488 # define Dos32FlagProcess DosFlagProcess
489 # pragma linkage (DosFlagProcess, far16 pascal)
490 # else
491 USHORT _THUNK_FUNCTION (Dos16FlagProcess) ();
492 # endif
493 
494 extern USHORT Dos32FlagProcess (PID, USHORT, USHORT, USHORT);
495 
496 # endif
497 
498 # if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_16)
499 # define Dos32FlagProcess DosFlagProcess
500 # endif
501 
502 # if (OS_TYPE == OS_NT)
503 int DosQFileMode (char *, DWORD *);
504 # endif
505 #endif
506 
507 /*
508  * Builtin Commands
509  */
510 
511 static int doexport (int, char **);
512 static int doreadonly (int, char **);
513 static int domsdos (int, char **);
514 static int dotypeset (int, char **);
515 static int dounalias (int, char **);
516 static int doalias (int, char **);
517 static int dolabel (int, char **);
518 static int dofalse (int, char **);
519 static int dochdir (int, char **);
520 static int dodrive (int, char **);
521 static int doshift (int, char **);
522 static int doumask (int, char **);
523 static int dodot (int, char **);
524 static int doecho (int, char **);
525 static int dolet (int, char **);
526 static int doshellinfo (int, char **);
527 
528 #if (OS_TYPE == OS_OS2)
529 static int dostart (int, char **);
530 #endif
531 
532 #if (OS_TYPE != OS_DOS)
533 static int dodetach (int, char **);
534 static int dokill (int, char **);
535 static int dojobs (int, char **);
536 static int dowait (int, char **);
537 #endif
538 
539 #if defined (FLAGS_EMACS) || defined (FLAGS_GMACS)
540 static int dobind (int, char **);
541 #endif
542 
543 #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) && !defined (__WATCOMC__)
544 static int dotimes (int, char **);
545 #endif
546 
547 #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_UNIX)
548 static int dotimes (int, char **);
549 #endif
550 
551 static int dogetopts (int, char **);
552 static int dopwd (int, char **);
553 static int doswap (int, char **);
554 static int dounset (int, char **);
555 static int dowhence (int, char **);
556 static int dofc (int, char **);
557 static int dotest (int, char **);
558 static int dover (int, char **);
559 static int doread (int, char **);
560 static int doeval (int, char **);
561 static int dotrap (int, char **);
562 static int dobuiltin (int, char **);
563 static int dobreak (int, char **);
564 static int docontinue (int, char **);
565 static int doexit (int, char **);
566 static int doreturn (int, char **);
567 static int doset (int, char **);
568 static int dofunctions (int, char **);
569 static int dohistory (int, char **);
570 
571 /*
572  * TWALK global values for DisplayVariable
573  */
574 
575 static unsigned int DVE_Mask;
576 static bool DVE_PrintValue;
577 
578 /*
579  * Local data structure for test command
580  */
581 
582 static char **TestArgumentList;
584 static jmp_buf TestErrorReturn;
585 static char *TestProgram;
586 static bool NewTestProgram;
587 
588 /*
589  * Static structure for typeset
590  */
591 
592 static struct TypesetValues {
593  unsigned int Flags_On;
594  unsigned int Flags_Off;
595  int Base;
596  int Width;
598 
599 /*
600  * Current position in Getoption string
601  */
602 
603 static int GetOptionPosition = 1; /* Current position */
604 static int BadOptionValue = 0; /* The bad option value */
605 
606 /*
607  * Common strings
608  */
609 
610 static char *DoubleHypen = "--";
611 static char *TypeSetUsage = "typeset [ [ [+|-][Hflprtux] ] [+|-][LRZi[n]] [ name [=value] ...]";
612 static char *NotBuiltinCommand = "not a builtin";
613 static char *NotAnAlias = "%s: %s is not an alias";
614 static char *NotValidAlias = "Invalid alias name";
615 static char *Reply_Array[] = {LIT_REPLY, (char *)NULL};
616 static char *BadDrive = "%c: bad drive";
617 static char *ShellInternalCommand = "is a shell internal command";
618 static char *FCTooLong = "fc: command line too long";
619 static char LIT_alias[] = "alias";
620 static char LIT_print[] = "print";
621 static char LIT_read[] = "read";
622 static char LIT_shift[] = "shift";
623 static char LIT_break[] = "break";
624 static char LIT_builtin[] = "builtin";
625 static char LIT_devfd[] = "/dev/fd/";
626 static char *BuiltinUsage = "builtin [ -ads ] [ commands ]";
627 static char *WhenceUsage = "whence [ -pvt ] [ commands ]";
628 static char LIT_continue[] = "continue";
629 static char LIT_type[] = "type";
630 static char LIT_unalias[] = "unalias";
631 static char LIT_unfunction[] = "unfunction";
632 static char *LIT_bun = "bad unit number";
633 static char *HistoryUsage = "history [ -iedsl ] [ number ]";
634 static char *ReturnUsage = "return [ value ]";
635 
636 #if (OS_TYPE == OS_OS2)
637 static char *StartUsage =
638 "start -O [dos | pm] [ -hHfWPFxibID ] [ -c [ vilsna ]] [ -t title ]\n [ -e string ]\n start [ -dfWPFibCISxhH ] [ -c [ vilsna ]] [ -t title ] [ -e string ]\n [ -X directory ] [ args.. ]\n start -A sessionId";
639 static char *Start_NoSession = "start: Cannot switch to session %lu\n%s";
640 #endif
641 
642 #if (OS_TYPE != OS_DOS)
643 static char *JobUsage = "jobs [-lp] [ -P pid]";
644 static char *KillUsage = "kill [ [-l] | [ [-sig] [ process id | %job number ] ... ] ]";
645 #endif
646 
647 /*
648  * Disable variables mapping
649  */
650 
652  char *name;
653  int flag;
654 } DisableVariableMap [] = {
662 
663 #if (OS_TYPE != OS_DOS)
664  { WinTitleVariable, DISABLE_WINTITLE },
665 #endif
666  { (char *)NULL, 0 },
667 };
668 
669 /*
670  * built-in commands: : and true
671  */
672 
673 static int dolabel (int argc, char **argv)
674 {
675  return 0;
676 }
677 
678 static int dofalse (int argc, char **argv)
679 {
680  return 1;
681 }
682 
683 /*
684  * Getopts - split arguments. getopts optstring name [ arg ... ]
685  */
686 
687 static int dogetopts (int argc, char **argv)
688 {
689  char **Arguments;
690  char *OptionString;
691  int result;
692  char SResult[3];
693  char BadResult[2];
694  int Mode = GETOPT_MESSAGE | GETOPT_PLUS;
695 
696  if (argc < 3)
697  return UsageError ("getopts optstring name [ arg ... ]");
698 
699  memset (SResult, 0, 3);
700 
701 /*
702  * A leading : in optstring causes getopts to store the letter of an
703  * invalid option in OPTARG, and to set name to ? for an unknown option and
704  * to : when a required option is missing.
705  */
706 
707  if (*(OptionString = argv[1]) == ':')
708  {
709  OptionString++;
710  Mode = GETOPT_PLUS;
711  }
712 
713 /*
714  * Use positional parameters
715  */
716 
717  if (argc == 3)
718  {
719  argc = ParameterCount + 1;
720  Arguments = ParameterArray;
721  }
722 
723 /* No - use supplied */
724 
725  else
726  {
727  Arguments = &argv[2]; /* Adjust pointers */
728  argc -= 2;
729  }
730 
731 /*
732  * Get the value of OPTIND and initialise the getopt function
733  */
734 
737 
738  else
740 
741 /* Initialise the other values */
742 
744  OptionArgument = (char *)NULL;
745 
746  result = GetOptions (argc, Arguments, OptionString, Mode);
747 
748 /* Save new positions */
749 
751 
752 /* Check for EOF */
753 
754  if (result == EOF)
755  return 1;
756 
757 /* Set up result string */
758 
759  *SResult = (char)result;
760 
761 /* Did we get an error. Yes. If message output, don't put value
762  * in OPTARG
763  */
764 
765  if (result == '?')
766  {
767  if (Mode & GETOPT_MESSAGE)
768  OptionArgument = (char *)NULL;
769 
770 /* Error, set up values in optarg and the result */
771 
772  else
773  {
774  SResult[0] = (char)((OptionArgument == (char *)NULL) ? '?' : ':');
775  *(OptionArgument = BadResult) = (char)BadOptionValue;
776  *(OptionArgument + 1) = 0;
777  }
778  }
779 
780 /* If the argument started with a +, tell them */
781 
782  else if (OptionStart == '+')
783  {
784  SResult[1] = (char)result;
785  SResult[0] = '+';
786  }
787 
788 /* If we got an argument, set the various shell variables */
789 
790  if ((OptionArgument != (char *)NULL) &&
793 
794  SetVariableFromString (argv[2], SResult);
795  return 0;
796 }
797 
798 /*
799  * Reset the getopts values
800  */
801 
802 void ResetGetoptsValues (bool Variable)
803 {
804  if (Variable && (!(DisabledVariables & DISABLE_OPTIND)))
806 
807  GetOptsIndex.Index = 1;
809 }
810 
811 /*
812  * Save the new Getopts values
813  */
814 
815 void SaveGetoptsValues (int Index, int Position)
816 {
818  SetVariableFromNumeric (OptIndVariable, (long)Index);
819 
820  GetOptsIndex.Index = Index;
821  GetOptsIndex.SubIndex = Position;
822 }
823 
824 /*
825  * Get the current Getopts values
826  */
827 
829 {
830  values->Index = GetOptsIndex.Index;
831  values->SubIndex = GetOptsIndex.SubIndex;
832 }
833 
834 /*
835  * Echo the parameters: echo [ -n ] parameters
836  */
837 
838 static int doecho (int argc, char **argv)
839 {
840  int flags = ECHO_ESCAPE;
841  int fid = 1;
842  char *ip; /* Input pointer */
843  char *op;
844  int c, c1;
845  int R_flag = GETOPT_PRINT; /* Enable -n test */
846 
847  ResetGetOptions (); /* Reset GetOptions */
848 
849 /* Echo or print? */
850 
851  if (strcmp (*argv, LIT_print) == 0)
852  {
853  R_flag = 0; /* Reset */
854 
855  while ((c = GetOptions (argc, argv, "Rnprsu:", R_flag)) != EOF)
856  {
857  switch (c)
858  {
859  case 'R':
860  R_flag = GETOPT_PRINT;
861  flags &= ~~ECHO_ESCAPE;
862  break;
863 
864  case 'n':
865  flags = ECHO_NO_EOL;
866  break;
867 
868  case 'r':
869  flags &= ~~ECHO_ESCAPE;
870  break;
871 
872  case 's':
873  flags |= ECHO_HISTORY;
874  break;
875 
876  case 'p':
877  break;
878 
879  case 'u':
880  if ((fid = GetUnitNumber (LIT_print)) == -1)
881  return 1;
882 
883  break;
884 
885  default:
886  return UsageError ("print [ -Rpnrsu[unit] ] ...");
887  }
888  }
889  }
890 
891  if ((OptionIndex < argc) && (R_flag == GETOPT_PRINT) &&
892  (!strcmp (argv[OptionIndex], "-n")))
893  {
894  flags |= ECHO_NO_EOL;
895  ++OptionIndex;
896  }
897 
898  argv += OptionIndex;
899 
900 /* Clear the history buffer so we can use it */
901 
904 
905 /* Process the arguments. Process \ as a special if necessary */
906 
907  while ((ip = *(argv++)) != NOWORD)
908  {
909 
910 /* Process the character */
911 
912  while ((c = (int)(*(ip++))))
913  {
914 
915 /* If echo too big - disable history save */
916 
917  if ((op - ConsoleLineBuffer) > LINE_MAX - 4)
918  {
919  *op = 0;
920 
921  if (!WriteOutLine (fid))
922  return 1;
923 
925 
926  if (flags & ECHO_HISTORY)
927  fprintf (stderr, BasicErrorMessage,
928  "Line too long for history", LIT_print);
929 
930  flags &= ~~ECHO_HISTORY;
931  }
932 
933  if ((flags & ECHO_ESCAPE) && (c == CHAR_META))
934  {
935  c1 = *ip;
936 
937  if ((c = ProcessOutputMetaCharacters (&ip)) == -1)
938  {
939  flags |= ECHO_NO_EOL;
940  continue;
941  }
942 
943 /* If unchanged - output backslash */
944 
945  else if ((c == c1) && (c != CHAR_META))
946  *(op++) = CHAR_META;
947 
948  if (c) /* bug fix */
949  *(op++) = (char)c; /* bug fix */
950  }
951 
952  else /* bug fix */
953  *(op++) = (char)c;
954  }
955 
956 /* End of string - check to see if a space if required */
957 
958  if (*argv != NOWORD)
959  *(op++) = CHAR_SPACE;
960  }
961 
962 /* Is EOL required ? */
963 
964  if (!(flags & ECHO_NO_EOL))
965  {
966 #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32)
967  if (IS_Console (fid))
968  *(op++) = CHAR_RETURN;
969 #endif
970  *(op++) = CHAR_NEW_LINE;
971  }
972 
973  *op = 0;
974 
975  if (!WriteOutLine (fid))
976  return 1;
977 
978 /* Save history */
979 
980  if (flags & ECHO_HISTORY)
981  {
983  AddHistory (FALSE);
984  }
985 
986  return 0;
987 }
988 
989 /*
990  * Write out the current line
991  */
992 
993 static bool F_LOCAL WriteOutLine (int fid)
994 {
995  size_t Len = strlen (ConsoleLineBuffer);
996 
997  if (write (fid, ConsoleLineBuffer, Len) != (int)Len)
998  {
999  PrintWarningMessage ("print: write error on unit %d\n", fid);
1000  return FALSE;
1001  }
1002 
1003  return TRUE;
1004 
1005 }
1006 
1007 /*
1008  * Display the current version: ver
1009  */
1010 
1011 static int dover (int argc, char **argv)
1012 {
1014  return 0;
1015 }
1016 
1017 #ifdef OS_SWAPPING
1018 static char *swap_device[] = {"disk", "extend", "expand"};
1019 #endif
1020 
1021 /*
1022  * Modify swapping information: swap [ options ]
1023  */
1024 
1025 static int doswap (int argc, char **argv)
1026 {
1027 #if (OS_TYPE != OS_DOS)
1028  printf ("Swapping not available on %s", LIT_OSname);
1029 #elif !defined (OS_SWAPPING)
1030  puts ("Swapping not available in 32-bit mode");
1031 #else
1032  int n = 1;
1033  char *cp;
1034 
1035 /* Display current values ? */
1036 
1037  if (argv[1] == NOWORD)
1038  {
1039  if (Swap_Mode == SWAP_OFF)
1040  puts ("Swapping disabled");
1041 
1042  else
1043  {
1044  int j;
1045 
1046  foputs ("Swap devices: ");
1047 
1048  for (j = 0, n = 1; j < 3; ++j, n <<= 1)
1049  {
1050  if (Swap_Mode & n)
1051  {
1052  printf ("%s ", swap_device[j]);
1053 
1054  if (n == SWAP_EXTEND)
1055  printf ("(0x%.6lx) ", SW_EMstart);
1056  }
1057  }
1058 
1059  fputchar (CHAR_NEW_LINE);
1060  }
1061 
1062  return 0;
1063  }
1064 
1065 /* Set up new values */
1066 
1067  Swap_Mode = SWAP_OFF;
1068  ClearSwapFile ();
1069 
1070  while ((cp = argv[n++]) != NOWORD)
1071  {
1072  if (strcmp (cp, "off") == 0)
1073  Swap_Mode = SWAP_OFF;
1074 
1075  else if (strcmp (cp, "on") == 0)
1077 
1078 /* Scan for valid arguments */
1079 
1080  else
1081  {
1082  int j, k;
1083 
1084  for (j = 0, k = 1; j < 3; ++j, k <<= 1)
1085  {
1086  if (strcmp (cp, swap_device[j]) == 0)
1087  {
1088  Swap_Mode |= k;
1089 
1090 /* If extended memory, they can specify the start address as a hex number */
1091 
1092  if (k == SWAP_EXTEND)
1093  {
1094  char *sp;
1095  long start;
1096 
1097 /* Check for not changed */
1098 
1099  if ((sp = argv[n]) == NOWORD)
1100  break;
1101 
1102 /* Convert hex number */
1103 
1104  if (!ConvertNumericValue (sp, &start, 16))
1105  break;
1106 
1107 /* Set used and saved new value */
1108 
1109  SW_EMstart = start;
1110  ++n;
1111 
1112  if ((SW_EMstart < 0x100000L) ||
1113  (SW_EMstart > 0xf00000L))
1114  SW_EMstart = 0x100000L;
1115 
1116  printf ("Extend memory start set to 0x%.6lx\n",
1117  SW_EMstart);
1118  }
1119 
1120  break;
1121  }
1122  }
1123 
1124  if (j == 3)
1125  return UsageError ("swap [ on | off | disk | expand | extend [ address ] ] ...");
1126  }
1127  }
1128 
1129 #endif
1130  return 0;
1131 }
1132 
1133 /*
1134  * Output the current path: pwd [drives]
1135  */
1136 
1137 static int dopwd (int argc, char **argv)
1138 {
1139  int i;
1140  int Start = 1;
1141  int RetVal = 0;
1142  char *sp;
1143  char ndrive;
1144  char ldir[PATH_MAX + 6];
1145  bool Physical = FALSE;
1146 
1147  if ((argc > 1) && CheckPhysLogical (argv[1], &Physical))
1148  Start++;
1149 
1150 /* Print the current directories on the selected drive */
1151 
1152  i = Start;
1153 
1154  while ((sp = argv[i++]) != NOWORD)
1155  {
1156 
1157 /* Select the drive and get the path */
1158 
1159  while ((ndrive = *(sp++)))
1160  {
1161  errno = 0;
1162 
1163  if (!S_getcwd (ldir, GetDriveNumber (ndrive)) || errno)
1164  RetVal = PrintWarningMessage (BadDrive, ndrive);
1165 
1166  else if (Physical)
1167  printf ("%c: path is %s\n", tolower (ndrive),
1168  GetPhysicalPath (ldir, TRUE));
1169 
1170  else
1171  puts (ldir);
1172  }
1173  }
1174 
1175 /* Print the current directory */
1176 
1177  if (argv[Start] == NOWORD)
1178  {
1179  strcpy (ldir, CurrentDirectory->value);
1180  puts ((Physical) ? GetPhysicalPath (ldir, TRUE) : ldir);
1181  }
1182 
1183  return RetVal;
1184 }
1185 
1186 /*
1187  * Unset a variable: unset [ flag ] [ variable name... ]
1188  * Delete a function: unfunction <names ...>
1189  */
1190 
1191 static int dounset (int argc, char **argv)
1192 {
1193  int n = 1;
1194  bool Fnc = FALSE;
1195  FunctionList *fp;
1196  char *cp;
1197  int i;
1198 
1199 /* -f, functions */
1200 
1201  if (strcmp (*argv, LIT_unfunction) == 0)
1202  Fnc = TRUE;
1203 
1204  else if ((argc > 1) && (strcmp (argv[1], "-f") == 0))
1205  {
1206  n++;
1207  Fnc = TRUE;
1208  }
1209 
1210 /* Unset the variables, flags or functions */
1211 
1212  while ((cp = argv[n++]) != NOWORD)
1213  {
1214  if (!Fnc)
1215  {
1216  UnSetVariable (cp, -1, FALSE);
1217 
1218  for (i = 0; DisableVariableMap[i].name != (char *)NULL; i++)
1219  {
1220  if (strcmp (DisableVariableMap[i].name, cp) == 0)
1221  {
1223  break;
1224  }
1225  }
1226  }
1227 
1228  else if ((fp = LookUpFunction (cp, TRUE)) != (FunctionList *)NULL)
1229  DeleteFunction (fp->tree);
1230  }
1231 
1232  return 0;
1233 }
1234 
1235 /* Delete a variable. If all is set, system variables can be deleted.
1236  * This is used to delete the trap functions
1237  */
1238 
1239 void UnSetVariable (char *cp, /* Variable name */
1240  int OIndex, /* Index preprocessed value */
1241  bool all) /* Any variable */
1242 {
1243  long Index;
1244  char *term;
1245  bool ArrayDetected;
1246 
1247 /* Unset a flag */
1248 
1249  if (*cp == '-')
1250  {
1251  while (*(++cp) != 0)
1252  {
1253  if (islower (*cp))
1254  FL_CLEAR (*cp);
1255  }
1256 
1257  SetShellSwitches ();
1258  return;
1259  }
1260 
1261 /* Ok - unset a variable and not a local value */
1262 
1263  if (!all && !(IS_VariableFC ((int)*cp)))
1264  return;
1265 
1266 /* Check in list */
1267 
1268  if (!GetVariableName (cp, &Index, &term, &ArrayDetected) || *term)
1269  return;
1270 
1271  if (OIndex != -1)
1272  Index = OIndex;
1273 
1274 /* Delete a specific entry or all? */
1275 
1276  if (!((ArrayDetected || (OIndex != -1))))
1277  Index = -1;
1278 
1279  RemoveVariable (cp, (int)Index);
1280 }
1281 
1282 /*
1283  * Delete a variable
1284  *
1285  * Index = -1 implies all references
1286  */
1287 
1288 static void F_LOCAL RemoveVariable (char *name, /* Variable name */
1289  int Index) /* Array index */
1290 {
1291  VariableList **vpp;
1292  VariableList *vp;
1294  void (*save_signal)(int);
1295 
1296  while (TRUE)
1297  {
1298  dummy.name = name;
1299  dummy.index = (int)Index;
1300 
1301  vpp = (VariableList **)tfind (&dummy, &VariableTree,
1303 
1304 /* If not found, Ignore unset request */
1305 
1306  if (vpp == (VariableList **)NULL)
1307  return;
1308 
1309 /* Error if read-only */
1310 
1311  vp = *vpp;
1312 
1314  {
1315  PrintWarningMessage ("unset: %s %s", vp->name,
1316  (vp->status & STATUS_CANNOT_UNSET)
1317  ? "cannot be unset" : LIT_IsReadonly);
1318  return;
1319  }
1320 
1321 /* Disable signals */
1322 
1323  save_signal = signal (SIGINT, SIG_IGN);
1324 
1325 /* Delete it */
1326 
1327  dummy.index = vp->index;
1329  ReleaseMemoryCell ((void *)vp->name);
1330 
1331  if (vp->value != null)
1332  ReleaseMemoryCell ((void *)vp->value);
1333 
1334  ReleaseMemoryCell ((void *)vp);
1335 
1336 /* Restore signals */
1337 
1338  signal (SIGINT, save_signal);
1339  }
1340 }
1341 
1342 /*
1343  * TFIND - Search the VARIABLE TREE for an entry to delete
1344  */
1345 
1346 static int DeleteAllVariables (const void *key1, const void *key2)
1347 {
1348  int diff;
1349 
1350  if ((diff = strcmp (((VariableList *)key1)->name,
1351  ((VariableList *)key2)->name)) != 0)
1352  return diff;
1353 
1354  if (((VariableList *)key1)->index == -1)
1355  return 0;
1356 
1357  return ((VariableList *)key1)->index - ((VariableList *)key2)->index;
1358 }
1359 
1360 /*
1361  * Execute a test: test <arguments>
1362  */
1363 
1364 static int dotest (int argc, char **argv)
1365 {
1366  int st = 0;
1367  char *End;
1368 
1370 
1371 /* Note that -a and -o change meaning if [[ ... ]] is used */
1372 
1373  if (NewTestProgram)
1374  {
1375  End = "]]";
1380  }
1381 
1382  else
1383  {
1384  End = "]";
1389  }
1390 
1391 /* Check out */
1392 
1394 
1395 /* If [ <arguments> ] or [[ <arguments> ]] form, check for end ] or ]] and
1396  * remove it
1397  */
1398 
1399  if (NewTestProgram || (strcmp (*argv, "[") == 0))
1400  {
1401  while (argv[++st] != NOWORD)
1402  ;
1403 
1404  if (strcmp (argv[--st], End) != 0)
1405  return PrintWarningMessage ("%s: missing '%s'", TestProgram, End);
1406 
1407  else
1408  argv[st] = NOWORD;
1409  }
1410 
1411 /* Check for null expression */
1412 
1413  if (*(TestArgumentList = &argv[1]) == NOWORD)
1414  return 1;
1415 
1416 /* Set abort address */
1417 
1418  if (setjmp (TestErrorReturn))
1419  return 1;
1420 
1422 
1423  if (*(TestArgumentList + 1) != NOWORD)
1424  TestSyntaxError ();
1425 
1426  return (st);
1427 }
1428 
1429 /*
1430  * Process next test expression
1431  */
1432 
1434 {
1435  int res;
1436 
1437  if (n == END_OF_INPUT)
1438  TestSyntaxError ();
1439 
1440  res = TestANDExpression (n);
1441 
1442  TestArgumentList++;
1443 
1444  if (TestLexicalAnalysis () == BINARY_OR)
1445  {
1446  TestArgumentList++;
1447 
1449  }
1450 
1451  TestArgumentList--;
1452  return res;
1453 }
1454 
1455 /*
1456  * Binary expression ( a AND b)
1457  */
1458 
1459 static int F_LOCAL TestANDExpression (int n)
1460 {
1461  int res;
1462 
1463  if (n == END_OF_INPUT)
1464  TestSyntaxError ();
1465 
1467  TestArgumentList++;
1468 
1469  if (TestLexicalAnalysis () == BINARY_AND)
1470  {
1471  TestArgumentList++;
1472  return TestANDExpression (TestLexicalAnalysis ()) && res;
1473  }
1474 
1475  TestArgumentList--;
1476  return res;
1477 }
1478 
1479 /*
1480  * Handle Primary expression
1481  */
1482 
1484 {
1485  int res;
1486 
1487  switch (n)
1488  {
1489  case END_OF_INPUT:
1490  TestSyntaxError ();
1491 
1492  case UNARY_NOT:
1493  TestArgumentList++;
1495 
1496  case LPAREN:
1497  TestArgumentList++;
1499 
1500  TestArgumentList++;
1501  if (TestLexicalAnalysis () != RPAREN)
1502  TestSyntaxError ();
1503 
1504  return res;
1505 
1506 /* Operand */
1507 
1508  case OPERAND:
1509  return TestBinaryOperand ();
1510 
1511 /* unary expression */
1512 
1513  default:
1515  (*++TestArgumentList == 0))
1516  TestSyntaxError ();
1517 
1518  return TestUnaryOperand (n);
1519  }
1520 }
1521 
1522 /*
1523  * Handle a Binary Operand
1524  */
1525 
1526 static int F_LOCAL TestBinaryOperand (void)
1527 {
1528  char *opnd1, *opnd2;
1529  struct _stat s, s1;
1530  short op;
1531 
1532  opnd1 = *(TestArgumentList++);
1534 
1535  if ((CurrentTestOperator != (struct TestOperator *)NULL) &&
1537  {
1539 
1540  TestArgumentList++;
1541 
1542  if ((opnd2 = *TestArgumentList) == NOWORD)
1543  TestSyntaxError ();
1544 
1545  switch (op)
1546  {
1547 
1548 /* String lengths */
1549 
1550  case STRING_EQUAL:
1551  return strcmp (opnd1, opnd2) == 0;
1552 
1553  case STRING_NOTEQUAL:
1554  return strcmp (opnd1, opnd2) != 0;
1555 
1556  case STRING_LESS:
1557  return strcmp (opnd1, opnd2) < 0;
1558 
1559  case STRING_GREATER:
1560  return strcmp (opnd1, opnd2) > 0;
1561 
1562 /* Numeric comparisions */
1563 
1564  case NUMBER_EQUAL:
1565  return GetNumberForTest (opnd1) == GetNumberForTest (opnd2);
1566 
1567  case NUMBER_NOTEQUAL:
1568  return GetNumberForTest (opnd1) != GetNumberForTest (opnd2);
1569 
1570  case NUMBER_EQ_GREAT:
1571  return GetNumberForTest (opnd1) >= GetNumberForTest (opnd2);
1572 
1573  case NUMBER_GREATER:
1574  return GetNumberForTest (opnd1) > GetNumberForTest (opnd2);
1575 
1576  case NUMBER_EQ_LESS:
1577  return GetNumberForTest (opnd1) <= GetNumberForTest (opnd2);
1578 
1579  case NUMBER_LESS:
1580  return GetNumberForTest (opnd1) < GetNumberForTest (opnd2);
1581 
1582 /* Older and Newer - if file not found - set to current time */
1583 
1584  case FILE_NEWER:
1585  case FILE_OLDER:
1586  if (!S_stat (opnd1, &s))
1587  return 0;
1588 
1589  if (!S_stat (opnd2, &s1))
1590  s1.st_mtime = 0L;
1591 
1592  return (op == FILE_NEWER) ? (s.st_mtime > s1.st_mtime)
1593  : (s.st_mtime < s1.st_mtime);
1594 
1595 /*
1596  * Equals - difficult on DOS. So just do want UNIX says, but first compare
1597  * the absolute path names
1598  */
1599 
1600  case FILE_EQUAL:
1601  {
1602 #if (OS_TYPE != OS_UNIX)
1603  char *a_opnd1;
1604  char *a_opnd2;
1605  int res;
1606 #endif
1607 
1608  if ((!S_stat (opnd1, &s)) || (!S_stat (opnd2, &s1)))
1609  return 0;
1610 
1611 #if (OS_TYPE == OS_UNIX)
1612  return ((s.st_dev == s1.st_dev) && (s.st_ino == s1.st_ino));
1613 #else
1614 
1615  a_opnd1 = AllocateMemoryCell (FFNAME_MAX);
1616  a_opnd2 = AllocateMemoryCell (FFNAME_MAX);
1617 
1618  if ((a_opnd1 == (char *)NULL) || (a_opnd1 == (char *)NULL))
1619  {
1621  return 0;
1622  }
1623 
1624  GenerateFullExecutablePath (strcpy (a_opnd1, opnd1));
1625  GenerateFullExecutablePath (strcpy (a_opnd2, opnd2));
1626 
1627 /* If this is OS/2, we need to decide if to check for HPFS */
1628 
1629 # if (OS_TYPE != OS_DOS)
1630  res = ((!IsHPFSFileSystem (a_opnd1)) ||
1632  ? stricmp (a_opnd1, a_opnd2)
1633  : strcmp (a_opnd1, a_opnd2);
1634 # else
1635  res = stricmp (a_opnd1, a_opnd2);
1636 # endif
1637 
1638  ReleaseMemoryCell (a_opnd1);
1639  ReleaseMemoryCell (a_opnd2);
1640 
1641  return (res == 0) ? 1
1642  : ((s.st_dev == s1.st_dev) &&
1643  (s.st_ino == s1.st_ino));
1644 #endif
1645  }
1646  }
1647  }
1648 
1649  TestArgumentList--;
1650  return strlen (opnd1) != 0;
1651 }
1652 
1653 /*
1654  * Handle a Unary Operand
1655  */
1656 
1657 static int F_LOCAL TestUnaryOperand (int n)
1658 {
1659  switch (n)
1660  {
1661  case STRING_ZERO:
1662  return strlen (*TestArgumentList) == 0;
1663 
1664  case STRING_NONZERO:
1665  return strlen (*TestArgumentList) != 0;
1666 
1667  case TEST_OPTION:
1668  return TestOptionValue (*TestArgumentList, TRUE) != 0;
1669 
1670 /* File functions */
1671 
1672  case FILE_EXISTS:
1673  return CheckFAccess (*TestArgumentList, F_OK);
1674 
1675  case FILE_READABLE:
1676  return CheckFAccess (*TestArgumentList, R_OK);
1677 
1678  case FILE_WRITABLE:
1679  return CheckFAccess (*TestArgumentList, W_OK);
1680 
1681  case FILE_EXECUTABLE:
1682  return CheckFAccess (*TestArgumentList, X_OK);
1683 
1684  case FILE_REGULAR:
1685  return CheckFType (*TestArgumentList, S_IFREG);
1686 
1687  case FILE_DIRECTRY:
1688  return CheckFType (*TestArgumentList, S_IFDIR);
1689 
1690  case FILE_NONZERO:
1691  return CheckFSize (*TestArgumentList);
1692 
1693  case FILE_TERMINAL:
1694  return IS_TTY ((int)GetNumberForTest (*TestArgumentList));
1695 
1696 /* The following have no meaning on MSDOS or OS/2. So we always return
1697  * fail for compatability
1698  */
1699 
1700 #if (OS_TYPE == OS_UNIX)
1701  case FILE_USER:
1702  return CheckFMode (*TestArgumentList, S_ISUID);
1703 
1704  case FILE_GROUP:
1705  return CheckFMode (*TestArgumentList, S_ISGID);
1706 
1707  case FILE_TEXT:
1708  return CheckFMode (*TestArgumentList, S_ISVTX);
1709 #else
1710  case FILE_USER:
1712 
1713  case FILE_GROUP:
1715 
1716  case FILE_TEXT:
1718 #endif
1719 
1720  case FILE_BLOCK:
1721  return CheckFType (*TestArgumentList, S_IFBLK);
1722 
1723  case FILE_CHARACTER:
1724  return CheckFType (*TestArgumentList, S_IFCHR);
1725 
1726 #if (OS_TYPE == OS_UNIX)
1727  case FILE_FIFO:
1728  return CheckFType (*TestArgumentList, S_IFIFO);
1729 
1730  case FILE_SYMBOLIC:
1731  return CheckFType (*TestArgumentList, S_IFLNK);
1732 
1733  case FILE_SOCKET:
1734  return CheckFType (*TestArgumentList, S_IFSOCK);
1735 #else
1736  case FILE_FIFO:
1737  case FILE_SYMBOLIC:
1738  case FILE_SOCKET:
1739  return 0;
1740 #endif
1741 
1742 /* Under MSDOS and OS/2, we always own the file. Not necessarily true on
1743  * networked versions. But there is no common way of finding out
1744  */
1745 
1746 #if (OS_TYPE == OS_UNIX)
1747  case FILE_OWNER:
1748  return CheckFOwner (*TestArgumentList);
1749 
1750  case FILE_GROUPER:
1751  return CheckFGroup (*TestArgumentList);
1752 #else
1753  case FILE_OWNER:
1754  case FILE_GROUPER:
1755  return 1;
1756 #endif
1757 
1758  }
1759  return 0;
1760 }
1761 
1762 /* Operator or Operand ? */
1763 
1764 static int F_LOCAL TestLexicalAnalysis (void)
1765 {
1766  struct TestOperator *op;
1767  char *s = *TestArgumentList;
1768 
1769  if (s == NOWORD)
1770  return END_OF_INPUT;
1771 
1772 /* This is a real pain. If the current string is a unary operator and the
1773  * next string is a binary operator, assume the current string is a parameter
1774  * to the binary operator and not a unary operator.
1775  */
1776 
1778  != (struct TestOperator *)NULL)
1779  {
1780  s = *(TestArgumentList + 1);
1781 
1783  (s == (char *)NULL) ||
1784  ((op = TestLookupOperator (s)) == (struct TestOperator *)NULL) ||
1785  (op->OperatorType != BINARY_OP))
1787  }
1788 
1790  return OPERAND;
1791 }
1792 
1793 /*
1794  * Look up the string and test for operator
1795  */
1796 
1797 static struct TestOperator * F_LOCAL TestLookupOperator (char *s)
1798 {
1800 
1801  while (op->OperatorName)
1802  {
1803  if (strcmp (s, op->OperatorName) == 0)
1804  return op;
1805 
1806  op++;
1807  }
1808 
1809  return (struct TestOperator *)NULL;
1810 }
1811 
1812 /*
1813  * Get a long numeric value
1814  */
1815 
1816 static long F_LOCAL GetNumberForTest (char *s)
1817 {
1818  long l;
1819 
1820  if (!ConvertNumericValue (s, &l, 10))
1821  TestSyntaxError ();
1822 
1823  return l;
1824 }
1825 
1826 /*
1827  * test syntax error - abort
1828  */
1829 
1830 static void F_LOCAL TestSyntaxError (void)
1831 {
1833  longjmp (TestErrorReturn, 1);
1834 }
1835 
1836 /*
1837  * Select a new drive: x:
1838  *
1839  * Select the drive, get the current directory and check that we have
1840  * actually selected the drive
1841  */
1842 
1843 static int dodrive (int argc, char **argv)
1844 {
1845  unsigned int ndrive = GetDriveNumber (**argv);
1846  char ldir[PATH_MAX + 6];
1847  bool bad = FALSE;
1848 
1849  if (argc != 1)
1850  return UsageError ("drive:");
1851 
1852  if (SetCurrentDrive (ndrive) == -1)
1853  bad = TRUE;
1854 
1855  else if (!S_getcwd (ldir, ndrive))
1856  bad = TRUE;
1857 
1858  else
1859  {
1861 
1862  if (ndrive != GetCurrentDrive ())
1863  bad = TRUE;
1864  }
1865 
1866  if (bad)
1867  return PrintWarningMessage (BadDrive, **argv);
1868 
1869  else
1870  return 0;
1871 }
1872 
1873 /*
1874  * Check for -L or -P switch
1875  */
1876 
1877 static bool F_LOCAL CheckPhysLogical (char *string, bool *physical)
1878 {
1879  if (strcmp (string, "-L") == 0)
1880  {
1881  *physical = FALSE;
1882  return TRUE;
1883  }
1884 
1885  if (strcmp (string, "-P") == 0)
1886  {
1887  *physical = TRUE;
1888  return TRUE;
1889  }
1890 
1891  return FALSE;
1892 }
1893 
1894 /*
1895  * Select a new directory:
1896  *
1897  * cd [ directory ] Select new directory
1898  * cd - Select previous directory
1899  * cd <search string> <new string> Select directory based on current
1900  * cd Select Home directory
1901  * cd [-L | -P] Use Logical or physical mapping
1902  * sort of symbolic links
1903  */
1904 
1905 static int dochdir (int argc, char **argv)
1906 {
1907  char *NewDirectory; /* Original new directory */
1908  char *CNDirectory; /* New directory */
1909  char *cp; /* In CDPATH Pointer */
1910  char *directory;
1911  bool first = TRUE;
1912  unsigned int Length;
1913  unsigned int cdrive;
1914  bool Physical = FALSE;
1915  int Start = 1;
1916  int dcount;
1917 
1918 /* If restricted shell - illegal */
1919 
1920  if (CheckForRestrictedShell ("cd"))
1921  return 1;
1922 
1923  if ((argc > 1) && CheckPhysLogical (argv[1], &Physical))
1924  Start = 2;
1925 
1926  if (argc - Start > 2)
1927  return UsageError ("cd [ -P | -L ] [ directory | - | search replace ]");
1928 
1929 /* Use default ? */
1930 
1931  if (((NewDirectory = argv[Start]) == NOWORD) &&
1932  ((NewDirectory = GetVariableAsString (HomeVariableName,
1933  FALSE)) == null))
1934  return PrintWarningMessage ("cd: no home directory");
1935 
1936  if ((directory = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
1937  return doOutofMemory ("cd");
1938 
1939  if ((strcmp (NewDirectory, ShellOptionsVariable) == 0) &&
1940  ((NewDirectory = GetVariableAsString (OldPWDVariable, FALSE)) == null))
1941  return PrintWarningMessage ("cd: no previous directory");
1942 
1943 /* Check for substitue */
1944 
1945  if ((argv[Start] != NOWORD) && (argv[Start + 1] != NOWORD))
1946  {
1947  if ((cp = strstr (CurrentDirectory->value, argv[Start]))
1948  == (char *)NULL)
1949  return PrintWarningMessage ("cd: string not in pwd: %s",
1950  argv[Start]);
1951 
1952  if (strlen (CurrentDirectory->value) - strlen (argv[Start]) +
1953  strlen (argv[Start + 1]) >= (size_t)FFNAME_MAX)
1954  return PrintWarningMessage ("cd: new directory string too long: %s",
1955  argv[Start]);
1956 /* Do the substitution */
1957 
1959  strncpy (NewDirectory, CurrentDirectory->value, Length);
1960  strcpy (NewDirectory + Length, argv[Start + 1]);
1961  strcat (NewDirectory,
1962  CurrentDirectory->value + strlen (argv[Start]) + Length);
1963  }
1964 
1965 /* Save the current drive */
1966 
1967  cdrive = GetCurrentDrive ();
1968 
1969 /* Remove trailing / */
1970 
1971  Length = strlen (NewDirectory) - 1;
1972 
1973  if (IsPathCharacter (NewDirectory[Length]) &&
1974  (!((!Length) || ((Length == 2) && IsDriveCharacter (NewDirectory[1])))))
1975  NewDirectory[Length] = 0;
1976 
1977 /*
1978  * Special case for . and .., it seems
1979  */
1980 
1981  if ((strcmp (NewDirectory, CurrentDirLiteral) == 0) &&
1982  S_chdir (NewDirectory))
1983  {
1985  return 0;
1986  }
1987 
1988  if (strcmp (NewDirectory, ParentDirLiteral) == 0)
1989  {
1990  if (S_chdir (NewDirectory))
1991  {
1993  return 0;
1994  }
1995 
1996 /* If we are in the root directory, .. does not move you! */
1997 
1998  dcount = 0;
2000 
2001  while ((cp = strchr (cp, CHAR_UNIX_DIRECTORY)) != (char *)NULL)
2002  {
2003  cp++;
2004 
2005  if (++dcount == 2)
2006  break;
2007  }
2008 
2009  if (dcount == 1)
2010  {
2012  return 0;
2013  }
2014  }
2015 
2016 
2017 /* Scan for the directory. If there is not a / or : at start, use the
2018  * CDPATH variable
2019  */
2020 
2021  cp = (IsPathCharacter (*NewDirectory) ||
2022  IsDriveCharacter (*(NewDirectory + 1)))
2024 
2025  do
2026  {
2027  cp = BuildNextFullPathName (cp, NewDirectory, directory);
2028 
2029 /* Check for new disk drive */
2030 
2031  if (Physical && (GetPhysicalPath (directory, FALSE) == (char *)NULL))
2032  return doOutofMemory ("cd");
2033 
2034  CNDirectory = directory;
2035 
2036 /* Was the change successful? */
2037 
2038  if (GotoDirectory (CNDirectory, cdrive))
2039  {
2040 
2041 /* OK - reset the current directory (in the shell) and display the new
2042  * path if appropriate
2043  */
2044 
2046 
2047  if (!first)
2048  puts (CurrentDirectory->value);
2049 
2050  return 0;
2051  }
2052 
2053  first = FALSE;
2054 
2055  } while (cp != (char *)NULL);
2056 
2057 /* Restore our original drive and restore directory info */
2058 
2060 
2061  return PrintWarningMessage (BasicErrorMessage, NewDirectory,
2062  "bad directory");
2063 }
2064 
2065 /*
2066  * Execute a shift command: shift [ n ]
2067  */
2068 
2069 static int doshift (int argc, char **argv)
2070 {
2071  int n;
2072  char *Nvalue = argv[1];
2073 
2074  if (argc > 2)
2075  return UsageError ("shift [ count ]");
2076 
2077  n = (Nvalue != NOWORD) ? GetNumericValue (Nvalue) : 1;
2078 
2079  if (n < 0)
2080  return PrintWarningMessage (LIT_Emsg, LIT_shift, "bad shift value",
2081  Nvalue);
2082 
2083  if (ParameterCount < n)
2085  "nothing to shift");
2086 
2088  ParameterCount + 1, LIT_shift);
2089 }
2090 
2091 /*
2092  * Execute a umask command: umask [ n ]
2093  */
2094 
2095 static int doumask (int argc, char **argv)
2096 {
2097  int i;
2098  char *cp;
2099  long value;
2100 
2101  if (argc > 2)
2102  return UsageError ("umask [ new mask ]");
2103 
2104  if ((cp = argv[1]) == NOWORD)
2105  {
2106  umask ((i = umask (0)));
2107  printf ("%o\n", i);
2108  }
2109 
2110  else if (!ConvertNumericValue (cp, &value, 8))
2111  return PrintWarningMessage ("umask: bad mask (%s)", cp);
2112 
2113 #if (__OS2__)
2114  else if (umask ((int)value) == 0xffff)
2115  return PrintWarningMessage ("umask: bad value (%s)", cp);
2116 #else
2117  umask ((int)value);
2118 #endif
2119 
2120  return 0;
2121 }
2122 
2123 /*
2124  * Execute an exec command: exec [ arguments ]
2125  */
2126 
2127 int doexec (C_Op *t)
2128 {
2129  int i = 0;
2130  jmp_buf ex;
2131  ErrorPoint OldERP;
2132 
2133 /* Shift arguments */
2134 
2135  do
2136  {
2137  t->args[i] = t->args[i + 1];
2138  i++;
2139 
2140  } while (t->args[i] != NOWORD);
2141 
2142 /* Left the I/O as it is */
2143 
2144  if (i == 1)
2145  return RestoreStandardIO (0, FALSE);
2146 
2148  OldERP = e.ErrorReturnPoint;
2149 
2150 /* Set execute function recursive level to zero */
2151 
2152  Execute_stack_depth = 0;
2153  t->ioact = (IO_Actions **)NULL;
2154 
2155  if (SetErrorPoint (ex) == 0)
2157 
2158 /* Clear the extended file if an interrupt happened */
2159 
2161  e.ErrorReturnPoint = OldERP;
2163  return 1;
2164 }
2165 
2166 /*
2167  * Execute a script in the current shell: . <filename>
2168  */
2169 
2170 static int dodot (int argc, char **argv)
2171 {
2172  int i;
2173  char *sp;
2174  char *cp;
2175  char *l_path;
2176  Source *s;
2177  FILE *Fp;
2178 
2179  if ((cp = argv[1]) == NOWORD)
2180  return 0;
2181 
2182 /* Get some space */
2183 
2184  if ((l_path = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
2185  return doOutofMemory (".");
2186 
2187 /* Save the current drive */
2188 
2189  sp = ((FindPathCharacter (cp) != (char *)NULL) || IsDriveCharacter (*(cp + 1)))
2190  ? null
2192 
2193  do
2194  {
2195  sp = BuildNextFullPathName (sp, cp, l_path);
2196 
2197  if ((i = OpenForExecution (l_path, (char **)NULL, (int *)NULL)) >= 0)
2198  {
2199  if ((Fp = MyReOpenFile (ReMapIOHandler (i),
2200  sOpenReadMode)) == (FILE *)NULL)
2201  return PrintWarningMessage ("Cannot remap file");
2202 
2203  (s = pushs (SFILE))->u.file = Fp;
2204  s->file = cp;
2205 
2206  RunACommand (s, &argv[1]);
2207  S_fclose (Fp, TRUE);
2208 
2209  return (int)GetVariableAsNumeric (StatusVariable);
2210  }
2211 
2212  } while (sp != (char *)NULL);
2213 
2215 }
2216 
2217 /*
2218  * Read from standard input into a variable list
2219  *
2220  * read [-prs] [-u unit] [ variable list ]
2221  */
2222 
2223 static int doread (int argc, char **argv)
2224 {
2225  char *cp, *op;
2226  int i;
2227  int Unit = STDIN_FILENO;
2228  bool EndOfInputDetected = FALSE;
2229  int PreviousCharacter = 0;
2230  bool SaveMode = FALSE;
2231  bool RawMode = FALSE;
2232  char *Prompt = (char *)NULL;
2233  char *NewBuffer;
2234  int eofc;
2235 
2236  if ((NewBuffer = AllocateMemoryCell (LINE_MAX + 2)) == (char *)NULL)
2237  return doOutofMemory (LIT_read);
2238 
2239 /* Check for variable name. If not defined, use $REPLY */
2240 
2241  ResetGetOptions (); /* Reset GetOptions */
2242 
2243  while ((i = GetOptions (argc, argv, "prsu:", 0)) != EOF)
2244  {
2245  switch (i)
2246  {
2247  case 'p': /* Clean up process */
2248  break;
2249 
2250  case 'r': /* Raw Mode */
2251  RawMode = TRUE;
2252  break;
2253 
2254  case 's': /* Save a command */
2255  SaveMode = TRUE;
2256  break;
2257 
2258  case 'u': /* Specify input unit */
2259  if ((Unit = GetUnitNumber (LIT_read)) == -1)
2260  return 2;
2261 
2262  default:
2263  return UsageError ("read [ -prs ] [ -u unit ] [ name?prompt ] [ name ... ]");
2264  }
2265  }
2266 
2267  argv += OptionIndex;
2268  argc -= OptionIndex;
2269 
2270  if (!argc)
2271  argv = Reply_Array;
2272 
2273 /* Get the prompt and write it */
2274 
2275  LastUserPrompt = null;
2276 
2277  if ((Prompt = strchr (argv[0], '?')) != (char *)NULL)
2278  {
2279  *(Prompt++) = 0;
2280  LastUserPrompt1 = Prompt;
2281  LastUserPrompt = (char *)NULL;
2282  }
2283 
2284 /* Check we have a name */
2285 
2286  if (!strlen (argv[0]))
2287  argv = Reply_Array;
2288 
2289 /* Check for valid names */
2290 
2291  for (i = 0; argv[i] != (char *)NULL; i++)
2292  {
2293  if (IsValidVariableName (argv[i]))
2294  {
2296  return 1;
2297  }
2298  }
2299 
2300 /* Clear the history buffer */
2301 
2302  FlushHistoryBuffer ();
2303 
2304 /* Read the line from the device */
2305 
2306  if (ReadALine (Unit, Prompt, SaveMode, FALSE, &eofc))
2307  return 1;
2308 
2310 
2311 /* For each variable, read the data until a white space is detected */
2312 
2313  while (*argv != NOWORD)
2314  {
2315 
2316 /* Read in until end of line, file or a field separator is detected */
2317 
2318  op = (char *)NULL;
2319 
2320  while (!EndOfInputDetected && *cp)
2321  {
2322 
2323 /* End of file */
2324 
2325  if (*cp == (char)eofc)
2326  return 1;
2327 
2328 /* Check for Newline or IFS character */
2329 
2330  if ((*cp == CHAR_NEW_LINE) ||
2331  ((argv[1] != NOWORD) &&
2333  *cp) != (char *)NULL))
2334  {
2335  if (*cp != CHAR_NEW_LINE)
2336  ;
2337 
2338  else if ((PreviousCharacter != CHAR_META) || (RawMode))
2339  EndOfInputDetected = TRUE;
2340 
2341 /* Handle continuation line */
2342 
2343  else if (ReadALine (Unit, Prompt, SaveMode, TRUE, &eofc))
2344  return 1;
2345 
2346  else
2347  {
2349  PreviousCharacter = 0;
2350  continue;
2351  }
2352 
2353  break;
2354  }
2355 
2356 /* Save the current character */
2357 
2358  if (op == (char *)NULL)
2359  op = NewBuffer;
2360 
2361  *(op++) = *cp;
2362  PreviousCharacter = *(cp++);
2363  }
2364 
2365 /* Skip over terminating character */
2366 
2367  if (*cp)
2368  ++cp;
2369 
2370 /* Check for null string */
2371 
2372  if (op == (char *)NULL)
2373  op = null;
2374 
2375 /* Terminate the string */
2376 
2377  else
2378  {
2379  *op = 0;
2380 
2381  if (!strlen (NewBuffer))
2382  continue;
2383 
2384  else
2385  op = NewBuffer;
2386  }
2387 
2388 /* Save the string value */
2389 
2390  SetVariableFromString (*(argv++), op);
2391  }
2392 
2393  ReleaseMemoryCell ((void *)NewBuffer);
2394 
2395  return 0;
2396 }
2397 
2398 /*
2399  * Read a line from either the console or a file for the read command.
2400  * The line is returned in the ConsoleLineBuffer.
2401  */
2402 
2403 static bool F_LOCAL ReadALine (int Unit, /* Unit to read from */
2404  char *Prompt, /* User prompt */
2405  bool SaveMode, /* Save in history */
2406  bool Append, /* Append to history */
2407  int *eofc) /* EOF character */
2408 {
2409  int NumberBytesRead;
2410  char *cp;
2411  int x;
2412 
2413 /* Generate the prompt */
2414 
2415  if ((Prompt != (char *)NULL) && (!IS_Console (Unit)) &&
2416  (!IS_TTY (Unit) || (write (Unit, Prompt, strlen (Prompt)) == -1)))
2417  feputs (Prompt);
2418 
2419 /* Read the line */
2420 
2421  *eofc = 0x1a;
2422 
2423  if (IS_Console (Unit))
2424  {
2425  *eofc = GetEOFKey ();
2426 
2427  if (!GetConsoleInput ()) /* get input */
2429  }
2430 
2431  else
2432  {
2433  NumberBytesRead = 0;
2435 
2436  while (NumberBytesRead++ < LINE_MAX)
2437  {
2438  if ((x = read (Unit, cp, 1)) == -1)
2439  return TRUE;
2440 
2441 /* EOF detected as first character on line */
2442 
2443  if ((NumberBytesRead == 1) && ((!x) || (*cp == (char)*eofc)))
2444  return TRUE;
2445 
2446 /* End read if NEWLINE or EOF character detected */
2447 
2448  if ((!x) || (*cp == CHAR_NEW_LINE) || (*cp == (char)*eofc))
2449  break;
2450 
2451  cp++;
2452  }
2453 
2454 /* Terminate the line the same in all cases */
2455 
2456  *(cp++) = CHAR_NEW_LINE;
2457  *cp = 0;
2458  }
2459 
2460 /* Save the history. Clean it up first */
2461 
2462  if (SaveMode)
2463  {
2464  char save;
2465 
2467  *eofc);
2468  AddHistory (Append);
2470  }
2471 
2472  return FALSE;
2473 }
2474 
2475 /*
2476  * Evaluate an expression: eval <expression>
2477  */
2478 
2479 static int doeval (int argc, char **argv)
2480 {
2481  Source *s;
2482 
2483  (s = pushs (SWORDS))->u.strv = argv + 1;
2484  return RunACommand (s, (char **)NULL);
2485 }
2486 
2487 /*
2488  * Map signals.
2489  *
2490  * Numeric values are assumed to be UNIX - map to appropriate DOS signal.
2491  * Character values are just mapped to the DOS signal
2492  */
2493 
2495 {
2496  static struct TrapSignalList Numeric = {NULL, 0};
2497  int n;
2498 
2499  if (isdigit (*name))
2500  {
2501  if (((n = GetNumericValue (name)) < 0) || (n >= MAX_SIG_MAP))
2502  return (struct TrapSignalList *)NULL;
2503 
2504 #if (OS_TYPE == OS_UNIX)
2505  Numeric.signo = n;
2506 #else
2507  Numeric.signo = UnixToDosSignals [n];
2508 #endif
2509  return &Numeric;
2510  }
2511 
2512 /* Check the character names */
2513 
2514  for (n = 0; n < MAX_TRAP_SIGNALS; n++)
2515  {
2516  if (stricmp (name, TrapSignalList[n].name) == 0)
2517  return &TrapSignalList[n];
2518  }
2519 
2520  return (struct TrapSignalList *)NULL;
2521 }
2522 
2523 /*
2524  * Execute a trap: trap [ number... ] [ command ]
2525  */
2526 
2527 static int dotrap (int argc, char **argv)
2528 {
2529  int i;
2530  bool SetSignal;
2531  char tval[10];
2532  char *cp;
2533  struct TrapSignalList *SignalInfo;
2534  void (*NewSignalFunc)(int);
2535 
2536  if ((argc == 2) && (strcmp (argv[1], "-l") == 0))
2537  {
2538  for (i = 3; i < MAX_TRAP_SIGNALS; i++)
2539  puts (TrapSignalList[i].name);
2540 
2541  return 0;
2542  }
2543 
2544 /* Display active traps ? */
2545 
2546  if (argc < 2)
2547  {
2548 
2549 /* Display trap - look up each trap and print those we find */
2550 
2551  for (i = 0; i < NSIG; i++)
2552  {
2553  sprintf (tval, "~%d", i);
2554 
2555  if ((cp = GetVariableAsString (tval, FALSE)) != null)
2556  printf ("%u: %s\n", i, cp);
2557  }
2558 
2559  if ((cp = GetVariableAsString (Trap_DEBUG, FALSE)) != null)
2561 
2562  if ((cp = GetVariableAsString (Trap_ERR, FALSE)) != null)
2564 
2565  return 0;
2566  }
2567 
2568 /* Check to see if signal re-set */
2569 
2570  SetSignal = C2bool(LookupSignalName (argv[1]) ==
2571  (struct TrapSignalList *)NULL);
2572 
2573  for (i = SetSignal ? 2 : 1; argv[i] != NOWORD; ++i)
2574  {
2575  if ((SignalInfo = LookupSignalName (argv[i]))
2576  == (struct TrapSignalList *)NULL)
2577  return PrintWarningMessage ("trap: bad signal number - %s",
2578  argv[i]);
2579 
2580 /* Check for no UNIX to DOS mapping */
2581 
2582  if (SignalInfo->signo == SIG_NO_MAP)
2583  {
2584  if (!FL_TEST (FLAG_WARNING))
2585  PrintWarningMessage ("trap: No UNIX to DOS signal map for %s",
2586  argv[i]);
2587 
2588  continue;
2589  }
2590 
2591 /* Check for ERR or DEBUG. cp points to the variable name */
2592 
2593  if (SignalInfo->signo == SIG_SPECIAL)
2594  cp = (SignalInfo->name) - 1;
2595 
2596 /* Generate the variable name for a numeric value */
2597 
2598  else
2599  sprintf (cp = tval, "~%d", SignalInfo->signo);
2600 
2601 /* Remove the old processing */
2602 
2603  RemoveVariable (cp, 0);
2604 
2605 /* Default to new function of ignore! */
2606 
2607  NewSignalFunc = SIG_DFL;
2608 
2609 /* Re-define signal processing */
2610 
2611  if (SetSignal)
2612  {
2613  if (*argv[1] != 0)
2614  {
2616  NewSignalFunc = TerminateSignalled;
2617  }
2618 
2619  else
2620  NewSignalFunc = SIG_IGN;
2621  }
2622 
2623 /* Clear signal processing */
2624 
2625  else if (InteractiveFlag)
2626  {
2627  if (SignalInfo->signo == SIGINT)
2628  NewSignalFunc = InterruptSignalled;
2629 
2630 #ifdef SIGQUIT
2631  else
2632  NewSignalFunc = (SignalInfo->signo == SIGQUIT)
2633  ? SIG_IGN : SIG_DFL;
2634 #endif
2635  }
2636 
2637 /* Set up new signal function */
2638 
2639  if (SignalInfo->signo > 0)
2640  signal (SignalInfo->signo, NewSignalFunc);
2641 
2642  }
2643 
2644  return 0;
2645 }
2646 
2647 /*
2648  * BREAK and CONTINUE processing: break/continue [ n ]
2649  */
2650 
2651 static int dobreak (int argc, char **argv)
2652 {
2653  if (argc > 2)
2654  return UsageError ("break [ count ]");
2655 
2656  return BreakContinueProcessing (argv[1], BC_BREAK);
2657 }
2658 
2659 static int docontinue (int argc, char **argv)
2660 {
2661  if (argc > 2)
2662  return UsageError ("continue [ count ]");
2663 
2665 }
2666 
2667 static int F_LOCAL BreakContinueProcessing (char *NumberOfLevels,
2668  int Type)
2669 {
2670  Break_C *Break_Loc;
2671  int LevelNumber;
2672  char *cType = (Type == BC_BREAK) ? LIT_break : LIT_continue;
2673 
2674 
2675  LevelNumber = (NumberOfLevels == (char *)NULL)
2676  ? 1 : GetNumericValue (NumberOfLevels);
2677 
2678  if (LevelNumber < 0)
2679  return PrintWarningMessage (LIT_Emsg, cType, "bad level number",
2680  NumberOfLevels);
2681 
2682 /* If level is invalid - clear all levels */
2683 
2684  if (LevelNumber <= 0)
2685  LevelNumber = 999;
2686 
2687 /* Move down the stack */
2688 
2689  do
2690  {
2691  if ((Break_Loc = Break_List) == (Break_C *)NULL)
2692  break;
2693 
2694  Break_List = Break_Loc->NextExitLevel;
2695 
2696  } while (--LevelNumber);
2697 
2698 /* Check level */
2699 
2700  if (LevelNumber)
2701  return PrintWarningMessage (BasicErrorMessage, cType, "bad level");
2702 
2703  longjmp (Break_Loc->CurrentReturnPoint, Type);
2704 
2705 /* NOTREACHED */
2706  return 1;
2707 }
2708 
2709 /*
2710  * Exit function: exit [ status ]
2711  */
2712 
2713 static int doexit (int argc, char **argv)
2714 {
2715  Break_C *SShell_Loc = SShell_List;
2716 
2718 
2719  if (argc > 2)
2720  return UsageError ("exit [ status ]");
2721 
2722 /* Set up error codes */
2723 
2724  ExitStatus = (argv[1] != NOWORD) ? GetNumericValue (argv[1]) : 0;
2725 
2727 
2728 /* Are we in a subshell. Yes - do a longjmp instead of an exit */
2729 
2730  if (SShell_Loc != (Break_C *)NULL)
2731  {
2732  SShell_List = SShell_Loc->NextExitLevel;
2733  longjmp (SShell_Loc->CurrentReturnPoint, ExitStatus);
2734  }
2735 
2736 /*
2737  * Check for active jobs
2738  */
2739 
2740 #if (OS_TYPE != OS_DOS)
2741  if (!ExitWithJobsActive)
2742  {
2743  if (NumberOfActiveJobs () && InteractiveFlag)
2744  {
2745  feputs ("You have running jobs.\n");
2746  ExitWithJobsActive = TRUE;
2747  return 0;
2748  }
2749  }
2750 #endif
2751 
2752  ExitTheShell (FALSE);
2753  return ExitStatus;
2754 }
2755 
2756 /*
2757  * Function return: return [ status ]
2758  *
2759  * Set exit value and return via a long jmp
2760  */
2761 
2762 static int doreturn (int argc, char **argv)
2763 {
2764  Break_C *Return_Loc = Return_List;
2765  int Retval = 0;
2766 
2767  if ((argc > 2) ||
2768  ((argc == 2) && ((Retval = GetNumericValue (argv[1])) == -1)))
2769  return UsageError (ReturnUsage);
2770 
2771  SetVariableFromNumeric (StatusVariable, (long) abs (Retval));
2772 
2773 /* If the return address is defined - return to it. Otherwise, return
2774  * the value
2775  */
2776 
2777  if (Return_Loc != (Break_C *)NULL)
2778  {
2779  Return_List = Return_Loc->NextExitLevel;
2780  longjmp (Return_Loc->CurrentReturnPoint, 1);
2781  }
2782 
2783  return Retval;
2784 }
2785 
2786 /*
2787  * Set function: set [ -/+flags ] [ parameters... ]
2788  */
2789 
2790 static int doset (int argc, char **argv)
2791 {
2792  int i;
2793 
2794 /* Display ? */
2795 
2796  if (argc < 2)
2797  return ListAllVariables (0xffff, TRUE);
2798 
2799 /* Set/Unset a flag ? */
2800 
2801  ResetGetOptions (); /* Reset GetOptions */
2802 
2803  while ((i = GetOptions (argc, argv, "VMA:abcdefghijklmno:pqrstuvwxyz",
2805  {
2806  switch (i)
2807  {
2808  case '?': /* Unknown */
2809  if (BadOptionValue != 'o')
2810  return UsageError ("set [ [-|+][switches] ] [ [-|+]o option ] [ parameter=value ] args");
2811 
2812  return PrintOptionSettings ();
2813 
2814  case 'r':
2815  return PrintWarningMessage ("set: r switch cannot be changed");
2816 
2817  case 'o':
2819  OptionStart == '-')) &&
2821  (bool)(OptionStart == '-'))))
2822  return PrintWarningMessage ("set: -o bad option (%s)",
2823  OptionArgument);
2824 
2825  break;
2826 
2827  case 'A':
2828  if (OptionStart == '-') /* If -, remove all values */
2830 
2831  i = 0;
2832 
2833  while (argv[OptionIndex] != NOWORD)
2835  argv[OptionIndex++]);
2836 
2837  return 0;
2838 
2839  case 'M':
2840  if (OptionStart == '-') /* If -, remove all values */
2842 
2843  else
2844  ShellGlobalFlags &= ~~FLAGS_MSDOS_FORMAT;
2845 
2846  break;
2847 
2848  case 'V':
2849  SetVerifyStatus (C2bool (OptionStart == '-'));
2850  break;
2851 
2852  default:
2853  if ((i == 'e') && InteractiveFlag)
2854  continue;
2855 
2856  SetClearFlag (i, (bool)(OptionStart == '-'));
2857  }
2858  }
2859 
2860  SetShellSwitches ();
2861 
2862 /* Check for --, ++, - and +, which we skip */
2863 
2864  if ((OptionIndex != argc) &&
2865  ((!strcmp (argv[OptionIndex], DoubleHypen)) ||
2867  (!strcmp (argv[OptionIndex], "+"))))
2868  OptionIndex++;
2869 
2870 /* Set up parameters ? */
2871 
2872  if (OptionIndex != argc)
2873  {
2875  return SetUpNewParameterVariables (argv, OptionIndex, argc, "set");
2876  }
2877 
2878  else
2879  return 0;
2880 }
2881 
2882 
2883 /*
2884  * Print the list of functions: functions [ names ]
2885  */
2886 
2887 static int dofunctions (int argc, char **argv)
2888 {
2889  FunctionList *fp;
2890  int i;
2891 
2892  if (argc < 2)
2893  return PrintAllFunctions ();
2894 
2895  for (i = 1; argv[i] != NOWORD; ++i)
2896  {
2897  if ((fp = LookUpFunction (argv[i], TRUE)) != (FunctionList *)NULL)
2898  PrintFunction (fp->tree, PF_MODE_NORMAL);
2899 
2900  else
2901  PrintWarningMessage ("functions: %s is not a function", argv[i]);
2902  }
2903 
2904  return 0;
2905 }
2906 
2907 /*
2908  * History functions - history [-ieds]
2909  */
2910 
2911 static int dohistory (int argc, char **argv)
2912 {
2913  int i;
2914  int Start;
2915  long value;
2916 
2917  if (!InteractiveFlag)
2918  return 1;
2919 
2920  if (argc < 2)
2921  Start = GetLastHistoryEvent () + 1;
2922 
2923 /*
2924  * Check for options
2925  */
2926 
2927  else if (**(argv + 1) == CHAR_SWITCH)
2928  {
2929  ResetGetOptions (); /* Reset GetOptions */
2930 
2931  while ((i = GetOptions (argc, argv, "sidel", 0)) != EOF)
2932  {
2933  switch (i)
2934  {
2935  case 's':
2936  DumpHistory ();
2937  break;
2938 
2939  case 'i':
2940  ClearHistory ();
2941  break;
2942 
2943  case 'l':
2944  LoadHistory ();
2945  break;
2946 
2947  case 'd':
2949  break;
2950 
2951  case 'e':
2952  HistoryEnabled = TRUE;
2953  break;
2954 
2955  default:
2956  return UsageError (HistoryUsage);
2957  }
2958  }
2959 
2960  return (OptionIndex != argc) ? UsageError (HistoryUsage) : 0;
2961  }
2962 
2963 /* Check for number of display */
2964 
2965  else if ((argc == 2) && ConvertNumericValue (*(argv + 1), &value, 0))
2966  Start = (int)value + 1;
2967 
2968  else
2969  return UsageError (HistoryUsage);
2970 
2971 /*
2972  * Display history
2973  */
2974 
2975  if ((i = (GetLastHistoryEvent () + 1) - Start) < 0)
2976  i = 0;
2977 
2979  return 0;
2980 }
2981 
2982 /*
2983  * Type function: type [ command ]
2984  *
2985  * For each name, indicate how it would be interpreted
2986  */
2987 
2988 static int dowhence (int argc, char **argv)
2989 {
2990  char *cp;
2991  int n; /* Argument count */
2992  int inb; /* Inbuilt function */
2993  bool v_flag;
2994  bool p_flag = FALSE;
2995  bool t_flag = FALSE;
2996  char *l_path;
2997  AliasList *al;
2998 
2999 /* Get some memory for the buffer */
3000 
3001  if ((l_path = AllocateMemoryCell (FFNAME_MAX + 4)) == (char *)NULL)
3002  return doOutofMemory (*argv);
3003 
3004  v_flag = (bool)(strcmp (*argv, LIT_type) == 0);
3005 
3006  ResetGetOptions (); /* Reset GetOptions */
3007 
3008  while ((n = GetOptions (argc, argv, "pvt", 0)) != EOF)
3009  {
3010  switch (n)
3011  {
3012  case 'v': v_flag = TRUE; break;
3013  case 'p': p_flag = TRUE; break;
3014  case 't': t_flag = TRUE; break;
3015 
3016  default:
3017  return UsageError (WhenceUsage);
3018  }
3019  }
3020 
3021 /* Process each parameter */
3022 
3023  while ((cp = argv[OptionIndex++]) != NOWORD)
3024  {
3025 
3026 /* Check for alias */
3027 
3028  if ((al = LookUpAlias (cp, FALSE)) != (AliasList *)NULL)
3029  {
3030  if (v_flag)
3031  printf ("%s is %s alias for %s", cp,
3032  (al->AFlags & ALIAS_TRACKED) ? "a tracked"
3033  : "an", al->value);
3034 
3035  else
3036  foputs (al->value);
3037  }
3038 
3039 
3040 /* Check for currently use inbuilt version */
3041 
3042  else if (!p_flag && IsCommandBuiltIn (cp, &inb) && (inb & BLT_CURRENT))
3044 
3045 /* Check for a function */
3046 
3047  else if (!p_flag &&
3049  WhenceLocation (v_flag, cp, "is a function");
3050 
3051 /* Scan the path for an executable */
3052 
3053  else if (FindLocationOfExecutable (l_path, cp) != EXTENSION_NOT_FOUND)
3054  {
3055  PATH_TO_LOWER_CASE (l_path);
3056 
3057  if (v_flag)
3058  printf ("%s is ", cp);
3059 
3060  foputs (PATH_TO_UNIX (l_path));
3061 
3062  if (t_flag)
3063  WhenceType (l_path);
3064  }
3065 
3066 /* If not found, check for inbuilt version */
3067 
3068  else if (!p_flag && (IsCommandBuiltIn (cp, &inb)))
3070 
3071  else if (!p_flag && LookUpSymbol (cp))
3072  WhenceLocation (v_flag, cp, "is a shell keyword");
3073 
3074  else if (v_flag)
3075  {
3077  continue;
3078  }
3079 
3080  fputchar (CHAR_NEW_LINE);
3081  }
3082 
3083  return 0;
3084 }
3085 
3086 /*
3087  * Output file type
3088  */
3089 
3090 static char *ExeType_Error[] = {
3091  "Not known",
3092  "Bad image",
3093  "Not executable",
3094  "File not found"
3095 };
3096 
3097 static char *ExeType_Dos[] = {
3098  "DOS Character",
3099  "Windows 16-bit",
3100  "Watcom 32 bit",
3101  "OS/2 Bound"
3102 };
3103 
3104 static char *ExeType_OS2[] = {
3105  "Not PM compatible",
3106  "PM compatible",
3107  "PM"
3108 };
3109 
3110 static char *ExeType_NT[] = {
3111  "Native",
3112  "Windows GUI",
3113  "Windows CUI",
3114  "OS2",
3115  "POSIX"
3116 };
3117 
3118 static void F_LOCAL WhenceType (char *path)
3119 {
3120  unsigned long type = QueryApplicationType (path);
3121 
3122  foputs (" [");
3123 
3124  if (type & EXETYPE_ERROR)
3125  foputs (ExeType_Error [type - 1]);
3126 
3127  else if (type & EXETYPE_DOS)
3128  foputs (ExeType_Dos [(type >> 4) - 1]);
3129 
3130  else if (type & EXETYPE_OS2)
3131  printf ("OS2 %dbit %s", (type & EXETYPE_OS2_32) ? 32 : 16,
3132  ExeType_OS2 [((type & EXETYPE_OS2_TYPE) >> 8) - 1]);
3133 
3134  else if (type & EXETYPE_NT)
3135  printf ("Win NT %s subsystem", ExeType_NT [(type >> 12) - 1]);
3136 
3137  fputchar (']');
3138 }
3139 
3140 /*
3141  * Output location
3142  */
3143 
3144 static void F_LOCAL WhenceLocation (bool v_flag, char *cp, char *mes)
3145 {
3146  foputs (cp);
3147 
3148  if (v_flag)
3149  printf (" %s", mes);
3150 }
3151 
3152 /*
3153  * Table of internal commands. Note that this table is sort in alphabetic
3154  * order.
3155  */
3156 
3157 static struct builtin builtin[] = {
3158  { "((", dolet, (BLT_ALWAYS | BLT_CURRENT) },
3159  { ".", dodot, (BLT_ALWAYS | BLT_CURRENT |
3160  BLT_CENVIRON) },
3161  { ":", dolabel, (BLT_ALWAYS | BLT_CURRENT |
3162  BLT_CENVIRON) },
3163  { "[", dotest, BLT_CURRENT },
3165  BLT_CENVIRON | BLT_NOGLOB) },
3168  BLT_NOWORDS) },
3169 #if defined (FLAGS_EMACS) || defined (FLAGS_GMACS)
3170  { "bind", dobind, (BLT_CURRENT | BLT_CENVIRON) },
3171 #endif
3174  { "cd", dochdir, BLT_CURRENT },
3175  { "chdir", dochdir, (BLT_ALWAYS | BLT_CURRENT) },
3177 
3178 #if (OS_TYPE != OS_DOS)
3179  { "detach", dodetach, (BLT_ALWAYS | BLT_CURRENT) },
3180 #endif
3181 
3182  { "echo", doecho, BLT_CURRENT },
3183  { "eval", doeval, (BLT_CURRENT | BLT_CENVIRON) },
3184  { LIT_exec, (int (*)(int, char **)) doexec,
3185  (BLT_CURRENT | BLT_CENVIRON) },
3188  BLT_NOGLOB | BLT_NOWORDS) },
3189 
3190 #if (OS_TYPE == OS_OS2)
3191  { "extproc", dolabel, (BLT_CURRENT | BLT_CENVIRON) },
3192 #endif
3193 
3194  { "false", dofalse, BLT_CURRENT },
3195  { "fc", dofc, BLT_CURRENT },
3196  { "functions", dofunctions, (BLT_ALWAYS | BLT_CURRENT) },
3197  { "getopts", dogetopts, BLT_CURRENT },
3199 
3200 #if (OS_TYPE != OS_DOS)
3201  { "jobs", dojobs, BLT_CURRENT },
3202  { "kill", dokill, BLT_CURRENT },
3203 #endif
3204 
3205  { "let", dolet, BLT_CURRENT },
3206  { "msdos", domsdos, BLT_CURRENT },
3208  { "pwd", dopwd, BLT_CURRENT },
3209  { LIT_read, doread, BLT_CURRENT },
3210  { "readonly", doreadonly, (BLT_CURRENT | BLT_CENVIRON |
3211  BLT_NOGLOB | BLT_NOWORDS) },
3212  { "return", doreturn, (BLT_CURRENT | BLT_CENVIRON) },
3213  { "set", doset, BLT_CURRENT },
3214  { "shellinfo", doshellinfo, BLT_CURRENT },
3216 
3217 #if (OS_TYPE == OS_OS2)
3218  { "start", dostart, BLT_CURRENT },
3219 #endif
3220 
3221  { "swap", doswap, BLT_CURRENT },
3222  { "test", dotest, BLT_CURRENT },
3223 #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) && !defined (__WATCOMC__)
3224  { "times", dotimes, BLT_CURRENT },
3225 #endif
3226 #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_UNIX)
3227  { "times", dotimes, BLT_CURRENT },
3228 #endif
3229  { "trap", dotrap, (BLT_CURRENT | BLT_CENVIRON) },
3230  { "true", dolabel, BLT_CURRENT },
3232  { "typeset", dotypeset, (BLT_CURRENT | BLT_CENVIRON |
3233  BLT_NOGLOB | BLT_NOWORDS) },
3234  { "umask", doumask, BLT_CURRENT },
3236  { LIT_unfunction,
3237  dounset, BLT_CURRENT },
3238  { "unset", dounset, BLT_CURRENT },
3239  { "ver", dover, BLT_CURRENT },
3240 
3241 #if (OS_TYPE != OS_DOS)
3242  { "wait", dowait, BLT_CURRENT },
3243 #endif
3244 
3245  { "whence", dowhence, BLT_CURRENT },
3246  { (char *)NULL, (int (*)(int, char **))NULL, 0 }
3247 };
3248 
3249 /*
3250  * Look up a built in command
3251  */
3252 
3253 int (*IsCommandBuiltIn (char *s, int *b))(int, char **)
3254 {
3255  struct builtin *bp;
3256  int res;
3257 
3258  *b = 0;
3259 
3260 /* Check for change drive */
3261 
3262  if ((strlen (s) == 2) && isalpha (*s) && IsDriveCharacter (*(s + 1)))
3263  {
3264  *b = BLT_ALWAYS | BLT_CURRENT;
3265  return dodrive;
3266  }
3267 
3268 /* Search for command */
3269 
3270  for (bp = builtin; bp->command != (char *)NULL; bp++)
3271  {
3272  if ((res = NOCASE_COMPARE (bp->command, s)) >= 0)
3273  {
3274  if (res != 0)
3275  {
3276  if (!isalpha (*(bp->command)))
3277  continue;
3278 
3279  break;
3280  }
3281 
3282  *b = bp->mode;
3283  return bp->fn;
3284  }
3285  }
3286 
3287  return (int (*)(int, char **))NULL;
3288 }
3289 
3290 /*
3291  * Builtin - either list builtins or execute it
3292  *
3293  * builtin [-asd] [ names ]
3294  */
3295 
3296 static int dobuiltin (int argc, char **argv)
3297 {
3298  struct builtin *bp;
3299  int (*shcom)(int, char **) = (int (*)(int, char **))NULL;
3300  int ReturnValue = 0;
3301  char *carg;
3302  int mode;
3303  int action = -1;
3304  int i;
3305 
3306  if (argc < 2)
3307  {
3308  for (bp = builtin; bp->command != (char *)NULL; bp++)
3309  printf (LIT_3Strings, LIT_builtin, bp->command,
3310  (bp->mode & BLT_CURRENT) ? " - preferred" : "");
3311  return 0;
3312  }
3313 
3314 /* Check for changing options */
3315 
3316  ResetGetOptions (); /* Reset GetOptions */
3317 
3318  while ((i = GetOptions (argc, argv, "sad", 0)) != EOF)
3319  {
3320  switch (i)
3321  {
3322  case 's': action = 0; break;
3323  case 'a': action = 1; break;
3324  case 'd': action = 2; break;
3325 
3326  default:
3327  return UsageError (BuiltinUsage);
3328  }
3329  }
3330 
3331 /* Check to see if we know about the builtin version */
3332 
3333  if (action == -1)
3334  {
3335  if ((shcom = IsCommandBuiltIn (argv[1], &mode))
3336  == (int (*)(int, char **))NULL)
3337  {
3339  return 1;
3340  }
3341 
3342 /* Yes - so execute the builtin version. Set up the word list correctly */
3343 
3344  argv++;
3345  ReturnValue = (*shcom)(CountNumberArguments (argv), argv);
3346  argv--;
3347  return ReturnValue;
3348  }
3349 
3350 /* Execute the requested functions against the builtin commands */
3351 
3352  while ((carg = argv[OptionIndex++]) != NOWORD)
3353  {
3354  for (bp = builtin;
3355  (bp->command != (char *)NULL) && (NOCASE_COMPARE (bp->command, carg) != 0);
3356  bp++)
3357  continue;
3358 
3359 /* Command is not builtin */
3360 
3361  if (bp->command == (char *)NULL)
3362  {
3364  ReturnValue = 1;
3365  continue;
3366  }
3367 
3368 /* Update on the action */
3369 
3370  switch (action)
3371  {
3372  case 0:
3373  printf (BasicErrorMessage, carg, (bp->mode & BLT_CURRENT)
3374  ? LIT_builtin : "external");
3375  break;
3376 
3377  case 1:
3378  bp->mode |= BLT_CURRENT;
3379  break;
3380 
3381  case 2:
3382  if (bp->mode & BLT_ALWAYS)
3383  printf (BasicErrorMessage, carg, "always builtin");
3384 
3385  else
3386  bp->mode &= ~~BLT_CURRENT;
3387 
3388  break;
3389  }
3390  }
3391 
3392  return ReturnValue;
3393 }
3394 
3395 /*
3396  * Report Usage error
3397  */
3398 
3399 static int F_LOCAL UsageError (char *string)
3400 {
3401  return PrintWarningMessage ("Usage: %s", string) + 1;
3402 }
3403 
3404 /*
3405  * Alias command: alias [ -t] [ name [=commands] ]...
3406  */
3407 
3408 static int doalias (int argc, char **argv)
3409 {
3410  int ReturnValue = 0;
3411  int i = 1;
3412  bool tracked = FALSE;
3413  char *path = (char *)NULL;
3414  char *cp;
3415 
3416 /* Check for tracked aliases */
3417 
3418  if ((argc > 1) && (strcmp (argv[1], "-t") == 0))
3419  {
3420  ++i;
3421  tracked = TRUE;
3422  }
3423 
3424 /* List only? */
3425 
3426  if (argc <= i)
3427  return PrintAllAlias (tracked);
3428 
3429 /* Set them up or print them */
3430 
3431  while (i < argc)
3432  {
3433 
3434 /* Not tracked - either set alias value if there is an equals or display
3435  * the alias value
3436  */
3437 
3438  if (!tracked)
3439  {
3440  if ((cp = strchr (argv[i], '=')) != (char *)NULL)
3441  *(cp++) = 0;
3442 
3443 /* Check for valid name */
3444 
3445  if (!IsValidAliasName (argv[i], TRUE))
3447  argv[i]);
3448 
3449 /* Save it if appropriate */
3450 
3451  if (cp != (char *)NULL)
3452  {
3453  if (!SaveAlias (argv[i], cp, tracked))
3454  ReturnValue = 1;
3455  }
3456 
3457 /* Print it */
3458 
3459  else if (LookUpAlias (argv[i], FALSE) == (AliasList *)NULL)
3460  {
3462  ReturnValue = 1;
3463  }
3464 
3465  else
3466  PrintAlias (argv[i]);
3467  }
3468 
3469 /* Set up tracked alias */
3470 
3471  else if (!IsValidAliasName (argv[i], TRUE))
3473  argv[i]);
3474 
3475  else if ((path == (char *)NULL) &&
3476  ((path = AllocateMemoryCell (FFNAME_MAX + 4)) == (char *)NULL))
3477  return doOutofMemory (*argv);
3478 
3479 /* Save the new path for the alias */
3480 
3481  else if (SaveAlias (argv[i],
3483  != EXTENSION_NOT_FOUND) ? path : null, tracked))
3484  ReturnValue = 1;
3485 
3486  ++i;
3487  }
3488 
3489  return ReturnValue;
3490 }
3491 
3492 /*
3493  * UnAlias command: unalias name...
3494  */
3495 
3496 static int dounalias (int argc, char **argv)
3497 {
3498  int i;
3499 
3500 /* Set them up or print them */
3501 
3502  for (i = 1; i < argc; ++i)
3503  {
3504  if (LookUpAlias (argv[i], FALSE) != (AliasList *)NULL)
3505  DeleteAlias (argv[i]);
3506 
3507  else
3509  }
3510 
3511  return 0;
3512 }
3513 
3514 /*
3515  * OS2 detach function
3516  */
3517 
3518 #if (OS_TYPE != OS_DOS)
3519 static int dodetach (int argc, char **argv)
3520 {
3521  int RetVal;
3522 
3523  if (argc < 2)
3524  return UsageError ("detach program [ parameters ... ]");
3525 
3526  argv++;
3527  RetVal = ExecuteACommand (argv, EXEC_SPAWN_DETACH);
3528  argv--;
3529  return RetVal;
3530 }
3531 #endif
3532 
3533 /*
3534  * Start a session
3535  */
3536 
3537 #if (OS_TYPE == OS_OS2)
3538 static int dostart (int argc, char **argv)
3539 {
3540  bool Direct = FALSE;
3541  bool UseCMD = FALSE;
3542  STARTDATA stdata;
3543  STARTDATA *sdp = &stdata;
3544  Word_B *wb = (Word_B *)NULL;
3545  Word_B *ep = (Word_B *)NULL;
3546  int c;
3547  bool options = FALSE;
3548  bool SetDefault = FALSE;
3549  char ChangeEnv = 0;
3550  bool FirstArg = TRUE;
3551  bool Display = FALSE;
3552  OSCALL_RET rc;
3553  OSCALL_PARAM ulSessID;
3554  char *sp;
3555  char p_name[FFNAME_MAX];
3556  char *SetPWD = GetVariableAsString (PWDVariable, FALSE);
3557 
3558 /* Initialise the session control info */
3559 
3560  memset (&stdata, 0, sizeof (STARTDATA));
3561  stdata.Length = sizeof (STARTDATA);
3562  stdata.FgBg = SSF_FGBG_BACK; /* Set background session */
3563  stdata.PgmTitle = (PGM_TITLE_TYPE *)NULL;
3564  stdata.Related = SSF_RELATED_CHILD;
3565  stdata.TraceOpt = SSF_TRACEOPT_NONE;
3566  stdata.InheritOpt = SSF_INHERTOPT_SHELL;
3567  stdata.IconFile = (char *)NULL;
3568  stdata.PgmHandle = 0L;
3569  stdata.PgmControl = SSF_CONTROL_NOAUTOCLOSE;
3570  stdata.InitXPos = 0;
3571  stdata.InitYPos = 0;
3572  stdata.InitXSize = 100;
3573  stdata.InitYSize = 100;
3574 
3575 /* These values get reset somewhere along the line */
3576 
3577 # if (OS_SIZE == OS_16)
3578  stdata.SessionType = SSF_TYPE_WINDOWABLEVIO;
3579 # else
3580  stdata.SessionType = SSF_TYPE_DEFAULT;
3581 # endif
3582 
3583  stdata.Environment = (PBYTE)1; /* Build the environment */
3584 
3585 /* Switch on the arguments */
3586 
3587  ResetGetOptions (); /* Reset GetOptions */
3588 
3589  while ((c = GetOptions (argc, argv,
3590  SetDefault ? "t:hHfWPFibIDxe:c:"
3591  : "O:St:dhHfWPFxibCIe:c:A:X:", 0)) != EOF)
3592  {
3593  switch (c)
3594  {
3595  case 'O':
3596  if (!FirstArg)
3597  return UsageError (StartUsage);
3598 
3599  else if (!stricmp (OptionArgument, LIT_dos))
3600  sdp = &DOS_SessionControlBlock;
3601 
3602  else if (!stricmp (OptionArgument, "pm"))
3603  sdp = &PM_SessionControlBlock;
3604 
3605  else
3606  return UsageError (StartUsage);
3607 
3608  SetDefault = TRUE;
3609  break;
3610 
3611  case 'A':
3612  if ((options) || (OptionIndex != argc))
3613  return UsageError (StartUsage);
3614 
3615  ulSessID = (OSCALL_PARAM) strtoul (OptionArgument, &sp, 0);
3616 
3617  if (*sp)
3618  return UsageError (StartUsage);
3619 
3620 # if (OS_SIZE == OS_16)
3621  if ((rc = DosSelectSession (ulSessID, 0L)))
3622 # else
3623  if ((rc = DosSelectSession (ulSessID)))
3624 # endif
3625  return PrintWarningMessage (Start_NoSession, ulSessID,
3626  GetOSSystemErrorMessage (rc));
3627  return 0;
3628 
3629  case 'c': /* Set program control */
3630  {
3631  sdp->PgmControl = 0;
3632  sp = OptionArgument;
3633 
3634  while (*sp)
3635  {
3636  switch (*(sp++))
3637  {
3638  case 'v':
3639  sdp->PgmControl |= SSF_CONTROL_VISIBLE;
3640  sdp->PgmControl &= ~~SSF_CONTROL_INVISIBLE;
3641  break;
3642 
3643  case 'i':
3644  sdp->PgmControl |= SSF_CONTROL_INVISIBLE;
3645  sdp->PgmControl &= ~~SSF_CONTROL_VISIBLE;
3646  break;
3647 
3648  case 'l':
3649  sdp->PgmControl |= SSF_CONTROL_MAXIMIZE;
3650  sdp->PgmControl &= ~~SSF_CONTROL_MINIMIZE;
3651  break;
3652 
3653  case 's':
3654  sdp->PgmControl |= SSF_CONTROL_MINIMIZE;
3655  sdp->PgmControl &= ~~SSF_CONTROL_MAXIMIZE;
3656  break;
3657 
3658  case 'n':
3659  sdp->PgmControl |= SSF_CONTROL_NOAUTOCLOSE;
3660  break;
3661 
3662  case 'a':
3663  sdp->PgmControl &= ~~SSF_CONTROL_NOAUTOCLOSE;
3664  break;
3665 
3666  default:
3667  return PrintWarningMessage
3668  ("start: option must be one of [vilsna]") + 1;
3669  }
3670  }
3671 
3672  break;
3673  }
3674 
3675  case 'X': /* Set startup directory */
3676  SetPWD = OptionArgument;
3677  break;
3678 
3679  case 't': /* Set title */
3680  if (SetDefault && (sdp->PgmTitle != (PGM_TITLE_TYPE *)NULL))
3681  ReleaseMemoryCell (sdp->PgmTitle);
3682 
3683  sdp->PgmTitle = (!strlen (OptionArgument))
3684  ? (char *)NULL
3685  : SetDefault
3687  : OptionArgument;
3688 
3689  if (strlen (OptionArgument) > 32)
3690  sdp->PgmTitle[33] = 0;
3691 
3692  break;
3693 
3694  case 'S': /* Script */
3695  Direct = FALSE;
3696  break;
3697 
3698  case 'd': /* Direct */
3699  Direct = TRUE;
3700  break;
3701 
3702  case 'H': /* Inherit options */
3703  sdp->InheritOpt = SSF_INHERTOPT_SHELL;
3704  break;
3705 
3706  case 'h': /* Inherit options */
3707  sdp->InheritOpt = SSF_INHERTOPT_PARENT;
3708  break;
3709 
3710  case 'f': /* Foreground */
3711  sdp->FgBg = SSF_FGBG_FORE;
3712  break;
3713 
3714  case 'b': /* Background */
3715  sdp->FgBg = SSF_FGBG_BACK;
3716  break;
3717 
3718  case 'I':
3719  sdp->Related = SSF_RELATED_INDEPENDENT;
3720  break;
3721 
3722  case 'x':
3723  sdp->Related = SSF_RELATED_CHILD;
3724  break;
3725 
3726  case 'W': /* PM Window */
3727  sdp->FgBg = SSF_FGBG_FORE;
3728  sdp->SessionType = (sdp == &DOS_SessionControlBlock)
3729  ? SSF_TYPE_WINDOWABLEVIO
3730  : SSF_TYPE_WINDOWEDVDM;
3731  break;
3732 
3733  case 'P': /* PM Session */
3734  sdp->FgBg = SSF_FGBG_FORE;
3735  sdp->SessionType = (sdp == &DOS_SessionControlBlock)
3736  ? SSF_TYPE_PM
3737  : SSF_TYPE_WINDOWEDVDM;
3738  break;
3739 
3740  case 'F': /* Full Screen */
3741  sdp->FgBg = SSF_FGBG_FORE;
3742  sdp->SessionType = (sdp == &DOS_SessionControlBlock)
3743  ? SSF_TYPE_FULLSCREEN
3744  : SSF_TYPE_VDM;
3745  break;
3746 
3747  case 'C': /* Use the CMD processor */
3748  UseCMD = TRUE;
3749  break;
3750 
3751  case 'D': /* Show options */
3752  Display = TRUE;
3753  break;
3754 
3755  case 'e': /* Define environment */
3756  if (ChangeEnv == 'i')
3757  return UsageError (StartUsage);
3758 
3759  if ((sdp->Environment != (PBYTE)NULL) &&
3760  (sdp->Environment != (PBYTE)1))
3761  {
3762  ReleaseMemoryCell (sdp->Environment);
3763  sdp->Environment = (PBYTE)NULL;
3764  }
3765 
3766  ChangeEnv = 'e';
3767  ep = AddWordToBlock (OptionArgument, ep);
3768  break;
3769 
3770  case 'i': /* Inherit environment */
3771  if (ChangeEnv == 'e')
3772  return UsageError (StartUsage);
3773 
3774  if ((sdp->Environment != (PBYTE)NULL) &&
3775  (sdp->Environment != (PBYTE)1))
3776  ReleaseMemoryCell (sdp->Environment);
3777 
3778  ChangeEnv = 'i';
3779  sdp->Environment = (PBYTE)NULL;
3780  break;
3781 
3782  default:
3783  return UsageError (StartUsage);
3784  }
3785 
3786  FirstArg = FALSE;
3787  options = TRUE;
3788  }
3789 
3790 /* If setting default, no more parameters allowed */
3791 
3792  if (SetDefault)
3793  {
3794  if (OptionIndex != argc)
3795  return UsageError (StartUsage);
3796  }
3797 
3798  else
3799  {
3800 
3801 /* Check for script */
3802 
3803  if ((OptionIndex != argc) && (!UseCMD) && (!Direct) &&
3806  Direct = TRUE;
3807 
3808 /* Find the program to start */
3809 
3810  if ((OptionIndex == argc) || (!Direct))
3811  {
3812  if (!UseCMD)
3813  {
3815  FALSE), wb);
3816  if (SetPWD != null)
3817  {
3818  wb = AddWordToBlock ("-X", wb);
3819  wb = AddWordToBlock (SetPWD, wb);
3820  }
3821  }
3822 
3823  else
3824  {
3826  FALSE), wb);
3827  if (OptionIndex != argc)
3828  wb = AddWordToBlock ("/c", wb);
3829  }
3830  }
3831 
3832  else
3833  wb = AddWordToBlock (argv[OptionIndex++], wb);
3834  }
3835 
3836 /* Set up environment */
3837 
3838  if ((ep != (Word_B *)NULL) &&
3839  ((sdp->Environment =
3841  == (PBYTE)NULL))
3842  return doOutofMemory ("start");
3843 
3844 /* OK, if we set the default, processing is completed */
3845 
3846  if (SetDefault)
3847  {
3848  if (ep != (Word_B *)NULL)
3849  SetMemoryAreaNumber (sdp->Environment, 0);
3850 
3851  if (Display)
3852  DisplayStartData (sdp);
3853 
3854  return 0;
3855  }
3856 
3857 /* Set up the session block and execute the command */
3858 
3859  SessionControlBlock = &stdata;
3860 
3861 /* Build the argument block */
3862 
3863  while (OptionIndex != argc)
3864  wb = AddWordToBlock (argv[OptionIndex++], wb);
3865 
3866 /* Start the session */
3867 
3869 
3870  return (ExecuteACommand (argv, 0) == -1) ? 1 : 0;
3871 }
3872 
3873 /*
3874  * Clean up the start data structure, removing allocated space
3875  */
3876 
3877 static void F_LOCAL DisplayStartData (STARTDATA *sdp)
3878 {
3879  char *sp;
3880 
3881  printf ("Start session defaults for %s mode\n",
3882  (sdp == &DOS_SessionControlBlock) ? LIT_dos : "PM");
3883 
3884  printf (" Window mode: %s%s%s %sautoclose.\n",
3885  (sdp->PgmControl & SSF_CONTROL_INVISIBLE)
3886  ? "invisible" : "visible",
3887  (sdp->PgmControl & SSF_CONTROL_MINIMIZE)
3888  ? " minimised" : null,
3889  (sdp->PgmControl & SSF_CONTROL_MAXIMIZE)
3890  ? " maximised" : null,
3891  (sdp->PgmControl & SSF_CONTROL_NOAUTOCLOSE)
3892  ? "no " : "");
3893 
3894  if (sdp->PgmTitle != (PGM_TITLE_TYPE *)NULL)
3895  printf (" Program Title: %s\n", sdp->PgmTitle);
3896 
3897  printf (" Run in %s.\n", (sdp->FgBg == SSF_FGBG_FORE)
3898  ? "foreground" : "background");
3899 
3900  printf (" %s session.\n",
3901  (sdp->Related == SSF_RELATED_INDEPENDENT)
3902  ? "Independent" : "Dependent");
3903 
3904  printf (" Session type: %s.\n",
3905  (sdp->SessionType == SSF_TYPE_WINDOWABLEVIO)
3906  ? "Windowed"
3907  : (sdp->SessionType == SSF_TYPE_PM)
3908  ? "PM"
3909  : (sdp->SessionType == SSF_TYPE_FULLSCREEN)
3910  ? "Full screen"
3911  : (sdp->SessionType == SSF_TYPE_WINDOWEDVDM)
3912  ? "DOS Windowed"
3913  : (sdp->SessionType == SSF_TYPE_VDM)
3914  ? "DOS Full screen"
3915  : "Default");
3916 
3917  printf (" Inherit %s environment\n",
3918  (sdp->InheritOpt == SSF_INHERTOPT_SHELL) ? "start up" : "current");
3919 
3920  if (sdp->Environment == (PBYTE)1)
3921  puts (" Use current environment variables.");
3922 
3923  else if (sdp->Environment == (PBYTE)NULL)
3924  puts (" Use start up environment variables.");
3925 
3926  else
3927  {
3928  sp = sdp->Environment;
3929  printf (" Environment variables:\n");
3930 
3931  while (*sp)
3932  {
3933  printf (" %s\n", sp);
3934  sp += strlen (sp) + 1;
3935  }
3936  }
3937 }
3938 #endif
3939 
3940 
3941 /*
3942  * Set up new Parameter Variables
3943  */
3944 
3945 static int F_LOCAL SetUpNewParameterVariables (char **Array,/* New values*/
3946  int Offset, /* Start offset */
3947  int Max, /* Number */
3948  char *function)
3949 {
3950  int n;
3951  Word_B *wb = (Word_B *)NULL;
3952  char *cp;
3953  bool Duplicate = (bool)(strcmp (function, LIT_shift) == 0);
3954 
3955  if ((wb = AddParameter (ParameterArray[0], wb, function)) == (Word_B *)NULL)
3956  return 1;
3957 
3958  for (n = Offset; n < Max; ++n)
3959  {
3960  if ((cp = (Duplicate) ? StringSave (Array[n]) : Array[n])
3961  == null)
3962  return doOutofMemory (function);
3963 
3964  if ((wb = AddParameter (cp, wb, function)) == (Word_B *)NULL)
3965  return 1;
3966  }
3967 
3968  return (AddParameter (NOWORD, wb, function) == (Word_B *)NULL)
3969  ? 1 : 0;
3970 }
3971 
3972 /*
3973  * dolet - Each arg is an arithmetic expression to be evaluated.
3974  */
3975 
3976 static int dolet (int argc, char **argv)
3977 {
3978  long Value = 0;
3979  int i;
3980 
3981 /* If (( )), ignore the terminating )) */
3982 
3983  if ((strcmp (argv[0], "((") == 0) &&
3984  (strcmp (argv[argc - 1], "))") == 0))
3985  argv[argc - 1] = NOWORD;
3986 
3988 
3989  for (i = 1; !ExpansionErrorDetected && (argv[i] != NOWORD); ++i)
3991 
3992  return !Value || ExpansionErrorDetected ? 1 : 0;
3993 }
3994 
3995 /*
3996  * Out of memory error
3997  */
3998 
3999 static int F_LOCAL doOutofMemory (char *s)
4000 {
4002 }
4003 
4004 /*
4005  * MSDOS, EXPORT and READONLY functions: xxxx [ variable names... ]
4006  */
4007 
4008 static int doexport (int argc, char **argv)
4009 {
4010  return UpdateVariableStatus (argv + 1, STATUS_EXPORT);
4011 }
4012 
4013 static int doreadonly (int argc, char **argv)
4014 {
4016 }
4017 
4018 static int domsdos (int argc, char **argv)
4019 {
4021 }
4022 
4023 static int F_LOCAL UpdateVariableStatus (char **argv, unsigned int Mask)
4024 {
4025  if (*argv == NOWORD)
4026  return ListAllVariables (Mask, FALSE);
4027 
4028  else
4029  {
4030  memset (&TypesetValues, 0, sizeof (TypesetValues));
4032  return TypesetVariables (argv);
4033  }
4034 }
4035 
4036 /*
4037  * List All variables matching a STATUS
4038  */
4039 
4040 static int F_LOCAL ListAllVariables (unsigned int Mask, bool PrintValue)
4041 {
4043 
4044  DVE_Mask = Mask;
4045  DVE_PrintValue = PrintValue;
4046 
4048  return 0;
4049 }
4050 
4051 /*
4052  * TWALK Function - display VARIABLE tree
4053  */
4054 
4055 static void DisplayVariableEntry (const void *key, VISIT visit, int level)
4056 {
4057  VariableList *vp = *(VariableList **)key;
4058 
4059  if ((visit == postorder) || (visit == leaf))
4060  {
4061  if ((IS_VariableFC ((int)*vp->name)) &&
4062  ((vp->status & DVE_Mask) ||
4063  (((vp->status & ~~STATUS_GLOBAL) == 0) && (DVE_Mask == 0xffff))))
4065  (DVE_Mask == 0xffff) ? 0 : DVE_Mask);
4066  }
4067 }
4068 
4069 /*
4070  * typeset function - [ [ [+-][Hflprtux] ] [+-][LRZi[n]] [ name [=value] ...]
4071  */
4072 
4073 static int dotypeset (int argc, char **argv)
4074 {
4075  int ReturnValue = 0;
4076  bool f_flag = FALSE;
4077  unsigned int *CurrentFlags;
4078  int tmp = 0;
4079  char c_opt;
4080  char *cp;
4081 
4082 /* Initialise save area */
4083 
4084  memset (&TypesetValues, 0, sizeof (TypesetValues));
4085  OptionIndex = 1;
4086 
4087 /* Scan the options */
4088 
4089  while ((cp = argv[OptionIndex]) != NOWORD)
4090  {
4091  if ((*cp != '-') && (*cp != '+'))
4092  break;
4093 
4094  CurrentFlags = (*cp == '-') ? &TypesetValues.Flags_On
4096 
4097  while (*(++cp))
4098  {
4099  switch (*cp)
4100  {
4101  case 'p':
4102  fprintf (stderr, "typeset: Option (%c) not supported\n",
4103  *cp);
4104  break;
4105 
4106  case 'H':
4107  *CurrentFlags |= STATUS_CONVERT_MSDOS;
4108  break;
4109 
4110  case 'f': /* Function only */
4111  f_flag = TRUE;
4112  break;
4113 
4114  case 'l':
4115  *CurrentFlags |= STATUS_LOWER_CASE;
4116  break;
4117 
4118  case 'r':
4119  *CurrentFlags |= STATUS_READONLY;
4120  break;
4121 
4122  case 't':
4123  *CurrentFlags |= STATUS_TAGGED;
4124  break;
4125 
4126  case 'u':
4127  *CurrentFlags |= STATUS_UPPER_CASE;
4128  break;
4129 
4130  case 'x':
4131  *CurrentFlags |= STATUS_EXPORT;
4132  break;
4133 
4134  case 'L':
4135  case 'R':
4136  case 'Z':
4137  case 'i':
4138  {
4139  switch (c_opt = *cp)
4140  {
4141  case 'L':
4142  *CurrentFlags |= STATUS_LEFT_JUSTIFY;
4143  break;
4144 
4145  case 'R':
4146  *CurrentFlags |= STATUS_RIGHT_JUSTIFY;
4147  break;
4148 
4149  case 'Z':
4150  *CurrentFlags |= STATUS_ZERO_FILL;
4151  break;
4152 
4153  case 'i':
4154  *CurrentFlags |= STATUS_INTEGER;
4155  break;
4156  }
4157 
4158 /* Only set width on on */
4159 
4160  if (CurrentFlags != &TypesetValues.Flags_On)
4161  break;
4162 
4163 /* Check for following numeric */
4164 
4165  if (isdigit (*(cp + 1)))
4166  tmp = (int)strtol (cp + 1, &cp, 10);
4167 
4168  else if ((*(cp + 1) == 0) &&
4169  (OptionIndex + 1 < argc) &&
4170  isdigit (*argv[OptionIndex + 1]))
4171  tmp = (int)strtol (argv[++OptionIndex], &cp, 10);
4172 
4173  else
4174  break;
4175 
4176 /* Check for invalid numeric */
4177 
4178  if (!tmp || *(cp--))
4179  return UsageError (TypeSetUsage);
4180 
4181 /* Width or base */
4182 
4183  if (c_opt == 'i')
4184  TypesetValues.Base = tmp;
4185 
4186  else
4187  TypesetValues.Width = tmp;
4188 
4189  break;
4190  }
4191 
4192  default:
4193  return UsageError (TypeSetUsage);
4194  }
4195  }
4196 
4197  ++OptionIndex;
4198  }
4199 
4200 /* Check for f flag - function processing. */
4201 
4202  if (f_flag)
4203  {
4205  ~(STATUS_TAGGED | STATUS_EXPORT)) ||
4207  return PrintWarningMessage ("typeset: Only -xt allowed with -f");
4208 
4209  for (tmp = OptionIndex; tmp < argc; tmp++)
4210  ReturnValue |= HandleFunction (argv[tmp]);
4211 
4212  return ReturnValue;
4213  }
4214 
4215 /* Process variable assignments */
4216 
4217  return TypesetVariables (&argv[OptionIndex]);
4218 }
4219 
4220 static int F_LOCAL TypesetVariables (char **argv)
4221 {
4222  bool PrintValue = C2bool (TypesetValues.Flags_Off);
4223  VariableList *vp;
4224  char *CurrentName;
4225  char *NewValue;
4226  char *OldValue;
4227  int OldStatus;
4228  long NValue;
4229  char *xp;
4230  int Retval = 0;
4231  unsigned int Mask;
4232  long Index;
4233 
4235  Mask = 0xffff;
4236 
4237 /* Switch off any appropriate flags */
4238 
4241 
4244 
4247 
4250 
4251 /* If no arguments, print all values matching the mask */
4252 
4253  if (*argv == NOWORD)
4254  return ListAllVariables (Mask, PrintValue);
4255 
4256 /* Process each argument. If no flags, print it */
4257 
4258  while ((CurrentName = *(argv++)) != NOWORD)
4259  {
4260  if (!GetVariableName (CurrentName, &Index, &NewValue, (bool *)NULL))
4261  {
4263  return 1;
4264  }
4265 
4266 /* Convert the = to a null so we get the name and value */
4267 
4268  if (*NewValue)
4269  *(NewValue++) = 0;
4270 
4271  else
4272  NewValue = (char *)NULL;
4273 
4274 /* If valid - update, otherwise print a message */
4275 
4276  if ((Mask != 0xffff) || (NewValue != (char *)NULL))
4277  {
4278 
4279 /* Get the original value */
4280 
4281  vp = LookUpVariable (CurrentName, (int)Index, TRUE);
4282  OldStatus = vp->status;
4283  OldValue = GetVariableArrayAsString (CurrentName, (int)Index,
4284  FALSE);
4285 
4286 /* Update status */
4287 
4288  vp->status &= ~(TypesetValues.Flags_Off);
4289  vp->status |= TypesetValues.Flags_On;
4290 
4291  if ((CurrentFunction != (FunctionList *)NULL) &&
4292  (!(vp->status & STATUS_GLOBAL)))
4293  vp->status |= STATUS_LOCAL;
4294 
4295  if (Index)
4296  vp->status &= ~(STATUS_EXPORT);
4297 
4298 /* Set up a new integer value. If the variable was not an integer
4299  * originally and there is an error, unset it
4300  */
4301 
4302  xp = (NewValue != (char *)NULL) ? NewValue : OldValue;
4303 
4304  if (vp->status & STATUS_INTEGER)
4305  {
4306  if (ValidMathsExpression (xp, &NValue))
4307  {
4308  Retval = PrintWarningMessage (LIT_Emsg, "bad numeric value",
4309  CurrentName, xp);
4310 
4311  if (!(OldStatus & STATUS_INTEGER))
4312  UnSetVariable (CurrentName, (int)Index, FALSE);
4313 
4314  continue;
4315  }
4316 
4317  else if (OldStatus & STATUS_READONLY)
4318  {
4319  Retval = PrintWarningMessage (LIT_2Strings, vp->name,
4320  LIT_IsReadonly);
4321  continue;
4322  }
4323 
4324 /* Save the new integer value and set up base etc */
4325 
4326  vp->nvalue = NValue;
4327 
4328  if (!vp->base)
4329  vp->base = (LastNumberBase != -1) ? LastNumberBase : 10;
4330 
4331  if (TypesetValues.Base)
4332  vp->base = TypesetValues.Base;
4333 
4334  if (vp->value != null)
4335  ReleaseMemoryCell ((void *)vp->value);
4336 
4337  vp->value = null;
4338  }
4339 
4340 /* String - update if appropriate, both the value and the width */
4341 
4342  else if ((OldStatus & STATUS_READONLY) ||
4343  (!(vp->status & STATUS_READONLY)))
4344  SetVariableArrayFromString (CurrentName, (int)Index, xp);
4345 
4346 /* New status is readonly - allow set, then stop them */
4347 
4348  else
4349  {
4350  vp->status &= ~~STATUS_READONLY;
4351  SetVariableArrayFromString (CurrentName, (int)Index, xp);
4352  vp->status |= STATUS_READONLY;
4353  }
4354 
4355  if (TypesetValues.Width)
4356  vp->width = TypesetValues.Width;
4357  }
4358 
4359 /* Print if appropriate */
4360 
4361  else
4362  PrintEntry (LookUpVariable (CurrentName, (int)Index, FALSE),
4363  PrintValue, Mask);
4364  }
4365 
4366  return Retval;
4367 }
4368 
4370  bool PrintValue,
4371  unsigned int Mask)
4372 {
4373  unsigned int Flags = vp->status & Mask;
4374 
4375  if (vp->status & STATUS_NOEXISTANT)
4376  return;
4377 
4378  if (Flags & STATUS_INTEGER)
4379  printf ("integer ");
4380 
4381  if (Flags & STATUS_LEFT_JUSTIFY)
4382  printf ("left justified %d ", vp->width);
4383 
4385  printf ("right justified %d ", vp->width);
4386 
4387  if (Flags & STATUS_ZERO_FILL)
4388  printf ("zero filled %d ", vp->width);
4389 
4391  printf ("MS-DOS Format ");
4392 
4393  if (Flags & STATUS_LOWER_CASE)
4394  printf ("lowercase ");
4395 
4396  if (Flags & STATUS_UPPER_CASE)
4397  printf ("uppercase ");
4398 
4399  if (Flags & STATUS_READONLY)
4400  printf ("readonly ");
4401 
4402  if (Flags & STATUS_TAGGED)
4403  printf ("tagged ");
4404 
4405  if (Flags & STATUS_EXPORT)
4406  printf ("exported ");
4407 
4408 /* Print the value */
4409 
4410  foputs (vp->name);
4411 
4412  if (vp->index || CountVariableArraySize (vp->name) > 1)
4413  printf (LIT_BNumber, vp->index);
4414 
4415  if (PrintValue)
4416  printf ("=%s", GetVariableArrayAsString (vp->name, vp->index, TRUE));
4417 
4418  fputchar (CHAR_NEW_LINE);
4419 }
4420 
4421 /*
4422  * Handle typeset -f
4423  */
4424 
4425 static int F_LOCAL HandleFunction (char *name)
4426 {
4427  FunctionList *fop;
4428 
4429  if (strchr (name, CHAR_ASSIGN) != (char *)NULL)
4430  return PrintWarningMessage ("typeset: cannot assign to functions");
4431 
4432  if ((fop = LookUpFunction (name, FALSE)) == (FunctionList *)NULL)
4433  return PrintWarningMessage ("typeset: function %s does not exist",
4434  name);
4435 
4437  return 0;
4438 }
4439 
4440 /*
4441  * Modified version of getopt for shell
4442  */
4443 
4444 void ResetGetOptions (void)
4445 {
4446  OptionIndex = 1; /* Reset the optind flag */
4447  GetOptionPosition = 1; /* Current position */
4448 }
4449 
4450 int GetOptions (int argc, /* Argument count */
4451  char **argv, /* Argument array */
4452  char *optstring, /* Options string */
4453  int flags) /* Control flags */
4454 {
4455  int cur_option; /* Current option */
4456  char *cp; /* Character pointer */
4457 
4458  BadOptionValue = 0;
4459 
4460  if (GetOptionPosition == 1)
4461  {
4462 
4463 /* Special for doecho */
4464 
4465  if (flags & GETOPT_PRINT)
4466  return EOF;
4467 
4468 /* Check for out of range, correct start character and not single */
4469 
4470  if ((OptionIndex >= argc) ||
4471  (!(((OptionStart = *argv[OptionIndex]) == '-') ||
4472  (((flags & GETOPT_PLUS) && (OptionStart == '+'))))) ||
4473  (!argv[OptionIndex][1]))
4474  return EOF;
4475 
4477  return EOF;
4478  }
4479 
4480 /* Get the current character from the current argument vector */
4481 
4482  cur_option = argv[OptionIndex][GetOptionPosition];
4483 
4484 /* Validate it */
4485 
4486  if ((cur_option == ':') ||
4487  ((cp = strchr (optstring, cur_option)) == (char *)NULL))
4488  {
4489  if (flags & GETOPT_MESSAGE)
4490  PrintWarningMessage ("%s: illegal option -- %c", argv[0],
4491  cur_option);
4492 
4493 /* Save the bad option value and move to the next offset */
4494 
4495  BadOptionValue = cur_option;
4496 
4498  {
4499  OptionIndex++;
4500  GetOptionPosition = 1;
4501  }
4502 
4503  return '?';
4504  }
4505 
4506 /* Parameters following ? */
4507 
4508  OptionArgument = (char *)NULL;
4509 
4510  if (*(++cp) == ':')
4511  {
4514 
4515  else if (++OptionIndex >= argc)
4516  {
4518  PrintWarningMessage ("%s: option (%c) requires an argument",
4519  argv[0], cur_option);
4520 
4521  BadOptionValue = cur_option;
4522  OptionArgument = (char *)-1;
4523  GetOptionPosition = 1;
4524  return '?';
4525  }
4526 
4527  else
4529 
4530  GetOptionPosition = 1;
4531  }
4532 
4533  else if (!argv[OptionIndex][++GetOptionPosition])
4534  {
4535  GetOptionPosition = 1;
4536  OptionIndex++;
4537  }
4538 
4539  return cur_option;
4540 }
4541 
4542 
4543 /*
4544  * Kill the specified processes
4545  */
4546 
4547 #if (OS_TYPE != OS_DOS)
4548 static struct KillSignalList {
4549  char *Name;
4550  int SigVal;
4551 } KillSignalList [] = {
4552  {"term", -1 },
4553 # if (OS_TYPE == OS_OS2)
4554  {"usr1", PFLG_A },
4555  {"usr2", PFLG_B },
4556  {"usr3", PFLG_C },
4557 # elif (OS_TYPE == OS_NT)
4558  {"break", CTRL_BREAK_EVENT},
4559  {"int", CTRL_C_EVENT},
4560 # endif
4561 };
4562 
4563 #define MAX_KILL_SIGNALS ARRAY_SIZE (KillSignalList)
4564 
4565 static int dokill (int argc, char **argv)
4566 {
4567  int i;
4568  int Sigv = -1;
4569  char *cp;
4570  PID pidProcess;
4571  long value;
4572 # if (OS_TYPE == OS_NT)
4573  HANDLE hp;
4574 # elif (OS_TYPE == OS_OS2)
4575  USHORT rc;
4576 # endif
4577 
4578  if (argc < 2)
4579  return UsageError (KillUsage);
4580 
4581 /* List signals ? */
4582 
4583  if (!strcmp (argv[1], "-l"))
4584  {
4585  for (i = 0; i < MAX_KILL_SIGNALS; ++i)
4586  puts (KillSignalList[i].Name);
4587 
4588  return 0;
4589  }
4590 
4591 /* Look up signal name */
4592 
4593  if (**(++argv) == '-')
4594  {
4595  cp = &argv[0][1];
4596 
4597  for (i = 0; i < MAX_KILL_SIGNALS; ++i)
4598  {
4599  if (!stricmp (KillSignalList[i].Name, cp))
4600  break;
4601  }
4602 
4603  if (i == MAX_KILL_SIGNALS)
4604  return PrintWarningMessage ("kill: bad signal name (%s)", cp);
4605 
4606  Sigv = KillSignalList[i].SigVal;
4607 
4608  if (*(++argv) == NOWORD)
4609  return UsageError (KillUsage);
4610  }
4611 
4612 /* Kill the processes */
4613 
4614  while (*argv != NOWORD)
4615  {
4616 
4617 /* Find the PID */
4618 
4619  if (((**argv == CHAR_JOBID) && !ConvertJobToPid ((*argv) + 1, &pidProcess)) ||
4620  ((**argv != CHAR_JOBID) && !ConvertNumericValue (*argv, &value, 0)))
4621  return PrintWarningMessage ("kill: bad process/job id (%s)",
4622  *argv);
4623 
4624 /* If Process ID, its in value */
4625 
4626  if (**argv != CHAR_JOBID)
4627  pidProcess = (PID)value;
4628 
4629 /* Send the signal */
4630 
4631 # if (OS_TYPE == OS_OS2)
4632  if (Sigv == -1)
4633  {
4634 # if (OS_SIZE == OS_16)
4635  rc = DosKillProcess (DKP_PROCESSTREE, pidProcess);
4636 # else
4637  if ((rc = DosKillProcess (DKP_PROCESSTREE, pidProcess)))
4638  rc = (USHORT)DosSendSignalException (pidProcess,
4639  XCPT_SIGNAL_BREAK);
4640 # endif
4641  }
4642 
4643  else
4644  rc = Dos32FlagProcess (pidProcess, FLGP_SUBTREE, Sigv, 0);
4645 
4646  if (rc)
4647  return PrintWarningMessage ("kill: Cannot signal process %s\n%s",
4648  *argv,
4649  GetOSSystemErrorMessage (rc));
4650 # elif (OS_TYPE == OS_NT)
4651 
4652 /* Check for Ctl C or Break */
4653 
4654  if ((Sigv == CTRL_BREAK_EVENT) || (Sigv == CTRL_C_EVENT))
4655  {
4656  if (!GenerateConsoleCtrlEvent (Sigv, pidProcess))
4657  return PrintWarningMessage ("kill: Cannot kill process %s\n%s",
4658  *argv,
4659  GetOSSystemErrorMessage (GetLastError ()));
4660 
4661  }
4662 
4663 /* Open the process and terminate it */
4664 
4665  else if ((hp = OpenProcess (PROCESS_ALL_ACCESS, TRUE,
4666  pidProcess)) == NULL)
4667  return PrintWarningMessage ("kill: Cannot access process %s\n%s",
4668  *argv,
4669  GetOSSystemErrorMessage (GetLastError ()));
4670  else if (!TerminateProcess (hp, 1))
4671  {
4672  PrintWarningMessage ("kill: Cannot kill process %s\n%s", *argv,
4673  GetOSSystemErrorMessage (GetLastError ()));
4674  CloseHandle (hp);
4675  return 1;
4676  }
4677 
4678  CloseHandle (hp);
4679 # endif
4680 
4681  argv++;
4682  }
4683 
4684  return 0;
4685 }
4686 
4687 /*
4688  * Wait for process to end
4689  */
4690 
4691 static int dowait (int argc, char **argv)
4692 {
4693  PID pidProcess;
4694  int TermStat;
4695 # if (OS_TYPE != OS_NT)
4696  int ReturnValue;
4697 # endif
4698  long value;
4699 
4700 /* Check usage */
4701 
4702  if (argc > 2)
4703  return UsageError ("wait [ job ]");
4704 
4705 /* Wait for all jobs ? */
4706 
4707  if (argc < 2)
4708  {
4709 # if (OS_TYPE == OS_NT)
4710  return PrintWarningMessage ("wait: any job not supported on NT");
4711 # else
4712  TermStat = -1;
4713 
4714 /* Yes - wait until wait returns an error */
4715 
4716  while ((pidProcess = wait (&ReturnValue)) != -1)
4717  {
4718  DeleteJob (pidProcess);
4719  TermStat = ReturnValue;
4720  }
4721 # endif
4722  }
4723 
4724 /* Wait for a specific process. Job or PID? */
4725 
4726  else
4727  {
4728 
4729 /* Move to the ID */
4730 
4731  argv++;
4732 
4733 /* Find the PID */
4734 
4735  if (((**argv == CHAR_JOBID) &&
4736  !ConvertJobToPid ((*argv) + 1, &pidProcess)) ||
4737  ((**argv != CHAR_JOBID) &&
4738  !ConvertNumericValue (*argv, &value, 0)))
4739  return PrintWarningMessage ("wait: bad process/job id (%s)",
4740  *argv);
4741 
4742 /* If Process ID, its in value */
4743 
4744  if (**argv != CHAR_JOBID)
4745  pidProcess = (PID)value;
4746 
4747 /* Wait for the specified process */
4748 
4749 # if (OS_TYPE == OS_UNIX)
4750  fputs ("UNIX: Wait for process NI\n", stderr);
4751 
4752 # else
4753  if (cwait (&TermStat, pidProcess, WAIT_GRANDCHILD) == -1)
4754  return PrintWarningMessage ("wait: Process id (%s) not active",
4755  *argv);
4756 # endif
4757 
4758  DeleteJob (pidProcess);
4759  }
4760 
4761 /* Convert termination status to return code */
4762 
4763  if (TermStat == -1)
4764  return -1;
4765 
4766  else if (TermStat & 0x00ff)
4767  return TermStat & 0x00ff;
4768 
4769  else
4770  return (TermStat >> 8) & 0x00ff;
4771 }
4772 
4773 /*
4774  * Print the job info
4775  */
4776 
4777 static int dojobs (int argc, char **argv)
4778 {
4779  bool ListMode = FALSE;
4780  bool ListTree = FALSE;
4781  pid_t p = getpid ();
4782 # if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_UNIX)
4783  long value;
4784 # endif
4785  int i;
4786 
4787 /* List signals ? */
4788 
4789  ResetGetOptions (); /* Reset GetOptions */
4790 
4791  while ((i = GetOptions (argc, argv, "plP:", 0)) != EOF)
4792  {
4793  switch (i)
4794  {
4795  case 'l':
4796  ListMode = TRUE;
4797  break;
4798 
4799 # if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_UNIX)
4800  case 'p':
4801  ListTree = TRUE;
4802  break;
4803 
4804  case 'P':
4805  ListTree = TRUE;
4806 
4808  return PrintWarningMessage ("jobs: bad process id (%s)",
4809  OptionArgument);
4810 
4811  p = (pid_t)value;
4812 
4813  break;
4814 # endif
4815 
4816  default:
4817  return UsageError (JobUsage);
4818  }
4819  }
4820 
4821 # if (OS_TYPE == OS_OS2) || (OS_TYPE == OS_UNIX)
4822  if (ListTree)
4823  return PrintProcessTree (p);
4824 # endif
4825 
4826 /* Look up job name */
4827 
4828  return PrintJobs (ListMode);
4829 }
4830 #endif
4831 
4832 /*
4833  * Print the option settings
4834  */
4835 
4836 static int F_LOCAL PrintOptionSettings (void)
4837 {
4838  int i;
4839 
4840  puts ("Current option settings:");
4841 
4842  for (i = 0; SetOptions[i].OptionName != (char *)NULL; i++)
4843  printf ("%-16s%s\n", SetOptions[i].OptionName,
4844  TestOptionValue (SetOptions[i].OptionName, FALSE)
4845  ? "on" : "off");
4846  return 0;
4847 }
4848 
4849 /*
4850  * Change option value
4851  */
4852 
4853 static bool F_LOCAL ChangeOptionValue (char *value, bool set)
4854 {
4856 
4857  if (entry == (struct SetOptions *)NULL)
4858  return FALSE;
4859 
4860  else if (entry->HasOptionValue)
4861  SetClearFlag (entry->FlagValue, set);
4862 
4863 /* If one of the Editor flags, disable all editor flags first */
4864 
4865  else if (entry->FlagValue == FLAGS_VERIFY_SWITCH)
4866  SetVerifyStatus (set);
4867 
4868 #ifdef FLAGS_BREAK_SWITCH
4869  else if (entry->FlagValue == FLAGS_BREAK_SWITCH)
4870  SetBreakStatus (set);
4871 #endif
4872 
4873 #ifdef FLAGS_SET_OS2
4874  else if (entry->FlagValue == FLAGS_SET_OS2)
4875  BaseOS = (set) ? BASE_OS_OS2 : BASE_OS_DOS;
4876 #endif
4877 
4878 #ifdef FLAGS_SET_NT
4879  else if (entry->FlagValue == FLAGS_SET_NT)
4880  BaseOS = (set) ? BASE_OS_NT : BASE_OS_DOS;
4881 #endif
4882 
4883 /* Change the editor flags */
4884 
4885  else if (set)
4886  {
4887  if (entry->FlagValue & FLAGS_EDITORS)
4889 
4890  ShellGlobalFlags |= entry->FlagValue;
4891  }
4892 
4893  else
4894  ShellGlobalFlags &= ~(entry->FlagValue);
4895 
4896 
4897  return TRUE;
4898 }
4899 
4900 /*
4901  * Update shell switches
4902  */
4903 
4904 static void F_LOCAL SetClearFlag (int Flag, bool set)
4905 {
4906  if (set)
4907  FL_SET (Flag);
4908 
4909  else
4910  FL_CLEAR (Flag);
4911 }
4912 
4913 /*
4914  * Test an option
4915  */
4916 
4917 static int F_LOCAL TestOptionValue (char *value, bool AllowJump)
4918 {
4920 
4921  if (entry == (struct SetOptions *)NULL)
4922  {
4923  PrintWarningMessage ("%s: unknown option - %s", TestProgram, value);
4924 
4925  if (AllowJump)
4926  longjmp (TestErrorReturn, 1);
4927 
4928  return 0;
4929  }
4930 
4931  else if (entry->FlagValue == FLAGS_VERIFY_SWITCH)
4932  {
4933 #if (OS_TYPE == OS_OS2)
4934  BOOL fVerifyOn;
4935 
4936  DosQVerify (&fVerifyOn);
4937  return fVerifyOn;
4938 
4939 #elif (OS_TYPE != OS_DOS)
4940 
4941  return FALSE;
4942 
4943 #elif (OS_TYPE == OS_DOS)
4944 
4945  union REGS r;
4946 
4947  r.x.REG_AX = 0x5400;
4948  DosInterrupt (&r, &r);
4949  return r.h.al;
4950 #endif
4951  }
4952 
4953 #if (OS_TYPE == OS_DOS)
4954  else if (entry->FlagValue == FLAGS_BREAK_SWITCH)
4955  {
4956  union REGS r;
4957 
4958  r.x.REG_AX = 0x3300;
4959  DosInterrupt (&r, &r);
4960  return r.h.dl;
4961  }
4962 #endif
4963 
4964 #ifdef FLAGS_SET_OS2
4965  else if (entry->FlagValue == FLAGS_SET_OS2)
4966  return BaseOS == BASE_OS_OS2;
4967 #endif
4968 
4969 #ifdef FLAGS_SET_NT
4970  else if (entry->FlagValue == FLAGS_SET_NT)
4971  return BaseOS == BASE_OS_NT;
4972 #endif
4973 
4974  else if (entry->HasOptionValue)
4975  return (FL_TEST (entry->FlagValue) != 0);
4976 
4977  return (ShellGlobalFlags & entry->FlagValue);
4978 }
4979 
4980 /*
4981  * Find an Option entry
4982  */
4983 
4985 {
4986  int i = 0;
4987  char *cp;
4988 
4989  while ((cp = SetOptions[i].OptionName) != (char *)NULL)
4990  {
4991  if (!strcmp (cp, value))
4992  return &SetOptions[i];
4993 
4994  ++i;
4995  }
4996 
4997  return (struct SetOptions *)NULL;
4998 }
4999 
5000 /*
5001  * Get Unit number
5002  */
5003 
5004 static int F_LOCAL GetUnitNumber (char *prog)
5005 {
5006  int Unit;
5007 
5008  if (((Unit = GetNumericValue (OptionArgument)) < 0) || (Unit > 9))
5009  {
5011  return -1;
5012  }
5013 
5014  return Unit;
5015 }
5016 
5017 /*
5018  * fc function - fc [-e EditorName] [-nlr] [First [Last]]
5019  * fc -e - [Old=New] [Command]
5020  */
5021 
5022 static int dofc (int argc, char **argv)
5023 {
5024  char *Editor = GetVariableAsString (FCEditVariable, FALSE);
5025  bool n_flag = TRUE;
5026  bool l_flag = FALSE;
5027  bool r_flag = FALSE;
5028  int EventNumber[2];
5029  char *Temp;
5030  char *Change = (char *)NULL;
5031  char *Change1;
5032  char *NewBuffer;
5033  char *NewArg[3];
5034  int i;
5035  FILE *fp;
5036 
5037 /* Check status */
5038 
5039  if (!(InteractiveFlag && IS_TTY (0)))
5040  return PrintWarningMessage ("fc: only available in interactive mode");
5041 
5042  if ((NewBuffer = AllocateMemoryCell (LINE_MAX + 3)) == (char *)NULL)
5043  return doOutofMemory ("fc");
5044 
5045 /* Process options */
5046 
5047  ResetGetOptions (); /* Reset GetOptions */
5048 
5049  while ((i = GetOptions (argc, argv, "e:nlr", 0)) != EOF)
5050  {
5051  switch (i)
5052  {
5053  case 'e': /* editor name */
5054  Editor = OptionArgument;
5055  break;
5056 
5057  case 'n': n_flag = FALSE; break;
5058  case 'l': l_flag = TRUE; break;
5059  case 'r': r_flag = TRUE; break;
5060 
5061  default:
5062  return UsageError ("fc [ -e EditorName ] [ -nlr ] [ First [Last]]\n fc -e - [ Old=New ] [ Command ]");
5063  }
5064  }
5065 
5066  argv += OptionIndex;
5067  argc -= OptionIndex;
5068 
5069 /* Check for [old=new] */
5070 
5071  if (argc && ((Change1 = strchr (*argv, CHAR_ASSIGN)) != (char *)NULL))
5072  {
5073  Change = *(argv++);
5074  *(Change1++) = 0;
5075  --argc;
5076  }
5077 
5078  if (!l_flag)
5079  DeleteLastHistory ();
5080 
5081 /* Get the first and last event number */
5082 
5083  for (i = 0; i < 2; i++)
5084  {
5085  EventNumber[i] = 0;
5086 
5087  if (argc)
5088  {
5089  EventNumber[i] = (int)strtol (*argv, &Temp, 10);
5090 
5091  if (*Temp)
5092  EventNumber[i] = SearchHistory (*argv);
5093 
5094  else if (EventNumber[i] < 0)
5095  EventNumber[i] += GetLastHistoryEvent () - 1;
5096 
5097  if (EventNumber[i] <= 0)
5098  return PrintWarningMessage ("fc: event <%s> not found",
5099  *argv);
5100 
5101  argv++;
5102  --argc;
5103  }
5104  }
5105 
5106 /* Set up first and last values */
5107 
5108  i = GetLastHistoryEvent () - 1;
5109 
5110  if (!EventNumber[0])
5111  {
5112  if ((EventNumber[0] = (l_flag) ? i - 16 : i) <= 0)
5113  EventNumber[0] = 1;
5114  }
5115 
5116  if (!EventNumber[1])
5117  EventNumber[1] = (l_flag) ? i : EventNumber[0];
5118 
5119 /* If l - print */
5120 
5121  if (l_flag)
5122  fp = stdout;
5123 
5124  else if (Editor == null)
5125  return PrintWarningMessage ("fc: editor not defined");
5126 
5127  else if ((fp = FOpenFile ((Temp = GenerateTemporaryFileName ()),
5128  sOpenWriteBinaryMode)) == (FILE *)NULL)
5129  return PrintWarningMessage ("fc: cannot create %s", Temp);
5130 
5131 /* Process history */
5132 
5133  if (!l_flag)
5134  n_flag = FALSE;
5135 
5136  PrintHistory (r_flag, n_flag, EventNumber[0], EventNumber[1], fp);
5137 
5138  if (l_flag)
5139  return 0;
5140 
5141 /* Check that we found some history */
5142 
5143  if (!ftell (fp))
5144  l_flag = TRUE;
5145 
5146  if (fp != stdout)
5147  CloseFile (fp);
5148 
5149  if (l_flag)
5150  {
5151  unlink (Temp);
5152  return PrintWarningMessage ("fc: no matches");
5153  }
5154 
5155 /* Invoke the editor */
5156 
5157  if (strcmp (Editor, ShellOptionsVariable))
5158  {
5159  NewArg[0] = Editor;
5160  NewArg[1] = Temp;
5161  NewArg[2] = (char *)NULL;
5162 
5163  if (ExecuteACommand (NewArg, 0) == -1)
5164  {
5165  unlink (Temp);
5166  return 1;
5167  }
5168  }
5169 
5170 /* Now execute it */
5171 
5172  if ((i = S_open (TRUE, Temp, O_RMASK)) < 0)
5173  return PrintWarningMessage ("fc: cannot re-open edit file (%s)",
5174  Temp);
5175 
5176  argc = read (i, NewBuffer, LINE_MAX + 1);
5177  S_close (i, TRUE);
5178 
5179  if (argc <= 0)
5180  return (argc == 0) ? 0 : 1;
5181 
5182  else if (argc >= LINE_MAX - 1)
5183  return PrintWarningMessage (FCTooLong);
5184 
5185 /* Strip off trailing EOFs and EOLs */
5186 
5187  CleanUpBuffer (argc, NewBuffer, 0x1a);
5188 
5189 /* Check for substitution */
5190 
5191  if (Change == (char *)NULL)
5192  strcpy (ConsoleLineBuffer, NewBuffer);
5193 
5194  else
5195  {
5196  if ((Temp = strstr (NewBuffer, Change)) == (char *)NULL)
5197  return PrintWarningMessage ("fc: string not found");
5198 
5199  if ((i = strlen (NewBuffer) - strlen (Change) +
5200  strlen (Change1)) >= LINE_MAX - 2)
5201  return PrintWarningMessage (FCTooLong);
5202 
5203 /* Do the substitution */
5204 
5205  i = Temp - NewBuffer;
5206  strncpy (ConsoleLineBuffer, NewBuffer, i);
5207  strcpy (ConsoleLineBuffer + i, Change1);
5208  strcat (ConsoleLineBuffer, NewBuffer + strlen (Change) + i);
5209  }
5210 
5211  ReleaseMemoryCell ((void *)NewBuffer);
5212 
5213 /* Tell the user what we've done */
5214 
5215  puts (ConsoleLineBuffer);
5216 
5217 /* Flag the console driver not to read from the console, but use the
5218  * current contents of the ConsoleLineBuffer
5219  */
5220 
5222  return 0;
5223 }
5224 
5225 /*
5226  * Convert Job ID to process id
5227  */
5228 
5229 
5230 #if (OS_TYPE != OS_DOS)
5231 static bool F_LOCAL ConvertJobToPid (char *String, PID *pid)
5232 {
5233  long value;
5234  JobList *jp;
5235 
5236 /* If numeric value, look up the job number */
5237 
5238  if (ConvertNumericValue (String, &value, 0))
5239  jp = LookUpJob ((int)value);
5240 
5241  else
5242  jp = SearchForJob (String);
5243 
5244  if (jp == (JobList *)NULL)
5245  return FALSE;
5246 
5247  PreviousJob = CurrentJob;
5248  CurrentJob = jp->pid;
5249 
5250  *pid = jp->pid;
5251  return TRUE;
5252 }
5253 #endif
5254 
5255 /*
5256  * Missing OS2 2.x API
5257  */
5258 
5259 #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32)
5260 APIRET DosQFileMode (PSZ pszFName, PULONG pusAttr)
5261 {
5262  APIRET rc;
5263  FILESTATUS3 status;
5264 
5265  if ((rc = DosQueryPathInfo (pszFName, FIL_STANDARD, &status,
5266  sizeof (FILESTATUS3))) == 0)
5267  *pusAttr = status.attrFile;
5268 
5269  return rc;
5270 }
5271 #endif
5272 
5273 #if (OS_TYPE == OS_NT)
5274 int DosQFileMode (char *pszFName, DWORD *pusAttr)
5275 {
5276  *pusAttr = GetFileAttributes (pszFName);
5277  return (*pusAttr == 0xffffffff) ? -1 : 0;
5278 }
5279 #endif
5280 
5281 /*
5282  * A sort of version of times
5283  */
5284 
5285 #if (OS_TYPE == OS_OS2) && (OS_SIZE == OS_32) && !defined (__WATCOMC__)
5286 static int dotimes (int argc, char **argv)
5287 {
5288  return PrintTimes ();
5289 }
5290 #endif
5291 
5292 #if (OS_TYPE == OS_NT) || (OS_TYPE == OS_UNIX)
5293 static int dotimes (int argc, char **argv)
5294 {
5295  return PrintTimes ();
5296 }
5297 #endif
5298 
5299 /*
5300  * Convert logical path to physical path, skipping out SUBST drives
5301  */
5302 
5303 #if (OS_TYPE == OS_OS2)
5304 static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork)
5305 {
5306 # ifndef __EMX__
5307  char *op;
5308  char *res;
5309 
5310  if ((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
5311  return inpath;
5312 
5313  if ((res = _fullpath (op, inpath, PATH_MAX + 6)) != (char *)NULL)
5314  strcpy (inpath, op);
5315 
5316  PATH_TO_LOWER_CASE (inpath);
5318 # endif
5319 
5320  return PATH_TO_UNIX (inpath);
5321 }
5322 #endif
5323 
5324 /* NT Version */
5325 
5326 #if (OS_TYPE == OS_NT)
5327 static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork)
5328 {
5329  char *op;
5330  char *res;
5331 
5332  if ((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
5333  return inpath;
5334 
5335  if (!GetFullPathName (inpath, FFNAME_MAX, op, &res))
5336  strcpy (inpath, op);
5337 
5338  PATH_TO_LOWER_CASE (inpath);
5340  return PATH_TO_UNIX (inpath);
5341 }
5342 #endif
5343 
5344 /* DOS version */
5345 
5346 #if (OS_TYPE == OS_DOS)
5347 static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork)
5348 {
5349 # ifndef __EMX__
5350  char *op;
5351  union REGS r;
5352  struct SREGS sr;
5353 
5354  if ((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
5355  return inpath;
5356 
5357  PATH_TO_DOS (inpath);
5358 
5359  r.h.ah = 0x60;
5360  r.x.REG_SI = FP_OFF (inpath);
5361  sr.ds = FP_SEG (inpath);
5362 
5363  r.x.REG_DI = FP_OFF (op);
5364  sr.es = FP_SEG (op);
5365 
5366  DosExtendedInterrupt (&r, &r, &sr);
5367 
5368 /* If we are succesfully and this is not a networked drive, replace the
5369  * original path
5370  */
5371 
5372  if ((!(r.x.REG_CFLAGS & 1)) &&
5373  (AllowNetwork || (strncmp (op, "\\\\", 2) != 0)))
5374  {
5375  strlwr (strcpy (inpath, op));
5377  }
5378 # endif
5379 
5380  return PATH_TO_UNIX (inpath);
5381 }
5382 #endif
5383 
5384 /* UNIX Version */
5385 
5386 #if (OS_TYPE == OS_UNIX)
5387 static char * F_LOCAL GetPhysicalPath (char *inpath, bool AllowNetwork)
5388 {
5389 # ifdef S_IFLNK
5390  char *op;
5391  struct _stat s;
5392  char *res;
5393  int len;
5394 
5395  if (((op = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL) ||
5396  (lstat (inpath, &s) != 0) || (s.st_mode & S_IFMT) != S_IFLNK)
5397  return inpath;
5398 
5399  if ((len = readlink (inpath, op, FFNAME_MAX)) == -1)
5400  return inpath;
5401 
5402  op [len] = 0;
5403  strcpy (inpath, op);
5405 
5406 # endif
5407 
5408  return inpath;
5409 }
5410 #endif
5411 
5412 
5413 /*
5414  * Change the verify status. No NT or UNIX functionality.
5415  */
5416 
5417 static void F_LOCAL SetVerifyStatus (bool On)
5418 {
5419 #if (OS_TYPE == OS_OS2)
5420  DosSetVerify (On);
5421 #elif (OS_TYPE == OS_DOS)
5422  union REGS r;
5423 
5424  r.x.REG_AX = On ? 0x2e01 : 0x2e00;
5425  r.x.REG_DX = 0;
5426  DosInterrupt (&r, &r);
5427 #endif
5428 }
5429 
5430 /*
5431  * Change the break status
5432  */
5433 
5434 #if (OS_TYPE == OS_DOS)
5435 static void F_LOCAL SetBreakStatus (bool On)
5436 {
5437  union REGS r;
5438 
5439  r.x.REG_AX = 0x3301;
5440  r.h.dl = (unsigned char)(On ? 1 : 0);
5441  DosInterrupt (&r, &r);
5442 }
5443 #endif
5444 
5445 /*
5446  * Bind an EMACS keystroke to a editing command
5447  */
5448 
5449 #if defined (FLAGS_EMACS) || defined (FLAGS_GMACS)
5450 static int dobind (int argc, char **argv)
5451 {
5452  bool macro = FALSE;
5453  char *cp;
5454  int c;
5455 
5456  ResetGetOptions (); /* Reset GetOptions */
5457 
5458  while ((c = GetOptions (argc, argv, "m", 0)) != EOF)
5459  {
5460  if (c == 'm')
5461  macro = TRUE;
5462 
5463  else
5464  return UsageError ("bind [ -m ] [ keystroke=edit command ] ...");
5465  }
5466 
5467  argv += OptionIndex;
5468 
5469 /* List All ? */
5470 
5471  if (*argv == (char *)NULL) /* list all */
5472  return BindKeyStroke ((char *)NULL, (char *)NULL, FALSE);
5473 
5474 /* Otherwise, bind */
5475 
5476  c = 0;
5477 
5478  while (*argv != (char *)NULL)
5479  {
5480  if ((cp = strchr (*argv, '=')) != (char *)NULL)
5481  *(cp++) = 0;
5482 
5483  c |= BindKeyStroke (*(argv++), cp, macro);
5484  }
5485 
5486  return c;
5487 }
5488 #endif
5489 
5490 static int doshellinfo (int argc, char **argv)
5491 {
5492  static char *OSName [] = { LIT_dos, "Windows", "OS/2", "Win NT",
5493  "UNIX"};
5494 
5495  printf ("Global Flags = 0x%.4x\n", ShellGlobalFlags);
5496  printf ("Max # of File descriptors = %d\n", MaxNumberofFDs);
5497  printf ("Disabled Variables = 0x%.4x\n", DisabledVariables);
5498  printf ("Execute stack depth = %d\n", Execute_stack_depth);
5499  printf ("Memory stack depth = %d\n", MemoryAreaLevel);
5500  printf ("IO stack depth = %d\n", NSave_IO_E);
5501  printf ("Subshell stack depth = %d\n", NSubShells);
5502  printf ("Underlying OS = <%s>\n", OSName[BaseOS]);
5503  return 0;
5504 }
5505 
5506 /*
5507  * Flag OS/2 process for _EMX_
5508  */
5509 
5510 #if defined (__EMX__) && (OS_TYPE == OS_OS2)
5511 USHORT Dos32FlagProcess (PID pid, USHORT fScope, USHORT usFlagNum,
5512  USHORT usFlagArg)
5513 {
5514  return ((USHORT)
5515  (_THUNK_PROLOG (2 + 2 + 2 + 2);
5516  _THUNK_SHORT (pid);
5517  _THUNK_SHORT (fScope);
5518  _THUNK_SHORT (usFlagNum);
5519  _THUNK_SHORT (usFlagArg);
5520  _THUNK_CALL (Dos16FlagProcess)));
5521 }
5522 #endif
5523 
5524 /*
5525  * Check file access
5526  */
5527 
5528 static int F_LOCAL CheckFAccess (char *Name, int mode)
5529 {
5530  int fdn = CheckForFD (CheckDOSFileName (Name));
5531 #if (OS_TYPE == OS_UNIX)
5532  struct _stat s;
5533  mode_t tm = 0;
5534 #else
5535  OSCALL_PARAM usAttr;
5536 #endif
5537 
5538  if (fdn < 0)
5539  return S_access (Name, mode);
5540 
5541 #if (OS_TYPE == OS_UNIX)
5542  if (fstat (fdn, &s) < 0)
5543  return 0;
5544 
5545  if (mode == 0)
5546  return 1;
5547 
5548 /* Find which bits to check */
5549 
5550  if (mode & W_OK)
5551  tm |= S_IWUSR;
5552 
5553  if (mode &