Subversion Repositories DevTools

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
227 dpurdie 1
/*
2
 * MS-DOS SHELL - Parse Tree Executor
3
 *
4
 * MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited and Ch/arles Forsyth
5
 *
6
 * This code is based on (in part) the shell program written by Charles
7
 * Forsyth and is subject to the following copyright restrictions:
8
 *
9
 * 1.  Redistribution and use in source and binary forms are permitted
10
 *     provided that the above copyright notice is duplicated in the
11
 *     source form and the copyright notice in file sh6.c is displayed
12
 *     on entry to the program.
13
 *
14
 * 2.  The sources (or parts thereof) or objects generated from the sources
15
 *     (or parts of sources) cannot be sold under any circumstances.
16
 *
17
 * When parts of the original 2.1 shell were replaced by the Lexical
18
 * Analsyer written by Simon J. Gerraty (for his Public Domain Korn Shell,
19
 * which is also based on Charles Forsyth original idea), a number of changes
20
 * were made to reflect the changes Simon made to the Parse output tree.  Some
21
 * parts of this code in this module are based on the algorithms/ideas that
22
 * he incorporated into his shell, in particular the TCASE and TTIME
23
 * functionality in ExecuteParseTree, and the PrintAList function.
24
 *
25
 *    $Header: /cvsroot/device/DEVL/UTILS/SH/Sh3.c,v 1.3 2004/11/04 07:10:08 ayoung Exp $
26
 *
27
 *    $Log: Sh3.c,v $
28
 *    Revision 1.3  2004/11/04 07:10:08  ayoung
29
 *    additional EXEC debug, ProcessSpaceInParamaters for generated indirect files
30
 *
31
 *    Revision 1.2  2004/05/10 09:30:07  ayoung
32
 *    improved Path/PATH handling
33
 *    Quote CreateProcess arg0 if embedded spaces are  encountered
34
 *    Native NT exec dont need to check command line length
35
 *    Warning when '@' within redirect list
36
 *    DEBUG_EXEC option, split from PRINT_EXEC option and improved
37
 *
38
 *    Revision 1.1  2002/08/02 06:49:34  adamy
39
 *    imported (reference only)
40
 *
41
 *    Revision 1.3  2002/02/05 05:18:33  ayoung
42
 *    removed functionality that cause drive scanning during application execution
43
 *
44
 *    Revision 1.2  2001/08/24 06:02:05  ayoung
45
 *    WIN32 'sh' return code processing corrected, which previously always returned 0.
46
 *
47
 *    Revision 1.1  2001/07/20 05:55:44  ayoung
48
 *    WIN32 support
49
 *
50
 *    Revision 1.3  2000/10/05 09:26:53  adamy
51
 *    no message
52
 *
53
 *    Revision 1.2  2000/09/27 08:33:35  adamy
54
 *    Added EXTENDED_LINE cache and use __systeml_mode during DOS builds.
55
 *
56
 *    Revision 1.1.1.1  1999/12/02 01:11:12  gordonh
57
 *    UTIL
58
 *
59
 *      Revision 2.17  1994/08/25  20:49:11  istewart
60
 *      MS Shell 2.3 Release
61
 *
62
 *      Revision 2.16  1994/02/23  09:23:38  istewart
63
 *      Beta 233 updates
64
 *
65
 *      Revision 2.15  1994/02/01  10:25:20  istewart
66
 *      Release 2.3 Beta 2, including first NT port
67
 *
68
 *      Revision 2.14  1994/01/20  14:51:43  istewart
69
 *      Release 2.3 Beta 1
70
 *
71
 *      Revision 2.13  1994/01/11  17:55:25  istewart
72
 *      Release 2.3 Beta 0 patches
73
 *
74
 *      Revision 2.12  1993/11/09  10:39:49  istewart
75
 *      Beta 226 checking
76
 *
77
 *      Revision 2.11  1993/08/25  16:03:57  istewart
78
 *      Beta 225 - see Notes file
79
 *
80
 *      Revision 2.10  1993/07/02  10:21:35  istewart
81
 *      224 Beta fixes
82
 *
83
 *      Revision 2.9  1993/06/14  11:00:12  istewart
84
 *      More changes for 223 beta
85
 *
86
 *      Revision 2.8  1993/06/02  09:52:35  istewart
87
 *      Beta 223 Updates - see Notes file
88
 *
89
 *      Revision 2.7  1993/02/16  16:03:15  istewart
90
 *      Beta 2.22 Release
91
 *
92
 *      Revision 2.6  1993/01/26  18:35:09  istewart
93
 *      Release 2.2 beta 0
94
 *
95
 *      Revision 2.5  1992/12/14  10:54:56  istewart
96
 *      BETA 215 Fixes and 2.1 Release
97
 *
98
 *      Revision 2.4  1992/11/06  10:03:44  istewart
99
 *      214 Beta test updates
100
 *
101
 *      Revision 2.3  1992/09/03  18:54:45  istewart
102
 *      Beta 213 Updates
103
 *
104
 *      Revision 2.2  1992/07/16  14:33:34  istewart
105
 *      Beta 212 Baseline
106
 *
107
 *      Revision 2.1  1992/07/10  10:52:48  istewart
108
 *      211 Beta updates
109
 *
110
 *      Revision 2.0  1992/05/07  20:31:39  Ian_Stewartson
111
 *      MS-Shell 2.0 Baseline release
112
 *
113
 */
114
 
115
#include <sys/types.h>
116
#include <sys/stat.h>
117
#include <stdio.h>
118
#include <signal.h>
119
#include <errno.h>
120
#include <setjmp.h>
121
#include <ctype.h>
122
#include <string.h>
123
#include <unistd.h>
124
#include <stdlib.h>
125
#include <fcntl.h>
126
#include <limits.h>
127
#include <dirent.h>
128
#include <ctype.h>
129
#include <time.h>
130
#include "sh.h"
131
#if (OS_TYPE == OS_DOS)
132
#define SYSTEML_MODE
133
#include "libdos.h"
134
#endif
135
 
136
/*
137
 * Save struct for Parameters ($1, $2 etc)
138
 */
139
typedef struct SaveParameters {
140
    char        **Array;                /* The parameters               */
141
    int         Count;                  /* Number of them               */
142
} SaveParameters;
143
 
144
/* static Function and string declarations */
145
 
146
static int F_LOCAL      ForkAndExecute (C_Op *, int, int, int, char **, char **,
147
                                        char **);
148
static bool F_LOCAL     SetUpIOHandlers (IO_Actions *, int, int);
149
static bool F_LOCAL     WriteToExtendedFile (FILE *, char *);
150
static void F_LOCAL     EchoCurrentCommand (char **);
151
static int F_LOCAL      ExecuteProgram (char *, char **, char **, int);
152
static void F_LOCAL     SaveNumericParameters (char **, SaveParameters *);
153
static void F_LOCAL     RestoreTheParameters (SaveParameters *);
154
static bool F_LOCAL     ExecuteFunction (char **, int *, bool);
155
static void F_LOCAL     PrintLoadError (char *);
156
static void F_LOCAL     TrackAllCommands (char *, char *);
157
static int F_LOCAL      ExtensionType (char *);
158
static char * F_LOCAL   FindFileAndExtension (char *, char *, char **);
159
static bool F_LOCAL     GetApplicationType (char *);
160
static bool F_LOCAL     BadApplication (char *);
161
static bool F_LOCAL     SetUpCLI (char *, Word_B **);
162
 
163
static char * F_LOCAL   ConvertErrorNumber (void);
164
static int F_LOCAL      BuildCommandLine (char *, char **, char **, int);
165
static int F_LOCAL      StartTheProcess (char *, char **, char **, int);
166
static int F_LOCAL      SetCommandReturnStatus (int);
167
static char ** F_LOCAL  FindNumberOfValues (char **, int *);
168
static int F_LOCAL      ExecuteScriptFile (char *, char **, char **, int, bool);
169
#if (OS_TYPE != OS_UNIX)
170
static int F_LOCAL      ExecuteWindows (char *, char **, char **, int);
171
#endif
172
static int F_LOCAL      ExecuteSpecialProcessor (char *, char **, char **, int,
173
                                                 Word_B *);
174
static int F_LOCAL      EnvironExecute (char **, int);
175
static bool F_LOCAL     CheckParameterLength (char **);
176
static int F_LOCAL      LocalExecve (char **, char **, int);
177
static unsigned int F_LOCAL CheckForCommonOptions (LineFields *, int);
178
static char ** F_LOCAL  ProcessSpaceInParameters (char **);
179
static int F_LOCAL      CountDoubleQuotes (char *);
180
static void             BuildEnvironmentEntry (const void *, VISIT, int);
181
static char ** F_LOCAL  BuildCommandEnvironment (void);
182
 
183
#if (OS_TYPE != OS_DOS)
184
static void F_LOCAL     PrintPidStarted (void);
185
 
186
#  if (OS_TYPE == OS_OS2)
187
static char             *InsertCharacterAtStart (char *);
188
static int F_LOCAL      OS_DosExecProgram (int, char *, char **, char **);
189
#  elif (OS_TYPE == OS_NT)
190
static int F_LOCAL      OS_DosExecProgram (int, char *, char **, char **);
191
#  endif
192
#else
193
#  define PrintPidStarted()
194
#  define InsertCharacterAtStart(a)
195
#endif
196
 
197
static void              PrintProgramMode (ExeMode *PMode);
198
 
199
#if (OS_TYPE == OS_OS2)
200
static int F_LOCAL      StartTheSession (STARTDATA *, char *, char **,
201
                                         char **, int);
202
#endif
203
 
204
static char             *AE2big = "arg/env list too big (sh)";
205
                        /* Extended Command line processing file name   */
206
static char             *Extend_file = (char *)NULL;
207
static char             *DoubleQuotes = "\"";
208
static char             *WildCards = "*?[\"'";
209
static unsigned long    ApplicationType;
210
 
211
#if (OS_TYPE == OS_DOS)
212
static char             *LIT_STARTWINP = "STARTWINP";
213
#endif
214
 
215
/*
216
 * Global variables for TWALK to build command environment
217
 */
218
 
219
static Word_B   *BCE_WordList;
220
static int      BCE_Length;
221
 
222
/*
223
 * List of Executable (shell script, .exe, .com) extensions and list
224
 * including functions.
225
 */
226
 
227
static char     **ExecutableList = (char **)NULL;
228
static char     **ExecuteFunctionList = (char **)NULL;
229
 
230
/*
231
 * OS2 load error mode
232
 */
233
 
234
#if (OS_TYPE == OS_OS2)
235
static char             FailName[FFNAME_MAX];
236
#endif
237
 
238
#if (OS_TYPE != OS_DOS)
239
static OSCALL_RET       OS_DosExecPgmReturnCode;
240
#endif
241
 
242
/*
243
 * Program started info
244
 */
245
 
246
#if (OS_TYPE != OS_DOS)
247
struct PidInfo {
248
    bool        Valid;
249
    int         JobNo;
250
    int         PidNo;
251
} PidInfo;
252
#endif
253
 
254
/*
255
 * execute tree recursively
256
 */
257
 
258
int
259
ExecuteParseTree(
260
    C_Op *t, int StandardIN, int StandardOUT, int Actions )
261
{
262
    int                 Count;
263
    int                 LocalPipeFP;    /* Pipe handlers                */
264
 
265
#if (OS_TYPE != OS_DOS)
266
    int                 ReadPipeFP;
267
    int                 WritePipeFP;
268
#  if OS_TYPE == OS_NT
269
    HANDLE              ReadPipeH;
270
    HANDLE              WritePipeH;
271
#  endif
272
#endif
273
 
274
    char                *cp;
275
    char                **AList;        /* Argument list                */
276
    Break_C             BreakContinue;
277
                                        /* Save longjmp returns         */
278
    Break_C             *S_RList = Return_List;
279
    Break_C             *S_BList = Break_List;
280
    Break_C             *S_SList = SShell_List;
281
 
282
    GetoptsIndex        GetoptsSave;
283
    int                 Local_depth;    /* Save local values            */
284
    int                 Local_MemoryAreaLevel;
285
    int                 RetVal = 0;     /* Return value                 */
286
    char                *InputBuffer;   /* Select input Buffer          */
287
    char                *EndIB;         /* End of buffer                */
288
    char                *LastWord = null;
289
 
290
/* End of tree ? */
291
 
292
    if (t == (C_Op *)NULL)
293
        return SetCommandReturnStatus (0);
294
 
295
    DPRINT (1, ("ExecuteParseTree: t->type = %d, Depth = %d",
296
                t->type, Execute_stack_depth));
297
 
298
/* Save original and Increment execute function recursive level */
299
 
300
    Local_depth = Execute_stack_depth++;
301
 
302
/* Save original and increment area number */
303
 
304
    Local_MemoryAreaLevel = MemoryAreaLevel++;
305
 
306
/* Switch on tree node type */
307
 
308
    switch (t->type)
309
    {
310
        case TFUNC:                     /* name () { list; }    */
311
            RetVal = SaveFunction (t) ? 0 : 1;
312
            SetCommandReturnStatus (RetVal);
313
            break;
314
 
315
/* In the case of a () command string, we need to save and restore the
316
 * current environment, directory and traps (can't think of anything else).
317
 * For any other, we just restore the current directory.  Also, we don't
318
 * want changes in the Variable list header saved for SubShells, because
319
 * we are effectively back at execute depth zero.
320
 */
321
        case TPAREN:                    /* ()                   */
322
            if ((RetVal = CreateGlobalVariableList (FLAGS_NONE)) == -1)
323
                break;
324
 
325
/* Save Getopts pointers */
326
 
327
            GetGetoptsValues (&GetoptsSave);
328
 
329
            if (setjmp (BreakContinue.CurrentReturnPoint) == 0)
330
            {
331
                Return_List = (Break_C *)NULL;
332
                Break_List  = (Break_C *)NULL;
333
                BreakContinue.NextExitLevel  = SShell_List;
334
                SShell_List = &BreakContinue;
335
                RetVal = ForkAndExecute (t, StandardIN, StandardOUT, Actions,
336
                                         NOWORDS, NOWORDS, &LastWord);
337
            }
338
 
339
/* Restore the original environment */
340
 
341
            else
342
                RetVal = (int)GetVariableAsNumeric (StatusVariable);
343
 
344
            SaveGetoptsValues (GetoptsSave.Index, GetoptsSave.SubIndex);
345
            Return_List = S_RList;
346
            Break_List  = S_BList;
347
            SShell_List = S_SList;
348
            RestoreEnvironment (RetVal, Local_depth);
349
            break;
350
 
351
/* After a normal command, we need to restore the original directory.  Note
352
 * that a cd will have updated the variable $~, so no problem
353
 */
354
 
355
        case TCOM:                      /* A command process    */
356
        {
357
            ExeMode     SaveValues;
358
            char        **SetVlist;     /* Set variable list            */
359
 
360
            SetVlist = ExpandWordList (t->vars, EXPAND_TILDE, (ExeMode *)NULL);
361
            AList = ExpandWordList (t->args, EXPAND_SPLITIFS | EXPAND_GLOBBING |
362
                                    EXPAND_TILDE, &SaveValues);
363
 
364
            ExecProcessingMode = SaveValues;
365
 
366
#if (OS_TYPE != OS_DOS)
367
            PidInfo.Valid = FALSE;
368
#endif
369
 
370
            RetVal = ForkAndExecute (t, StandardIN, StandardOUT, Actions,
371
                                     AList, SetVlist, &LastWord);
372
 
373
            PrintPidStarted ();
374
            RestoreEnvironment (RetVal, Local_depth);
375
 
376
/* Save last word if appropriate */
377
 
378
            if (!(DisabledVariables & DISABLE_LASTWORD))
379
            {
380
                SetVariableFromString (LastWordVariable, LastWord);
381
                SetVariableStatus (LastWordVariable, STATUS_EXPORT);
382
            }
383
            break;
384
        }
385
 
386
        case TTIME:                     /* Time a command process       */
387
        {
388
            clock_t     stime;
389
            clock_t     etime;
390
            clock_t     dif;
391
 
392
            stime = clock ();
393
            RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);
394
            etime = clock ();
395
 
396
            feputc (CHAR_NEW_LINE);
397
 
398
            if ((dif = (etime - stime) / (60L * (clock_t)CLOCKS_PER_SEC)) != 0)
399
                fprintf (stderr, "%ldm ", dif);
400
 
401
            dif = (etime - stime) % (60L * (clock_t)CLOCKS_PER_SEC);
402
 
403
            fprintf (stderr, "%ld.%.3lds real\n",
404
                     dif / (clock_t)CLOCKS_PER_SEC,
405
                     dif % (clock_t)CLOCKS_PER_SEC);
406
 
407
            break;
408
        }
409
 
410
        case TPIPE:                     /* Pipe processing              */
411
 
412
            Actions |= EXEC_PIPE_IN;
413
 
414
#if (OS_TYPE == OS_UNIX)
415
            fputs ("UNIX: Pipes not implemented\n", stderr);
416
            RetVal = -1;
417
 
418
#else
419
#  if (OS_TYPE != OS_DOS)
420
/* Do we want to use real pipes under OS2? */
421
 
422
            if (ShellGlobalFlags & FLAGS_REALPIPES)
423
            {
424
#    if (OS_TYPE == OS_OS2)
425
#      if (OS_SIZE == OS_32)
426
                if (DosCreatePipe ((PHFILE) &ReadPipeFP,
427
                                   (PHFILE) &WritePipeFP, 4096))
428
                    break;
429
#      else
430
                if (DosMakePipe ((PHFILE) &ReadPipeFP,
431
                                 (PHFILE) &WritePipeFP, 0))
432
                    break;
433
#      endif
434
 
435
/* Remap the IO handler */
436
 
437
                ReadPipeFP = ReMapIOHandler (ReadPipeFP);
438
                WritePipeFP = ReMapIOHandler (WritePipeFP);
439
                DosSetFHandState (ReadPipeFP, OPEN_FLAGS_NOINHERIT);
440
                DosSetFHandState (WritePipeFP, OPEN_FLAGS_NOINHERIT);
441
 
442
#    elif (OS_TYPE == OS_NT)
443
                if (!CreatePipe (&ReadPipeH, &WritePipeH, 0, 0))
444
                    break;
445
 
446
/* Remap the IO handler */
447
 
448
                ReadPipeFP = _open_osfhandle ((long)ReadPipeH, _O_RDONLY);
449
                WritePipeFP = _open_osfhandle ((long)WritePipeH, _O_APPEND);
450
                ReadPipeFP = ReMapIOHandler (ReadPipeFP);
451
                WritePipeFP = ReMapIOHandler (WritePipeFP);
452
#    endif
453
 
454
 
455
/* Is this a foreground thingy? */
456
 
457
                if (!(Actions & (EXEC_SPAWN_NOWAIT | EXEC_SPAWN_IGNOREWAIT)))
458
                {
459
                    int WaitPid;
460
 
461
                    WaitPid = ExecuteParseTree (t->left, StandardIN,
462
                                                WritePipeFP,
463
                                                EXEC_SPAWN_IGNOREWAIT |
464
                                                (Actions & (EXEC_PIPE_IN |
465
                                                            EXEC_PIPE_SUBS)));
466
                    S_close (WritePipeFP, TRUE);
467
 
468
                    RetVal = ExecuteParseTree (t->right, ReadPipeFP,
469
                                               StandardOUT,
470
                                               Actions | EXEC_PIPE_SUBS);
471
                    S_close (ReadPipeFP, TRUE);
472
                    cwait (&WaitPid, WaitPid, WAIT_GRANDCHILD);
473
                }
474
 
475
/* Background processing */
476
 
477
                else
478
                {
479
                    ExecuteParseTree (t->left, StandardIN, WritePipeFP,
480
                                      EXEC_SPAWN_IGNOREWAIT |
481
                                      (Actions & (EXEC_PIPE_IN |
482
                                                  EXEC_PIPE_SUBS)));
483
 
484
                    S_close (WritePipeFP, TRUE);
485
 
486
                    RetVal = ExecuteParseTree (t->right, ReadPipeFP,
487
                                               StandardOUT,
488
                                               Actions | EXEC_PIPE_SUBS);
489
                    S_close (ReadPipeFP, TRUE);
490
                }
491
 
492
                break;
493
            }
494
#  endif
495
 
496
/* MSDOS or OS/2 without real pipes - use files.  Safer */
497
 
498
            if ((RetVal = OpenAPipe ()) < 0)
499
                break;
500
 
501
/* Create pipe, execute command, reset pipe, execute the other side, close
502
 * the pipe and fini
503
 */
504
 
505
            LocalPipeFP = ReMapIOHandler (RetVal);
506
            ExecuteParseTree (t->left, StandardIN, LocalPipeFP,
507
                              (Actions & (EXEC_PIPE_IN | EXEC_PIPE_SUBS)));
508
 
509
/* Close the Input to release the file descriptor */
510
 
511
            CloseThePipe (StandardIN);
512
 
513
            lseek (LocalPipeFP, 0L, SEEK_SET);
514
            RetVal = ExecuteParseTree (t->right, LocalPipeFP, StandardOUT,
515
                                       Actions | EXEC_PIPE_SUBS);
516
            CloseThePipe (LocalPipeFP);
517
#endif
518
            break;
519
 
520
        case TLIST:                     /* Entries in a for statement   */
521
            while (t->type == TLIST)
522
            {
523
                ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);
524
                t = t->right;
525
            }
