#include "./stdinc.h" // // Purpose: xsshA test harness // Usage: xsshA [-x] [-d] < Commands // -x display expanded command line // (output is marked with '\t+++') // -d debug on (extra feature) // // --| constants and enums |-- const int MAX_NWORDS = 10; const char *UNDEF = ""; // undefined variable value const int MAX_LINE = 50; // --| prototypes |-- void do_subst(char *word[], char *xword[]); void eval_bg(int nwords, char *xword[]); void eval_builtin(int nwords, char *xword[]); void eval_echo(char *xword[]); void eval_quit(char *xword[]); void eval_nonbuiltin(int nwords, char *xword[]); void eval_set(char *xword[]); void eval_wait(char *xword[]); void getArgs(int argc, char *argv[]); void init (void); void show_words(char *word[]); // --| globals |-- char *builtin[] = { "echo", "quit", "wait", "set", 0}; char *cmnd[][MAX_NWORDS] = { { "echo", "hello", "there", 0}, { "ls", "..", 0}, { "set", "Y", "tmp", "$!", 0}, { "touch", "$Y", 0}, //XXX { "find", "/usr", "-name", "core", "&", 0}, { "find", "/usr/local", "-name", "core", "&", 0}, { "set", "X", "$!", 0}, { "find", ".", "-mtime", "-1", 0}, { "wait", "$X", 0}, { "quit", "3", 0}, { 0 } // NULL ptr }; int showx = 0; // -x int debug = 0; // -d pid_t bg_pid; // $! Bool is_done = NO; // set to YES by quit command int quit_status; // exit status of shell (quit arg) // --| defines and static inlines |-- #define DEBUG(arglst) if (debug) { fprintf arglst; } #define DEBUG2(arglst) fprintf arglst; // same as DEBUG, but assumes debug on static inline Bool is_bg(int nwords, char *xword[]) { // YES if bg command if (strcmp(xword[nwords-1], "&") == 0) return YES; else return NO; } static inline void ShowPrompt(void) { printf(">> "); } static inline void DebugWords(char *word[]) { for (int i=0; word[i] != NULL; i++) { DEBUG( (stderr, " %s", word[i]) ); } DEBUG( (stderr, "\n") ); } static inline int Ok(int x) { return (x == 0 ? 1 : 0); } // 0 ==> ok static inline int is_spcl(char c) { return ( (c == '?') || (c == '$') || (c == '!') ? 1 : 0 ); } static inline Bool is_builtin(char *cmnd) { for (int i=0; builtin[i] != NULL; i++) { if (strcmp(builtin[i], cmnd) == 0) return YES; } return NO; } static inline void free_words(char *word[]) { char **p = word; for (int i=0; *p != NULL; p++, i++) { free(word[i]); } } // begin C++ stuff struct ltstr { bool operator()(const char *s1, const char *s2) const { return strcmp(s1, s2) < 0; } }; using namespace std; map value; // variables static inline Bool is_defined(char *name) { // YES if name in symbol table return ((value.find(name) == value.end()) ? NO : YES); } // --| |-- int main (int argc, char *argv[]) { char **word; // words before substitution char *xword[MAX_NWORDS]; // words after substitution Bool quit = NO; // YES when quit command encountered setlinebuf(stdout); // line buffered getArgs(argc, argv); init(); ShowPrompt(); for (int i=0; (cmnd[i][0] != NULL) && (!is_done); i++) { int nwords = 0; printf("%s\n", cmnd[i][0]); for (int j=0; cmnd[i][j] != 0; j++) ++nwords; word = &(cmnd[i][0]); do_subst(word, xword); if (showx) { printf("\t+++ "); show_words(xword); } if (is_builtin(xword[0])) { // eval builtin DEBUG( (stderr, "\t\t+++ Found built-in\n") ); eval_builtin(nwords, xword); } else { // eval non-builtin DEBUG( (stderr, "\t\t+++ Not built-in\n") ); if (is_bg(nwords, xword)) eval_bg(nwords, xword); else eval_nonbuiltin(nwords, xword); } free_words(xword); ShowPrompt(); } return quit_status; } void getArgs(int argc, char *argv[]) { int c; int ok = 1; while ((c = getopt(argc, argv, "dx")) != -1) { switch(c) { case 'd': debug = 1; break; case 'x': showx = 1; break; default: ok = 0; DEBUG( (stderr, "\t\t+++ getArgs: Unknown flag %c\n", c) ); break; } } if (!ok) { fprintf(stderr, "Usage: xssh [-d] [-x] < Command\n"); exit(1); } } // --| initialize |-- void init (void) { // --| variable value table |-- value["!"] = ""; } // --| variable substitution |-- // word[] ptrs to words before substitution // xword[] ptrs to words after substitution void do_subst(char *word[], char *xword[]) { int nwords = 0; DEBUG( (stderr, "*** do_subst called with: ") ); DebugWords(word); for (int i=0; word[i] != NULL; i++, nwords++) { char *var_name; DEBUG( (stderr, "\t\t+++ do_subst: inside word %d for loop (%s)\n", i, word[i]) ); if (word[i][0] == '$') { // found variable var_name = &(word[i][1]); if (is_defined(var_name)) { // lookup value char *v = value[var_name]; DEBUG( (stderr, "\t\t+++ do_subst: %s = <%s>\n", var_name, v) ); xword[i] = strdup(v); } else { // undefined var = "" DEBUG( (stderr, "\t\t+++ do_subst: %s = UNDEF\n", var_name) ); xword[i] = strdup(""); } } else { // copy word as is DEBUG( (stderr, "\t\t+++ do_subst: NOT a var. Copy %s\n", word[i]) ); xword[i] = strdup(word[i]); } } xword[nwords] = NULL; if (debug) { fprintf(stderr, "\tAfter substitution:\n"); for (int i=0; xword[i] != NULL; i++) { fprintf(stderr, "\t\txword[%d] = %s\n", i, xword[i]); } } } // --| show sequence of words |-- void show_words (char *word[]) { char **p = word; for (; *p != NULL; p++) { printf("%s", *p); printf(" "); } printf("\n"); } // --| evaluate non-builtin commands |-- void eval_nonbuiltin(int nwords, char *xword[]) { pid_t pid; int status; DEBUG( (stderr, "*** eval_nonbuiltin called with: ") ); DebugWords(xword); pid = Fork(); if (pid == 0) { // child Execvp(xword[0], xword); exit(1); } else { DEBUG( (stderr, "*** eval_nonbuiltin: child pid = %d\n", pid) ); Waitpid(pid, &status, 0); } } // --| evaluate backgrounded command |-- void eval_bg(int nwords, char *xword[]) { int status; char str_bg_pid[10]; DEBUG( (stderr, "\t\t+++ eval_bg called\n") ); DebugWords(xword); free(xword[nwords-1]); // delete '&' xword[nwords-1] = NULL; bg_pid = Fork(); if (bg_pid == 0) { // child Execvp(xword[0], xword); exit(1); } sprintf(str_bg_pid, "%d", bg_pid); value["!"] = strdup(str_bg_pid); DEBUG( (stderr, "*** eval_bg: child pid = %d\n", bg_pid) ); } // --| evaluate builtin commands |-- void eval_builtin(int nwords, char *xword[]) { DEBUG( (stderr, "*** eval_builtin called with: ") ); DebugWords(xword); if (strcmp(xword[0], "quit") == 0) { eval_quit(xword); is_done = YES; } else if (strcmp(xword[0], "echo") == 0) eval_echo(xword); else if (strcmp(xword[0], "wait") == 0) eval_wait(xword); else if (strcmp(xword[0], "set") == 0) eval_set(xword); else { // nothing for now } } void eval_echo(char *xword[]) { // --| echo W ... |-- DEBUG( (stderr, "*** eval_echo called with: ") ); DebugWords(xword); show_words(&(xword[1])); } void eval_quit(char *xword[]) { // --| quit; quit W |-- DEBUG( (stderr, "*** eval_quit called with: ") ); DebugWords(xword); if (xword[1] != NULL) quit_status = atoi(xword[1]); else quit_status = 0; } void eval_wait(char *xword[]) { // --| wait W |-- int status; pid_t pid = atoi(xword[1]); int rc; DEBUG( (stderr, "*** eval_wait called with: ") ); DebugWords(xword); rc = waitpid(pid, &status, 0); if (rc != pid) { // bad pid fprintf(stderr, "*** eval_wait: waitpid(%d,...) returned %d\n", pid, rc); exit(1); } } void eval_set(char *xword[]) { // --| set W; set W1 W2 ... |-- char valbuf[2*MAX_LINE]; DEBUG( (stderr, "*** eval_set called with: ") ); DebugWords(xword); if (xword[1] == NULL) { fprintf(stderr, "*** eval_set: Gurk! Missing var and value\n"); exit(1); } strcpy(valbuf,""); for (int i=2; (xword[i] != NULL) && (i < MAX_NWORDS); i++) { strncat(valbuf, xword[i], 2*MAX_LINE-strlen(valbuf)); } DEBUG( (stderr, "*** eval_set: value is <%s>\n", valbuf) ); value[strdup(xword[1])] = strdup(valbuf); // >>> should probably free old value if there was one <<< }