├── .github └── FUNDING.yml ├── LICENSE.md ├── Makefile ├── README.md ├── astree.c ├── astree.h ├── command.c ├── command.h ├── execute.c ├── execute.h ├── lexer.c ├── lexer.h ├── parser.c ├── parser.h └── shell.c /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Swoorup] 4 | buy_me_a_coffee: Swoorup 5 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | mysh and all the source and headers are licensed under the MIT License. 2 | 3 | > The MIT License (MIT) 4 | > 5 | > Copyright (c) 2014-2015 Swoorup Joshi 6 | > 7 | > Permission is hereby granted, free of charge, to any person obtaining a copy 8 | > of this software and associated documentation files (the "Software"), to deal 9 | > in the Software without restriction, including without limitation the rights 10 | > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | > copies of the Software, and to permit persons to whom the Software is 12 | > furnished to do so, subject to the following conditions: 13 | > 14 | > The above copyright notice and this permission notice shall be included in 15 | > all copies or substantial portions of the Software. 16 | > 17 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | > THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | CFLAGS = -m64 3 | 4 | default: shell 5 | 6 | shell: lexer.o shell.o parser.o astree.o execute.o command.o 7 | $(CC) $(CFLAGS) parser.o lexer.o shell.o astree.o execute.o command.o -o shell 8 | 9 | command.o: command.c 10 | $(CC) $(CFLAGS) -c command.c 11 | 12 | shell.o: shell.c 13 | $(CC) $(CFLAGS) -c shell.c 14 | 15 | execute.o: execute.c execute.h 16 | $(CC) $(CFLAGS) -c execute.c 17 | 18 | parser.o: parser.c parser.h 19 | $(CC) $(CFLAGS) -c parser.c 20 | 21 | lexer.o: lexer.h lexer.c 22 | $(CC) $(CFLAGS) -c lexer.c 23 | 24 | astree.o: astree.c astree.h 25 | $(CC) $(CFLAGS) -c astree.c 26 | 27 | clean: 28 | rm *.o 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mysh 2 | A very simply sh-like unix shell written in C using recursive descent parser technique. 3 | 4 | # WHY 5 | Last summer, I started out writing a shell for my assignment. Thought I'd share it as well. I'll explain this in conjunction with the project hosted in [GitHub](https://github.com/Swoorup/mysh/blob/master/lexer.c#L97). Please download it and have your favourite editor ready or you might not be able to make any single sense out of this. 6 | 7 | I am sure you may have come across using a shell. You have used command prompt in windows, or the popular bash in Linux. But how do they work? Lets see if we can break it down. 8 | 9 | Without further ado, lets start at the concepts. Now the shell consists of number of rules which we take as granted. And by these rules I meant think of it as a computer working on your high school algebra. 10 | 11 | `E.g: (1+2 % 4 x (1 + 1))` 12 | 13 | It would have to work out the BDMAS (brackets first, then divide, multiply, addition and subtraction respectively) rule here. Think of the possibilities of the user input first. You first need to make sure that the input is valid and error-free (You don't want your device to do unexpected things when the user enter unexpected input). You would then need to break down each individual tokens into an ordered list, check if there are any errors in the syntax and then try and parse them into an execution tree. If you want your computer to solve something like this, its obvious it would need more than a page worth of code. You can off-course do it in many ways, but its better to stick to a well known structure. 14 | 15 | # Brief Summary 16 | 17 | Now I wont dive into deeper into every aspects of the shell code. I'll just highlight the main concepts. We start at the entry point of the program. Since I am programming this for *nix system, we would have to get or redirect the unix signals like Ctrl-Z, Ctrl-C and Ctrl-\\. 18 | 19 | ```c 20 | int main() 21 | { 22 | // ignore Ctrl-\ Ctrl-C Ctrl-Z signals 23 | ignore_signal_for_shell(); 24 | 25 | // set the prompt 26 | set_prompt("swoorup % "); 27 | 28 | while (1) 29 | ``` 30 | 31 | Then there is a main loop which would frequently scan for commands, parse it into token, check the grammar and syntax and either produce an error or an output the resulting command. 32 | 33 | ```c 34 | // keep getline in a loop in case interruption occurs 35 | int again = 1; 36 | while (again) { 37 | again = 0; 38 | printf("%s", getprompt()); 39 | linebuffer = NULL; 40 | len = 0; 41 | ssize_t nread = getline(&linebuffer, &len, stdin); 42 | if (nread <= 0 && errno == EINTR) { 43 | again = 1; // signal interruption, read again 44 | clearerr(stdin); // clear the error 45 | } 46 | } 47 | 48 | // user pressed ctrl-D 49 | if (feof(stdin)) { 50 | exit(0); 51 | return 0; 52 | } 53 | 54 | // lexically analyze and build a list of tokens 55 | lexer_build(linebuffer, len, &lexerbuf); 56 | ``` 57 | 58 | Until now, we have scanned the user input, have broken them down into array of lexis or tokens and now we try and run them through a parser which parses them into an [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree). If there is an error in the user grammar, the parse will throw out an error. 59 | 60 | ```c 61 | // parse the tokens into an abstract syntax tree 62 | if (!lexerbuf.ntoks || parse(&lexerbuf, &exectree) != 0) { 63 | continue; 64 | } 65 | execute_syntax_tree(exectree); 66 | ``` 67 | 68 | # The Lexical Analyzer 69 | 70 | The lexical analysis is probably the most simplest, yet an important part of the shell. All it does is simply break the input string from the user to a series of tokens. Now tokens could either be a character or a series of characters. It also groups characters that are inside a single or double quotations. 71 | 72 | It is also able to expand wildcard characters. This is done via simply using the [glob ](http://linux.die.net/man/3/glob)POSIX function. It is probably better to have a look at the [lexer_build](https://github.com/Swoorup/mysh/blob/master/lexer.c#L97) function in lexer.c 73 | 74 | # The Syntax Tree Parser 75 | 76 | This topic is probably the most actively discussed topic in compiler theory and designs. In this case, I have a very simply implementation based on recursive parsing technique. 77 | 78 | After we get the tokens from the lexer, we feed them to the parser or the syntax tree builder. This is done entirely in [parser.c](https://github.com/Swoorup/mysh/blob/master/parser.c#L418). For those who do not know what a syntax tree is, it is the most important part of the compiler/interpreter. Put simply, it is a binary tree-like data structure that holds tokens and operations in order of the execution. Now, there are automatic ways to actually define the whole functions that parses texts in syntax tree. In this one, I have implemented it manually for the sake of simplicity. 79 | 80 | The shell language grammer is defined as follows in [Backus–Naur form](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form): 81 | 82 | ```c 83 | :: 84 | | '&' 85 | | '&' 86 | | ';' 87 | | ';' 88 | 89 | ::= 90 | | < job > '|' < command > 91 | 92 | 93 | | '<' 94 | | '>' 95 | 96 | ::= 97 | | 98 | ``` 99 | 100 | 101 | You can observe in parser.c that for all the productions in the grammer, set of functions are defined which validates each rules along with the main function that calls those set for the equivalent production. The purpose is to recursively check if the order of tokens belongs to a particular grammer. This reduces down to check for terminal symbols and non-terminals. In case of a non-terminal the function sets equivalent to that production is simply called. 102 | 103 | Our topmost grammer is the command-line grammer. So in the parse function, we first checks if the list of tokens in the sequential order is a command-line production by calling CMDLINE() which tests all its rules. 104 | 105 | The astree functions which is used throught parser.c simply define the data structure for a binary tree. 106 | 107 | ```c 108 | 109 | typedef struct ASTreeNode 110 | { 111 | int type; 112 | char* szData; 113 | struct ASTreeNode* left; 114 | struct ASTreeNode* right; 115 | 116 | } ASTreeNode; 117 | ``` 118 | # Execution 119 | 120 | After we build our syntax tree, it is quite easy to traverse through out the tokens in order and make executions if necessary. We are certain that our syntax tree works because the parser would throw an error in the first place if it wasn't. The syntax tree is executed from [execute_syntax_tree](https://github.com/Swoorup/mysh/blob/master/execute.c#L139) function in execute.c. I hope its self-explanatary enough as adding some more explanations here would simply be a burden to read. :P 121 | 122 | Phew! Well I hope that was informative. 123 | 124 | -Swoorup 125 | -------------------------------------------------------------------------------- /astree.c: -------------------------------------------------------------------------------- 1 | #include "astree.h" 2 | #include 3 | #include 4 | 5 | void ASTreeAttachBinaryBranch(ASTreeNode* root, ASTreeNode* leftNode, ASTreeNode* rightNode) 6 | { 7 | assert(root != NULL); 8 | root->left = leftNode; 9 | root->right = rightNode; 10 | } 11 | 12 | void ASTreeNodeSetType(ASTreeNode* node, NodeType nodetype) 13 | { 14 | assert(node != NULL); 15 | node->type = nodetype; 16 | } 17 | 18 | void ASTreeNodeSetData(ASTreeNode* node, char* data) 19 | { 20 | assert(node != NULL); 21 | if(data != NULL) { 22 | node->szData = data; 23 | node->type |= NODE_DATA; 24 | } 25 | } 26 | 27 | void ASTreeNodeDelete(ASTreeNode* node) 28 | { 29 | if (node == NULL) 30 | return; 31 | 32 | if (node->type & NODE_DATA) 33 | free(node->szData); 34 | 35 | 36 | ASTreeNodeDelete(node->left); 37 | ASTreeNodeDelete(node->right); 38 | free(node); 39 | } 40 | -------------------------------------------------------------------------------- /astree.h: -------------------------------------------------------------------------------- 1 | #ifndef ASTREE_H 2 | #define ASTREE_H 3 | 4 | typedef enum { 5 | NODE_PIPE = (1 << 0), 6 | NODE_BCKGRND = (1 << 1), 7 | NODE_SEQ = (1 << 2), 8 | NODE_REDIRECT_IN = (1 << 3), 9 | NODE_REDIRECT_OUT = (1 << 4), 10 | NODE_CMDPATH = (1 << 5), 11 | NODE_ARGUMENT = (1 << 6), 12 | 13 | NODE_DATA = (1 << 7), 14 | } NodeType; 15 | 16 | typedef struct ASTreeNode 17 | { 18 | int type; 19 | char* szData; 20 | struct ASTreeNode* left; 21 | struct ASTreeNode* right; 22 | 23 | } ASTreeNode; 24 | 25 | #define NODETYPE(a) (a & (~NODE_DATA)) // get the type of the nodes 26 | 27 | void ASTreeAttachBinaryBranch (ASTreeNode * root , ASTreeNode * leftNode , ASTreeNode * rightNode); 28 | void ASTreeNodeSetType (ASTreeNode * node , NodeType nodetype ); 29 | void ASTreeNodeSetData (ASTreeNode * node , char * data ); 30 | void ASTreeNodeDelete (ASTreeNode * node ); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /command.c: -------------------------------------------------------------------------------- 1 | #include "command.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | char* prompt = NULL; 12 | bool signalset = false; 13 | void (*SIGINT_handler)(int); 14 | 15 | void set_prompt(char* str) 16 | { 17 | if (prompt != NULL) 18 | free(prompt); 19 | 20 | prompt = malloc(strlen(str) + 1); 21 | strcpy(prompt, str); 22 | } 23 | 24 | char* getprompt() 25 | { 26 | return prompt; 27 | } 28 | 29 | void ignore_signal_for_shell() 30 | { 31 | signalset = true; 32 | 33 | // ignore "Ctrl-C" 34 | SIGINT_handler = signal(SIGINT, SIG_IGN); 35 | // ignore "Ctrl-Z" 36 | signal(SIGTSTP, SIG_IGN); 37 | // ignore "Ctrl-\" 38 | signal(SIGQUIT, SIG_IGN); 39 | } 40 | 41 | // restore Ctrl-C signal in the child process 42 | void restore_sigint_in_child() 43 | { 44 | if (signalset) 45 | signal(SIGINT, SIGINT_handler); 46 | } 47 | 48 | // built-in command cd 49 | void execute_cd(CommandInternal* cmdinternal) 50 | { 51 | if (cmdinternal->argc == 1) { 52 | struct passwd *pw = getpwuid(getuid()); 53 | const char *homedir = pw->pw_dir; 54 | chdir(homedir); 55 | } 56 | else if (cmdinternal->argc > 2) 57 | printf("cd: Too many arguments\n"); 58 | else { 59 | if (chdir(cmdinternal->argv[1]) != 0) 60 | perror(cmdinternal->argv[1]); 61 | } 62 | } 63 | 64 | // built-in command prompt 65 | void execute_prompt(CommandInternal* cmdinternal) 66 | { 67 | if (cmdinternal->argc == 1) 68 | printf("prompt: Please specify the prompt string\n"); 69 | else if (cmdinternal->argc > 2) 70 | printf("prompt: Too many arguments\n"); 71 | else { 72 | set_prompt(cmdinternal->argv[1]); 73 | } 74 | } 75 | 76 | // built-in command pwd 77 | void execute_pwd(CommandInternal* cmdinternal) 78 | { 79 | pid_t pid; 80 | if((pid = fork()) == 0 ) { 81 | if (cmdinternal->redirect_out) { 82 | int fd = open(cmdinternal->redirect_out, O_WRONLY | O_CREAT | O_TRUNC, 83 | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 84 | if (fd == -1) { 85 | perror(cmdinternal->redirect_out); 86 | exit(1); 87 | } 88 | 89 | dup2(fd, STDOUT_FILENO); 90 | } 91 | 92 | if (cmdinternal->stdout_pipe) 93 | dup2(cmdinternal->pipe_write, STDOUT_FILENO); 94 | 95 | char cwd[1024]; 96 | if (getcwd(cwd, sizeof(cwd)) != NULL) 97 | fprintf(stdout, "%s\n", cwd); 98 | else 99 | perror("getcwd() error"); 100 | 101 | exit(0); 102 | } 103 | else if (pid < 0) { 104 | perror("fork"); 105 | return; 106 | } 107 | else 108 | while (waitpid(pid, NULL, 0) <= 0); 109 | 110 | return; 111 | } 112 | 113 | void zombie_process_handler(int signum) 114 | { 115 | int more = 1; // more zombies to claim 116 | pid_t pid; // pid of the zombie 117 | int status; // termination status of the zombie 118 | 119 | while (more) { 120 | pid = waitpid(-1, &status, WNOHANG); 121 | if (pid > 0) 122 | printf("\n%d terminated\n", pid); 123 | if (pid<=0) 124 | more = 0; 125 | } 126 | } 127 | 128 | void execute_command_internal(CommandInternal* cmdinternal) 129 | { 130 | 131 | if (cmdinternal->argc < 0) 132 | return; 133 | 134 | // check for built-in commands 135 | if (strcmp(cmdinternal->argv[0], "cd") == 0) { 136 | execute_cd(cmdinternal); 137 | return; 138 | } 139 | else if (strcmp(cmdinternal->argv[0], "prompt") == 0) { 140 | execute_prompt(cmdinternal); 141 | return; 142 | } 143 | else if (strcmp(cmdinternal->argv[0], "pwd") == 0) 144 | return execute_pwd(cmdinternal); 145 | else if(strcmp(cmdinternal->argv[0], "exit") == 0) { 146 | exit(0); 147 | return; 148 | } 149 | 150 | pid_t pid; 151 | if((pid = fork()) == 0 ) { 152 | // restore the signals in the child process 153 | restore_sigint_in_child(); 154 | 155 | // store the stdout file desc 156 | int stdoutfd = dup(STDOUT_FILENO); 157 | 158 | // for bckgrnd jobs redirect stdin from /dev/null 159 | if (cmdinternal->asynchrnous) { 160 | int fd = open("/dev/null",O_RDWR); 161 | if (fd == -1) { 162 | perror("/dev/null"); 163 | exit(1); 164 | } 165 | dup2(fd, STDIN_FILENO); 166 | } 167 | 168 | // redirect stdin from file if specified 169 | if (cmdinternal->redirect_in) { 170 | int fd = open(cmdinternal->redirect_in, O_RDONLY); 171 | if (fd == -1) { 172 | perror(cmdinternal->redirect_in); 173 | exit(1); 174 | } 175 | 176 | dup2(fd, STDIN_FILENO); 177 | } 178 | 179 | // redirect stdout to file if specified 180 | else if (cmdinternal->redirect_out) { 181 | int fd = open(cmdinternal->redirect_out, O_WRONLY | O_CREAT | O_TRUNC, 182 | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 183 | if (fd == -1) { 184 | perror(cmdinternal->redirect_out); 185 | exit(1); 186 | } 187 | 188 | dup2(fd, STDOUT_FILENO); 189 | } 190 | 191 | // read stdin from pipe if present 192 | if (cmdinternal->stdin_pipe) 193 | dup2(cmdinternal->pipe_read, STDIN_FILENO); 194 | 195 | // write stdout to pipe if present 196 | if (cmdinternal->stdout_pipe) 197 | dup2(cmdinternal->pipe_write, STDOUT_FILENO); 198 | 199 | if (execvp(cmdinternal->argv[0], cmdinternal->argv) == -1) { 200 | // restore the stdout for displaying error message 201 | dup2(stdoutfd, STDOUT_FILENO); 202 | 203 | printf("Command not found: \'%s\'\n", cmdinternal->argv[0]); 204 | exit(1); 205 | } 206 | 207 | 208 | } 209 | else if (pid < 0) { 210 | perror("fork"); 211 | return; 212 | } 213 | 214 | if (!cmdinternal->asynchrnous) 215 | { 216 | // wait till the process has not finished 217 | while (waitpid(pid, NULL, 0) <= 0); 218 | } 219 | else 220 | { 221 | // set the sigchild handler for the spawned process 222 | printf("%d started\n", pid); 223 | struct sigaction act; 224 | act.sa_flags = 0; 225 | act.sa_handler = zombie_process_handler; 226 | sigfillset( & (act.sa_mask) ); // to block all 227 | 228 | if (sigaction(SIGCHLD, &act, NULL) != 0) 229 | perror("sigaction"); 230 | } 231 | 232 | return; 233 | } 234 | 235 | int init_command_internal(ASTreeNode* simplecmdNode, 236 | CommandInternal* cmdinternal, 237 | bool async, 238 | bool stdin_pipe, 239 | bool stdout_pipe, 240 | int pipe_read, 241 | int pipe_write, 242 | char* redirect_in, 243 | char* redirect_out) 244 | { 245 | if (simplecmdNode == NULL || !(NODETYPE(simplecmdNode->type) == NODE_CMDPATH)) 246 | { 247 | cmdinternal->argc = 0; 248 | return -1; 249 | } 250 | 251 | ASTreeNode* argNode = simplecmdNode; 252 | 253 | int i = 0; 254 | while (argNode != NULL && (NODETYPE(argNode->type) == NODE_ARGUMENT || NODETYPE(argNode->type) == NODE_CMDPATH)) { 255 | argNode = argNode->right; 256 | i++; 257 | } 258 | 259 | cmdinternal->argv = (char**)malloc(sizeof(char*) * (i + 1)); 260 | argNode = simplecmdNode; 261 | i = 0; 262 | while (argNode != NULL && (NODETYPE(argNode->type) == NODE_ARGUMENT || NODETYPE(argNode->type) == NODE_CMDPATH)) { 263 | cmdinternal->argv[i] = (char*)malloc(strlen(argNode->szData) + 1); 264 | strcpy(cmdinternal->argv[i], argNode->szData); 265 | 266 | argNode = argNode->right; 267 | i++; 268 | } 269 | 270 | cmdinternal->argv[i] = NULL; 271 | cmdinternal->argc = i; 272 | 273 | cmdinternal->asynchrnous = async; 274 | cmdinternal->stdin_pipe = stdin_pipe; 275 | cmdinternal->stdout_pipe = stdout_pipe; 276 | cmdinternal->pipe_read = pipe_read; 277 | cmdinternal->pipe_write = pipe_write; 278 | cmdinternal->redirect_in = redirect_in; 279 | cmdinternal->redirect_out = redirect_out; 280 | 281 | return 0; 282 | } 283 | 284 | void destroy_command_internal(CommandInternal* cmdinternal) 285 | { 286 | int i; 287 | for (i = 0; i < cmdinternal->argc; i++) 288 | free(cmdinternal->argv[i]); 289 | 290 | free(cmdinternal->argv); 291 | cmdinternal->argc = 0; 292 | } 293 | -------------------------------------------------------------------------------- /command.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMAND_H 2 | #define COMMAND_H 3 | 4 | #include 5 | #include 6 | #include "astree.h" 7 | 8 | struct CommandInternal 9 | { 10 | int argc; 11 | char **argv; 12 | bool stdin_pipe; 13 | bool stdout_pipe; 14 | int pipe_read; 15 | int pipe_write; 16 | char* redirect_in; 17 | char* redirect_out; 18 | bool asynchrnous; 19 | }; 20 | 21 | typedef struct CommandInternal CommandInternal; 22 | 23 | void set_prompt(char* str); 24 | char* getprompt(); 25 | void ignore_signal_for_shell(); 26 | void execute_cd(CommandInternal* cmdinternal); 27 | void execute_prompt(CommandInternal* cmdinternal); 28 | void execute_pwd(CommandInternal* cmdinternal); 29 | void execute_command_internal(CommandInternal* cmdinternal); 30 | int init_command_internal(ASTreeNode* simplecmdNode, 31 | CommandInternal* cmdinternal, 32 | bool async, 33 | bool stdin_pipe, 34 | bool stdout_pipe, 35 | int pipe_read, 36 | int pipe_write, 37 | char* redirect_in, 38 | char* redirect_out 39 | ); 40 | void destroy_command_internal(CommandInternal* cmdinternal); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /execute.c: -------------------------------------------------------------------------------- 1 | #include "command.h" 2 | #include 3 | #include 4 | 5 | void execute_simple_command(ASTreeNode* simple_cmd_node, 6 | bool async, 7 | bool stdin_pipe, 8 | bool stdout_pipe, 9 | int pipe_read, 10 | int pipe_write, 11 | char* redirect_in, 12 | char* redirect_out 13 | ) 14 | { 15 | CommandInternal cmdinternal; 16 | init_command_internal(simple_cmd_node, &cmdinternal, async, stdin_pipe, stdout_pipe, 17 | pipe_read, pipe_write, redirect_in, redirect_out 18 | ); 19 | execute_command_internal(&cmdinternal); 20 | destroy_command_internal(&cmdinternal); 21 | } 22 | 23 | void execute_command(ASTreeNode* cmdNode, 24 | bool async, 25 | bool stdin_pipe, 26 | bool stdout_pipe, 27 | int pipe_read, 28 | int pipe_write) 29 | { 30 | if (cmdNode == NULL) 31 | return; 32 | 33 | switch (NODETYPE(cmdNode->type)) 34 | { 35 | case NODE_REDIRECT_IN: // right side contains simple cmd node 36 | execute_simple_command(cmdNode->right, 37 | async, 38 | stdin_pipe, 39 | stdout_pipe, 40 | pipe_read, 41 | pipe_write, 42 | cmdNode->szData, NULL 43 | ); 44 | break; 45 | case NODE_REDIRECT_OUT: // right side contains simple cmd node 46 | execute_simple_command(cmdNode->right, 47 | async, 48 | stdin_pipe, 49 | stdout_pipe, 50 | pipe_read, 51 | pipe_write, 52 | NULL, cmdNode->szData 53 | ); 54 | break; 55 | case NODE_CMDPATH: 56 | execute_simple_command(cmdNode, 57 | async, 58 | stdin_pipe, 59 | stdout_pipe, 60 | pipe_read, 61 | pipe_write, 62 | NULL, NULL 63 | ); 64 | break; 65 | } 66 | } 67 | 68 | void execute_pipeline(ASTreeNode* t, bool async) 69 | { 70 | int file_desc[2]; 71 | 72 | pipe(file_desc); 73 | int pipewrite = file_desc[1]; 74 | int piperead = file_desc[0]; 75 | 76 | // read input from stdin for the first job 77 | execute_command(t->left, async, false, true, 0, pipewrite); 78 | ASTreeNode* jobNode = t->right; 79 | 80 | while (jobNode != NULL && NODETYPE(jobNode->type) == NODE_PIPE) 81 | { 82 | close(pipewrite); // close the write end 83 | pipe(file_desc); 84 | pipewrite = file_desc[1]; 85 | 86 | execute_command(jobNode->left, async, true, true, piperead, pipewrite); 87 | close(piperead); 88 | piperead = file_desc[0]; 89 | 90 | jobNode = jobNode->right; 91 | } 92 | 93 | piperead = file_desc[0]; 94 | close(pipewrite); 95 | 96 | // write output to stdout for the last job 97 | execute_command(jobNode, async, true, false, piperead, 0); // only wait for the last command if necessary 98 | close(piperead); 99 | } 100 | 101 | void execute_job(ASTreeNode* jobNode, bool async) 102 | { 103 | if (jobNode == NULL) 104 | return; 105 | 106 | switch (NODETYPE(jobNode->type)) 107 | { 108 | case NODE_PIPE: 109 | execute_pipeline(jobNode, async); 110 | break; 111 | case NODE_CMDPATH: 112 | default: 113 | execute_command(jobNode, async, false, false, 0, 0); 114 | break; 115 | } 116 | } 117 | 118 | void execute_cmdline(ASTreeNode* cmdline) 119 | { 120 | if (cmdline == NULL) 121 | return; 122 | 123 | switch(NODETYPE(cmdline->type)) 124 | { 125 | case NODE_SEQ: 126 | execute_job(cmdline->left, false); 127 | execute_cmdline(cmdline->right); 128 | break; 129 | 130 | case NODE_BCKGRND: 131 | execute_job(cmdline->left, true); // job to be background 132 | execute_cmdline(cmdline->right); 133 | break; 134 | default: 135 | execute_job(cmdline, false); 136 | } 137 | } 138 | 139 | void execute_syntax_tree(ASTreeNode* tree) 140 | { 141 | // interpret the syntax tree 142 | execute_cmdline(tree); 143 | } 144 | -------------------------------------------------------------------------------- /execute.h: -------------------------------------------------------------------------------- 1 | #ifndef EXECUTE_H 2 | #define EXECUTE_H 3 | 4 | #include "astree.h" 5 | #include 6 | 7 | void execute_syntax_tree(ASTreeNode* tree); 8 | 9 | #endif -------------------------------------------------------------------------------- /lexer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "lexer.h" 5 | 6 | int getchartype(char c) 7 | { 8 | switch(c) 9 | { 10 | case '\'': 11 | return CHAR_QOUTE; 12 | break; 13 | case '\"': 14 | return CHAR_DQUOTE; 15 | break; 16 | case '|': 17 | return CHAR_PIPE; 18 | break; 19 | case '&': 20 | return CHAR_AMPERSAND; 21 | break; 22 | case ' ': 23 | return CHAR_WHITESPACE; 24 | break; 25 | case ';': 26 | return CHAR_SEMICOLON; 27 | break; 28 | case '\\': 29 | return CHAR_ESCAPESEQUENCE; 30 | break; 31 | case '\t': 32 | return CHAR_TAB; 33 | break; 34 | case '\n': 35 | return CHAR_NEWLINE; 36 | break; 37 | case '>': 38 | return CHAR_GREATER; 39 | break; 40 | case '<': 41 | return CHAR_LESSER; 42 | break; 43 | case 0: 44 | return CHAR_NULL; 45 | break; 46 | default: 47 | return CHAR_GENERAL; 48 | }; 49 | 50 | return CHAR_GENERAL; 51 | } 52 | 53 | void strip_quotes(char* src, char* dest) 54 | { 55 | int n = strlen(src); 56 | if (n <= 1) { 57 | strcpy(dest, src); 58 | return; 59 | } 60 | 61 | int i; 62 | 63 | char lastquote = 0; 64 | int j = 0; 65 | 66 | for (i=0; i < n; i++) 67 | { 68 | char c = src[i]; 69 | if ((c == '\'' || c == '\"') && lastquote == 0) 70 | lastquote = c; 71 | else if (c == lastquote) 72 | lastquote = 0; 73 | else 74 | dest[j++] = c; 75 | } 76 | 77 | dest[j] = 0; 78 | } 79 | 80 | void tok_init(tok_t* tok, int datasize) 81 | { 82 | tok->data = malloc(datasize + 1); // 1 for null terminator 83 | tok->data[0] = 0; 84 | 85 | tok->type = CHAR_NULL; 86 | tok->next = NULL; 87 | } 88 | 89 | void tok_destroy(tok_t* tok) { 90 | if (tok != NULL) { 91 | free(tok->data); 92 | tok_destroy(tok->next); 93 | free(tok); 94 | } 95 | } 96 | 97 | int lexer_build(char* input, int size, lexer_t* lexerbuf) 98 | { 99 | if (lexerbuf == NULL) 100 | return -1; 101 | 102 | if (size == 0) { 103 | lexerbuf->ntoks = 0; 104 | return 0; 105 | } 106 | 107 | lexerbuf->llisttok = malloc(sizeof(tok_t)); 108 | 109 | // allocate the first token 110 | tok_t* token = lexerbuf->llisttok; 111 | tok_init(token, size); 112 | 113 | int i = 0; 114 | int j = 0, ntemptok = 0; 115 | 116 | char c; 117 | int state = STATE_GENERAL; 118 | 119 | do 120 | { 121 | c = input[i]; 122 | int chtype = getchartype(c); 123 | 124 | if (state == STATE_GENERAL) 125 | { 126 | switch (chtype) 127 | { 128 | case CHAR_QOUTE: 129 | state = STATE_IN_QUOTE; 130 | token->data[j++] = CHAR_QOUTE; 131 | token->type = TOKEN; 132 | break; 133 | 134 | case CHAR_DQUOTE: 135 | state = STATE_IN_DQUOTE; 136 | token->data[j++] = CHAR_DQUOTE; 137 | token->type = TOKEN; 138 | break; 139 | 140 | case CHAR_ESCAPESEQUENCE: 141 | token->data[j++] = input[++i]; 142 | token->type = TOKEN; 143 | break; 144 | 145 | case CHAR_GENERAL: 146 | token->data[j++] = c; 147 | token->type = TOKEN; 148 | break; 149 | 150 | case CHAR_WHITESPACE: 151 | if (j > 0) { 152 | token->data[j] = 0; 153 | token->next = malloc(sizeof(tok_t)); 154 | token = token->next; 155 | tok_init(token, size - i); 156 | j = 0; 157 | } 158 | break; 159 | 160 | case CHAR_SEMICOLON: 161 | case CHAR_GREATER: 162 | case CHAR_LESSER: 163 | case CHAR_AMPERSAND: 164 | case CHAR_PIPE: 165 | 166 | // end the token that was being read before 167 | if (j > 0) { 168 | token->data[j] = 0; 169 | token->next = malloc(sizeof(tok_t)); 170 | token = token->next; 171 | tok_init(token, size - i); 172 | j = 0; 173 | } 174 | 175 | // next token 176 | token->data[0] = chtype; 177 | token->data[1] = 0; 178 | token->type = chtype; 179 | 180 | token->next = malloc(sizeof(tok_t)); 181 | token = token->next; 182 | tok_init(token, size - i); 183 | break; 184 | } 185 | } 186 | else if (state == STATE_IN_DQUOTE) { 187 | token->data[j++] = c; 188 | if (chtype == CHAR_DQUOTE) 189 | state = STATE_GENERAL; 190 | 191 | } 192 | else if (state == STATE_IN_QUOTE) { 193 | token->data[j++] = c; 194 | if (chtype == CHAR_QOUTE) 195 | state = STATE_GENERAL; 196 | } 197 | 198 | if (chtype == CHAR_NULL) { 199 | if (j > 0) { 200 | token->data[j] = 0; 201 | ntemptok++; 202 | j = 0; 203 | } 204 | } 205 | 206 | i++; 207 | } while (c != '\0'); 208 | 209 | token = lexerbuf->llisttok; 210 | int k = 0; 211 | while (token != NULL) 212 | { 213 | if (token->type == TOKEN) 214 | { 215 | glob_t globbuf; 216 | glob(token->data, GLOB_TILDE, NULL, &globbuf); 217 | 218 | if (globbuf.gl_pathc > 0) 219 | { 220 | k += globbuf.gl_pathc; 221 | // save the next token so we can attach it later 222 | tok_t* saved = token->next; 223 | 224 | // replace the current token with the first one 225 | free(token->data); 226 | token->data = malloc(strlen(globbuf.gl_pathv[0]) + 1); 227 | strcpy(token->data, globbuf.gl_pathv[0]); 228 | 229 | int i; 230 | for (i = 1; i < globbuf.gl_pathc; i++) 231 | { 232 | token->next = malloc(sizeof(tok_t)); 233 | tok_init(token->next, strlen(globbuf.gl_pathv[i])); 234 | token = token->next; 235 | token->type = TOKEN; 236 | strcpy(token->data, globbuf.gl_pathv[i]); 237 | } 238 | 239 | token->next = saved; 240 | } 241 | else { 242 | // token from the user might be inside quotation to escape special characters 243 | // hence strip the quotation symbol 244 | char* stripped = malloc(strlen(token->data) + 1); 245 | strip_quotes(token->data, stripped); 246 | free(token->data); 247 | token->data = stripped; 248 | k++; 249 | } 250 | } 251 | 252 | token = token->next; 253 | } 254 | 255 | lexerbuf->ntoks = k; 256 | return k; 257 | } 258 | 259 | void lexer_destroy(lexer_t* lexerbuf) 260 | { 261 | if (lexerbuf == NULL) 262 | return; 263 | 264 | tok_destroy(lexerbuf->llisttok); 265 | } 266 | -------------------------------------------------------------------------------- /lexer.h: -------------------------------------------------------------------------------- 1 | #ifndef LEXER_H 2 | #define LEXER_H 3 | 4 | enum TokenType{ 5 | CHAR_GENERAL = -1, 6 | CHAR_PIPE = '|', 7 | CHAR_AMPERSAND = '&', 8 | CHAR_QOUTE = '\'', 9 | CHAR_DQUOTE = '\"', 10 | CHAR_SEMICOLON = ';', 11 | CHAR_WHITESPACE = ' ', 12 | CHAR_ESCAPESEQUENCE = '\\', 13 | CHAR_TAB = '\t', 14 | CHAR_NEWLINE = '\n', 15 | CHAR_GREATER = '>', 16 | CHAR_LESSER = '<', 17 | CHAR_NULL = 0, 18 | 19 | TOKEN = -1, 20 | }; 21 | 22 | enum { 23 | STATE_IN_DQUOTE, 24 | STATE_IN_QUOTE, 25 | 26 | STATE_IN_ESCAPESEQ, 27 | STATE_GENERAL, 28 | }; 29 | 30 | typedef struct tok tok_t; 31 | typedef struct lexer lexer_t; 32 | 33 | struct tok { 34 | char* data; 35 | int type; 36 | tok_t* next; 37 | }; 38 | 39 | struct lexer 40 | { 41 | tok_t* llisttok; 42 | int ntoks; 43 | }; 44 | 45 | int lexer_build(char* input, int size, lexer_t* lexerbuf); 46 | void lexer_destroy(lexer_t* lexerbuf); 47 | #endif 48 | -------------------------------------------------------------------------------- /parser.c: -------------------------------------------------------------------------------- 1 | #include "parser.h" 2 | #include "lexer.h" 3 | #include "astree.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /*** Shell Grammer as given in the assignment question 1 ***/ 10 | 11 | /** 12 | * 13 | ::= 14 | | '&' 15 | | '&' 16 | | ';' 17 | | ';' 18 | 19 | ::= 20 | | < job > '|' < command > 21 | 22 | ::= 23 | | '<' 24 | | '>' 25 | 26 | ::= 27 | | 28 | * 29 | * 30 | * 31 | **/ 32 | 33 | /*** Shell Grammer for recursive descent parser ***/ 34 | /*** Removed left recursion and left factoring ***/ 35 | 36 | /** 37 | * 38 | ::= ';' 39 | | ';' 40 | | '&' 41 | | '&' 42 | 43 | 44 | ::= '|' 45 | | 46 | 47 | ::= '<' // this grammer is a bit incorrect, see grammer.llf 48 | | '>' 49 | | 50 | 51 | ::= 52 | 53 | ::= 54 | | (EMPTY) 55 | 56 | * 57 | * 58 | * 59 | **/ 60 | 61 | ASTreeNode* CMDLINE(); // test all command line production orderwise 62 | ASTreeNode* CMDLINE1(); // ';' 63 | ASTreeNode* CMDLINE2(); // ';' 64 | ASTreeNode* CMDLINE3(); // '&' 65 | ASTreeNode* CMDLINE4(); // '&' 66 | ASTreeNode* CMDLINE5(); // 67 | 68 | ASTreeNode* JOB(); // test all job production in order 69 | ASTreeNode* JOB1(); // '|' 70 | ASTreeNode* JOB2(); // 71 | 72 | ASTreeNode* CMD(); // test all command production orderwise 73 | ASTreeNode* CMD1(); // '<' 74 | ASTreeNode* CMD2(); // '>' 75 | ASTreeNode* CMD3(); // 76 | 77 | ASTreeNode* SIMPLECMD(); // test simple cmd production 78 | ASTreeNode* SIMPLECMD1(); // 79 | 80 | ASTreeNode* TOKENLIST(); // test tokenlist production 81 | ASTreeNode* TOKENLIST1(); // 82 | ASTreeNode* TOKENLIST2(); // EMPTY 83 | 84 | // curtok token pointer 85 | tok_t* curtok = NULL; 86 | 87 | bool term(int toketype, char** bufferptr) 88 | { 89 | if (curtok == NULL) 90 | return false; 91 | 92 | if (curtok->type == toketype) 93 | { 94 | if (bufferptr != NULL) { 95 | *bufferptr = malloc(strlen(curtok->data) + 1); 96 | strcpy(*bufferptr, curtok->data); 97 | } 98 | curtok = curtok->next; 99 | return true; 100 | } 101 | 102 | curtok = curtok->next; 103 | return false; 104 | } 105 | 106 | ASTreeNode* CMDLINE() 107 | { 108 | tok_t* save = curtok; 109 | 110 | ASTreeNode* node; 111 | 112 | if ((curtok = save, node = CMDLINE1()) != NULL) 113 | return node; 114 | 115 | if ((curtok = save, node = CMDLINE2()) != NULL) 116 | return node; 117 | 118 | if ((curtok = save, node = CMDLINE3()) != NULL) 119 | return node; 120 | 121 | if ((curtok = save, node = CMDLINE4()) != NULL) 122 | return node; 123 | 124 | if ((curtok = save, node = CMDLINE5()) != NULL) 125 | return node; 126 | 127 | return NULL; 128 | } 129 | 130 | ASTreeNode* CMDLINE1() 131 | { 132 | ASTreeNode* jobNode; 133 | ASTreeNode* cmdlineNode; 134 | ASTreeNode* result; 135 | 136 | if ((jobNode = JOB()) == NULL) 137 | return NULL; 138 | 139 | if (!term(CHAR_SEMICOLON, NULL)) { 140 | ASTreeNodeDelete(jobNode); 141 | return NULL; 142 | } 143 | 144 | if ((cmdlineNode = CMDLINE()) == NULL) { 145 | ASTreeNodeDelete(jobNode); 146 | return NULL; 147 | } 148 | 149 | result = malloc(sizeof(*result)); 150 | ASTreeNodeSetType(result, NODE_SEQ); 151 | ASTreeAttachBinaryBranch(result, jobNode, cmdlineNode); 152 | 153 | return result; 154 | } 155 | 156 | ASTreeNode* CMDLINE2() 157 | { 158 | ASTreeNode* jobNode; 159 | ASTreeNode* result; 160 | 161 | if ((jobNode = JOB()) == NULL) 162 | return NULL; 163 | 164 | if (!term(CHAR_SEMICOLON, NULL)) { 165 | ASTreeNodeDelete(jobNode); 166 | return NULL; 167 | } 168 | 169 | result = malloc(sizeof(*result)); 170 | ASTreeNodeSetType(result, NODE_SEQ); 171 | ASTreeAttachBinaryBranch(result, jobNode, NULL); 172 | 173 | return result; 174 | } 175 | 176 | ASTreeNode* CMDLINE3() 177 | { 178 | ASTreeNode* jobNode; 179 | ASTreeNode* cmdlineNode; 180 | ASTreeNode* result; 181 | 182 | if ((jobNode = JOB()) == NULL) 183 | return NULL; 184 | 185 | if (!term(CHAR_AMPERSAND, NULL)) { 186 | ASTreeNodeDelete(jobNode); 187 | return NULL; 188 | } 189 | 190 | if ((cmdlineNode = CMDLINE()) == NULL) { 191 | ASTreeNodeDelete(jobNode); 192 | return NULL; 193 | } 194 | 195 | result = malloc(sizeof(*result)); 196 | ASTreeNodeSetType(result, NODE_BCKGRND); 197 | ASTreeAttachBinaryBranch(result, jobNode, cmdlineNode); 198 | 199 | return result; 200 | } 201 | 202 | ASTreeNode* CMDLINE4() 203 | { 204 | ASTreeNode* jobNode; 205 | ASTreeNode* result; 206 | 207 | if ((jobNode = JOB()) == NULL) 208 | return NULL; 209 | 210 | if (!term(CHAR_AMPERSAND, NULL)) { 211 | ASTreeNodeDelete(jobNode); 212 | return NULL; 213 | } 214 | 215 | result = malloc(sizeof(*result)); 216 | ASTreeNodeSetType(result, NODE_BCKGRND); 217 | ASTreeAttachBinaryBranch(result, jobNode, NULL); 218 | 219 | return result; 220 | } 221 | 222 | ASTreeNode* CMDLINE5() 223 | { 224 | return JOB(); 225 | } 226 | 227 | ASTreeNode* JOB() 228 | { 229 | tok_t* save = curtok; 230 | 231 | ASTreeNode* node; 232 | 233 | if ((curtok = save, node = JOB1()) != NULL) 234 | return node; 235 | 236 | if ((curtok = save, node = JOB2()) != NULL) 237 | return node; 238 | 239 | return NULL; 240 | } 241 | 242 | ASTreeNode* JOB1() 243 | { 244 | ASTreeNode* cmdNode; 245 | ASTreeNode* jobNode; 246 | ASTreeNode* result; 247 | 248 | if ((cmdNode = CMD()) == NULL) 249 | return NULL; 250 | 251 | if (!term(CHAR_PIPE, NULL)) { 252 | ASTreeNodeDelete(cmdNode); 253 | return NULL; 254 | } 255 | 256 | if ((jobNode = JOB()) == NULL) { 257 | ASTreeNodeDelete(cmdNode); 258 | return NULL; 259 | } 260 | 261 | result = malloc(sizeof(*result)); 262 | ASTreeNodeSetType(result, NODE_PIPE); 263 | ASTreeAttachBinaryBranch(result, cmdNode, jobNode); 264 | 265 | return result; 266 | } 267 | 268 | ASTreeNode* JOB2() 269 | { 270 | return CMD(); 271 | } 272 | 273 | ASTreeNode* CMD() 274 | { 275 | tok_t* save = curtok; 276 | 277 | ASTreeNode* node; 278 | 279 | if ((curtok = save, node = CMD1()) != NULL) 280 | return node; 281 | 282 | if ((curtok = save, node = CMD2()) != NULL) 283 | return node; 284 | 285 | if ((curtok = save, node = CMD3()) != NULL) 286 | return node; 287 | 288 | return NULL; 289 | } 290 | 291 | ASTreeNode* CMD1() 292 | { 293 | ASTreeNode* simplecmdNode; 294 | ASTreeNode* result; 295 | 296 | if ((simplecmdNode = SIMPLECMD()) == NULL) 297 | return NULL; 298 | 299 | if (!term(CHAR_LESSER, NULL)) { 300 | ASTreeNodeDelete(simplecmdNode); 301 | return NULL; 302 | } 303 | 304 | char* filename; 305 | if (!term(TOKEN, &filename)) { 306 | free(filename); 307 | ASTreeNodeDelete(simplecmdNode); 308 | return NULL; 309 | } 310 | 311 | result = malloc(sizeof(*result)); 312 | ASTreeNodeSetType(result, NODE_REDIRECT_IN); 313 | ASTreeNodeSetData(result, filename); 314 | ASTreeAttachBinaryBranch(result, NULL, simplecmdNode); 315 | 316 | return result; 317 | } 318 | 319 | ASTreeNode* CMD2() 320 | { 321 | ASTreeNode* simplecmdNode; 322 | ASTreeNode* result; 323 | 324 | if ((simplecmdNode = SIMPLECMD()) == NULL) 325 | return NULL; 326 | 327 | if (!term(CHAR_GREATER, NULL)) { 328 | ASTreeNodeDelete(simplecmdNode); 329 | return NULL; 330 | } 331 | 332 | char* filename; 333 | if (!term(TOKEN, &filename)) { 334 | free(filename); 335 | ASTreeNodeDelete(simplecmdNode); 336 | return NULL; 337 | } 338 | 339 | result = malloc(sizeof(*result)); 340 | ASTreeNodeSetType(result, NODE_REDIRECT_OUT); 341 | ASTreeNodeSetData(result, filename); 342 | ASTreeAttachBinaryBranch(result, NULL, simplecmdNode); 343 | 344 | return result; 345 | } 346 | 347 | ASTreeNode* CMD3() 348 | { 349 | return SIMPLECMD(); 350 | } 351 | 352 | ASTreeNode* SIMPLECMD() 353 | { 354 | tok_t* save = curtok; 355 | return SIMPLECMD1(); 356 | } 357 | 358 | ASTreeNode* SIMPLECMD1() 359 | { 360 | ASTreeNode* tokenListNode; 361 | ASTreeNode* result; 362 | 363 | char* pathname; 364 | if (!term(TOKEN, &pathname)) 365 | return NULL; 366 | 367 | tokenListNode = TOKENLIST(); 368 | // we don't check whether tokenlistNode is NULL since its a valid grammer 369 | 370 | result = malloc(sizeof(*result)); 371 | ASTreeNodeSetType(result, NODE_CMDPATH); 372 | ASTreeNodeSetData(result, pathname); 373 | ASTreeAttachBinaryBranch(result, NULL, tokenListNode); 374 | 375 | return result; 376 | } 377 | 378 | ASTreeNode* TOKENLIST() 379 | { 380 | tok_t* save = curtok; 381 | 382 | ASTreeNode* node; 383 | 384 | if ((curtok = save, node = TOKENLIST1()) != NULL) 385 | return node; 386 | 387 | if ((curtok = save, node = TOKENLIST2()) != NULL) 388 | return node; 389 | 390 | return NULL; 391 | } 392 | 393 | ASTreeNode* TOKENLIST1() 394 | { 395 | ASTreeNode* tokenListNode; 396 | ASTreeNode* result; 397 | 398 | char* arg; 399 | if (!term(TOKEN, &arg)) 400 | return NULL; 401 | 402 | tokenListNode = TOKENLIST(); 403 | // we don't check whether tokenlistNode is NULL since its a valid grammer 404 | 405 | result = malloc(sizeof(*result)); 406 | ASTreeNodeSetType(result, NODE_ARGUMENT); 407 | ASTreeNodeSetData(result, arg); 408 | ASTreeAttachBinaryBranch(result, NULL, tokenListNode); 409 | 410 | return result; 411 | } 412 | 413 | ASTreeNode* TOKENLIST2() 414 | { 415 | return NULL; 416 | } 417 | 418 | int parse(lexer_t* lexbuf, ASTreeNode** syntax_tree) 419 | { 420 | if (lexbuf->ntoks == 0) 421 | return -1; 422 | 423 | curtok = lexbuf->llisttok; 424 | *syntax_tree = CMDLINE(); 425 | 426 | if (curtok != NULL && curtok->type != 0) 427 | { 428 | printf("Syntax Error near: %s\n", curtok->data); 429 | return -1; 430 | } 431 | 432 | return 0; 433 | } 434 | -------------------------------------------------------------------------------- /parser.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_H 2 | #define PARSER_H 3 | 4 | #include "astree.h" 5 | #include "lexer.h" 6 | 7 | int parse(lexer_t* lexbuf, ASTreeNode** syntax_tree); 8 | 9 | #endif -------------------------------------------------------------------------------- /shell.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "lexer.h" 7 | #include "parser.h" 8 | #include "execute.h" 9 | #include "command.h" 10 | 11 | int main() 12 | { 13 | // ignore Ctrl-\ Ctrl-C Ctrl-Z signals 14 | ignore_signal_for_shell(); 15 | 16 | // set the prompt 17 | set_prompt("swoorup % "); 18 | 19 | while (1) 20 | { 21 | char* linebuffer; 22 | size_t len; 23 | 24 | lexer_t lexerbuf; 25 | ASTreeNode* exectree; 26 | 27 | // keep getline in a loop in case interruption occurs 28 | int again = 1; 29 | while (again) { 30 | again = 0; 31 | printf("%s", getprompt()); 32 | linebuffer = NULL; 33 | len = 0; 34 | ssize_t nread = getline(&linebuffer, &len, stdin); 35 | if (nread <= 0 && errno == EINTR) { 36 | again = 1; // signal interruption, read again 37 | clearerr(stdin); // clear the error 38 | } 39 | } 40 | 41 | // user pressed ctrl-D 42 | if (feof(stdin)) { 43 | exit(0); 44 | return 0; 45 | } 46 | 47 | // lexically analyze and build a list of tokens 48 | lexer_build(linebuffer, len, &lexerbuf); 49 | free(linebuffer); 50 | 51 | // parse the tokens into an abstract syntax tree 52 | if (!lexerbuf.ntoks || parse(&lexerbuf, &exectree) != 0) { 53 | continue; 54 | } 55 | 56 | execute_syntax_tree(exectree); 57 | 58 | // free the structures 59 | ASTreeNodeDelete(exectree); 60 | lexer_destroy(&lexerbuf); 61 | } 62 | 63 | return 0; 64 | } 65 | 66 | --------------------------------------------------------------------------------