526
 
527
            RetVal = ExecuteParseTree (t, StandardIN, StandardOUT, 0);
528
            break;
529
 
530
        case TCOPROCESS:                /* Co processes                 */
531
            if (!FL_TEST (FLAG_WARNING))
532
                PrintWarningMessage ("sh: co-processes not supported");
533
 
534
            SetCommandReturnStatus (RetVal = -1);
535
            break;
536
 
537
        case TASYNC:                    /* Async - not supported        */
538
#if (OS_TYPE == OS_DOS)
539
            if (!FL_TEST (FLAG_WARNING))
540
                PrintWarningMessage ("sh: Async commands not supported");
541
 
542
            SetCommandReturnStatus (RetVal = -1);
543
#else
544
            RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
545
                                       EXEC_SPAWN_NOWAIT);
546
#endif
547
            break;
548
 
549
        case TOR:                       /* || and &&                    */
550
        case TAND:
551
            RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT, 0);
552
 
553
            if ((t->right != (C_Op *)NULL) &&
554
                ((RetVal == 0) == (t->type == TAND)))
555
                RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT,
556
                                           0);
557
 
558
            break;
559
 
560
 
561
/* for x do...done and for x in y do...done - find the start of the variables
562
 * count the number.
563
 */
564
        case TFOR:
565
        case TSELECT:
566
            AList = FindNumberOfValues (ExpandWordList (t->vars,
567
                                                        EXPAND_SPLITIFS |
568
                                                        EXPAND_GLOBBING |
569
                                                        EXPAND_TILDE,
570
                                                        (ExeMode *)NULL),
571
                                                        &Count);
572
 
573
 
574
/* Set up a long jump return point before executing the for function so that
575
 * the continue statement is executed, ie we reprocessor the for condition.
576
 */
577
 
578
            while ((RetVal = setjmp (BreakContinue.CurrentReturnPoint)) != 0)
579
            {
580
 
581
/* Restore the current stack level and clear out any I/O */
582
 
583
                RestoreEnvironment (0, Local_depth + 1);
584
                Return_List = S_RList;
585
                SShell_List = S_SList;
586
 
587
/* If this is a break - clear the variable and terminate the while loop and
588
 * switch statement
589
 */
590
 
591
                if (RetVal == BC_BREAK)
592
                    break;
593
            }
594
 
595
            if (RetVal == BC_BREAK)
596
                break;
597
 
598
/* Process the next entry - Add to the break/continue chain */
599
 
600
            BreakContinue.NextExitLevel = Break_List;
601
            Break_List = &BreakContinue;
602
 
603
/* Execute the command tree */
604
 
605
            if (t->type == TFOR)
606
            {
607
                while (Count--)
608
                {
609
                    SetVariableFromString (t->str, *AList++);
610
                    RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
611
                                               0);
612
                }
613
            }
614
 
615
/* Select option */
616
 
617
            else if (!Count)
618
            /* SKIP */;
619
 
620
/* Get some memory for the select input buffer */
621
 
622
            else if ((InputBuffer = AllocateMemoryCell (LINE_MAX))
623
                        == (char *)NULL)
624
            {
625
                ShellErrorMessage (Outofmemory1);
626
                RetVal = -1;
627
            }
628
 
629
/* Process the select command */
630
 
631
            else
632
            {
633
                bool    OutputList = TRUE;
634
 
635
                EndIB = &InputBuffer[LINE_MAX - 2];
636
 
637
                for (;;)
638
                {
639
                    int         ReadCount;      /* Local counter        */
640
                    int         OnlyDigits;     /* Only digits in string*/
641
 
642
/* Output list of words */
643
 
644
                    if (OutputList)
645
                    {
646
                        PrintAList (Count, AList);
647
                        OutputList = FALSE;
648
                    }
649
 
650
/* Output prompt */
651
 
652
                    OutputUserPrompt (PS3);
653
                    OnlyDigits = 1;
654
 
655
/* Read in until end of line, file or a field separator is detected */
656
 
657
                    for (cp = InputBuffer; (cp < EndIB); cp++)
658
                    {
659
                        if (((ReadCount = read (STDIN_FILENO, cp, 1)) != 1) ||
660
                            (*cp == CHAR_NEW_LINE))
661
                        {
662
                            break;
663
                        }
664
 
665
                        OnlyDigits = OnlyDigits && isdigit (*cp);
666
                    }
667
 
668
                    *cp = 0;
669
 
670
/* Check for end of file */
671
 
672
                    if (ReadCount != 1)
673
                        break;
674
 
675
/* Check for empty line */
676
 
677
                    if (!strlen (InputBuffer))
678
                    {
679
                        OutputList = TRUE;
680
                        continue;
681
                    }
682
 
683
                    SetVariableFromString (LIT_REPLY, InputBuffer);
684
 
685
/* Check that OnlyDigits is a valid number in the select range */
686
 
687
                    if (OnlyDigits &&
688
                        ((OnlyDigits = atoi (InputBuffer)) > 0) &&
689
                        (OnlyDigits <= Count))
690
                        SetVariableFromString (t->str, AList[OnlyDigits - 1]);
691
 
692
                    else
693
                        SetVariableFromString (t->str, null);
694
 
695
                    RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
696
                                               0);
697
                }
698
            }
699
 
700
/* Remove this tree from the break list */
701
 
702
            Break_List = S_BList;
703
            break;
704
 
705
/* While and Until function.  Similar to the For function.  Set up a
706
 * long jump return point before executing the while function so that
707
 * the continue statement is executed OK.
708
 */
709
 
710
        case TWHILE:                    /* WHILE and UNTIL functions    */
711
        case TUNTIL:
712
            while ((RetVal = setjmp (BreakContinue.CurrentReturnPoint)) != 0)
713
            {
714
 
715
/* Restore the current stack level and clear out any I/O */
716
 
717
                RestoreEnvironment (0, Local_depth + 1);
718
                Return_List = S_RList;
719
                SShell_List = S_SList;
720
 
721
/* If this is a break, terminate the while and switch statements */
722
 
723
                if (RetVal == BC_BREAK)
724
                    break;
725
            }
726
 
727
            if (RetVal == BC_BREAK)
728
                break;
729
 
730
/* Set up links */
731
 
732
            BreakContinue.NextExitLevel = Break_List;
733
            Break_List = &BreakContinue;
734
 
735
            while ((ExecuteParseTree (t->left, StandardIN, StandardOUT, 0) == 0)
736
                                == (t->type == TWHILE))
737
                RetVal = ExecuteParseTree (t->right, StandardIN, StandardOUT,
738
                                           0);
739
 
740
            Break_List = S_BList;
741
            break;
742
 
743
        case TIF:                       /* IF and ELSE IF functions     */
744
        case TELIF:
745
            if (t->right != (C_Op *)NULL)
746
                RetVal = ExecuteParseTree (!ExecuteParseTree (t->left,
747
                                                              StandardIN,
748
                                                              StandardOUT, 0)
749
                                            ? t->right->left : t->right->right,
750
                                            StandardIN, StandardOUT, 0);
751
 
752
            break;
753
 
754
        case TCASE:                     /* CASE function                */
755
        {
756
            C_Op        *ts = t->left;
757
 
758
            cp = ExpandAString (t->str, 0);
759
 
760
            while ((ts != (C_Op *)NULL) && (ts->type == TPAT))
761
            {
762
                for (AList = ts->vars; *AList != NOWORD; AList++)
763
                {
764
                    if ((EndIB = ExpandAString (*AList, EXPAND_PATTERN)) != 0 &&
765
                        GeneralPatternMatch (cp, (unsigned char *)EndIB, FALSE,
766
                                             (char **)NULL, GM_ALL))
767
                        goto Found;
768
                }
769
 
770
                ts = ts->right;
771
            }
772
 
773
            break;
774
 
775
Found:
776
            RetVal = ExecuteParseTree (ts->left, StandardIN, StandardOUT, 0);
777
            break;
778
        }
779
 
780
        case TBRACE:                    /* {} statement                 */
781
            if ((RetVal >= 0) && (t->left != (C_Op *)NULL))
782
                RetVal = ExecuteParseTree (t->left, StandardIN, StandardOUT,
783
                                           (Actions & EXEC_FUNCTION));
784
 
785
            break;
786
    }
787
 
788
/* Processing Completed - Restore environment */
789
 
790
    Execute_stack_depth = Local_depth;
791
 
792
/* Remove unwanted malloced space */
793
 
794
    ReleaseMemoryArea (MemoryAreaLevel);
795
    MemoryAreaLevel = Local_MemoryAreaLevel;
796
 
797
/* Check for traps */
798
 
799
    if (t->type == TCOM)
800
    {
801
        RunTrapCommand (-1);            /* Debug trap                   */
802
 
803
        if (RetVal)
804
            RunTrapCommand (-2);        /* Err trap                     */
805
    }
806
 
807
/* Interrupt traps */
808
 
809
    if ((Count = InterruptTrapPending) != 0)
810
    {
811
        InterruptTrapPending = 0;
812
        RunTrapCommand (Count);
813
    }
814
 
815
/* Check for interrupts */
816
 
817
    if (InteractiveFlag && IS_TTY (0) && SW_intr)
818
    {
819
        CloseAllHandlers ();
820
        TerminateCurrentEnvironment (TERMINATE_COMMAND);
821
    }
822
 
823
    return RetVal;
824
}
825
 
826
 
827
/*
828
 * Restore the original directory
829
 */
830
void RestoreCurrentDirectory (char *path)
831
{
832
    SetCurrentDrive (GetDriveNumber (*path));
833
 
834
    if (!S_chdir (&path[2]))
835
    {
836
        if (!FL_TEST (FLAG_WARNING))
837
            feputs ("Warning: current directory reset to /\n");
838
 
839
        S_chdir (DirectorySeparator);
840
        GetCurrentDirectoryPath ();
841
    }
842
}
843
 
844
 
845
/*
846
 * Ok - execute the program, resetting any I/O required
847
 */
848
static int F_LOCAL
849
ForkAndExecute (
850
    C_Op *t, int StandardIN, int StandardOUT,
851
     int ForkAction, char **AList, char **VList, char **LastWord )
852
{
853
    int                 RetVal = -1;    /* Return value                 */
854
    int                 (*shcom)(int, char **) = (int (*)(int, char **))NULL;
855
    char                *cp;
856
    char                **alp;
857
    IO_Actions          **iopp = t->ioact;
858
    int                 builtin = 0;    /* Builtin function             */
859
    bool                CGVLCalled = FALSE;
860
    bool                InternalExec = FALSE;
861
    int                 Index;
862
 
863
    if (t->type == TCOM)
864
    {
865
        cp = *AList;
866
 
867
/* strip all initial assignments not correct wrt PATH=yyy command  etc */
868
 
869
        if (FL_TEST (FLAG_PRINT_EXECUTE) ||
870
           ((CurrentFunction != (FunctionList *)NULL) &&
871
            CurrentFunction->Traced))
872
            EchoCurrentCommand (cp != NOWORD ? AList : VList);
873
 
874
/* Is it only an assignement? */
875
 
876
        if ((cp == NOWORD) && (t->ioact == (IO_Actions **)NULL))
877
        {
878
            while (((cp = *(VList++)) != NOWORD) &&
879
                   AssignVariableFromString (cp, &Index))
880
                continue;
881
 
882
#if (OS_TYPE != OS_DOS)
883
            ExitWithJobsActive = FALSE;
884
#endif
885
 
886
/* Get the status variable if expand changed it */
887
 
888
            return (int)GetVariableAsNumeric (StatusVariable);
889
        }
890
 
891
/* Check for built in commands */
892
 
893
        else if (cp != NOWORD)
894
        {
895
            shcom = IsCommandBuiltIn (cp, &builtin);
896
            InternalExec = C2bool ((strcmp (cp, LIT_exec)) == 0);
897
 
898
/*
899
 * Reset the ExitWithJobsActive flag to enable the 'jobs active' on exit
900
 * message
901
 */
902
 
903
#if (OS_TYPE != OS_DOS)
904
            if (strcmp (cp, LIT_exit))
905
                ExitWithJobsActive = FALSE;
906
#endif
907
        }
908
 
909
#if (OS_TYPE != OS_DOS)
910
        else
911
            ExitWithJobsActive = FALSE;
912
#endif
913
    }
914
 
915
#if (OS_TYPE != OS_DOS)
916
    else
917
        ExitWithJobsActive = FALSE;
918
#endif
919
 
920
/*
921
 * UNIX fork simulation?
922
 */
923
 
924
/* If there is a command to execute or we are exec'ing and this is not a
925
 * TPAREN, save the current environment
926
 */
927
 
928
    if (t->type != TPAREN)
929
    {
930
        if (((*VList != NOWORD) &&
931
             ((builtin & BLT_SKIPENVIR) != BLT_SKIPENVIR)) ||
932
            (ForkAction & EXEC_WITHOUT_FORK))
933
        {
934
            if (CreateGlobalVariableList (FLAGS_FUNCTION) == -1)
935
                return -1;
936
 
937
            CGVLCalled = TRUE;
938
        }
939
 
940
/* Set up any variables.  Note there is an assumption that
941
 * AssignVariableFromString sets the equals sign to 0, hiding the value;
942
 */
943
 
944
        if (shcom == (int (*)(int, char **))NULL)
945
        {
946
            while (((cp = *(VList++)) != NOWORD) &&
947
                   AssignVariableFromString (cp, &Index))
948
                SetVariableArrayStatus (cp, Index,
949
                                        (STATUS_EXPORT | STATUS_LOCAL));
950
 
951
/* If the child shells from this process communicate via pipes, set the
952
 * environment so the child shell will know.  This is a special case for OS/2
953
 * (and Win NT?) EMACS.
954
 */
955
 
956
           if (ExecProcessingMode.Flags & EP_PSEUDOTTY)
957
           {
958
               char     temp[30];
959
 
960
               AssignVariableFromString (strcat (strcpy (temp, LIT_AllowTTY),
961
                                                 "=true"), &Index);
962
               SetVariableArrayStatus (temp, Index,
963
                                       (STATUS_EXPORT | STATUS_LOCAL));
964
           }
965
        }
966
    }
967
 
968
 
969
/* If this is the first command in a pipe, set stdin to /dev/null */
970
 
971
   if ((ForkAction & (EXEC_PIPE_IN | EXEC_PIPE_SUBS)) == EXEC_PIPE_IN)
972
   {
973
        if ((Index = S_open (FALSE, "/dev/null", O_RDONLY)) != 0)
974
        {
975
            S_dup2 (Index, 0);
976
            S_close (Index, TRUE);
977
        }
978
   }
979
 
980
/* We cannot close the pipe, because once the exec/spawn has taken place
981
 * the processing of the pipe is not yet complete.
982
 */
983
 
984
    if (StandardIN != NOPIPE)
985
    {
986
        S_dup2 (StandardIN, STDIN_FILENO);
987
        /*lseek (STDIN_FILENO, 0L, SEEK_SET);*/
988
    }
989
 
990
    if (StandardOUT != NOPIPE)
991
    {
992
        FlushStreams ();
993
        S_dup2 (StandardOUT, STDOUT_FILENO);
994
        lseek (STDOUT_FILENO, 0L, SEEK_END);
995
    }
996
 
997
/* Set up any other IO required */
998
 
999
    FlushStreams ();
1000
 
1001
    if (iopp != (IO_Actions **)NULL)
1002
    {
1003
        while (*iopp != (IO_Actions *)NULL)
1004
        {
1005
            if (SetUpIOHandlers (*(iopp++), StandardIN, StandardOUT))
1006
                return RetVal;
1007
        }
1008
    }
1009
 
1010
 
1011
/* All fids above 10 are autoclosed in the exec file because we have used
1012
 * the O_NOINHERIT flag.  Note I patched open.obj to pass this flag to the
1013
 * open function.
1014
 */
1015
 
1016
    if (t->type == TPAREN)
1017
        return RestoreStandardIO (ExecuteParseTree (t->left, NOPIPE, NOPIPE, 0),
1018
                                  TRUE);
1019
 
1020
/* Are we just changing the I/O re-direction for the shell ? */
1021
 
1022
    if (*AList == NOWORD)
1023
    {
1024
        if ((ForkAction & EXEC_WITHOUT_FORK) == 0)
1025
            RestoreStandardIO (0, TRUE);
1026
 
1027
/* Get the status variable if expand changed it */
1028
 
1029
        return (int)GetVariableAsNumeric (StatusVariable);
1030
    }
1031
 
1032
/*
1033
 * Find the end of the parameters and set up $_ environment variable
1034
 */
1035
 
1036
    alp = AList;
1037
    while (*alp != NOWORD)
1038
       alp++;
1039
 
1040
/* Move back to last parameter, and save it */
1041
 
1042
    *LastWord = StringCopy (*(alp - 1));
1043
 
1044
/* No - Check for a function the program.  At this point, we need to put
1045
 * in some processing for return.
1046
 */
1047
 
1048
    if (!(builtin & BLT_CURRENT) &&
1049
            ExecuteFunction (AList, &RetVal, CGVLCalled))
1050
        return RetVal;
1051
 
1052
/* Check for another drive or directory in the restricted shell */
1053
 
1054
    if ((strpbrk (*AList, ":/\\") != (char *)NULL) &&
1055
            CheckForRestrictedShell (*AList))
1056
        return RestoreStandardIO (-1, TRUE);
1057
 
1058
/* A little cheat to allow us to use the same code to start OS/2 sessions
1059
 * as to load and execute a program
1060
 */
1061
 
1062
#if (OS_TYPE == OS_OS2)
1063
    SessionControlBlock = (STARTDATA *)NULL;
1064
#endif
1065
 
1066
/* Ok - execute the program */
1067
 
1068
    if (!(builtin & BLT_CURRENT))
1069
    {
1070
        RetVal = EnvironExecute (AList, ForkAction);
1071
 
1072
        if (ExecProcessingMode.Flags != EP_ENVIRON)
1073
            RetVal = LocalExecve (AList, BuildCommandEnvironment(), ForkAction);
1074
    }
1075
 
1076
/* If we didn't find it, check for internal command
1077
 *
1078
 * Note that the exec command is a special case
1079
 */
1080
 
1081
    if ((builtin & BLT_CURRENT) || ((RetVal == -1) && (errno == ENOENT)))
1082
    {
1083
        if (shcom != (int (*)(int, char **))NULL)
1084
        {
1085
            if (InternalExec)
1086
                RetVal = doexec (t);
1087
 
1088
            else
1089
                RetVal = (*shcom)(CountNumberArguments (AList), AList);
1090
 
1091
            SetCommandReturnStatus (RetVal);
1092
        }
1093
    }
1094
 
1095
    if (RetVal == -1)
1096
        PrintLoadError (*AList);
1097
 
1098
    return RestoreStandardIO (RetVal, TRUE);
1099
}
1100
 
