static void performCommand(char *acLine, DynArray_T oHistoryList, char *pcProgName) /* Expand any !commandprefix in acLine. Insert acLine into oHistoryList iff the expanding succeeds and acLine does not consist of entirely whitespace characters. Lexically and syntactically analyze acLine. Execute acLine if no errors are found. It is a checked runtime error for acLine, oHistory, or pcProgName to be NULL. */ { char *pcTemp; Command_T oCommand; DynArray_T oTokens; int iSuccessful; assert(acLine != NULL); assert(oHistoryList != NULL); assert(pcProgName != NULL); if(histHasCommandPrefix(acLine)) { iSuccessful = histExpandLine(acLine, oHistoryList, pcProgName); if(iSuccessful) printf("%s\n", acLine); else return; } oTokens = DynArray_new(0); iSuccessful = lexLine(acLine, oTokens, pcProgName); if(DynArray_getLength(oTokens) > 0) { /* Allocate memory to store command in oHistoryList iff command does not consist of entirely whitespace characters. */ pcTemp = (char*)malloc(strlen(acLine) + 1); assert(pcTemp != NULL); strcpy(pcTemp, acLine); DynArray_add(oHistoryList, pcTemp); if(iSuccessful) { oCommand = Command_new(); iSuccessful = parseToken(oTokens, oCommand, pcProgName); if(iSuccessful) execute(oCommand, oHistoryList, pcProgName); Command_free(oCommand, NULL); } } DynArray_map(oTokens, Token_free, NULL); DynArray_free(oTokens); }
static DynArray_T lexLine(const char *pcLine) { /* lexLine() uses a DFA approach. It "reads" its characters from pcLine. The DFA has these three states: */ enum LexState {STATE_START, STATE_IN_NUMBER, STATE_IN_WORD}; /* The current state of the DFA. */ enum LexState eState = STATE_START; /* An index into pcLine. */ int iLineIndex = 0; /* Pointer to a buffer in which the characters comprising each token are accumulated. */ char *pcBuffer; /* An index into the buffer. */ int iBufferIndex = 0; char c; struct Token *psToken; DynArray_T oTokens; int iSuccessful; assert(pcLine != NULL); /* Create an empty token DynArray object. */ oTokens = DynArray_new(0); if (oTokens == NULL) {perror(pcPgmName); exit(EXIT_FAILURE);} /* Allocate memory for a buffer that is large enough to store the largest token that might appear within pcLine. */ pcBuffer = (char*)malloc(strlen(pcLine) + 1); if (pcBuffer == NULL) {perror(pcPgmName); exit(EXIT_FAILURE);} for (;;) { /* "Read" the next character from pcLine. */ c = pcLine[iLineIndex++]; switch (eState) { /* Handle the START state. */ case STATE_START: if (c == '\0') { free(pcBuffer); return oTokens; } else if (isdigit(c)) { pcBuffer[iBufferIndex++] = c; eState = STATE_IN_NUMBER; } else if (isalpha(c)) { pcBuffer[iBufferIndex++] = c; eState = STATE_IN_WORD; } else if (isspace(c)) eState = STATE_START; else { fprintf(stderr, "Invalid line\n"); free(pcBuffer); freeTokens(oTokens); DynArray_free(oTokens); return NULL; } break; /* Handle the IN_NUMBER state. */ case STATE_IN_NUMBER: if (c == '\0') { /* Create a NUMBER token. */ pcBuffer[iBufferIndex] = '\0'; psToken = newToken(TOKEN_NUMBER, pcBuffer); iSuccessful = DynArray_add(oTokens, psToken); if (! iSuccessful) {perror(pcPgmName); exit(EXIT_FAILURE);} iBufferIndex = 0; free(pcBuffer); return oTokens; } else if (isdigit(c)) { pcBuffer[iBufferIndex++] = c; eState = STATE_IN_NUMBER; } else if (isspace(c)) { /* Create a NUMBER token. */ pcBuffer[iBufferIndex] = '\0'; psToken = newToken(TOKEN_NUMBER, pcBuffer); iSuccessful = DynArray_add(oTokens, psToken); if (! iSuccessful) {perror(pcPgmName); exit(EXIT_FAILURE);} iBufferIndex = 0; eState = STATE_START; } else { fprintf(stderr, "Invalid line\n"); free(pcBuffer); freeTokens(oTokens); DynArray_free(oTokens); return NULL; } break; /* Handle the IN_WORD state. */ case STATE_IN_WORD: if (c == '\0') { /* Create a WORD token. */ pcBuffer[iBufferIndex] = '\0'; psToken = newToken(TOKEN_WORD, pcBuffer); iSuccessful = DynArray_add(oTokens, psToken); if (! iSuccessful) {perror(pcPgmName); exit(EXIT_FAILURE);} iBufferIndex = 0; free(pcBuffer); return oTokens; } else if (isalpha(c)) { pcBuffer[iBufferIndex++] = c; eState = STATE_IN_WORD; } else if (isspace(c)) { /* Create a WORD token. */ pcBuffer[iBufferIndex] = '\0'; psToken = newToken(TOKEN_WORD, pcBuffer); iSuccessful = DynArray_add(oTokens, psToken); if (! iSuccessful) {perror(pcPgmName); exit(EXIT_FAILURE);} iBufferIndex = 0; eState = STATE_START; } else { fprintf(stderr, "Invalid line\n"); free(pcBuffer); freeTokens(oTokens); DynArray_free(oTokens); return NULL; } break; default: assert(0); } } }
Command_T Command_new(char *pcName, DynArray_T oArgs, char *pcStdIn, char *pcStdOut) { int i; int argsLength; /* length of oArgs array */ char* pcArg; size_t pcArgLength; /* track how much memory to give pcArg */ Command_T oCommand; /* a command must have a name */ assert(pcName != NULL); assert(oArgs != NULL); oCommand = (Command_T) malloc(sizeof(struct Command)); if (oCommand == NULL) { fprintf(stderr, "%s: Can't allocate memory for command\n", pcPgmName); exit(EXIT_FAILURE); } /* allocate memory for and set the command name */ oCommand->pcName = (char*)malloc(strlen(pcName) + 1); if (oCommand->pcName == NULL) { fprintf(stderr, "%s: Can't allocate memory for command\n", pcPgmName); exit(EXIT_FAILURE); } strcpy(oCommand->pcName, pcName); /* allocate memory for and set the command arguments*/ argsLength = DynArray_getLength(oArgs); oCommand->oArgs = DynArray_new(0); for (i = 0; i < argsLength; i++) { pcArgLength = strlen((char*) DynArray_get(oArgs, i)); pcArg = (char*) malloc(pcArgLength + 1); strcpy(pcArg, DynArray_get(oArgs, i)); if (pcArg == NULL) { fprintf(stderr, "%s: Can't allocate memory for command args\n", pcPgmName); exit(EXIT_FAILURE); } DynArray_add(oCommand->oArgs, pcArg); } /* allocate memory for and set the command stdin redirect */ if (pcStdIn == NULL) { oCommand->pcStdIn = NULL; } else { oCommand->pcStdIn = (char*)malloc(strlen(pcStdIn) + 1); if (oCommand->pcStdIn == NULL) { fprintf(stderr, "%s: Can't allocate memory for command\n", pcPgmName); exit(EXIT_FAILURE); } strcpy(oCommand->pcStdIn, pcStdIn); } /* allocate memory for and set the command stdout redirect*/ if (pcStdOut == NULL) { oCommand->pcStdOut = NULL; } else { oCommand->pcStdOut = (char*)malloc(strlen(pcStdOut) + 1); if (oCommand->pcStdOut == NULL) { fprintf(stderr, "%s: Can't allocate memory for command\n", pcPgmName); exit(EXIT_FAILURE); } strcpy(oCommand->pcStdOut, pcStdOut); } return oCommand; }
int lexLine(const char *pcLine, DynArray_T oTokens, char *pcProgName) /* Lexically analyze string pcLine. Populate oTokens with the tokens that pcLine contains. Return TRUE if successful, and FALSE if pcLine contains a lexical error. In the latter case, oTokens may contain tokens that were discovered before the lexical error. pcProgName is used in printing error messages. It is a checked runtime error for pcLine, oTokens, or pcProgName to be NULL. It is a checked runtime error for the size of pcLine to be greater than MAX_LINE_SIZE. */ /* lexLine() uses a DFA approach. It "reads" its characters from pcLine. */ { char acValue[MAX_LINE_SIZE]; char c; int iLineIndex = 0; int iValueIndex = 0; enum LexState eState = STATE_START; struct Token *psToken; assert(pcLine != NULL); assert(strlen(pcLine) + 1 <= MAX_LINE_SIZE); assert(oTokens != NULL); assert(pcProgName != NULL); while ((eState != STATE_EXIT) && (eState != STATE_ERROR)) { /* "Read" the next character from pcLine. */ c = pcLine[iLineIndex++]; switch (eState) { case STATE_START: if ((c == '\n') || (c == '\0')) eState = STATE_EXIT; else if (c == '<') { /* Create a STDIN token. */ acValue[iValueIndex++] = c; acValue[iValueIndex] = '\0'; psToken = makeToken(TOKEN_STDIN, acValue); DynArray_add(oTokens, psToken); iValueIndex = 0; eState = STATE_START; } else if (c == '>') { /* Create a STDOUT token. */ acValue[iValueIndex++] = c; acValue[iValueIndex] = '\0'; psToken = makeToken(TOKEN_STDOUT, acValue); DynArray_add(oTokens, psToken); iValueIndex = 0; eState = STATE_START; } else if (c == '"') { eState = STATE_IN_QUOTE; } else if ((int)c > 0x20 && (int)c < 0x7F) { acValue[iValueIndex++] = c; eState = STATE_IN_WORD; } else if ((c == ' ') || (c == '\t')) eState = STATE_START; else eState = STATE_ERROR; break; case STATE_IN_WORD: if ((c == '\n') || (c == '\0')) { /* Create a WORD token. */ acValue[iValueIndex] = '\0'; psToken = makeToken(TOKEN_WORD, acValue); DynArray_add(oTokens, psToken); iValueIndex = 0; eState = STATE_EXIT; } else if ((c == ' ') || (c == '\t')) { /* Create a WORD token. */ acValue[iValueIndex] = '\0'; psToken = makeToken(TOKEN_WORD, acValue); DynArray_add(oTokens, psToken); iValueIndex = 0; eState = STATE_START; } else if (c == '<') { /* Create a WORD token. */ acValue[iValueIndex] = '\0'; psToken = makeToken(TOKEN_WORD, acValue); DynArray_add(oTokens, psToken); iValueIndex = 0; /* Create a STDIN token. */ acValue[iValueIndex++] = c; acValue[iValueIndex] = '\0'; psToken = makeToken(TOKEN_STDIN, acValue); DynArray_add(oTokens, psToken); iValueIndex = 0; eState = STATE_START; } else if (c == '>') { /* Create a WORD token. */ acValue[iValueIndex] = '\0'; psToken = makeToken(TOKEN_WORD, acValue); DynArray_add(oTokens, psToken); iValueIndex = 0; /* Create a STDOUT token. */ acValue[iValueIndex++] = c; acValue[iValueIndex] = '\0'; psToken = makeToken(TOKEN_STDOUT, acValue); DynArray_add(oTokens, psToken); iValueIndex = 0; eState = STATE_START; } else if (c == '"') { eState = STATE_IN_QUOTE; } else if ((int)c > 0x20 && (int)c < 0x7F) { acValue[iValueIndex++] = c; eState = STATE_IN_WORD; } else eState = STATE_ERROR; break; case STATE_IN_QUOTE: if ((c == '\n') || (c == '\0')) { /* Create a token so we know command did not consist of entirely white spaces. The eType TOKEN_WORD is chosen arbitrarily. */ acValue[iValueIndex] = '\0'; psToken = makeToken(TOKEN_WORD, acValue); DynArray_add(oTokens, psToken); iValueIndex = 0; fprintf(stderr, "%s: Unmatched quote\n", pcProgName); eState = STATE_ERROR; } else if (c == '"') { eState = STATE_IN_WORD; } else if ((c == ' ') || (c == '\t') || ((int)c > 0x20 && (int)c < 0x7F)) { acValue[iValueIndex++] = c; eState = STATE_IN_QUOTE; } else eState = STATE_ERROR; break; default: assert(0); } } if (eState == STATE_ERROR) return FALSE; return TRUE; }