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