1101
/*
1102
 * Restore Local Environment
1103
 */
1104
 
1105
void RestoreEnvironment (int retval, int stack)
1106
{
1107
    Execute_stack_depth = stack;
1108
    DeleteGlobalVariableList ();
1109
    RestoreCurrentDirectory (CurrentDirectory->value);
1110
    RestoreStandardIO (SetCommandReturnStatus (retval), TRUE);
1111
}
1112
 
1113
/*
1114
 * Set up I/O redirection.  0< 1> are ignored as required within pipelines.
1115
 */
1116
 
1117
static bool F_LOCAL SetUpIOHandlers (IO_Actions *iop,
1118
                                     int        pipein,
1119
                                     int        pipeout)
1120
{
1121
    int         u = -1;
1122
    char        *cp = 0;
1123
    char        *msg;
1124
 
1125
/* Check for pipes */
1126
 
1127
    if ((pipein != NOPIPE) && (iop->io_unit == STDIN_FILENO))
1128
        return FALSE;
1129
 
1130
    if ((pipeout != NOPIPE) && (iop->io_unit == STDOUT_FILENO))
1131
        return FALSE;
1132
 
1133
    msg = ((iop->io_flag & IOTYPE) == IOWRITE) ? "create" : "open";
1134
 
1135
    if ((iop->io_flag & IOTYPE) != IOHERE)
1136
        cp = ExpandOneStringFirstComponent (iop->io_name,
1137
                                            EXPAND_TILDE | EXPAND_GLOBBING);
1138
 
1139
/*
1140
 * Duplicate - check for close
1141
 */
1142
 
1143
    if ((iop->io_flag & IOTYPE) == IODUP)
1144
    {
1145
        if ((cp[1]) || (!isdigit (*cp) && (*cp != CHAR_CLOSE_FD)))
1146
        {
1147
            ShellErrorMessage ("illegal >& argument (%s)", cp);
1148
            return TRUE;
1149
        }
1150
 
1151
        if (*cp == CHAR_CLOSE_FD)
1152
        {
1153
            iop->io_flag &= ~IOTYPE;
1154
            iop->io_flag |= IOCLOSE;
1155
        }
1156
    }
1157
 
1158
/*
1159
 * When writing to /dev/???, we have to cheat because MSDOS appears to
1160
 * have a problem with /dev/ files after find_first/find_next.
1161
 */
1162
 
1163
#if (OS_TYPE != OS_UNIX)
1164
    if (((iop->io_flag & IOTYPE) == IOWRITE) &&
1165
        (strnicmp (cp, DeviceNameHeader, LEN_DEVICE_NAME_HEADER) == 0))
1166
    {
1167
        iop->io_flag &= ~IOTYPE;
1168
        iop->io_flag |= IOCAT;
1169
    }
1170
#endif
1171
 
1172
/* Open the file in the appropriate mode */
1173
 
1174
    switch (iop->io_flag & IOTYPE)
1175
    {
1176
        case IOREAD:                            /* <                    */
1177
            u = S_open (FALSE, cp, O_RDONLY);
1178
            break;
1179
 
1180
        case IOHERE:                            /* <<                   */
1181
            u = OpenHereFile (iop->io_name, C2bool (iop->io_flag & IOEVAL));
1182
            cp = "here file";
1183
            break;
1184
 
1185
        case IORDWR:                            /* <>                   */
1186
            if (CheckForRestrictedShell (cp))
1187
                return TRUE;
1188
 
1189
            u = S_open (FALSE, cp, O_RDWR);
1190
            break;
1191
 
1192
        case IOCAT:                             /* >>                   */
1193
            if (CheckForRestrictedShell (cp))
1194
                return TRUE;
1195
 
1196
            if ((u = S_open (FALSE, cp, O_WRONLY | O_BINARY)) >= 0)
1197
            {
1198
                lseek (u, 0L, SEEK_END);
1199
                break;
1200
            }
1201
 
1202
        case IOWRITE:                           /* >                    */
1203
            {
1204
                if (CheckForRestrictedShell (cp))
1205
                    return TRUE;
1206
 
1207
                if ((ShellGlobalFlags & FLAGS_NOCLOBER) &&
1208
                        (!(iop->io_flag & IOCLOBBER)) &&
1209
                        (S_access (cp, F_OK)))
1210
                {
1211
                   u = -1;
1212
                   break;
1213
                }
1214
            }
1215
            u = S_open (FALSE, cp, O_CMASK);
1216
            break;
1217
 
1218
        case IODUP:                             /* >&                   */
1219
            if (CheckForRestrictedShell (cp))
1220
                return TRUE;
1221
 
1222
            u = S_dup2 (*cp - '0', iop->io_unit);
1223
            break;
1224
 
1225
        case IOCLOSE:                           /* >-                   */
1226
            if ((iop->io_unit >= STDIN_FILENO) &&
1227
                (iop->io_unit <= STDERR_FILENO))
1228
                S_dup2 (-1, iop->io_unit);
1229
 
1230
            S_close (iop->io_unit, TRUE);
1231
            return FALSE;
1232
    }
1233
 
1234
    if (u < 0)
1235
    {
1236
        PrintWarningMessage (LIT_3Strings, cp, "cannot ", msg);
1237
        return TRUE;
1238
    }
1239
 
1240
    else if (u != iop->io_unit)
1241
    {
1242
        S_dup2 (u, iop->io_unit);
1243
        S_close (u, TRUE);
1244
    }
1245
 
1246
    return FALSE;
1247
}
1248
 
1249
/*
1250
 * -x flag - echo command to be executed
1251
 */
1252
 
1253
static void F_LOCAL EchoCurrentCommand (char **wp)
1254
{
1255
    int         i;
1256
 
1257
    if ((CurrentFunction != (FunctionList *)NULL) && CurrentFunction->Traced)
1258
        fprintf (stderr, "%s:", CurrentFunction->tree->str);
1259
 
1260
    feputs (GetVariableAsString (PS4, TRUE));
1261
 
1262
    for (i = 0; wp[i] != NOWORD; i++)
1263
    {
1264
        if (i)
1265
            feputc (CHAR_SPACE);
1266
 
1267
        feputs (wp[i]);
1268
    }
1269
 
1270
    feputc (CHAR_NEW_LINE);
1271
}
1272
 
1273
 
1274
/*
1275
 * Set up the status on exit from a command
1276
 */
1277
static int F_LOCAL
1278
SetCommandReturnStatus (int s)
1279
{
1280
    SetVariableFromNumeric (StatusVariable, (long) abs (ExitStatus = s));
1281
    return s;
1282
}
1283
 
1284
 
1285
/*
1286
 * Execute a command
1287
 */
1288
int
1289
ExecuteACommand(
1290
    char **argv, int mode )
1291
{
1292
    int         RetVal;
1293
 
1294
    CheckProgramMode (*argv, &ExecProcessingMode);
1295
    RetVal = EnvironExecute (argv, 0);
1296
 
1297
    if (ExecProcessingMode.Flags != EP_ENVIRON)
1298
        RetVal = LocalExecve (argv, BuildCommandEnvironment (), mode);
1299
 
1300
    return RetVal;
1301
}
1302
 
1303
 
1304
/*
1305
 * PATH-searching interface to execve.
1306
 */
1307
static int F_LOCAL
1308
LocalExecve(
1309
    char **argv, char **envp, int ForkAction )
1310
{
1311
    int                 res = -1;               /* Result               */
1312
    char                *p_name;                /* Program name         */
1313
    int                 i;
1314
 
1315
/* Clear the DosExec Failname field */
1316
 
1317
#if (OS_TYPE == OS_OS2)
1318
    *FailName = 0;
1319
#endif
1320
 
1321
#if (OS_TYPE != OS_DOS)
1322
    OS_DosExecPgmReturnCode = 0;
1323
#endif
1324
 
1325
/* If the environment is null - It is too big - error */
1326
 
1327
    if (envp == NOWORDS)
1328
        errno = E2BIG;
1329
 
1330
    else if ((p_name = AllocateMemoryCell (FFNAME_MAX)) == (char *)NULL)
1331
        errno = ENOMEM;
1332
 
1333
    else
1334
    {
1335
/* Start off on the search path for the executable file */
1336
 
1337
        switch (i = FindLocationOfExecutable (p_name, argv[0]))
1338
        {
1339
            case EXTENSION_EXECUTABLE:
1340
                res = ExecuteProgram (p_name, argv, envp, ForkAction);
1341
                break;
1342
 
1343
/* Script file */
1344
 
1345
            case EXTENSION_BATCH:
1346
            case EXTENSION_SHELL_SCRIPT:
1347
                res = ExecuteScriptFile (p_name, argv, envp, ForkAction,
1348
                         (bool)(i == EXTENSION_SHELL_SCRIPT));
1349
                break;
1350
        }
1351
 
1352
        if (res != -1)
1353
        {
1354
            TrackAllCommands (p_name, *argv);
1355
            return res;
1356
        }
1357
    }
1358
 
1359
/* If no fork - exit */
1360
 
1361
    if (ForkAction & EXEC_WITHOUT_FORK)
1362
    {
1363
        PrintLoadError (*argv);
1364
        FinalExitCleanUp (-1);
1365
    }
1366
 
1367
    return -1;
1368
}
1369
 
1370
 
1371
/*
1372
 * Exec or spawn the program ?
1373
 */
1374
static int F_LOCAL
1375
ExecuteProgram (
1376
    char *path, char **parms, char **envp, int  ForkAction)
1377
{
1378
    int                 res;
1379
 
1380
/* Check we have access to the file */
1381
 
1382
    if (!S_access (path, F_OK))
1383
        return SetCommandReturnStatus (-1);
1384
 
1385
/* Process the command line */
1386
 
1387
    res = BuildCommandLine (path, parms, envp, ForkAction);
1388
 
1389
    SetWindowName ((char *)NULL);
1390
    ClearExtendedLineFile ();
1391
    return SetCommandReturnStatus (res);
1392
}
1393
 
1394
 
1395
/* Set up command line.  If the EXTENDED_LINE variable is set, we create
1396
 * a temporary file, write the argument list (one entry per line) to the
1397
 * this file and set the command line to @<filename>.  If NOSWAPPING, we
1398
 * execute the program because I have to modify the argument line
1399
 */
1400
static int F_LOCAL
1401
BuildCommandLine (
1402
    char *path, char **argv, char **envp, int ForkAction)
1403
{
1404
    char                **pl = argv;
1405
    FILE                *fd;
1406
    bool                found;
1407
    char                *new_args[3];
1408
    char                cmd_line[FFNAME_MAX];
1409
 
1410
/* Translate process name to MSDOS format */
1411
 
1412
    if ((GenerateFullExecutablePath (path) == (char *)NULL) ||
1413
        (!GetApplicationType (path)))
1414
        return -1;
1415
 
1416
/* If this is a windows program - special */
1417
 
1418
#if (OS_TYPE != OS_UNIX)
1419
    if ((ApplicationType == EXETYPE_DOS_GUI) && (BaseOS != BASE_OS_NT))
1420
        return ExecuteWindows (path, argv, envp, ForkAction);
1421
#endif
1422
 
1423
/* Extended command line processing */
1424
 
1425
    Extend_file = (char *)NULL;         /* Set no file          */
1426
    found = C2bool ((ExecProcessingMode.Flags & EP_UNIXMODE) ||
1427
                    (ExecProcessingMode.Flags & EP_DOSMODE));
1428
 
1429
/* Set up a blank command line */
1430
 
1431
    cmd_line[0] = 0;
1432
    cmd_line[1] = CHAR_RETURN;
1433
 
1434
/* If there are no parameters, or they fit in the DOS command line
1435
 * - start the process */
1436
 
1437
    if ((*(++pl) == (char *)NULL) || CheckParameterLength (pl))
1438
        return StartTheProcess (path, argv, envp, ForkAction);
1439
 
1440
/* If we can use an alternative approach - indirect files, use it */
1441
 
1442
    else if (found)
1443
    {
1444
        char    **pl1 = pl;
1445
 
1446
/* Check parameters don't contain a re-direction parameter */
1447
 
1448
        while (*pl1 != NOWORD)
1449
        {
1450
            if (**(pl1++) == CHAR_INDIRECT)
1451
            {
1452
               if (found && !FL_TEST (FLAG_WARNING))
1453
                    PrintWarningMessage (
1454
"sh: cannot exec, existing redirect(s) %c located within arg list.", CHAR_INDIRECT );
1455
 
1456
                found = FALSE;
1457
                errno = E2BIG;
1458
                break;
1459
            }
1460
        }
1461
 
1462
/* If we find it - create a temporary file and write the stuff */
1463
 
1464
        if ((found) &&
1465
            ((fd = FOpenFile (Extend_file = GenerateTemporaryFileName (),
1466
                              sOpenWriteMode)) != (FILE *)NULL))
1467
        {
1468
            if (FL_TEST(FLAG_DEBUG_EXECUTE))
1469
                fprintf (stderr, "Creating temporary file (%s)\n", Extend_file);
1470
 
1471
            if ((Extend_file = StringSave (Extend_file)) == null)
1472
                Extend_file = (char *)NULL;
1473
 
1474
/* Copy to end of list */
1475
 
1476
            do {
1477
                if (!WriteToExtendedFile (fd, *pl))
1478
                    return -1;
1479
            } while (*(pl++) != NOWORD);
1480
 
1481
/* Set up cmd_line[1] to contain the filename */
1482
 
1483
            memset (cmd_line, 0, FFNAME_MAX);
1484
            cmd_line[1] = CHAR_SPACE;
1485
            cmd_line[2] = CHAR_INDIRECT;
1486
            strcpy (&cmd_line[3], Extend_file);
1487
            cmd_line[0] = (char)(strlen (Extend_file) + 2);
1488
 
1489
/* If the name in the file is in upper case - use \ for separators */
1490
 
1491
            if (ExecProcessingMode.Flags & EP_DOSMODE)
1492
                PATH_TO_DOS (&cmd_line[2]);
1493
 
1494
/* OK we are ready to execute */
1495
 
1496
            new_args[0] = *argv;
1497
            new_args[1] = &cmd_line[2];
1498
            new_args[2] = (char *)NULL;
1499
 
1500
            return StartTheProcess (path, new_args, envp, ForkAction);
1501
        }
1502
    }
1503
 
1504
    return -1;
1505
}
1506
 
1507
/*
1508
 * Clear Extended command line file
1509
 */
1510
 
1511
void ClearExtendedLineFile (void)
1512
{
1513
    if (Extend_file != (char *)NULL)
1514
    {
1515
        if (!FL_TEST(FLAG_DEBUG_EXECUTE))       /* leave temporary images? */
1516
            unlink (Extend_file);               
1517
        ReleaseMemoryCell ((void *)Extend_file);
1518
    }
1519
 
1520
    Extend_file = (char *)NULL;
1521
}
1522
 
1523
/*
1524
 * Convert the executable path to the full path name
1525
 */
1526
char *
1527
GenerateFullExecutablePath (char *path)
1528
{
1529
    char                cpath[PATH_MAX + 6];
1530
    char                npath[FFNAME_MAX + 2];
1531
    char                n1path[PATH_MAX + 6];
1532
    char                *p;
1533
    int                 drive;
1534
 
1535
    PATH_TO_UPPER_CASE (path);
1536
 
1537
/* Get the current path */
1538
 
1539
    S_getcwd (cpath, 0);
1540
    strcpy (npath, cpath);
1541
 
1542
/* In current directory ? */
1543
 
1544
    if ((p = FindLastPathCharacter (path)) == (char *)NULL)
1545
    {
1546
         p = path;
1547
 
1548
/* Check for a:program case */
1549
 
1550
         if (IsDriveCharacter (*(p + 1)))
1551
         {
1552
            p += 2;
1553
 
1554
/* Get the path of the other drive */
1555
 
1556
            S_getcwd (npath, GetDriveNumber (*path));
1557
         }
1558
    }
1559
 
1560
/* In root directory */
1561
 
1562
    else if ((p - path) == 0)
1563
    {
1564
        ++p;
1565
        strcpy (npath, RootDirectory);
1566
        *npath = *path;
1567
        *npath = *cpath;
1568
    }
1569
 
1570
    else if (((p - path) == 2) && IsDriveCharacter (*(path + 1)))
1571
    {
1572
        ++p;
1573
        strcpy (npath, RootDirectory);
1574
        *npath = *path;
1575
    }
1576
 
1577
/* Find the directory */
1578
 
1579
    else
1580
    {
1581
        *(p++) = 0;
1582
 
1583
/* Change to the directory containing the executable */
1584
 
1585
        drive = IsDriveCharacter (*(path + 1))
1586
                        ? GetDriveNumber (*path)
1587
#if (OS_TYPE == OS_OS2) && !defined (__WATCOMC__)
1588
                        : _getdrive ();
1589
#else
1590
                        : 0;
1591
#endif
1592
 
1593
/* Save the current directory on this drive */
1594
 
1595
        S_getcwd (n1path, drive);
1596
 
1597
/* Find the directory we want */
1598
 
1599
        if (!S_chdir (path))
1600
            return (char *)NULL;
1601
 
1602
        S_getcwd (npath, drive);                /* Save its full name */
1603
        S_chdir (n1path);                       /* Restore the original */
1604
 
1605
/* Restore our original directory */
1606
 
1607
        if (!S_chdir (cpath))
1608
            return (char *)NULL;
1609
    }
1610
 
1611
    if (!IsPathCharacter (npath[strlen (npath) - 1]))
1612
        strcat (npath, DirectorySeparator);
1613
 
1614
    strcat (npath, p);
1615
    p = PATH_TO_DOS (strcpy (path, npath));
1616
    return p;
1617
}
1618
 
1619
 
1620
/*
1621
 * Find the number of values to use for a for or select statement
1622
 */
1623
static char ** F_LOCAL
1624
FindNumberOfValues (char **wp, int *Count)
1625
{
1626
 
1627
/* select/for x do...done - use the parameter values.  Need to know how many as
1628
 * it is not a NULL terminated array
1629
 */
1630
 
1631
    if (wp == NOWORDS)
1632
    {
1633
        if ((*Count = ParameterCount) < 0)
1634
            *Count = 0;
1635
 
1636
        return ParameterArray + 1;
1637
    }
1638
 
1639
/* select/for x in y do...done - find the start of the variables and
1640
 * use them all
1641
 */
1642
 
1643
    *Count = CountNumberArguments (wp);
1644
 
1645
    return wp;
1646
}
1647
 
1648
 
1649
/*
1650
 * Count the number of entries in an array
1651
 */
1652
int CountNumberArguments (char **wp)
1653
{
1654
    int         Count = 0;
1655
 
1656
    while (*(wp++) != NOWORD)
1657
        Count++;
1658
 
1659
    return Count;
1660
}
1661
 
1662
 
1663
/*
1664
 * Write a command string to the extended file
1665
 */
