Merge remote-tracking branch 'origin/vendor/XZ'
[dragonfly.git] / contrib / tcsh-6 / sh.c
1 /* $Header: /p/tcsh/cvsroot/tcsh/sh.c,v 3.185 2015/05/10 13:28:54 christos Exp $ */
2 /*
3  * sh.c: Main shell routines
4  */
5 /*-
6  * Copyright (c) 1980, 1991 The Regents of the University of California.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 #define EXTERN  /* Intern */
34 #include "sh.h"
35
36 #ifndef lint
37 char    copyright[] =
38 "@(#) Copyright (c) 1991 The Regents of the University of California.\n\
39  All rights reserved.\n";
40 #endif /* not lint */
41
42 RCSID("$tcsh: sh.c,v 3.185 2015/05/10 13:28:54 christos Exp $")
43
44 #include "tc.h"
45 #include "ed.h"
46 #include "tw.h"
47
48 extern int MapsAreInited;
49 extern int NLSMapsAreInited;
50
51 /*
52  * C Shell
53  *
54  * Bill Joy, UC Berkeley, California, USA
55  * October 1978, May 1980
56  *
57  * Jim Kulp, IIASA, Laxenburg, Austria
58  * April 1980
59  *
60  * Filename recognition added:
61  * Ken Greer, Ind. Consultant, Palo Alto CA
62  * October 1983.
63  *
64  * Karl Kleinpaste, Computer Consoles, Inc.
65  * Added precmd, periodic/tperiod, prompt changes,
66  * directory stack hack, and login watch.
67  * Sometime March 1983 - Feb 1984.
68  *
69  * Added scheduled commands, including the "sched" command,
70  * plus the call to sched_run near the precmd et al
71  * routines.
72  * Upgraded scheduled events for running events while
73  * sitting idle at command input.
74  *
75  * Paul Placeway, Ohio State
76  * added stuff for running with twenex/inputl  9 Oct 1984.
77  *
78  * ported to Apple Unix (TM) (OREO)  26 -- 29 Jun 1987
79  */
80
81 jmp_buf_t reslab IZERO_STRUCT;
82 struct wordent paraml IZERO_STRUCT;
83
84 static const char tcshstr[] = "tcsh";
85
86 struct sigaction parintr;       /* Parents interrupt catch */
87 struct sigaction parterm;       /* Parents terminate catch */
88
89 #ifdef TESLA
90 int do_logout = 0;
91 #endif /* TESLA */
92
93
94 int    use_fork = 0;            /* use fork() instead of vfork()? */
95
96 /*
97  * Magic pointer values. Used to specify other invalid conditions aside
98  * from null.
99  */
100 static Char     INVCHAR;
101 Char    *INVPTR = &INVCHAR;
102 Char    **INVPPTR = &INVPTR;
103
104 static int    fast = 0;
105 static int    mflag = 0;
106 static int    prompt = 1;
107 int     enterhist = 0;
108 int    tellwhat = 0;
109 time_t  t_period;
110 Char  *ffile = NULL;
111 int     dolzero = 0;
112 int     insource = 0;
113 int     exitset = 0;
114 static time_t  chktim;          /* Time mail last checked */
115 char *progname;
116 int tcsh;
117
118 /*
119  * This preserves the input state of the shell. It is used by
120  * st_save and st_restore to manupulate shell state.
121  */
122 struct saved_state {
123     int           insource;
124     int           OLDSTD;
125     int           SHIN;
126     int           SHOUT;
127     int           SHDIAG;
128     int           intty;
129     struct whyle *whyles;
130     Char         *gointr;
131     Char         *arginp;
132     Char         *evalp;
133     Char        **evalvec;
134     Char         *alvecp;
135     Char        **alvec;
136     int           onelflg;
137     int   enterhist;
138     Char        **argv;
139     Char        **av;
140     Char          HIST;
141     int   cantell;
142     struct Bin    B;
143     int           justpr;
144 };
145
146 static  int               srccat        (Char *, Char *);
147 #ifndef WINNT_NATIVE
148 static  int               srcfile       (const char *, int, int, Char **);
149 #else
150 int               srcfile       (const char *, int, int, Char **);
151 #endif /*WINNT_NATIVE*/
152 static  void              srcunit       (int, int, int, Char **);
153 static  void              mailchk       (void);
154 #ifndef _PATH_DEFPATH
155 static  Char            **defaultpath   (void);
156 #endif
157 static  void              record        (void);
158 static  void              st_save       (struct saved_state *, int, int,
159                                          Char **, Char **);
160 static  void              st_restore    (void *);
161
162         int               main          (int, char **);
163
164 #ifndef LOCALEDIR
165 #define LOCALEDIR "/usr/share/locale"
166 #endif
167
168 #ifdef NLS_CATALOGS
169 static void
170 add_localedir_to_nlspath(const char *path)
171 {
172     static const char msgs_LOC[] = "/%L/LC_MESSAGES/%N.cat";
173     static const char msgs_lang[] = "/%l/LC_MESSAGES/%N.cat";
174     char *old;
175     char *new, *new_p;
176     size_t len;
177     int add_LOC = 1;
178     int add_lang = 1;
179     char trypath[MAXPATHLEN];
180     struct stat st;
181
182     if (path == NULL)
183         return;
184
185     (void) xsnprintf(trypath, sizeof(trypath), "%s/en/LC_MESSAGES/tcsh.cat",
186         path);
187     if (stat(trypath, &st) == -1)
188         return;
189
190     if ((old = getenv("NLSPATH")) != NULL)
191         len = strlen(old) + 1;  /* don't forget the colon. */
192     else
193         len = 0;
194
195     len += 2 * strlen(path) +
196            sizeof(msgs_LOC) + sizeof(msgs_lang); /* includes the extra colon */
197
198     new = new_p = xcalloc(len, 1);
199
200     if (old != NULL) {
201         size_t pathlen = strlen(path);
202         char *old_p;
203
204         (void) xsnprintf(new_p, len, "%s", old);
205         new_p += strlen(new_p);
206         len -= new_p - new;
207
208         /* Check if the paths we try to add are already present in NLSPATH.
209            If so, note it by setting the appropriate flag to 0. */
210         for (old_p = old; old_p; old_p = strchr(old_p, ':'),
211                                  old_p = old_p ? old_p + 1 : NULL) {
212             if (strncmp(old_p, path, pathlen) != 0)
213                 continue;
214             if (strncmp(old_p + pathlen, msgs_LOC, sizeof(msgs_LOC) - 1) == 0)
215                 add_LOC = 0;
216             else if (strncmp(old_p + pathlen, msgs_lang,
217                               sizeof(msgs_lang) - 1) == 0)
218                 add_lang = 0;
219         }
220     }
221
222     /* Add the message catalog paths not already present to NLSPATH. */
223     if (add_LOC || add_lang)
224         (void) xsnprintf(new_p, len, "%s%s%s%s%s%s",
225                          old ? ":" : "",
226                          add_LOC ? path : "", add_LOC ? msgs_LOC : "",
227                          add_LOC && add_lang ? ":" : "",
228                          add_lang ? path : "", add_lang ? msgs_lang : "");
229
230     tsetenv(STRNLSPATH, str2short(new));
231     free(new);
232 }
233 #endif
234
235 int
236 main(int argc, char **argv)
237 {
238     int batch = 0;
239     volatile int nexececho = 0;
240     int nofile = 0;
241     volatile int nverbose = 0;
242     volatile int rdirs = 0;
243     int quitit = 0;
244     Char *cp;
245 #ifdef AUTOLOGOUT
246     Char *cp2;
247 #endif
248     char *tcp, *ttyn;
249     int f, reenter;
250     char **tempv;
251     int osetintr;
252     struct sigaction oparintr;
253
254 #ifdef WINNT_NATIVE
255     nt_init();
256 #endif /* WINNT_NATIVE */
257
258     (void)memset(&reslab, 0, sizeof(reslab));
259 #if defined(NLS_CATALOGS) && defined(LC_MESSAGES)
260     (void) setlocale(LC_MESSAGES, "");
261 #endif /* NLS_CATALOGS && LC_MESSAGES */
262
263 #ifdef NLS
264 # ifdef LC_CTYPE
265     (void) setlocale(LC_CTYPE, ""); /* for iscntrl */
266 # endif /* LC_CTYPE */
267 #endif /* NLS */
268
269     STR_environ = blk2short(environ);
270     environ = short2blk(STR_environ);   /* So that we can free it */
271
272 #ifdef NLS_CATALOGS
273     add_localedir_to_nlspath(LOCALEDIR);
274 #endif
275
276     nlsinit();
277
278 #ifdef MALLOC_TRACE
279     mal_setstatsfile(fdopen(dmove(xopen("/tmp/tcsh.trace", 
280         O_WRONLY|O_CREAT|O_LARGEFILE, 0666), 25), "w"));
281     mal_trace(1);
282 #endif /* MALLOC_TRACE */
283
284 #if !(defined(BSDTIMES) || defined(_SEQUENT_)) && defined(POSIX)
285 # ifdef _SC_CLK_TCK
286     clk_tck = (clock_t) sysconf(_SC_CLK_TCK);
287 # else /* ! _SC_CLK_TCK */
288 #  ifdef CLK_TCK
289     clk_tck = CLK_TCK;
290 #  else /* !CLK_TCK */
291     clk_tck = HZ;
292 #  endif /* CLK_TCK */
293 # endif /* _SC_CLK_TCK */
294 #endif /* !BSDTIMES && POSIX */
295
296     settimes();                 /* Immed. estab. timing base */
297 #ifdef TESLA
298     do_logout = 0;
299 #endif /* TESLA */
300
301     /*
302      * Make sure we have 0, 1, 2 open
303      * Otherwise `` jobs will not work... (From knaff@poly.polytechnique.fr)
304      */
305     {
306         do 
307             if ((f = xopen(_PATH_DEVNULL, O_RDONLY|O_LARGEFILE)) == -1 &&
308                 (f = xopen("/", O_RDONLY|O_LARGEFILE)) == -1) 
309                 exit(1);
310         while (f < 3);
311         xclose(f);
312     }
313
314     osinit();                   /* Os dependent initialization */
315
316     
317     {
318         char *t;
319
320         t = strrchr(argv[0], '/');
321 #ifdef WINNT_NATIVE
322         {
323             char *s = strrchr(argv[0], '\\');
324             if (s)
325                 t = s;
326         }
327 #endif /* WINNT_NATIVE */
328         t = t ? t + 1 : argv[0];
329         if (*t == '-') t++;
330         progname = strsave((t && *t) ? t : tcshstr);    /* never want a null */
331         tcsh = strncmp(progname, tcshstr, sizeof(tcshstr) - 1) == 0;
332     }
333
334     /*
335      * Initialize non constant strings
336      */
337 #ifdef _PATH_BSHELL
338     STR_BSHELL = SAVE(_PATH_BSHELL);
339 #endif
340 #ifdef _PATH_TCSHELL
341     STR_SHELLPATH = SAVE(_PATH_TCSHELL);
342 #else
343 # ifdef _PATH_CSHELL
344     STR_SHELLPATH = SAVE(_PATH_CSHELL);
345 # endif
346 #endif
347     STR_WORD_CHARS = SAVE(WORD_CHARS);
348
349     HIST = '!';
350     HISTSUB = '^';
351     PRCH = tcsh ? '>' : '%';    /* to replace %# in $prompt for normal users */
352     PRCHROOT = '#';             /* likewise for root */
353     word_chars = STR_WORD_CHARS;
354     bslash_quote = 0;           /* PWP: do tcsh-style backslash quoting? */
355     anyerror = 1;               /* for compatibility */
356     setcopy(STRanyerror, STRNULL, VAR_READWRITE);
357
358     /* Default history size to 100 */
359     setcopy(STRhistory, str2short("100"), VAR_READWRITE);
360     sethistory(100);
361
362     tempv = argv;
363     ffile = SAVE(tempv[0]);
364     dolzero = 0;
365     if (eq(ffile, STRaout))     /* A.out's are quittable */
366         quitit = 1;
367     uid = getuid();
368     gid = getgid();
369     euid = geteuid();
370     egid = getegid();
371     /*
372      * We are a login shell if: 1. we were invoked as -<something> with
373      * optional arguments 2. or we were invoked only with the -l flag
374      */
375     loginsh = (**tempv == '-') || (argc == 2 &&
376                                    tempv[1][0] == '-' && tempv[1][1] == 'l' &&
377                                                 tempv[1][2] == '\0');
378 #ifdef _VMS_POSIX
379     /* No better way to find if we are a login shell */
380     if (!loginsh) {
381         loginsh = (argc == 1 && getppid() == 1);
382         **tempv = '-';  /* Avoid giving VMS an acidic stomach */
383     }
384 #endif /* _VMS_POSIX */
385
386     if (loginsh && **tempv != '-') {
387         char *argv0;
388
389         /*
390          * Mangle the argv space
391          */
392         tempv[1][0] = '\0';
393         tempv[1][1] = '\0';
394         tempv[1] = NULL;
395         argv0 = strspl("-", *tempv);
396         *tempv = argv0;
397         argc--;
398     }
399     if (loginsh) {
400         (void) time(&chktim);
401         setNS(STRloginsh);
402     }
403
404     NoNLSRebind = getenv("NOREBIND") != NULL;
405 #ifdef NLS
406 # ifdef SETLOCALEBUG
407     dont_free = 1;
408 # endif /* SETLOCALEBUG */
409     (void) setlocale(LC_ALL, "");
410 # ifdef LC_COLLATE
411     (void) setlocale(LC_COLLATE, "");
412 # endif
413 # ifdef SETLOCALEBUG
414     dont_free = 0;
415 # endif /* SETLOCALEBUG */
416 # ifdef STRCOLLBUG
417     fix_strcoll_bug();
418 # endif /* STRCOLLBUG */
419
420     /*
421      * On solaris ISO8859-1 contains no printable characters in the upper half
422      * so we need to test only for MB_CUR_MAX == 1, otherwise for multi-byte
423      * locales we are always AsciiOnly == 0.
424      */
425     if (MB_CUR_MAX == 1) {
426         int     k;
427
428         for (k = 0200; k <= 0377 && !isprint(CTL_ESC(k)); k++)
429             continue;
430         AsciiOnly = k > 0377;
431     } else
432         AsciiOnly = 0;
433 #else
434     AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL;
435 #endif                          /* NLS */
436     if (MapsAreInited && !NLSMapsAreInited)
437         ed_InitNLSMaps();
438     ResetArrowKeys();
439
440     /*
441      * Initialize for periodic command intervals. Also, initialize the dummy
442      * tty list for login-watch.
443      */
444     (void) time(&t_period);
445 #ifndef HAVENOUTMP
446     initwatch();
447 #endif /* !HAVENOUTMP */
448
449 #if defined(alliant)
450     /*
451      * From:  Jim Pace <jdp@research.att.com>
452      * tcsh does not work properly on the alliants through an rlogin session.
453      * The shell generally hangs.  Also, reference to the controlling terminal
454      * does not work ( ie: echo foo > /dev/tty ).
455      *
456      * A security feature was added to rlogind affecting FX/80's Concentrix
457      * from revision 5.5.xx upwards (through 5.7 where this fix was implemented)
458      * This security change also affects the FX/2800 series.
459      * The security change to rlogind requires the process group of an rlogin
460      * session become disassociated with the tty in rlogind.
461      *
462      * The changes needed are:
463      * 1. set the process group
464      * 2. reenable the control terminal
465      */
466      if (loginsh && isatty(SHIN)) {
467          ttyn = ttyname(SHIN);
468          xclose(SHIN);
469          SHIN = xopen(ttyn, O_RDWR|O_LARGEFILE);
470          shpgrp = getpid();
471          (void) ioctl (SHIN, TIOCSPGRP, (ioctl_t) &shpgrp);
472          (void) setpgid(0, shpgrp);
473      }
474 #endif /* alliant */
475
476     /*
477      * Move the descriptors to safe places. The variable didfds is 0 while we
478      * have only FSH* to work with. When didfds is true, we have 0,1,2 and
479      * prefer to use these.
480      */
481     initdesc();
482
483     cdtohome = 1;
484     setv(STRcdtohome, SAVE(""), VAR_READWRITE);
485
486     /*
487      * Get and set the tty now
488      */
489     if ((ttyn = ttyname(SHIN)) != NULL) {
490         /*
491          * Could use rindex to get rid of other possible path components, but
492          * hpux preserves the subdirectory /pty/ when storing the tty name in
493          * utmp, so we keep it too.
494          */
495         if (strncmp(ttyn, "/dev/", 5) == 0)
496             setv(STRtty, cp = SAVE(ttyn + 5), VAR_READWRITE);
497         else
498             setv(STRtty, cp = SAVE(ttyn), VAR_READWRITE);
499     }
500     else
501         setv(STRtty, cp = SAVE(""), VAR_READWRITE);
502
503     /*
504      * Initialize the shell variables. ARGV and PROMPT are initialized later.
505      * STATUS is also munged in several places. CHILD is munged when
506      * forking/waiting
507      */
508
509     /*
510      * 7-10-87 Paul Placeway autologout should be set ONLY on login shells and
511      * on shells running as root.  Out of these, autologout should NOT be set
512      * for any psudo-terminals (this catches most window systems) and not for
513      * any terminal running X windows.
514      * 
515      * At Ohio State, we have had problems with a user having his X session 
516      * drop out from under him (on a Sun) because the shell in his master 
517      * xterm timed out and exited.
518      * 
519      * Really, this should be done with a program external to the shell, that
520      * watches for no activity (and NO running programs, such as dump) on a
521      * terminal for a long peroid of time, and then SIGHUPS the shell on that
522      * terminal.
523      * 
524      * bugfix by Rich Salz <rsalz@PINEAPPLE.BBN.COM>: For root rsh things 
525      * allways first check to see if loginsh or really root, then do things 
526      * with ttyname()
527      * 
528      * Also by Jean-Francois Lamy <lamy%ai.toronto.edu@RELAY.CS.NET>: check the
529      * value of cp before using it! ("root can rsh too")
530      * 
531      * PWP: keep the nested ifs; the order of the tests matters and a good 
532      * (smart) C compiler might re-arange things wrong.
533      */
534 #ifdef AUTOLOGOUT
535 # ifdef convex
536     if (uid == 0)
537         /*  root always has a 15 minute autologout  */
538         setcopy(STRautologout, STRrootdefautologout, VAR_READWRITE);
539     else
540         if (loginsh)
541             /*  users get autologout set to 0  */
542             setcopy(STRautologout, STR0, VAR_READWRITE);
543 # else /* convex */
544     if (loginsh || (uid == 0)) {
545         if (*cp) {
546             /* only for login shells or root and we must have a tty */
547             if (((cp2 = Strrchr(cp, (Char) '/')) != NULL) &&
548                 (Strncmp(cp, STRptssl, 3) != 0)) {
549                 cp2 = cp2 + 1;
550             }
551             else
552                 cp2 = cp;
553             if (!(((Strncmp(cp2, STRtty, 3) == 0) && Isalpha(cp2[3])) ||
554                   Strstr(cp, STRptssl) != NULL)) {
555                 if (getenv("DISPLAY") == NULL) {
556                     /* NOT on X window shells */
557                     setcopy(STRautologout, STRdefautologout, VAR_READWRITE);
558                 }
559             }
560         }
561     }
562 # endif /* convex */
563 #endif /* AUTOLOGOUT */
564
565     sigset_interrupting(SIGALRM, queue_alrmcatch);
566
567     setcopy(STRstatus, STR0, VAR_READWRITE);
568
569     /*
570      * get and set machine specific environment variables
571      */
572     getmachine();
573
574
575     /*
576      * Publish the selected echo style
577      */
578 #if ECHO_STYLE != BSD_ECHO
579     if (tcsh) {
580 # if ECHO_STYLE == NONE_ECHO
581         setcopy(STRecho_style, STRnone, VAR_READWRITE);
582 # endif /* ECHO_STYLE == NONE_ECHO */
583 # if ECHO_STYLE == SYSV_ECHO
584         setcopy(STRecho_style, STRsysv, VAR_READWRITE);
585 # endif /* ECHO_STYLE == SYSV_ECHO */
586 # if ECHO_STYLE == BOTH_ECHO
587         setcopy(STRecho_style, STRboth, VAR_READWRITE);
588 # endif /* ECHO_STYLE == BOTH_ECHO */
589     } else
590 #endif /* ECHO_STYLE != BSD_ECHO */
591         setcopy(STRecho_style, STRbsd, VAR_READWRITE);
592
593     /*
594      * increment the shell level.
595      */
596     shlvl(1);
597
598     if ((tcp = getenv("HOME")) != NULL)
599         cp = quote(SAVE(tcp));
600     else
601 #ifdef __ANDROID__
602         /* On Android, $HOME usually isn't set, so we can't load user RC files.
603            Check for the environment variable EXTERNAL_STORAGE, which contains
604            the mount point of the external storage (SD card, mostly).  If
605            EXTERNAL_STORAGE isn't set fall back to "/sdcard". */
606     if ((tcp = getenv("EXTERNAL_STORAGE")) != NULL)
607         cp = quote(SAVE(tcp));
608     else
609         cp = quote(SAVE("/sdcard"));
610 #else
611         cp = NULL;
612 #endif
613
614     if (cp == NULL)
615         fast = 1;               /* No home -> can't read scripts */
616     else
617         setv(STRhome, cp, VAR_READWRITE);
618
619     dinit(cp);                  /* dinit thinks that HOME == cwd in a login
620                                  * shell */
621     /*
622      * Grab other useful things from the environment. Should we grab
623      * everything??
624      */
625     {
626         char *cln, *cus, *cgr;
627         struct passwd *pw;
628         struct group *gr;
629
630
631 #ifdef apollo
632         int     oid = getoid();
633
634         setv(STRoid, Itoa(oid, 0, 0), VAR_READWRITE);
635 #endif /* apollo */
636
637         setv(STReuid, Itoa(euid, 0, 0), VAR_READWRITE);
638         if ((pw = xgetpwuid(euid)) == NULL)
639             setcopy(STReuser, STRunknown, VAR_READWRITE);
640         else
641             setcopy(STReuser, str2short(pw->pw_name), VAR_READWRITE);
642
643         setv(STRuid, Itoa(uid, 0, 0), VAR_READWRITE);
644
645         setv(STRgid, Itoa(gid, 0, 0), VAR_READWRITE);
646
647         cln = getenv("LOGNAME");
648         cus = getenv("USER");
649         if (cus != NULL)
650             setv(STRuser, quote(SAVE(cus)), VAR_READWRITE);
651         else if (cln != NULL)
652             setv(STRuser, quote(SAVE(cln)), VAR_READWRITE);
653         else if ((pw = xgetpwuid(uid)) == NULL)
654             setcopy(STRuser, STRunknown, VAR_READWRITE);
655         else
656             setcopy(STRuser, str2short(pw->pw_name), VAR_READWRITE);
657         if (cln == NULL)
658             tsetenv(STRLOGNAME, varval(STRuser));
659         if (cus == NULL)
660             tsetenv(STRKUSER, varval(STRuser));
661         
662         cgr = getenv("GROUP");
663         if (cgr != NULL)
664             setv(STRgroup, quote(SAVE(cgr)), VAR_READWRITE);
665         else if ((gr = xgetgrgid(gid)) == NULL)
666             setcopy(STRgroup, STRunknown, VAR_READWRITE);
667         else
668             setcopy(STRgroup, str2short(gr->gr_name), VAR_READWRITE);
669         if (cgr == NULL)
670             tsetenv(STRKGROUP, varval(STRgroup));
671     }
672
673     /*
674      * HOST may be wrong, since rexd transports the entire environment on sun
675      * 3.x Just set it again
676      */
677     {
678         char    cbuff[MAXHOSTNAMELEN];
679
680         if (gethostname(cbuff, sizeof(cbuff)) >= 0) {
681             cbuff[sizeof(cbuff) - 1] = '\0';    /* just in case */
682             tsetenv(STRHOST, str2short(cbuff));
683         }
684         else
685             tsetenv(STRHOST, STRunknown);
686     }
687
688
689 #ifdef REMOTEHOST
690     /*
691      * Try to determine the remote host we were logged in from.
692      */
693     remotehost();
694 #endif /* REMOTEHOST */
695  
696 #ifdef apollo
697     if ((tcp = getenv("SYSTYPE")) == NULL)
698         tcp = "bsd4.3";
699     tsetenv(STRSYSTYPE, quote(str2short(tcp)));
700 #endif /* apollo */
701
702     /*
703      * set editing on by default, unless running under Emacs as an inferior
704      * shell.
705      * We try to do this intelligently. If $TERM is available, then it
706      * should determine if we should edit or not. $TERM is preserved
707      * across rlogin sessions, so we will not get confused if we rlogin
708      * under an emacs shell. Another advantage is that if we run an
709      * xterm under an emacs shell, then the $TERM will be set to 
710      * xterm, so we are going to want to edit. Unfortunately emacs
711      * does not restore all the tty modes, so xterm is not very well
712      * set up. But this is not the shell's fault.
713      * Also don't edit if $TERM == wm, for when we're running under an ATK app.
714      * Finally, emacs compiled under terminfo, sets the terminal to dumb,
715      * so disable editing for that too.
716      * 
717      * Unfortunately, in some cases the initial $TERM setting is "unknown",
718      * "dumb", or "network" which is then changed in the user's startup files.
719      * We fix this by setting noediting here if $TERM is unknown/dumb and
720      * if noediting is set, we switch on editing if $TERM is changed.
721      */
722     if ((tcp = getenv("TERM")) != NULL) {
723         setv(STRterm, quote(SAVE(tcp)), VAR_READWRITE);
724         noediting = strcmp(tcp, "unknown") == 0 || strcmp(tcp, "dumb") == 0 ||
725                     strcmp(tcp, "network") == 0;
726         editing = strcmp(tcp, "emacs") != 0 && strcmp(tcp, "wm") != 0 &&
727                   !noediting;
728     }
729     else {
730         noediting = 0;
731         editing = ((tcp = getenv("EMACS")) == NULL || strcmp(tcp, "t") != 0);
732     }
733
734     /* 
735      * The 'edit' variable is either set or unset.  It doesn't 
736      * need a value.  Making it 'emacs' might be confusing. 
737      */
738     if (editing)
739         setNS(STRedit);
740
741
742     /*
743      * still more mutability: make the complete routine automatically add the
744      * suffix of file names...
745      */
746     setNS(STRaddsuffix);
747
748     /*
749      * Compatibility with tcsh >= 6.12 by default
750      */
751     setNS(STRcsubstnonl);
752     
753     /*
754      * Random default kill ring size
755      */
756     setcopy(STRkillring, str2short("30"), VAR_READWRITE);
757
758     /*
759      * Re-initialize path if set in environment
760      */
761     if ((tcp = getenv("PATH")) == NULL) {
762 #ifdef _PATH_DEFPATH
763         importpath(str2short(_PATH_DEFPATH));
764 #else /* !_PATH_DEFPATH */
765         setq(STRpath, defaultpath(), &shvhed, VAR_READWRITE);
766 #endif /* _PATH_DEFPATH */
767         /*
768          * Export the path setting so that subsequent processes use the same path as we do.
769          */
770         exportpath(adrof(STRpath)->vec);
771     } else
772         /* Importpath() allocates memory for the path, and the
773          * returned pointer from SAVE() was discarded, so
774          * this was a memory leak.. (sg)
775          *
776          * importpath(SAVE(tcp));
777          */
778         importpath(str2short(tcp));
779
780
781     {
782         /* If the SHELL environment variable ends with "tcsh", set
783          * STRshell to the same path.  This is to facilitate using
784          * the executable in environments where the compiled-in
785          * default isn't appropriate (sg).
786          */
787
788         size_t sh_len = 0;
789
790         if ((tcp = getenv("SHELL")) != NULL) {
791             sh_len = strlen(tcp);
792             if ((sh_len >= 5 && strcmp(tcp + (sh_len - 5), "/tcsh") == 0) || 
793                 (!tcsh && sh_len >= 4 && strcmp(tcp + (sh_len - 4), "/csh") == 0))
794                 setv(STRshell, quote(SAVE(tcp)), VAR_READWRITE);
795             else
796                 sh_len = 0;
797         }
798         if (sh_len == 0)
799             setcopy(STRshell, STR_SHELLPATH, VAR_READWRITE);
800     }
801
802 #ifdef _OSD_POSIX  /* BS2000 needs this variable set to "SHELL" */
803     if ((tcp = getenv("PROGRAM_ENVIRONMENT")) == NULL)
804         tcp = "SHELL";
805     tsetenv(STRPROGRAM_ENVIRONMENT, quote(str2short(tcp)));
806 #endif /* _OSD_POSIX */
807
808 #ifdef COLOR_LS_F
809     if ((tcp = getenv("LS_COLORS")) != NULL)
810         parseLS_COLORS(str2short(tcp));
811     if ((tcp = getenv("LSCOLORS")) != NULL)
812         parseLSCOLORS(str2short(tcp));
813 #endif /* COLOR_LS_F */
814
815     doldol = putn((tcsh_number_t)getpid());     /* For $$ */
816 #ifdef WINNT_NATIVE
817     {
818         char *tmp;
819         Char *tmp2;
820         if ((tmp = getenv("TMP")) != NULL) {
821             tmp = xasprintf("%s/%s", tmp, "sh");
822             tmp2 = SAVE(tmp);
823             xfree(tmp);
824         }
825         else {
826             tmp2 = SAVE(""); 
827         }
828         shtemp = Strspl(tmp2, doldol);  /* For << */
829         xfree(tmp2);
830     }
831 #else /* !WINNT_NATIVE */
832 #ifdef HAVE_MKSTEMP
833     {
834         const char *tmpdir = getenv ("TMPDIR");
835         if (!tmpdir)
836             tmpdir = "/tmp";
837         shtemp = Strspl(SAVE(tmpdir), SAVE("/sh" TMP_TEMPLATE)); /* For << */
838     }
839 #else /* !HAVE_MKSTEMP */
840     shtemp = Strspl(STRtmpsh, doldol);  /* For << */
841 #endif /* HAVE_MKSTEMP */
842 #endif /* WINNT_NATIVE */
843
844     /*
845      * Record the interrupt states from the parent process. If the parent is
846      * non-interruptible our hand must be forced or we (and our children) won't
847      * be either. Our children inherit termination from our parent. We catch it
848      * only if we are the login shell.
849      */
850     sigaction(SIGINT, NULL, &parintr);
851     sigaction(SIGTERM, NULL, &parterm);
852
853
854 #ifdef TCF
855     /* Enable process migration on ourselves and our progeny */
856     (void) signal(SIGMIGRATE, SIG_DFL);
857 #endif /* TCF */
858
859     /*
860      * dspkanji/dspmbyte autosetting
861      */
862     /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */
863 #if defined(DSPMBYTE)
864 #if defined(NLS) && defined(LC_CTYPE)
865     if (((tcp = setlocale(LC_CTYPE, NULL)) != NULL || (tcp = getenv("LANG")) != NULL) && !adrof(CHECK_MBYTEVAR))
866 #else
867     if ((tcp = getenv("LANG")) != NULL && !adrof(CHECK_MBYTEVAR))
868 #endif
869     {
870         autoset_dspmbyte(str2short(tcp));
871     }
872 #if defined(WINNT_NATIVE)
873     else if (!adrof(CHECK_MBYTEVAR))
874       nt_autoset_dspmbyte();
875 #endif /* WINNT_NATIVE */
876 #endif
877 #if defined(AUTOSET_KANJI) 
878 # if defined(NLS) && defined(LC_CTYPE)
879     if (setlocale(LC_CTYPE, NULL) != NULL || getenv("LANG") != NULL)
880 # else
881     if (getenv("LANG") != NULL)
882 # endif
883         autoset_kanji();
884 #endif /* AUTOSET_KANJI */
885     fix_version();              /* publish the shell version */
886
887     if (argc > 1 && strcmp(argv[1], "--version") == 0) {
888         xprintf("%S\n", varval(STRversion));
889         xexit(0);
890     }
891     if (argc > 1 && strcmp(argv[1], "--help") == 0) {
892         xprintf("%S\n\n", varval(STRversion));
893         xprintf("%s", CGETS(11, 8, HELP_STRING));
894         xexit(0);
895     }
896     /*
897      * Process the arguments.
898      * 
899      * Note that processing of -v/-x is actually delayed till after script
900      * processing.
901      * 
902      * We set the first character of our name to be '-' if we are a shell 
903      * running interruptible commands.  Many programs which examine ps'es 
904      * use this to filter such shells out.
905      */
906     argc--, tempv++;
907     while (argc > 0 && (tcp = tempv[0])[0] == '-' &&
908            *++tcp != '\0' && !batch) {
909         do
910             switch (*tcp++) {
911
912             case 0:             /* -    Interruptible, no prompt */
913                 prompt = 0;
914                 setintr = 1;
915                 nofile = 1;
916                 break;
917
918             case 'b':           /* -b   Next arg is input file */
919                 batch = 1;
920                 break;
921
922             case 'c':           /* -c   Command input from arg */
923                 if (argc == 1)
924                     xexit(0);
925                 argc--, tempv++;
926 #ifdef M_XENIX
927                 /* Xenix Vi bug:
928                    it relies on a 7 bit environment (/bin/sh), so it
929                    pass ascii arguments with the 8th bit set */
930                 if (!strcmp(argv[0], "sh"))
931                   {
932                     char *p;
933
934                     for (p = tempv[0]; *p; ++p)
935                       *p &= ASCII;
936                   }
937 #endif
938                 arginp = SAVE(tempv[0]);
939
940                 /*
941                  * we put the command into a variable
942                  */
943                 if (arginp != NULL)
944                     setv(STRcommand, quote(Strsave(arginp)), VAR_READWRITE);
945
946                 /*
947                  * * Give an error on -c arguments that end in * backslash to
948                  * ensure that you don't make * nonportable csh scripts.
949                  */
950                 {
951                     int count;
952
953                     cp = Strend(arginp);
954                     count = 0;
955                     while (cp > arginp && *--cp == '\\')
956                         ++count;
957                     if ((count & 1) != 0) {
958                         exiterr = 1;
959                         stderror(ERR_ARGC);
960                     }
961                 }
962                 prompt = 0;
963                 nofile = 1;
964                 break;
965             case 'd':           /* -d   Load directory stack from file */
966                 rdirs = 1;
967                 break;
968
969 #ifdef apollo
970             case 'D':           /* -D   Define environment variable */
971                 {
972                     Char *dp;
973
974                     cp = str2short(tcp);
975                     if (dp = Strchr(cp, '=')) {
976                         *dp++ = '\0';
977                         tsetenv(cp, dp);
978                     }
979                     else
980                         tsetenv(cp, STRNULL);
981                 }
982                 *tcp = '\0';    /* done with this argument */
983                 break;
984 #endif /* apollo */
985
986             case 'e':           /* -e   Exit on any error */
987                 exiterr = 1;
988                 break;
989
990             case 'f':           /* -f   Fast start */
991                 fast = 1;
992                 break;
993
994             case 'i':           /* -i   Interactive, even if !intty */
995                 intact = 1;
996                 nofile = 1;
997                 break;
998
999             case 'm':           /* -m   read .cshrc (from su) */
1000                 mflag = 1;
1001                 break;
1002
1003             case 'n':           /* -n   Don't execute */
1004                 noexec = 1;
1005                 break;
1006
1007             case 'q':           /* -q   (Undoc'd) ... die on quit */
1008                 quitit = 1;
1009                 break;
1010
1011             case 's':           /* -s   Read from std input */
1012                 nofile = 1;
1013                 break;
1014
1015             case 't':           /* -t   Read one line from input */
1016                 onelflg = 2;
1017                 prompt = 0;
1018                 nofile = 1;
1019                 break;
1020
1021             case 'v':           /* -v   Echo hist expanded input */
1022                 nverbose = 1;   /* ... later */
1023                 break;
1024
1025             case 'x':           /* -x   Echo just before execution */
1026                 nexececho = 1;  /* ... later */
1027                 break;
1028
1029             case 'V':           /* -V   Echo hist expanded input */
1030                 setNS(STRverbose);      /* NOW! */
1031                 break;
1032
1033             case 'X':           /* -X   Echo just before execution */
1034                 setNS(STRecho); /* NOW! */
1035                 break;
1036
1037             case 'F':
1038                 /*
1039                  * This will cause children to be created using fork instead of
1040                  * vfork.
1041                  */
1042                 use_fork = 1;
1043                 break;
1044
1045             case ' ':
1046             case '\t':
1047             case '\r':
1048             case '\n':
1049                 /* 
1050                  * for O/S's that don't do the argument parsing right in 
1051                  * "#!/foo -f " scripts
1052                  */
1053                 break;
1054
1055             default:            /* Unknown command option */
1056                 exiterr = 1;
1057                 stderror(ERR_TCSHUSAGE, tcp-1, progname);
1058                 break;
1059
1060         } while (*tcp);
1061         tempv++, argc--;
1062     }
1063
1064     if (quitit)                 /* With all due haste, for debugging */
1065         (void) signal(SIGQUIT, SIG_DFL);
1066
1067     /*
1068      * Unless prevented by -, -c, -i, -s, or -t, if there are remaining
1069      * arguments the first of them is the name of a shell file from which to
1070      * read commands.
1071      */
1072     if (nofile == 0 && argc > 0) {
1073         nofile = xopen(tempv[0], O_RDONLY|O_LARGEFILE);
1074         if (nofile < 0) {
1075             child = 1;          /* So this ... */
1076             /* ... doesn't return */
1077             stderror(ERR_SYSTEM, tempv[0], strerror(errno));
1078         }
1079         xfree(ffile);
1080         dolzero = 1;
1081         ffile = SAVE(tempv[0]);
1082         /* 
1083          * Replace FSHIN. Handle /dev/std{in,out,err} specially
1084          * since once they are closed we cannot open them again.
1085          * In that case we use our own saved descriptors
1086          */
1087         if ((SHIN = dmove(nofile, FSHIN)) < 0) 
1088             switch(nofile) {
1089             case 0:
1090                 SHIN = FSHIN;
1091                 break;
1092             case 1:
1093                 SHIN = FSHOUT;
1094                 break;
1095             case 2:
1096                 SHIN = FSHDIAG;
1097                 break;
1098             default:
1099                 stderror(ERR_SYSTEM, tempv[0], strerror(errno));
1100                 break;
1101             }
1102         (void) close_on_exec(SHIN, 1);
1103         prompt = 0;
1104          /* argc not used any more */ tempv++;
1105     }
1106
1107     /* 
1108      * Call to closem() used to be part of initdesc(). Now called below where
1109      * the script name argument has become stdin. Kernel may have used a file
1110      * descriptor to hold the name of the script (setuid case) and this name
1111      * mustn't be lost by closing the fd too soon.
1112      */
1113     closem();
1114
1115     /*
1116      * Consider input a tty if it really is or we are interactive. but not for
1117      * editing (christos)
1118      */
1119     if (!(intty = isatty(SHIN))) {
1120         if (adrof(STRedit))
1121             unsetv(STRedit);
1122         editing = 0;
1123     }
1124     intty |= intact;
1125 #ifndef convex
1126     if (intty || (intact && isatty(SHOUT))) {
1127         if (!batch && (uid != euid || gid != egid)) {
1128             errno = EACCES;
1129             child = 1;          /* So this ... */
1130             /* ... doesn't return */
1131             stderror(ERR_SYSTEM, progname, strerror(errno));
1132         }
1133     }
1134 #endif /* convex */
1135     isoutatty = isatty(SHOUT);
1136     isdiagatty = isatty(SHDIAG);
1137     /*
1138      * Decide whether we should play with signals or not. If we are explicitly
1139      * told (via -i, or -) or we are a login shell (arg0 starts with -) or the
1140      * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
1141      * Note that in only the login shell is it likely that parent may have set
1142      * signals to be ignored
1143      */
1144     if (loginsh || intact || (intty && isatty(SHOUT)))
1145         setintr = 1;
1146     settell();
1147     /*
1148      * Save the remaining arguments in argv.
1149      */
1150     setq(STRargv, blk2short(tempv), &shvhed, VAR_READWRITE);
1151
1152     /*
1153      * Set up the prompt.
1154      */
1155     if (prompt) {
1156         setcopy(STRprompt, STRdefprompt, VAR_READWRITE);
1157         /* that's a meta-questionmark */
1158         setcopy(STRprompt2, STRmquestion, VAR_READWRITE);
1159         setcopy(STRprompt3, STRKCORRECT, VAR_READWRITE);
1160     }
1161
1162     /*
1163      * If we are an interactive shell, then start fiddling with the signals;
1164      * this is a tricky game.
1165      */
1166     shpgrp = mygetpgrp();
1167     opgrp = tpgrp = -1;
1168     if (setintr) {
1169         struct sigaction osig;
1170
1171         **argv = '-';
1172         if (!quitit)            /* Wary! */
1173             (void) signal(SIGQUIT, SIG_IGN);
1174         pintr_disabled = 1;
1175         sigset_interrupting(SIGINT, queue_pintr);
1176         (void) signal(SIGTERM, SIG_IGN);
1177
1178         /* 
1179          * No reason I can see not to save history on all these events..
1180          * Most usual occurrence is in a window system, where we're not a login
1181          * shell, but might as well be... (sg)
1182          * But there might be races when lots of shells exit together...
1183          * [this is also incompatible].
1184          * We have to be mre careful here. If the parent wants to 
1185          * ignore the signals then we leave them untouched...
1186          * We also only setup the handlers for shells that are trully
1187          * interactive.
1188          */
1189         sigaction(SIGHUP, NULL, &osig);
1190         if (loginsh || osig.sa_handler != SIG_IGN)
1191             /* exit processing on HUP */
1192             sigset_interrupting(SIGHUP, queue_phup);
1193 #ifdef SIGXCPU
1194         sigaction(SIGXCPU, NULL, &osig);
1195         if (loginsh || osig.sa_handler != SIG_IGN)
1196             /* exit processing on XCPU */
1197             sigset_interrupting(SIGXCPU, queue_phup);
1198 #endif
1199 #ifdef SIGXFSZ
1200         sigaction(SIGXFSZ, NULL, &osig);
1201         if (loginsh || osig.sa_handler != SIG_IGN)
1202             /* exit processing on XFSZ */
1203             sigset_interrupting(SIGXFSZ, queue_phup);
1204 #endif
1205
1206         if (quitit == 0 && arginp == 0) {
1207 #ifdef SIGTSTP
1208             (void) signal(SIGTSTP, SIG_IGN);
1209 #endif
1210 #ifdef SIGTTIN
1211             (void) signal(SIGTTIN, SIG_IGN);
1212 #endif
1213 #ifdef SIGTTOU
1214             (void) signal(SIGTTOU, SIG_IGN);
1215 #endif
1216             /*
1217              * Wait till in foreground, in case someone stupidly runs csh &
1218              * dont want to try to grab away the tty.
1219              */
1220             if (isatty(FSHDIAG))
1221                 f = FSHDIAG;
1222             else if (isatty(FSHOUT))
1223                 f = FSHOUT;
1224             else if (isatty(OLDSTD))
1225                 f = OLDSTD;
1226             else
1227                 f = -1;
1228
1229 #ifdef NeXT
1230             /* NeXT 2.0 /usr/etc/rlogind, does not set our process group! */
1231             if (f != -1 && shpgrp == 0) {
1232                 shpgrp = getpid();
1233                 (void) setpgid(0, shpgrp);
1234                 (void) tcsetpgrp(f, shpgrp);
1235             }
1236 #endif /* NeXT */
1237 #ifdef BSDJOBS                  /* if we have tty job control */
1238             if (f != -1 && grabpgrp(f, shpgrp) != -1) {
1239                 /*
1240                  * Thanks to Matt Day for the POSIX references, and to
1241                  * Paul Close for the SGI clarification.
1242                  */
1243                 if (setdisc(f) != -1) {
1244                     opgrp = shpgrp;
1245                     shpgrp = getpid();
1246                     tpgrp = shpgrp;
1247                     if (tcsetpgrp(f, shpgrp) == -1) {
1248                         /*
1249                          * On hpux 7.03 this fails with EPERM. This happens on
1250                          * the 800 when opgrp != shpgrp at this point. (we were
1251                          * forked from a non job control shell)
1252                          * POSIX 7.2.4, says we failed because the process
1253                          * group specified did not belong to a process
1254                          * in the same session with the tty. So we set our
1255                          * process group and try again.
1256                          */
1257                         if (setpgid(0, shpgrp) == -1) {
1258                             xprintf("setpgid:");
1259                             goto notty;
1260                         }
1261                         if (tcsetpgrp(f, shpgrp) == -1) {
1262                             xprintf("tcsetpgrp:");
1263                             goto notty;
1264                         }
1265                     }
1266                     /*
1267                      * We check the process group now. If it is the same, then
1268                      * we don't need to set it again. On hpux 7.0 on the 300's
1269                      * if we set it again it fails with EPERM. This is the
1270                      * correct behavior according to POSIX 4.3.3 if the process
1271                      * was a session leader .
1272                      */
1273                     else if (shpgrp != mygetpgrp()) {
1274                         if(setpgid(0, shpgrp) == -1) {
1275                             xprintf("setpgid:");
1276                             goto notty;
1277                         }
1278                     }
1279 #ifdef IRIS4D
1280                     /*
1281                      * But on irix 3.3 we need to set it again, even if it is
1282                      * the same. We do that to tell the system that we
1283                      * need BSD process group compatibility.
1284                      */
1285                     else
1286                         (void) setpgid(0, shpgrp);
1287 #endif
1288                     (void) close_on_exec(dcopy(f, FSHTTY), 1);
1289                 }
1290                 else
1291                     tpgrp = -1;
1292             }
1293             if (tpgrp == -1) {
1294         notty:
1295                 xprintf(CGETS(11, 1, "Warning: no access to tty (%s).\n"),
1296                     strerror(errno));
1297                 xprintf("%s",
1298                     CGETS(11, 2, "Thus no job control in this shell.\n"));
1299                 /*
1300                  * Fix from:Sakari Jalovaara <sja@sirius.hut.fi> if we don't
1301                  * have access to tty, disable editing too
1302                  */
1303                 if (adrof(STRedit))
1304                     unsetv(STRedit);
1305                 editing = 0;
1306             }
1307 #else   /* BSDJOBS */           /* don't have job control, so frotz it */
1308             tpgrp = -1;
1309 #endif                          /* BSDJOBS */
1310         }
1311     }
1312     if (setintr == 0 && parintr.sa_handler == SIG_DFL)
1313         setintr = 1;
1314
1315 /*
1316  * SVR4 doesn't send a SIGCHLD when a child is stopped or continued if the
1317  * handler is installed with signal(2) or sigset(2).  sigaction(2) must
1318  * be used instead.
1319  *
1320  * David Dawes (dawes@physics.su.oz.au) Sept 1991
1321  */
1322     sigset_interrupting(SIGCHLD, queue_pchild);
1323
1324     if (intty && !arginp)       
1325         (void) ed_Setup(editing);/* Get the tty state, and set defaults */
1326                                  /* Only alter the tty state if editing */
1327     
1328     /*
1329      * Set an exit here in case of an interrupt or error reading the shell
1330      * start-up scripts.
1331      */
1332     osetintr = setintr;
1333     oparintr = parintr;
1334     (void)cleanup_push_mark(); /* There is no outer handler */
1335     if (setexit() != 0) /* PWP */
1336         reenter = 1;
1337     else
1338         reenter = 0;
1339     exitset++;
1340     haderr = 0;                 /* In case second time through */
1341     if (!fast && reenter == 0) {
1342         /* Will have varval(STRhome) here because set fast if don't */
1343         {
1344             pintr_disabled++;
1345             cleanup_push(&pintr_disabled, disabled_cleanup);
1346             setintr = 0;/*FIXRESET:cleanup*/
1347             /* onintr in /etc/ files has no effect */
1348             parintr.sa_handler = SIG_IGN;/*FIXRESET: cleanup*/
1349 #ifdef LOGINFIRST
1350 #ifdef _PATH_DOTLOGIN
1351             if (loginsh)
1352                 (void) srcfile(_PATH_DOTLOGIN, 0, 0, NULL);
1353 #endif
1354 #endif
1355
1356 #ifdef _PATH_DOTCSHRC
1357             (void) srcfile(_PATH_DOTCSHRC, 0, 0, NULL);
1358 #endif
1359             if (!arginp && !onelflg && !havhash)
1360                 dohash(NULL,NULL);
1361 #ifndef LOGINFIRST
1362 #ifdef _PATH_DOTLOGIN
1363             if (loginsh)
1364                 (void) srcfile(_PATH_DOTLOGIN, 0, 0, NULL);
1365 #endif
1366 #endif
1367             cleanup_until(&pintr_disabled);
1368             setintr = osetintr;
1369             parintr = oparintr;
1370         }
1371 #ifdef LOGINFIRST
1372         if (loginsh)
1373             (void) srccat(varval(STRhome), STRsldotlogin);
1374 #endif
1375         /* upward compat. */
1376         if (!srccat(varval(STRhome), STRsldottcshrc))
1377             (void) srccat(varval(STRhome), STRsldotcshrc);
1378
1379         if (!arginp && !onelflg && !havhash)
1380             dohash(NULL,NULL);
1381
1382         /*
1383          * Source history before .login so that it is available in .login
1384          */
1385         loadhist(NULL, 0);
1386 #ifndef LOGINFIRST
1387         if (loginsh)
1388             (void) srccat(varval(STRhome), STRsldotlogin);
1389 #endif
1390         if (loginsh || rdirs)
1391             loaddirs(NULL);
1392     }
1393     /* Reset interrupt flag */
1394     setintr = osetintr;
1395     parintr = oparintr;
1396     exitset--;
1397
1398     /* Initing AFTER .cshrc is the Right Way */
1399     if (intty && !arginp) {     /* PWP setup stuff */
1400         ed_Init();              /* init the new line editor */
1401 #ifdef SIG_WINDOW
1402         check_window_size(1);   /* mung environment */
1403 #endif                          /* SIG_WINDOW */
1404     }
1405
1406     /*
1407      * Now are ready for the -v and -x flags
1408      */
1409     if (nverbose)
1410         setNS(STRverbose);
1411     if (nexececho)
1412         setNS(STRecho);
1413     
1414     /*
1415      * All the rest of the world is inside this call. The argument to process
1416      * indicates whether it should catch "error unwinds".  Thus if we are a
1417      * interactive shell our call here will never return by being blown past on
1418      * an error.
1419      */
1420     process(setintr);
1421
1422     /*
1423      * Mop-up.
1424      */
1425     /* Take care of these (especially HUP) here instead of inside flush. */
1426     handle_pending_signals();
1427     if (intty) {
1428         if (loginsh) {
1429             xprintf("logout\n");
1430             xclose(SHIN);
1431             child = 1;
1432 #ifdef TESLA
1433             do_logout = 1;
1434 #endif                          /* TESLA */
1435             goodbye(NULL, NULL);
1436         }
1437         else {
1438             xprintf("exit\n");
1439         }
1440     }
1441     record();
1442     exitstat();
1443     return (0);
1444 }
1445
1446 void
1447 untty(void)
1448 {
1449 #ifdef BSDJOBS
1450     if (tpgrp > 0 && opgrp != shpgrp) {
1451         (void) setpgid(0, opgrp);
1452         (void) tcsetpgrp(FSHTTY, opgrp);
1453         (void) resetdisc(FSHTTY);
1454     }
1455 #endif /* BSDJOBS */
1456 }
1457
1458 void
1459 importpath(Char *cp)
1460 {
1461     size_t i = 0;
1462     Char *dp;
1463     Char **pv;
1464     int     c;
1465
1466     for (dp = cp; *dp; dp++)
1467         if (*dp == PATHSEP)
1468             i++;
1469     /*
1470      * i+2 where i is the number of colons in the path. There are i+1
1471      * directories in the path plus we need room for a zero terminator.
1472      */
1473     pv = xcalloc(i + 2, sizeof(Char *));
1474     dp = cp;
1475     i = 0;
1476     if (*dp)
1477         for (;;) {
1478             if ((c = *dp) == PATHSEP || c == 0) {
1479                 *dp = 0;
1480                 pv[i++] = Strsave(*cp ? cp : STRdot);
1481                 if (c) {
1482                     cp = dp + 1;
1483                     *dp = PATHSEP;
1484                 }
1485                 else
1486                     break;
1487             }
1488 #ifdef WINNT_NATIVE
1489             else if (*dp == '\\')
1490                 *dp = '/';
1491 #endif /* WINNT_NATIVE */
1492             dp++;
1493         }
1494     pv[i] = 0;
1495     cleanup_push(pv, blk_cleanup);
1496     setq(STRpath, pv, &shvhed, VAR_READWRITE);
1497     cleanup_ignore(pv);
1498     cleanup_until(pv);
1499 }
1500
1501 /*
1502  * Source to the file which is the catenation of the argument names.
1503  */
1504 static int
1505 srccat(Char *cp, Char *dp)
1506 {
1507     if (cp[0] == '/' && cp[1] == '\0') 
1508         return srcfile(short2str(dp), (mflag ? 0 : 1), 0, NULL);
1509     else {
1510         Char *ep;
1511         char   *ptr;
1512         int rv;
1513
1514 #ifdef WINNT_NATIVE
1515         ep = Strend(cp);
1516         if (ep != cp && ep[-1] == '/' && dp[0] == '/') /* silly win95 */
1517             dp++;
1518 #endif /* WINNT_NATIVE */
1519
1520         ep = Strspl(cp, dp);
1521         cleanup_push(ep, xfree);
1522         ptr = short2str(ep);
1523
1524         rv = srcfile(ptr, (mflag ? 0 : 1), 0, NULL);
1525         cleanup_until(ep);
1526         return rv;
1527     }
1528 }
1529
1530 /*
1531  * Source to a file putting the file descriptor in a safe place (> 2).
1532  */
1533 #ifndef WINNT_NATIVE
1534 static int
1535 #else
1536 int
1537 #endif /*WINNT_NATIVE*/
1538 srcfile(const char *f, int onlyown, int flag, Char **av)
1539 {
1540     int unit;
1541
1542     if ((unit = xopen(f, O_RDONLY|O_LARGEFILE)) == -1) 
1543         return 0;
1544     cleanup_push(&unit, open_cleanup);
1545     unit = dmove(unit, -1);
1546     cleanup_ignore(&unit);
1547     cleanup_until(&unit);
1548
1549     (void) close_on_exec(unit, 1);
1550     srcunit(unit, onlyown, flag, av);
1551     return 1;
1552 }
1553
1554
1555 /*
1556  * Save the shell state, and establish new argument vector, and new input
1557  * fd.
1558  */
1559 static void
1560 st_save(struct saved_state *st, int unit, int hflg, Char **al, Char **av)
1561 {
1562     st->insource        = insource;
1563     st->SHIN            = SHIN;
1564     /* Want to preserve the meaning of "source file >output".
1565      * Save old descriptors, move new 0,1,2 to safe places and assign
1566      * them to SH* and let process() redo 0,1,2 from them.
1567      *
1568      * The macro returns true if d1 and d2 are good and they point to
1569      * different things.  If you don't avoid saving duplicate
1570      * descriptors, you really limit the depth of "source" recursion
1571      * you can do because of all the open file descriptors.  -IAN!
1572      */
1573 #define NEED_SAVE_FD(d1,d2) \
1574     (fstat(d1, &s1) != -1 && fstat(d2, &s2) != -1 \
1575         && (s1.st_ino != s2.st_ino || s1.st_dev != s2.st_dev) )
1576
1577     st->OLDSTD = st->SHOUT = st->SHDIAG = -1;/* test later to restore these */
1578     if (didfds) {
1579             struct stat s1, s2;
1580             if (NEED_SAVE_FD(0,OLDSTD)) {
1581                     st->OLDSTD = OLDSTD;
1582                     OLDSTD = dmove(0, -1);
1583                     (void)close_on_exec(OLDSTD, 1);
1584             }
1585             if (NEED_SAVE_FD(1,SHOUT)) {
1586                     st->SHOUT = SHOUT;
1587                     SHOUT = dmove(1, -1);
1588                     (void)close_on_exec(SHOUT, 1);
1589             }
1590             if (NEED_SAVE_FD(2,SHDIAG)) {
1591                     st->SHDIAG = SHDIAG;
1592                     SHDIAG = dmove(2, -1);
1593                     (void)close_on_exec(SHDIAG, 1);
1594             }
1595             donefds();
1596     }
1597
1598     st->intty           = intty;
1599     st->whyles          = whyles;
1600     st->gointr          = gointr;
1601     st->arginp          = arginp;
1602     st->evalp           = evalp;
1603     st->evalvec         = evalvec;
1604     st->alvecp          = alvecp;
1605     st->alvec           = alvec;
1606     st->onelflg         = onelflg;
1607     st->enterhist       = enterhist;
1608     st->justpr          = justpr;
1609     if (hflg)
1610         st->HIST        = HIST;
1611     else
1612         st->HIST        = '\0';
1613     st->cantell         = cantell;
1614     cpybin(st->B, B);
1615
1616     /*
1617      * we can now pass arguments to source. 
1618      * For compatibility we do that only if arguments were really
1619      * passed, otherwise we keep the old, global $argv like before.
1620      */
1621     if (av != NULL && *av != NULL) {
1622         struct varent *vp;
1623         if ((vp = adrof(STRargv)) != NULL && vp->vec != NULL)
1624             st->argv = saveblk(vp->vec);
1625         else
1626             st->argv = NULL;
1627         setq(STRargv, saveblk(av), &shvhed, VAR_READWRITE);
1628     }
1629     else
1630         st->argv = NULL;
1631     st->av = av;
1632
1633     SHIN        = unit; /* Do this first */
1634
1635     /* Establish new input arena */
1636     {
1637         fbuf = NULL;
1638         fseekp = feobp = fblocks = 0;
1639         settell();
1640     }
1641
1642     arginp      = 0;
1643     onelflg     = 0;
1644     intty       = isatty(SHIN);
1645     whyles      = 0;
1646     gointr      = 0;
1647     evalvec     = 0;
1648     evalp       = 0;
1649     alvec       = al;
1650     alvecp      = 0;
1651     enterhist   = hflg;
1652     if (enterhist)
1653         HIST    = '\0';
1654     insource    = 1;
1655 }
1656
1657
1658 /*
1659  * Restore the shell to a saved state
1660  */
1661 static void
1662 st_restore(void *xst)
1663 {
1664     struct saved_state *st;
1665
1666     st = xst;
1667     if (st->SHIN == -1)
1668         return;
1669
1670     /* Reset input arena */
1671     {
1672         int i;
1673         Char** nfbuf = fbuf;
1674         int nfblocks = fblocks;
1675
1676         fblocks = 0;
1677         fbuf = NULL;
1678         for (i = 0; i < nfblocks; i++)
1679             xfree(nfbuf[i]);
1680         xfree(nfbuf);
1681     }
1682     cpybin(B, st->B);
1683
1684     xclose(SHIN);
1685
1686     insource    = st->insource;
1687     SHIN        = st->SHIN;
1688     if (st->OLDSTD != -1)
1689         xclose(OLDSTD), OLDSTD = st->OLDSTD;
1690     if (st->SHOUT != -1)
1691         xclose(SHOUT),  SHOUT = st->SHOUT;
1692     if (st->SHDIAG != -1)
1693         xclose(SHDIAG), SHDIAG = st->SHDIAG;
1694     arginp      = st->arginp;
1695     onelflg     = st->onelflg;
1696     evalp       = st->evalp;
1697     evalvec     = st->evalvec;
1698     alvecp      = st->alvecp;
1699     alvec       = st->alvec;
1700     intty       = st->intty;
1701     whyles      = st->whyles;
1702     gointr      = st->gointr;
1703     if (st->HIST != '\0')
1704         HIST    = st->HIST;
1705     enterhist   = st->enterhist;
1706     cantell     = st->cantell;
1707     justpr      = st->justpr;
1708
1709     if (st->argv != NULL)
1710         setq(STRargv, st->argv, &shvhed, VAR_READWRITE);
1711     else if (st->av != NULL  && *st->av != NULL && adrof(STRargv) != NULL)
1712         unsetv(STRargv);
1713 }
1714
1715 /*
1716  * Source to a unit.  If onlyown it must be our file or our group or
1717  * we don't chance it.  This occurs on ".cshrc"s and the like.
1718  */
1719 static void
1720 srcunit(int unit, int onlyown, int hflg, Char **av)
1721 {
1722     struct saved_state st;
1723
1724     st.SHIN = -1;       /* st_restore checks this */
1725
1726     if (unit < 0)
1727         return;
1728
1729     if (onlyown) {
1730         struct stat stb;
1731
1732         if (fstat(unit, &stb) < 0) {
1733             xclose(unit);
1734             return;
1735         }
1736     }
1737
1738     /* Does nothing before st_save() because st.SHIN == -1 */
1739     cleanup_push(&st, st_restore);
1740     if (setintr) {
1741         pintr_disabled++;
1742         cleanup_push(&pintr_disabled, disabled_cleanup);
1743     }
1744
1745     /* Save the current state and move us to a new state */
1746     st_save(&st, unit, hflg, NULL, av);
1747
1748     /*
1749      * Now if we are allowing commands to be interrupted, we let ourselves be
1750      * interrupted.
1751      */
1752     if (setintr) {
1753         cleanup_until(&pintr_disabled);
1754         pintr_disabled++;
1755         cleanup_push(&pintr_disabled, disabled_cleanup);
1756     }
1757
1758     process(0);         /* 0 -> blow away on errors */
1759
1760     /* Restore the old state */
1761     cleanup_until(&st);
1762 }
1763
1764
1765 /*ARGSUSED*/
1766 void
1767 goodbye(Char **v, struct command *c)
1768 {
1769     USE(v);
1770     USE(c);
1771     record();
1772
1773     if (loginsh) {
1774         size_t omark;
1775         sigset_t set;
1776
1777         sigemptyset(&set);
1778         signal(SIGQUIT, SIG_IGN);
1779         sigaddset(&set, SIGQUIT);
1780         sigprocmask(SIG_UNBLOCK, &set, NULL);
1781         signal(SIGINT, SIG_IGN);
1782         sigaddset(&set, SIGINT);
1783         signal(SIGTERM, SIG_IGN);
1784         sigaddset(&set, SIGTERM);
1785         signal(SIGHUP, SIG_IGN);
1786         sigaddset(&set, SIGHUP);
1787         sigprocmask(SIG_UNBLOCK, &set, NULL);
1788         phup_disabled = 1;
1789         setintr = 0;            /* No interrupts after "logout" */
1790         /* Trap errors inside .logout */
1791         omark = cleanup_push_mark();
1792         if (setexit() == 0) {
1793             if (!(adrof(STRlogout)))
1794                 setcopy(STRlogout, STRnormal, VAR_READWRITE);
1795 #ifdef _PATH_DOTLOGOUT
1796             (void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL);
1797 #endif
1798             if (adrof(STRhome))
1799                 (void) srccat(varval(STRhome), STRsldtlogout);
1800 #ifdef TESLA
1801             do_logout = 1;
1802 #endif /* TESLA */
1803         }
1804         cleanup_pop_mark(omark);
1805     }
1806     exitstat();
1807 }
1808
1809 void
1810 exitstat(void)
1811 {
1812 #ifdef PROF
1813     _mcleanup();
1814 #endif
1815     /*
1816      * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit
1817      * directly because we poke child here. Otherwise we might continue
1818      * unwarrantedly (sic).
1819      */
1820     child = 1;
1821
1822     xexit(getn(varval(STRstatus)));
1823 }
1824
1825 /*
1826  * in the event of a HUP we want to save the history
1827  */
1828 void
1829 phup(void)
1830 {
1831     if (loginsh) {
1832         setcopy(STRlogout, STRhangup, VAR_READWRITE);
1833 #ifdef _PATH_DOTLOGOUT
1834         (void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL);
1835 #endif
1836         if (adrof(STRhome))
1837             (void) srccat(varval(STRhome), STRsldtlogout);
1838     }
1839
1840     record();
1841
1842 #ifdef POSIXJOBS 
1843     /*
1844      * We kill the last foreground process group. It then becomes
1845      * responsible to propagate the SIGHUP to its progeny. 
1846      */
1847     {
1848         struct process *pp, *np;
1849
1850         for (pp = proclist.p_next; pp; pp = pp->p_next) {
1851             np = pp;
1852             /* 
1853              * Find if this job is in the foreground. It could be that
1854              * the process leader has exited and the foreground flag
1855              * is cleared for it.
1856              */
1857             do 
1858                 /*
1859                  * If a process is in the foreground we try to kill
1860                  * it's process group. If we succeed, then the 
1861                  * whole job is gone. Otherwise we keep going...
1862                  * But avoid sending HUP to the shell again.
1863                  */
1864                 if (((np->p_flags & PFOREGND) != 0) && np->p_jobid != shpgrp) {
1865                     np->p_flags &= ~PHUP;
1866                     if (killpg(np->p_jobid, SIGHUP) != -1) {
1867                         /* In case the job was suspended... */
1868 #ifdef SIGCONT
1869                         (void) killpg(np->p_jobid, SIGCONT);
1870 #endif
1871                         break;
1872                     }
1873                 }
1874             while ((np = np->p_friends) != pp);
1875         }
1876     }
1877 #endif /* POSIXJOBS */
1878
1879     xexit(SIGHUP);
1880 }
1881
1882 static Char   *jobargv[2] = {STRjobs, 0};
1883
1884 /*
1885  * Catch an interrupt, e.g. during lexical input.
1886  * If we are an interactive shell, we reset the interrupt catch
1887  * immediately.  In any case we drain the shell output,
1888  * and finally go through the normal error mechanism, which
1889  * gets a chance to make the shell go away.
1890  */
1891 int just_signaled;              /* bugfix by Michael Bloom (mg@ttidca.TTI.COM) */
1892
1893 void
1894 pintr(void)
1895 {
1896     just_signaled = 1;
1897     pintr1(1);
1898 }
1899
1900 void
1901 pintr1(int wantnl)
1902 {
1903     if (setintr) {
1904         if (pjobs) {
1905             pjobs = 0;
1906             xputchar('\n');
1907             dojobs(jobargv, NULL);
1908             stderror(ERR_NAME | ERR_INTR);
1909         }
1910     }
1911     /* MH - handle interrupted completions specially */
1912     {
1913         if (InsideCompletion)
1914             stderror(ERR_SILENT);
1915     }
1916     /* JV - Make sure we shut off inputl */
1917     {
1918         (void) Cookedmode();
1919         GettingInput = 0;
1920         if (evalvec)
1921             doneinp = 1;
1922     }
1923     drainoline();
1924 #ifdef HAVE_GETPWENT
1925     (void) endpwent();
1926 #endif
1927
1928     /*
1929      * If we have an active "onintr" then we search for the label. Note that if
1930      * one does "onintr -" then we shan't be interruptible so we needn't worry
1931      * about that here.
1932      */
1933     if (gointr) {
1934         gotolab(gointr);
1935         reset();
1936     }
1937     else if (intty && wantnl) {
1938         if (editing) {
1939             /* 
1940              * If we are editing a multi-line input command, and move to
1941              * the beginning of the line, we don't want to trash it when
1942              * we hit ^C
1943              */
1944             PastBottom();
1945             ClearLines();
1946             ClearDisp();
1947         }
1948         else {
1949             /* xputchar('\n'); *//* Some like this, others don't */
1950             (void) putraw('\r');
1951             (void) putraw('\n');
1952         }
1953     }
1954     stderror(ERR_SILENT);
1955 }
1956
1957 /*
1958  * Process is the main driving routine for the shell.
1959  * It runs all command processing, except for those within { ... }
1960  * in expressions (which is run by a routine evalav in sh.exp.c which
1961  * is a stripped down process), and `...` evaluation which is run
1962  * also by a subset of this code in sh.glob.c in the routine backeval.
1963  *
1964  * The code here is a little strange because part of it is interruptible
1965  * and hence freeing of structures appears to occur when none is necessary
1966  * if this is ignored.
1967  *
1968  * Note that if catch is not set then we will unwind on any error.
1969  * If an end-of-file occurs, we return.
1970  */
1971 void
1972 process(int catch)
1973 {
1974     jmp_buf_t osetexit;
1975     /* PWP: This might get nuked by longjmp so don't make it a register var */
1976     size_t omark;
1977     volatile int didexitset = 0;
1978
1979     getexit(osetexit);
1980     omark = cleanup_push_mark();
1981     for (;;) {
1982         struct command *t;
1983         int hadhist, old_pintr_disabled;
1984
1985         (void)setexit();
1986         if (didexitset == 0) {
1987             exitset++;
1988             didexitset++;
1989         }
1990         pendjob();
1991
1992         justpr = enterhist;     /* execute if not entering history */
1993
1994         if (haderr) {
1995             if (!catch) {
1996                 /* unwind */
1997                 doneinp = 0;
1998                 cleanup_pop_mark(omark);
1999                 resexit(osetexit);
2000                 reset();
2001             }
2002             haderr = 0;
2003             /*
2004              * Every error is eventually caught here or the shell dies.  It is
2005              * at this point that we clean up any left-over open files, by
2006              * closing all but a fixed number of pre-defined files.  Thus
2007              * routines don't have to worry about leaving files open due to
2008              * deeper errors... they will get closed here.
2009              */
2010             closem();
2011             continue;
2012         }
2013         if (doneinp) {
2014             doneinp = 0;
2015             break;
2016         }
2017         if (chkstop)
2018             chkstop--;
2019         if (neednote)
2020             pnote();
2021         if (intty && prompt && evalvec == 0) {
2022             just_signaled = 0;
2023             mailchk();
2024             /*
2025              * Watch for logins/logouts. Next is scheduled commands stored
2026              * previously using "sched." Then execute periodic commands.
2027              * Following that, the prompt precmd is run.
2028              */
2029 #ifndef HAVENOUTMP
2030             watch_login(0);
2031 #endif /* !HAVENOUTMP */
2032             sched_run();
2033             period_cmd();
2034             precmd();
2035             /*
2036              * If we are at the end of the input buffer then we are going to
2037              * read fresh stuff. Otherwise, we are rereading input and don't
2038              * need or want to prompt.
2039              */
2040             if (fseekp == feobp && aret == TCSH_F_SEEK)
2041                 printprompt(0, NULL);
2042             flush();
2043             setalarm(1);
2044         }
2045         if (seterr) {
2046             xfree(seterr);
2047             seterr = NULL;
2048         }
2049
2050         /*
2051          * Interruptible during interactive reads
2052          */
2053         if (setintr)
2054             pintr_push_enable(&old_pintr_disabled);
2055         hadhist = lex(&paraml);
2056         if (setintr)
2057             cleanup_until(&old_pintr_disabled);
2058         cleanup_push(&paraml, lex_cleanup);
2059
2060         /*
2061          * Echo not only on VERBOSE, but also with history expansion. If there
2062          * is a lexical error then we forego history echo.
2063          * Do not echo if we're only entering history (source -h).
2064          */
2065         if ((hadhist && !seterr && intty && !tellwhat && !Expand && !whyles) ||
2066             (!enterhist && adrof(STRverbose)))
2067         {
2068             int odidfds = didfds;
2069             haderr = 1;
2070             didfds = 0;
2071             prlex(&paraml);
2072             flush();
2073             haderr = 0;
2074             didfds = odidfds;
2075         }
2076         (void) alarm(0);        /* Autologout OFF */
2077         alrmcatch_disabled = 1;
2078
2079         /*
2080          * Save input text on the history list if reading in old history, or it
2081          * is from the terminal at the top level and not in a loop.
2082          * 
2083          * PWP: entry of items in the history list while in a while loop is done
2084          * elsewhere...
2085          */
2086         if (enterhist || (catch && intty && !whyles && !tellwhat && !arun))
2087             savehist(&paraml, enterhist > 1);
2088
2089         if (Expand && seterr)
2090             Expand = 0;
2091
2092         /*
2093          * Print lexical error messages, except when sourcing history lists.
2094          */
2095         if (!enterhist && seterr)
2096             stderror(ERR_OLD);
2097
2098         /*
2099          * If had a history command :p modifier then this is as far as we
2100          * should go
2101          */
2102         if (justpr)
2103             goto cmd_done;
2104
2105         /*
2106          * If had a tellwhat from twenex() then do
2107          */
2108         if (tellwhat) {
2109             (void) tellmewhat(&paraml, NULL);
2110             goto cmd_done;
2111         }
2112
2113         alias(&paraml);
2114
2115 #ifdef BSDJOBS
2116         /*
2117          * If we are interactive, try to continue jobs that we have stopped
2118          */
2119         if (prompt)
2120             continue_jobs(&paraml);
2121 #endif                          /* BSDJOBS */
2122
2123         /*
2124          * Check to see if the user typed "rm * .o" or something
2125          */
2126         if (prompt)
2127             rmstar(&paraml);
2128         /*
2129          * Parse the words of the input into a parse tree.
2130          */
2131         t = syntax(paraml.next, &paraml, 0);
2132         /*
2133          * We cannot cleanup push here, because cd /blah; echo foo
2134          * would rewind t on the chdir error, and free the rest of the command
2135          */
2136         if (seterr) {
2137             freesyn(t);
2138             stderror(ERR_OLD);
2139         }
2140
2141         postcmd();
2142         /*
2143          * Execute the parse tree From: Michael Schroeder
2144          * <mlschroe@immd4.informatik.uni-erlangen.de> was execute(t, tpgrp);
2145          */
2146         execute(t, (tpgrp > 0 ? tpgrp : -1), NULL, NULL, TRUE);
2147         freesyn(t);
2148
2149         /*
2150          * Made it!
2151          */
2152 #ifdef SIG_WINDOW
2153         if (windowchg || (catch && intty && !whyles && !tellwhat)) {
2154             (void) check_window_size(0);        /* for window systems */
2155         }
2156 #endif /* SIG_WINDOW */
2157         setcopy(STR_, InputBuf, VAR_READWRITE | VAR_NOGLOB);
2158     cmd_done:
2159         if (cleanup_reset())
2160             cleanup_until(&paraml);
2161         else
2162             haderr = 1;
2163     }
2164     cleanup_pop_mark(omark);
2165     resexit(osetexit);
2166     exitset--;
2167     handle_pending_signals();
2168 }
2169
2170 /*ARGSUSED*/
2171 void
2172 dosource(Char **t, struct command *c)
2173 {
2174     Char *f;
2175     int    hflg = 0;
2176     char *file;
2177
2178     USE(c);
2179     t++;
2180     if (*t && eq(*t, STRmh)) {
2181         if (*++t == NULL)
2182             stderror(ERR_NAME | ERR_HFLAG);
2183         hflg++;
2184     }
2185     else if (*t && eq(*t, STRmm)) {
2186         if (*++t == NULL)
2187             stderror(ERR_NAME | ERR_MFLAG);
2188         hflg = 2;
2189     }
2190
2191     f = globone(*t++, G_ERROR);
2192     file = strsave(short2str(f));
2193     cleanup_push(file, xfree);
2194     xfree(f);
2195     t = glob_all_or_error(t);
2196     cleanup_push(t, blk_cleanup);
2197     if ((!srcfile(file, 0, hflg, t)) && (!hflg) && (!bequiet))
2198         stderror(ERR_SYSTEM, file, strerror(errno));
2199     cleanup_until(file);
2200 }
2201
2202 /*
2203  * Check for mail.
2204  * If we are a login shell, then we don't want to tell
2205  * about any mail file unless its been modified
2206  * after the time we started.
2207  * This prevents us from telling the user things he already
2208  * knows, since the login program insists on saying
2209  * "You have mail."
2210  */
2211
2212 /*
2213  * The AMS version.
2214  * This version checks if the file is a directory, and if so,
2215  * tells you the number of files in it, otherwise do the old thang.
2216  * The magic "+1" in the time calculation is to compensate for
2217  * an AFS bug where directory mtimes are set to 1 second in
2218  * the future.
2219  */
2220
2221 static void
2222 mailchk(void)
2223 {
2224     struct varent *v;
2225     Char **vp;
2226     time_t  t;
2227     int     intvl, cnt;
2228     struct stat stb;
2229     int    new;
2230
2231     v = adrof(STRmail);
2232     if (v == NULL || v->vec == NULL)
2233         return;
2234     (void) time(&t);
2235     vp = v->vec;
2236     cnt = blklen(vp);
2237     intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
2238     if (intvl < 1)
2239         intvl = 1;
2240     if (chktim + intvl > t)
2241         return;
2242     for (; *vp; vp++) {
2243         char *filename = short2str(*vp);
2244         char *mboxdir = filename;
2245
2246         if (stat(filename, &stb) < 0)
2247             continue;
2248 #if defined(BSDTIMES) || defined(_SEQUENT_)
2249         new = stb.st_mtime > time0.tv_sec;
2250 #else
2251         new = stb.st_mtime > seconds0;
2252 #endif
2253         if (S_ISDIR(stb.st_mode)) {
2254             DIR *mailbox;
2255             int mailcount = 0;
2256             char *tempfilename;
2257             struct stat stc;
2258
2259             tempfilename = xasprintf("%s/new", filename);
2260
2261             if (stat(tempfilename, &stc) != -1 && S_ISDIR(stc.st_mode)) {
2262                 /*
2263                  * "filename/new" exists and is a directory; you are
2264                  * using Qmail.
2265                  */
2266                 stb = stc;
2267 #if defined(BSDTIMES) || defined(_SEQUENT_)
2268                 new = stb.st_mtime > time0.tv_sec;
2269 #else
2270                 new = stb.st_mtime > seconds0;
2271 #endif
2272                 mboxdir = tempfilename;
2273             }
2274
2275             if (stb.st_mtime <= chktim + 1 || (loginsh && !new)) {
2276                 xfree(tempfilename);
2277                 continue;
2278             }
2279
2280             mailbox = opendir(mboxdir);
2281             xfree(tempfilename);
2282             if (mailbox == NULL)
2283                 continue;
2284
2285             /* skip . and .. */
2286             if (!readdir(mailbox) || !readdir(mailbox)) {
2287                 (void)closedir(mailbox);
2288                 continue;
2289             }
2290
2291             while (readdir(mailbox))
2292                 mailcount++;
2293
2294             (void)closedir(mailbox);
2295             if (mailcount == 0)
2296                 continue;
2297
2298             if (cnt == 1)
2299                 xprintf(CGETS(11, 3, "You have %d mail messages.\n"),
2300                         mailcount);
2301             else
2302                 xprintf(CGETS(11, 4, "You have %d mail messages in %s.\n"),
2303                         mailcount, filename);
2304         }
2305         else {
2306             char *type;
2307             
2308             if (stb.st_size == 0 || stb.st_atime >= stb.st_mtime ||
2309                 (stb.st_atime <= chktim && stb.st_mtime <= chktim) ||
2310                 (loginsh && !new))
2311                 continue;
2312             type = strsave(new ? CGETS(11, 6, "new ") : "");
2313             cleanup_push(type, xfree);
2314             if (cnt == 1)
2315                 xprintf(CGETS(11, 5, "You have %smail.\n"), type);
2316             else
2317                 xprintf(CGETS(11, 7, "You have %smail in %s.\n"), type, filename);
2318             cleanup_until(type);
2319         }
2320     }
2321     chktim = t;
2322 }
2323
2324 /*
2325  * Extract a home directory from the password file
2326  * The argument points to a buffer where the name of the
2327  * user whose home directory is sought is currently.
2328  * We return home directory of the user, or NULL.
2329  */
2330 Char *
2331 gethdir(const Char *home)
2332 {
2333     Char   *h;
2334
2335     /*
2336      * Is it us?
2337      */
2338     if (*home == '\0') {
2339         if ((h = varval(STRhome)) != STRNULL)
2340             return Strsave(h);
2341         else
2342             return NULL;
2343     }
2344
2345     /*
2346      * Look in the cache
2347      */
2348     if ((h = gettilde(home)) == NULL)
2349         return NULL;
2350     else
2351         return Strsave(h);
2352 }
2353
2354 /*
2355  * Move the initial descriptors to their eventual
2356  * resting places, closing all other units.
2357  */
2358 void
2359 initdesc(void)
2360 {
2361 #ifdef NLS_BUGS
2362 #ifdef NLS_CATALOGS
2363     nlsclose();
2364 #endif /* NLS_CATALOGS */
2365 #endif /* NLS_BUGS */
2366
2367
2368     didfds = 0;                 /* 0, 1, 2 aren't set up */
2369     (void) close_on_exec(SHIN = dcopy(0, FSHIN), 1);
2370     (void) close_on_exec(SHOUT = dcopy(1, FSHOUT), 1);
2371     (void) close_on_exec(SHDIAG = dcopy(2, FSHDIAG), 1);
2372     (void) close_on_exec(OLDSTD = dcopy(SHIN, FOLDSTD), 1);
2373 #ifndef CLOSE_ON_EXEC
2374     didcch = 0;                 /* Havent closed for child */
2375 #endif /* CLOSE_ON_EXEC */
2376     if (SHDIAG >= 0)
2377         isdiagatty = isatty(SHDIAG);
2378     else
2379         isdiagatty = 0;
2380     if (SHDIAG >= 0)
2381         isoutatty = isatty(SHOUT);
2382     else
2383         isoutatty = 0;
2384 #ifdef NLS_BUGS
2385 #ifdef NLS_CATALOGS
2386     nlsinit();
2387 #endif /* NLS_CATALOGS */
2388 #endif /* NLS_BUGS */
2389 }
2390
2391
2392 void
2393 #ifdef PROF
2394 done(int i)
2395 #else
2396 xexit(int i)
2397 #endif
2398 {
2399 #ifdef TESLA
2400     if (loginsh && do_logout) {
2401         /* this is to send hangup signal to the develcon */
2402         /* we toggle DTR. clear dtr - sleep 1 - set dtr */
2403         /* ioctl will return ENOTTY for pty's but we ignore it   */
2404         /* exitstat will run after disconnect */
2405         /* we sleep for 2 seconds to let things happen in */
2406         /* .logout and rechist() */
2407 #ifdef TIOCCDTR
2408         (void) sleep(2);
2409         (void) ioctl(FSHTTY, TIOCCDTR, NULL);
2410         (void) sleep(1);
2411         (void) ioctl(FSHTTY, TIOCSDTR, NULL);
2412 #endif /* TIOCCDTR */
2413     }
2414 #endif /* TESLA */
2415
2416     {
2417         struct process *pp, *np;
2418         pid_t mypid = getpid();
2419         /* Kill all processes marked for hup'ing */
2420         for (pp = proclist.p_next; pp; pp = pp->p_next) {
2421             np = pp;
2422             do
2423                 if ((np->p_flags & PHUP) && np->p_jobid != shpgrp &&
2424                     np->p_parentid == mypid) {
2425                     if (killpg(np->p_jobid, SIGHUP) != -1) {
2426                         /* In case the job was suspended... */
2427 #ifdef SIGCONT
2428                         (void) killpg(np->p_jobid, SIGCONT);
2429 #endif
2430                         break;
2431                     }
2432                 }
2433             while ((np = np->p_friends) != pp);
2434         }
2435     }
2436     untty();
2437 #ifdef NLS_CATALOGS
2438     /*
2439      * We need to call catclose, because SVR4 leaves symlinks behind otherwise
2440      * in the catalog directories. We cannot close on a vforked() child,
2441      * because messages will stop working on the parent too.
2442      */
2443     if (child == 0)
2444         nlsclose();
2445 #endif /* NLS_CATALOGS */
2446 #ifdef WINNT_NATIVE
2447     nt_cleanup();
2448 #endif /* WINNT_NATIVE */
2449     _exit(i);
2450 }
2451
2452 #ifndef _PATH_DEFPATH
2453 static Char **
2454 defaultpath(void)
2455 {
2456     char   *ptr;
2457     Char  **blk, **blkp;
2458     struct stat stb;
2459
2460     blkp = blk = xmalloc(sizeof(Char *) * 10);
2461
2462 #ifndef NODOT
2463 # ifndef DOTLAST
2464     *blkp++ = Strsave(STRdot);
2465 # endif
2466 #endif
2467
2468 #define DIRAPPEND(a)  \
2469         if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \
2470                 *blkp++ = SAVE(ptr)
2471
2472 #ifdef _PATH_LOCAL
2473     DIRAPPEND(_PATH_LOCAL);
2474 #endif
2475
2476 #ifdef _PATH_USRUCB
2477     DIRAPPEND(_PATH_USRUCB);
2478 #endif
2479
2480 #ifdef _PATH_USRBSD
2481     DIRAPPEND(_PATH_USRBSD);
2482 #endif
2483
2484 #ifdef _PATH_BIN
2485     DIRAPPEND(_PATH_BIN);
2486 #endif
2487
2488 #ifdef _PATH_USRBIN
2489     DIRAPPEND(_PATH_USRBIN);
2490 #endif
2491
2492 #undef DIRAPPEND
2493
2494 #ifndef NODOT
2495 # ifdef DOTLAST
2496     *blkp++ = Strsave(STRdot);
2497 # endif
2498 #endif
2499     *blkp = NULL;
2500     return (blk);
2501 }
2502 #endif
2503
2504 static void
2505 record(void)
2506 {
2507     if (!fast) {
2508         recdirs(NULL, adrof(STRsavedirs) != NULL);
2509         rechist(NULL, adrof(STRsavehist) != NULL);
2510     }
2511     displayHistStats("Exiting");        /* no-op unless DEBUG_HIST */
2512 }
2513
2514 /*
2515  * Grab the tty repeatedly, and give up if we are not in the correct
2516  * tty process group.
2517  */
2518 int
2519 grabpgrp(int fd, pid_t desired)
2520 {
2521     struct sigaction old;
2522     pid_t pgrp;
2523     size_t i;
2524
2525     for (i = 0; i < 100; i++) {
2526         if ((pgrp = tcgetpgrp(fd)) == -1)
2527             return -1;
2528         if (pgrp == desired)
2529             return 0;
2530         (void)sigaction(SIGTTIN, NULL, &old);
2531         (void)signal(SIGTTIN, SIG_DFL);
2532         (void)kill(0, SIGTTIN);
2533         (void)sigaction(SIGTTIN, &old, NULL);
2534     }
2535     errno = EPERM;
2536     return -1;
2537 }