00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "defs.h"
00022 #include "../c5/statuspr.h"
00023 #include "../c5/builtin.h"
00024
00025 #define WANT_TESTx
00026
00027
00028 #define MAXARG 50
00029 #define MAXFNAME 500
00030 #define MAXWORD 500
00031
00032 #define PROMPT "* "
00033
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
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
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
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
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;
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
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
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);
00248 EC_CLEANUP_END
00249 }
00250
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
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
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
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
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