1666
static bool F_LOCAL
1667
WriteToExtendedFile (FILE *fd, char *string)
1668
{
1669
    char        *sp = string;
1670
    char        *cp = string;
1671
    bool        WriteOk = TRUE;
1672
 
1673
    if (string == (char *)NULL)
1674
    {
1675
        if (CloseFile (fd) != EOF)
1676
            return TRUE;
1677
 
1678
        WriteOk = FALSE;
1679
    }
1680
 
1681
    else if (strlen (string))
1682
    {
1683
 
1684
/* Write the string, converting newlines to backslash newline */
1685
 
1686
        while (WriteOk && (cp != (char *)NULL))
1687
        {
1688
            if ((cp = strchr (sp, CHAR_NEW_LINE)) != (char *)NULL)
1689
                *cp = 0;
1690
 
1691
            if (fputs (sp, fd) == EOF)
1692
                WriteOk = FALSE;
1693
 
1694
            else if (cp != (char *)NULL)
1695
                WriteOk = C2bool (fputs ("\\\n", fd) != EOF);
1696
 
1697
            sp = cp + 1;
1698
        }
1699
    }
1700
 
1701
    if (WriteOk && (fputc (CHAR_NEW_LINE, fd) != EOF))
1702
        return TRUE;
1703
 
1704
    CloseFile (fd);
1705
    ClearExtendedLineFile ();
1706
    errno = ENOSPC;
1707
    return FALSE;
1708
}
1709
 
1710
/*
1711
 * Execute or spawn the process
1712
 */
1713
 
1714
/* DOS, 16/32 bit version */
1715
 
1716
#if (OS_TYPE == OS_DOS)
1717
static int F_LOCAL
1718
StartTheProcess(
1719
    char *path, char **argv, char **envp, int ForkAction )
1720
{
1721
    int         retval;
1722
 
1723
    __systeml_sh = 1;                           /* 27/09/00 */
1724
    if (FL_TEST(FLAG_DEBUG_EXECUTE))
1725
        __systeml_debug = 1;                    /* 25/09/00 */
1726
 
1727
    if (ForkAction & EXEC_WITHOUT_FORK)
1728
    {
1729
        DumpHistory ();                         /* Exec - save history */
1730
 
1731
        if ((retval = long_execvpe(path, argv, envp)) != -1)
1732
            exit (retval);
1733
        PrintErrorMessage ("unexpected return from exec (%d)", errno);
1734
        return retval;
1735
    }
1736
    return long_spawnvpe(path, argv, envp);
1737
}
1738
#endif
1739
 
1740
 
1741
/* OS/2 Version */
1742
 
1743
#if (OS_TYPE == OS_OS2)
1744
static int F_LOCAL
1745
StartTheProcess (
1746
    char *path, char **argv, char **envp, int ForkAction)
1747
{
1748
    int                 RetVal;
1749
    STARTDATA           stdata;
1750
 
1751
/* Is this a start session option */
1752
 
1753
    if (SessionControlBlock != (STARTDATA *)NULL)
1754
        return StartTheSession (SessionControlBlock, path, argv, envp,
1755
                                ForkAction);
1756
 
1757
/* Exec ? */
1758
 
1759
    if (ForkAction & EXEC_WITHOUT_FORK)
1760
        return OS_DosExecProgram (OLD_P_OVERLAY, path, argv, envp);
1761
 
1762
    if (ForkAction & (EXEC_SPAWN_DETACH | EXEC_SPAWN_NOWAIT |
1763
                      EXEC_SPAWN_IGNOREWAIT))
1764
    {
1765
        int     Mode = (ForkAction & EXEC_SPAWN_DETACH)
1766
                        ? P_DETACH
1767
                        : ((ForkAction & EXEC_SPAWN_IGNOREWAIT)
1768
                           ? P_NOWAITO
1769
                           : (FL_TEST (FLAG_SEPARATE_GROUP)
1770
                             ? P_DETACH
1771
                             : P_NOWAIT));
1772
 
1773
        RetVal = OS_DosExecProgram (Mode, path, argv, envp);
1774
 
1775
/* Remove the reference to the temporary file for background tasks */
1776
 
1777
        ReleaseMemoryCell ((void *)Extend_file);
1778
        Extend_file = (char *)NULL;
1779
 
1780
        if ((RetVal != -1) && (Mode != P_NOWAITO))
1781
        {
1782
            if (InteractiveFlag && (source->type == STTY))
1783
            {
1784
                if (ForkAction & EXEC_SPAWN_DETACH)
1785
                    fprintf (stderr, "[0] Process %d detached\n", RetVal);
1786
 
1787
                else
1788
                {
1789
                    PidInfo.Valid = TRUE;
1790
                    PidInfo.JobNo = AddNewJob (RetVal, 0, path);
1791
                    PidInfo.PidNo = RetVal;
1792
                }
1793
            }
1794
 
1795
            SetVariableFromNumeric ("!", RetVal);
1796
            return 0;
1797
        }
1798
 
1799
        return RetVal;
1800
    }
1801
 
1802
/* In OS/2, we need the type of the program because PM programs have to be
1803
 * started in a session (or at least that was the only way I could get them
1804
 * to work).
1805
 */
1806
 
1807
/* Do we need to start a new session for this program ? */
1808
 
1809
    if (((ApplicationType & EXETYPE_OS2_TYPE) == EXETYPE_OS2_GUI) ||
1810
        (ApplicationType & EXETYPE_DOS))
1811
    {
1812
        if ((OS_VERS_N > 1) && (ApplicationType & EXETYPE_DOS))
1813
            memcpy (&stdata, &DOS_SessionControlBlock, sizeof (STARTDATA));
1814
 
1815
        else
1816
            memcpy (&stdata, &PM_SessionControlBlock, sizeof (STARTDATA));
1817
 
1818
        if (ForkAction & EXEC_WINDOWS)
1819
            stdata.PgmControl = 0;
1820
 
1821
        return StartTheSession (&stdata, path, argv, envp, ForkAction);
1822
    }
1823
 
1824
/* Just DosExecPgm it */
1825
 
1826
    return OS_DosExecProgram (P_WAIT, path, argv, envp);
1827
}
1828
#endif
1829
 
1830
/* NT Version */
1831
 
1832
#if (OS_TYPE == OS_NT)
1833
static int F_LOCAL
1834
StartTheProcess (
1835
    char *path, char **argv, char **envp, int ForkAction )
1836
{
1837
    int                 RetVal;
1838
 
1839
/* Exec ? */
1840
 
1841
    if (ForkAction & EXEC_WITHOUT_FORK)
1842
        return OS_DosExecProgram (OLD_P_OVERLAY, path, argv, envp);
1843
 
1844
    if (ForkAction & (EXEC_SPAWN_DETACH | EXEC_SPAWN_NOWAIT |
1845
                      EXEC_SPAWN_IGNOREWAIT))
1846
    {
1847
        int     Mode = (ForkAction & EXEC_SPAWN_DETACH)
1848
                        ? P_DETACH
1849
                        : ((ForkAction & EXEC_SPAWN_IGNOREWAIT)
1850
                           ? P_NOWAITO
1851
                           : (FL_TEST (FLAG_SEPARATE_GROUP)
1852
                             ? P_DETACH
1853
                             : P_NOWAIT));
1854
 
1855
        RetVal = OS_DosExecProgram (Mode, path, argv, envp);
1856
 
1857
/* Remove the reference to the temporary file for background tasks */
1858
 
1859
        ReleaseMemoryCell ((void *)Extend_file);
1860
        Extend_file = (char *)NULL;
1861
 
1862
        if ((RetVal != -1) && (Mode != P_NOWAITO))
1863
        {
1864
            if (InteractiveFlag && (source->type == STTY))
1865
            {
1866
                if (ForkAction & EXEC_SPAWN_DETACH)
1867
                    fprintf (stderr, "[0] Process %d detached\n", RetVal);
1868
 
1869
                else
1870
                {
1871
                    PidInfo.Valid = TRUE;
1872
                    PidInfo.JobNo = AddNewJob (RetVal, 0, path);
1873
                    PidInfo.PidNo = RetVal;
1874
                }
1875
            }
1876
 
1877
            SetVariableFromNumeric ("!", RetVal);
1878
            return 0;
1879
        }
1880
 
1881
        else
1882
            return RetVal;
1883
    }
1884
 
1885
/* Just DosExecPgm it */
1886
 
1887
    return OS_DosExecProgram (P_WAIT, path, argv, envp);
1888
}
1889
#endif
1890
 
1891
 
1892
/*
1893
 * Start a session
1894
 */
1895
#if (OS_TYPE == OS_OS2)
1896
static int F_LOCAL
1897
StartTheSession(
1898
    STARTDATA *SessionData, char *path,
1899
      char **argv, char **envp, int ForkAction )
1900
{
1901
    OSCALL_PARAM        usType;
1902
    OSCALL_PARAM        idSession;
1903
#  if (OS_SIZE == OS_32)
1904
    PID                 pid;
1905
#  else
1906
    USHORT              pid;
1907
#  endif
1908
 
1909
/*
1910
 * For OS/2 2.x, we can start DOS sessions!!
1911
 */
1912
    if ((OS_VERS_N > 1) && (ApplicationType & EXETYPE_DOS))
1913
    {
1914
        if ((ForkAction & EXEC_WINDOWS) ||
1915
            (SessionData->SessionType == SSF_TYPE_FULLSCREEN))
1916
            SessionData->SessionType = SSF_TYPE_VDM;
1917
 
1918
        else
1919
            SessionData->SessionType = SSF_TYPE_WINDOWEDVDM;
1920
 
1921
        if (SessionData->Environment == (PBYTE)1)
1922
            SessionData->Environment = (PBYTE)NULL;
1923
    }
1924
 
1925
    else if ((ApplicationType & EXETYPE_OS2_TYPE) == EXETYPE_OS2_GUI)
1926
        SessionData->SessionType = SSF_TYPE_PM;
1927
 
1928
    SessionData->PgmName = path;
1929
    SessionData->TermQ = SessionEndQName;       /* Queue name           */
1930
 
1931
    /*
1932
     * Build the environment if the current value is 1
1933
     */
1934
 
1935
    if ((SessionData->Environment == (PBYTE)1) &&
1936
        ((SessionData->Environment = (PBYTE)BuildOS2String (envp, 0))
1937
                                   == (PBYTE)NULL))
1938
        return -1;
1939
 
1940
    ProcessSpaceInParameters (argv);
1941
 
1942
    if ((SessionData->PgmInputs = (PBYTE)BuildOS2String (&argv[1], CHAR_SPACE))
1943
                                == (PBYTE)NULL)
1944
        return -1;
1945
 
1946
/*
1947
 * Start the session.  Do not record if it is independent
1948
 */
1949
 
1950
    DISABLE_HARD_ERRORS;
1951
#  if (OS_SIZE == OS_16) && !defined (OS2_16_BUG)
1952
    SessionData->Related = SSF_RELATED_INDEPENDENT;
1953
#  endif
1954
    usType = DosStartSession (SessionData, &idSession, &pid);
1955
    ENABLE_HARD_ERRORS;
1956
 
1957
    if ((!usType) || (usType == ERROR_SMG_START_IN_BACKGROUND))
1958
    {
1959
        if (InteractiveFlag && (source->type == STTY))
1960
        {
1961
            if (SessionData->Related == SSF_RELATED_INDEPENDENT)
1962
                fprintf (stderr, "[0] Independent Session %d started\n",
1963
                         idSession);
1964
 
1965
            else
1966
                fprintf (stderr, "[%d] Session %d (PID %d) started\n",
1967
                         AddNewJob (pid, idSession, path), idSession, pid);
1968
 
1969
            if (usType)
1970
                fprintf (stderr, "%s\n", GetOSSystemErrorMessage (usType));
1971
        }
1972
 
1973
        SetVariableFromNumeric ("!", pid);
1974
        return 0;
1975
    }
1976
 
1977
    else
1978
    {
1979
        strcpy (FailName, GetOSSystemErrorMessage (usType));
1980
 
1981
        errno = ENOENT;
1982
        return -1;
1983
    }
1984
}
1985
#endif
1986
 
1987
 
1988
/*
1989
 * Build the OS2 format <value>\0<value>\0 etc \0
1990
 */
1991
char *
1992
BuildOS2String (char **Array, char sep)
1993
{
1994
    int         i = 0;
1995
    int         Length = 0;
1996
    char        *Output;
1997
    char        *sp, *cp;
1998
 
1999
/* Find the total data length */
2000
 
2001
    while ((sp = Array[i++]) != NOWORD)
2002
        Length += strlen (sp) + 1;
2003
 
2004
    Length += 2;
2005
 
2006
    if ((Output = AllocateMemoryCell (Length)) == (char *)NULL)
2007
        return (char *)NULL;
2008
 
2009
/* Build the new string */
2010
 
2011
    i = 0;
2012
    sp = Output;
2013
 
2014
/* Build the string */
2015
 
2016
    while ((cp = Array[i++]) != NOWORD)
2017
    {
2018
        while ((*sp = *(cp++)) != 0)
2019
            ++sp;
2020
 
2021
        if (!sep || (Array[i] != NOWORD))
2022
            *(sp++) = sep;
2023
    }
2024
 
2025
    *sp = 0;
2026
    return Output;
2027
}
2028
 
2029
 
2030
/*
2031
 * List of default known extensions and their type
2032
 */
2033
 
2034
#define MAX_DEFAULT_EXTENSIONS  ARRAY_SIZE (DefaultExtensions)
2035
 
2036
static struct ExtType {
2037
    char        *Extension;
2038
    char        Type;
2039
} DefaultExtensions [] = {
2040
    { null,             EXTENSION_SHELL_SCRIPT },
2041
    { SHELLExtension,   EXTENSION_SHELL_SCRIPT },
2042
    { KSHELLExtension,  EXTENSION_SHELL_SCRIPT },
2043
#if (OS_TYPE != OS_UNIX)
2044
    { BATExtension,     EXTENSION_BATCH },
2045
    { EXEExtension,     EXTENSION_EXECUTABLE },
2046
    { COMExtension,     EXTENSION_EXECUTABLE },
2047
#endif
2048
};
2049
 
2050
/*
2051
 * Built the list of valid extensions
2052
 */
2053
 
2054
void
2055
BuildExtensionLists (void)
2056
{
2057
    Word_B      *DList = (Word_B *)NULL;
2058
    Word_B      *FList = (Word_B *)NULL;
2059
    int         i;
2060
    char        *pe;
2061
    char        *sp;
2062
    void        (_SIGDECL *save_signal)(int);
2063
 
2064
/* Disable signals */
2065
 
2066
    save_signal = signal (SIGINT, SIG_IGN);
2067
 
2068
/* Release Old memory */
2069
 
2070
    if (ExecuteFunctionList != (char **)NULL)
2071
    {
2072
        ReleaseMemoryCell (*ExecuteFunctionList);
2073
        ReleaseMemoryCell (ExecuteFunctionList);
2074
    }
2075
 
2076
    if (ExecutableList != (char **)NULL)
2077
        ReleaseMemoryCell (ExecutableList);
2078
 
2079
/* No extensions ? */
2080
 
2081
    if ((pe = GetVariableAsString (PathExtsLiteral, FALSE)) == null)
2082
    {
2083
        for (i = 0; i < MAX_DEFAULT_EXTENSIONS; i++)
2084
            DList = AddWordToBlock (DefaultExtensions [i].Extension, DList);
2085
 
2086
        ExecutableList = GetWordList (AddWordToBlock ((char *)NULL, DList));
2087
        ExecuteFunctionList = (char **)NULL;
2088
 
2089
/* Restore signals */
2090
 
2091
        signal (SIGINT, save_signal);
2092
 
2093
        return;
2094
    }
2095
 
2096
/* OK - scan */
2097
 
2098
    pe = StringSave (pe);
2099
 
2100
/* Split out on semi-colons */
2101
 
2102
    do
2103
    {
2104
        if ((sp = strchr (pe, ';')) != (char *)NULL)
2105
            *(sp++) = 0;
2106
 
2107
        FList = AddWordToBlock (pe, FList);
2108
 
2109
/* Build the new order of default extensions */
2110
 
2111
        for (i = 0; i < MAX_DEFAULT_EXTENSIONS; i++)
2112
        {
2113
            if (!NOCASE_COMPARE (pe, DefaultExtensions [i].Extension))
2114
            {
2115
                DList = AddWordToBlock (pe, DList);
2116
                break;
2117
            }
2118
        }
2119
 
2120
        pe = sp;
2121
    } while (pe != (char *)NULL);
2122
 
2123
/* Set up the two lists */
2124
 
2125
    ExecutableList = GetWordList (AddWordToBlock ((char *)NULL, DList));
2126
    ExecuteFunctionList = GetWordList (AddWordToBlock ((char *)NULL, FList));
2127
 
2128
/* Restore signals */
2129
 
2130
    signal (SIGINT, save_signal);
2131
}
2132
 
2133
 
2134
/*
2135
 * Find the location of an executable and return it's full path
2136
 * name
2137
 */
2138
int
2139
FindLocationOfExecutable (
2140
    char *FullPath, char *name)
2141
{
2142
    return ExtensionType (FindFileAndExtension (FullPath, name, ExecutableList));
2143
}
2144
 
2145
 
2146
/*
2147
 * Return the extension type for an extension
2148
 */
2149
static int F_LOCAL
2150
ExtensionType (char *ext)
2151
{
2152
    int         i;
2153
 
2154
    if (ext == (char *)NULL)
2155
        return EXTENSION_NOT_FOUND;
2156
 
2157
    for (i = 0; i < MAX_DEFAULT_EXTENSIONS; i++)
2158
    {
2159
        if (!NOCASE_COMPARE (ext, DefaultExtensions [i].Extension))
2160
            return (int)(DefaultExtensions [i].Type);
2161
    }
2162
 
2163
    return EXTENSION_OTHER;
2164
}
2165
 
2166
 
2167
/*
2168
 * Search the path for a file with the appropriate extension
2169
 *
2170
 * Returns a pointer to the extension.
2171
 */
2172
static char * F_LOCAL
2173
FindFileAndExtension (
2174
    char *FullPath, char *name, char **Extensions )
2175
{
2176
    char        *sp;                    /* Path pointers        */
2177
    char        *ep;
2178
    char        *xp;                    /* In file name pointers */
2179
    char        *xp1;
2180
    int         i, fp;
2181
    int         pathlen;
2182
 
2183
/* No extensions - no file */
2184
 
2185
    if (Extensions == (char **)NULL)
2186
    {
2187
        errno = ENOENT;
2188
        return (char *)NULL;
2189
    }
2190
 
2191
    if (FL_TEST(FLAG_DEBUG_EXECUTE))
2192
    {
2193
        fprintf (stderr, "Searching for: %s (", name);
2194
        for (i = 0; Extensions[i] != (char *)NULL; i++)
2195
            fprintf (stderr, "%s%s", (i ? " " : ""), 
2196
               (Extensions[i][0] ? Extensions[i] : "."));
2197
        fprintf (stderr, ") using\n");
2198
        fprintf (stderr, " PATH=%s\n", GetVariableAsString (PathLiteral, FALSE) );
2199
    }
2200
 
2201
/* Scan the path for an executable */
2202
 
2203
    sp = ((FindPathCharacter (name) != (char *)NULL) ||
2204
          (IsDriveCharacter (*(name + 1))))
2205
              ? null : GetVariableAsString (PathLiteral, FALSE);
2206
 
2207
    do
2208
    {
2209
        sp = BuildNextFullPathName (sp, name, FullPath);
2210
        ep = &FullPath[pathlen = strlen (FullPath)];
2211
 
2212
        if (FL_TEST(FLAG_DEBUG_EXECUTE))
2213
             fprintf (stderr, " Check: %s\n", FullPath);
2214
 
2215
/* Get start of file name */
2216
 
2217
        if ((xp1 = FindLastPathCharacter (FullPath)) == (char *)NULL)
2218
            xp1 = FullPath;
2219
 
2220
        else
2221
            ++xp1;
2222
 
2223
/* Look up all 5 types */
2224
 
2225
        for (i = 0; Extensions[i] != (char *)NULL; i++)
2226
        {
2227
            if (pathlen + strlen (Extensions[i]) > (size_t)(FFNAME_MAX - 1))
2228
                continue;
2229
 
2230
            strcpy (ep, Extensions[i]);
2231
 
2232
            if (S_access (FullPath, F_OK))
2233
            {
2234
/* If no extension, .ksh or .sh extension, check for shell script */
2235
 
2236
                if (((xp = strrchr (xp1, CHAR_PERIOD)) == (char *)NULL) ||
2237
                    (NOCASE_COMPARE (xp, SHELLExtension) == 0) ||
2238
                    (NOCASE_COMPARE (xp, KSHELLExtension) == 0))
2239
                {
2240
 
2241
                    if ((fp = CheckForScriptFile (FullPath, (char **)NULL,
2242
                                                  (int *)NULL)) < 0) {
2243
                        if (FL_TEST(FLAG_DEBUG_EXECUTE))
2244
                              fprintf (stderr, "Unable to determine type\n" );
2245
                        continue;
2246
                    }
2247
 
2248
                    S_close (fp, TRUE);
2249
 
2250
                    if (FL_TEST(FLAG_DEBUG_EXECUTE))
2251
                         fprintf (stderr, " Match: script %s\n", FullPath);
2252
 
2253
                    return (xp == (char *)NULL) ? null : xp;
2254
                }
2255
 
2256
                if (FL_TEST(FLAG_DEBUG_EXECUTE))
2257
                    fprintf (stderr, " Match: binary %s\n", FullPath);
2258
 
2259
                return xp;
2260
            }
2261
        }
2262
    } while (sp != (char *)NULL);
2263
 
2264
/* Not found */
2265
 
2266
    errno = ENOENT;
2267
    return (char *)NULL;
2268
}
2269
 
