1: /* $Header: /home/hyperion/mu/christos/src/sys/tcsh-6.00/RCS/sh.c,v 3.0 1991/07/04 23:34:26 christos Exp $ */
   2: /*
   3:  * sh.c: Main shell routines
   4:  */
   5: /*-
   6:  * Copyright (c) 1980, 1991 The Regents of the University of California.
   7:  * All rights reserved.
   8:  *
   9:  * Redistribution and use in source and binary forms, with or without
  10:  * modification, are permitted provided that the following conditions
  11:  * are met:
  12:  * 1. Redistributions of source code must retain the above copyright
  13:  *    notice, this list of conditions and the following disclaimer.
  14:  * 2. Redistributions in binary form must reproduce the above copyright
  15:  *    notice, this list of conditions and the following disclaimer in the
  16:  *    documentation and/or other materials provided with the distribution.
  17:  * 3. 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: #include "config.h"
  38: #if !defined(lint)  && !defined(pdp11)
  39: char    copyright[] =
  40: "@(#) Copyright (c) 1991 The Regents of the University of California.\n\
  41:  All rights reserved.\n";
  42: #endif				/* not lint */
  43: 
  44: #if !defined(lint) && !defined(pdp11)
  45: static char *rcsid()
  46:     { return "$Id: sh.c,v 3.0 1991/07/04 23:34:26 christos Exp $"; }
  47: #endif
  48: 
  49: #include "sh.h"
  50: #include "ed.h"
  51: 
  52: extern bool MapsAreInited;
  53: extern bool NLSMapsAreInited;
  54: extern bool NoNLSRebind;
  55: 
  56: /*
  57:  * C Shell
  58:  *
  59:  * Bill Joy, UC Berkeley, California, USA
  60:  * October 1978, May 1980
  61:  *
  62:  * Jim Kulp, IIASA, Laxenburg, Austria
  63:  * April 1980
  64:  *
  65:  * Filename recognition added:
  66:  * Ken Greer, Ind. Consultant, Palo Alto CA
  67:  * October 1983.
  68:  *
  69:  * Karl Kleinpaste, Computer Consoles, Inc.
  70:  * Added precmd, periodic/tperiod, prompt changes,
  71:  * directory stack hack, and login watch.
  72:  * Sometime March 1983 - Feb 1984.
  73:  *
  74:  * Added scheduled commands, including the "sched" command,
  75:  * plus the call to sched_run near the precmd et al
  76:  * routines.
  77:  * Upgraded scheduled events for running events while
  78:  * sitting idle at command input.
  79:  *
  80:  * Paul Placeway, Ohio State
  81:  * added stuff for running with twenex/inputl  9 Oct 1984.
  82:  *
  83:  * ported to Apple Unix (TM) (OREO)  26 -- 29 Jun 1987
  84:  */
  85: 
  86: #ifdef TESLA
  87: static int do_logout;
  88: #endif				/* TESLA */
  89: 
  90: Char   *dumphist[] = {STRhsto, STRmh, 0, 0};
  91: Char   *loadhist[] = {STRsource, STRmh, STRhstf, 0};
  92: 
  93: #ifdef CSHDIRS
  94: Char   *loaddirs[] = {STRsource, STRdirfile, 0};
  95: bool    dflag = 0;
  96: #endif
  97: 
  98: #if defined(convex) || defined(__convex__)
  99: bool    use_fork = 0;       /* use fork() instead of vfork()? */
 100: #endif
 101: 
 102: int     nofile = 0;
 103: bool    reenter = 0;
 104: bool    nverbose = 0;
 105: bool    nexececho = 0;
 106: bool    quitit = 0;
 107: bool    fast = 0;
 108: bool    batch = 0;
 109: bool    mflag = 0;
 110: bool    prompt = 1;
 111: bool    enterhist = 0;
 112: bool    tellwhat = 0;
 113: time_t  t_period;
 114: 
 115: extern char **environ;
 116: 
 117: static  int       srccat    __P((Char *, Char *));
 118: static  int       srcfile   __P((char *, bool, bool));
 119: static  sigret_t      phup      __P((int));
 120: static  void          srcunit   __P((int, bool, bool));
 121: static  void          mailchk   __P((void));
 122: static  Char        **d_path    __P((void));
 123: 
 124: int
 125: main(argc, argv)
 126:     int     argc;
 127:     char  **argv;
 128: {
 129:     register Char *cp, *cp2;
 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:     settimes();         /* Immed. estab. timing base */
 139: #ifdef TESLA
 140:     do_logout = 0;
 141: #endif				/* TESLA */
 142: 
 143:     osinit();           /* Os dependent initialization */
 144: 
 145:     /*
 146:      * Initialize non constant strings
 147:      */
 148: #ifdef _PATH_BSHELL
 149:     STR_BSHELL = SAVE(_PATH_BSHELL);
 150: #endif
 151: #ifdef _PATH_CSHELL
 152:     STR_SHELLPATH = SAVE(_PATH_CSHELL);
 153: #endif
 154: #ifdef _PATH_TCSHELL
 155:     STR_SHELLPATH = SAVE(_PATH_TCSHELL);
 156: #endif
 157:     STR_environ = blk2short(environ);
 158:     environ = short2blk(STR_environ);   /* So that we can free it */
 159:     STR_WORD_CHARS = SAVE(WORD_CHARS);
 160: 
 161:     HIST = '!';
 162:     HISTSUB = '^';
 163:     word_chars = STR_WORD_CHARS;
 164:     bslash_quote = 0;       /* PWP: do tcsh-style backslash quoting? */
 165: 
 166:     tempv = argv;
 167:     if (eq(str2short(tempv[0]), STRaout))   /* A.out's are quittable */
 168:     quitit = 1;
 169:     uid = getuid();
 170:     gid = getgid();
 171:     /*
 172:      * We are a login shell if: 1. we were invoked as -<something> and we had
 173:      * no arguments 2. or we were invoked only with the -l flag
 174:      */
 175:     loginsh = (**tempv == '-' && argc == 1) || (argc == 2 &&
 176:                    tempv[1][0] == '-' && tempv[1][1] == 'l' &&
 177:                         tempv[1][2] == '\0');
 178:     if (loginsh && **tempv != '-') {
 179:     /*
 180: 	 * Mangle the argv space
 181: 	 */
 182:     tempv[1][0] = '\0';
 183:     tempv[1][1] = '\0';
 184:     tempv[1] = NULL;
 185:     for (tcp = *tempv; *tcp++;);
 186:     for (tcp--; tcp >= *tempv; tcp--)
 187:         tcp[1] = tcp[0];
 188:     *++tcp = '-';
 189:     argc--;
 190:     }
 191:     if (loginsh)
 192:     (void) time(&chktim);
 193: 
 194:     AsciiOnly = 1;
 195:     NoNLSRebind = getenv("NOREBIND") != NULL;
 196: #ifdef NLS
 197:     (void) setlocale(LC_ALL, "");
 198:     {
 199:     int     k;
 200: 
 201:     for (k = 0200; k <= 0377 && !Isprint(k); k++);
 202:     AsciiOnly = k > 0377;
 203:     }
 204: #else
 205:     AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL;
 206: #endif				/* NLS */
 207:     if (MapsAreInited && !NLSMapsAreInited)
 208:     ed_INLSMaps();
 209: 
 210:     /*
 211:      * Initialize for periodic command intervals. Also, initialize the dummy
 212:      * tty list for login-watch.
 213:      */
 214:     (void) time(&t_period);
 215:     initwatch();
 216: 
 217:     /*
 218:      * Move the descriptors to safe places. The variable didfds is 0 while we
 219:      * have only FSH* to work with. When didfds is true, we have 0,1,2 and
 220:      * prefer to use these.
 221:      */
 222:     initdesc();
 223: 
 224:     /*
 225:      * Get and set the tty now
 226:      */
 227:     if (ttyn = ttyname(SHIN)) {
 228:     /*
 229: 	 * Could use rindex to get rid of other possible path components, but
 230: 	 * hpux preserves the subdirectory /pty/ when storing the tty name in
 231: 	 * utmp, so we keep it too.
 232: 	 */
 233:     if (strncmp(ttyn, "/dev/", 5) == 0)
 234:         set(STRtty, cp = SAVE(ttyn + 5));
 235:     else
 236:         set(STRtty, cp = SAVE(ttyn));
 237:     }
 238:     else
 239:     set(STRtty, cp = SAVE(""));
 240:     /*
 241:      * Initialize the shell variables. ARGV and PROMPT are initialized later.
 242:      * STATUS is also munged in several places. CHILD is munged when
 243:      * forking/waiting
 244:      */
 245: 
 246:     /*
 247:      * 7-10-87 Paul Placeway autologout should be set ONLY on login shells and
 248:      * on shells running as root.  Out of these, autologout should NOT be set
 249:      * for any psudo-terminals (this catches most window systems) and not for
 250:      * any terminal running X windows.
 251:      *
 252:      * At Ohio State, we have had problems with a user having his X session drop
 253:      * out from under him (on a Sun) because the shell in his master xterm
 254:      * timed out and exited.
 255:      *
 256:      * Really, this should be done with a program external to the shell, that
 257:      * watches for no activity (and NO running programs, such as dump) on a
 258:      * terminal for a long peroid of time, and then SIGHUPS the shell on that
 259:      * terminal.
 260:      *
 261:      * bugfix by Rich Salz <rsalz@PINEAPPLE.BBN.COM>: For root rsh things allways
 262:      * first check to see if loginsh or really root, then do things with
 263:      * ttyname()
 264:      *
 265:      * Also by Jean-Francois Lamy <lamy%ai.toronto.edu@RELAY.CS.NET>: check the
 266:      * value of cp before using it! ("root can rsh too")
 267:      *
 268:      * PWP: keep the nested ifs; the order of the tests matters and a good (smart)
 269:      * C comp_r might re-arange things wrong.
 270:      */
 271: #ifdef AUTOLOGOUT
 272:     if (loginsh || (uid == 0)) {
 273:     if (*cp) {
 274:         /* only for login shells or root and we must have a tty */
 275:         if ((cp2 = Strrchr(cp, (Char) '/')) != NULL) {
 276:         cp = cp2 + 1;
 277:         }
 278:         if (!((Strncmp(cp, STRtty, 3) == 0) &&
 279:           (cp[3] >= 'p' && cp[3] <= 'u'))) {
 280:         if (getenv("DISPLAY") == NULL) {
 281:             /* NOT on X window shells */
 282:             set(STRa_logout, Strsave(STRdflt_auto_logout));
 283:         }
 284:         }
 285:     }
 286:     }
 287: #endif				/* AUTOLOGOUT */
 288: 
 289:     (void) sigset(SIGALRM, alrmcatch);
 290: 
 291:     set(STRstatus, Strsave(STR0));
 292:     fix_version();      /* publish the shell version */
 293: 
 294:     /*
 295:      * set the shell-level var to 1 or increment it.
 296:      */
 297:     if ((tcp = getenv("SHLVL")) != NULL) {
 298:     Char    buff[BUFSIZ];
 299: 
 300:     Itoa(1 + atoi(tcp), buff);
 301:     set(STRshlvl, Strsave(buff));
 302:     Setenv(STRSHLVL, buff);
 303:     }
 304:     else {
 305:     set(STRshlvl, SAVE("1"));
 306:     Setenv(STRSHLVL, str2short("1"));
 307:     }
 308: 
 309:     if ((tcp = getenv("HOME")) != NULL)
 310:     cp = SAVE(tcp);
 311:     else
 312:     cp = NOSTR;
 313:     if (cp == NOSTR)
 314:     fast = 1;       /* No home -> can't read scripts */
 315:     else
 316:     set(STRhome, cp);
 317:     dinit(cp);          /* dinit thinks that HOME == cwd in a login
 318: 				 * shell */
 319:     /*
 320:      * Grab other useful things from the environment. Should we grab
 321:      * everything??
 322:      */
 323:     if ((tcp = getenv("LOGNAME")) != NULL || (tcp = getenv("USER")) != NULL)
 324:     set(STRuser, SAVE(tcp));
 325:     if ((tcp = getenv("TERM")) != NULL)
 326:     set(STRterm, SAVE(tcp));
 327: 
 328:     /*
 329:      * set usefull environment things for the user
 330:      */
 331:     {
 332: 
 333:     Char    buff[BUFSIZ];
 334: 
 335: #ifdef apollo
 336:     int     oid = getoid();
 337: 
 338:     Itoa(oid, buff);
 339:     set(STRoid, Strsave(buff));
 340: #endif				/* apollo */
 341:     Itoa(uid, buff);
 342:     set(STRuid, Strsave(buff));
 343: 
 344:     Itoa(gid, buff);
 345:     set(STRgid, Strsave(buff));
 346:     }
 347: 
 348:     /*
 349:      * HOST may be wrong, since rexd transports the entire environment on sun
 350:      * 3.x Just set it again
 351:      */
 352:     {
 353:     char    cbuff[MAXHOSTNAMELEN];
 354: 
 355:     if (gethostname(cbuff, sizeof(cbuff)) >= 0) {
 356:         cbuff[sizeof(cbuff) - 1] = '\0';    /* just in case */
 357:         Setenv(STRHST, str2short(cbuff));
 358:     }
 359:     else
 360:         Setenv(STRHST, str2short("unknown"));
 361:     }
 362: 
 363: 
 364:     /*
 365:      * HOSTTYPE, too. Just set it again.
 366:      */
 367:     Setenv(STRHSTTYPE, GetHostType());
 368: #ifdef apollo
 369:     if ((tcp = getenv("SYSTYPE")) == NULL)
 370:     tcp = "bsd4.3";
 371:     Setenv(STRSYSTYPE, str2short(tcp));
 372: #endif				/* apollo */
 373: 
 374:     /*
 375:      * set editing on by default, unless running under Emacs as an inferior
 376:      * shell.
 377:      */
 378:     if ((tcp = getenv("EMACS")) == NULL || strcmp(tcp, "t") != 0) {
 379:     /* not running under Emacs */
 380:     /* The 'edit' variable is either set or unset.  It doesn't */
 381:     /* need a value.  Making it 'emacs' might be confusing.    */
 382:     set(STRedit, Strsave(STRNULL));
 383:     editing = 1;
 384:     }
 385:     else {          /* running under Emacs */
 386:     /* we don't set edit to anything */
 387:     editing = 0;
 388:     }
 389: 
 390: 
 391:     /*
 392:      * still more mutability: make the complete routine automatically add the
 393:      * suffix of file names...
 394:      */
 395:     set(STRaddsuffix, Strsave(STRNULL));
 396: 
 397:     /*
 398:      * Re-initialize path if set in environment
 399:      */
 400:     if ((tcp = getenv("PATH")) == NULL)
 401:     set1(STRpath, d_path(), &shvhed);
 402:     else
 403:     importpath(SAVE(tcp));
 404: 
 405:     set(STRshell, Strsave(STR_SHELLPATH));
 406: 
 407:     doldol = putn((int) getpid());  /* For $$ */
 408:     shtemp = Strspl(STRtmpsh, doldol);  /* For << */
 409: 
 410:     /*
 411:      * Record the interrupt states from the parent process. If the parent is
 412:      * non-interruptible our hand must be forced or we (and our children) won't
 413:      * be either. Our children inherit termination from our parent. We catch it
 414:      * only if we are the login shell.
 415:      */
 416: #ifdef BSDSIGS
 417:     /* parents interruptibility */
 418:     (void) mysigvec(SIGINT, NULL, &osv);
 419:     parintr = (sigret_t(*) ()) osv.sv_handler;
 420:     (void) mysigvec(SIGTERM, NULL, &osv);
 421:     parterm = (sigret_t(*) ()) osv.sv_handler;
 422: #else               /* BSDSIGS */
 423:     parintr = signal(SIGINT, SIG_IGN);  /* parents interruptibility */
 424:     (void) sigset(SIGINT, parintr); /* ... restore */
 425:     parterm = signal(SIGTERM, SIG_IGN); /* parents terminability */
 426:     (void) sigset(SIGTERM, parterm);    /* ... restore */
 427: #endif				/* BSDSIGS */
 428: 
 429:     if (loginsh) {
 430:     (void) signal(SIGHUP, phup);    /* exit processing on HUP */
 431: #ifdef SIGXCPU
 432:     (void) signal(SIGXCPU, phup);   /* ...and on XCPU */
 433: #endif				/* SIGXCPU */
 434: #ifdef SIGXFSZ
 435:     (void) signal(SIGXFSZ, phup);   /* ...and on XFSZ */
 436: #endif				/* SIGXFSZ */
 437:     }
 438: 
 439: #ifdef TCF
 440:     /* Enable process migration on ourselves and our progeny */
 441:     (void) signal(SIGMIGRATE, SIG_DFL);
 442: #endif				/* TCF */
 443: 
 444:     /*
 445:      * Process the arguments.
 446:      *
 447:      * Note that processing of -v/-x is actually delayed till after script
 448:      * processing.
 449:      *
 450:      * We set the first character of our name to be '-' if we are a shell running
 451:      * interruptible commands.  Many programs which examine ps'es use this to
 452:      * filter such shells out.
 453:      */
 454:     argc--, tempv++;
 455:     while (argc > 0 && (tcp = tempv[0])[0] == '-' &&
 456:        *++tcp != '\0' && !batch) {
 457:     do
 458:         switch (*tcp++) {
 459: 
 460:         case 0:     /* -	Interruptible, no prompt */
 461:         prompt = 0;
 462:         setintr = 1;
 463:         nofile = 1;
 464:         break;
 465: 
 466:         case 'b':       /* -b	Next arg is input file */
 467:         batch = 1;
 468:         break;
 469: 
 470:         case 'c':       /* -c	Command input from arg */
 471:         if (argc == 1)
 472:             xexit(0);
 473:         argc--, tempv++;
 474:         arginp = SAVE(tempv[0]);
 475:         /*
 476: 		 * * Give an error on -c arguments that end in * backslash to
 477: 		 * ensure that you don't make * nonportable csh scripts.
 478: 		 */
 479:         {
 480:             register Char *cp;
 481:             register int count;
 482: 
 483:             cp = arginp + Strlen(arginp);
 484:             count = 0;
 485:             while (cp > arginp && *--cp == '\\')
 486:             ++count;
 487:             if ((count & 1) != 0) {
 488:             exiterr = 1;
 489:             stderror(ERR_ARGC);
 490:             }
 491:         }
 492:         prompt = 0;
 493:         nofile = 1;
 494:         break;
 495: 
 496: #ifdef CSHDIRS
 497:         case 'd':       /* -d   Force load of ~/.cshdirs */
 498:         dflag++;
 499:         break;
 500: #endif
 501: 
 502: 
 503:         case 'e':       /* -e	Exit on any error */
 504:         exiterr = 1;
 505:         break;
 506: 
 507:         case 'f':       /* -f	Fast start */
 508:         fast = 1;
 509:         break;
 510: 
 511:         case 'i':       /* -i	Interactive, even if !intty */
 512:         intact = 1;
 513:         nofile = 1;
 514:         break;
 515: 
 516:         case 'm':       /* -m	read .cshrc (from su) */
 517:         mflag = 1;
 518:         break;
 519: 
 520:         case 'n':       /* -n	Don't execute */
 521:         noexec = 1;
 522:         break;
 523: 
 524:         case 'q':       /* -q	(Undoc'd) ... die on quit */
 525:         quitit = 1;
 526:         break;
 527: 
 528:         case 's':       /* -s	Read from std input */
 529:         nofile = 1;
 530:         break;
 531: 
 532:         case 't':       /* -t	Read one line from input */
 533:         onelflg = 2;
 534:         prompt = 0;
 535:         nofile = 1;
 536:         break;
 537: 
 538:         case 'v':       /* -v	Echo hist expanded input */
 539:         nverbose = 1;   /* ... later */
 540:         break;
 541: 
 542:         case 'x':       /* -x	Echo just before execution */
 543:         nexececho = 1;  /* ... later */
 544:         break;
 545: 
 546:         case 'V':       /* -V	Echo hist expanded input */
 547:         setNS(STRverbose);  /* NOW! */
 548:         break;
 549: 
 550:         case 'X':       /* -X	Echo just before execution */
 551:         setNS(STRecho); /* NOW! */
 552:         break;
 553: 
 554: #if defined(__convex__) || defined(convex)
 555:         case 'F':       /* Undocumented flag */
 556:         /*
 557: 		 * This will cause children to be created using fork instead of
 558: 		 * vfork.
 559: 		 */
 560:         use_fork = 1;
 561:         break;
 562: #endif
 563: 
 564:     } while (*tcp);
 565:     tempv++, argc--;
 566:     }
 567: 
 568:     if (quitit)         /* With all due haste, for debugging */
 569:     (void) signal(SIGQUIT, SIG_DFL);
 570: 
 571:     /*
 572:      * Unless prevented by -, -c, -i, -s, or -t, if there are remaining
 573:      * arguments the first of them is the name of a shell file from which to
 574:      * read commands.
 575:      */
 576:     if (nofile == 0 && argc > 0) {
 577:     nofile = open(tempv[0], O_RDONLY);
 578:     if (nofile < 0) {
 579:         child = 1;      /* So this ... */
 580:         /* ... doesn't return */
 581:         stderror(ERR_SYSTEM, tempv[0], strerror(errno));
 582:     }
 583:     ffile = SAVE(tempv[0]);
 584:     /*
 585: 	 * Replace FSHIN. Handle /dev/std{in,out,err} specially
 586: 	 * since once they are closed we cannot open them again.
 587: 	 * In that case we use our own saved descriptors
 588: 	 */
 589:     if ((SHIN = dmove(nofile, FSHIN)) < 0)
 590:         switch(nofile) {
 591:         case 0:
 592:         SHIN = FSHIN;
 593:         break;
 594:         case 1:
 595:         SHIN = FSHOUT;
 596:         break;
 597:         case 2:
 598:         SHIN = FSHDIAG;
 599:         break;
 600:         default:
 601:         stderror(ERR_SYSTEM, tempv[0], strerror(errno));
 602:         break;
 603:         }
 604: #ifdef FIOCLEX
 605:     (void) ioctl(SHIN, FIOCLEX, NULL);
 606: #endif
 607:     prompt = 0;
 608:      /* argc not used any more */ tempv++;
 609:     }
 610:     /*
 611:      * Consider input a tty if it really is or we are interactive. but not for
 612:      * editing (christos)
 613:      */
 614:     if (!(intty = isatty(SHIN))) {
 615:     if (adrof(STRedit))
 616:         unsetv(STRedit);
 617:     editing = 0;
 618:     }
 619:     intty |= intact;
 620:     if (intty || (intact && isatty(SHOUT))) {
 621:     if (!batch && (uid != geteuid() || gid != getegid())) {
 622:         errno = EACCES;
 623:         child = 1;      /* So this ... */
 624:         /* ... doesn't return */
 625:         stderror(ERR_SYSTEM, "tcsh", strerror(errno));
 626:     }
 627:     }
 628:     isoutatty = isatty(SHOUT);
 629:     isdiagatty = isatty(SHDIAG);
 630:     /*
 631:      * Decide whether we should play with signals or not. If we are explicitly
 632:      * told (via -i, or -) or we are a login shell (arg0 starts with -) or the
 633:      * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx")
 634:      * Note that in only the login shell is it likely that parent may have set
 635:      * signals to be ignored
 636:      */
 637:     if (loginsh || intact || intty && isatty(SHOUT))
 638:     setintr = 1;
 639:     settell();
 640:     /*
 641:      * Save the remaining arguments in argv.
 642:      */
 643:     setq(STRargv, blk2short(tempv), &shvhed);
 644: 
 645:     /*
 646:      * Set up the prompt.
 647:      */
 648:     if (prompt) {
 649:     set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymarrow));
 650:     /* that's a meta-questionmark */
 651:     set(STRp_2, Strsave(STRmquestion));
 652:     set(STRp_3, Strsave(STRCORRECT));
 653:     }
 654: 
 655:     /*
 656:      * If we are an interactive shell, then start fiddling with the signals;
 657:      * this is a tricky game.
 658:      */
 659:     shpgrp = mygetpgrp();
 660:     opgrp = tpgrp = -1;
 661:     if (setintr) {
 662:     **argv = '-';
 663:     if (!quitit)        /* Wary! */
 664:         (void) signal(SIGQUIT, SIG_IGN);
 665:     (void) sigset(SIGINT, pintr);
 666:     (void) sighold(SIGINT);
 667:     (void) signal(SIGTERM, SIG_IGN);
 668:     if (quitit == 0 && arginp == 0) {
 669: #ifdef SIGTSTP
 670:         (void) signal(SIGTSTP, SIG_IGN);
 671: #endif
 672: #ifdef SIGTTIN
 673:         (void) signal(SIGTTIN, SIG_IGN);
 674: #endif
 675: #ifdef SIGTTOU
 676:         (void) signal(SIGTTOU, SIG_IGN);
 677: #endif
 678:         /*
 679: 	     * Wait till in foreground, in case someone stupidly runs csh &
 680: 	     * dont want to try to grab away the tty.
 681: 	     */
 682:         if (isatty(FSHDIAG))
 683:         f = FSHDIAG;
 684:         else if (isatty(FSHOUT))
 685:         f = FSHOUT;
 686:         else if (isatty(OLDSTD))
 687:         f = OLDSTD;
 688:         else
 689:         f = -1;
 690: 
 691:     retry:
 692: #ifdef BSDJOBS          /* if we have tty job control */
 693:         if ((tpgrp = tcgetpgrp(f)) != -1) {
 694:         if (tpgrp != shpgrp) {
 695:             sigret_t(*old) () = signal(SIGTTIN, SIG_DFL);
 696:             (void) kill(0, SIGTTIN);
 697:             (void) signal(SIGTTIN, old);
 698:             goto retry;
 699:         }
 700:         /*
 701: 		 * Thanks to Matt Day for the POSIX references, and to
 702: 		 * Paul Close for the SGI clarification.
 703: 		 */
 704:         if (setdisc(f) != -1) {
 705:             opgrp = shpgrp;
 706:             shpgrp = getpid();
 707:             tpgrp = shpgrp;
 708:             if (tcsetpgrp(f, shpgrp) == -1) {
 709:             /*
 710: 			 * On hpux 7.03 this fails with EPERM. This happens on
 711: 			 * the 800 when opgrp != shpgrp at this point. (we were
 712: 			 * forked from a non job control shell)
 713: 			 * POSIX 7.2.4, says we failed because the process
 714: 			 * group specified did not belong to a process
 715: 			 * in the same session with the tty. So we set our
 716: 			 * process group and try again.
 717: 			 */
 718:             if (setpgid(0, shpgrp) == -1) {
 719:                 xprintf("setpgid:");
 720:                 goto notty;
 721:             }
 722:             if (tcsetpgrp(f, shpgrp) == -1) {
 723:                 xprintf("tcsetpgrp:");
 724:                 goto notty;
 725:             }
 726:             }
 727:             /*
 728: 		     * We check the process group now. If it is the same, then
 729: 		     * we don't need to set it again. On hpux 7.0 on the 300's
 730: 		     * if we set it again it fails with EPERM. This is the
 731: 		     * correct behavior according to POSIX 4.3.3 if the process
 732: 		     * was a session leader .
 733: 		     */
 734:             else if (shpgrp != mygetpgrp()) {
 735:             if(setpgid(0, shpgrp) == -1) {
 736:                 xprintf("setpgid:");
 737:                 goto notty;
 738:             }
 739:             }
 740: #ifdef IRIS4D
 741:             /*
 742: 		     * But on irix 3.3 we need to set it again, even if it is
 743: 		     * the same. We do that to tell the system that we
 744: 		     * need BSD process group compatibility.
 745: 		     */
 746:             else
 747:             (void) setpgid(0, shpgrp);
 748: #endif
 749: #ifdef FIOCLEX
 750:             (void) ioctl(dcopy(f, FSHTTY), FIOCLEX, NULL);
 751: #else               /* FIOCLEX */
 752:             (void) dcopy(f, FSHTTY);
 753: #endif				/* FIOCLEX */
 754:         }
 755:         else
 756:             tpgrp = -1;
 757:         }
 758:         if (tpgrp == -1) {
 759:     notty:
 760:         xprintf("Warning: no access to tty (%s).\n", strerror(errno));
 761:         xprintf("Thus no job control in this shell.\n");
 762:         /*
 763: 		 * Fix from:Sakari Jalovaara <sja@sirius.hut.fi> if we don't
 764: 		 * have access to tty, disable editing too
 765: 		 */
 766:         if (adrof(STRedit))
 767:             unsetv(STRedit);
 768:         editing = 0;
 769:         }
 770: #else   /* BSDJOBS */       /* don't have job control, so frotz it */
 771:         tpgrp = -1;
 772: #endif				/* BSDJOBS */
 773:     }
 774:     }
 775:     if ((setintr == 0) && (parintr == SIG_DFL))
 776:     setintr = 1;
 777:     (void) sigset(SIGCHLD, pchild); /* while signals not ready */
 778: 
 779:     /*
 780:      * Set an exit here in case of an interrupt or error reading the shell
 781:      * start-up scripts.
 782:      */
 783:     reenter = setexit();    /* PWP */
 784:     haderr = 0;         /* In case second time through */
 785:     if (!fast && reenter == 0) {
 786:     /* Will have value(STRhome) here because set fast if don't */
 787:     {
 788:         int     osetintr = setintr;
 789: 
 790: #ifdef BSDSIGS
 791:         sigmask_t omask = sigblock(sigmask(SIGINT));
 792: #else
 793:         sighold(SIGINT);
 794: #endif
 795:         setintr = 0;
 796: #ifdef _PATH_DOTCSHRC
 797:         (void) srcfile(_PATH_DOTCSHRC, 0, 0);
 798: #endif
 799:         if (!fast && !arginp && !onelflg)
 800:         dohash();
 801: #ifdef _PATH_DOTLOGIN
 802:         if (loginsh)
 803:         (void) srcfile(_PATH_DOTLOGIN, 0, 0);
 804: #endif
 805: #ifdef BSDSIGS
 806:         (void) sigsetmask(omask);
 807: #else
 808:         (void) sigrelse(SIGINT);
 809: #endif
 810:         setintr = osetintr;
 811:     }
 812: #ifdef LOGINFIRST
 813:     if (loginsh)
 814:         (void) srccat(value(STRhome), STRd_login);
 815: #endif
 816:     /* upward compat. */
 817:     if (!srccat(value(STRhome), STRd_tcshrc))
 818:         (void) srccat(value(STRhome), STRd_cshrc);
 819: 
 820:     if (!fast && !arginp && !onelflg && !havhash)
 821:         dohash();
 822:     /*
 823: 	 * Source history before .login so that it is available in .login
 824: 	 */
 825:     dosource(loadhist);
 826: #ifndef LOGINFIRST
 827:     if (loginsh)
 828:         (void) srccat(value(STRhome), STRd_login);
 829: #endif
 830: #ifdef CSHDIRS
 831:     /*
 832: 	 * if dflag then source ~/.cshdirs, but if fast ALWAYS skip the dirs
 833: 	 * restoring. (dflag used to get non-login shells to source the save
 834: 	 * dirs file). Of course, ~/.cshdirs must exist. -strike
 835: 	 */
 836:     {
 837:         extern int bequiet; /* make dirs shut up */
 838:         Char    cshd[BUFSIZ];
 839:         struct stat st;
 840: 
 841:         (void) Strcpy(cshd, value(STRhome));
 842:         (void) Strcat(cshd, STRs_dirs);
 843:         if (!stat(short2str(cshd), &st) &&
 844:         (dflag || loginsh) && !fast) {
 845:         bequiet = 1;
 846:         dosource(loaddirs);
 847:         bequiet = 0;
 848:         }
 849:     }
 850: #endif
 851:     }
 852:     /* Initing AFTER .cshrc is the Right Way */
 853:     if (intty && !arginp) { /* PWP setup stuff */
 854:     ed_I();     /* init the new line editor */
 855: #ifdef SIG_WINDOW
 856:     check_window_size(1);   /* mung environment */
 857: #endif				/* SIG_WINDOW */
 858:     }
 859: 
 860:     /*
 861:      * Now are ready for the -v and -x flags
 862:      */
 863:     if (nverbose)
 864:     setNS(STRverbose);
 865:     if (nexececho)
 866:     setNS(STRecho);
 867:     /*
 868:      * All the rest of the world is inside this call. The argument to process
 869:      * indicates whether it should catch "error unwinds".  Thus if we are a
 870:      * interactive shell our call here will never return by being blown past on
 871:      * an error.
 872:      */
 873:     process(setintr);
 874: 
 875:     /*
 876:      * Mop-up.
 877:      */
 878:     if (intty) {
 879:     if (loginsh) {
 880:         xprintf("logout\n");
 881:         (void) close(SHIN);
 882:         child = 1;
 883: #ifdef TESLA
 884:         do_logout = 1;
 885: #endif				/* TESLA */
 886:         goodbye();
 887:     }
 888:     else {
 889:         xprintf("exit\n");
 890:     }
 891:     }
 892: #ifdef CSHDIRS
 893:     /*
 894:      * save the directory stack -strike
 895:      */
 896:     recdirs();
 897: #endif
 898:     rechist();
 899:     exitstat();
 900:     return (0);
 901: }
 902: 
 903: void
 904: untty()
 905: {
 906: #ifdef BSDJOBS
 907:     if (tpgrp > 0) {
 908:     (void) setpgid(0, opgrp);
 909:     (void) tcsetpgrp(FSHTTY, opgrp);
 910:     (void) resetdisc(FSHTTY);
 911:     }
 912: #endif				/* BSDJOBS */
 913: }
 914: 
 915: void
 916: importpath(cp)
 917:     Char   *cp;
 918: {
 919:     register int i = 0;
 920:     register Char *dp;
 921:     register Char **pv;
 922:     int     c;
 923: 
 924:     for (dp = cp; *dp; dp++)
 925:     if (*dp == ':')
 926:         i++;
 927:     /*
 928:      * i+2 where i is the number of colons in the path. There are i+1
 929:      * directories in the path plus we need room for a zero terminator.
 930:      */
 931:     pv = (Char **) xcalloc((size_t) (i + 2), sizeof(Char **));
 932:     dp = cp;
 933:     i = 0;
 934:     if (*dp)
 935:     for (;;) {
 936:         if ((c = *dp) == ':' || c == 0) {
 937:         *dp = 0;
 938:         pv[i++] = Strsave(*cp ? cp : STRdot);
 939:         if (c) {
 940:             cp = dp + 1;
 941:             *dp = ':';
 942:         }
 943:         else
 944:             break;
 945:         }
 946:         dp++;
 947:     }
 948:     pv[i] = 0;
 949:     set1(STRpath, pv, &shvhed);
 950: }
 951: 
 952: /*
 953:  * Source to the file which is the catenation of the argument names.
 954:  */
 955: static int
 956: srccat(cp, dp)
 957:     Char   *cp, *dp;
 958: {
 959:     if (cp[0] == '/' && cp[1] == '\0')
 960:     return srcfile(short2str(dp), mflag ? 0 : 1, 0);
 961:     else {
 962:     register Char *ep = Strspl(cp, dp);
 963:     char   *ptr = short2str(ep);
 964: 
 965:     xfree((ptr_t) ep);
 966:     return srcfile(ptr, mflag ? 0 : 1, 0);
 967:     }
 968: }
 969: 
 970: /*
 971:  * Source to a file putting the file descriptor in a safe place (> 2).
 972:  */
 973: static int
 974: srcfile(f, onlyown, flag)
 975:     char   *f;
 976:     bool    onlyown, flag;
 977: {
 978:     register int unit;
 979: 
 980:     if ((unit = open(f, O_RDONLY)) == -1)
 981:     return 0;
 982:     unit = dmove(unit, -1);
 983: 
 984: #ifdef FIOCLEX
 985:     (void) ioctl(unit, FIOCLEX, NULL);
 986: #endif
 987:     srcunit(unit, onlyown, flag);
 988:     return 1;
 989: }
 990: 
 991: /*
 992:  * Source to a unit.  If onlyown it must be our file or our group or
 993:  * we don't chance it.	This occurs on ".cshrc"s and the like.
 994:  */
 995: int     insource;
 996: static void
 997: srcunit(unit, onlyown, hflg)
 998:     register int unit;
 999:     bool    onlyown, hflg;
