©2004 by Marc J. Rochkind. All rights reserved. Portions marked "Open Source" may be copied under license.

 

Main Page   Modules   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

c6/sh3.c

Go to the documentation of this file.
00001 /*
00002     real shell (version 4)
00003     AUP2, Sec. 6.04
00004 
00005     Copyright 2003 by Marc J. Rochkind. All rights reserved.
00006     May be copied only for purposes and under conditions described
00007     on the Web page www.basepath.com/aup/copyright.htm.
00008 
00009     The Example Files are provided "as is," without any warranty;
00010     without even the implied warranty of merchantability or fitness
00011     for a particular purpose. The author and his publisher are not
00012     responsible for any damages, direct or incidental, resulting
00013     from the use or non-use of these Example Files.
00014 
00015     The Example Files may contain defects, and some contain deliberate
00016     coding mistakes that were included for educational reasons.
00017     You are responsible for determining if and how the Example Files
00018     are to be used.
00019 
00020 */
00021 #include "defs.h"
00022 #include "../c5/statuspr.h"
00023 #include "../c5/builtin.h"
00024 
00025 #define WANT_TESTx
00026 
00027 /*[macros]*/
00028 #define MAXARG 50           /* max args in command */
00029 #define MAXFNAME 500        /* max chars in file name */
00030 #define MAXWORD 500         /* max chars in arg */
00031 /*[macros2]*/
00032 #define PROMPT "* "         /* prompt */
00033 /*[fd_check]*/
00034 static void fd_check(void)
00035 {
00036     int fd;
00037     bool ok = true;
00038 
00039     for (fd = 3; fd < 20; fd++)
00040         if (fcntl(fd, F_GETFL) != -1 || errno != EBADF) {
00041             ok = false;
00042             fprintf(stderr, "*** fd %d is open ***\n", fd);
00043         }
00044     if (!ok)
00045         _exit(EXIT_FAILURE);
00046 }
00047 /*[store_char]*/
00048 static bool store_char(char *word, size_t maxword, int c, size_t *np)
00049 {
00050     errno = E2BIG;
00051     ec_false( *np < maxword )
00052     word[(*np)++] = c;
00053     return true;
00054 
00055 EC_CLEANUP_BGN
00056     return false;
00057 EC_CLEANUP_END
00058 }
00059 /*[gettoken]*/
00060 typedef enum {T_WORD, T_BAR, T_AMP, T_SEMI, T_GT, T_GTGT, T_LT,
00061   T_NL, T_EOF, T_ERROR} TOKEN;
00062 
00063 static TOKEN gettoken(char *word, size_t maxword)
00064 {
00065     enum {NEUTRAL, GTGT, INQUOTE, INWORD} state = NEUTRAL;
00066     int c;
00067     size_t wordn = 0;
00068 
00069     while ((c = getchar()) != EOF) {
00070         switch (state) {
00071         case NEUTRAL:
00072             switch (c) {
00073             case ';':
00074                 return T_SEMI;
00075             case '&':
00076                 return T_AMP;
00077             case '|':
00078                 return T_BAR;
00079             case '<':
00080                 return T_LT;
00081             case '\n':
00082                 return T_NL;
00083             case ' ':
00084             case '\t':
00085                 continue;
00086             case '>':
00087                 state = GTGT;
00088                 continue;
00089             case '"':
00090                 state = INQUOTE;
00091                 continue;
00092             default:
00093                 state = INWORD;
00094                 ec_false( store_char(word, maxword, c, &wordn) )
00095                 continue;
00096             }
00097         case GTGT:
00098             if (c == '>')
00099                 return T_GTGT;
00100             ungetc(c, stdin);
00101             return T_GT;
00102         case INQUOTE:
00103             switch (c) {
00104             case '\\':
00105                 if ((c = getchar()) == EOF)
00106                     c = '\\';
00107                 ec_false( store_char(word, maxword, c, &wordn) );
00108                 continue;
00109             case '"':
00110                 ec_false( store_char(word, maxword, '\0', &wordn) )
00111                 return T_WORD;
00112             default:
00113                 ec_false( store_char(word, maxword, c, &wordn) )
00114                 continue;
00115             }
00116         case INWORD:
00117             switch (c) {
00118             case ';':
00119             case '&':
00120             case '|':
00121             case '<':
00122             case '>':
00123             case '\n':
00124             case ' ':
00125             case '\t':
00126                 ungetc(c, stdin);
00127                 ec_false( store_char(word, maxword, '\0', &wordn) )
00128                 return T_WORD;
00129             default:
00130                 ec_false( store_char(word, maxword, c, &wordn) )
00131                 continue;
00132             }
00133         }
00134     }
00135     ec_false( !ferror(stdin) )
00136     return T_EOF;
00137 
00138 EC_CLEANUP_BGN
00139     return T_ERROR;
00140 EC_CLEANUP_END
00141 }
00142 /*[builtin]*/
00143 static bool builtin(int argc, char *argv[], int srcfd, int dstfd)
00144 {
00145     char *path;
00146 
00147     if (strchr(argv[0], '=') != NULL)
00148         asg(argc, argv);
00149     else if (strcmp(argv[0], "set") == 0)
00150         set(argc, argv);
00151     else if (strcmp(argv[0], "cd") == 0) {
00152         if (argc > 1)
00153             path = argv[1];
00154         else if ((path = getenv("HOME")) == NULL)
00155             path = ".";
00156         if (chdir(path) == -1)
00157             fprintf(stderr, "%s: bad directory\n", path);
00158     }
00159     else
00160         return false;
00161     if (srcfd != STDIN_FILENO || dstfd != STDOUT_FILENO)
00162         fprintf(stderr, "Illegal redirection or pipeline\n");
00163     return true;
00164 }
00165 /*[ignore_sig]*/
00166 static struct sigaction entry_int, entry_quit;
00167 
00168 static bool ignore_sig(void)
00169 {
00170     static bool first = true;
00171     struct sigaction act_ignore;
00172 
00173     memset(&act_ignore, 0, sizeof(act_ignore));
00174     act_ignore.sa_handler = SIG_IGN; /* may generate warning on Solaris */
00175     if (first) {
00176         first = false;
00177         ec_neg1( sigaction(SIGINT, &act_ignore, &entry_int) )
00178         ec_neg1( sigaction(SIGQUIT, &act_ignore, &entry_quit) )
00179     }
00180     else {
00181         ec_neg1( sigaction(SIGINT, &act_ignore, NULL) )
00182         ec_neg1( sigaction(SIGQUIT, &act_ignore, NULL) )
00183     }
00184     return true;
00185 
00186 EC_CLEANUP_BGN
00187     return false;
00188 EC_CLEANUP_END
00189 }
00190 
00191 static bool entry_sig(void)
00192 {
00193     ec_neg1( sigaction(SIGINT, &entry_int, NULL) )
00194     ec_neg1( sigaction(SIGQUIT, &entry_quit, NULL) )
00195     return true;
00196 
00197 EC_CLEANUP_BGN
00198     return false;
00199 EC_CLEANUP_END
00200 }
00201 /*[wait_and_display]*/
00202 static bool wait_and_display(pid_t pid)
00203 {
00204     pid_t wpid;
00205     int status;
00206 
00207     do {
00208         ec_neg1( wpid = waitpid(-1, &status, 0) )
00209         display_status(wpid, status);
00210     } while (wpid != pid);
00211     return true;
00212 
00213 EC_CLEANUP_BGN
00214     return false;
00215 EC_CLEANUP_END
00216 }
00217 /*[redirect]*/
00218 static void redirect(int srcfd, const char *srcfile, int dstfd,
00219   const char *dstfile, bool append, bool bckgrnd)
00220 {
00221     int flags;
00222 
00223     if (srcfd == STDIN_FILENO && bckgrnd) {
00224         srcfile = "/dev/null";
00225         srcfd = -1;
00226     }
00227     if (srcfile[0] != '\0')
00228         ec_neg1( srcfd = open(srcfile, O_RDONLY, 0) )
00229     ec_neg1( dup2(srcfd, STDIN_FILENO) )
00230     if (srcfd != STDIN_FILENO)
00231         ec_neg1( close(srcfd) )
00232     if (dstfile[0] != '\0') {
00233         flags = O_WRONLY | O_CREAT;
00234         if (append)
00235             flags |= O_APPEND;
00236         else
00237             flags |= O_TRUNC;
00238         ec_neg1( dstfd = open(dstfile, flags, PERM_FILE) )
00239     }
00240     ec_neg1( dup2(dstfd, STDOUT_FILENO) )
00241     if (dstfd != STDOUT_FILENO)
00242         ec_neg1( close(dstfd) )
00243     fd_check();
00244     return;
00245 
00246 EC_CLEANUP_BGN
00247     _exit(EXIT_FAILURE); /* we are in child */
00248 EC_CLEANUP_END
00249 }
00250 /*[invoke]*/
00251 static pid_t invoke(int argc, char *argv[], int srcfd, const char *srcfile,
00252   int dstfd, const char *dstfile, bool append, bool bckgrnd, int closefd)
00253 {
00254     pid_t pid;
00255     char *cmdname, *cmdpath;
00256 
00257     if (argc == 0 || builtin(argc, argv, srcfd, dstfd))
00258         return 0;
00259     switch (pid = fork()) {
00260     case -1:
00261         fprintf(stderr, "Can't create new process\n");
00262         return 0;
00263     case 0:
00264         if (closefd != -1)
00265             ec_neg1( close(closefd) )
00266         if (!bckgrnd)
00267             ec_false( entry_sig() )
00268         redirect(srcfd, srcfile, dstfd, dstfile, append, bckgrnd);
00269         cmdname = strchr(argv[0], '/');
00270         if (cmdname == NULL)
00271             cmdname = argv[0];
00272         else
00273             cmdname++;
00274         cmdpath = argv[0];
00275         argv[0] = cmdname;
00276         execvp(cmdpath, argv);
00277         fprintf(stderr, "Can't execute %s\n", cmdpath);
00278         _exit(EXIT_FAILURE);
00279     }
00280     /* parent */
00281     if (srcfd > STDOUT_FILENO)
00282         ec_neg1( close(srcfd) )
00283     if (dstfd > STDOUT_FILENO)
00284         ec_neg1( close(dstfd) )
00285     if (bckgrnd)
00286         printf("%ld\n", (long)pid);
00287     return pid;
00288 
00289 EC_CLEANUP_BGN
00290     if (pid == 0)
00291         _exit(EXIT_FAILURE);
00292     return -1;
00293 EC_CLEANUP_END
00294 }
00295 /*[command]*/
00296 static TOKEN command(pid_t *wpid, bool makepipe, int *pipefdp)
00297 {
00298     TOKEN token, term;
00299     int argc, srcfd, dstfd, pid, pfd[2] = {-1, -1};
00300     char *argv[MAXARG], srcfile[MAXFNAME] = "", dstfile[MAXFNAME] = "";
00301     char word[MAXWORD];
00302     bool append;
00303 
00304     argc = 0;
00305     srcfd = STDIN_FILENO;
00306     dstfd = STDOUT_FILENO;
00307     while (true) {
00308         switch (token = gettoken(word, sizeof(word))) {
00309         case T_WORD:
00310             if (argc >= MAXARG - 1) {
00311                 fprintf(stderr, "Too many args\n");
00312                 continue;
00313             }
00314             if ((argv[argc] = malloc(strlen(word) + 1)) == NULL) {
00315                 fprintf(stderr, "Out of arg memory\n");
00316                 continue;
00317             }
00318             strcpy(argv[argc], word);
00319             argc++;
00320             continue;
00321         case T_LT:
00322             if (makepipe) {
00323                 fprintf(stderr, "Extra <\n");
00324                 break;
00325             }
00326             if (gettoken(srcfile, sizeof(srcfile)) != T_WORD) {
00327                 fprintf(stderr, "Illegal <\n");
00328                 break;
00329             }
00330             srcfd = -1;
00331             continue;
00332         case T_GT:
00333         case T_GTGT:
00334             if (dstfd != STDOUT_FILENO) {
00335                 fprintf(stderr, "Extra > or >>\n");
00336                 break;
00337             }
00338             if (gettoken(dstfile, sizeof(dstfile)) != T_WORD) {
00339                 fprintf(stderr, "Illegal > or >>\n");
00340                 break;
00341             }
00342             dstfd = -1;
00343             append = token == T_GTGT;
00344             continue;
00345         case T_BAR:
00346         case T_AMP:
00347         case T_SEMI:
00348         case T_NL:
00349             argv[argc] = NULL;
00350             if (token == T_BAR) {
00351                 if (dstfd != STDOUT_FILENO) {
00352                     fprintf(stderr, "> or >> conflicts with |\n");
00353                     break;
00354                 }
00355                 term = command(wpid, true, &dstfd);
00356                 if (term == T_ERROR)
00357                     return T_ERROR;
00358             }
00359             else
00360                 term = token;
00361             if (makepipe) {
00362                 ec_neg1( pipe(pfd) )
00363                 *pipefdp = pfd[1];
00364                 srcfd = pfd[0];
00365             }
00366             ec_neg1( pid = invoke(argc, argv, srcfd, srcfile, dstfd,
00367               dstfile, append, term == T_AMP, pfd[1]) )
00368             if (token != T_BAR)
00369                 *wpid = pid;
00370             if (argc == 0 && (token != T_NL || srcfd > 1))
00371                 fprintf(stderr, "Missing command\n");
00372             while (--argc >= 0)
00373                 free(argv[argc]);
00374             return term;
00375         case T_EOF:
00376             exit(EXIT_SUCCESS);
00377         case T_ERROR:
00378             return T_ERROR;
00379         }
00380     }
00381 
00382 EC_CLEANUP_BGN
00383     return T_ERROR;
00384 EC_CLEANUP_END
00385 }
00386 /*[]*/
00387 #ifndef WANT_TEST
00388 /*[main]*/
00389 int main(void)
00390 {
00391     pid_t pid;
00392     TOKEN term = T_NL;
00393 
00394     ignore_sig();
00395     while (true) {
00396         if (term == T_NL)
00397             printf("%s", PROMPT);
00398         term = command(&pid, false, NULL);
00399         if (term == T_ERROR) {
00400             fprintf(stderr, "Bad command\n");
00401             EC_FLUSH("main--bad command")
00402             term = T_NL;
00403         }
00404         if (term != T_AMP && pid > 0)
00405             wait_and_display(pid);
00406         fd_check();
00407     }
00408 }
00409 /*[]*/
00410 #else
00411 /*[gettoken-test]*/
00412 int main(void)
00413 {
00414     char word[200];
00415 
00416     while (1)
00417         switch (gettoken(word, sizeof(word))) {
00418         case T_WORD:
00419             printf("T_WORD <%s>\n", word);
00420             break;
00421         case T_BAR:
00422             printf("T_BAR\n");
00423             break;
00424         case T_AMP:
00425             printf("T_AMP\n");
00426             break;
00427         case T_SEMI:
00428             printf("T_SEMI\n");
00429             break;
00430         case T_GT:
00431             printf("T_GT\n");
00432             break;
00433         case T_GTGT:
00434             printf("T_GTGT\n");
00435             break;
00436         case T_LT:
00437             printf("T_LT\n");
00438             break;
00439         case T_NL:
00440             printf("T_NL\n");
00441             break;
00442         case T_EOF:
00443             printf("T_EOF\n");
00444             exit(EXIT_SUCCESS);
00445         case T_ERROR:
00446             printf("T_ERROR\n");
00447             exit(EXIT_SUCCESS);
00448         }
00449 }
00450 /*[]*/
00451 #endif

Generated on Fri Apr 23 10:56:58 2004 for AUP2 Example Source by doxygen 1.3.1