2270
 
2271
/*
2272
 * Execute a script file
2273
 */
2274
static int F_LOCAL
2275
ExecuteScriptFile (
2276
    char *Fullpath, char **argv, char **envp, int ForkAction, bool ShellScript)
2277
{
2278
    int         res;                    /* Result               */
2279
    char        *params = 0;            /* Script parameters    */
2280
    int         nargc = 0;              /* # script args        */
2281
    Word_B      *wb = (Word_B *)NULL;
2282
#if (OS_TYPE == OS_DOS) && (OS_SIZE == OS_16)
2283
    union REGS  r;
2284
#endif
2285
 
2286
/* Batfile - convert to DOS Format file name */
2287
 
2288
    if (!ShellScript)
2289
    {
2290
        PATH_TO_DOS (Fullpath);
2291
        nargc = 0;
2292
    }
2293
 
2294
    else if ((res = OpenForExecution (Fullpath, &params, &nargc)) >= 0)
2295
        S_close (res, TRUE);
2296
 
2297
    else
2298
    {
2299
        errno = ENOENT;
2300
        return -1;
2301
    }
2302
 
2303
/* If BAT file, use command.com else use sh */
2304
 
2305
    if (!ShellScript)
2306
    {
2307
        if (!SetUpCLI (GetVariableAsString (ComspecVariable, FALSE), &wb))
2308
            return -1;
2309
 
2310
        wb = AddWordToBlock ("/c", wb);
2311
 
2312
/* Get the switch character */
2313
 
2314
#if (OS_TYPE == OS_DOS) && (OS_SIZE == OS_16)
2315
        r.x.REG_AX = 0x3700;
2316
        DosInterrupt (&r, &r);
2317
 
2318
        if ((r.h.al == 0) && (_osmajor < 4))
2319
            *(wb->w_words[wb->w_nword - 1]) = (char)(r.h.dl);
2320
#endif
2321
    }
2322
 
2323
/* Stick in the pre-fix arguments */
2324
 
2325
    else if (nargc)
2326
        wb = SplitString (params, wb);
2327
 
2328
    else if (params != null)
2329
        wb = AddWordToBlock (params, wb);
2330
 
2331
    else
2332
        wb = AddWordToBlock (GetVariableAsString (ShellVariableName, FALSE),
2333
                             wb);
2334
 
2335
/* Add the rest of the parameters, and execute */
2336
 
2337
    res = ExecuteSpecialProcessor (Fullpath, argv, envp, ForkAction, wb);
2338
 
2339
/* Release allocated space */
2340
 
2341
    if (params != null)
2342
        ReleaseMemoryCell ((void *)params);
2343
 
2344
    return res;
2345
}
2346
 
2347
 
2348
/*
2349
 * Convert errno to error message on execute
2350
 */
2351
static char * F_LOCAL
2352
ConvertErrorNumber (void)
2353
{
2354
    switch (errno)
2355
    {
2356
        case ENOMEM:
2357
            return strerror (ENOMEM);
2358
 
2359
        case ENOEXEC:
2360
            return "program corrupt";
2361
 
2362
        case E2BIG:
2363
            return AE2big;
2364
 
2365
        case ENOENT:
2366
            return NotFound;
2367
 
2368
        case 0:
2369
            return "No Shell";
2370
    }
2371
 
2372
    return "cannot execute";
2373
}
2374
 
2375
 
2376
/*
2377
 * Convert UNIX format lines to DOS format if appropriate.
2378
 * Build Environment variable for some programs.
2379
 */
2380
static int F_LOCAL
2381
EnvironExecute (char **argv, int ForkAction)
2382
{
2383
    int         s_errno;
2384
    int         RetVal = 1;
2385
    char        *NewArgs[3];
2386
    char        *cp;
2387
 
2388
/* If this command does not pass the command string in the environment,
2389
 * no action required
2390
 */
2391
 
2392
    if (ExecProcessingMode.Flags != EP_ENVIRON)
2393
        return 0;
2394
 
2395
    if ((cp = BuildOS2String (&argv[1], ExecProcessingMode.FieldSep))
2396
                 == (char *)NULL)
2397
    {
2398
        ExecProcessingMode.Flags = EP_NONE;
2399
        return 0;
2400
    }
2401
 
2402
    SetVariableFromString (ExecProcessingMode.Name, cp);
2403
    SetVariableStatus (ExecProcessingMode.Name, STATUS_EXPORT);
2404
 
2405
/* Build and execute the environment */
2406
 
2407
    NewArgs[0] = argv[0];
2408
    NewArgs[1] = ExecProcessingMode.Name;
2409
    NewArgs[2] = (char *)NULL;
2410
 
2411
    RetVal = LocalExecve (NewArgs, BuildCommandEnvironment (), ForkAction);
2412
    s_errno = errno;
2413
    UnSetVariable (ExecProcessingMode.Name, -1, FALSE);
2414
    errno = s_errno;
2415
    return RetVal;
2416
}
2417
 
2418
 
2419
/*
2420
 * Check Parameter line length
2421
 *
2422
 * Under OS2, we don't build the command line.  Just check it.
2423
 */
2424
static bool F_LOCAL
2425
CheckParameterLength (char **argv)
2426
{
2427
    int         CmdLineLength;
2428
    char        *CommandLine;
2429
    bool        RetVal = FALSE;
2430
    char        **SavedArgs = argv;
2431
    int         i;
2432
 
2433
/* Check for special case.  If there are any special characters and we can
2434
 * use UNIX mode, use it
2435
 */
2436
 
2437
    if (FL_TEST(FLAG_DEBUG_EXECUTE))
2438
    {
2439
        fprintf (stderr, " Mode: ");
2440
        PrintProgramMode (&ExecProcessingMode);
2441
 
2442
        fprintf (stderr, " args: Raw dump ...\n");
2443
        for (i = 0; argv[i]; i++)
2444
        {
2445
            fprintf (stderr, " args: [%02d] %s.\n", i, argv[i]);
2446
        }
2447
 
2448
    }
2449
 
2450
    if (ExecProcessingMode.Flags & EP_UNIXMODE)
2451
    {
2452
        for (i = 0; argv[i]; i++)
2453
            if (strpbrk (argv[i], WildCards) != (char *)NULL)
2454
            {
2455
                if (FL_TEST(FLAG_DEBUG_EXECUTE))
2456
                    fprintf (stderr, " args: contains wild cards @%02d.\n",i);
2457
 
2458
                return FALSE;
2459
            }
2460
    }
2461
 
2462
#if (OS_TYPE == OS_NT)
2463
    if (ApplicationType & EXETYPE_NT)
2464
    {
2465
        if (FL_TEST(FLAG_DEBUG_EXECUTE))
2466
            fprintf (stderr, " args: NT target.\n");
2467
 
2468
        return TRUE;                            /* APY, 05/05/04 */
2469
    }
2470
#endif
2471
 
2472
/*
2473
 * Do any parameter conversion - adding quotes or backslashes, but don't
2474
 * update argv.
2475
 */
2476
 
2477
    CmdLineLength = CountNumberArguments (argv = SavedArgs);
2478
 
2479
    if ((SavedArgs = (char **)
2480
                AllocateMemoryCell ((CmdLineLength + 1) * sizeof (char *)))
2481
                == (char **)NULL)
2482
        return FALSE;
2483
 
2484
/* Save a copy of the argument addresses */
2485
 
2486
    memcpy (SavedArgs, argv, (CmdLineLength + 1) * sizeof (char *));
2487
 
2488
/* Build the command line */
2489
 
2490
    if ((CommandLine = BuildOS2String (ProcessSpaceInParameters (SavedArgs),
2491
                CHAR_SPACE)) == (char *)NULL)
2492
    {
2493
        ReleaseMemoryCell (SavedArgs);
2494
        return FALSE;
2495
    }
2496
 
2497
/*
2498
 * Check command line length. Under DOS, this is simple.  On OS/2, we have
2499
 * to remember that we default to the EMX interface, which requires
2500
 * twice as much space, plus some nulls.  Also remember start DOS programs
2501
 * on OS/2 or NT have restricted space.
2502
 *
2503
 * HACK:        When a DOS extender assume 1/3 the command line
2504
 *              is needed by the stub (eg DOS4G).
2505
 */
2506
    CmdLineLength = strlen (CommandLine);
2507
 
2508
    if ((ApplicationType & EXETYPE_DOS) &&
2509
         ((CmdLineLength >= CMD_LINE_MAX - 2) ||
2510
          ((ExecProcessingMode.Flags & EP_DOSEXT) && CmdLineLength >= (CMD_LINE_MAX/3*2))) )
2511
    {
2512
        errno = E2BIG;
2513
    }
2514
 
2515
#if (OS_TYPE == OS_OS2)
2516
    else if (CmdLineLength >= (((CMD_LINE_MAX - 2) / 2) +
2517
                CountNumberArguments (SavedArgs) + 3))
2518
    {
2519
        errno = E2BIG;
2520
    }
2521
 
2522
#elif (OS_TYPE == OS_NT)
2523
    else if (CmdLineLength >= CMD_LINE_MAX - 2)
2524
    {
2525
        errno = E2BIG;
2526
    }
2527
#endif
2528
 
2529
/* Terminate the line */
2530
 
2531
    else
2532
    {
2533
        RetVal = TRUE;
2534
    }
2535
 
2536
    if (!RetVal && FL_TEST(FLAG_DEBUG_EXECUTE))
2537
        fprintf (stderr, " args: length exceeds command line.\n");
2538
 
2539
    ReleaseMemoryCell (SavedArgs);
2540
    ReleaseMemoryCell (CommandLine);
2541
 
2542
    return RetVal;
2543
}
2544
 
2545
 
2546
/*
2547
 * Convert any parameters with spaces in the to start and end with double
2548
 * quotes.
2549
 *
2550
 * Under OS2, the old string is NOT released.
2551
 */
2552
 
2553
static char ** F_LOCAL
2554
ProcessSpaceInParameters (char **argv)
2555
{
2556
    char        **Start = argv;
2557
    char        *new;
2558
    char        *cp;
2559
    char        *sp;
2560
    int         Count;
2561
 
2562
/* If noquote set, don't even try */
2563
 
2564
    if (ExecProcessingMode.Flags & EP_NOQUOTE)
2565
        return Start;
2566
 
2567
/* Protect parameters with TABS */
2568
 
2569
    while (*argv != (char *)NULL)
2570
    {
2571
        if ((strpbrk (*argv, " \t") != (char *)NULL) ||
2572
            (strlen (*argv) == 0) ||
2573
            ((ExecProcessingMode.Flags & EP_QUOTEWILD) &&
2574
             (strpbrk (*argv, WildCards) != (char *)NULL)))
2575
        {
2576
 
2577
/* Count number of Double quotes in the parameter */
2578
 
2579
            Count = CountDoubleQuotes (*argv);
2580
 
2581
/* Get some memory - give up update if out of memory */
2582
 
2583
            if ((new = GetAllocatedSpace (strlen (*argv) + (Count * 2) +
2584
                                          3)) == (char *)NULL)
2585
                return Start;
2586
 
2587
            SetMemoryAreaNumber ((void *)new,
2588
                                 GetMemoryAreaNumber ((void *)*argv));
2589
            *new = CHAR_DOUBLE_QUOTE;
2590
 
2591
/* Escape any double quotes in the string */
2592
 
2593
            cp = *argv;
2594
            sp = new + 1;
2595
 
2596
            while (*cp)
2597
            {
2598
                if (*cp == CHAR_DOUBLE_QUOTE)
2599
                {
2600
                    *(sp++) = CHAR_META;
2601
                    *(sp++) = *(cp++);
2602
                }
2603
 
2604
                else if (*cp != CHAR_META)
2605
                    *(sp++) = *(cp++);
2606
 
2607
/* Handle escapes - count them */
2608
 
2609
                else
2610
                {
2611
                    *(sp++) = *(cp++);
2612
 
2613
                    if (*cp == CHAR_DOUBLE_QUOTE)
2614
                    {
2615
                        *(sp++) = CHAR_META;
2616
                        *(sp++) = CHAR_META;
2617
                    }
2618
 
2619
                    else if (*cp == 0)
2620
                    {
2621
                        *(sp++) = CHAR_META;
2622
                        break;
2623
                    }
2624
 
2625
                    *(sp++) = *(cp++);
2626
                }
2627
            }
2628
 
2629
/* Append the terminating double quotes */
2630
 
2631
            strcpy (sp, DoubleQuotes);
2632
            *argv = new;
2633
        }
2634
 
2635
/* Check for any double quotes */
2636
 
2637
        else if ((Count = CountDoubleQuotes (*argv)) != 0)
2638
        {
2639
 
2640
/* Got them - escape them */
2641
 
2642
            if ((new = GetAllocatedSpace (strlen (*argv) + Count + 1))
2643
                        == (char *)NULL)
2644
                return Start;
2645
 
2646
            SetMemoryAreaNumber ((void *)new,
2647
                                 GetMemoryAreaNumber ((void *)*argv));
2648
 
2649
/* Copy the string, escaping DoubleQuotes */
2650
 
2651
            cp = *argv;
2652
            sp = new;
2653
 
2654
            while ((*sp = *(cp++)) != 0)
2655
            {
2656
                if (*sp == CHAR_DOUBLE_QUOTE)
2657
                {
2658
                    *(sp++) = CHAR_META;
2659
                    *sp = CHAR_DOUBLE_QUOTE;
2660
                }
2661
 
2662
                sp++;
2663
            }
2664
 
2665
            *argv = new;
2666
        }
2667
 
2668
/* Next parameter */
2669
 
2670
        argv++;
2671
    }
2672
 
2673
    return Start;
2674
}
2675
 
2676
 
2677
/*
2678
 * Count DoubleQuotes
2679
 */
2680
static int F_LOCAL
2681
CountDoubleQuotes (char *string)
2682
{
2683
    int         Count = 0;
2684
 
2685
    while ((string = strchr (string, CHAR_DOUBLE_QUOTE)) != (char *)NULL)
2686
    {
2687
        Count++;
2688
        string++;
2689
    }
2690
 
2691
    return Count;
2692
}
2693
 
2694
 
2695
/*
2696
 * Save and Restore the Parameters array ($1, $2 etc)
2697
 */
2698
static void F_LOCAL
2699
SaveNumericParameters (char **wp, SaveParameters *SaveArea)
2700
{
2701
    SaveArea->Array = ParameterArray;
2702
    SaveArea->Count = ParameterCount;
2703
 
2704
    ParameterArray = wp;
2705
    for (ParameterCount = 0; ParameterArray[ParameterCount] != NOWORD;
2706
         ++ParameterCount)
2707
        continue;
2708
 
2709
    SetVariableFromNumeric (ParameterCountVariable, (long)--ParameterCount);
2710
}
2711
 
2712
static void F_LOCAL
2713
RestoreTheParameters (SaveParameters *SaveArea)
2714
{
2715
    ParameterArray = SaveArea->Array;
2716
    ParameterCount = SaveArea->Count;
2717
    SetVariableFromNumeric (ParameterCountVariable, (long)ParameterCount);
2718
}
2719
 
2720
 
2721
/*
2722
 * Special OS/2 processing for execve and spawnve
2723
 */
2724
#if (OS_TYPE == OS_OS2)
2725
static int F_LOCAL
2726
OS_DosExecProgram (
2727
    int Mode, char *Program, char **argv, char **envp)
