home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Professional / OS2PRO194.ISO / os2 / sysutils / kornshel / main.c < prev    next >
C/C++ Source or Header  |  1992-09-26  |  11KB  |  546 lines

  1. /*
  2.  * startup, main loop, enviroments and error handling
  3.  */
  4.  
  5. #ifndef lint
  6. static char *RCSid = "$Id: main.c,v 1.6 1992/08/12 14:15:39 sjg Exp $";
  7. #endif
  8.  
  9. #define    EXTERN                /* define EXTERNs in sh.h */
  10.  
  11. #include "stdh.h"
  12. #ifndef OS2
  13. #include <unistd.h>
  14. #endif
  15. #include <fcntl.h>
  16. #include <signal.h>
  17. #include <errno.h>
  18. #include <setjmp.h>
  19. #include <time.h>
  20. #include "sh.h"
  21.  
  22. #if !defined(HAVE_REMOVE) && !defined(remove)
  23. #define remove(x)    unlink(x)
  24. #endif
  25.  
  26. /*
  27.  * global data
  28.  */
  29.  
  30. Area    aperm;
  31.  
  32. static    void    reclaim ARGS((void));
  33. static    int    loginshell;
  34.  
  35. #ifdef EDIT
  36. void x_init();
  37. #endif
  38.  
  39. /*
  40.  * shell initialization
  41.  */
  42.  
  43. static    char    initifs [] = "IFS= \t\n"; /* must be R/W */
  44.  
  45. static    const    char   initsubs [] =
  46. #ifdef sun                /* sun's don't have a real /bin */
  47.   "${SHELL:=/bin/sh} ${PATH:=/usr/bin:/usr/ucb:.} ${HOME:=/} ${PS1:=$ } ${PS2:=> } ${PS3:=#? } ${MAILCHECK:=600}";
  48. #else
  49.   "${SHELL:=/bin/sh} ${PATH:=/bin:/usr/bin:.} ${HOME:=/} ${PS1:=$ } ${PS2:=> } ${PS3:=#? } ${MAILCHECK:=600}";
  50. #endif
  51.  
  52. static    const    char *initcoms [] = {
  53.     "cd", ".", NULL,        /* set up $PWD */
  54.     "typeset", "-x", "SHELL", "PATH", "HOME", NULL,
  55.     "typeset", "-r", "PWD", "OLDPWD", NULL,
  56.     "typeset", "-i", "SECONDS=0", "OPTIND=1", NULL,
  57.     "alias",
  58.       "integer=typeset -i", "pwd=print -r \"$PWD\"",
  59.       "history=fc -l", "r=fc -e -", "nohup=nohup ",
  60.       "login=exec login", "newgrp=exec newgrp",
  61.       "type=whence -v", "functions=typeset -f",
  62.       "echo=print", "true=:", "false=let", "[=\\[",
  63. #ifdef JOBS
  64.       "suspend=kill -STOP $$",
  65. #endif
  66.       NULL,
  67.  
  68.     
  69.     NULL
  70. };
  71.  
  72. #ifdef USE_TRACE
  73. /*
  74.  * use SIGUSR1 to bump up Trace_level
  75.  * use SIGUSR2 to clear Trace_level
  76.  */
  77. void
  78. set_TraceLev(sig)
  79.   int sig;
  80. {
  81.   switch(sig)
  82.   {
  83.   case SIGUSR1:
  84.     Trace_level++;
  85.     break;
  86.   case SIGUSR2:
  87.     Trace_level = 0;
  88.     break;
  89.   }
  90. #if defined(_SYSV) && !defined(USE_SIGACT)
  91.   if (sig > 0)
  92.     (void) signal(sig, set_TraceLev);
  93. #endif
  94.   return;
  95. }
  96. #endif
  97.  
  98. main(argc, argv, envp)
  99.     int argc;
  100.     register char **argv;
  101.     char **envp;
  102. {
  103.     register int i;
  104.     register char *arg;
  105.     int cflag = 0, qflag = 0, fflag = 0;
  106.     int argi;
  107.     char *name;
  108.     register Source *s;
  109.     register struct block *l = &globals;
  110.     register char **wp0, **wp;
  111.     extern char ksh_version [];
  112.     extern time_t time();
  113.  
  114. #ifdef USE_SIGACT
  115.     sigemptyset(&Sigact.sa_mask);
  116.     sigemptyset(&Sigact_dfl.sa_mask);
  117.     sigemptyset(&Sigact_ign.sa_mask);
  118.     sigemptyset(&Sigact_trap.sa_mask);
  119.     Sigact.sa_flags = 0;
  120.     Sigact_dfl.sa_flags = 0;
  121.     Sigact_ign.sa_flags = 0;
  122.     Sigact_trap.sa_flags = 0;
  123.     Sigact_dfl.sa_handler = SIG_DFL;
  124.     Sigact_ign.sa_handler = SIG_IGN;
  125.     Sigact_trap.sa_handler = trapsig;
  126. #endif
  127.     ainit(&aperm);        /* initialize permanent Area */
  128.  
  129. #ifndef F_SETFD
  130.   init_clexec();
  131. #endif
  132.     /* set up base enviroment */
  133.     e.type = E_NONE;
  134.     ainit(&e.area);
  135.     e.loc = l;
  136.     e.savefd = NULL;
  137.     e.oenv = NULL;
  138.  
  139.     initctypes();
  140.  
  141.     /* open file streams for fd's 0,1,2 */
  142.     fopenshf(0);    fopenshf(1);    fopenshf(2);
  143.  
  144.     /* set up variable and command dictionaries */
  145.     newblock();        /* set up global l->vars and l->funs */
  146.     tinit(&commands, APERM);
  147.     tinit(&builtins, APERM);
  148.     tinit(&lexicals, APERM);
  149.     tinit(&homedirs, APERM);
  150.  
  151.     init_histvec();
  152.     
  153.     /* import enviroment */
  154.     if (envp != NULL)
  155.         for (wp = envp; *wp != NULL; wp++)
  156.             import(*wp);
  157.  
  158.     kshpid = getpid();
  159.     typeset(initifs, 0, 0);    /* for security */
  160.     typeset(ksh_version, 0, 0); /* RDONLY */
  161.  
  162. #ifdef USE_TRACE
  163. #ifdef USE_SIGACT
  164.     Sigact.sa_handler = set_TraceLev;
  165.     sigaction(SIGUSR1, &Sigact, NULL);
  166.     sigaction(SIGUSR2, &Sigact, NULL);
  167. #else
  168.     (void) signal(SIGUSR1, set_TraceLev);
  169.     (void) signal(SIGUSR2, set_TraceLev);
  170. #endif
  171.     _TRACE(0, ("Traces enabled.")); /* allow _TRACE to setup */
  172. #endif
  173.  
  174.     /* define shell keywords */
  175.     keywords();
  176.  
  177.     /* define built-in commands */
  178.     for (i = 0; shbuiltins[i].name != NULL; i++)
  179.         builtin(shbuiltins[i].name, shbuiltins[i].func);
  180.     for (i = 0; kshbuiltins[i].name != NULL; i++)
  181.         builtin(kshbuiltins[i].name, kshbuiltins[i].func);
  182.  
  183.     /* assign default shell variable values */
  184.     substitute(initsubs, 0);
  185.     setint(typeset("PPID", INTEGER, 0), (long) getppid());
  186.     typeset("PPID", RDONLY, 0);
  187.     setint(typeset("RANDOM", INTEGER, 0), (long) time((time_t *)0));
  188.     /* execute initialization statements */
  189.     for (wp0 = (char**) initcoms; *wp0 != NULL; wp0 = wp+1) {
  190.         /* copy because the alias initializers are readonly */
  191.         for (wp = wp0; *wp != NULL; wp++)
  192.             *wp = strsave(*wp, ATEMP);
  193.         shcomexec(wp0);
  194.     }
  195.     afreeall(ATEMP);
  196.  
  197.     if (geteuid() == 0)
  198.         setstr(global("PS1"), "# ");
  199.  
  200.     s = pushs(SFILE);
  201.     s->u.file = stdin;
  202.     cflag = 0;
  203.     name = *argv;
  204.  
  205.     /* what a bloody mess */
  206.     if ((argi = 1) < argc) {
  207.         if (argv[argi][0] == '-' && argv[argi][1] != '\0') {
  208.             for (arg = argv[argi++]+1; *arg; arg++) {
  209.                 switch (*arg) {
  210.                   case 'c':
  211.                     cflag = 1;
  212.                     if (argi < argc) {
  213.                         s->type = SSTRING;
  214.                         s->str = argv[argi++];
  215.                     }
  216.                     break;
  217.  
  218.                                   case '0':
  219.                                         name = "-ksh";
  220.                                         break;
  221.  
  222.                   case 'q':
  223.                     qflag = 1;
  224.                     break;
  225.  
  226.                   default:
  227.                     if (*arg>='a' && *arg<='z')
  228.                         flag[FLAG(*arg)]++;
  229.                 }
  230.             }
  231.         }
  232.         if (s->type == SFILE && argi < argc && !flag[FSTDIN]) {
  233.             s->file = name = argv[argi];
  234.             if ((s->u.file = fopen(name, "r")) == NULL)
  235.                 errorf("%s: cannot open\n", name);
  236.             fflag = 1;
  237.             fileno(s->u.file) = savefd(fileno(s->u.file));
  238.             setvbuf(s->u.file, (char *)NULL, _IOFBF, BUFSIZ);
  239.         }
  240.     }
  241.  
  242.     if (s->type == SFILE) {
  243.         if (fileno(s->u.file) == 0)
  244.             flag[FSTDIN] = 1;
  245.         if (isatty(0) && isatty(1) && !cflag && !fflag)
  246.             flag[FTALKING] = 1;
  247.         if (flag[FTALKING] && flag[FSTDIN])
  248.             s->type = STTY;
  249.     }
  250.     if (s->type == STTY) {
  251. #ifdef OS2
  252.             ttyfd = getdup(0, FDBASE);
  253. #else
  254.         ttyfd = fcntl(0, F_DUPFD, FDBASE);
  255. #endif
  256. #ifdef F_SETFD
  257.         (void) fcntl(ttyfd, F_SETFD, 1);
  258. #else
  259.         (void) fd_clexec(ttyfd);
  260. #endif
  261. #ifdef EMACS
  262.           x_init_emacs();
  263. #endif
  264.     }
  265.  
  266.     /* initialize job control */
  267.     j_init();
  268.  
  269. #ifndef OS2
  270.     if (!qflag)
  271.         ignoresig(SIGQUIT);
  272. #endif
  273.  
  274.     l->argv = &argv[argi];
  275.     l->argc = argc - argi;
  276.     l->argv[0] = name;
  277.     resetopts();
  278.  
  279.     if (name[0] == '-') {
  280.         loginshell = 1;
  281.         flag[FTALKING] = 1;
  282. #ifdef OS2
  283.         arg = substitute("$INIT/profile.ksh", 0);
  284.         (void) include(*arg ? arg : "/etc/profile.ksh");
  285.                 (void) include("profile.ksh");
  286. #else
  287.         (void) include("/etc/profile");
  288.         (void) include(".profile");
  289. #endif
  290.     }
  291.  
  292. #ifdef OS2
  293.     arg = substitute("~/kshrc.ksh", DOTILDE);
  294. #else
  295.     arg = substitute("~/.kshrc", DOTILDE);
  296. #endif
  297.     if (*arg != '\0')
  298.         (void) include(arg);
  299.  
  300.     /* include $ENV */
  301.     arg = substitute(strval(global("ENV")), DOTILDE);
  302.     if (*arg != '\0')
  303.         (void) include(arg);
  304.  
  305.     if (flag[FTALKING])
  306.     {
  307. #ifdef USE_SIGACT
  308.       sigaction(SIGTERM, &Sigact_trap, NULL);
  309. #else
  310.       signal(SIGTERM, trapsig);
  311. #endif
  312.       ignoresig(SIGINT);
  313. #if defined(EMACS) || defined(VI)
  314.       init_editmode();
  315. #endif
  316.     } else
  317.       flag[FHASHALL] = 1;
  318.  
  319. #ifdef JOBS            /* todo: could go before includes? */
  320.     if (s->type == STTY) {
  321.         flag[FMONITOR] = 1;
  322.         j_change();
  323.     }
  324. #endif
  325.     if (flag[FTALKING])
  326.     {
  327.       hist_init(s);
  328.     }
  329.     argc = shell(s);
  330.     leave(argc);
  331.     return 0;
  332. }
  333.  
  334. int
  335. include(name)
  336.     register char *name;
  337. {
  338.     register FILE *f;
  339.     register Source *s;
  340.  
  341.     if (strcmp(name, "-") != 0) {
  342.         f = fopen(name, "r");
  343.         if (f == NULL)
  344.             return 0;
  345.         /* todo: the savefd doesn't get popped */
  346.         fileno(f) = savefd(fileno(f)); /* questionable */
  347.         setvbuf(f, (char *)NULL, _IOFBF, BUFSIZ);
  348.     } else
  349.         f = stdin;
  350.     s = pushs(SFILE);
  351.     s->u.file = f;
  352.     s->file = name;
  353.     /*return*/ shell(s);
  354.     if (f != stdin)
  355.         fclose(f);
  356.     return 1;
  357. }
  358.  
  359. int
  360. command(comm)
  361.     register char *comm;
  362. {
  363.     register Source *s;
  364.  
  365.     s = pushs(SSTRING);
  366.     s->str = comm;
  367.     return shell(s);
  368. }
  369.  
  370. /*
  371.  * run the commands from the input source, returning status.
  372.  */
  373. int
  374. shell(s)
  375.     Source *s;        /* input source */
  376. {
  377.     struct op *t;
  378.     volatile int attempts = 13;
  379.     volatile int wastty;
  380.     volatile int reading = 0;
  381.     extern void mcheck();
  382.  
  383.     newenv(E_PARSE);
  384.     e.interactive = 1;
  385.     exstat = 0;
  386.     if (setjmp(e.jbuf)) {
  387.         /*shellf("<unwind>");*/
  388.         if (trap)    /* pending SIGINT */
  389.             shellf("\n");
  390.         if (reading && s->type == STTY && s->line)
  391.             s->line--;
  392.         sigtraps[SIGINT].set = 0;
  393.     }
  394.  
  395.     while (1) {
  396.         if (trap)
  397.             runtraps();
  398.         if (flag[FTALKING])
  399.         {
  400. #ifdef USE_SIGACT
  401.           sigaction(SIGINT, &Sigact_trap, NULL);
  402. #else
  403.           signal(SIGINT, trapsig);
  404. #endif
  405.         }
  406.  
  407.         if (s->next == NULL)
  408.             s->echo = flag[FVERBOSE];
  409.  
  410.         j_notify();
  411.  
  412.         if ((wastty = (s->type == STTY)) || s->type == SHIST) {
  413.             prompt = substitute(strval(global("PS1")), 0);
  414.             mcheck();
  415.         }
  416.  
  417.         reading = 1;
  418.         t = compile(s);
  419.         reading = 0;
  420.         j_reap();
  421.         if (t != NULL && t->type == TEOF)
  422.             if (wastty && flag[FIGNEOF] && --attempts > 0) {
  423.                 shellf("Use `exit'\n");
  424.                 s->type = STTY;
  425.                 continue;
  426.             }
  427.             else
  428.                 break;
  429.         flushshf(2);    /* flush -v output */
  430.  
  431.         if (!flag[FNOEXEC] || s->type == STTY)
  432.             execute(t, 0);
  433.  
  434.         reclaim();
  435.     }
  436.     /* Error: */
  437.     quitenv();
  438.     return exstat;
  439. }
  440.  
  441. void
  442. leave(rv)
  443.     int rv;
  444. {
  445.     char *arg;
  446.  
  447.     if (e.type == E_TCOM && e.oenv != NULL)    /* exec'd command */
  448.         unwind();
  449.     runtrap(&sigtraps[0]);
  450.     if (flag[FTALKING])
  451.     {
  452.       hist_finish();
  453.     }
  454.     j_exit();
  455.     if ( loginshell )
  456.     {
  457. #ifdef OS2
  458.         arg = substitute("~/kshexit.ksh", DOTILDE);
  459. #else
  460.         arg = substitute("~/.kshexit", DOTILDE);
  461. #endif
  462.         if (*arg != '\0')
  463.             (void) include(arg);
  464.     }
  465.     exit(rv);
  466.     /* NOTREACHED */
  467. }
  468.  
  469. error()
  470. {
  471.     if (flag[FERREXIT] || !flag[FTALKING])
  472.         leave(1);
  473.     unwind();
  474. }
  475.  
  476. /* return to closest error handler or shell(), exit if none found */
  477. unwind()
  478. {
  479.     while (1)
  480.         switch (e.type) {
  481.           case E_NONE:
  482.             leave(1);
  483.             /* NOTREACHED */
  484.           case E_PARSE:
  485.             longjmp(e.jbuf, 1);
  486.             /* NOTREACHED */
  487.           case E_ERRH:
  488.             longjmp(e.jbuf, 1);
  489.             /* NOTREACHED */
  490.           default:
  491.             quitenv();
  492.             break;
  493.         }
  494. }
  495.  
  496. newenv(type)
  497. {
  498.     register struct env *ep;
  499.  
  500.     ep = (struct env *) alloc(sizeof(*ep), ATEMP);
  501.     *ep = e;
  502.     ainit(&e.area);
  503.     e.type = type;
  504.     e.oenv = ep;
  505.     e.savefd = NULL;
  506.     e.temps = NULL;
  507. }
  508.  
  509. quitenv()
  510. {
  511.     register struct env *ep;
  512.     register int fd;
  513.  
  514.     if ((ep = e.oenv) == NULL)
  515.         exit(exstat);    /* exit child */
  516.     if (e.loc != ep->loc)
  517.         popblock();
  518.     if (e.savefd != NULL)
  519.         for (fd = 0; fd < NUFILE; fd++)
  520.             restfd(fd, e.savefd[fd]);
  521.     reclaim();
  522.     e = *ep;
  523.     afree(ep, ATEMP);
  524. }
  525.  
  526. /* remove temp files and free ATEMP Area */
  527. static void
  528. reclaim()
  529. {
  530.     register struct temp *tp;
  531.  
  532.     for (tp = e.temps; tp != NULL; tp = tp->next)
  533.         remove(tp->name);
  534.     e.temps = NULL;
  535.     afreeall(&e.area);
  536. }
  537.  
  538. void
  539. aerror(ap, msg)
  540.     Area *ap;
  541.     const char *msg;
  542. {
  543.     errorf("alloc internal error: %s\n", msg);
  544. }
  545.  
  546.