source: trunk/third/tcsh/sh.c @ 9031

Revision 9031, 49.5 KB checked in by ghudson, 28 years ago (diff)
Backwards compatibility with old Athena tcsh: * Set the shell variable host to the hostname. * Set the shell variable hosttype to the host type. * Set the shell variable autolist to the empty string. * Always source .cshrc in addition to .tcshrc.
Line 
1/* $Header: /afs/dev.mit.edu/source/repository/third/tcsh/sh.c,v 1.2 1996-10-03 04:40:33 ghudson 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. All advertising materials mentioning features or use of this software
18 *    must display the following acknowledgement:
19 *      This product includes software developed by the University of
20 *      California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 *    may be used to endorse or promote products derived from this software
23 *    without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37#define EXTERN  /* Intern */
38#include "sh.h"
39
40#ifndef lint
41char    copyright[] =
42"@(#) Copyright (c) 1991 The Regents of the University of California.\n\
43 All rights reserved.\n";
44#endif                          /* not lint */
45
46RCSID("$Id: sh.c,v 1.2 1996-10-03 04:40:33 ghudson Exp $")
47
48#include "tc.h"
49#include "ed.h"
50
51extern bool MapsAreInited;
52extern bool NLSMapsAreInited;
53extern bool NoNLSRebind;
54
55/*
56 * C Shell
57 *
58 * Bill Joy, UC Berkeley, California, USA
59 * October 1978, May 1980
60 *
61 * Jim Kulp, IIASA, Laxenburg, Austria
62 * April 1980
63 *
64 * Filename recognition added:
65 * Ken Greer, Ind. Consultant, Palo Alto CA
66 * October 1983.
67 *
68 * Karl Kleinpaste, Computer Consoles, Inc.
69 * Added precmd, periodic/tperiod, prompt changes,
70 * directory stack hack, and login watch.
71 * Sometime March 1983 - Feb 1984.
72 *
73 * Added scheduled commands, including the "sched" command,
74 * plus the call to sched_run near the precmd et al
75 * routines.
76 * Upgraded scheduled events for running events while
77 * sitting idle at command input.
78 *
79 * Paul Placeway, Ohio State
80 * added stuff for running with twenex/inputl  9 Oct 1984.
81 *
82 * ported to Apple Unix (TM) (OREO)  26 -- 29 Jun 1987
83 */
84
85jmp_buf_t reslab;
86
87#ifdef TESLA
88int do_logout;
89#endif                          /* TESLA */
90
91
92#ifdef convex
93bool    use_fork = 0;           /* use fork() instead of vfork()? */
94#endif /* convex */
95
96static int     nofile = 0;
97static bool    reenter = 0;
98static bool    nverbose = 0;
99static bool    nexececho = 0;
100static bool    quitit = 0;
101static bool    rdirs = 0;
102bool    fast = 0;
103static bool    batch = 0;
104static bool    mflag = 0;
105static bool    prompt = 1;
106static bool    enterhist = 0;
107bool    tellwhat = 0;
108time_t  t_period;
109Char  *ffile = NULL;
110static time_t  chktim;          /* Time mail last checked */
111
112extern char **environ;
113
114static  int               srccat        __P((Char *, Char *));
115static  int               srcfile       __P((char *, bool, bool, Char **));
116static  sigret_t          phup          __P((int));
117static  void              srcunit       __P((int, bool, bool, Char **));
118static  void              mailchk       __P((void));
119static  Char            **defaultpath   __P((void));
120
121int
122main(argc, argv)
123    int     argc;
124    char  **argv;
125{
126    register Char *cp;
127#ifdef AUTOLOGOUT
128    register Char *cp2;
129#endif
130    register char *tcp, *ttyn;
131    register int f;
132    register char **tempv;
133
134#ifdef BSDSIGS
135    sigvec_t osv;
136#endif /* BSDSIGS */
137
138#ifdef MALLOC_TRACE
139     mal_setstatsfile(fdopen(dup2(open("/tmp/tcsh.trace",
140                                       O_WRONLY|O_CREAT, 0666), 25), "w"));
141     mal_trace(1);
142#endif /* MALLOC_TRACE */
143
144#if !(defined(BSDTIMES) || defined(_SEQUENT_)) && defined(POSIX)
145# ifdef _SC_CLK_TCK
146    clk_tck = (clock_t) sysconf(_SC_CLK_TCK);
147# else /* ! _SC_CLK_TCK */
148#  ifdef CLK_TCK
149    clk_tck = CLK_TCK;
150#  else /* !CLK_TCK */
151    clk_tck = HZ;
152#  endif /* CLK_TCK */
153# endif /* _SC_CLK_TCK */
154#endif /* !BSDTIMES && POSIX */
155
156    settimes();                 /* Immed. estab. timing base */
157#ifdef TESLA
158    do_logout = 0;
159#endif /* TESLA */
160
161    /*
162     * Make sure we have 0, 1, 2 open
163     * Otherwise `` jobs will not work... (From knaff@poly.polytechnique.fr)
164     */
165    {
166        do
167            if ((f = open(_PATH_DEVNULL, O_RDONLY)) == -1 &&
168                (f = open("/", O_RDONLY)) == -1)
169                exit(1);
170        while (f < 3);
171        (void) close(f);
172    }
173
174    osinit();                   /* Os dependent initialization */
175
176    /*
177     * Initialize non constant strings
178     */
179#ifdef _PATH_BSHELL
180    STR_BSHELL = SAVE(_PATH_BSHELL);
181#endif
182#ifdef _PATH_TCSHELL
183    STR_SHELLPATH = SAVE(_PATH_TCSHELL);
184#else
185# ifdef _PATH_CSHELL
186    STR_SHELLPATH = SAVE(_PATH_CSHELL);
187# endif
188#endif
189    STR_environ = blk2short(environ);
190    environ = short2blk(STR_environ);   /* So that we can free it */
191    STR_WORD_CHARS = SAVE(WORD_CHARS);
192
193    HIST = '!';
194    HISTSUB = '^';
195    word_chars = STR_WORD_CHARS;
196    bslash_quote = 0;           /* PWP: do tcsh-style backslash quoting? */
197
198    set(STRhistory, SAVE("100"));       /* Default history size to 100 */
199
200    tempv = argv;
201    ffile = SAVE(tempv[0]);
202    if (eq(ffile, STRaout))     /* A.out's are quittable */
203        quitit = 1;
204    uid = getuid();
205    gid = getgid();
206    euid = geteuid();
207    egid = getegid();
208#ifdef OREO
209    /*
210     * We are a login shell if: 1. we were invoked as -<something> with
211     * optional arguments 2. or we were invoked only with the -l flag
212     */
213    loginsh = (**tempv == '-') || (argc == 2 &&
214                                   tempv[1][0] == '-' && tempv[1][1] == 'l' &&
215                                                tempv[1][2] == '\0');
216#else
217    /*
218     * We are a login shell if: 1. we were invoked as -<something> and we had
219     * no arguments 2. or we were invoked only with the -l flag
220     */
221    loginsh = (**tempv == '-' && argc == 1) || (argc == 2 &&
222                                   tempv[1][0] == '-' && tempv[1][1] == 'l' &&
223                                                tempv[1][2] == '\0');
224#endif
225
226#ifdef _VMS_POSIX
227    /* No better way to find if we are a login shell */
228    if (!loginsh)
229        loginsh = (argc == 1 && getppid() == 1);
230#endif /* _VMS_POSIX */
231
232    if (loginsh && **tempv != '-') {
233        /*
234         * Mangle the argv space
235         */
236        tempv[1][0] = '\0';
237        tempv[1][1] = '\0';
238        tempv[1] = NULL;
239        for (tcp = *tempv; *tcp++;)
240             continue;
241        for (tcp--; tcp >= *tempv; tcp--)
242            tcp[1] = tcp[0];
243        *++tcp = '-';
244        argc--;
245    }
246    if (loginsh) {
247        (void) time(&chktim);
248        set(STRloginsh, Strsave(STRNULL));
249    }
250
251    AsciiOnly = 1;
252    NoNLSRebind = getenv("NOREBIND") != NULL;
253#ifdef NLS
254# ifdef SETLOCALEBUG
255    dont_free = 1;
256# endif /* SETLOCALEBUG */
257    (void) setlocale(LC_ALL, "");
258# ifdef LC_COLLATE
259    (void) setlocale(LC_COLLATE, "");
260# endif
261# ifdef SETLOCALEBUG
262    dont_free = 0;
263# endif /* SETLOCALEBUG */
264# ifdef STRCOLLBUG
265    fix_strcoll_bug();
266# endif /* STRCOLLBUG */
267
268    {
269        int     k;
270
271        for (k = 0200; k <= 0377 && !Isprint(k); k++)
272            continue;
273        AsciiOnly = k > 0377;
274    }
275#else
276    AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL;
277#endif                          /* NLS */
278    if (MapsAreInited && !NLSMapsAreInited)
279        ed_InitNLSMaps();
280    ResetArrowKeys();
281
282    /*
283     * Initialize for periodic command intervals. Also, initialize the dummy
284     * tty list for login-watch.
285     */
286    (void) time(&t_period);
287#ifndef HAVENOUTMP
288    initwatch();
289#endif /* !HAVENOUTMP */
290
291#if defined(alliant)
292    /*
293     * From:  Jim Pace <jdp@research.att.com>
294     * tcsh does not work properly on the alliants through an rlogin session.
295     * The shell generally hangs.  Also, reference to the controlling terminal
296     * does not work ( ie: echo foo > /dev/tty ).
297     *
298     * A security feature was added to rlogind affecting FX/80's Concentrix
299     * from revision 5.5.xx upwards (through 5.7 where this fix was implemented)
300     * This security change also affects the FX/2800 series.
301     * The security change to rlogind requires the process group of an rlogin
302     * session become disassociated with the tty in rlogind.
303     *
304     * The changes needed are:
305     * 1. set the process group
306     * 2. reenable the control terminal
307     */
308     if (loginsh && isatty(SHIN)) {
309         ttyn = (char *) ttyname(SHIN);
310         (void) close(SHIN);
311         SHIN = open(ttyn, O_RDWR);
312         shpgrp = getpid();
313         (void) ioctl (SHIN, TIOCSPGRP, (ptr_t) &shpgrp);
314         (void) setpgid(0, shpgrp);
315     }
316#endif /* alliant */
317
318    /*
319     * Move the descriptors to safe places. The variable didfds is 0 while we
320     * have only FSH* to work with. When didfds is true, we have 0,1,2 and
321     * prefer to use these.
322     */
323    initdesc();
324
325    /*
326     * Get and set the tty now
327     */
328    if ((ttyn = ttyname(SHIN)) != NULL) {
329        /*
330         * Could use rindex to get rid of other possible path components, but
331         * hpux preserves the subdirectory /pty/ when storing the tty name in
332         * utmp, so we keep it too.
333         */
334        if (strncmp(ttyn, "/dev/", 5) == 0)
335            set(STRtty, cp = SAVE(ttyn + 5));
336        else
337            set(STRtty, cp = SAVE(ttyn));
338    }
339    else
340        set(STRtty, cp = SAVE(""));
341    /*
342     * Initialize the shell variables. ARGV and PROMPT are initialized later.
343     * STATUS is also munged in several places. CHILD is munged when
344     * forking/waiting
345     */
346
347    /*
348     * 7-10-87 Paul Placeway autologout should be set ONLY on login shells and
349     * on shells running as root.  Out of these, autologout should NOT be set
350     * for any psudo-terminals (this catches most window systems) and not for
351     * any terminal running X windows.
352     *
353     * At Ohio State, we have had problems with a user having his X session
354     * drop out from under him (on a Sun) because the shell in his master
355     * xterm timed out and exited.
356     *
357     * Really, this should be done with a program external to the shell, that
358     * watches for no activity (and NO running programs, such as dump) on a
359     * terminal for a long peroid of time, and then SIGHUPS the shell on that
360     * terminal.
361     *
362     * bugfix by Rich Salz <rsalz@PINEAPPLE.BBN.COM>: For root rsh things
363     * allways first check to see if loginsh or really root, then do things
364     * with ttyname()
365     *
366     * Also by Jean-Francois Lamy <lamy%ai.toronto.edu@RELAY.CS.NET>: check the
367     * value of cp before using it! ("root can rsh too")
368     *
369     * PWP: keep the nested ifs; the order of the tests matters and a good
370     * (smart) C compiler might re-arange things wrong.
371     */
372#ifdef AUTOLOGOUT
373    if (loginsh || (uid == 0)) {
374        if (*cp) {
375            /* only for login shells or root and we must have a tty */
376            if ((cp2 = Strrchr(cp, (Char) '/')) != NULL) {
377                cp = cp2 + 1;
378            }
379            if (!((Strncmp(cp, STRtty, 3) == 0) &&
380                  (cp[3] >= 'p' && cp[3] <= 'u'))) {
381                if (getenv("DISPLAY") == NULL) {
382                    /* NOT on X window shells */
383                    set(STRautologout, Strsave(STRdefautologout));
384                }
385            }
386        }
387    }
388#endif /* AUTOLOGOUT */
389
390    (void) sigset(SIGALRM, alrmcatch);
391
392    set(STRstatus, Strsave(STR0));
393    fix_version();              /* publish the shell version */
394
395    /*
396     * Publish the selected echo style
397     */
398#if ECHO_STYLE == NONE_ECHO
399    set(STRecho_style, Strsave(STRnone));
400#endif /* ECHO_STYLE == NONE_ECHO */
401#if ECHO_STYLE == BSD_ECHO
402    set(STRecho_style, Strsave(STRbsd));
403#endif /* ECHO_STYLE == BSD_ECHO */
404#if ECHO_STYLE == SYSV_ECHO
405    set(STRecho_style, Strsave(STRsysv));
406#endif /* ECHO_STYLE == SYSV_ECHO */
407#if ECHO_STYLE == BOTH_ECHO
408    set(STRecho_style, Strsave(STRboth));
409#endif /* ECHO_STYLE == BOTH_ECHO */
410
411    /*
412     * increment the shell level.
413     */
414    shlvl(1);
415
416    if ((tcp = getenv("HOME")) != NULL)
417        cp = quote(SAVE(tcp));
418    else
419        cp = NULL;
420    if (cp == NULL)
421        fast = 1;               /* No home -> can't read scripts */
422    else
423        set(STRhome, cp);
424    dinit(cp);                  /* dinit thinks that HOME == cwd in a login
425                                 * shell */
426    /*
427     * Grab other useful things from the environment. Should we grab
428     * everything??
429     */
430    {
431        char *cln, *cus;
432        Char    buff[BUFSIZE];
433        struct passwd *pw;
434
435
436#ifdef apollo
437        int     oid = getoid();
438
439        Itoa(oid, buff);
440        set(STRoid, Strsave(buff));
441#endif /* apollo */
442
443        Itoa(uid, buff);
444        set(STRuid, Strsave(buff));
445
446        Itoa(gid, buff);
447        set(STRgid, Strsave(buff));
448
449        cln = getenv("LOGNAME");
450        cus = getenv("USER");
451        if (cus != NULL)
452            set(STRuser, quote(SAVE(cus)));
453        else if (cln != NULL)
454            set(STRuser, quote(SAVE(cln)));
455        else if ((pw = getpwuid(uid)) == NULL)
456            set(STRuser, SAVE("unknown"));
457        else
458            set(STRuser, SAVE(pw->pw_name));
459        if (cln == NULL)
460            tsetenv(STRLOGNAME, value(STRuser));
461        if (cus == NULL)
462            tsetenv(STRKUSER, value(STRuser));
463    }
464
465    /*
466     * HOST may be wrong, since rexd transports the entire environment on sun
467     * 3.x Just set it again
468     */
469    {
470        char    cbuff[MAXHOSTNAMELEN];
471
472        if (gethostname(cbuff, sizeof(cbuff)) >= 0) {
473            cbuff[sizeof(cbuff) - 1] = '\0';    /* just in case */
474            tsetenv(STRHOST, str2short(cbuff));
475            set(STRhost, SAVE(cbuff));
476        }
477        else {
478            tsetenv(STRHOST, str2short("unknown"));
479            set(STRhost, SAVE("unknown"));
480        }
481    }
482
483    /*
484     * HOSTTYPE, too. Just set it again.
485     */
486    tsetenv(STRHOSTTYPE, str2short(gethosttype()));
487    set(STRhosttype, SAVE(gethosttype()));
488    set(STRautolist, Strsave(STRNULL));
489 
490#ifdef apollo
491    if ((tcp = getenv("SYSTYPE")) == NULL)
492        tcp = "bsd4.3";
493    Setenv(STRSYSTYPE, quote(str2short(tcp)));
494#endif /* apollo */
495
496    /*
497     * set editing on by default, unless running under Emacs as an inferior
498     * shell.
499     * We try to do this intelligently. If $TERM is available, then it
500     * should determine if we should edit or not. $TERM is preserved
501     * across rlogin sessions, so we will not get confused if we rlogin
502     * under an emacs shell. Another advantage is that if we run an
503     * xterm under an emacs shell, then the $TERM will be set to
504     * xterm, so we are going to want to edit. Unfortunately emacs
505     * does not restore all the tty modes, so xterm is not very well
506     * set up. But this is not the shell's fault.
507     */
508    if ((tcp = getenv("TERM")) != NULL) {
509        set(STRterm, quote(SAVE(tcp)));
510        editing = (strcmp(tcp, "emacs") != 0);
511    }
512    else
513        editing = ((tcp = getenv("EMACS")) == NULL || strcmp(tcp, "t") != 0);
514
515    /*
516     * The 'edit' variable is either set or unset.  It doesn't
517     * need a value.  Making it 'emacs' might be confusing.
518     */
519    if (editing)
520        set(STRedit, Strsave(STRNULL));
521
522
523    /*
524     * still more mutability: make the complete routine automatically add the
525     * suffix of file names...
526     */
527    set(STRaddsuffix, Strsave(STRNULL));
528
529    /*
530     * Re-initialize path if set in environment
531     */
532    if ((tcp = getenv("PATH")) == NULL)
533        set1(STRpath, defaultpath(), &shvhed);
534    else
535        /* Importpath() allocates memory for the path, and the
536         * returned pointer from SAVE() was discarded, so
537         * this was a memory leak.. (sg)
538         *
539         * importpath(SAVE(tcp));
540         */
541        importpath(str2short(tcp));
542
543
544    {
545        /* If the SHELL environment variable ends with "tcsh", set
546         * STRshell to the same path.  This is to facilitate using
547         * the executable in environments where the compiled-in
548         * default isn't appropriate (sg).
549         */
550
551        int sh_len;
552
553        if ((tcp = getenv("SHELL")) != (char *)0 &&
554            (sh_len = strlen(tcp)) >= 5 &&
555            strcmp(tcp + (sh_len - 5), "/tcsh") == 0)
556        {
557            set(STRshell, quote(SAVE(tcp)));
558        }
559        else
560            set(STRshell, Strsave(STR_SHELLPATH));
561    }
562
563    doldol = putn((int) getpid());      /* For $$ */
564    shtemp = Strspl(STRtmpsh, doldol);  /* For << */
565
566    /*
567     * Record the interrupt states from the parent process. If the parent is
568     * non-interruptible our hand must be forced or we (and our children) won't
569     * be either. Our children inherit termination from our parent. We catch it
570     * only if we are the login shell.
571     */
572#ifdef BSDSIGS
573    /* parents interruptibility */
574    (void) mysigvec(SIGINT, NULL, &osv);
575    parintr = (sigret_t(*) ()) osv.sv_handler;
576    (void) mysigvec(SIGTERM, NULL, &osv);
577    parterm = (sigret_t(*) ()) osv.sv_handler;
578#else                           /* BSDSIGS */
579    parintr = signal(SIGINT, SIG_IGN);  /* parents interruptibility */
580    (void) sigset(SIGINT, parintr);     /* ... restore */
581
582# ifdef COHERENT
583    if (loginsh) /* it seems that SIGTERM is always set to SIG_IGN by */
584                 /* init/getty so it should be set to SIG_DFL - there may be */
585                 /* a better fix for this. */
586         parterm = SIG_DFL;
587    else
588# else /* !COHERENT */
589    parterm = signal(SIGTERM, SIG_IGN); /* parents terminability */
590# endif /* COHERENT */
591    (void) sigset(SIGTERM, parterm);    /* ... restore */
592
593#endif /* BSDSIGS */
594
595    /* No reason I can see not to save history on all these events..
596     * Most usual occurrence is in a window system, where we're not a login
597     * shell, but might as well be... (sg)
598     * But there might be races when lots of shells exit together...
599     * [this is also incompatible].
600     */
601    (void) signal(SIGHUP, phup);        /* exit processing on HUP */
602#ifdef SIGXCPU
603    (void) signal(SIGXCPU, phup);       /* ...and on XCPU */
604#endif
605#ifdef SIGXFSZ
606    (void) signal(SIGXFSZ, phup);       /* ...and on XFSZ */
607#endif
608
609#ifdef TCF
610    /* Enable process migration on ourselves and our progeny */
611    (void) signal(SIGMIGRATE, SIG_DFL);
612#endif /* TCF */
613
614    /*
615     * Process the arguments.
616     *
617     * Note that processing of -v/-x is actually delayed till after script
618     * processing.
619     *
620     * We set the first character of our name to be '-' if we are a shell
621     * running interruptible commands.  Many programs which examine ps'es
622     * use this to filter such shells out.
623     */
624    argc--, tempv++;
625    while (argc > 0 && (tcp = tempv[0])[0] == '-' &&
626           *++tcp != '\0' && !batch) {
627        do
628            switch (*tcp++) {
629
630            case 0:             /* -    Interruptible, no prompt */
631                prompt = 0;
632                setintr = 1;
633                nofile = 1;
634                break;
635
636            case 'b':           /* -b   Next arg is input file */
637                batch = 1;
638                break;
639
640            case 'c':           /* -c   Command input from arg */
641                if (argc == 1)
642                    xexit(0);
643                argc--, tempv++;
644#ifdef M_XENIX
645                /* Xenix Vi bug:
646                   it relies on a 7 bit environment (/bin/sh), so it
647                   pass ascii arguments with the 8th bit set */
648                if (!strcmp(argv[0], "sh"))
649                  {
650                    char *p;
651
652                    for (p = tempv[0]; *p; ++p)
653                      *p &= ASCII;
654                  }
655#endif
656                arginp = SAVE(tempv[0]);
657                /*
658                 * * Give an error on -c arguments that end in * backslash to
659                 * ensure that you don't make * nonportable csh scripts.
660                 */
661                {
662                    register Char *cp;
663                    register int count;
664
665                    cp = arginp + Strlen(arginp);
666                    count = 0;
667                    while (cp > arginp && *--cp == '\\')
668                        ++count;
669                    if ((count & 1) != 0) {
670                        exiterr = 1;
671                        stderror(ERR_ARGC);
672                    }
673                }
674                prompt = 0;
675                nofile = 1;
676                break;
677            case 'd':           /* -d   Load directory stack from file */
678                rdirs = 1;
679                break;
680
681#ifdef apollo
682            case 'D':           /* -D   Define environment variable */
683                {
684                    register Char *cp, *dp;
685
686                    cp = str2short(tcp);
687                    if (dp = Strchr(cp, '=')) {
688                        *dp++ = '\0';
689                        Setenv(cp, dp);
690                    }
691                    else
692                        Setenv(cp, STRNULL);
693                }
694                *tcp = '\0';    /* done with this argument */
695                break;
696#endif /* apollo */
697
698            case 'e':           /* -e   Exit on any error */
699                exiterr = 1;
700                break;
701
702            case 'f':           /* -f   Fast start */
703                fast = 1;
704                break;
705
706            case 'i':           /* -i   Interactive, even if !intty */
707                intact = 1;
708                nofile = 1;
709                break;
710
711            case 'm':           /* -m   read .cshrc (from su) */
712                mflag = 1;
713                break;
714
715            case 'n':           /* -n   Don't execute */
716                noexec = 1;
717                break;
718
719            case 'q':           /* -q   (Undoc'd) ... die on quit */
720                quitit = 1;
721                break;
722
723            case 's':           /* -s   Read from std input */
724                nofile = 1;
725                break;
726
727            case 't':           /* -t   Read one line from input */
728                onelflg = 2;
729                prompt = 0;
730                nofile = 1;
731                break;
732
733            case 'v':           /* -v   Echo hist expanded input */
734                nverbose = 1;   /* ... later */
735                break;
736
737            case 'x':           /* -x   Echo just before execution */
738                nexececho = 1;  /* ... later */
739                break;
740
741            case 'V':           /* -V   Echo hist expanded input */
742                setNS(STRverbose);      /* NOW! */
743                break;
744
745            case 'X':           /* -X   Echo just before execution */
746                setNS(STRecho); /* NOW! */
747                break;
748
749#ifdef convex
750            case 'F':           /* Undocumented flag */
751                /*
752                 * This will cause children to be created using fork instead of
753                 * vfork.
754                 */
755                use_fork = 1;
756                break;
757#endif /* convex */
758            default:            /* Unknown command option */
759                exiterr = 1;
760                stderror(ERR_TCSHUSAGE, tcp-1);
761                break;
762
763        } while (*tcp);
764        tempv++, argc--;
765    }
766
767    if (quitit)                 /* With all due haste, for debugging */
768        (void) signal(SIGQUIT, SIG_DFL);
769
770    /*
771     * Unless prevented by -, -c, -i, -s, or -t, if there are remaining
772     * arguments the first of them is the name of a shell file from which to
773     * read commands.
774     */
775    if (nofile == 0 && argc > 0) {
776        nofile = open(tempv[0], O_RDONLY);
777        if (nofile < 0) {
778            child = 1;          /* So this ... */
779            /* ... doesn't return */
780            stderror(ERR_SYSTEM, tempv[0], strerror(errno));
781        }
782        if (ffile != NULL)
783            xfree((ptr_t) ffile);
784        ffile = SAVE(tempv[0]);
785        /*
786         * Replace FSHIN. Handle /dev/std{in,out,err} specially
787         * since once they are closed we cannot open them again.
788         * In that case we use our own saved descriptors
789         */
790        if ((SHIN = dmove(nofile, FSHIN)) < 0)
791            switch(nofile) {
792            case 0:
793                SHIN = FSHIN;
794                break;
795            case 1:
796                SHIN = FSHOUT;
797                break;
798            case 2:
799                SHIN = FSHDIAG;
800                break;
801            default:
802                stderror(ERR_SYSTEM, tempv[0], strerror(errno));
803                break;
804            }
805        (void) close_on_exec(SHIN, 1);
806        prompt = 0;
807         /* argc not used any more */ tempv++;
808    }
809
810
811    /*
812     * Consider input a tty if it really is or we are interactive. but not for
813     * editing (christos)
814     */
815    if (!(intty = isatty(SHIN))) {
816        if (adrof(STRedit))
817            unsetv(STRedit);
818        editing = 0;
819    }
820    intty |= intact;
821    if (intty || (intact && isatty(SHOUT))) {
822        if (!batch && (uid != euid || gid != egid)) {
823            errno = EACCES;
824            child = 1;          /* So this ... */
825            /* ... doesn't return */
826            stderror(ERR_SYSTEM, "tcsh", strerror(errno));
827        }
828    }
829    isoutatty = isatty(SHOUT);
830    isdiagatty = isatty(SHDIAG);
831    /*
832     * Decide whether we should play with signals or not. If we are explicitly
833     * told (via -i, or -) or we are a login shell (arg0 starts with -) or the
834     * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
835     * Note that in only the login shell is it likely that parent may have set
836     * signals to be ignored
837     */
838    if (loginsh || intact || (intty && isatty(SHOUT)))
839        setintr = 1;
840    settell();
841    /*
842     * Save the remaining arguments in argv.
843     */
844    setq(STRargv, blk2short(tempv), &shvhed);
845
846    /*
847     * Set up the prompt.
848     */
849    if (prompt) {
850        set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymarrow));
851        /* that's a meta-questionmark */
852        set(STRprompt2, Strsave(STRmquestion));
853        set(STRprompt3, Strsave(STRKCORRECT));
854    }
855
856    /*
857     * If we are an interactive shell, then start fiddling with the signals;
858     * this is a tricky game.
859     */
860    shpgrp = mygetpgrp();
861    opgrp = tpgrp = -1;
862    if (setintr) {
863        **argv = '-';
864        if (!quitit)            /* Wary! */
865            (void) signal(SIGQUIT, SIG_IGN);
866        (void) sigset(SIGINT, pintr);
867        (void) sighold(SIGINT);
868        (void) signal(SIGTERM, SIG_IGN);
869        if (quitit == 0 && arginp == 0) {
870#ifdef SIGTSTP
871            (void) signal(SIGTSTP, SIG_IGN);
872#endif
873#ifdef SIGTTIN
874            (void) signal(SIGTTIN, SIG_IGN);
875#endif
876#ifdef SIGTTOU
877            (void) signal(SIGTTOU, SIG_IGN);
878#endif
879            /*
880             * Wait till in foreground, in case someone stupidly runs csh &
881             * dont want to try to grab away the tty.
882             */
883            if (isatty(FSHDIAG))
884                f = FSHDIAG;
885            else if (isatty(FSHOUT))
886                f = FSHOUT;
887            else if (isatty(OLDSTD))
888                f = OLDSTD;
889            else
890                f = -1;
891
892#ifdef NeXT
893            /* NeXT 2.0 /usr/etc/rlogind, does not set our process group! */
894            if (shpgrp == 0) {
895                shpgrp = getpid();
896                (void) setpgid(0, shpgrp);
897                (void) tcsetpgrp(f, shpgrp);
898            }
899#endif /* NeXT */
900#ifdef BSDJOBS                  /* if we have tty job control */
901    retry:
902            if ((tpgrp = tcgetpgrp(f)) != -1) {
903                if (tpgrp != shpgrp) {
904                    sigret_t(*old) () = signal(SIGTTIN, SIG_DFL);
905                    (void) kill(0, SIGTTIN);
906                    (void) signal(SIGTTIN, old);
907                    goto retry;
908                }
909                /*
910                 * Thanks to Matt Day for the POSIX references, and to
911                 * Paul Close for the SGI clarification.
912                 */
913                if (setdisc(f) != -1) {
914                    opgrp = shpgrp;
915                    shpgrp = getpid();
916                    tpgrp = shpgrp;
917                    if (tcsetpgrp(f, shpgrp) == -1) {
918                        /*
919                         * On hpux 7.03 this fails with EPERM. This happens on
920                         * the 800 when opgrp != shpgrp at this point. (we were
921                         * forked from a non job control shell)
922                         * POSIX 7.2.4, says we failed because the process
923                         * group specified did not belong to a process
924                         * in the same session with the tty. So we set our
925                         * process group and try again.
926                         */
927                        if (setpgid(0, shpgrp) == -1) {
928                            xprintf("setpgid:");
929                            goto notty;
930                        }
931                        if (tcsetpgrp(f, shpgrp) == -1) {
932                            xprintf("tcsetpgrp:");
933                            goto notty;
934                        }
935                    }
936                    /*
937                     * We check the process group now. If it is the same, then
938                     * we don't need to set it again. On hpux 7.0 on the 300's
939                     * if we set it again it fails with EPERM. This is the
940                     * correct behavior according to POSIX 4.3.3 if the process
941                     * was a session leader .
942                     */
943                    else if (shpgrp != mygetpgrp()) {
944                        if(setpgid(0, shpgrp) == -1) {
945                            xprintf("setpgid:");
946                            goto notty;
947                        }
948                    }
949#ifdef IRIS4D
950                    /*
951                     * But on irix 3.3 we need to set it again, even if it is
952                     * the same. We do that to tell the system that we
953                     * need BSD process group compatibility.
954                     */
955                    else
956                        (void) setpgid(0, shpgrp);
957#endif
958                    (void) close_on_exec(dcopy(f, FSHTTY), 1);
959                }
960                else
961                    tpgrp = -1;
962            }
963            if (tpgrp == -1) {
964        notty:
965                xprintf("Warning: no access to tty (%s).\n", strerror(errno));
966                xprintf("Thus no job control in this shell.\n");
967                /*
968                 * Fix from:Sakari Jalovaara <sja@sirius.hut.fi> if we don't
969                 * have access to tty, disable editing too
970                 */
971                if (adrof(STRedit))
972                    unsetv(STRedit);
973                editing = 0;
974            }
975#else   /* BSDJOBS */           /* don't have job control, so frotz it */
976            tpgrp = -1;
977#endif                          /* BSDJOBS */
978        }
979    }
980    if ((setintr == 0) && (parintr == SIG_DFL))
981        setintr = 1;
982
983/*
984 * SVR4 doesn't send a SIGCHLD when a child is stopped or continued if the
985 * handler is installed with signal(2) or sigset(2).  sigaction(2) must
986 * be used instead.
987 *
988 * David Dawes (dawes@physics.su.oz.au) Sept 1991
989 */
990
991#if SYSVREL > 3
992    {
993        struct sigaction act;
994        act.sa_handler=pchild;
995        sigemptyset(&(act.sa_mask)); /* Don't block any extra sigs when the
996                                      * handler is called
997                                      */
998        act.sa_flags=0;            /* want behaviour of sigset() without
999                                    * SA_NOCLDSTOP
1000                                    */
1001        sigaction(SIGCHLD,&act,(struct sigaction *)NULL);
1002    }
1003#else /* SYSVREL <= 3 */
1004    (void) sigset(SIGCHLD, pchild);     /* while signals not ready */
1005#endif /* SYSVREL <= 3 */
1006
1007
1008    if (intty && !arginp)       
1009        (void) ed_Setup(editing);/* Get the tty state, and set defaults */
1010                                 /* Only alter the tty state if editing */
1011   
1012    /*
1013     * Set an exit here in case of an interrupt or error reading the shell
1014     * start-up scripts.
1015     */
1016    reenter = setexit();        /* PWP */
1017    haderr = 0;                 /* In case second time through */
1018    if (!fast && reenter == 0) {
1019        /* Will have value(STRhome) here because set fast if don't */
1020        {
1021            int     osetintr = setintr;
1022            sigret_t (*oparintr)() = parintr;
1023
1024#ifdef BSDSIGS
1025            sigmask_t omask = sigblock(sigmask(SIGINT));
1026#else
1027            sighold(SIGINT);
1028#endif
1029            setintr = 0;
1030            parintr = SIG_IGN;  /* onintr in /etc/ files has no effect */
1031#ifdef _PATH_DOTCSHRC
1032            (void) srcfile(_PATH_DOTCSHRC, 0, 0, NULL);
1033#endif
1034            if (!arginp && !onelflg && !havhash)
1035                dohash(NULL,NULL);
1036#ifdef _PATH_DOTLOGIN
1037            if (loginsh)
1038                (void) srcfile(_PATH_DOTLOGIN, 0, 0, NULL);
1039#endif
1040#ifdef BSDSIGS
1041            (void) sigsetmask(omask);
1042#else
1043            (void) sigrelse(SIGINT);
1044#endif
1045            setintr = osetintr;
1046            parintr = oparintr;
1047        }
1048#ifdef LOGINFIRST
1049        if (loginsh)
1050            (void) srccat(value(STRhome), STRsldotlogin);
1051#endif
1052        (void) srccat(value(STRhome), STRsldottcshrc);
1053        (void) srccat(value(STRhome), STRsldotcshrc);
1054
1055        if (!fast && !arginp && !onelflg && !havhash)
1056            dohash(NULL,NULL);
1057
1058        /*
1059         * Source history before .login so that it is available in .login
1060         */
1061        loadhist(NULL);
1062#ifndef LOGINFIRST
1063        if (loginsh)
1064            (void) srccat(value(STRhome), STRsldotlogin);
1065#endif
1066        if (!fast && (loginsh || rdirs))
1067            loaddirs(NULL);
1068    }
1069    /* Initing AFTER .cshrc is the Right Way */
1070    if (intty && !arginp) {     /* PWP setup stuff */
1071        ed_Init();              /* init the new line editor */
1072#ifdef SIG_WINDOW
1073        check_window_size(1);   /* mung environment */
1074#endif                          /* SIG_WINDOW */
1075    }
1076
1077    /*
1078     * Now are ready for the -v and -x flags
1079     */
1080    if (nverbose)
1081        setNS(STRverbose);
1082    if (nexececho)
1083        setNS(STRecho);
1084   
1085    /*
1086     * All the rest of the world is inside this call. The argument to process
1087     * indicates whether it should catch "error unwinds".  Thus if we are a
1088     * interactive shell our call here will never return by being blown past on
1089     * an error.
1090     */
1091    process(setintr);
1092
1093    /*
1094     * Mop-up.
1095     */
1096    if (intty) {
1097        if (loginsh) {
1098            xprintf("logout\n");
1099            (void) close(SHIN);
1100            child = 1;
1101#ifdef TESLA
1102            do_logout = 1;
1103#endif                          /* TESLA */
1104            goodbye(NULL, NULL);
1105        }
1106        else {
1107            xprintf("exit\n");
1108        }
1109    }
1110    recdirs(NULL);
1111    rechist(NULL);
1112    exitstat();
1113    return (0);
1114}
1115
1116void
1117untty()
1118{
1119#ifdef BSDJOBS
1120    if (tpgrp > 0) {
1121        (void) setpgid(0, opgrp);
1122        (void) tcsetpgrp(FSHTTY, opgrp);
1123        (void) resetdisc(FSHTTY);
1124    }
1125#endif /* BSDJOBS */
1126}
1127
1128void
1129importpath(cp)
1130    Char   *cp;
1131{
1132    register int i = 0;
1133    register Char *dp;
1134    register Char **pv;
1135    int     c;
1136
1137    for (dp = cp; *dp; dp++)
1138        if (*dp == PATHSEP)
1139            i++;
1140    /*
1141     * i+2 where i is the number of colons in the path. There are i+1
1142     * directories in the path plus we need room for a zero terminator.
1143     */
1144    pv = (Char **) xcalloc((size_t) (i + 2), sizeof(Char *));
1145    dp = cp;
1146    i = 0;
1147    if (*dp)
1148        for (;;) {
1149            if ((c = *dp) == PATHSEP || c == 0) {
1150                *dp = 0;
1151                pv[i++] = Strsave(*cp ? cp : STRdot);
1152                if (c) {
1153                    cp = dp + 1;
1154                    *dp = PATHSEP;
1155                }
1156                else
1157                    break;
1158            }
1159            dp++;
1160        }
1161    pv[i] = 0;
1162    set1(STRpath, pv, &shvhed);
1163}
1164
1165/*
1166 * Source to the file which is the catenation of the argument names.
1167 */
1168static int
1169srccat(cp, dp)
1170    Char   *cp, *dp;
1171{
1172    if (cp[0] == '/' && cp[1] == '\0')
1173        return srcfile(short2str(dp), (mflag ? 0 : 1), 0, NULL);
1174    else {
1175        register Char *ep = Strspl(cp, dp);
1176        char   *ptr = short2str(ep);
1177
1178        xfree((ptr_t) ep);
1179        return srcfile(ptr, (mflag ? 0 : 1), 0, NULL);
1180    }
1181}
1182
1183/*
1184 * Source to a file putting the file descriptor in a safe place (> 2).
1185 */
1186static int
1187srcfile(f, onlyown, flag, av)
1188    char   *f;
1189    bool    onlyown, flag;
1190    Char **av;
1191{
1192    register int unit;
1193
1194    if ((unit = open(f, O_RDONLY)) == -1)
1195        return 0;
1196    unit = dmove(unit, -1);
1197
1198    (void) close_on_exec(unit, 1);
1199    srcunit(unit, onlyown, flag, av);
1200    return 1;
1201}
1202
1203/*
1204 * Source to a unit.  If onlyown it must be our file or our group or
1205 * we don't chance it.  This occurs on ".cshrc"s and the like.
1206 */
1207int     insource;
1208static  Char **goargv = NULL;
1209static void
1210srcunit(unit, onlyown, hflg, av)
1211    register int unit;
1212    bool    onlyown, hflg;
1213    Char **av;
1214{
1215    /*
1216     * PWP: this is arranged like this so that an optimizing compiler won't go
1217     * and put things like oSHIN in a register that longjmp() restores.  The
1218     * problem is that on my Sun 3/50, gcc will put oSHIN in a register.  That
1219     * would be OK, but setjmp() saves ALL of the registers and longjmp()
1220     * restores ALL of them, thus if you do a setjmp(), set oSHIN to something
1221     * (like SHIN), then do a longjmp(), the value of oSHIN will magically
1222     * become -1 again.
1223     *
1224     * Perhaps setjmp() should only save the stack pointer, frame pointer, and
1225     * program counter...
1226     */
1227
1228    /* We have to push down a lot of state here */
1229    /* All this could go into a structure */
1230    int     oSHIN = -1, oldintty = intty, oinsource = insource;
1231    struct whyle *oldwhyl = whyles;
1232    Char   *ogointr = gointr, *oarginp = arginp;
1233    Char   *oevalp = evalp, **oevalvec = evalvec;
1234    int     oonelflg = onelflg;
1235    bool    oenterhist = enterhist;
1236    char    OHIST = HIST;
1237    bool    otell = cantell;
1238    Char **oargv;
1239    struct Bin saveB;
1240#ifdef BSDSIGS
1241    volatile sigmask_t omask = (sigmask_t) 0;
1242#endif
1243    jmp_buf_t oldexit;
1244
1245    /* The (few) real local variables */
1246    int     my_reenter;
1247
1248
1249    if (unit < 0)
1250        return;
1251    if (didfds)
1252        donefds();
1253    if (onlyown) {
1254        struct stat stb;
1255
1256        if (fstat(unit, &stb) < 0) {
1257            (void) close(unit);
1258            return;
1259        }
1260    }
1261
1262    /*
1263     * There is a critical section here while we are pushing down the input
1264     * stream since we have stuff in different structures. If we weren't
1265     * careful an interrupt could corrupt SHIN's Bin structure and kill the
1266     * shell.
1267     *
1268     * We could avoid the critical region by grouping all the stuff in a single
1269     * structure and pointing at it to move it all at once.  This is less
1270     * efficient globally on many variable references however.
1271     */
1272    insource = 1;
1273    getexit(oldexit);
1274
1275    if (setintr)
1276#ifdef BSDSIGS
1277        omask = sigblock(sigmask(SIGINT));
1278#else
1279        (void) sighold(SIGINT);
1280#endif
1281    /*
1282     * Bugfix for running out of memory by: Jak Kirman
1283     * <jak%cs.brown.edu@RELAY.CS.NET>.  Solution: pay attention to what
1284     * setexit() is returning because my_reenter _may_ be in a register, and
1285     * thus restored to 0 on a longjump(). (PWP: insert flames about
1286     * compiler-dependant code here) PWP: THANKS LOTS !!!
1287     */
1288    /* Setup the new values of the state stuff saved above */
1289
1290#ifdef NO_STRUCT_ASSIGNMENT
1291    (void) memmove((ptr_t) &(saveB), (ptr_t) &B, sizeof(B));
1292#else
1293    saveB = B;
1294#endif
1295    fbuf = NULL;
1296    fseekp = feobp = fblocks = 0;
1297    oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
1298    intty = isatty(SHIN), whyles = 0, gointr = 0;
1299    evalvec = 0;
1300    evalp = 0;
1301    enterhist = hflg;
1302    if (enterhist)
1303        HIST = '\0';
1304    /*
1305     * we can now pass arguments to source.
1306     * For compatibility we do that only if arguments were really
1307     * passed, otherwise we keep the old, global $argv like before.
1308     */
1309
1310    oargv = goargv;
1311    goargv = NULL;
1312    if (av != NULL && *av != NULL && **av != '\0') {
1313        struct varent *vp;
1314        if ((vp = adrof(STRargv)) != NULL)
1315            goargv = saveblk(vp->vec);
1316        setq(STRargv, saveblk(av), &shvhed);
1317    }
1318
1319    /*
1320     * Now if we are allowing commands to be interrupted, we let ourselves be
1321     * interrupted.
1322     */
1323    if (setintr)
1324#ifdef BSDSIGS
1325        (void) sigsetmask(omask);
1326#else
1327        (void) sigrelse(SIGINT);
1328#endif
1329    settell();
1330
1331/* PWP: think of this as like a LISP (unwind-protect ...) */
1332/* thanks to Diana Smetters for pointing out how this _should_ be written */
1333#ifdef cray
1334    my_reenter = 1;             /* assume non-zero return val */
1335    if (setexit() == 0) {
1336        my_reenter = 0;         /* Oh well, we were wrong */
1337#else
1338    if ((my_reenter = setexit()) == 0) {
1339#endif
1340        process(0);             /* 0 -> blow away on errors */
1341    }
1342
1343    if (setintr)
1344#ifdef BSDSIGS
1345        (void) sigsetmask(omask);
1346#else
1347        (void) sigrelse(SIGINT);
1348#endif
1349    if (oSHIN >= 0) {
1350        register int i;
1351
1352        register Char** nfbuf = fbuf;
1353        register int nfblocks = fblocks;
1354        fblocks = 0;
1355        fbuf = NULL;
1356        for (i = 0; i < nfblocks; i++)
1357            xfree((ptr_t) nfbuf[i]);
1358        xfree((ptr_t) nfbuf);
1359
1360        /* Reset input arena */
1361#ifdef NO_STRUCT_ASSIGNMENT
1362        (void) memmove((ptr_t) &B, (ptr_t) &(saveB), sizeof(B));
1363#else
1364        B = saveB;
1365#endif
1366        (void) close(SHIN), SHIN = oSHIN;
1367        arginp = oarginp, onelflg = oonelflg;
1368        evalp = oevalp, evalvec = oevalvec;
1369        intty = oldintty, whyles = oldwhyl, gointr = ogointr;
1370        if (enterhist)
1371            HIST = OHIST;
1372        if (av != NULL && *av != NULL && **av != '\0') {
1373            if (goargv)
1374                setq(STRargv, goargv, &shvhed);
1375            else
1376                unsetv(STRargv);
1377        }
1378        enterhist = oenterhist;
1379        cantell = otell;
1380    }
1381
1382    resexit(oldexit);
1383    goargv = oargv;
1384    /*
1385     * If process reset() (effectively an unwind) then we must also unwind.
1386     */
1387    if (my_reenter)
1388        stderror(ERR_SILENT);
1389    insource = oinsource;
1390}
1391
1392
1393/*ARGSUSED*/
1394void
1395goodbye(v, c)
1396    Char **v;
1397    struct command *c;
1398{
1399    rechist(NULL);
1400    recdirs(NULL);
1401
1402    if (loginsh) {
1403        (void) signal(SIGQUIT, SIG_IGN);
1404        (void) sigset(SIGINT, SIG_IGN);
1405        (void) signal(SIGTERM, SIG_IGN);
1406        setintr = 0;            /* No interrupts after "logout" */
1407        if (!(adrof(STRlogout)))
1408            set(STRlogout, STRnormal);
1409#ifdef _PATH_DOTLOGOUT
1410        (void) srcfile(_PATH_DOTLOGOUT, 0, 0, NULL);
1411#endif
1412        if (adrof(STRhome))
1413            (void) srccat(value(STRhome), STRsldtlogout);
1414#ifdef TESLA
1415        do_logout = 1;
1416#endif /* TESLA */
1417    }
1418    exitstat();
1419}
1420
1421void
1422exitstat()
1423{
1424    register Char *cp;
1425    register int i;
1426#ifdef PROF
1427    monitor(0);
1428#endif
1429    /*
1430     * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit
1431     * directly because we poke child here. Otherwise we might continue
1432     * unwarrantedly (sic).
1433     */
1434    child = 1;
1435
1436    /*
1437     * PWP: do this step-by-step because we might get a bus error if
1438     * status isn't set, so we call getn(NULL).
1439     */
1440    cp = value(STRstatus);
1441
1442    if (!cp)
1443        i = 13;
1444    else
1445        i = getn(cp);
1446
1447    xexit(i);
1448}
1449
1450/*
1451 * in the event of a HUP we want to save the history
1452 */
1453static  sigret_t
1454phup(snum)
1455int snum;
1456{
1457#ifdef UNRELSIGS
1458    if (snum)
1459        (void) sigset(snum, SIG_IGN);
1460#endif /* UNRELSIGS */
1461    rechist(NULL);
1462    recdirs(NULL);
1463
1464#ifdef POSIXJOBS
1465    /*
1466     * We kill the last foreground process group. It then becomes
1467     * responsible to propagate the SIGHUP to its progeny.
1468     */
1469    {
1470        struct process *pp, *np;
1471        int foregnd;
1472
1473        for (pp = proclist.p_next; pp; pp = pp->p_next) {
1474            foregnd = 0;
1475            np = pp;
1476            /*
1477             * Find if this job is in the foreground. It could be that
1478             * the process leader has exited and the foreground flag
1479             * is cleared for it.
1480             */
1481            do
1482                if ((np->p_flags & PFOREGND) != 0) {
1483                    foregnd = 1;
1484                    break;
1485                }
1486            while ((np = np->p_friends) != pp);
1487
1488            /* Kill the leader of the foreground job */
1489            if (foregnd && pp->p_procid == pp->p_jobid)
1490                (void) killpg (pp->p_jobid, SIGHUP);
1491        }
1492    }
1493#endif /* POSIXJOBS */
1494
1495    xexit(snum);
1496#ifndef SIGVOID
1497    return (snum);
1498#endif
1499}
1500
1501static Char   *jobargv[2] = {STRjobs, 0};
1502
1503/*
1504 * Catch an interrupt, e.g. during lexical input.
1505 * If we are an interactive shell, we reset the interrupt catch
1506 * immediately.  In any case we drain the shell output,
1507 * and finally go through the normal error mechanism, which
1508 * gets a chance to make the shell go away.
1509 */
1510int     just_signaled;          /* bugfix by Michael Bloom (mg@ttidca.TTI.COM) */
1511
1512#ifdef SIGVOID
1513/*ARGSUSED*/
1514#endif
1515sigret_t
1516pintr(snum)
1517int snum;
1518{
1519#ifdef UNRELSIGS
1520    if (snum)
1521        (void) sigset(snum, pintr);
1522#endif /* UNRELSIGS */
1523    just_signaled = 1;
1524    pintr1(1);
1525#ifndef SIGVOID
1526    return (snum);
1527#endif
1528}
1529
1530void
1531pintr1(wantnl)
1532    bool    wantnl;
1533{
1534    register Char **v;
1535#ifdef BSDSIGS
1536    sigmask_t omask;
1537#endif
1538
1539#ifdef BSDSIGS
1540    omask = sigblock((sigmask_t) 0);
1541#endif
1542    if (setintr) {
1543#ifdef BSDSIGS
1544        (void) sigsetmask(omask & ~sigmask(SIGINT));
1545#else
1546        (void) sigrelse(SIGINT);
1547#endif
1548        if (pjobs) {
1549            pjobs = 0;
1550            xputchar('\n');
1551            dojobs(jobargv, NULL);
1552            stderror(ERR_NAME | ERR_INTR);
1553        }
1554    }
1555    /* MH - handle interrupted completions specially */
1556    {
1557        extern int InsideCompletion;
1558
1559        if (InsideCompletion)
1560            stderror(ERR_SILENT);
1561    }
1562    /* JV - Make sure we shut off inputl */
1563    {
1564        extern Char GettingInput;
1565
1566        (void) Cookedmode();
1567        GettingInput = 0;
1568    }
1569#ifdef BSDSIGS
1570    (void) sigsetmask(omask & ~sigmask(SIGCHLD));
1571#else
1572    if (setintr)
1573        (void) sighold(SIGINT);
1574    (void) sigrelse(SIGCHLD);
1575#endif
1576    draino();
1577#ifndef _VMS_POSIX
1578    (void) endpwent();
1579#endif /*atp vmsposix */
1580
1581    /*
1582     * If we have an active "onintr" then we search for the label. Note that if
1583     * one does "onintr -" then we shan't be interruptible so we needn't worry
1584     * about that here.
1585     */
1586    if (gointr) {
1587        gotolab(gointr);
1588        timflg = 0;
1589        if ((v = pargv) != 0)
1590            pargv = 0, blkfree(v);
1591        if ((v = gargv) != 0)
1592            gargv = 0, blkfree(v);
1593        reset();
1594    }
1595    else if (intty && wantnl) {
1596        /* xputchar('\n'); *//* Some like this, others don't */
1597        (void) putraw('\r');
1598        (void) putraw('\n');
1599    }
1600    stderror(ERR_SILENT);
1601}
1602
1603/*
1604 * Process is the main driving routine for the shell.
1605 * It runs all command processing, except for those within { ... }
1606 * in expressions (which is run by a routine evalav in sh.exp.c which
1607 * is a stripped down process), and `...` evaluation which is run
1608 * also by a subset of this code in sh.glob.c in the routine backeval.
1609 *
1610 * The code here is a little strange because part of it is interruptible
1611 * and hence freeing of structures appears to occur when none is necessary
1612 * if this is ignored.
1613 *
1614 * Note that if catch is not set then we will unwind on any error.
1615 * If an end-of-file occurs, we return.
1616 */
1617static struct command *savet = NULL;
1618void
1619process(catch)
1620    bool    catch;
1621{
1622    extern char Expand;
1623    jmp_buf_t osetexit;
1624    /* PWP: This might get nuked my longjmp so don't make it a register var */
1625    struct command *t = savet;
1626
1627    savet = NULL;
1628    getexit(osetexit);
1629    for (;;) {
1630
1631        pendjob();
1632
1633        /* This was leaking memory badly, particularly when sourcing
1634         * files, etc.. For whatever reason we were arriving here with
1635         * allocated pointers still active, and the code was simply
1636         * overwriting them.  I can't say I fully understand the
1637         * control flow here, but according to Purify this particular
1638         * leak has been plugged, and I haven't noticed any ill
1639         * effects.. (sg)
1640         */
1641        if (paraml.next && paraml.next != &paraml)
1642            freelex(&paraml);
1643
1644        paraml.next = paraml.prev = &paraml;
1645        paraml.word = STRNULL;
1646        (void) setexit();
1647        justpr = enterhist;     /* execute if not entering history */
1648
1649        /*
1650         * Interruptible during interactive reads
1651         */
1652        if (setintr)
1653#ifdef BSDSIGS
1654            (void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT));
1655#else
1656            (void) sigrelse(SIGINT);
1657#endif
1658
1659
1660        /*
1661         * For the sake of reset()
1662         */
1663        freelex(&paraml);
1664        if (savet)
1665            freesyn(savet), savet = NULL;
1666
1667        if (haderr) {
1668            if (!catch) {
1669                /* unwind */
1670                doneinp = 0;
1671                savet = t;
1672                resexit(osetexit);
1673                reset();
1674            }
1675            haderr = 0;
1676            /*
1677             * Every error is eventually caught here or the shell dies.  It is
1678             * at this point that we clean up any left-over open files, by
1679             * closing all but a fixed number of pre-defined files.  Thus
1680             * routines don't have to worry about leaving files open due to
1681             * deeper errors... they will get closed here.
1682             */
1683            closem();
1684            continue;
1685        }
1686        if (doneinp) {
1687            doneinp = 0;
1688            break;
1689        }
1690        if (chkstop)
1691            chkstop--;
1692        if (neednote)
1693            pnote();
1694        if (intty && prompt && evalvec == 0) {
1695            mailchk();
1696            /*
1697             * Watch for logins/logouts. Next is scheduled commands stored
1698             * previously using "sched." Then execute periodic commands.
1699             * Following that, the prompt precmd is run.
1700             */
1701#ifndef HAVENOUTMP
1702            watch_login();
1703#endif /* !HAVENOUTMP */
1704            sched_run();
1705            period_cmd();
1706            precmd();
1707            /*
1708             * If we are at the end of the input buffer then we are going to
1709             * read fresh stuff. Otherwise, we are rereading input and don't
1710             * need or want to prompt.
1711             */
1712            if (fseekp == feobp && aret == F_SEEK)
1713                printprompt(0, NULL);
1714            flush();
1715            setalarm(1);
1716        }
1717        if (seterr) {
1718            xfree((ptr_t) seterr);
1719            seterr = NULL;
1720        }
1721
1722        /*
1723         * Echo not only on VERBOSE, but also with history expansion. If there
1724         * is a lexical error then we forego history echo.
1725         */
1726        if ((lex(&paraml) && !seterr && intty && !tellwhat && !Expand) ||
1727            adrof(STRverbose)) {
1728            haderr = 1;
1729            prlex(&paraml);
1730            haderr = 0;
1731        }
1732        (void) alarm(0);        /* Autologout OFF */
1733
1734        /*
1735         * The parser may lose space if interrupted.
1736         */
1737        if (setintr)
1738#ifdef BSDSIGS
1739            (void) sigblock(sigmask(SIGINT));
1740#else
1741            (void) sighold(SIGINT);
1742#endif
1743
1744        /*
1745         * Save input text on the history list if reading in old history, or it
1746         * is from the terminal at the top level and not in a loop.
1747         *
1748         * PWP: entry of items in the history list while in a while loop is done
1749         * elsewhere...
1750         */
1751        if (enterhist || (catch && intty && !whyles && !tellwhat))
1752            savehist(&paraml);
1753
1754        if (Expand && seterr)
1755            Expand = 0;
1756
1757        /*
1758         * Print lexical error messages, except when sourcing history lists.
1759         */
1760        if (!enterhist && seterr)
1761            stderror(ERR_OLD);
1762
1763        /*
1764         * If had a history command :p modifier then this is as far as we
1765         * should go
1766         */
1767        if (justpr)
1768            reset();
1769
1770        /*
1771         * If had a tellwhat from twenex() then do
1772         */
1773        if (tellwhat) {
1774            tellmewhat(&paraml);
1775            reset();
1776        }
1777
1778        alias(&paraml);
1779
1780#ifdef BSDJOBS
1781        /*
1782         * If we are interactive, try to continue jobs that we have stopped
1783         */
1784        if (prompt)
1785            continue_jobs(&paraml);
1786#endif                          /* BSDJOBS */
1787
1788        /*
1789         * Check to see if the user typed "rm * .o" or something
1790         */
1791        if (prompt)
1792            rmstar(&paraml);
1793        /*
1794         * Parse the words of the input into a parse tree.
1795         */
1796        savet = syntax(paraml.next, &paraml, 0);
1797        if (seterr)
1798            stderror(ERR_OLD);
1799
1800        /*
1801         * Execute the parse tree From: Michael Schroeder
1802         * <mlschroe@immd4.informatik.uni-erlangen.de> was execute(t, tpgrp);
1803         */
1804        execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
1805
1806        /*
1807         * Made it!
1808         */
1809        freelex(&paraml);
1810        freesyn(savet), savet = NULL;
1811#ifdef SIG_WINDOW
1812        if (catch && intty && !whyles && !tellwhat)
1813            window_change(0);   /* for window systems */
1814#endif                          /* SIG_WINDOW */
1815    }
1816    savet = t;
1817    resexit(osetexit);
1818}
1819
1820/*ARGSUSED*/
1821void
1822dosource(t, c)
1823    register Char **t;
1824    struct command *c;
1825{
1826    register Char *f;
1827    bool    hflg = 0;
1828    extern int bequiet;
1829    char    buf[BUFSIZE];
1830
1831    t++;
1832    if (*t && eq(*t, STRmh)) {
1833        if (*++t == NULL)
1834            stderror(ERR_NAME | ERR_HFLAG);
1835        hflg++;
1836    }
1837    f = globone(*t++, G_ERROR);
1838    (void) strcpy(buf, short2str(f));
1839    xfree((ptr_t) f);
1840    if ((!srcfile(buf, 0, hflg, t)) && (!hflg) && (!bequiet))
1841        stderror(ERR_SYSTEM, buf, strerror(errno));
1842}
1843
1844/*
1845 * Check for mail.
1846 * If we are a login shell, then we don't want to tell
1847 * about any mail file unless its been modified
1848 * after the time we started.
1849 * This prevents us from telling the user things he already
1850 * knows, since the login program insists on saying
1851 * "You have mail."
1852 */
1853static void
1854mailchk()
1855{
1856    register struct varent *v;
1857    register Char **vp;
1858    time_t  t;
1859    int     intvl, cnt;
1860    struct stat stb;
1861    bool    new;
1862
1863    v = adrof(STRmail);
1864    if (v == 0)
1865        return;
1866    (void) time(&t);
1867    vp = v->vec;
1868    cnt = blklen(vp);
1869    intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
1870    if (intvl < 1)
1871        intvl = 1;
1872    if (chktim + intvl > t)
1873        return;
1874    for (; *vp; vp++) {
1875        if (stat(short2str(*vp), &stb) < 0)
1876            continue;
1877#if defined(BSDTIMES) || defined(_SEQUENT_)
1878        new = stb.st_mtime > time0.tv_sec;
1879#else
1880        new = stb.st_mtime > time0;
1881#endif
1882        if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
1883            (stb.st_atime <= chktim && stb.st_mtime <= chktim) ||
1884            (loginsh && !new))
1885            continue;
1886        if (cnt == 1)
1887            xprintf("You have %smail.\n", new ? "new " : "");
1888        else
1889            xprintf("%s in %S.\n", new ? "New mail" : "Mail", *vp);
1890    }
1891    chktim = t;
1892}
1893
1894/*
1895 * Extract a home directory from the password file
1896 * The argument points to a buffer where the name of the
1897 * user whose home directory is sought is currently.
1898 * We write the home directory of the user back there.
1899 */
1900int
1901gethdir(home)
1902    Char   *home;
1903{
1904    Char   *h;
1905
1906    /*
1907     * Is it us?
1908     */
1909    if (*home == '\0') {
1910        if ((h = value(STRhome)) != STRNULL) {
1911            (void) Strcpy(home, h);
1912            return 0;
1913        }
1914        else
1915            return 1;
1916    }
1917
1918    /*
1919     * Look in the cache
1920     */
1921    if ((h = gettilde(home)) == NULL)
1922        return 1;
1923    else {
1924        (void) Strcpy(home, h);
1925        return 0;
1926    }
1927}
1928
1929/*
1930 * Move the initial descriptors to their eventual
1931 * resting places, closing all other units.
1932 */
1933void
1934initdesc()
1935{
1936
1937    didfds = 0;                 /* 0, 1, 2 aren't set up */
1938    (void) close_on_exec(SHIN = dcopy(0, FSHIN), 1);
1939    (void) close_on_exec(SHOUT = dcopy(1, FSHOUT), 1);
1940    (void) close_on_exec(SHDIAG = dcopy(2, FSHDIAG), 1);
1941    (void) close_on_exec(OLDSTD = dcopy(SHIN, FOLDSTD), 1);
1942#ifndef CLOSE_ON_EXEC
1943    didcch = 0;                 /* Havent closed for child */
1944#endif /* CLOSE_ON_EXEC */
1945    isdiagatty = isatty(SHDIAG);
1946    isoutatty = isatty(SHOUT);
1947    closem();
1948}
1949
1950
1951void
1952#ifdef PROF
1953done(i)
1954#else
1955xexit(i)
1956#endif
1957    int     i;
1958{
1959#ifdef TESLA
1960    if (loginsh && do_logout) {
1961        /* this is to send hangup signal to the develcon */
1962        /* we toggle DTR. clear dtr - sleep 1 - set dtr */
1963        /* ioctl will return ENOTTY for pty's but we ignore it   */
1964        /* exitstat will run after disconnect */
1965        /* we sleep for 2 seconds to let things happen in */
1966        /* .logout and rechist() */
1967#ifdef TIOCCDTR
1968        (void) sleep(2);
1969        (void) ioctl(FSHTTY, TIOCCDTR, NULL);
1970        (void) sleep(1);
1971        (void) ioctl(FSHTTY, TIOCSDTR, NULL);
1972#endif /* TIOCCDTR */
1973    }
1974#endif /* TESLA */
1975
1976    untty();
1977    _exit(i);
1978}
1979
1980static Char **
1981defaultpath()
1982{
1983    char   *ptr;
1984    Char  **blk, **blkp;
1985    struct stat stb;
1986
1987    blkp = blk = (Char **) xmalloc((size_t) sizeof(Char *) * 10);
1988
1989#ifndef DOTLAST
1990    *blkp++ = Strsave(STRdot);
1991#endif
1992
1993#define DIRAPPEND(a)  \
1994        if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \
1995                *blkp++ = SAVE(ptr)
1996
1997#ifdef _PATH_LOCAL
1998    DIRAPPEND(_PATH_LOCAL);
1999#endif
2000
2001#ifdef _PATH_USRUCB
2002    DIRAPPEND(_PATH_USRUCB);
2003#endif
2004
2005#ifdef _PATH_USRBSD
2006    DIRAPPEND(_PATH_USRBSD);
2007#endif
2008
2009#ifdef _PATH_BIN
2010    DIRAPPEND(_PATH_BIN);
2011#endif
2012
2013#ifdef _PATH_USRBIN
2014    DIRAPPEND(_PATH_USRBIN);
2015#endif
2016
2017#undef DIRAPPEND
2018
2019#ifdef DOTLAST
2020    *blkp++ = Strsave(STRdot);
2021#endif
2022    *blkp = NULL;
2023    return (blk);
2024}
Note: See TracBrowser for help on using the repository browser.