2728
{
2729
    OSCALL_PARAM        fExecFlags;
2730
    void                (_SIGDECL *sig_int)();           /* Interrupt signal     */
2731
    RESULTCODES         rescResults;
2732
    PID                 pidProcess;
2733
    PID                 pidWait;
2734
    char                *OS2Environment;
2735
    char                *OS2Arguments;
2736
    char                **SavedArgs;
2737
    int                 argc;
2738
 
2739
/* Set the error module to null */
2740
 
2741
    *FailName = 0;
2742
    OS_DosExecPgmReturnCode = 0;
2743
 
2744
/* Convert spawn mode to DosExecPgm mode */
2745
 
2746
    switch (Mode)
2747
    {
2748
        case P_WAIT:
2749
        case P_NOWAIT:
2750
            fExecFlags = EXEC_ASYNCRESULT;
2751
            break;
2752
 
2753
        case P_NOWAITO:
2754
        case OLD_P_OVERLAY:
2755
            fExecFlags = EXEC_ASYNC;
2756
            break;
2757
 
2758
        case P_DETACH:
2759
            fExecFlags = EXEC_BACKGROUND;
2760
            break;
2761
    }
2762
 
2763
/* Build OS/2 argument string
2764
 *
2765
 * 1.  Count the number of arguments.
2766
 * 2.  Add 2 for: 1 - NULL; 2 - argv[0]; 3 - the stringed arguments
2767
 * 3.  save copy of arguments at offset 2.
2768
 * 4.  On original argv, process white space and convert to OS2 Argument string.
2769
 * 5.  Set up program name at offset 2 as ~argv
2770
 * 6.  Convert zero length args to "~" and args beginning with ~ to ~~
2771
 * 7.  Build OS2 Argument string (at last).
2772
 */
2773
 
2774
    argc = CountNumberArguments (argv);
2775
 
2776
    if ((SavedArgs = (char **)AllocateMemoryCell ((argc + 3) * sizeof (char *)))
2777
            == (char **)NULL)
2778
        return -1;
2779
 
2780
    memcpy (SavedArgs + 2, argv, (argc + 1) * sizeof (char *));
2781
 
2782
/* Set program name at Offset 0 */
2783
 
2784
    SavedArgs[0] = *argv;
2785
 
2786
/* Build OS2 Argument string in Offset 1 */
2787
 
2788
    if ((SavedArgs[1] = BuildOS2String (ProcessSpaceInParameters (&argv[1]),
2789
                                        CHAR_SPACE)) == (char *)NULL)
2790
        return -1;
2791
 
2792
/* Set up the new arg 2 - ~ + programname */
2793
 
2794
    if ((SavedArgs[2] = InsertCharacterAtStart (*argv)) == (char *)NULL)
2795
        return -1;
2796
 
2797
/* Convert zero length args and args starting with a ~ */
2798
 
2799
    for (argc = 3; SavedArgs[argc] != NOWORD; argc++)
2800
    {
2801
        if (strlen (SavedArgs[argc]) == 0)
2802
            SavedArgs[argc] = "~";
2803
 
2804
        else if ((*SavedArgs[argc] == CHAR_TILDE) &&
2805
                 ((SavedArgs[argc] = InsertCharacterAtStart (SavedArgs[argc]))
2806
                        == (char *)NULL))
2807
            return -1;
2808
    }
2809
 
2810
/* Build the full argument list */
2811
 
2812
    if ((OS2Arguments = BuildOS2String (SavedArgs, 0)) == (char *)NULL)
2813
        return -1;
2814
 
2815
/* Build OS/2 environment string */
2816
 
2817
    if ((OS2Environment = BuildOS2String (envp, 0)) == (char *)NULL)
2818
        return -1;
2819
 
2820
/* Change signal handling */
2821
 
2822
    if (fExecFlags == EXEC_ASYNCRESULT)
2823
    {
2824
        char    *cp;
2825
 
2826
/* Also change the window title to match the foreground program */
2827
 
2828
        if ((cp = strrchr (Program, CHAR_DOS_PATH)) == (char *)NULL)
2829
            cp = Program;
2830
 
2831
        else
2832
            cp++;
2833
 
2834
        strcpy (FailName, cp);
2835
 
2836
        if ((cp = strrchr (FailName, CHAR_PERIOD)) != (char *)NULL)
2837
            *cp = 0;
2838
 
2839
        SetWindowName (FailName);
2840
        *FailName = 0;
2841
 
2842
        IgnoreInterrupts = TRUE;
2843
        sig_int = signal (SIGINT, SIG_IGN);
2844
    }
2845
 
2846
/* Exec it - with hard-error processing turned off */
2847
 
2848
    DISABLE_HARD_ERRORS;
2849
 
2850
    OS_DosExecPgmReturnCode = DosExecPgm (FailName, sizeof (FailName),
2851
                                          fExecFlags, OS2Arguments,
2852
                                          OS2Environment, &rescResults,
2853
                                          Program);
2854
 
2855
    ENABLE_HARD_ERRORS;
2856
 
2857
    if (fExecFlags == EXEC_ASYNCRESULT)
2858
    {
2859
        signal (SIGINT, SIG_IGN);
2860
 
2861
/* If the process started OK, wait for it */
2862
 
2863
        if (((OS_DosExecPgmReturnCode == NO_ERROR) ||
2864
             (OS_DosExecPgmReturnCode == ERROR_INTERRUPT)) &&
2865
            (Mode == P_WAIT))
2866
        {
2867
            pidWait = rescResults.codeTerminate;
2868
 
2869
/* Re-try on interrupted system calls - and kill the child.  Why, because
2870
 * sometimes the kill does not go to the child
2871
 */
2872
 
2873
            while ((OS_DosExecPgmReturnCode = DosCwait (DCWA_PROCESSTREE,
2874
                                                        DCWW_WAIT,
2875
                                                        &rescResults,
2876
                                                        &pidProcess,
2877
                                                        pidWait))
2878
                                             == ERROR_INTERRUPT)
2879
                DosKillProcess (DKP_PROCESS, pidWait);
2880
        }
2881
 
2882
        IgnoreInterrupts = FALSE;
2883
        signal (SIGINT, sig_int);
2884
    }
2885
 
2886
 
2887
/*
2888
 * What happened ?.  OS/2 Error - Map to UNIX errno.  Why can't people
2889
 * write libraries right??  Or provide access at a high level.  We could
2890
 * call _dosret if the interface did not require me to write more
2891
 * assembler.
2892
 */
2893
 
2894
    if (OS_DosExecPgmReturnCode != 0)
2895
    {
2896
        switch (OS_DosExecPgmReturnCode)
2897
        {
2898
#ifdef EAGAIN
2899
            case ERROR_NO_PROC_SLOTS:
2900
                errno = EAGAIN;
2901
                return -1;
2902
#endif
2903
 
2904
            case ERROR_NOT_ENOUGH_MEMORY:
2905
                errno = ENOMEM;
2906
                return -1;
2907
 
2908
            case ERROR_ACCESS_DENIED:
2909
            case ERROR_DRIVE_LOCKED:
2910
            case ERROR_LOCK_VIOLATION:
2911
            case ERROR_SHARING_VIOLATION:
2912
                errno = EACCES;
2913
                return -1;
2914
 
2915
            case ERROR_FILE_NOT_FOUND:
2916
            case ERROR_PATH_NOT_FOUND:
2917
            case ERROR_PROC_NOT_FOUND:
2918
                errno = ENOENT;
2919
                return -1;
2920
 
2921
            case ERROR_BAD_ENVIRONMENT:
2922
            case ERROR_INVALID_DATA:
2923
            case ERROR_INVALID_FUNCTION:
2924
            case ERROR_INVALID_ORDINAL:
2925
            case ERROR_INVALID_SEGMENT_NUMBER:
2926
            case ERROR_INVALID_STACKSEG:
2927
            case ERROR_INVALID_STARTING_CODESEG:
2928
                errno = EINVAL;
2929
                return -1;
2930
 
2931
            case ERROR_TOO_MANY_OPEN_FILES:
2932
                errno = EMFILE;
2933
                return -1;
2934
 
2935
            case ERROR_INTERRUPT:
2936
            case ERROR_NOT_DOS_DISK:
2937
            case ERROR_SHARING_BUFFER_EXCEEDED:
2938
                errno = EIO;
2939
                return -1;
2940
 
2941
            case ERROR_BAD_ARGUMENTS:
2942
                errno = E2BIG;
2943
                return -1;
2944
 
2945
            default:
2946
                errno = ENOEXEC;
2947
                return -1;
2948
        }
2949
    }
2950
 
2951
/* If exec - exit */
2952
 
2953
    switch (Mode)
2954
    {
2955
        case OLD_P_OVERLAY:                     /* exec - exit at once */
2956
            FinalExitCleanUp (0);
2957
 
2958
        case P_WAIT:                            /* Get exit code        */
2959
            return rescResults.codeResult;
2960
 
2961
        case P_NOWAIT:                          /* Get PID or SID       */
2962
        case P_NOWAITO:
2963
        case P_DETACH:
2964
            return rescResults.codeTerminate;
2965
    }
2966
 
2967
    errno = EINVAL;
2968
    return -1;
2969
}
2970
 
2971
 
2972
/*
2973
 * Insert character at start of string
2974
 *
2975
 * Return NULL or new string
2976
 */
2977
 
2978
static char     *InsertCharacterAtStart (char *string)
2979
{
2980
    char        *cp;
2981
 
2982
    if ((cp = (char *)AllocateMemoryCell (strlen (string) + 2)) == (char *)NULL)
2983
        return (char *)NULL;
2984
 
2985
    *cp = CHAR_TILDE;
2986
    strcpy (cp + 1, string);
2987
 
2988
    return cp;
2989
}
2990
#endif  /*OS2*/
2991
 
2992
 
2993
/*
2994
 * Special NT processing for execve and spawnve.  Not really sure what to
2995
 * do here yet!
2996
 */
2997
#if (OS_TYPE == OS_NT)
2998
static int F_LOCAL
2999
OS_DosExecProgram (
3000
    int Mode, char *Program, char **argv, char **envp )
3001
{
3002
#if (REMOVED)
3003
    DWORD                       dwLogicalDrives = GetLogicalDrives();
3004
    char                        szNewDrive[4];
3005
    char                        *nt_ep;
3006
    char                        *temp;
3007
    unsigned int                i;
3008
#endif
3009
    char                        **new_envp;
3010
    char                        ldir[PATH_MAX + 6];
3011
    int                         off = 0;
3012
    STARTUPINFO                 StartupInfo;
3013
    PROCESS_INFORMATION         ProcessInformation;
3014
    char                        *Environment;
3015
    char                        *PgmInputs;
3016
    BOOL                        rv;
3017
    int                         ExitCode;
3018
 
3019
    OS_DosExecPgmReturnCode = 0;
3020
 
3021
/*
3022
 * Set up NT directory variables
3023
 */
3024
 
3025
    if ((new_envp = (char **)GetAllocatedSpace (GetMemoryCellSize (envp) +
3026
                                                sizeof (char *) * 26))
3027
                  == (char **)NULL)
3028
    {
3029
        errno = ENOMEM;
3030
        return -1;
3031
    }
3032
 
3033
#if (REMOVED)
3034
    strcpy (szNewDrive, "x:");
3035
 
3036
    for (i = 0; i < 25; i++)
3037
    {
3038
        if (dwLogicalDrives & (1L << i))
3039
        {
3040
            szNewDrive[0] = (char)(i + 'A');
3041
 
3042
/* If the drive does not exist - give up */
3043
 
3044
            DISABLE_HARD_ERRORS;
3045
            ExitCode = GetFullPathName (szNewDrive, PATH_MAX + 6, ldir, &temp);
3046
            ENABLE_HARD_ERRORS;
3047
 
3048
            if (!ExitCode)
3049
                continue;
3050
 
3051
            if ((nt_ep = GetAllocatedSpace (strlen (ldir) + 5)) == (char *)NULL)
3052
            {
3053
                errno = ENOMEM;
3054
                return -1;
3055
            }
3056
 
3057
            sprintf (nt_ep, "=%c:=%s", i + 'A', ldir);
3058
            new_envp[off++] = nt_ep;
3059
        }
3060
    }
3061
#endif  /*REMOVED*/
3062
 
3063
    memcpy (&new_envp[off], envp, GetMemoryCellSize (envp));
3064
 
3065
/* Setup environment block */
3066
 
3067
    if ((Environment = BuildOS2String (new_envp, 0)) == (char *)NULL)
3068
        return -1;
3069
 
3070
/* Setup parameter block */
3071
 
3072
    ProcessSpaceInParameters (argv);
3073
 
3074
    if (strchr(Program, ' ') == NULL || Program[0] == '"')
3075
        argv[0] = Program;                      /* no spaces or quoted */
3076
    else
3077
    {                                           /* must quote .. i hate WIN */
3078
        (void)sprintf( ldir, "\"%s\"", Program );
3079
        argv[0] = ldir;
3080
    }
3081
 
3082
    if ((PgmInputs = BuildOS2String (argv, CHAR_SPACE)) == (char *)NULL)
3083
        return -1;
3084
 
3085
/* Set up startup info */
3086
 
3087
    memset (&StartupInfo, 0, sizeof (StartupInfo));
3088
 
3089
    StartupInfo.cb = sizeof (StartupInfo);
3090
    StartupInfo.lpDesktop = NULL;
3091
    StartupInfo.lpTitle = NULL;
3092
    StartupInfo.dwFlags = 0;
3093
 
3094
/* Change Window name ? */
3095
 
3096
    if (Mode == P_WAIT)
3097
    {
3098
        char    *cp;
3099
        char    *Name;
3100
 
3101
/* Also change the window title to match the foreground program */
3102
 
3103
        if ((cp = strrchr (Program, CHAR_DOS_PATH)) == (char *)NULL)
3104
            cp = Program;
3105
 
3106
        else
3107
            cp++;
3108
 
3109
        Name = StringCopy (cp);
3110
 
3111
        if ((cp = strrchr (Name, CHAR_PERIOD)) != (char *)NULL)
3112
            *cp = 0;
3113
 
3114
        SetWindowName (Name);
3115
        ReleaseMemoryCell (Name);
3116
    }
3117
 
3118
/* Exec it - with hard-error processing turned off */
3119
 
3120
    if (FL_TEST(FLAG_DEBUG_EXECUTE))
3121
        fprintf (stderr, "CreateProcess(%s,%s)\n", Program, PgmInputs);
3122
 
3123
    DISABLE_HARD_ERRORS;
3124
    rv = CreateProcess (
3125
                Program, PgmInputs, NULL, NULL, TRUE,
3126
                NORMAL_PRIORITY_CLASS,
3127
                Environment, NULL,
3128
                &StartupInfo, &ProcessInformation );
3129
    ENABLE_HARD_ERRORS;
3130
 
3131
    if (!rv)
3132
    {
3133
        OS_DosExecPgmReturnCode = GetLastError ();
3134
        errno = ENOENT;
3135
        return -1;
3136
    }
3137
 
3138
    CloseHandle(ProcessInformation.hThread);
3139
 
3140
    if (Mode == P_WAIT)
3141
    {
3142
        if (WaitForSingleObject(
3143
                ProcessInformation.hProcess, INFINITE) == WAIT_OBJECT_0)
3144
        {
3145
            DWORD exitcode = 0;
3146
 
3147
            GetExitCodeProcess(ProcessInformation.hProcess, &exitcode);
3148
            ExitCode = (int) exitcode;
3149
            return ExitCode;
3150
        }
3151
        else
3152
        {
3153
            OS_DosExecPgmReturnCode = GetLastError ();
3154
            errno = EINVAL;
3155
            ExitCode = -1;
3156
        }
3157
        CloseHandle(ProcessInformation.hProcess);
3158
        return (ExitCode);
3159
    }
3160
 
3161
/* Otherwise, return the process ID. */
3162
 
3163
    return (ProcessInformation.dwProcessId);
3164
}
3165
#endif  /*OS_NT*/
3166
 
3167
 
3168
/*
3169
 * Execute a Function
3170
 */
3171
static bool F_LOCAL
3172
ExecuteFunction (
3173
     char **wp, int *RetVal, bool CGVLCalled)
3174
{
3175
    Break_C                     *s_RList = Return_List;
3176
    Break_C                     *s_BList = Break_List;
3177
    Break_C                     *s_SList = SShell_List;
3178
    Break_C                     BreakContinue;
3179
    C_Op                        *New;
3180
    FunctionList                *s_CurrentFunction = CurrentFunction;
3181
    SaveParameters              s_Parameters;
3182
    FunctionList                *fop;
3183
    GetoptsIndex                GetoptsSave;
3184
    bool                        TrapZeroExists;
3185
    char                        *Ext;
3186
    char                        *FullPath;
3187
 
3188
    TrapZeroExists = C2bool (GetVariableAsString ("~0", FALSE) != null);
3189
 
3190
/* Find the extension type - if it exists */
3191
 
3192
    FullPath = GetAllocatedSpace (FFNAME_MAX);
3193
 
3194
/* Check for a psuedo-function */
3195
 
3196
    switch (ExtensionType (Ext = FindFileAndExtension (FullPath,
3197
                                                       wp[0],
3198
                                                       ExecuteFunctionList)))
3199
    {
3200
        case EXTENSION_OTHER:
3201
            if ((fop = LookUpFunction (Ext, TRUE)) == (FunctionList *)NULL)
3202
                return FALSE;
3203
 
3204
            wp[0] = FullPath;
3205
            break;
3206
 
3207
/* Common extension for all .sh, .ksh and "" scripts.  If we find one, use
3208
 * it, otherwise, check for a function of this name
3209
 */
3210
 
3211
        case EXTENSION_SHELL_SCRIPT:
3212
            Ext = ".ksh";
3213
 
3214
        case EXTENSION_BATCH:
3215
            if ((fop = LookUpFunction (Ext, TRUE)) != (FunctionList *)NULL)
3216
            {
3217
                wp[0] = FullPath;
3218
                break;
3219
            }
3220
 
3221
/* Check for a real function */
3222
 
3223
        case EXTENSION_NOT_FOUND:
3224
        case EXTENSION_EXECUTABLE:
3225
        default:
3226
            if ((fop = LookUpFunction (wp[0], FALSE)) == (FunctionList *)NULL)
3227
                return FALSE;
3228
    }
3229
 
3230
/* Ok, we really have a function to execute.
3231
 * Save the current variable list
3232
 */
3233
 
3234
    if (!CGVLCalled && (CreateGlobalVariableList (FLAGS_FUNCTION) == -1))
3235
    {
3236
        *RetVal =  -1;
3237
        return TRUE;
3238
    }
3239
 
3240
/* Set up $0..$n for the function */
3241
 
3242
    SaveNumericParameters (wp, &s_Parameters);
3243
 
3244
/* Save Getopts pointers */
3245
 
3246
    GetGetoptsValues (&GetoptsSave);
3247
 
3248
/* Process the function */
3249
 
3250
    if (setjmp (BreakContinue.CurrentReturnPoint) == 0)
3251
    {
3252
        CurrentFunction = fop;
3253
        Break_List = (Break_C *)NULL;
3254
        BreakContinue.NextExitLevel = Return_List;
3255
        Return_List = &BreakContinue;
3256
        New = CopyFunction (fop->tree->left);
3257
        *RetVal = ExecuteParseTree (New, NOPIPE, NOPIPE, EXEC_FUNCTION);
3258
    }
3259
 
3260
/* A return has been executed - Unlike, while and for, we just need to
3261
 * restore the local execute stack level and the return will restore
3262
 * the correct I/O.
3263
 */
3264
 
3265
    else
3266
        *RetVal = (int)GetVariableAsNumeric (StatusVariable);
3267
 
3268
    if (!TrapZeroExists)
3269
        RunTrapCommand (0);             /* Exit trap                    */
3270
 
3271
/* Restore the old $0, and previous return address */
3272
 
3273
    SaveGetoptsValues (GetoptsSave.Index, GetoptsSave.SubIndex);
3274
    Break_List  = s_BList;
3275
    Return_List = s_RList;
3276
    SShell_List = s_SList;
3277
    CurrentFunction = s_CurrentFunction;
3278
    RestoreTheParameters (&s_Parameters);
3279
 
3280
    return TRUE;
3281
}
3282
 
3283
 
3284
/*
3285
 * Print Load error message
3286
 */
3287
 
3288
#if (OS_TYPE == OS_OS2)
3289
static void F_LOCAL PrintLoadError (char *path)
3290
{
3291
    if (*FailName)
3292
        PrintWarningMessage ("%s: %s\nSYS1804: Cannot find file - %s", path,
3293
                             ConvertErrorNumber(), FailName);
3294
 
3295
    else if (OS_DosExecPgmReturnCode)
3296
        PrintWarningMessage ("%s: %s\n%s", path, ConvertErrorNumber(),
3297
                     GetOSSystemErrorMessage (OS_DosExecPgmReturnCode));
3298
 
3299
    else
3300
        PrintWarningMessage ("%s: %s", path, ConvertErrorNumber());
3301
}
3302
#endif
3303
 
3304
/* NT Version */
3305
 
3306
#if (OS_TYPE == OS_NT)
3307
static void F_LOCAL PrintLoadError (char *path)
3308
{
3309
    if (OS_DosExecPgmReturnCode)
3310
        PrintWarningMessage ("%s: %s\n%s", path, ConvertErrorNumber(),
3311
                     GetOSSystemErrorMessage (OS_DosExecPgmReturnCode));
3312
 
3313
    else
3314
        PrintWarningMessage ("%s: %s", path, ConvertErrorNumber());
3315
}
3316
#endif
3317
 
3318
/* DOS Version */
3319
 
3320
#if (OS_TYPE == OS_DOS)
3321
static void F_LOCAL PrintLoadError (char *path)
3322
{
3323
    PrintWarningMessage (BasicErrorMessage, path, ConvertErrorNumber ());
3324
}
3325
#endif
3326
 
3327
/* DOS Version */
3328
 
3329
#if (OS_TYPE == OS_UNIX)
3330
static void F_LOCAL PrintLoadError (char *path)
3331
{
3332
    PrintWarningMessage (BasicErrorMessage, path, ConvertErrorNumber ());
3333
}
3334
#endif
3335
 
3336
/*
3337
 * Make the exported environment from the exported names in the dictionary.
3338
 * Keyword assignments will already have been done.  Convert to MSDOS
3339
 * format if flag set and m enabled
3340
 */
3341
static char ** F_LOCAL
3342
BuildCommandEnvironment (void)
3343
{
3344
/* Update SECONDS and RANDOM */
3345
 
3346
    HandleSECONDandRANDOM ();
3347
 
3348
/* Build the environment by walking the tree */
3349
 
3350
    BCE_WordList = (Word_B *)NULL;
3351
    BCE_Length   = 0;
3352
 
3353
    twalk (VariableTree, BuildEnvironmentEntry);
3354
 
3355
    if (BCE_Length >= 0x7f00)
3356
        return (char **)NULL;
3357
 
3358
    return GetWordList (AddWordToBlock (NOWORD, BCE_WordList));
3359
}
3360
 
