"Fossies" - the Fresh Open Source Software Archive

Member "tcsh-6.22.03/sh.c" (18 Nov 2020, 63662 Bytes) of package /linux/misc/tcsh-6.22.03.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sh.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 6.22.02_vs_6.22.03.

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