assignment 1
This commit is contained in:
249
shell.c
Normal file
249
shell.c
Normal file
@@ -0,0 +1,249 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "scanner.h"
|
||||
|
||||
/**
|
||||
* The function _acceptToken checks whether the current token matches a target identifier.
|
||||
* if this is the case, it will mark this token as the given Type, then move to the next token.
|
||||
* @param lp List pointer to the start of the tokenlist.
|
||||
* @param ident target identifier
|
||||
* @param type enum describing the nature to assign if the token is accepted.
|
||||
* @return a bool denoting whether the current token matches the target identifier.
|
||||
*/
|
||||
bool _acceptToken(List *lp, char *ident, enum Type type) {
|
||||
if (*lp != NULL && strcmp(((*lp)->t), ident) == 0) {
|
||||
(*lp)->type = type;
|
||||
*lp = (*lp)->next;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function parseExecutable parses an executable.
|
||||
* @param lp List pointer to the start of the tokenlist.
|
||||
* @return a bool denoting whether the executable was parsed successfully.
|
||||
*/
|
||||
bool _parseExecutable(List *lp) {
|
||||
char s = (*lp)->t[0];
|
||||
// Right now we only check if the first character is illegal (to prevent an operator token from mistakenly being assigned a COMMAND type.)
|
||||
switch (s) {
|
||||
case '|':
|
||||
case '&':
|
||||
case ';':
|
||||
case '\n':
|
||||
case '>':
|
||||
case '<':
|
||||
return false;
|
||||
}
|
||||
|
||||
(*lp)->type = EXECUTABLE;
|
||||
*lp = (*lp)->next;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the input string \param s is an operator.
|
||||
* @param s input string.
|
||||
* @return a bool denoting whether the current string is an operator.
|
||||
*/
|
||||
//bool isOperator(char *s) {
|
||||
bool _isOperator(List *lp) {
|
||||
// NULL-terminated array makes it easy to expand this array later
|
||||
// without changing the code at other places.
|
||||
char *operators[] = {
|
||||
"&",
|
||||
"&&",
|
||||
"||",
|
||||
";",
|
||||
"<",
|
||||
">",
|
||||
"|",
|
||||
NULL
|
||||
};
|
||||
|
||||
for (int i = 0; operators[i] != NULL; i++) {
|
||||
if (strcmp((*lp)->t, operators[i]) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function parseOptions parses options.
|
||||
* @param lp List pointer to the start of the tokenlist.
|
||||
* @return a bool denoting whether the options were parsed successfully.
|
||||
*/
|
||||
bool _parseOptions(List *lp) {
|
||||
while (*lp != NULL && !_isOperator(lp)) {
|
||||
(*lp)->type = OPTION;
|
||||
(*lp) = (*lp)->next;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function parseRedirections parses a command according to the grammar:
|
||||
*
|
||||
* <command> ::= <executable> <options>
|
||||
*
|
||||
* @param lp List pointer to the start of the tokenlist.
|
||||
* @return a bool denoting whether the command was parsed successfully.
|
||||
*/
|
||||
bool _parseCommand(List *lp) {
|
||||
return _parseExecutable(lp) && _parseOptions(lp);
|
||||
}
|
||||
|
||||
/**
|
||||
* The function parsePipeline parses a pipeline according to the grammar:
|
||||
*
|
||||
* <pipeline> ::= <command> "|" <pipeline>
|
||||
* | <command>
|
||||
*
|
||||
* @param lp List pointer to the start of the tokenlist.
|
||||
* @return a bool denoting whether the pipeline was parsed successfully.
|
||||
*/
|
||||
bool _parsePipeline(List *lp) {
|
||||
if (!_parseCommand(lp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_acceptToken(lp, "|", PIPELINE)) {
|
||||
return _parsePipeline(lp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function parseFileName parses a filename.
|
||||
* @param lp List pointer to the start of the tokenlist.
|
||||
* @return a bool denoting whether the filename was parsed successfully.
|
||||
*/
|
||||
bool _parseFilename(List *lp) {
|
||||
//we run a POSIX compliant system, meaning all characters save '/' and NULL are allowed in filenames.
|
||||
//NULL is already taken care of by the List library, and / just means the file is located in a directory.
|
||||
//as a result the only limit we place on filenames are the reserved (operator) characters.
|
||||
switch ((*lp)->t[0]) {
|
||||
case '|':
|
||||
case '&':
|
||||
case ';':
|
||||
case '\n':
|
||||
case '>':
|
||||
case '<':
|
||||
return false;
|
||||
}
|
||||
(*lp)->type = FILENAME;
|
||||
*lp = (*lp)->next;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function parseRedirections parses redirections according to the grammar:
|
||||
*
|
||||
* <redirections> ::= <pipeline> <redirections>
|
||||
* | <builtin> <options>
|
||||
*
|
||||
* @param lp List pointer to the start of the tokenlist.
|
||||
* @return a bool denoting whether the redirections were parsed successfully.
|
||||
*/
|
||||
bool _parseRedirections(List *lp) {
|
||||
if (isEmpty(*lp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_acceptToken(lp, "<", REDIRECT)) {
|
||||
if (!_parseFilename(lp)) return false;
|
||||
if (_acceptToken(lp, ">", REDIRECT)) return _parseFilename(lp);
|
||||
else return true;
|
||||
} else if (_acceptToken(lp, ">", REDIRECT)) {
|
||||
if (!_parseFilename(lp)) return false;
|
||||
if (_acceptToken(lp, "<", REDIRECT)) return _parseFilename(lp);
|
||||
else return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function parseBuiltIn parses a builtin.
|
||||
* @param lp List pointer to the start of the tokenlist.
|
||||
* @return a bool denoting whether the builtin was parsed successfully.
|
||||
*/
|
||||
bool _parseBuiltIn(List *lp) {
|
||||
// NULL-terminated array makes it easy to expand this array later
|
||||
// without changing the code at other places.
|
||||
char *builtIns[] = {
|
||||
"exit",
|
||||
"status",
|
||||
"true",
|
||||
"false",
|
||||
#if EXT_PROMPT
|
||||
"debug",
|
||||
"cd",
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
for (int i = 0; builtIns[i] != NULL; i++) {
|
||||
if (_acceptToken(lp, builtIns[i], BUILTIN)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function parseChain parses a chain according to the grammar:
|
||||
*
|
||||
* <chain> ::= <pipeline> <redirections>
|
||||
* | <builtin> <options>
|
||||
*
|
||||
* @param lp List pointer to the start of the tokenlist.
|
||||
* @return a bool denoting whether the chain was parsed successfully.
|
||||
*/
|
||||
bool _parseChain(List *lp) {
|
||||
if (_parseBuiltIn(lp)) {
|
||||
return _parseOptions(lp);
|
||||
}
|
||||
if (_parsePipeline(lp)) {
|
||||
return _parseRedirections(lp);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The function parseInputLine parses an inputline according to the grammar:
|
||||
*
|
||||
* <inputline> ::= <chain> & <inputline>
|
||||
* | <chain> && <inputline>
|
||||
* | <chain> || <inputline>
|
||||
* | <chain> ; <inputline>
|
||||
* | <chain>
|
||||
* | <empty>
|
||||
*
|
||||
* @param lp List pointer to the start of the tokenlist.
|
||||
* @return a bool denoting whether the inputline was parsed successfully.
|
||||
*/
|
||||
bool parseInputLine(List *lp) {
|
||||
if (isEmpty(*lp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_parseChain(lp)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_acceptToken(lp, "&", BACKGROUND) || _acceptToken(lp, "&&", COMPOSITION)) {
|
||||
return parseInputLine(lp);
|
||||
} else if (_acceptToken(lp, "||", COMPOSITION)) {
|
||||
return parseInputLine(lp);
|
||||
} else if (_acceptToken(lp, ";", COMPOSITION)) {
|
||||
return parseInputLine(lp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user