3361
 
3362
/*
3363
 * TWALK Function - Build Export VARIABLE list from VARIABLE tree
3364
 */
3365
static void
3366
BuildEnvironmentEntry (const void *key, VISIT visit, int level)
3367
{
3368
    VariableList        *vp = *(VariableList **)key;
3369
    char                *cp;
3370
    char                *sp;
3371
    int                 tlen;
3372
 
3373
    (void) level;
3374
 
3375
    if ((visit != postorder) && (visit != leaf))
3376
        return;
3377
 
3378
    if ((vp->status & STATUS_EXPORT) && (vp->index == 0))
3379
    {
3380
        cp = GetVariableAsString (vp->name, TRUE);
3381
        tlen = strlen (vp->name) + strlen (cp) + 2;
3382
 
3383
        if ((BCE_Length += tlen) >= 0x7f00)
3384
            return;
3385
 
3386
        strcpy ((sp = GetAllocatedSpace (tlen)), vp->name);
3387
        SetMemoryAreaNumber ((void *)sp, MemoryAreaLevel);
3388
        strcat (sp, "=");
3389
        strcat (sp, cp);
3390
 
3391
        BCE_WordList = AddWordToBlock (sp, BCE_WordList);
3392
 
3393
/* If MSDOS mode, we need to copy the variable, convert / to \ and put
3394
 * the copy in the environment list instead
3395
 */
3396
 
3397
        if (((ShellGlobalFlags & FLAGS_MSDOS_FORMAT) ||
3398
             (ExecProcessingMode.Flags & EP_EXPORT)) &&
3399
            (vp->status & STATUS_CONVERT_MSDOS))
3400
        {
3401
            cp = StringCopy (BCE_WordList->w_words[BCE_WordList->w_nword - 1]);
3402
            BCE_WordList->w_words[BCE_WordList->w_nword - 1] = PATH_TO_DOS (cp);
3403
        }
3404
    }
3405
}
3406
 
3407
 
3408
/*
3409
 * Parse and Execute a command in the current shell
3410
 */
3411
int
3412
RunACommand (Source *s, char **params)
3413
{
3414
    jmp_buf             erp;
3415
    int                 RetVal = -1;
3416
    Break_C             *S_RList = Return_List; /* Save loval links     */
3417
    Break_C             *S_BList = Break_List;
3418
    int                 LS_depth = Execute_stack_depth++;
3419
    C_Op                *outtree;
3420
    bool                s_ProcessingEXECCommand = ProcessingEXECCommand;
3421
    SaveParameters      s_Parameters;
3422
 
3423
/* Create a new save area */
3424
 
3425
    MemoryAreaLevel++;
3426
 
3427
/* Set up $0..$n for the command if appropriate.  Note that $0 does not
3428
 * change
3429
 */
3430
 
3431
    if (params != NOWORDS)
3432
    {
3433
        SaveNumericParameters (params, &s_Parameters);
3434
        ParameterArray[0] = s_Parameters.Array[0];
3435
    }
3436
 
3437
/* Execute the command */
3438
 
3439
    CreateNewEnvironment ();
3440
 
3441
    Return_List = (Break_C *)NULL;
3442
    Break_List  = (Break_C *)NULL;
3443
    ProcessingEXECCommand = TRUE;
3444
    e.ErrorReturnPoint = (ErrorPoint)NULL;
3445
 
3446
    if (SetErrorPoint (erp) == 0)
3447
    {
3448
 
3449
/* Read Input until completed */
3450
 
3451
        for (;;)
3452
        {
3453
            if (((outtree = BuildParseTree (s)) != (C_Op *)NULL) &&
3454
                (outtree->type == TEOF))
3455
                break;
3456
 
3457
            RetVal = ExecuteParseTree (outtree, NOPIPE, NOPIPE, 0);
3458
        }
3459
    }
3460
 
3461
    QuitCurrentEnvironment ();
3462
 
3463
/* Restore the environment */
3464
 
3465
    ClearExtendedLineFile ();
3466
    Return_List = S_RList;
3467
    Break_List = S_BList;
3468
    ProcessingEXECCommand = s_ProcessingEXECCommand;
3469
 
3470
/* Restore $0..$n */
3471
 
3472
    if (params != NOWORDS)
3473
        RestoreTheParameters (&s_Parameters);
3474
 
3475
    RestoreEnvironment (RetVal, LS_depth);
3476
    ReleaseMemoryArea (MemoryAreaLevel--);
3477
    return RetVal;
3478
}
3479
 
3480
 
3481
/*
3482
 * Get the OS/2 Error message
3483
 */
3484
#if (OS_TYPE == OS_OS2)
3485
char *
3486
GetOSSystemErrorMessage (OSCALL_RET code)
3487
{
3488
    static char         Buffer [FFNAME_MAX + 9];
3489
    char                *Buffer1;
3490
    OSCALL_PARAM        MsgLength;
3491
    OSCALL_RET          rc;
3492
    char                *ip;
3493
    char                *op = Buffer;
3494
    char                *sp;
3495
    int                 len;
3496
 
3497
/* For some reason DPATH does not work with the DosGetMessage API as the
3498
 * spec say it should on OS/2 2.x.  Probably something we do.  It usually
3499
 * is!  So emulate it!
3500
 */
3501
 
3502
    if ((len = strlen (sp = GetVariableAsString ("DPATH", FALSE))) < FFNAME_MAX)
3503
        len = FFNAME_MAX;
3504
 
3505
    if ((Buffer1 = GetAllocatedSpace (len)) == (char *)NULL)
3506
    {
3507
        sprintf (Buffer, "SYS%.4d: No memory to get message text", code);
3508
        return Buffer;
3509
    }
3510
 
3511
    sp = PATH_TO_UNIX (strcpy (Buffer1, sp));
3512
 
3513
    do
3514
    {
3515
        sp = BuildNextFullPathName (sp, "OSO001.MSG", Buffer);
3516
    } while ((access (Buffer, F_OK) != 0) && (sp != (char *)NULL));
3517
 
3518
/* If not found - use the default */
3519
 
3520
    if (sp == (char *)NULL)
3521
    {
3522
        strcpy (Buffer, "c:/OS2/SYSTEM/OSO001.MSG");
3523
        *Buffer = GetDriveLetter (GetRootDiskDrive ());
3524
    }
3525
 
3526
/* Read the message */
3527
 
3528
    if ((rc = DosGetMessage (NULL, 0, ip = Buffer1, len, code, Buffer,
3529
                            &MsgLength)))
3530
        sprintf (Buffer, "SYS%.4d: No error message available (%d)",
3531
                 code, rc);
3532
 
3533
    else
3534
    {
3535
        if ((Buffer1[MsgLength - 1] == CHAR_NEW_LINE) &&
3536
            (Buffer1[MsgLength - 2] == CHAR_RETURN))
3537
            Buffer1[MsgLength - 2] = 0;
3538
 
3539
        else
3540
            Buffer1[MsgLength] = 0;
3541
 
3542
/* Check the error number is there */
3543
 
3544
        if (strncmp (Buffer1, "SYS", 3) != 0)
3545
        {
3546
            sprintf (op, "SYS%.4d: ", code);
3547
            op += strlen (op);
3548
        }
3549
 
3550
/* Remove interior CRs & NLs */
3551
 
3552
        while ((*(op) = *(ip++)))
3553
        {
3554
            if (*op == CHAR_NEW_LINE)
3555
                *(op++) = CHAR_SPACE;
3556
 
3557
            else if (*op != CHAR_RETURN)
3558
                op++;
3559
        }
3560
    }
3561
 
3562
    ReleaseMemoryCell (Buffer1);
3563
    return Buffer;
3564
}
3565
 
3566
 
3567
#  ifdef __WATCOMC__
3568
/*
3569
 * A cheat for WATCOM which does not have the DosGetMessage API.  This
3570
 * function is based an interpretation of the System message file and one or
3571
 * two others.  The format of the header is not know except for the flag at
3572
 * offset 0x0f.  It is not guaranteed to work.
3573
 */
3574
APIRET APIENTRY
3575
DosGetMessage(
3576
    PCHAR *ppchVTable, ULONG usVCount, PCHAR pchBuf,
3577
      ULONG cbBuf, ULONG usMsgNum, PSZ pszFileName, PULONG pcbMsg )
3578
{
3579
    int                         fd;
3580
#  pragma pack (1)
3581
    union {
3582
        struct {
3583
            USHORT              Start;
3584
            USHORT              End;
3585
        }                       ShortE;
3586
        struct {
3587
            ULONG               Start;
3588
            ULONG               End;
3589
        }                       LongE;
3590
    }                           Start, Current;
3591
#  pragma pack ()
3592
    char                        Type;
3593
    int                         Len;
3594
    unsigned long               Offset;
3595
    APIRET                      Res;
3596
 
3597
    if ((fd = open (pszFileName, O_RDONLY | O_BINARY)) < 0)
3598
        return _doserrno;
3599
 
3600
/* Get the message file format */
3601
 
3602
    if ((lseek (fd, 0x0fL, SEEK_SET) != 0x0fL) ||
3603
        (read (fd, &Type, 1) != 1) ||
3604
        (lseek (fd, 0x1fL, SEEK_SET) != 0x1fL))
3605
    {
3606
        Res = _doserrno;
3607
        close (fd);
3608
        return Res;
3609
    }
3610
 
3611
/* Read the start of message text location */
3612
 
3613
    Len = (Type) ? 4 : 8;
3614
 
3615
    if (read (fd, &Start, Len) != Len)
3616
    {
3617
        Res = _doserrno;
3618
        close (fd);
3619
        return Res;
3620
    }
3621
 
3622
/* Check the offset to the message */
3623
 
3624
    Offset = 0x1fL + (usMsgNum * ((Type) ? 2L : 4L));
3625
 
3626
    if (((Type) && (Offset >= Start.ShortE.Start)) ||
3627
        ((!Type) && (Offset >= Start.LongE.Start)))
3628
    {
3629
        close (fd);
3630
        return ERROR_MR_MID_NOT_FOUND;
3631
    }
3632
 
3633
/* Get the message location */
3634
 
3635
    if ((lseek (fd, Offset, SEEK_SET) != Offset) ||
3636
        (read (fd, &Current, Len) != Len))
3637
    {
3638
        Res = _doserrno;
3639
        close (fd);
3640
        return Res;
3641
    }
3642
 
3643
    if (((Type) && (Offset == Start.ShortE.Start - 2)) ||
3644
            ((!Type) && (Offset == Start.LongE.Start - 4)))
3645
    {
3646
        if ((Offset = lseek (fd, 0L, SEEK_END)) == -1L)
3647
        {
3648
            Res = _doserrno;
3649
            close (fd);
3650
            return Res;
3651
        }
3652
 
3653
        else if (Type)
3654
            Current.ShortE.End = (USHORT)Offset;
3655
 
3656
        else
3657
            Current.LongE.End = Offset;
3658
    }
3659
 
3660
/* Get the message length */
3661
 
3662
    if (Type)
3663
    {
3664
        *pcbMsg = Current.ShortE.End - Current.ShortE.Start;
3665
        Offset = Current.ShortE.Start;
3666
    }
3667
 
3668
    else
3669
    {
3670
        *pcbMsg = (USHORT)(Current.LongE.End - Current.LongE.Start);
3671
        Offset = Current.LongE.Start;
3672
    }
3673
 
3674
/* Check the message length */
3675
 
3676
    *pcbMsg += 8;
3677
 
3678
    if (*pcbMsg >= cbBuf)
3679
        return ERROR_MR_MSG_TOO_LONG;
3680
 
3681
    sprintf (pchBuf, "SYS%.4d: ", usMsgNum);
3682
 
3683
/* Seek to the start of the message and read its type */
3684
 
3685
    if ((lseek (fd, Offset, SEEK_SET) != Offset) ||
3686
        (read (fd, &Type, 1) != 1))
3687
    {
3688
        Res = _doserrno;
3689
        close (fd);
3690
        return Res;
3691
    }
3692
 
3693
    if (Type == '?')
3694
    {
3695
        close (fd);
3696
        return ERROR_MR_MID_NOT_FOUND;
3697
    }
3698
 
3699
/* Get the message itself */
3700
 
3701
    else if (read (fd, pchBuf + 9, *pcbMsg - 8) != *pcbMsg - 8)
3702
    {
3703
        Res = _doserrno;
3704
        close (fd);
3705
        return Res;
3706
    }
3707
 
3708
    close (fd);
3709
    *(pchBuf + *pcbMsg) = 0;
3710
    return 0;
3711
}
3712
#  endif
3713
#endif
3714
 
3715
 
3716
/*
3717
 * Get the Win NT Error message
3718
 */
3719
#if (OS_TYPE == OS_NT)
3720
char *
3721
GetOSSystemErrorMessage (OSCALL_RET code)
3722
{
3723
    DWORD               Source = 0;
3724
    static char         EBuffer[100];
3725
    char                *OSBuffer;
3726
    char                *Buffer1;
3727
    char                *ip;
3728
    char                *op;
3729
 
3730
/* Read the message */
3731
 
3732
    if (!FormatMessage ((FORMAT_MESSAGE_FROM_SYSTEM |
3733
                         FORMAT_MESSAGE_ALLOCATE_BUFFER), &Source, code, 0,
3734
                        (LPSTR)&OSBuffer, 100, NULL))
3735
    {
3736
        sprintf (EBuffer, "SYS%.4d: No error message available (%ld)",
3737
                 code, GetLastError ());
3738
        OSBuffer = EBuffer;
3739
    }
3740
 
3741
    else
3742
    {
3743
        op = &OSBuffer[strlen (OSBuffer) - 1];
3744
 
3745
        while (isspace (*op) && (op != OSBuffer))
3746
            op--;
3747
    }
3748
 
3749
/* Allocate local space.  If there isn't any give up and hope for the best */
3750
 
3751
    if ((Buffer1 = GetAllocatedSpace (strlen (OSBuffer) + 20)) == (char *)NULL)
3752
        return OSBuffer;
3753
 
3754
/* Transfer the system buffer to a local buffer.
3755
 * Check the error number is there
3756
 */
3757
 
3758
    op = Buffer1;
3759
    ip = OSBuffer;
3760
 
3761
    if (strncmp (OSBuffer, "SYS", 3) != 0)
3762
    {
3763
        sprintf (op, "SYS%.4d: ", code);
3764
        op += strlen (op);
3765
    }
3766
 
3767
/* Remove interior CRs & NLs */
3768
 
3769
    while ((*(op) = *(ip++)) != 0)
3770
    {
3771
        if (*op == CHAR_NEW_LINE)
3772
            *(op++) = CHAR_SPACE;
3773
 
3774
        else if (*op != CHAR_RETURN)
3775
            op++;
3776
    }
3777
 
3778
    if (OSBuffer != EBuffer)
3779
        LocalFree (OSBuffer);
3780
 
3781
    return Buffer1;
3782
}
3783
#endif
3784
 
3785
 
3786
/*
3787
 * Display a started job info
3788
 */
3789
#if (OS_TYPE != OS_DOS)
3790
static void F_LOCAL
3791
PrintPidStarted (void)
3792
{
3793
    if (PidInfo.Valid)
3794
        fprintf (stderr, "[%d] %d\n", PidInfo.JobNo, PidInfo.PidNo);
3795
 
3796
    PidInfo.Valid = FALSE;
3797
}
3798
#endif
3799
 
3800
 
3801
/*
3802
 * Print a list of names, with or without numbers
3803
 */
3804
void PrintAList (int ArgCount, char **ArgList)
3805
{
3806
    char        **pp = ArgList;
3807
    int         i, j;
3808
    int         ix;
3809
    int         MaxArgWidth = 0;
3810
    int         NumberWidth = 0;
3811
    int         ncols;
3812
    int         nrows;
3813
 
3814
    if (!ArgCount)
3815
        return;
3816
 
3817
/* get dimensions of the list */
3818
 
3819
    while (*pp != (char *)NULL)
3820
    {
3821
        i = strlen (*(pp++)) + 1;
3822
        MaxArgWidth = (i > MaxArgWidth) ? i : MaxArgWidth;
3823
    }
3824
 
3825
/*
3826
 * We print an index of the form
3827
 *      %d)
3828
 * in front of each entry.  Get the max width of this
3829
 */
3830
 
3831
    for (i = ArgCount, NumberWidth = 1; i >= 10; i /= 10)
3832
        NumberWidth++;
3833
 
3834
/* In the case of numbered lists, we go down if less than screen length */
3835
 
3836
    if (ArgCount < (MaximumLines - 5))
3837
    {
3838
        ncols = 1;
3839
        nrows = ArgCount;
3840
    }
3841
 
3842
    else
3843
    {
3844
        ncols = MaximumColumns / (MaxArgWidth + NumberWidth + 3);
3845
        nrows = ArgCount / ncols;
3846
 
3847
        if (ArgCount % ncols)
3848
            nrows++;
3849
 
3850
        if (!nrows)
3851
            nrows = 1;
3852
 
3853
        if (ncols > nrows)
3854
        {
3855
            nrows = ncols;
3856
            ncols = 1;
3857
        }
3858
    }
3859
 
3860
/* Display the list */
3861
 
3862
    for (i = 0; i < nrows; i++)
3863
    {
3864
        for (j = 0; j < ncols; j++)
3865
        {
3866
            if ((ix = j * nrows + i) < ArgCount)
3867
            {
3868
                printf ("%*d) ", NumberWidth, ix + 1);
3869
 
3870
                if (j != (ncols - 1))
3871
                    printf ("%-*.*s", MaxArgWidth, MaxArgWidth, ArgList[ix]);
3872
 
3873
                else
3874
                    printf ("%-s", ArgList[ix]);
3875
            }
3876
        }
3877
 
3878
        fputchar (CHAR_NEW_LINE);
3879
    }
3880
}
3881
 
3882
/*
3883
 * Are we tracking all commands?  Check there is no path in the command
3884
 */
3885
static void F_LOCAL
3886
TrackAllCommands (char *path, char *arg)
3887
{
3888
    if (FL_TEST (FLAG_TRACK_ALL) &&
3889
        (FindPathCharacter (arg) == (char *)NULL) &&
3890
        (!IsDriveCharacter (arg[1])))
3891
        SaveAlias (arg, PATH_TO_UNIX (path), TRUE);
3892
}
3893
 
3894
 
3895
/*
3896
 * Get the application type and get we can do it
3897
 */