1000: {
1001:     /*
1002:      * PWP: this is arranged like this so that an optimizing comp_r won't go
1003:      * and put things like oSHIN in a register that longjmp() restores.  The
1004:      * problem is that on my Sun 3/50, gcc will put oSHIN in a register.  That
1005:      * would be OK, but setjmp() saves ALL of the registers and longjmp()
1006:      * restores ALL of them, thus if you do a setjmp(), set oSHIN to something
1007:      * (like SHIN), then do a longjmp(), the value of oSHIN will magically
1008:      * become -1 again.
1009:      *
1010:      * Perhaps setjmp() should only save the stack pointer, frame pointer, and
1011:      * program counter...
1012:      */
1013: 
1014:     /* We have to push down a lot of state here */
1015:     /* All this could go into a structure */
1016:     int     oSHIN = -1, oldintty = intty, oinsource = insource;
1017:     struct whyle *oldwhyl = whyles;
1018:     Char   *ogointr = gointr, *oarginp = arginp;
1019:     Char   *oevalp = evalp, **oevalvec = evalvec;
1020:     int     oonelflg = onelflg;
1021:     bool    oenterhist = enterhist;
1022:     char    OHIST = HIST;
1023:     bool    otell = cantell;
1024:     struct Bin saveB;
1025: #ifdef BSDSIGS
1026:     volatile sigmask_t omask = (sigmask_t) 0;
1027: #endif
1028:     jmp_buf oldexit;
1029: 
1030:     /* The (few) real local variables */
1031:     int     my_reenter;
1032: 
1033:     if (unit < 0)
1034:     return;
1035:     if (didfds)
1036:     donefds();
1037:     if (onlyown) {
1038:     struct stat stb;
1039: 
1040:     if (fstat(unit, &stb) < 0
1041:     /* || (stb.st_uid != uid && stb.st_gid != gid) */
1042:         ) {
1043:         (void) close(unit);
1044:         return;
1045:     }
1046:     }
1047: 
1048:     /*
1049:      * There is a critical section here while we are pushing down the input
1050:      * stream since we have stuff in different structures. If we weren't
1051:      * careful an interrupt could corrupt SHIN's Bin structure and kill the
1052:      * shell.
1053:      *
1054:      * We could avoid the critical region by grouping all the stuff in a single
1055:      * structure and pointing at it to move it all at once.  This is less
1056:      * efficient globally on many variable references however.
1057:      */
1058:     insource = 1;
1059:     getexit(oldexit);
1060: 
1061:     if (setintr)
1062: #ifdef BSDSIGS
1063:     omask = sigblock(sigmask(SIGINT));
1064: #else
1065:     (void) sighold(SIGINT);
1066: #endif
1067:     /*
1068:      * Bugfix for running out of memory by: Jak Kirman
1069:      * <jak%cs.brown.edu@RELAY.CS.NET>.  Solution: pay attention to what
1070:      * setexit() is returning because my_reenter _may_ be in a register, and
1071:      * thus restored to 0 on a longjump(). (PWP: insert flames about
1072:      * comp_r-dependant code here) PWP: THANKS LOTS !!!
1073:      */
1074:     /* Setup the new values of the state stuff saved above */
1075:     copy((char *) &(saveB), (char *) &B, sizeof(B));
1076:     fbuf = NULL;
1077:     fseekp = feobp = fblocks = 0;
1078:     oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
1079:     intty = isatty(SHIN), whyles = 0, gointr = 0;
1080:     evalvec = 0;
1081:     evalp = 0;
1082:     enterhist = hflg;
1083:     if (enterhist)
1084:     HIST = '\0';
1085: 
1086:     /*
1087:      * Now if we are allowing commands to be interrupted, we let ourselves be
1088:      * interrupted.
1089:      */
1090:     if (setintr)
1091: #ifdef BSDSIGS
1092:     (void) sigsetmask(omask);
1093: #else
1094:     (void) sigrelse(SIGINT);
1095: #endif
1096:     settell();
1097: 
1098: /* PWP: think of this as like a LISP (unwind-protect ...) */
1099: /* thanks to Diana Smetters for pointing out how this _should_ be written */
1100:     if ((my_reenter = setexit()) == 0) {
1101:     process(0);     /* 0 -> blow away on errors */
1102:     }
1103: 
1104:     if (setintr)
1105: #ifdef BSDSIGS
1106:     (void) sigsetmask(omask);
1107: #else
1108:     (void) sigrelse(SIGINT);
1109: #endif
1110:     if (oSHIN >= 0) {
1111:     register int i;
1112: 
1113:     /* We made it to the new state... free up its storage */
1114:     /* This code could get run twice but xfree doesn't care */
1115:     for (i = 0; i < fblocks; i++)
1116:         xfree((ptr_t) fbuf[i]);
1117:     xfree((ptr_t) fbuf);
1118: 
1119:     /* Reset input arena */
1120:     copy((char *) &B, (char *) &(saveB), sizeof(B));
1121: 
1122:     (void) close(SHIN), SHIN = oSHIN;
1123:     arginp = oarginp, onelflg = oonelflg;
1124:     evalp = oevalp, evalvec = oevalvec;
1125:     intty = oldintty, whyles = oldwhyl, gointr = ogointr;
1126:     if (enterhist)
1127:         HIST = OHIST;
1128:     enterhist = oenterhist;
1129:     cantell = otell;
1130:     }
1131: 
1132:     resexit(oldexit);
1133:     /*
1134:      * If process reset() (effectively an unwind) then we must also unwind.
1135:      */
1136:     if (my_reenter)
1137:     stderror(ERR_SILENT);
1138:     insource = oinsource;
1139: }
1140: 
1141: void
1142: rechist()
1143: {
1144:     Char    buf[BUFSIZ];
1145:     int     fp, ftmp, oldidfds;
1146: 
1147:     if (!fast) {
1148:     if (value(STRsvhist)[0] == '\0')
1149:         return;
1150:     (void) Strcpy(buf, value(STRhome));
1151:     (void) Strcat(buf, STRs_hist);
1152:     fp = creat(short2str(buf), 0600);
1153:     if (fp == -1)
1154:         return;
1155:     oldidfds = didfds;
1156:     didfds = 0;
1157:     ftmp = SHOUT;
1158:     SHOUT = fp;
1159:     (void) Strcpy(buf, value(STRsvhist));
1160:     dumphist[2] = buf;
1161:     dohist(dumphist);
1162:     (void) close(fp);
1163:     SHOUT = ftmp;
1164:     didfds = oldidfds;
1165:     }
1166: }
1167: 
1168: void
1169: goodbye()
1170: {
1171:     rechist();
1172: 
1173: #ifdef CSHDIRS
1174:     recdirs();
1175: #endif
1176: 
1177:     if (loginsh) {
1178:     (void) signal(SIGQUIT, SIG_IGN);
1179:     (void) sigset(SIGINT, SIG_IGN);
1180:     (void) signal(SIGTERM, SIG_IGN);
1181:     setintr = 0;        /* No interrupts after "logout" */
1182:     if (!(adrof(STRlogout)))
1183:         set(STRlogout, STRnormal);
1184: #ifdef _PATH_DOTLOGOUT
1185:     (void) srcfile(_PATH_DOTLOGOUT, 0, 0);
1186: #endif
1187:     if (adrof(STRhome))
1188:         (void) srccat(value(STRhome), STRs_logout);
1189: #ifdef TESLA
1190:     do_logout = 1;
1191: #endif				/* TESLA */
1192:     }
1193:     exitstat();
1194: }
1195: 
1196: void
1197: exitstat()
1198: {
1199: 
1200: #ifdef PROF
1201:     monitor(0);
1202: #endif
1203:     /*
1204:      * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit
1205:      * directly because we poke child here. Otherwise we might continue
1206:      * unwarrantedly (sic).
1207:      */
1208:     child = 1;
1209:     xexit(getn(value(STRstatus)));
1210: }
1211: 
1212: /*
1213:  * in the event of a HUP we want to save the history
1214:  */
1215: static  sigret_t
1216: phup(i)
1217: int i;
1218: {
1219:     rechist();
1220: #ifdef CSHDIRS
1221:     /*
1222:      * save the directory stack on HUP - strike
1223:      */
1224:     recdirs();
1225: #endif
1226:     xexit(i);
1227: #ifndef SIGVOID
1228:     return (i);
1229: #endif
1230: }
1231: 
1232: Char   *jobargv[2] = {STRjobs, 0};
1233: 
1234: /*
1235:  * Catch an interrupt, e.g. during lexical input.
1236:  * If we are an interactive shell, we reset the interrupt catch
1237:  * immediately.  In any case we drain the shell output,
1238:  * and finally go through the normal error mechanism, which
1239:  * gets a chance to make the shell go away.
1240:  */
1241: int     just_signaled;      /* bugfix by Michael Bloom (mg@ttidca.TTI.COM) */
1242: 
1243: sigret_t
1244: pintr(i)
1245: int i;
1246: {
1247:     just_signaled = 1;
1248:     pintr1(1);
1249: #ifndef SIGVOID
1250:     return (i);
1251: #endif
1252: }
1253: 
1254: void
1255: pintr1(wantnl)
1256:     bool    wantnl;
1257: {
1258:     register Char **v;
1259: #ifdef BSDSIGS
1260:     sigmask_t omask;
1261: #endif
1262: 
1263: #ifdef BSDSIGS
1264:     omask = sigblock((sigmask_t) 0);
1265: #endif
1266:     if (setintr) {
1267: #ifdef BSDSIGS
1268:     (void) sigsetmask(omask & ~sigmask(SIGINT));
1269: #else
1270:     (void) sigrelse(SIGINT);
1271: #endif
1272:     if (pjobs) {
1273:         pjobs = 0;
1274:         xprintf("\n");
1275:         dojobs(jobargv);
1276:         stderror(ERR_NAME | ERR_INTR);
1277:     }
1278:     }
1279:     /* JV - Make sure we shut off inputl */
1280:     {
1281:     extern Char GettingInput;
1282: 
1283:     (void) Cookedmode();
1284:     GettingInput = 0;
1285:     }
1286: #ifdef BSDSIGS
1287:     (void) sigsetmask(omask & ~sigmask(SIGCHLD));
1288: #else
1289:     if (setintr)
1290:     (void) sighold(SIGINT);
1291:     (void) sigrelse(SIGCHLD);
1292: #endif
1293:     draino();
1294:     (void) endpwent();
1295: 
1296:     /*
1297:      * If we have an active "onintr" then we search for the label. Note that if
1298:      * one does "onintr -" then we shan't be interruptible so we needn't worry
1299:      * about that here.
1300:      */
1301:     if (gointr) {
1302:     search(T_GOTO, 0, gointr);
1303:     timflg = 0;
1304:     if (v = pargv)
1305:         pargv = 0, blkfree(v);
1306:     if (v = gargv)
1307:         gargv = 0, blkfree(v);
1308:     reset();
1309:     }
1310:     else if (intty && wantnl) {
1311:     /* xprintf("\n"); *//* Some like this, others don't */
1312:     (void) putraw('\r');
1313:     (void) putraw('\n');
1314:     }
1315:     stderror(ERR_SILENT);
1316: }
1317: 
1318: /*
1319:  * Process is the main driving routine for the shell.
1320:  * It runs all command processing, except for those within { ... }
1321:  * in expressions (which is run by a routine evalav in sh.exp.c which
1322:  * is a stripped down process), and `...` evaluation which is run
1323:  * also by a subset of this code in sh.glob.c in the routine backeval.
1324:  *
1325:  * The code here is a little strange because part of it is interruptible
1326:  * and hence freeing of structures appears to occur when none is necessary
1327:  * if this is ignored.
1328:  *
1329:  * Note that if catch is not set then we will unwind on any error.
1330:  * If an end-of-file occurs, we return.
1331:  */
1332: struct command *savet = NULL;
1333: void
1334: process(catch)
1335:     bool    catch;
1336: {
1337:     extern char Expand;
1338:     jmp_buf osetexit;
1339:     /* PWP: This might get nuked my longjmp so don't make it a register var */
1340:     struct command *t = savet;
1341: 
1342:     savet = NULL;
1343:     getexit(osetexit);
1344:     for (;;) {
1345: 
1346:     pendjob();
1347:     paraml.next = paraml.prev = &paraml;
1348:     paraml.word = STRNULL;
1349:     (void) setexit();
1350:     justpr = enterhist; /* execute if not entering history */
1351: 
1352:     /*
1353: 	 * Interruptible during interactive reads
1354: 	 */
1355:     if (setintr)
1356: #ifdef BSDSIGS
1357:         (void) sigsetmask(sigblock((sigmask_t) 0) & ~sigmask(SIGINT));
1358: #else
1359:         (void) sigrelse(SIGINT);
1360: #endif
1361: 
1362: 
1363:     /*
1364: 	 * For the sake of reset()
1365: 	 */
1366:     freelex(&paraml);
1367:     if (savet)
1368:         freesyn(savet), savet = NULL;
1369: 
1370:     if (haderr) {
1371:         if (!catch) {
1372:         /* unwind */
1373:         doneinp = 0;
1374:         savet = t;
1375:         resexit(osetexit);
1376:         reset();
1377:         }
1378:         haderr = 0;
1379:         /*
1380: 	     * Every error is eventually caught here or the shell dies.  It is
1381: 	     * at this point that we clean up any left-over open files, by
1382: 	     * closing all but a fixed number of pre-defined files.  Thus
1383: 	     * routines don't have to worry about leaving files open due to
1384: 	     * deeper errors... they will get closed here.
1385: 	     */
1386:         closem();
1387:         continue;
1388:     }
1389:     if (doneinp) {
1390:         doneinp = 0;
1391:         break;
1392:     }
1393:     if (chkstop)
1394:         chkstop--;
1395:     if (neednote)
1396:         pnote();
1397:     if (intty && prompt && evalvec == 0) {
1398:         mailchk();
1399:         /*
1400: 	     * Watch for logins/logouts. Next is scheduled commands stored
1401: 	     * previously using "sched." Then execute periodic commands.
1402: 	     * Following that, the prompt precmd is run.
1403: 	     */
1404:         watch_login();
1405:         sched_run();
1406:         period_cmd();
1407:         precmd();
1408:         /*
1409: 	     * If we are at the end of the input buffer then we are going to
1410: 	     * read fresh stuff. Otherwise, we are rereading input and don't
1411: 	     * need or want to prompt.
1412: 	     */
1413:         if (fseekp == feobp)
1414:         printprompt(0, NULL);
1415:         flush();
1416:         setalarm();
1417:     }
1418:     if (seterr) {
1419:         xfree((ptr_t) seterr);
1420:         seterr = NULL;
1421:     }
1422: 
1423:     /*
1424: 	 * Echo not only on VERBOSE, but also with history expansion. If there
1425: 	 * is a lexical error then we forego history echo.
1426: 	 */
1427:     if (lex(&paraml) && !seterr && intty && !tellwhat && !Expand ||
1428:         adrof(STRverbose)) {
1429:         haderr = 1;
1430:         prlex(&paraml);
1431:         haderr = 0;
1432:     }
1433:     (void) alarm(0);    /* Autologout OFF */
1434: 
1435:     /*
1436: 	 * The parser may lose space if interrupted.
1437: 	 */
1438:     if (setintr)
1439: #ifdef BSDSIGS
1440:         (void) sigblock(sigmask(SIGINT));
1441: #else
1442:         (void) sighold(SIGINT);
1443: #endif
1444: 
1445:     /*
1446: 	 * Save input text on the history list if reading in old history, or it
1447: 	 * is from the terminal at the top level and not in a loop.
1448: 	 *
1449: 	 * PWP: entry of items in the history list while in a while loop is done
1450: 	 * elsewhere...
1451: 	 */
1452:     if (enterhist || catch && intty && !whyles && !tellwhat)
1453:         savehist(&paraml);
1454: 
1455:     if (Expand && seterr)
1456:         Expand = 0;
1457: 
1458:     /*
1459: 	 * Print lexical error messages, except when sourcing history lists.
1460: 	 */
1461:     if (!enterhist && seterr)
1462:         stderror(ERR_OLD);
1463: 
1464:     /*
1465: 	 * If had a history command :p modifier then this is as far as we
1466: 	 * should go
1467: 	 */
1468:     if (justpr)
1469:         reset();
1470: 
1471:     /*
1472: 	 * If had a tellwhat from twenex() then do
1473: 	 */
1474:     if (tellwhat) {
1475:         tellmewhat(&paraml);
1476:         reset();
1477:     }
1478: 
1479:     alias(&paraml);
1480: 
1481: #ifdef BSDJOBS
1482:     /*
1483: 	 * If we are interactive, try to continue jobs that we have stopped
1484: 	 */
1485:     if (prompt)
1486:         con_jobs(&paraml);
1487: #endif				/* BSDJOBS */
1488: 
1489:     /*
1490: 	 * Check to see if the user typed "rm * .o" or something
1491: 	 */
1492:     if (prompt)
1493:         rmstar(&paraml);
1494:     /*
1495: 	 * Parse the words of the input into a parse tree.
1496: 	 */
1497:     savet = syntax(paraml.next, &paraml, 0);
1498:     if (seterr)
1499:         stderror(ERR_OLD);
1500: 
1501:     /*
1502: 	 * Execute the parse tree From: Michael Schroeder
1503: 	 * <mlschroe@immd4.informatik.uni-erlangen.de> was execute(t, tpgrp);
1504: 	 */
1505:     execute(savet, (tpgrp > 0 ? tpgrp : -1), NULL, NULL);
1506: 
1507:     /*
1508: 	 * Made it!
1509: 	 */
1510:     freelex(&paraml);
1511:     freesyn(savet), savet = NULL;
1512: #ifdef SIG_WINDOW
1513:     if (catch && intty && !whyles && !tellwhat)
1514:         window_change(0);   /* for window systems */
1515: #endif				/* SIG_WINDOW */
1516:     }
1517:     savet = t;
1518:     resexit(osetexit);
1519: }
1520: 
1521: void
1522: dosource(t)
1523:     register Char **t;
1524: {
1525:     register Char *f;
1526:     bool    hflg = 0;
1527:     Char    buf[BUFSIZ];
1528: 
1529:     t++;
1530:     if (*t && eq(*t, STRmh)) {
1531:     if (*++t == NOSTR)
1532:         stderror(ERR_NAME | ERR_HFLAG);
1533:     hflg++;
1534:     }
1535:     (void) Strcpy(buf, *t);
1536:     f = globone(buf, G_ERROR);
1537:     (void) strcpy((char *) buf, short2str(f));
1538:     xfree((ptr_t) f);
1539:     if (!srcfile((char *) buf, 0, hflg) && !hflg)
1540:     stderror(ERR_SYSTEM, (char *) buf, strerror(errno));
1541: }
1542: 
1543: /*
1544:  * Check for mail.
1545:  * If we are a login shell, then we don't want to tell
1546:  * about any mail file unless its been modified
1547:  * after the time we started.
1548:  * This prevents us from telling the user things he already
1549:  * knows, since the login program insists on saying
1550:  * "You have mail."
1551:  */
1552: static void
1553: mailchk()
1554: {
1555:     register struct varent *v;
1556:     register Char **vp;
1557:     time_t  t;
1558:     int     intvl, cnt;
1559:     struct stat stb;
1560:     bool    new;
1561: 
1562:     v = adrof(STRmail);
1563:     if (v == 0)
1564:     return;
1565:     (void) time(&t);
1566:     vp = v->vec;
1567:     cnt = blklen(vp);
1568:     intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
1569:     if (intvl < 1)
1570:     intvl = 1;
1571:     if (chktim + intvl > t)
1572:     return;
1573:     for (; *vp; vp++) {
1574:     if (stat(short2str(*vp), &stb) < 0)
1575:         continue;
1576: #if defined(BSDTIMES) || defined(_SEQUENT_)
1577:     new = stb.st_mtime > time0.tv_sec;
1578: #else
1579:     new = stb.st_mtime > time0;
1580: #endif
1581:     if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
1582:         (stb.st_atime < chktim && stb.st_mtime < chktim) ||
1583:         loginsh && !new)
1584:         continue;
1585:     if (cnt == 1)
1586:         xprintf("You have %smail.\n", new ? "new " : "");
1587:     else
1588:         xprintf("%s in %s.\n", new ? "New mail" : "Mail",
1589:             short2str(*vp));
1590:     }
1591:     chktim = t;
1592: }
1593: 
1594: /*
1595:  * Extract a home directory from the password file
1596:  * The argument points to a buffer where the name of the
1597:  * user whose home directory is sought is currently.
1598:  * We write the home directory of the user back there.
1599:  */
1600: int
1601: gethdir(home)
1602:     Char   *home;
1603: {
1604:     Char   *h;
1605: 
1606:     /*
1607:      * Is it us?
1608:      */
1609:     if (*home == '\0') {
1610:     if (h = value(STRhome)) {
1611:         (void) Strcpy(home, h);
1612:         return 0;
1613:     }
1614:     else
1615:         return 1;
1616:     }
1617: 
1618:     /*
1619:      * Look in the cache
1620:      */
1621:     if ((h = gettilde(home)) == NULL)
1622:     return 1;
1623:     else {
1624:     (void) Strcpy(home, h);
1625:     return 0;
1626:     }
1627: }
1628: 
1629: /*
1630:  * Move the initial descriptors to their eventual
1631:  * resting places, closing all other units.
1632:  */
1633: void
1634: initdesc()
1635: {
1636: 
1637:     didfds = 0;         /* 0, 1, 2 aren't set up */
1638: #ifdef FIOCLEX
1639:     (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, NULL);
1640:     (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL);
1641:     (void) ioctl(SHDIAG = dcopy(2, FSHDIAG), FIOCLEX, NULL);
1642:     (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL);
1643: #else
1644:     didcch = 0;         /* Havent closed for child */
1645:     SHIN = dcopy(0, FSHIN);
1646:     SHOUT = dcopy(1, FSHOUT);
1647:     isoutatty = isatty(SHOUT);
1648:     SHDIAG = dcopy(2, FSHDIAG);
1649:     isdiagatty = isatty(SHDIAG);
1650:     OLDSTD = dcopy(SHIN, FOLDSTD);
1651: #endif
1652:     closem();
1653: }
1654: 
1655: 
1656: void
1657: #ifdef PROF
1658: done(i)
1659: #else
1660: xexit(i)
1661: #endif
1662:     int     i;
1663: {
1664: #ifdef TESLA
1665:     if (loginsh && do_logout) {
1666:     /* this is to send hangup signal to the develcon */
1667:     /* we toggle DTR. clear dtr - sleep 1 - set dtr */
1668:     /* ioctl will return ENOTTY for pty's but we ignore it 	 */
1669:     /* exitstat will run after disconnect */
1670:     /* we sleep for 2 seconds to let things happen in */
1671:     /* .logout and rechist() */
1672: #ifdef TIOCCDTR
1673:     (void) sleep(2);
1674:     (void) ioctl(FSHTTY, TIOCCDTR, NULL);
1675:     (void) sleep(1);
1676:     (void) ioctl(FSHTTY, TIOCSDTR, NULL);
1677: #endif				/* TIOCCDTR */
1678:     }
1679: #endif				/* TESLA */
1680: 
1681:     untty();
1682:     _exit(i);
1683: }
1684: 
1685: static Char **
1686: d_path()
1687: {
1688:     char   *ptr;
1689:     Char  **blk, **blkp;
1690:     struct stat stb;
1691: 
1692:     blkp = blk = (Char **) xmalloc((size_t) sizeof(Char *) * 10);
1693: 
1694: #ifndef DOTLAST
1695:     *blkp++ = Strsave(STRdot);
1696: #endif
1697: 
1698: #define DIRAPPEND(a)  \
1699:     if (stat(ptr = a, &stb) == 0 && S_ISDIR(stb.st_mode)) \
1700:         *blkp++ = SAVE(ptr)
1701: 
1702: #ifdef _PATH_LOCAL
1703:     DIRAPPEND(_PATH_LOCAL);
1704: #endif
1705: 
1706: #ifdef _PATH_USRUCB
1707:     DIRAPPEND(_PATH_USRUCB);
1708: #endif
1709: 
1710: #ifdef _PATH_USRBSD
1711:     DIRAPPEND(_PATH_USRBSD);
1712: #endif
1713: 
1714: #ifdef _PATH_BIN
1715:     DIRAPPEND(_PATH_BIN);
1716: #endif
1717: 
1718: #ifdef _PATH_USRBIN
1719:     DIRAPPEND(_PATH_USRBIN);
1720: #endif
1721: 
1722: #undef DIRAPPEND
1723: 
1724: #ifdef DOTLAST
1725:     *blkp++ = Strsave(STRdot);
1726: #endif
1727:     *blkp = NULL;
1728:     return (blk);
1729: }

