251 lines
6.7 KiB
C
251 lines
6.7 KiB
C
#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) {
|
|
if (*lp == NULL) return false;
|
|
|
|
|
|
//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",
|
|
"cd",
|
|
#if EXT_PROMPT
|
|
"debug",
|
|
#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;
|
|
}
|