3898
static bool F_LOCAL
3899
GetApplicationType (char *path)
3900
{
3901
    ApplicationType = QueryApplicationType (path);
3902
 
3903
    if (FL_TEST(FLAG_DEBUG_EXECUTE)) 
3904
        WhenceTypeDebug (ApplicationType);
3905
 
3906
/* Some type of error */
3907
 
3908
    if (ApplicationType & EXETYPE_ERROR)
3909
    {
3910
        if (ApplicationType == EXETYPE_UNKNOWN)
3911
        {
3912
            if (!FL_TEST (FLAG_WARNING))
3913
                fprintf (stderr, "sh: Cannot determine executable type <%s>\n",
3914
                         path);
3915
 
3916
            return TRUE;
3917
        }
3918
 
3919
        else if (ApplicationType == EXETYPE_BAD_FILE)
3920
            errno = ENOENT;
3921
 
3922
        else
3923
            errno = ENOEXEC;
3924
 
3925
        return FALSE;
3926
    }
3927
 
3928
/*
3929
 * This is where it gets complicated - Sort out DOS!
3930
 */
3931
#if (OS_TYPE == OS_DOS)
3932
    if (ExecProcessingMode.Flags & EP_IGNTYPE)
3933
    {
3934
        ApplicationType = EXETYPE_DOS_CUI;
3935
        return TRUE;
3936
    }
3937
 
3938
    else if (ApplicationType == EXETYPE_DOS_GUI)
3939
    {
3940
        if ((BaseOS == BASE_OS_WIN) &&
3941
            (GetVariableAsString (LIT_STARTWINP, FALSE) == null))
3942
        {
3943
            if (!FL_TEST (FLAG_WARNING))
3944
                feputs ("sh: Start this applications from Windows\n");
3945
 
3946
            errno = ENOEXEC;
3947
            return FALSE;
3948
        }
3949
    }
3950
 
3951
    else if ((ApplicationType == EXETYPE_DOS_32) && (BaseOS == BASE_OS_NT))
3952
        return BadApplication ("DOS 32-bit");
3953
 
3954
    else if (ApplicationType & EXETYPE_OS2)
3955
        return BadApplication ("OS/2");
3956
 
3957
    else if (ApplicationType & EXETYPE_NT)
3958
        return (BaseOS == BASE_OS_NT) ? TRUE : BadApplication ("Win NT");
3959
 
3960
#elif (OS_TYPE == OS_OS2)
3961
    if (ApplicationType & EXETYPE_NT)
3962
        return BadApplication ("Win NT");
3963
 
3964
# if (OS_SIZE == OS_16)
3965
    if (OS_VERS_N < 2)
3966
    {
3967
        if (ApplicationType & EXETYPE_DOS)
3968
            return BadApplication (LIT_dos);
3969
 
3970
        else if (ApplicationType & EXETYPE_OS2_32)
3971
            return BadApplication ("OS/2 32-bit");
3972
    }
3973
#  endif
3974
 
3975
    if (ApplicationType == EXETYPE_DOS_32)
3976
        return BadApplication ("DOS 32-bit");
3977
 
3978
#elif (OS_TYPE == OS_NT)
3979
    if (ApplicationType == EXETYPE_DOS_32)
3980
        return BadApplication ("DOS 32-bit");
3981
 
3982
/*  Yes we can ,,,,,
3983
 *  else if (ApplicationType & EXETYPE_OS2_32)
3984
 *      return BadApplication ("OS/2 32-bit");
3985
 */
3986
#endif
3987
 
3988
/* In the end - execute it */
3989
 
3990
    return TRUE;
3991
}
3992
 
3993
 
3994
/*
3995
 * Print Bad application warning
3996
 */
3997
static bool F_LOCAL
3998
BadApplication (char *mes)
3999
{
4000
    if (!FL_TEST (FLAG_WARNING))
4001
        fprintf (stderr, "sh: Cannot execute %s applications\n", mes);
4002
 
4003
    errno = ENOEXEC;
4004
    return FALSE;
4005
}
4006
 
4007
 
4008
/*
4009
 * Handle a Windows Program
4010
 */
4011
#if (OS_TYPE != OS_UNIX)
4012
static int F_LOCAL
4013
ExecuteWindows (
4014
    char *Fullpath, char **argv, char **envp, int  ForkAction )
4015
{
4016
    int         res;
4017
#if (OS_TYPE == OS_DOS)
4018
    Word_B      *wb = (Word_B *)NULL;
4019
 
4020
    if (!SetUpCLI ((BaseOS != BASE_OS_WIN)
4021
                        ? "win"
4022
                        : GetVariableAsString (LIT_STARTWINP, FALSE), &wb))
4023
        return -1;
4024
 
4025
#elif (OS_TYPE == OS_OS2)
4026
    Word_B      *wb = AddWordToBlock ("winos2", (Word_B *)NULL);
4027
#elif (OS_TYPE == OS_NT)
4028
    Word_B      *wb = (Word_B *)NULL;
4029
#endif
4030
 
4031
/* Add the rest of the parameters and execute */
4032
 
4033
    ForkAction |= EXEC_WINDOWS;
4034
 
4035
    res = ExecuteSpecialProcessor (Fullpath, argv, envp, ForkAction, wb);
4036
 
4037
/* A cheat to stop us exec'ing the program again.  Normally,
4038
 * ExecuteSpecialProcessor is call below ExecuteProgram.  However, in the
4039
 * case of a Windows prog, its called above, so we set this to stop
4040
 * Executeprogram invoking the program again, but telling it that no
4041
 * swapping was required.
4042
 */
4043
 
4044
    ExecProcessingMode.Flags = EP_NOSWAP;
4045
    return res;
4046
}
4047
#endif  /*!OS_UNIX*/
4048
 
4049
 
4050
/*
4051
 * Generic processing for script and windows programs
4052
 */
4053
static int F_LOCAL
4054
ExecuteSpecialProcessor (
4055
    char *Fullpath, char **argv, char **envp, int ForkAction, Word_B *wb )
4056
{
4057
    char        **nargv;
4058
    char        *p_name;                /* Program name         */
4059
    char        *cp;
4060
    int         j;
4061
 
4062
/* Add the rest of the parameters */
4063
 
4064
    wb = AddWordToBlock (Fullpath, wb);
4065
 
4066
    j = 1;
4067
    while (argv[j] != NOWORD)
4068
        wb = AddWordToBlock (argv[j++], wb);
4069
 
4070
/* Execute the program */
4071
 
4072
    nargv = GetWordList (AddWordToBlock (NOWORD, wb));
4073
 
4074
/* Special for UNIX compatability, use ourselves */
4075
 
4076
    if ((strcmp (nargv[0], "/bin/sh") == 0) ||
4077
        (strcmp (nargv[0], "/bin/ksh") == 0))
4078
        nargv[0] = GetVariableAsString (ShellVariableName, FALSE);
4079
 
4080
/* See if the program exists.  If it doesn't, strip the path */
4081
 
4082
    else if (((cp = strrchr (nargv[0], CHAR_UNIX_DIRECTORY)) != (char *)NULL) &&
4083
             ((p_name = AllocateMemoryCell (FFNAME_MAX)) != (char *)NULL))
4084
    {
4085
        if (FindLocationOfExecutable (p_name, nargv[0]) == EXTENSION_NOT_FOUND)
4086
            strcpy (nargv[0], cp + 1);
4087
 
4088
        ReleaseMemoryCell (p_name);
4089
    }
4090
 
4091
/* Get the new program mode */
4092
 
4093
    CheckProgramMode (*nargv, &ExecProcessingMode);
4094
 
4095
    j = EnvironExecute (nargv, ForkAction);
4096
 
4097
    if (ExecProcessingMode.Flags != EP_ENVIRON)
4098
        j = LocalExecve (nargv, envp, ForkAction);
4099
 
4100
/* 0 is a special case - see ConvertErrorNumber */
4101
 
4102
    if (j == -1)
4103
        errno = 0;
4104
 
4105
    return j;
4106
}
4107
 
4108
 
4109
/*
4110
 * Parse a new CLI string
4111
 */
4112
static bool F_LOCAL
4113
SetUpCLI (char *string, Word_B **wb)
4114
{
4115
    char        *sp = StringCopy (string);
4116
 
4117
    if (sp == null)
4118
        return FALSE;
4119
 
4120
    *wb = SplitString (sp, *wb);
4121
    return TRUE;
4122
}
4123
 
4124
 
4125
static void
4126
PrintProgramMode ( ExeMode *PMode )
4127
{
4128
    if (PMode->Flags & EP_DOSMODE)   fprintf (stderr, "DOSMODE ");
4129
    if (PMode->Flags & EP_UNIXMODE)  fprintf (stderr, "UNIXMODE ");
4130
    if (PMode->Flags & EP_NOEXPAND)  fprintf (stderr, "NOEXPAND ");
4131
    if (PMode->Flags & EP_ENVIRON)   fprintf (stderr, "ENVIRON ");
4132
    if (PMode->Flags & EP_NOSWAP)    fprintf (stderr, "NOSWAP ");
4133
    if (PMode->Flags & EP_QUOTEWILD) fprintf (stderr, "QUOTEWILD ");
4134
    if (PMode->Flags & EP_EXPORT)    fprintf (stderr, "EXPORT ");
4135
    if (PMode->Flags & EP_CONVERT)   fprintf (stderr, "CONVERT ");
4136
    if (PMode->Flags & EP_NOWORDS)   fprintf (stderr, "NOWORDS ");
4137
    if (PMode->Flags & EP_NOQUOTE)   fprintf (stderr, "NOQUOTE ");
4138
    if (PMode->Flags & EP_IGNTYPE)   fprintf (stderr, "IGNTYPE ");
4139
    if (PMode->Flags & EP_PSEUDOTTY) fprintf (stderr, "PSEUDOTTY ");
4140
    if (PMode->Flags & EP_DOSEXT)    fprintf (stderr, "DOSEXT ");
4141
    fprintf (stderr, "\n");
4142
}
4143
 
4144
 
4145
#if (OS_TYPE == OS_DOS)
4146
/*
4147
 * Check the program type
4148
 */
4149
void
4150
CheckProgramMode (
4151
    char *Pname, ExeMode *PMode )
4152
{
4153
    char                *pEnv;
4154
    int                 builtin;                /* Builtin function     */
4155
    ExecMode_t          mode;
4156
 
4157
/* Check for internal no-globbed commands */
4158
 
4159
    if ((IsCommandBuiltIn (Pname, &builtin) != (int (*)(int, char **))NULL) &&
4160
        ((builtin & BLT_SKIPGLOB) == BLT_SKIPGLOB))
4161
    {
4162
        PMode->Flags = EP_NOEXPAND | ((builtin & BLT_NOWORDS) ? EP_NOWORDS : 0);
4163
        return;
4164
    }
4165
 
4166
/* Set not found */
4167
 
4168
    mode.Flags = EM_NONE;
4169
    mode.Name = (char *)NULL;
4170
 
4171
/* Check not a function */
4172
 
4173
    if ((Pname != (char *)NULL) &&
4174
           ((pEnv = GetVariableAsString("EXTENDED_LINE", FALSE)) != null))
4175
    {
4176
        if (FL_TEST(FLAG_DEBUG_EXECUTE))
4177
            __systeml_debug = 1;
4178
        __systeml_mode(pEnv, Pname, &mode);     /* Read extend config */
4179
    }
4180
 
4181
/* Copy results */
4182
 
4183
    PMode->Name = mode.Name;
4184
    PMode->Flags = EP_NONE;
4185
    if (mode.Flags & EM_DOSMODE)   PMode->Flags |= EP_DOSMODE;
4186
    if (mode.Flags & EM_UNIXMODE)  PMode->Flags |= EP_UNIXMODE;
4187
    if (mode.Flags & EM_NOEXPAND)  PMode->Flags |= EP_NOEXPAND;
4188
    if (mode.Flags & EM_ENVIRON)   PMode->Flags |= EP_ENVIRON;
4189
    if (mode.Flags & EM_NOSWAP)    PMode->Flags |= EP_NOSWAP;
4190
    if (mode.Flags & EM_QUOTEWILD) PMode->Flags |= EP_QUOTEWILD;
4191
    if (mode.Flags & EM_EXPORT)    PMode->Flags |= EP_EXPORT;
4192
    if (mode.Flags & EM_CONVERT)   PMode->Flags |= EP_CONVERT;
4193
    if (mode.Flags & EM_NOWORDS)   PMode->Flags |= EP_NOWORDS;
4194
    if (mode.Flags & EM_NOQUOTE)   PMode->Flags |= EP_NOQUOTE;
4195
    if (mode.Flags & EM_IGNTYPE)   PMode->Flags |= EP_IGNTYPE;
4196
    if (mode.Flags & EM_PSEUDOTTY) PMode->Flags |= EP_PSEUDOTTY;
4197
    if (mode.Flags & EM_DOSEXT)    PMode->Flags |= EP_DOSEXT;
4198
    PMode->FieldSep = mode.FieldSep;
4199
}
4200
 
4201
 
4202
#else   /*!OS_DOS*/
4203
 
4204
/*
4205
 * Common fields in EXTENDED_LINE file
4206
 */
4207
#define COMMON_FIELD_COUNT      ARRAY_SIZE (CommonFields)
4208
 
4209
static struct CommonFields {
4210
    char                *Name;
4211
    unsigned int        Flag;
4212
} CommonFields [] = {
4213
    { "switch",         EP_CONVERT },
4214
    { LIT_export,       EP_EXPORT },
4215
    { "noswap",         EP_NOSWAP },
4216
    { "noexpand",       EP_NOEXPAND },
4217
    { "noquote",        EP_NOQUOTE },
4218
    { "ignoretype",     EP_IGNTYPE },
4219
    { "pipetty",        EP_PSEUDOTTY },
4220
    { "quotewild",      EP_QUOTEWILD },
4221
    { "dosext",         EP_DOSEXT },
4222
};
4223
 
4224
 
4225
/*
4226
 * Check the program type
4227
 */
4228
void CheckProgramMode (char *Pname, ExeMode *PMode)
4229
{
4230
#if defined(USE_STUPID_BROKEN_CACHE)
4231
    static struct {                     /* Single level Cache */
4232
        char            *name;
4233
        ExeMode         mode;
4234
    } cache = { NULL, 0 };
4235
#endif
4236
    char                *sp, *sp1;      /* Line pointers        */
4237
    int                 nFields;
4238
    char                *SPname;
4239
    int                 builtin;        /* Builtin function     */
4240
    LineFields          LF;
4241
    long                value;
4242
 
4243
/* Check for internal no-globbed commands */
4244
 
4245
    if ((IsCommandBuiltIn (Pname, &builtin) != (int (*)(int, char **))NULL) &&
4246
        ((builtin & BLT_SKIPGLOB) == BLT_SKIPGLOB))
4247
    {
4248
        PMode->Flags = EP_NOEXPAND | ((builtin & BLT_NOWORDS) ? EP_NOWORDS : 0);
4249
        return;
4250
    }
4251
 
4252
/* Set not found */
4253
 
4254
    PMode->Flags = EP_NONE;
4255
    PMode->Name = NULL;
4256
 
4257
/* Check not a function */
4258
 
4259
    if ((Pname == (char *)NULL) ||
4260
                ((sp = GetVariableAsString ("EXTENDED_LINE", FALSE)) == null))
4261
        return;
4262
 
4263
/* Remove terminating .exe etc */
4264
 
4265
    sp1 = ((sp1 = FindLastPathCharacter (Pname)) == (char *)NULL)
4266
                 ? Pname : sp1 + 1;
4267
 
4268
    if (IsDriveCharacter (*(sp1 + 1)))
4269
        sp1 += 2;
4270
 
4271
    if ((SPname = StringCopy (sp1)) == null)
4272
        return;
4273
 
4274
    if ((sp1 = strrchr (SPname, CHAR_PERIOD)) != (char *)NULL)
4275
        *sp1 = 0;
4276
 
4277
#if defined(USE_STUPID_BROKEN_CACHE)
4278
/* Check cache (26/09/00) */
4279
    if (cache.name)
4280
    {
4281
       if (! NOCASE_COMPARE (cache.name, SPname))
4282
       {
4283
            PMode->Flags |= (cache.mode.Flags & EP_CACHED);
4284
            if (cache.mode.Name)
4285
               PMode->Name = StringCopy(cache.mode.Name);
4286
            else PMode->Name = NULL;
4287
            PMode->FieldSep = cache.mode.FieldSep;
4288
            ReleaseMemoryCell ((void *)SPname);
4289
            return;
4290
       }
4291
    }
4292
#endif
4293
 
4294
/* Open the file */
4295
 
4296
    if ((LF.Line = AllocateMemoryCell (LF.LineLength = 200)) == (char *)NULL)
4297
    {
4298
        ReleaseMemoryCell ((void *)SPname);
4299
        return;
4300
    }
4301
 
4302
    if ((LF.FP = FOpenFile (sp, sOpenReadMode)) == (FILE *)NULL)
4303
    {
4304
        ReleaseMemoryCell ((void *)LF.Line);
4305
        ReleaseMemoryCell ((void *)SPname);
4306
        return;
4307
    }
4308
 
4309
/* Initialise the internal buffer */
4310
 
4311
    LF.Fields = (Word_B *)NULL;
4312
 
4313
/* Scan for the file name */
4314
 
4315
    while ((nFields = ExtractFieldsFromLine (&LF)) != -1)
4316
    {
4317
        if (nFields < 2)
4318
            continue;
4319
 
4320
/* Remove terminating .exe etc */
4321
 
4322
#if (OS_TYPE != OS_UNIX)
4323
        if ((sp = strrchr (LF.Fields->w_words[0], CHAR_PERIOD)) != (char *)NULL)
4324
            *sp = 0;
4325
#endif
4326
 
4327
        if (NOCASE_COMPARE (LF.Fields->w_words[0], SPname))
4328
            continue;
4329
 
4330
/* What type? */
4331
 
4332
        if (NOCASE_COMPARE (LF.Fields->w_words[1], "unix") == 0)
4333
            PMode->Flags = (unsigned int )(EP_UNIXMODE |
4334
                                CheckForCommonOptions (&LF, 2));
4335
 
4336
        else if (NOCASE_COMPARE (LF.Fields->w_words[1], LIT_dos) == 0)
4337
            PMode->Flags = (unsigned int )(EP_DOSMODE |
4338
                                CheckForCommonOptions (&LF, 2));
4339
 
4340
/* Must have a valid name and we can get memory for it */
4341
 
4342
        else if ((NOCASE_COMPARE (LF.Fields->w_words[1], "environ") == 0) &&
4343
                 (nFields >= 3) &&
4344
                 (!IsValidVariableName (LF.Fields->w_words[2])) &&
4345
                 ((PMode->Name =
4346
                     StringCopy (LF.Fields->w_words[2])) != null))
4347
        {
4348
            PMode->Flags = EP_ENVIRON;
4349
            PMode->FieldSep = 0;
4350
 
4351
            if ((nFields >= 4) &&
4352
                ConvertNumericValue (LF.Fields->w_words[3], &value, 0))
4353
                PMode->FieldSep = (unsigned char)value;
4354
 
4355
            if (!PMode->FieldSep)
4356
                PMode->FieldSep = CHAR_SPACE;
4357
        }
4358
 
4359
        else
4360
            PMode->Flags = CheckForCommonOptions (&LF, 1);
4361
 
4362
        break;
4363
    }
4364
 
4365
    CloseFile (LF.FP);
4366
    ReleaseMemoryCell ((void *)LF.Line);
4367
 
4368
#if defined(USE_STUPID_BROKEN_CACHE)
4369
/* Update cache (26/09/00) */
4370
    if (cache.name)                     /* program name */
4371
        ReleaseMemoryCell (cache.name);
4372
    cache.name = SPname;
4373
 
4374
    cache.mode.Flags = PMode->Flags;    /* exec mode */
4375
    if (cache.mode.Name)
4376
        ReleaseMemoryCell ((void *)cache.mode.Name);
4377
 
4378
    if ((PMode->Flags & EP_ENVIRON) && PMode->Name)
4379
        cache.mode.Name = StringCopy(PMode->Name);
4380
    else
4381
        cache.mode.Name = NULL;
4382
    cache.mode.FieldSep = PMode->FieldSep;
4383
#endif
4384
}
4385
 
4386
 
4387
/*
4388
 * Check for common fields
4389
 */
4390
static unsigned int F_LOCAL
4391
CheckForCommonOptions(
4392
    LineFields *LF, int Start )
4393
{
4394
    unsigned int        Flags = 0;
4395
    int                 i, j;
4396
 
4397
    if (LF->Fields == (Word_B *)NULL)
4398
        return 0;
4399
 
4400
    for (i = Start; i < LF->Fields->w_nword; i++)
4401
    {
4402
        for (j = 0; j < COMMON_FIELD_COUNT; ++j)
4403
        {
4404
            if (!NOCASE_COMPARE (LF->Fields->w_words[i], CommonFields[j].Name))
4405
            {
4406
                Flags |= CommonFields[j].Flag;
4407
                break;
4408
            }
4409
        }
4410
    }
4411
 
4412
    return Flags;
4413
}
4414
#endif  /*OS_DOS*/