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