Defined functions

d_path defined in line 1685; used 1 times
done defined in line 1656; used 9 times
dosource defined in line 1521; used 3 times
gethdir defined in line 1600; used 2 times
goodbye defined in line 1168; used 4 times
importpath defined in line 915; used 3 times
initdesc defined in line 1633; used 2 times
mailchk defined in line 1552; used 1 times
main defined in line 124; never used
phup defined in line 1215; used 3 times
pintr defined in line 1243; used 1 times
pintr1 defined in line 1254; used 2 times
process defined in line 1333; used 3 times
rcsid defined in line 45; never used
rechist defined in line 1141; used 5 times
srccat defined in line 955; used 5 times
srcfile defined in line 973; used 6 times
srcunit defined in line 996; used 1 times
untty defined in line 903; used 4 times
xexit defined in line 1656; never used

Defined variables

batch defined in line 108; used 3 times
copyright defined in line 39; never used
dflag defined in line 95; used 2 times
do_logout defined in line 87; used 4 times
dumphist defined in line 90; used 2 times
enterhist defined in line 111; used 8 times
fast defined in line 107; used 7 times
insource defined in line 995; used 3 times
jobargv defined in line 1232; used 1 times
just_signaled defined in line 1241; used 3 times
loaddirs defined in line 94; used 1 times
loadhist defined in line 91; used 1 times
mflag defined in line 109; used 3 times
nexececho defined in line 105; used 2 times
nofile defined in line 102; used 10 times
nverbose defined in line 104; used 2 times
prompt defined in line 110; used 8 times
quitit defined in line 106; used 5 times
reenter defined in line 103; used 2 times
savet defined in line 1332; used 11 times
t_period defined in line 113; used 1 times
tellwhat defined in line 112; used 4 times
use_fork defined in line 99; used 1 times

Defined macros

DIRAPPEND defined in line 1698; used 6 times
Last modified: 1994-01-03
Generated: 2016-12-26
Generated by src2html V0.67
page hit count: 8294
Valid CSS Valid XHTML 1.0 Strict