├── AUTHORS ├── Makefile ├── README.md ├── __alias.c ├── __cd.c ├── __env.c ├── __exec.c ├── __exit.c ├── __help.c ├── __setenv.c ├── __unsetenv.c ├── _isalnum.c ├── _isalpha.c ├── _isdigit.c ├── _isident.c ├── _isnumber.c ├── _isquote.c ├── _isspace.c ├── _isspecial_double.c ├── _quote.c ├── _signal.c ├── alias.h ├── arrdup.c ├── arrjoin.c ├── atou.c ├── builtins.c ├── builtins.h ├── cmd_to_list.c ├── cmdlist.c ├── cmdtree.c ├── command.h ├── ctype.h ├── dequote.c ├── dict.c ├── dict.h ├── dict_to_env.c ├── env.h ├── env_to_dict.c ├── error.c ├── error.h ├── execute.c ├── expand_aliases.c ├── expand_vars.c ├── gcc ├── getline.c ├── getline.h ├── history.h ├── hsh ├── hsh.c ├── hsh.h ├── info.c ├── info.h ├── input.c ├── list.c ├── list.h ├── man_1_simple_shell ├── mem.c ├── num_to_str.c ├── parse.c ├── path.c ├── path.h ├── quote.c ├── quote.h ├── remove_comments.c ├── split_cmd.c ├── str.c ├── string.h ├── strjoin.c ├── strn.c ├── tokens.c ├── tokens.h └── types.h /AUTHORS: -------------------------------------------------------------------------------- 1 | Contributors 2 | ------------ 3 | 4 | Mmjosh 5 | Victor Preston 6 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = gcc 2 | SRC = $(wildcard *.c) 3 | OBJ = $(SRC:.c=.o) 4 | NAME = hsh 5 | CFLAGS = -Wall -Werror -Wextra -pedantic 6 | RM = rm -f 7 | .PHONY: all clean oclean fclean re 8 | 9 | all: $(OBJ) 10 | $(CC) $(CFLAGS) $(OBJ) -o $(NAME) 11 | clean: 12 | $(RM) *~ 13 | $(RM) *.swp 14 | $(RM) $(NAME) 15 | oclean: 16 | $(RM) $(OBJ) 17 | fclean: 18 | $(RM) *~ 19 | $(RM) *.swp 20 | $(RM) $(NAME) 21 | $(RM) $(OBJ) 22 | re: fclean all 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Implementation of sh in C programming language 2 | 3 | ## Overview 4 | Simple Shell is a Holberton School pair project. The general goal of the project is to write a simple UNIX command interpreter. 5 | ## Content 6 | * [Installation](#Installation) 7 | * [Builtins](#Builtins) 8 | * [General Requirements](#General-Requirements) 9 | * [Target output](#Target-output) 10 | * [List of allowed functions](#List-of-allowed-functions) 11 | * [Compilation](#Compilation) 12 | * [Testing](#Testing) 13 | * [Tasks](#Tasks) 14 | 15 | ## Installation 16 | ``` 17 | git clone https://github.com/patrickdeyoreo/simple_shell.git 18 | cd simple_shell 19 | gcc *.c -o hsh 20 | ``` 21 | 22 | ## Builtins 23 | | Command | Synopsis | Description | 24 | | ------- | -------- | ----------- | 25 | | `alias` | `alias [NAME[='VALUE'] ...]` | Print and define command aliases | 26 | | `cd` | `cd [DIRECTORY]` | Change the current working directory | 27 | | `env` | `env` | Print the environment | 28 | | `exit` | `exit [STATUS]` | Exit the shell | 29 | | `help` | `help [BUILTIN]` | Print a help messages for built-ins | 30 | | `setenv` | `setenv VARIABLE VALUE` | Set an environment variable | 31 | | `unsetenv` | `unsetenv VARIABLE` | Unset an environment variable | 32 | 33 | ## General Requirements 34 | * Allowed editors: vi, vim, emacs 35 | * All your files will be compiled on Ubuntu 14.04 LTS 36 | * Your C programs and functions will be compiled with gcc 4.8.4 using the flags -Wall -Werror -Wextra and -pedantic 37 | * All your files should end with a new line 38 | * A README.md file, at the root of the folder of the project is mandatory 39 | * Your code should use the Betty style. It will be checked using betty-style.pl and betty-doc.pl 40 | * No more than 5 functions per file 41 | * All your header files should be include guarded 42 | * Use system calls only when you need to 43 | 44 | ## Target output 45 | * Unless specified otherwise, your program must have the exact same output as sh (/bin/sh) as well as the exact same error output. 46 | * The only difference is when you print an error, the name of the program must be equivalent to your argv[0] 47 | 48 | - Example of error with sh: 49 | ``` 50 | $ echo "qwerty" | /bin/sh 51 | /bin/sh: 1: qwerty: not found 52 | $ echo "qwerty" | /bin/../bin/sh 53 | /bin/../bin/sh: 1: qwerty: not found 54 | $ 55 | ``` 56 | - Same error with your program hsh: 57 | ``` 58 | $ echo "qwerty" | ./hsh 59 | ./hsh: 1: qwerty: not found 60 | $ echo "qwerty" | ./././hsh 61 | ./././hsh: 1: qwerty: not found 62 | $ 63 | ``` 64 | 65 | ## List of allowed functions / syscalls 66 | | Functions | Reference | 67 | | --------- | --------- | 68 | | `access` | [man 2 access](https://linux.die.net/man/2/access) | 69 | | `chdir` | [man 2 chdir](https://linux.die.net/man/2/chdir) | 70 | | `close` | [man 2 close](https://linux.die.net/man/2/close) | 71 | | `closedir` | [man 3 closedir](https://linux.die.net/man/3/closedir) | 72 | | `execve` | [man 2 execve](https://linux.die.net/man/2/execve) | 73 | | `exit` | [man 3 exit](https://linux.die.net/man/3/exit) | 74 | | `_exit` | [man 2 \_exit](https://linux.die.net/man/2/_exit) | 75 | | `fflush` | [man 3 fflush](https://linux.die.net/man/3/fflush) | 76 | | `fork` | [man 2 fork](https://linux.die.net/man/2/fork) | 77 | | `free` | [man 3 free](https://linux.die.net/man/3/free) | 78 | | `getcwd` | [man 3 getcwd](https://linux.die.net/man/3/getcwd) | 79 | | `getline` | [man 3 getline](https://linux.die.net/man/3/getline) | 80 | | `isatty` | [man 3 isatty](https://linux.die.net/man/3/isatty) | 81 | | `kill` | [man 2 kill](https://linux.die.net/man/2/kill) | 82 | | `malloc` | [man 3 malloc](https://linux.die.net/man/3/malloc) | 83 | | `open` | [man 2 open](https://linux.die.net/man/2/open) | 84 | | `opendir` | [man 3 opendir](https://linux.die.net/man/3/opendir) | 85 | | `perror` | [man 3 perror](https://linux.die.net/man/3/perror) | 86 | | `read` | [man 2 read](https://linux.die.net/man/2/read) | 87 | | `readdir` | [man 3 readdir](https://linux.die.net/man/3/readdir) | 88 | | `signal` | [man 2 signal](https://linux.die.net/man/2/signal) | 89 | | `stat` | [(\_\_xstat) man 2 stat](https://linux.die.net/man/2/stat) | 90 | | `lstat` | [(\_\_lxstat) man 2 lstat](https://linux.die.net/man/2/lstat) | 91 | | `fstat` | [(\_\_fxstat) man 2 fstat](https://linux.die.net/man/2/fstat) | 92 | | `strtok` | [man 3 strtok](https://linux.die.net/man/3/strtok) | 93 | | `wait` | [man 2 wait](https://linux.die.net/man/2/wait) | 94 | | `waitpid` | [man 2 waitpid](https://linux.die.net/man/2/waitpid) | 95 | | `wait3` | [man 2 wait3](https://linux.die.net/man/2/wait3) | 96 | | `wait4` | [man 2 wait4](https://linux.die.net/man/2/wait4) | 97 | | `write` | [man 2 write](https://linux.die.net/man/2/write) | 98 | 99 | ## Compilation 100 | ``` 101 | gcc -Wall -Werror -Wextra -pedantic *.c -o hsh 102 | ``` 103 | ## Testing 104 | - Interactive mode: 105 | ``` 106 | $ ./hsh 107 | ($) /bin/ls 108 | hsh main.c shell.c 109 | ($) 110 | ($) exit 111 | $ 112 | ``` 113 | 114 | - Non-interactive mode: 115 | ``` 116 | $ echo "/bin/ls" | ./hsh 117 | hsh main.c shell.c test_ls_2 118 | $ 119 | $ cat test_ls_2 120 | /bin/ls 121 | /bin/ls 122 | $ 123 | $ cat test_ls_2 | ./hsh 124 | hsh main.c shell.c test_ls_2 125 | hsh main.c shell.c test_ls_2 126 | $ 127 | ``` 128 | ## Tasks 129 | ### Mandatory: 130 | 1. README, man, AUTHORS 131 | - Write a README 132 | - Write a man for your shell. 133 | - You should have an AUTHORS file at the root of your repository, listing all individuals having contributed content to the repository. 134 | 135 | 2. Simple shell 0.1 136 | - Write a UNIX command line interpreter. 137 | - Your Shell should: 138 | Display a prompt and wait for the user to type a command. A command line always ends with a new line. 139 | The prompt is displayed again each time a command has been executed. 140 | The command lines are simple, no semicolons, no pipes, no redirections or any other advanced features. 141 | The command lines are made only of one word. No arguments will be passed to programs. 142 | If an executable cannot be found, print an error message and display the prompt again. 143 | Handle errors. 144 | You have to handle the “end of file” condition (Ctrl+D) 145 | - You don’t have to: 146 | use the PATH 147 | implement built-ins 148 | handle special characters : ", ', `, \, *, &, # 149 | be able to move the cursor 150 | handle commands with arguments 151 | 152 | 3. Simple shell 0.2 153 | - Handle command lines with arguments 154 | 155 | 4. Simple shell 0.3 156 | - Handle the PATH 157 | 158 | 5. Simple shell 0.4 159 | - Implement the exit built-in, that exits the shell 160 | - Usage: exit 161 | - You don’t have to handle any argument to the built-in exit 162 | 163 | 6. Simple shell 1.0 164 | - Implement the env built-in, that prints the current environment 165 | 166 | 7. Write a blogpost "What happens when you type ls -l in the shell" 167 | 168 | ### Advanced 169 | 1. Test suite 170 | - Contribute to a test suite for your shell 171 | 172 | 2. Simple shell 0.2.1 173 | - Write your own strtok function 174 | 175 | 3. Simple shell 0.4.1 176 | - handle arguments for the built-in exit 177 | 178 | 4. Simple shell 0.4.2 179 | - Handle Ctrl+C: your shell should not quit when the user inputs ^C 180 | 181 | 5. setenv, unsetenv 182 | - Implement the setenv and unsetenv builtin commands 183 | 184 | 6. cd 185 | - Implement the builtin command cd 186 | 187 | 7. ; 188 | - Handle the commands separator ; 189 | 190 | 8. alias 191 | - Implement the alias builtin command 192 | 193 | 19. Comments 194 | - Handle comments (#) 195 | 196 | 10. help 197 | - Implement the help built-in 198 | 199 | 11. File as an input 200 | - Your shell should take a file as a command line argument 201 | 202 | ## Authors 203 | * [Mmjosh](https://github.com/Mmjosh) 204 | * [Victor Preston](https://github.com/victorpreston) 205 | -------------------------------------------------------------------------------- /__alias.c: -------------------------------------------------------------------------------- 1 | #include "builtins.h" 2 | 3 | /** 4 | * __alias_add - add an alias to a list of aliases 5 | * @aliases: a pointer to a list of aliases 6 | * @name: the name of the alias 7 | * @value: the value of the alias 8 | */ 9 | void __alias_add(alias_t **aliases, const char *name, const char *value) 10 | { 11 | alias_t *alias = get_dict_node(aliases ? *aliases : NULL, name); 12 | 13 | if (alias) 14 | { 15 | free(alias->val); 16 | alias->val = _strdup(value); 17 | } 18 | else 19 | { 20 | add_dict_node_end(aliases, name, value); 21 | } 22 | } 23 | 24 | 25 | /** 26 | * __alias_print - print an alias 27 | * @alias: the alias to print 28 | */ 29 | void __alias_print(alias_t *alias) 30 | { 31 | write(STDOUT_FILENO, alias->key, _strlen(alias->key)); 32 | write(STDOUT_FILENO, "='", 2); 33 | write(STDOUT_FILENO, alias->val, _strlen(alias->val)); 34 | write(STDOUT_FILENO, "'\n", 2); 35 | } 36 | 37 | 38 | /** 39 | * __alias - create and display aliases 40 | * @info: shell info struct 41 | * 42 | * Return: status 43 | */ 44 | int __alias(info_t *info) 45 | { 46 | alias_t *alias; 47 | char *name, **args = info->tokens + 1; 48 | ssize_t name_len; 49 | 50 | info->status = EXIT_SUCCESS; 51 | if (*args) 52 | { 53 | do { 54 | name_len = _strchr(*args, '='); 55 | if (name_len == -1) 56 | { 57 | alias = get_dict_node(info->aliases, *args); 58 | if (alias) 59 | { 60 | __alias_print(alias); 61 | } 62 | else 63 | { 64 | perrorl("not found", *info->tokens, *args, NULL); 65 | info->status = EXIT_FAILURE; 66 | } 67 | } 68 | else 69 | { 70 | name = _strndup(*args, name_len); 71 | __alias_add(&info->aliases, name, *args + name_len + 1); 72 | free(name); 73 | } 74 | } while (*++args); 75 | } 76 | else 77 | { 78 | for (alias = info->aliases; alias; alias = alias->next) 79 | __alias_print(alias); 80 | } 81 | return (info->status); 82 | } 83 | -------------------------------------------------------------------------------- /__cd.c: -------------------------------------------------------------------------------- 1 | #include "builtins.h" 2 | 3 | /** 4 | * __cd_error - print an error upon failure to change directory 5 | * @info: shell info 6 | * @dir: directory 7 | */ 8 | void __cd_error(info_t *info, char *dir) 9 | { 10 | char *error = strjoin(NULL, " ", "can't cd to", dir); 11 | 12 | perrorl_default(*info->argv, info->lineno, error, *info->tokens, NULL); 13 | 14 | info->status = 2; 15 | 16 | free(error); 17 | } 18 | 19 | 20 | /** 21 | * __cd_success - update the environment upon success 22 | * @info: shell info 23 | */ 24 | void __cd_success(info_t *info) 25 | { 26 | char **tokens = info->tokens; 27 | char *setenv_tokens[] = {"setenv", NULL, NULL, NULL}; 28 | 29 | info->tokens = setenv_tokens; 30 | 31 | setenv_tokens[1] = "OLDPWD"; 32 | setenv_tokens[2] = info->cwd; 33 | 34 | __setenv(info); 35 | 36 | free(info->cwd); 37 | info->cwd = getcwd(NULL, 0); 38 | 39 | setenv_tokens[1] = "PWD"; 40 | setenv_tokens[2] = info->cwd; 41 | 42 | __setenv(info); 43 | 44 | info->tokens = tokens; 45 | 46 | info->status = EXIT_SUCCESS; 47 | } 48 | 49 | 50 | /** 51 | * __cd - changes the directory 52 | * @info: arguments passed 53 | * 54 | * Return: int 55 | */ 56 | int __cd(info_t *info) 57 | { 58 | char *dir = NULL, **args = info->tokens + 1; 59 | 60 | info->status = EXIT_SUCCESS; 61 | if (*args) 62 | { 63 | if (!_strcmp(*args, "-")) 64 | { 65 | dir = get_dict_val(info->env, "OLDPWD"); 66 | if (!dir) 67 | dir = info->cwd; 68 | 69 | info->status = chdir(dir); 70 | if (!info->status) 71 | { 72 | write(STDOUT_FILENO, dir, _strlen(dir)); 73 | write(STDOUT_FILENO, "\n", 1); 74 | } 75 | } 76 | else 77 | { 78 | dir = *args; 79 | info->status = chdir(dir); 80 | } 81 | } 82 | else 83 | { 84 | dir = get_dict_val(info->env, "HOME"); 85 | if (dir) 86 | info->status = chdir(dir); 87 | } 88 | if (info->status != -1) 89 | __cd_success(info); 90 | else 91 | __cd_error(info, dir); 92 | 93 | return (info->status); 94 | } 95 | -------------------------------------------------------------------------------- /__env.c: -------------------------------------------------------------------------------- 1 | #include "builtins.h" 2 | 3 | /** 4 | * __env - displays environment 5 | * @info: arguments passed 6 | * Return: int 7 | */ 8 | int __env(info_t *info) 9 | { 10 | env_t *var; 11 | 12 | info->status = EXIT_SUCCESS; 13 | 14 | for (var = info->env; var; var = var->next) 15 | { 16 | if (var->key) 17 | write(STDOUT_FILENO, var->key, _strlen(var->key)); 18 | write(STDOUT_FILENO, "=", 1); 19 | if (var->val) 20 | write(STDOUT_FILENO, var->val, _strlen(var->val)); 21 | write(STDOUT_FILENO, "\n", 1); 22 | } 23 | return (info->status); 24 | } 25 | -------------------------------------------------------------------------------- /__exec.c: -------------------------------------------------------------------------------- 1 | #include "builtins.h" 2 | 3 | /** 4 | * __exec - replace the running shell with a new program 5 | * @info: arguments passed 6 | * Return: int 7 | */ 8 | int __exec(info_t *info) 9 | { 10 | char *exe, **args = info->tokens + 1, **env = NULL; 11 | 12 | if (!*args) 13 | return ((info->status = EXIT_SUCCESS)); 14 | 15 | info->tokens = args; 16 | args = arrdup(args); 17 | 18 | if (_strchr(*args, '/') == -1) 19 | { 20 | free_list(&info->path); 21 | info->path = str_to_list(get_dict_val(info->env, "PATH"), ':'); 22 | exe = search_path(info, info->path); 23 | } 24 | else 25 | { 26 | exe = _strdup(*args); 27 | } 28 | info->tokens -= 1; 29 | 30 | if (access(exe, X_OK) == 0) 31 | { 32 | env = dict_to_env(info->env); 33 | 34 | free_info(info); 35 | execve(exe, args, env); 36 | perrorl_default(*info->argv, info->lineno, "Not found", 37 | *info->tokens, *args, NULL); 38 | free(exe); 39 | free_tokens(&args); 40 | free_tokens(&env); 41 | exit(127); 42 | } 43 | perrorl_default(*info->argv, info->lineno, "Permission denied", 44 | *info->tokens, *args, NULL); 45 | free(exe); 46 | free_tokens(&args); 47 | free_info(info); 48 | exit(126); 49 | } 50 | -------------------------------------------------------------------------------- /__exit.c: -------------------------------------------------------------------------------- 1 | #include "builtins.h" 2 | 3 | /** 4 | * __exit - exits from shell 5 | * @info: arguments passed 6 | * Return: int 7 | */ 8 | int __exit(info_t *info) 9 | { 10 | char **args = info->tokens + 1; 11 | 12 | if (*args) 13 | { 14 | if (_isnumber(*args) && atou(*args) <= INT_MAX) 15 | { 16 | info->status = atou(*args); 17 | } 18 | else 19 | { 20 | perrorl_default(*info->argv, info->lineno, *args, 21 | *info->tokens, "Illegal number", NULL); 22 | info->status = 2; 23 | 24 | return (info->status); 25 | } 26 | } 27 | if (info->file) 28 | close(info->fileno); 29 | 30 | exit(free_info(info)); 31 | } 32 | -------------------------------------------------------------------------------- /__help.c: -------------------------------------------------------------------------------- 1 | #include "builtins.h" 2 | 3 | /** 4 | * __help - show help for builtins commands 5 | * @info: shell info 6 | * Return: status 7 | */ 8 | int __help(struct info *info) 9 | { 10 | const builtin_t *bp = NULL; 11 | char * const *args = info->tokens + 1; 12 | const char *desc = NULL; 13 | size_t len = 0; 14 | 15 | if (*args) 16 | { 17 | info->status = EXIT_FAILURE; 18 | while (*args) 19 | { 20 | bp = get_builtin(*args); 21 | if (bp) 22 | { 23 | write(STDOUT_FILENO, bp->name, _strlen(bp->name)); 24 | write(STDOUT_FILENO, ": ", 2); 25 | write(STDOUT_FILENO, bp->help, _strlen(bp->help)); 26 | write(STDOUT_FILENO, "\n", 1); 27 | for (desc = bp->desc; (len = _strlen(desc)); desc += len + 1) 28 | { 29 | write(STDOUT_FILENO, " ", 4); 30 | write(STDOUT_FILENO, desc, len); 31 | write(STDOUT_FILENO, "\n", 1); 32 | } 33 | info->status = EXIT_SUCCESS; 34 | } 35 | args += 1; 36 | } 37 | if (info->status == EXIT_FAILURE) 38 | perrorl_default(*info->argv, info->lineno, "No topics match", 39 | *info->tokens, *(args - 1), NULL); 40 | return (info->status); 41 | } 42 | info->status = EXIT_SUCCESS; 43 | for (bp = get_builtins(); bp->name; bp += 1) 44 | { 45 | write(STDOUT_FILENO, bp->help, _strlen(bp->help)); 46 | write(STDOUT_FILENO, "\n", 1); 47 | } 48 | return (info->status); 49 | } 50 | -------------------------------------------------------------------------------- /__setenv.c: -------------------------------------------------------------------------------- 1 | #include "builtins.h" 2 | 3 | /** 4 | * __setenv - sets the environment variables 5 | * @info: arguments passed 6 | * 7 | * Return: status 8 | */ 9 | int __setenv(info_t *info) 10 | { 11 | env_t *var; 12 | char **args = info->tokens + 1, *val; 13 | 14 | if (args[0]) 15 | { 16 | if (args[1]) 17 | { 18 | if (args[2]) 19 | { 20 | perrorl("Too many arguments.", *info->tokens, NULL); 21 | info->status = EXIT_FAILURE; 22 | return (info->status); 23 | } 24 | val = args[1]; 25 | } 26 | else 27 | { 28 | val = ""; 29 | } 30 | var = get_dict_node(info->env, args[0]); 31 | if (var) 32 | { 33 | free(var->val); 34 | var->val = _strdup(val); 35 | } 36 | else 37 | { 38 | add_dict_node_end(&info->env, args[0], val); 39 | } 40 | info->status = EXIT_SUCCESS; 41 | } 42 | else 43 | { 44 | __env(info); 45 | } 46 | return (info->status); 47 | } 48 | -------------------------------------------------------------------------------- /__unsetenv.c: -------------------------------------------------------------------------------- 1 | #include "builtins.h" 2 | 3 | /** 4 | * __unsetenv - unsets the environment variable 5 | * @info: arguments passed 6 | * Return: status 7 | */ 8 | int __unsetenv(info_t *info) 9 | { 10 | char **args = info->tokens + 1; 11 | 12 | if (*args) 13 | { 14 | while (*args) 15 | del_dict_node(&info->env, *args++); 16 | info->status = EXIT_SUCCESS; 17 | } 18 | else 19 | { 20 | perrorl("Too few arguments.", *info->tokens, NULL); 21 | info->status = EXIT_FAILURE; 22 | } 23 | return (info->status); 24 | } 25 | -------------------------------------------------------------------------------- /_isalnum.c: -------------------------------------------------------------------------------- 1 | #include "ctype.h" 2 | 3 | /** 4 | * _isalnum - checks if the character is alphanumeric 5 | * @c: character to check 6 | * Return: If c is alphanumeric, return 1. Otherwise, return 0. 7 | */ 8 | bool _isalnum(int c) 9 | { 10 | return (_isalpha(c) || _isdigit(c)); 11 | } 12 | -------------------------------------------------------------------------------- /_isalpha.c: -------------------------------------------------------------------------------- 1 | #include "ctype.h" 2 | 3 | /** 4 | * _isalpha - checks if the character is alphabetic 5 | * @c: character to check 6 | * Return: If c is alphabetic, return 1. Otherwise, return 0. 7 | */ 8 | bool _isalpha(int c) 9 | { 10 | return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')); 11 | } 12 | -------------------------------------------------------------------------------- /_isdigit.c: -------------------------------------------------------------------------------- 1 | #include "ctype.h" 2 | 3 | /** 4 | * _isdigit - checks if the character is digit 5 | * @c: character to check 6 | * Return: int 7 | */ 8 | bool _isdigit(int c) 9 | { 10 | return (c >= '0' && c <= '9'); 11 | } 12 | -------------------------------------------------------------------------------- /_isident.c: -------------------------------------------------------------------------------- 1 | #include "ctype.h" 2 | 3 | /** 4 | * _isident - checks if the character is a valid identifier character 5 | * @c: character to check 6 | * Return: If c is an identifier character, return 1. Otherwise, return 0. 7 | */ 8 | bool _isident(int c) 9 | { 10 | return (c == '_' || _isalnum(c)); 11 | } 12 | -------------------------------------------------------------------------------- /_isnumber.c: -------------------------------------------------------------------------------- 1 | #include "ctype.h" 2 | 3 | /** 4 | * _isnumber - check if a string consists only of digits 5 | * @s: pointer to string 6 | * Return: true or false 7 | */ 8 | bool _isnumber(const char *s) 9 | { 10 | if (s) 11 | { 12 | while (*s) 13 | { 14 | if (!_isdigit(*s++)) 15 | return (false); 16 | } 17 | return (true); 18 | } 19 | return (false); 20 | } 21 | -------------------------------------------------------------------------------- /_isquote.c: -------------------------------------------------------------------------------- 1 | #include "ctype.h" 2 | 3 | /** 4 | * _isquote - check if a character is a quote 5 | * @c: the character to check 6 | * Return: If c is a quote, return 1. Otherwise, return 0. 7 | */ 8 | bool _isquote(int c) 9 | { 10 | return (c == '"' || c == '\'' || c == '\\'); 11 | } 12 | -------------------------------------------------------------------------------- /_isspace.c: -------------------------------------------------------------------------------- 1 | #include "ctype.h" 2 | 3 | /** 4 | * _isspace - check if a character is whitespace 5 | * @c: the character to check 6 | * Return: If c is whitespace, return 1. Otherwise, return 0. 7 | */ 8 | bool _isspace(int c) 9 | { 10 | return (c == ' ' || (c >= 0x09 && c <= 0x0d)); 11 | } 12 | -------------------------------------------------------------------------------- /_isspecial_double.c: -------------------------------------------------------------------------------- 1 | #include "quote.h" 2 | 3 | /** 4 | * _isspecial_double - check if a character is special inside double quotes 5 | * @c: the character to check 6 | * Return: If c is special, return 1. Otherwise, return 0. 7 | */ 8 | int _isspecial_double(char c) 9 | { 10 | return (c == '"' || c == '$' || c == '\\'); 11 | } 12 | -------------------------------------------------------------------------------- /_quote.c: -------------------------------------------------------------------------------- 1 | #include "quote.h" 2 | 3 | /** 4 | * _quote_state_none - get state length and next state 5 | * @str: string 6 | * @state: state 7 | * 8 | * Return: length of state 9 | */ 10 | size_t _quote_state_none(const char *str, quote_state_t *state) 11 | { 12 | size_t len = 0; 13 | 14 | while (_isspace(*str)) 15 | ++str, ++len; 16 | if (state && *str) 17 | *state = quote_state(*str); 18 | return (len); 19 | } 20 | 21 | 22 | /** 23 | * _quote_state_word - get state length and next state 24 | * @str: string 25 | * @state: state 26 | * 27 | * Return: length of state 28 | */ 29 | size_t _quote_state_word(const char *str, quote_state_t *state) 30 | { 31 | size_t len = 0; 32 | 33 | while (*str && !_isspace(*str) && !_isquote(*str)) 34 | ++str, ++len; 35 | if (state && *str) 36 | *state = quote_state(*str); 37 | return (len); 38 | } 39 | 40 | 41 | /** 42 | * _quote_state_double - get state length and next state 43 | * @str: string 44 | * @state: state 45 | * 46 | * Return: length of state 47 | */ 48 | size_t _quote_state_double(const char *str, quote_state_t *state) 49 | { 50 | size_t len = 0; 51 | 52 | while (*str && *str != '"') 53 | ++str, ++len; 54 | if (state && *str) 55 | *state = quote_state(*(str + 1)); 56 | return (len); 57 | } 58 | 59 | 60 | /** 61 | * _quote_state_single - get state length and next state 62 | * @str: string 63 | * @state: state 64 | * 65 | * Return: length of state 66 | */ 67 | size_t _quote_state_single(const char *str, quote_state_t *state) 68 | { 69 | size_t len = 0; 70 | 71 | while (*str && *str != '\'') 72 | ++str, ++len; 73 | if (state && *str) 74 | *state = quote_state(*(str + 1)); 75 | return (len); 76 | } 77 | 78 | 79 | /** 80 | * _quote_state_escape - get state length and next state 81 | * @str: string 82 | * @state: state 83 | * 84 | * Return: length of state 85 | */ 86 | size_t _quote_state_escape(const char *str, quote_state_t *state) 87 | { 88 | if (*str) 89 | { 90 | if (state && *(++str)) 91 | *state = quote_state(*str); 92 | return (1); 93 | } 94 | return (0); 95 | } 96 | 97 | -------------------------------------------------------------------------------- /_signal.c: -------------------------------------------------------------------------------- 1 | #include "hsh.h" 2 | 3 | /** 4 | * _sigint - reprompts 5 | * @signal: signal passed 6 | */ 7 | void _sigint(int signal __attribute__((unused))) 8 | { 9 | fflush(STDIN_FILENO); 10 | write(STDERR_FILENO, "\n$ ", 3); 11 | } 12 | -------------------------------------------------------------------------------- /alias.h: -------------------------------------------------------------------------------- 1 | #ifndef _ALIAS_H_ 2 | #define _ALIAS_H_ 3 | 4 | #include "dict.h" 5 | #include "types.h" 6 | 7 | typedef dict_t alias_t; 8 | 9 | #endif /* _ALIAS_H_ */ 10 | -------------------------------------------------------------------------------- /arrdup.c: -------------------------------------------------------------------------------- 1 | #include "tokens.h" 2 | 3 | /** 4 | * arrdup - duplicate a (NULL-terminated) array 5 | * @arr: the array to duplicate 6 | * 7 | * Return: a duplicate of arr 8 | */ 9 | char **arrdup(char **arr) 10 | { 11 | char **dup = NULL; 12 | size_t len = 0; 13 | 14 | if (!arr) 15 | return (NULL); 16 | while (arr[len++]) 17 | ; 18 | dup = malloc(sizeof(*dup) * len); 19 | if (!dup) 20 | return (NULL); 21 | while (len--) 22 | dup[len] = _strdup(arr[len]); 23 | return (dup); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /arrjoin.c: -------------------------------------------------------------------------------- 1 | #include "tokens.h" 2 | 3 | /** 4 | * arrjoin - join two arrays 5 | * @arr1: the first array 6 | * @arr2: the second array 7 | * 8 | * Return: a dynamically-allocated array of the elements from arr1 and arr2 9 | */ 10 | char **arrjoin(char **arr1, char **arr2) 11 | { 12 | char **new; 13 | size_t arr1_len = 0, arr2_len = 0, new_len; 14 | 15 | if (arr1) 16 | { 17 | while (arr1[arr1_len]) 18 | ++arr1_len; 19 | } 20 | if (arr2) 21 | { 22 | while (arr2[arr2_len]) 23 | ++arr2_len; 24 | } 25 | new = malloc(sizeof(char *) * (arr1_len + arr2_len + 1)); 26 | if (!new) 27 | return (NULL); 28 | 29 | new_len = 0; 30 | if (arr1) 31 | { 32 | while (*arr1) 33 | new[new_len++] = _strdup(*arr1++); 34 | } 35 | if (arr2) 36 | { 37 | while (*arr2) 38 | new[new_len++] = _strdup(*arr2++); 39 | } 40 | new[new_len] = NULL; 41 | return (new); 42 | } 43 | -------------------------------------------------------------------------------- /atou.c: -------------------------------------------------------------------------------- 1 | #include "builtins.h" 2 | 3 | /** 4 | * atou - convert a string to an integer 5 | * @s: character to check 6 | * Return: int 7 | */ 8 | unsigned int atou(char *s) 9 | { 10 | size_t i; 11 | unsigned int number = 0; 12 | unsigned int to_add; 13 | 14 | for (i = 0; s[i] != '\0'; i++) 15 | { 16 | if (UINT_MAX / 10 < number) 17 | return (UINT_MAX); 18 | number *= 10; 19 | to_add = s[i] - '0'; 20 | if (UINT_MAX - to_add < number) 21 | return (UINT_MAX); 22 | number += to_add; 23 | 24 | } 25 | return (number); 26 | } 27 | -------------------------------------------------------------------------------- /builtins.c: -------------------------------------------------------------------------------- 1 | #include "builtins.h" 2 | 3 | /** 4 | * get_builtins - get the builtins 5 | * Return: pointer to a NULL-terminated statically-allocated array of builtins 6 | */ 7 | const builtin_t *get_builtins(void) 8 | { 9 | static builtin_t builtins[] = { 10 | {"alias", __alias, ALIAS_HELP, ALIAS_DESC}, 11 | {"cd", __cd, CD_HELP, CD_DESC}, 12 | {"env", __env, ENV_HELP, ENV_DESC}, 13 | {"exec", __exec, EXEC_HELP, EXEC_DESC}, 14 | {"exit", __exit, EXIT_HELP, EXIT_DESC}, 15 | {"help", __help, HELP_HELP, HELP_DESC}, 16 | {"setenv", __setenv, SETENV_HELP, SETENV_DESC}, 17 | {"unsetenv", __unsetenv, UNSETENV_HELP, UNSETENV_DESC}, 18 | {0} 19 | }; 20 | 21 | return (builtins); 22 | } 23 | 24 | /** 25 | * get_builtin - get a builtin by name 26 | * @name: the name of the builtin to retrieve 27 | * Return: NULL if no match is found, otherwise a pointer to the builtin 28 | */ 29 | const builtin_t *get_builtin(const char *name) 30 | { 31 | const builtin_t *builtin = NULL; 32 | 33 | for (builtin = get_builtins(); builtin->name; builtin += 1) 34 | { 35 | if (_strcmp(name, builtin->name) == 0) 36 | return (builtin); 37 | } 38 | return (NULL); 39 | } 40 | -------------------------------------------------------------------------------- /builtins.h: -------------------------------------------------------------------------------- 1 | #ifndef _BUILTINS_H_ 2 | #define _BUILTINS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "alias.h" 9 | #include "env.h" 10 | #include "error.h" 11 | #include "info.h" 12 | #include "path.h" 13 | #include "string.h" 14 | #include "types.h" 15 | 16 | #define ALIAS_HELP "alias [KEY[=VALUE] ...]" 17 | #define ALIAS_DESC \ 18 | "Define and display aliases.\n\0" \ 19 | "If given no arguments, existing alias definitions are displayed.\0" \ 20 | "Otherwise, an alias is defined for each KEY=VALUE pair provided.\0" \ 21 | "For each KEY with no VALUE the corresponding alias is displayed.\0" \ 22 | "If VALUE ends with a space, the following word will be expanded.\0" \ 23 | "\0" 24 | 25 | #define CD_HELP "cd [DIR]" 26 | #define CD_DESC \ 27 | "Change the current working directory to DIR.\n\0" \ 28 | "If DIR is omitted, it defaults to the value of the variable HOME.\0" \ 29 | "If DIR is -, the current directory reverts to its previous value.\0" \ 30 | "\0" 31 | 32 | #define ENV_HELP "env" 33 | #define ENV_DESC \ 34 | "Print the environment.\0" \ 35 | "\0" 36 | 37 | #define EXEC_HELP "exec COMMAND [ARGS ...]" 38 | #define EXEC_DESC \ 39 | "Replace the shell with the given command.\n\0" \ 40 | "COMMAND is executed, replacing the executing shell.\0" \ 41 | "ARGS are passed as positional arguments to COMMAND.\0" \ 42 | "If the command cannot be executed, the shell exits.\0" \ 43 | "\0" 44 | 45 | #define EXIT_HELP "exit [STATUS]" 46 | #define EXIT_DESC \ 47 | "Exit the shell with a status of STATUS.\n\0" \ 48 | "If STATUS is omitted, the exit status is that of the last command.\0" \ 49 | "\0" 50 | 51 | #define HELP_HELP "help [BUILTIN]" 52 | #define HELP_DESC \ 53 | "Display information about builtin commands.\n\0" \ 54 | "If BUILTIN is omitted, the available commands are displayed.\0" \ 55 | "\0" 56 | 57 | #define SETENV_HELP "setenv [NAME [VALUE]]" 58 | #define SETENV_DESC \ 59 | "Set the environment variable NAME to VALUE.\n\0" \ 60 | "If NAME is omitted, the shell execution environment is displayed.\0" \ 61 | "If VALUE is omitted, the value of NAME is set to an empty string.\0" \ 62 | "\0" 63 | 64 | #define UNSETENV_HELP "unsetenv NAME" 65 | #define UNSETENV_DESC \ 66 | "Remove the variable NAME from the environment.\0" \ 67 | "\0" 68 | 69 | typedef int (*builtin_fp)(info_t *); 70 | 71 | /** 72 | * struct builtin - builtin command 73 | * @name: command name 74 | * @f: function to call 75 | * @help: command usage 76 | * @desc: command description 77 | */ 78 | struct builtin 79 | { 80 | const char *name; 81 | builtin_fp f; 82 | const char *help; 83 | const char *desc; 84 | }; 85 | 86 | const struct builtin *get_builtin(const char *name); 87 | const struct builtin *get_builtins(void); 88 | 89 | int __alias(info_t *info); 90 | int __cd(info_t *info); 91 | int __env(info_t *info); 92 | int __exec(info_t *info); 93 | int __exit(info_t *info); 94 | int __help(info_t *info); 95 | int __history(info_t *info); 96 | int __setenv(info_t *info); 97 | int __unsetenv(info_t *info); 98 | 99 | #endif /* _BUILTINS_H_ */ 100 | -------------------------------------------------------------------------------- /cmd_to_list.c: -------------------------------------------------------------------------------- 1 | #include "command.h" 2 | 3 | /** 4 | * cmd_to_list - construct a linked list of tokenized commands 5 | * @cmd: the command to parse 6 | * 7 | * Return: If memory allocation fails, return NULL. Otherwise, return a 8 | * pointer to the head of the new list. 9 | */ 10 | cmdlist_t *cmd_to_list(const char *cmd) 11 | { 12 | cmdlist_t *head = NULL; 13 | size_t count; 14 | char *split = _strdup(cmd); 15 | 16 | if (!split) 17 | return (NULL); 18 | 19 | count = split_cmd(split); 20 | 21 | if (!_cmd_to_list(&head, split, count)) 22 | { 23 | free_cmdlist(&head); 24 | return (NULL); 25 | } 26 | free(split); 27 | 28 | return (head); 29 | } 30 | 31 | 32 | /** 33 | * _cmd_to_list - construct a linked list of tokenized commands (helper) 34 | * @tailptr: pointer to the tail of the command list 35 | * @split: a line split with null bytes on separators 36 | * @count: the number of commands contained in split 37 | * 38 | * Return: If memory allocation fails, return NULL. Otherwise, return a 39 | * pointer to the tail of the new list. 40 | */ 41 | cmdlist_t *_cmd_to_list(cmdlist_t **tailptr, char *split, size_t count) 42 | { 43 | cmdlist_t *tail; 44 | 45 | if (!count) 46 | return (*tailptr); 47 | 48 | tail = add_cmd_end(tailptr, split); 49 | if (!tail) 50 | return (NULL); 51 | 52 | while (*split++) 53 | ; 54 | 55 | return (_cmd_to_list(&tail, split, count - 1)); 56 | } 57 | -------------------------------------------------------------------------------- /cmdlist.c: -------------------------------------------------------------------------------- 1 | #include "command.h" 2 | 3 | 4 | /** 5 | * add_cmd_end - add a command at the end of the list 6 | * @headptr: a pointer to the address of the first list node 7 | * @cmd: the cmd to add to the list 8 | * 9 | * Return: If memory allocation fails, return NULL. Otherwise, return the 10 | * address of the new node. 11 | */ 12 | cmdlist_t *add_cmd_end(cmdlist_t **headptr, const char *cmd) 13 | { 14 | cmdlist_t *new; 15 | 16 | if (!headptr) 17 | return (NULL); 18 | if (*headptr) 19 | return (add_cmd_end(&((*headptr)->next), cmd)); 20 | 21 | new = malloc(sizeof(cmdlist_t)); 22 | if (!new) 23 | return (NULL); 24 | 25 | new->next = NULL; 26 | new->tree = NULL; 27 | 28 | new->tokens = tokenize(cmd); 29 | if (!new->tokens) 30 | { 31 | free(new); 32 | return (NULL); 33 | } 34 | *headptr = new; 35 | 36 | return (new); 37 | } 38 | 39 | 40 | /** 41 | * del_cmd - remove a command from a command list 42 | * @headptr: the first node 43 | * @index: argument passed 44 | * Return: address of deleted node 45 | */ 46 | cmdlist_t *del_cmd(cmdlist_t **headptr, size_t index) 47 | { 48 | cmdlist_t *old; 49 | 50 | if (!(headptr && *headptr)) 51 | return (NULL); 52 | if (index) 53 | return (del_cmd(&((*headptr)->next), index - 1)); 54 | 55 | old = *headptr; 56 | *headptr = (*headptr)->next; 57 | free_cmdtree(&(old->tree)); 58 | free_tokens(&(old->tokens)); 59 | free(old); 60 | 61 | return (old); 62 | } 63 | 64 | 65 | /** 66 | * pop_cmd - remove a node and retrieve it's tokens 67 | * @headptr: the first node 68 | * Return: command tokens 69 | */ 70 | char **pop_cmd(cmdlist_t **headptr) 71 | { 72 | cmdlist_t *pop; 73 | char **tokens; 74 | 75 | if (!(headptr && *headptr)) 76 | return (NULL); 77 | 78 | pop = *headptr; 79 | tokens = pop->tokens; 80 | *headptr = (*headptr)->next; 81 | 82 | free_cmdtree(&(pop->tree)); 83 | free(pop); 84 | 85 | return (tokens); 86 | } 87 | 88 | 89 | /** 90 | * free_cmdlist - free a linked list and and set head to NULL 91 | * @headptr: the first node 92 | */ 93 | void free_cmdlist(cmdlist_t **headptr) 94 | { 95 | if (headptr && *headptr) 96 | { 97 | free_cmdlist(&((*headptr)->next)); 98 | free_cmdtree(&((*headptr)->tree)); 99 | free_tokens(&((*headptr)->tokens)); 100 | free(*headptr); 101 | *headptr = NULL; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /cmdtree.c: -------------------------------------------------------------------------------- 1 | #include "command.h" 2 | 3 | /** 4 | * cmd_to_tree - construct a binary tree of commands 5 | * @tokens: the command to parse 6 | * 7 | * Return: If memory allocation fails, return NULL. Otherwise, return the 8 | * address of the root of the new tree 9 | */ 10 | cmdtree_t *cmd_to_tree(const char * const *tokens __attribute__((unused))) 11 | { 12 | return (NULL); 13 | } 14 | 15 | /** 16 | * free_cmdtree - free a binary tree and and set root to NULL 17 | * @rootptr: pointer 18 | * Return: NULL 19 | */ 20 | void free_cmdtree(cmdtree_t **rootptr) 21 | { 22 | if (rootptr && *rootptr) 23 | { 24 | free_cmdtree(&((*rootptr)->success)); 25 | free_cmdtree(&((*rootptr)->failure)); 26 | (*rootptr)->tokens = NULL; 27 | (*rootptr) = NULL; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /command.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMAND_H_ 2 | #define _COMMAND_H_ 3 | 4 | #include 5 | #include "quote.h" 6 | #include "string.h" 7 | #include "tokens.h" 8 | #include "types.h" 9 | 10 | /** 11 | * enum cmdlist_sep_n - numeric values for commmand list separators 12 | * @SEMICOLON: ; 13 | * @AMPERSAND: & 14 | * @AND: && 15 | * @OR: || 16 | */ 17 | typedef enum cmdlist_sep_n 18 | { 19 | SEMICOLON = 1, 20 | AMPERSAND = 2, 21 | AND = 4, 22 | OR = 8 23 | } cmdlist_sep_n_t; 24 | 25 | /** 26 | * struct cmdlist_sep - command list separator structure 27 | * @sep: the command separator 28 | * @n: the corresponding numeric value 29 | */ 30 | typedef struct cmdlist_sep 31 | { 32 | const char *sep; 33 | enum cmdlist_sep_n n; 34 | } cmdlist_sep_t; 35 | 36 | /** 37 | * struct cmdlist - a linked list of commands 38 | * @next: the next command 39 | * @tree: a binary tree of commands 40 | * @tokens: the tokens for each command in the tree 41 | */ 42 | struct cmdlist 43 | { 44 | struct cmdlist *next; 45 | struct cmdtree *tree; 46 | char **tokens; 47 | }; 48 | 49 | /** 50 | * struct cmdtree - a binary tree of commands 51 | * @success: the command to execute upon failure 52 | * @failure: the command to execute upon success 53 | * @tokens: a simple command with no separators 54 | * @sep: the preceding list separator 55 | */ 56 | struct cmdtree 57 | { 58 | struct cmdtree *success; 59 | struct cmdtree *failure; 60 | const char * const *tokens; 61 | struct cmdlist_sep sep; 62 | }; 63 | 64 | cmdlist_t *cmd_to_list(const char *cmd); 65 | cmdlist_t *_cmd_to_list(cmdlist_t **tailptr, char *split, size_t count); 66 | 67 | size_t split_cmd(char *cmd); 68 | 69 | cmdlist_t *add_cmd_end(cmdlist_t **headptr, const char *cmd); 70 | cmdlist_t *del_cmd(cmdlist_t **headptr, size_t index); 71 | char **pop_cmd(cmdlist_t **headptr); 72 | void free_cmdlist(cmdlist_t **headptr); 73 | 74 | cmdtree_t *cmd_to_tree(const char * const *tokens); 75 | void free_cmdtree(cmdtree_t **rootptr); 76 | 77 | #endif /* _COMMAND_H_ */ 78 | -------------------------------------------------------------------------------- /ctype.h: -------------------------------------------------------------------------------- 1 | #ifndef _CTYPE_H_ 2 | #define _CTYPE_H_ 3 | 4 | #include 5 | #include 6 | 7 | bool _isalnum(int c); 8 | bool _isalpha(int c); 9 | bool _isdigit(int c); 10 | bool _isident(int c); 11 | bool _isspace(int c); 12 | bool _isquote(int c); 13 | 14 | bool _isnumber(const char *s); 15 | 16 | #endif /* _CTYPE_H_ */ 17 | -------------------------------------------------------------------------------- /dequote.c: -------------------------------------------------------------------------------- 1 | #include "quote.h" 2 | 3 | /** 4 | * dequote - dequote a string 5 | * @str: the string to dequote 6 | * Return: If memory allocation fails, return NULL. 7 | * Otherwise return a dequoted copy of str. 8 | */ 9 | char *dequote(const char *str) 10 | { 11 | char *new; 12 | size_t len = 0, state_len; 13 | quote_state_t state; 14 | 15 | if (!str) 16 | return (NULL); 17 | 18 | new = malloc(sizeof(char) * (dequote_len(str) + 1)); 19 | if (!new) 20 | return (NULL); 21 | 22 | while (*str) 23 | { 24 | state = quote_state(*str); 25 | str += (1 && (state & (QUOTE_DOUBLE | QUOTE_SINGLE | QUOTE_ESCAPE))); 26 | state_len = quote_state_len(str, state); 27 | if (state & QUOTE_DOUBLE) 28 | { 29 | for ( ; state_len; --state_len) 30 | { 31 | if (quote_state(*str++) & QUOTE_ESCAPE) 32 | { 33 | if (*str == '\n') 34 | { 35 | ++str, --state_len; 36 | continue; 37 | } 38 | if (_isspecial_double(*str)) 39 | ++str, --state_len; 40 | } 41 | new[len++] = str[-1]; 42 | } 43 | } 44 | _memcpy(new + len, str, state_len); 45 | len += state_len; 46 | str += state_len; 47 | str += (*str && (state & (QUOTE_DOUBLE | QUOTE_SINGLE))); 48 | } 49 | new[len] = '\0'; 50 | return (new); 51 | } 52 | 53 | 54 | /** 55 | * dequote_len - compute the length of a string after dequoting 56 | * @str: the string to evaluate 57 | * Return: Return the length of str after dequoting 58 | */ 59 | size_t dequote_len(const char *str) 60 | { 61 | size_t len = 0, state_len; 62 | quote_state_t state; 63 | 64 | while (*str) 65 | { 66 | state = quote_state(*str); 67 | str += (1 && (state & (QUOTE_DOUBLE | QUOTE_SINGLE | QUOTE_ESCAPE))); 68 | state_len = quote_state_len(str, state); 69 | if (state & QUOTE_DOUBLE) 70 | { 71 | for ( ; state_len; --state_len) 72 | { 73 | if (quote_state(*str++) & QUOTE_ESCAPE) 74 | { 75 | if (*str == '\n') 76 | { 77 | ++str, --state_len; 78 | continue; 79 | } 80 | if (_isspecial_double(*str)) 81 | ++str, --state_len; 82 | } 83 | len++; 84 | } 85 | } 86 | len += state_len; 87 | str += state_len; 88 | str += (*str && (state & (QUOTE_DOUBLE | QUOTE_SINGLE))); 89 | } 90 | return (len); 91 | } 92 | -------------------------------------------------------------------------------- /dict.c: -------------------------------------------------------------------------------- 1 | #include "dict.h" 2 | 3 | /** 4 | * get_dict_val - get a value from a dictionary 5 | * @head: dict 6 | * @key: entry key 7 | * 8 | * Return: pointer to the entry value 9 | */ 10 | char *get_dict_val(dict_t *head, const char *key) 11 | { 12 | if (!key) 13 | return (NULL); 14 | 15 | while (head) 16 | { 17 | if (!_strcmp(head->key, key)) 18 | return (head->val); 19 | head = head->next; 20 | } 21 | 22 | return (NULL); 23 | } 24 | 25 | 26 | /** 27 | * get_dict_node - retrieve a node by key 28 | * @head: dict 29 | * @key: entry key 30 | * 31 | * Return: node or NULL 32 | */ 33 | dict_t *get_dict_node(dict_t *head, const char *key) 34 | { 35 | if (!head) 36 | return (NULL); 37 | if (!_strcmp(head->key, key)) 38 | return (head); 39 | return (get_dict_node(head->next, key)); 40 | } 41 | 42 | 43 | /** 44 | * add_dict_node_end - adds node at the end of list 45 | * @headptr: pointer to dict 46 | * @key: entry key 47 | * @val: entry val 48 | * Return: pointer to list 49 | */ 50 | dict_t *add_dict_node_end(dict_t **headptr, const char *key, const char *val) 51 | { 52 | dict_t *new; 53 | 54 | if (!headptr) 55 | return (NULL); 56 | 57 | if (*headptr) 58 | return (add_dict_node_end(&((*headptr)->next), key, val)); 59 | 60 | new = malloc(sizeof(dict_t)); 61 | if (!new) 62 | return (NULL); 63 | 64 | new->key = _strdup(key); 65 | new->val = _strdup(val); 66 | new->next = NULL; 67 | 68 | *headptr = new; 69 | return (new); 70 | } 71 | 72 | 73 | /** 74 | * del_dict_node - delete a node 75 | * @headptr: pointer to dict 76 | * @key: entry key 77 | * 78 | * Return: pointer to resulting list 79 | */ 80 | dict_t *del_dict_node(dict_t **headptr, const char *key) 81 | { 82 | dict_t *tmp; 83 | 84 | if (!(headptr && *headptr)) 85 | return (NULL); 86 | if (!_strcmp((*headptr)->key, key)) 87 | { 88 | tmp = *headptr; 89 | (*headptr) = tmp->next; 90 | free(tmp->key); 91 | free(tmp->val); 92 | free(tmp); 93 | } 94 | else 95 | del_dict_node(&((*headptr)->next), key); 96 | return (*headptr); 97 | } 98 | 99 | 100 | /** 101 | * free_dict - free a linked list and and set head to NULL 102 | * @headptr: the first list node 103 | */ 104 | void free_dict(dict_t **headptr) 105 | { 106 | if (!*headptr) 107 | return; 108 | 109 | free_dict(&((*headptr)->next)); 110 | free((*headptr)->key); 111 | free((*headptr)->val); 112 | free(*headptr); 113 | *headptr = NULL; 114 | } 115 | -------------------------------------------------------------------------------- /dict.h: -------------------------------------------------------------------------------- 1 | #ifndef _DICT_H_ 2 | #define _DICT_H_ 3 | 4 | #include 5 | 6 | #include "string.h" 7 | #include "types.h" 8 | 9 | /** 10 | * struct dict - singly linked list of key-value pairs 11 | * @key: variable name 12 | * @val: value of variable 13 | * @next: pointer to the next node 14 | */ 15 | struct dict 16 | { 17 | char *key; 18 | char *val; 19 | struct dict *next; 20 | }; 21 | 22 | char *get_dict_val(dict_t *head, const char *key); 23 | dict_t *get_dict_node(dict_t *head, const char *key); 24 | dict_t *add_dict_node_end(dict_t **headptr, const char *key, const char *val); 25 | dict_t *del_dict_node(dict_t **headptr, const char *key); 26 | void free_dict(dict_t **headptr); 27 | 28 | #endif /* _DICT_H_ */ 29 | -------------------------------------------------------------------------------- /dict_to_env.c: -------------------------------------------------------------------------------- 1 | #include "env.h" 2 | 3 | /** 4 | * dict_to_env - creates linked list from environment 5 | * @head: argument passed 6 | * Return: pointer to list 7 | */ 8 | char **dict_to_env(env_t *head) 9 | { 10 | env_t *tmp = head; 11 | char **env; 12 | size_t len = 0; 13 | 14 | while (tmp) 15 | ++len, tmp = tmp->next; 16 | 17 | env = malloc(sizeof(char *) * (len + 1)); 18 | if (!env) 19 | return (NULL); 20 | 21 | for (len = 0; head; head = head->next) 22 | env[len++] = strjoin(NULL, "=", head->key, head->val); 23 | env[len] = NULL; 24 | 25 | return (env); 26 | } 27 | -------------------------------------------------------------------------------- /env.h: -------------------------------------------------------------------------------- 1 | #ifndef _ENV_H_ 2 | #define _ENV_H_ 3 | 4 | #include 5 | #include "dict.h" 6 | #include "string.h" 7 | #include "types.h" 8 | 9 | typedef dict_t env_t; 10 | 11 | env_t *env_to_dict(char **env); 12 | env_t *_env_to_dict(env_t **tailptr, char **env); 13 | char **dict_to_env(env_t *head); 14 | 15 | #endif /* _ENV_H_ */ 16 | -------------------------------------------------------------------------------- /env_to_dict.c: -------------------------------------------------------------------------------- 1 | #include "env.h" 2 | 3 | /** 4 | * env_to_dict - creates a list from environment 5 | * @env: environment passed 6 | * Return: head 7 | */ 8 | env_t *env_to_dict(char **env) 9 | { 10 | env_t *head = NULL; 11 | 12 | if (!_env_to_dict(&head, env)) 13 | free_dict(&head); 14 | 15 | return (head); 16 | } 17 | 18 | 19 | /** 20 | * _env_to_dict - turn the environment into a linked list (helper) 21 | * @tailptr: pointer to the tail of the list 22 | * @env: environment 23 | * 24 | * Return: pointer to the tail of the list 25 | */ 26 | env_t *_env_to_dict(env_t **tailptr, char **env) 27 | { 28 | env_t *tail; 29 | char *env_str; 30 | ssize_t key_len; 31 | 32 | if (!*env) 33 | return (*tailptr); 34 | 35 | env_str = _strdup(*env); 36 | if (!env_str) 37 | return (NULL); 38 | 39 | key_len = _strchr(*env, '='); 40 | 41 | if (key_len == -1) 42 | return (NULL); 43 | 44 | env_str[key_len] = '\0'; 45 | tail = add_dict_node_end(tailptr, env_str, env_str + key_len + 1); 46 | free(env_str); 47 | 48 | return (_env_to_dict(&tail, env + 1)); 49 | } 50 | -------------------------------------------------------------------------------- /error.c: -------------------------------------------------------------------------------- 1 | #include "error.h" 2 | 3 | /** 4 | * perrorl - print a formatted message to standard error 5 | * @msg: error message 6 | * @...: NULL-terminated list of context strings to prepend 7 | */ 8 | void perrorl(const char *msg, ...) 9 | { 10 | const char *str; 11 | va_list context; 12 | 13 | va_start(context, msg); 14 | while ((str = va_arg(context, char *))) 15 | { 16 | write(STDERR_FILENO, str, _strlen(str)); 17 | write(STDERR_FILENO, ": ", 2); 18 | } 19 | va_end(context); 20 | 21 | if (msg) 22 | write(STDERR_FILENO, msg, _strlen(msg)); 23 | write(STDERR_FILENO, "\n", 1); 24 | } 25 | 26 | 27 | /** 28 | * perrorl_default - print a formatted message to standard error 29 | * @arg0: argument vector 30 | * @lineno: line number 31 | * @msg: error message 32 | * @...: NULL-terminated list of context strings to prepend 33 | */ 34 | void perrorl_default(const char *arg0, size_t lineno, const char *msg, ...) 35 | { 36 | char *linenostr = num_to_str(lineno); 37 | const char *str = NULL; 38 | va_list ap; 39 | 40 | if (arg0) 41 | write(STDERR_FILENO, arg0, _strlen(arg0)); 42 | write(STDERR_FILENO, ": ", 2); 43 | 44 | if (linenostr) 45 | write(STDERR_FILENO, linenostr, _strlen(linenostr)); 46 | write(STDERR_FILENO, ": ", 2); 47 | 48 | va_start(ap, msg); 49 | while ((str = va_arg(ap, char *))) 50 | { 51 | write(STDERR_FILENO, str, _strlen(str)); 52 | write(STDERR_FILENO, ": ", 2); 53 | } 54 | va_end(ap); 55 | 56 | if (msg) 57 | write(STDERR_FILENO, msg, _strlen(msg)); 58 | write(STDERR_FILENO, "\n", 1); 59 | free(linenostr); 60 | } 61 | -------------------------------------------------------------------------------- /error.h: -------------------------------------------------------------------------------- 1 | #ifndef _ERROR_H_ 2 | #define _ERROR_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "string.h" 9 | 10 | void perrorl(const char *msg, ...); 11 | void perrorl_default(const char *arg0, size_t lineno, const char *msg, ...); 12 | 13 | #endif /* _ERROR_H_ */ 14 | -------------------------------------------------------------------------------- /execute.c: -------------------------------------------------------------------------------- 1 | #include "hsh.h" 2 | 3 | /** 4 | * execute - execute a command 5 | * @info: arguments passed 6 | * 7 | * Return: status 8 | */ 9 | int execute(info_t *info) 10 | { 11 | const builtin_t *builtin = get_builtin(*info->tokens); 12 | 13 | if (builtin) 14 | { 15 | return (builtin->f(info)); 16 | } 17 | if (_strchr(*info->tokens, '/') == -1) 18 | { 19 | free_list(&info->path); 20 | info->path = str_to_list(get_dict_val(info->env, "PATH"), ':'); 21 | info->exe = search_path(info, info->path); 22 | } 23 | else 24 | { 25 | info->exe = _strdup(*info->tokens); 26 | } 27 | if (info->exe && access(info->exe, X_OK) == 0) 28 | { 29 | return (_execute(info)); 30 | } 31 | if (info->exe) 32 | { 33 | perrorl_default(*info->argv, info->lineno, "Permission denied", 34 | *info->tokens, NULL); 35 | info->status = 126; 36 | } 37 | else 38 | { 39 | perrorl_default(*info->argv, info->lineno, "not found", 40 | *info->tokens, NULL); 41 | info->status = 127; 42 | } 43 | return (info->status); 44 | } 45 | 46 | 47 | /** 48 | * _execute - fork and exec the current command 49 | * @info: shell information 50 | * 51 | * Return: exit status of the child process 52 | */ 53 | int _execute(info_t *info) 54 | { 55 | char *exe, **argv, **env; 56 | 57 | switch (fork()) 58 | { 59 | case 0: 60 | exe = info->exe; 61 | argv = info->tokens; 62 | env = dict_to_env(info->env); 63 | 64 | info->exe = NULL; 65 | info->tokens = NULL; 66 | free_info(info); 67 | 68 | execve(exe, argv, env); 69 | perror(*argv); 70 | 71 | if (info->file) 72 | close(info->fileno); 73 | 74 | free(exe); 75 | free_tokens(&argv); 76 | free_tokens(&env); 77 | exit(EXIT_FAILURE); 78 | break; 79 | case -1: 80 | perrorl_default(*info->argv, info->lineno, "Cannot fork", NULL); 81 | info->status = 2; 82 | break; 83 | default: 84 | wait(&info->status); 85 | info->status = WEXITSTATUS(info->status); 86 | break; 87 | } 88 | free(info->exe); 89 | info->exe = NULL; 90 | 91 | return (info->status); 92 | } 93 | -------------------------------------------------------------------------------- /expand_aliases.c: -------------------------------------------------------------------------------- 1 | #include "hsh.h" 2 | 3 | /** 4 | * expand_aliases - perform recursive alias expansion on the current command 5 | * @aliases: alias list 6 | * @tokptr: pointer to the current tokens 7 | * 8 | * Return: If expansion succeeds, return a pointer t to the otherwise 0 9 | */ 10 | void expand_aliases(alias_t *aliases, char ***tokptr) 11 | { 12 | char **new, **old, *name, *value, *temp; 13 | 14 | if (!*tokptr) 15 | return; 16 | do { 17 | name = expand_alias(aliases, tokptr); 18 | value = get_dict_val(aliases, name); 19 | if (value && *value && _isspace(value[_strlen(value) - 1])) 20 | { 21 | old = *tokptr; 22 | new = arrdup(old + 1); 23 | 24 | expand_aliases(aliases, &new); 25 | temp = *(old + 1); 26 | 27 | *(old + 1) = NULL; 28 | *tokptr = arrjoin(old, new); 29 | *(old + 1) = temp; 30 | 31 | free_tokens(&old); 32 | free_tokens(&new); 33 | } 34 | } while (name && **tokptr && _strcmp(name, **tokptr)); 35 | } 36 | 37 | 38 | /** 39 | * expand_alias - perform a single alias expansion on the current command 40 | * @aliases: alias list 41 | * @tokptr: pointer to the current tokens 42 | * 43 | * Return: If expansion succeeds, return a pointer the alias name. 44 | * Otherwise, return NULL. 45 | */ 46 | char *expand_alias(alias_t *aliases, char ***tokptr) 47 | { 48 | char **alias_tokens, **tokens = *tokptr; 49 | 50 | if (!*tokens) 51 | return (NULL); 52 | 53 | while (aliases) 54 | { 55 | if (!_strcmp(*tokens, aliases->key)) 56 | { 57 | alias_tokens = tokenize(aliases->val); 58 | *tokptr = arrjoin(alias_tokens, tokens + 1); 59 | 60 | free_tokens(&tokens); 61 | free_tokens(&alias_tokens); 62 | 63 | return (aliases->key); 64 | } 65 | aliases = aliases->next; 66 | } 67 | return (NULL); 68 | } 69 | -------------------------------------------------------------------------------- /expand_vars.c: -------------------------------------------------------------------------------- 1 | #include "hsh.h" 2 | 3 | /** 4 | * expand_vars - perform variable expansion on the current set of tokens 5 | * @info: shell information 6 | * @tokptr: pointer to the current tokens 7 | */ 8 | void expand_vars(info_t *info, char ***tokptr) 9 | { 10 | char **new = NULL, **old, **tmp, **tokens; 11 | 12 | for (tokens = *tokptr; **tokptr; ++(*tokptr)) 13 | { 14 | old = new; 15 | tmp = _expand_vars(info, tokptr); 16 | new = arrjoin(old, tmp); 17 | free_tokens(&old); 18 | free_tokens(&tmp); 19 | free(**tokptr); 20 | } 21 | free(tokens); 22 | *tokptr = new; 23 | } 24 | 25 | 26 | /** 27 | * _expand_vars - perform variable expansion on a token 28 | * @info: shell information 29 | * @tokptr: pointer to the current tokens 30 | * 31 | * Return: the expanded token 32 | */ 33 | char **_expand_vars(info_t *info, char ***tokptr) 34 | { 35 | char *var = NULL, *val = NULL, *tok = **tokptr; 36 | size_t pos = 0, var_len, val_len; 37 | quote_state_t state = QUOTE_NONE; 38 | 39 | while (var_len = val_len = 1, tok[pos]) 40 | { 41 | if (quote_state_len(tok + pos, state) == 0) 42 | { 43 | if ((state & (QUOTE_DOUBLE | QUOTE_SINGLE)) && !tok[++pos]) 44 | break; 45 | state = quote_state(tok[pos]); 46 | if (state & (QUOTE_DOUBLE | QUOTE_SINGLE | QUOTE_ESCAPE)) 47 | ++pos; 48 | continue; 49 | } 50 | if ((state & (QUOTE_DOUBLE)) && (quote_state(tok[pos]) & QUOTE_ESCAPE)) 51 | { 52 | if (!tok[++pos] || !tok[++pos]) 53 | break; 54 | state = quote_state(tok[pos]); 55 | if (state & (QUOTE_DOUBLE | QUOTE_SINGLE | QUOTE_ESCAPE)) 56 | ++pos; 57 | continue; 58 | } 59 | if (state & (QUOTE_SINGLE)) 60 | { 61 | pos += quote_state_len(tok + pos, state); 62 | if (tok[pos]) 63 | ++pos; 64 | continue; 65 | } 66 | if (state & (QUOTE_ESCAPE)) 67 | { 68 | if (!tok[++pos] || !tok[++pos]) 69 | break; 70 | state = quote_state(tok[pos]); 71 | if (state & (QUOTE_DOUBLE | QUOTE_SINGLE | QUOTE_ESCAPE)) 72 | ++pos; 73 | continue; 74 | } 75 | if (tok[pos] != '$') 76 | { 77 | ++pos; 78 | continue; 79 | } 80 | if (tok[pos + 1] == '$') 81 | { 82 | val = num_to_str(info->pid); 83 | } 84 | else if (tok[pos + 1] == '?') 85 | { 86 | val = num_to_str(info->status); 87 | } 88 | else if (_isident(tok[pos + 1]) && !_isdigit(tok[pos + 1])) 89 | { 90 | while (_isident(tok[pos + var_len + 1])) 91 | ++var_len; 92 | 93 | var = _strndup(tok + pos + 1, var_len); 94 | val = get_dict_val(info->env, var); 95 | 96 | if (val) 97 | val = _strdup(val); 98 | else 99 | val = _strdup(""); 100 | 101 | free(var); 102 | var = NULL; 103 | } 104 | if (val) 105 | { 106 | val_len = _strlen(val); 107 | **tokptr = malloc(sizeof(char) * ( 108 | pos + val_len + _strlen(tok + pos + var_len) + 1 109 | )); 110 | _memcpy(**tokptr, tok, pos); 111 | _memcpy(**tokptr + pos, val, val_len); 112 | _strcpy(**tokptr + pos + val_len, tok + pos + var_len + 1); 113 | 114 | free(tok); 115 | tok = **tokptr; 116 | 117 | free(val); 118 | val = NULL; 119 | } 120 | pos += val_len; 121 | } 122 | return (tokenize(**tokptr)); 123 | } 124 | -------------------------------------------------------------------------------- /gcc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | gcc -g -Wall -Werror -Wextra -pedantic -std=gnu89 *.c -o hsh 3 | -------------------------------------------------------------------------------- /getline.c: -------------------------------------------------------------------------------- 1 | #include "getline.h" 2 | #include "string.h" 3 | 4 | /** 5 | * _realloc - reallocate a buffer 6 | * @old: pointer to the buffer 7 | * @old_size: current size of the buffer 8 | * @new_size: desired size of the buffer 9 | * Return: If memory allocation fails, return NULL. 10 | * Otherwise, return a pointer to the new buffer. 11 | */ 12 | static void *_realloc(void *old, size_t old_size, size_t new_size) 13 | { 14 | void *new = NULL; 15 | 16 | if (old) 17 | { 18 | if (new_size) 19 | { 20 | new = malloc(new_size); 21 | if (new) 22 | { 23 | _memcpy(new, old, old_size < new_size ? old_size : new_size); 24 | free(old); 25 | } 26 | } 27 | else 28 | { 29 | free(old); 30 | } 31 | } 32 | return (new); 33 | } 34 | 35 | /** 36 | * _getline_next - read a line of input 37 | * @buf: pointer to the static buffer 38 | * @line: address of a pointer to the line 39 | * @size: address of a pointer to the line size 40 | * @n: number of characters to copy from the buffer 41 | * Return: If memory allocation fails, return NULL. 42 | * Otherwise, return a pointer to the line of input. 43 | */ 44 | static char *_getline_next(buf_t *buf, char **line, size_t *size, size_t n) 45 | { 46 | char *temp = NULL; 47 | 48 | if (*line) 49 | temp = _realloc(*line, *size, *size + n); 50 | else 51 | temp = malloc(n + 1); 52 | 53 | if (temp) 54 | { 55 | *line = temp; 56 | 57 | if (*size) 58 | *size -= 1; 59 | 60 | _memcpy(*line + *size, buf->next, n); 61 | *size += n; 62 | 63 | (*line)[*size] = '\0'; 64 | *size += 1; 65 | } 66 | else 67 | { 68 | free(*line); 69 | *line = NULL; 70 | *size = 0; 71 | } 72 | return (*line); 73 | } 74 | 75 | /** 76 | * _getline_buf - create, get, and delete buffers 77 | * @table: buffers indexed by file descriptor 78 | * @fd: file descriptor 79 | * Return: NULL or a pointer to the buffer associated with fd 80 | */ 81 | static buf_t *_getline_buf(buf_table_t *table, const int fd) 82 | { 83 | buf_table_node_t *item = NULL; 84 | size_t index = fd % GETLINE_TABLE_SIZE; 85 | 86 | if (table) 87 | { 88 | if (fd < 0) 89 | { 90 | for (index = 0; index < GETLINE_TABLE_SIZE; index += 1) 91 | { 92 | while ((item = (*table)[index])) 93 | { 94 | (*table)[index] = item->next; 95 | free(item); 96 | } 97 | } 98 | } 99 | else 100 | { 101 | item = (*table)[index]; 102 | while (item && item->fd != fd) 103 | item = item->next; 104 | if (item == NULL) 105 | { 106 | item = malloc(sizeof(*item)); 107 | if (item) 108 | { 109 | item->fd = fd; 110 | item->buf.next = NULL; 111 | item->buf.remaining = 0; 112 | item->next = (*table)[index]; 113 | (*table)[index] = item; 114 | } 115 | } 116 | } 117 | } 118 | return (item ? &item->buf : NULL); 119 | } 120 | 121 | /** 122 | * _getline - read a line of input 123 | * @fd: file descriptor from which to read 124 | * Return: If an error occurs or there are no more lines, return NULL. 125 | * Otherwise, return the next line of input. 126 | */ 127 | char *_getline(const int fd) 128 | { 129 | static buf_table_t table; 130 | buf_t *buf = _getline_buf(&table, fd); 131 | char *line = NULL; 132 | size_t size = 0; 133 | ssize_t eol = 0, n_read = 0; 134 | 135 | if (buf) 136 | { 137 | do { 138 | if (buf->remaining == 0) 139 | buf->next = buf->buffer; 140 | if (n_read) 141 | buf->remaining = n_read; 142 | if (buf->remaining) 143 | { 144 | eol = _memchr(buf->next, '\n', buf->remaining); 145 | if (eol == -1) 146 | { 147 | if (_getline_next(buf, &line, &size, buf->remaining)) 148 | buf->next += buf->remaining, buf->remaining = 0; 149 | else 150 | break; 151 | } 152 | else 153 | { 154 | if (_getline_next(buf, &line, &size, eol + 1)) 155 | buf->next += eol + 1, buf->remaining -= eol + 1; 156 | break; 157 | } 158 | } 159 | } while ((n_read = read(fd, buf->buffer, GETLINE_BUFFER_SIZE)) > 0); 160 | if (n_read == -1) 161 | { 162 | free(line); 163 | line = NULL; 164 | size = 0; 165 | } 166 | } 167 | return (line); 168 | } 169 | -------------------------------------------------------------------------------- /getline.h: -------------------------------------------------------------------------------- 1 | #ifndef _GETLINE_H_ 2 | #define _GETLINE_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "string.h" 8 | 9 | #define GETLINE_BUFFER_SIZE 4096 10 | 11 | /** 12 | * struct buf_s - input buffer 13 | * @buffer: the buffer 14 | * @next: pointer to the next value 15 | * @remaining: number of values remaining 16 | */ 17 | typedef struct buf_s 18 | { 19 | char buffer[GETLINE_BUFFER_SIZE]; 20 | char *next; 21 | size_t remaining; 22 | } buf_t; 23 | 24 | #define GETLINE_TABLE_SIZE 127 25 | 26 | /** 27 | * struct buf_table_node_s - input buffer hash table 28 | * @fd: file descriptor 29 | * @buf: associated buffer 30 | * @next: next buffer in chain 31 | */ 32 | typedef struct buf_table_node_s 33 | { 34 | int fd; 35 | struct buf_s buf; 36 | struct buf_table_node_s *next; 37 | } buf_table_node_t; 38 | 39 | typedef buf_table_node_t *buf_table_t[GETLINE_TABLE_SIZE]; 40 | 41 | char *_getline(const int fd); 42 | 43 | #endif /* _GETLINE_H_ */ 44 | -------------------------------------------------------------------------------- /history.h: -------------------------------------------------------------------------------- 1 | #ifndef _HISTORY_H_ 2 | #define _HISTORY_H_ 3 | 4 | #include "list.h" 5 | #include "types.h" 6 | 7 | /** 8 | * struct history - shell command history 9 | * @head: a pointer to the head of the history list 10 | * @filename: the name of the history to file 11 | * @n: the number of entries in the history list 12 | */ 13 | struct history 14 | { 15 | struct list *head; 16 | char *filename; 17 | size_t n; 18 | }; 19 | 20 | #endif /* _HISTORY_H_ */ 21 | -------------------------------------------------------------------------------- /hsh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/victorpreston/simple_shell/a6c2280968dd5683cce5c8ad1b3a6d48a0212b44/hsh -------------------------------------------------------------------------------- /hsh.c: -------------------------------------------------------------------------------- 1 | #include "hsh.h" 2 | 3 | /** 4 | * main - entry point 5 | * @argc: the argument count 6 | * @argv: the argument vector 7 | * 8 | * Return: Always 0 9 | */ 10 | int main(int argc, char **argv) 11 | { 12 | info_t *info = init_info(argc, argv); 13 | 14 | signal(2, _sigint); 15 | 16 | while (read_input(info)) 17 | { 18 | parse(info); 19 | while ((info->tokens = pop_cmd(&(info->commands)))) 20 | { 21 | execute(info); 22 | free_tokens(&(info->tokens)); 23 | } 24 | free(info->line); 25 | info->line = NULL; 26 | } 27 | if (info->interactive) 28 | write(STDOUT_FILENO, "\n", 1); 29 | 30 | if (info->file) 31 | close(info->fileno); 32 | 33 | exit(free_info(info)); 34 | } 35 | -------------------------------------------------------------------------------- /hsh.h: -------------------------------------------------------------------------------- 1 | #ifndef SHELL_H 2 | #define SHELL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "alias.h" 14 | #include "builtins.h" 15 | #include "command.h" 16 | #include "ctype.h" 17 | #include "dict.h" 18 | #include "env.h" 19 | #include "error.h" 20 | #include "info.h" 21 | #include "list.h" 22 | #include "path.h" 23 | #include "quote.h" 24 | #include "string.h" 25 | #include "tokens.h" 26 | #include "types.h" 27 | 28 | extern char **environ; 29 | 30 | bool read_input(info_t *info); 31 | quote_state_t _read_input(char **lineptr, int fd); 32 | 33 | int parse(info_t *info); 34 | 35 | int execute(info_t *info); 36 | int _execute(info_t *info); 37 | 38 | void expand_aliases(alias_t *aliases, char ***tokptr); 39 | char *expand_alias(alias_t *aliases, char ***tokptr); 40 | 41 | void expand_vars(info_t *info, char ***tokptr); 42 | char **_expand_vars(info_t *info, char ***tokptr); 43 | 44 | void remove_comments(cmdlist_t *cmd); 45 | 46 | void open_script(info_t *info); 47 | 48 | void _sigint(int signal); 49 | 50 | #endif /* SHELL_H */ 51 | -------------------------------------------------------------------------------- /info.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "info.h" 6 | 7 | /** 8 | * init_info - initialize shell info 9 | * @argc: the arg count 10 | * @argv: the arg values 11 | * Return: pointer to the info 12 | */ 13 | info_t *init_info(int argc, char **argv) 14 | { 15 | static info_t info; 16 | char *error = NULL; 17 | 18 | info.argc = argc; 19 | info.argv = argv; 20 | info.fileno = STDIN_FILENO; 21 | if (argc > 1) 22 | { 23 | info.file = argv[1]; 24 | info.fileno = open(info.file, O_RDONLY); 25 | if (info.fileno == -1) 26 | { 27 | error = strjoin(NULL, " ", "Can't open", info.file); 28 | perrorl_default(*argv, info.lineno, error, NULL); 29 | free(error); 30 | info.status = 127; 31 | exit(free_info(&info)); 32 | } 33 | } 34 | info.interactive = isatty(info.fileno); 35 | info.pid = getpid(); 36 | info.cwd = getcwd(NULL, 0); 37 | info.env = env_to_dict(environ); 38 | return (&info); 39 | } 40 | 41 | 42 | /** 43 | * free_info - free and nullify dynamically allocated info 44 | * @info: pointer to the info 45 | * Return: current exit status 46 | */ 47 | int free_info(info_t *info) 48 | { 49 | free(info->line); 50 | info->line = _getline(-1); 51 | free_tokens(&info->tokens); 52 | free(info->cwd); 53 | info->cwd = NULL; 54 | free(info->exe); 55 | info->exe = NULL; 56 | free_dict(&info->env); 57 | free_list(&info->path); 58 | free_dict(&info->aliases); 59 | free_cmdlist(&info->commands); 60 | return (info->status); 61 | } 62 | -------------------------------------------------------------------------------- /info.h: -------------------------------------------------------------------------------- 1 | #ifndef _INFO_H_ 2 | #define _INFO_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "alias.h" 8 | #include "command.h" 9 | #include "env.h" 10 | #include "error.h" 11 | #include "getline.h" 12 | #include "history.h" 13 | #include "list.h" 14 | #include "string.h" 15 | #include "tokens.h" 16 | #include "types.h" 17 | 18 | extern char **environ; 19 | 20 | /** 21 | * struct info - shell state 22 | * @interactive: arguments passed 23 | * @argc: arguments passed 24 | * @argv: arguments passed 25 | * @file: arguments passed 26 | * @fileno: arguments passed 27 | * @status: arguments passed 28 | * @line: arguments passed 29 | * @lineno: arguments passed 30 | * @tokens: arguments passed 31 | * @pid: arguments passed 32 | * @cwd: arguments passed 33 | * @exe: arguments passed 34 | * @env: arguments passed 35 | * @path: arguments passed 36 | * @aliases: arguments passed 37 | * @history: arguments passed 38 | * @commands: arguments passed 39 | */ 40 | struct info 41 | { 42 | int interactive; 43 | int argc; 44 | char **argv; 45 | char *file; 46 | int fileno; 47 | int status; 48 | char *line; 49 | size_t lineno; 50 | char **tokens; 51 | pid_t pid; 52 | char *cwd; 53 | char *exe; 54 | env_t *env; 55 | list_t *path; 56 | alias_t *aliases; 57 | history_t *history; 58 | cmdlist_t *commands; 59 | }; 60 | 61 | info_t *init_info(int argc, char **argv); 62 | int free_info(info_t *info); 63 | 64 | #endif /* _INFO_H_ */ 65 | -------------------------------------------------------------------------------- /input.c: -------------------------------------------------------------------------------- 1 | #include "hsh.h" 2 | #include "getline.h" 3 | 4 | /** 5 | * read_input - get input 6 | * @info: shell information 7 | * 8 | * Return: line size 9 | */ 10 | bool read_input(info_t *info) 11 | { 12 | char *line = NULL, *temp = NULL; 13 | 14 | if (info->interactive) 15 | write(STDERR_FILENO, "$ ", 2); 16 | 17 | info->lineno += 1; 18 | while (_read_input(&info->line, info->fileno) & 19 | (QUOTE_DOUBLE | QUOTE_SINGLE | QUOTE_ESCAPE)) 20 | { 21 | temp = line; 22 | line = strjoin(NULL, "", temp, info->line); 23 | free(temp); 24 | free(info->line); 25 | if (info->interactive) 26 | write(STDERR_FILENO, "> ", 2); 27 | info->lineno += 1; 28 | } 29 | if (line) 30 | { 31 | temp = info->line; 32 | info->line = strjoin(NULL, "", line, temp); 33 | free(temp); 34 | free(line); 35 | } 36 | return (info->line); 37 | } 38 | 39 | 40 | /** 41 | * _read_input - read a single line 42 | * @lineptr: line buffer 43 | * @fd: file descriptor to read from 44 | * 45 | * Return: ending quote state 46 | */ 47 | quote_state_t _read_input(char **lineptr, int fd) 48 | { 49 | char *line = *lineptr = _getline(fd); 50 | static quote_state_t state = QUOTE_NONE; 51 | size_t index = 0; 52 | 53 | if (line) 54 | { 55 | switch (state & (QUOTE_DOUBLE | QUOTE_SINGLE)) 56 | { 57 | case QUOTE_DOUBLE: 58 | case QUOTE_SINGLE: 59 | do { 60 | index += quote_state_len(line + index, state); 61 | if (line[index] == '\0') 62 | continue; 63 | if (state & (QUOTE_DOUBLE | QUOTE_SINGLE)) 64 | index += 1; 65 | /* fall through */ 66 | case 0: 67 | state = quote_state(line[index]); 68 | if (state & (QUOTE_DOUBLE | QUOTE_SINGLE | QUOTE_ESCAPE)) 69 | index += 1; 70 | } while (line[index]); 71 | } 72 | } 73 | return (state); 74 | } 75 | -------------------------------------------------------------------------------- /list.c: -------------------------------------------------------------------------------- 1 | #include "list.h" 2 | 3 | /** 4 | * str_to_list - turn a string into a linked list 5 | * @str: string passed 6 | * @delim: delimiter passed 7 | * Return: pointer to list 8 | */ 9 | list_t *str_to_list(const char *str, char delim) 10 | { 11 | list_t *head = NULL; 12 | 13 | if (!str) 14 | return (NULL); 15 | 16 | if (!_str_to_list(&head, str, delim)) 17 | free_list(&head); 18 | 19 | return (head); 20 | } 21 | 22 | 23 | /** 24 | * _str_to_list - turn a string into a linked list (helper) 25 | * @tailptr: pointer to the tail of the list 26 | * @str: string 27 | * @delim: delimiter 28 | * 29 | * Return: pointer to the tail of the list 30 | */ 31 | list_t *_str_to_list(list_t **tailptr, const char *str, char delim) 32 | { 33 | list_t *tail; 34 | ssize_t len = _strchr(str, delim); 35 | 36 | if (len == -1) 37 | len = _strlen(str); 38 | 39 | tail = add_node_end(tailptr, NULL); 40 | if (!tail) 41 | return (NULL); 42 | 43 | tail->str = _memdup(str, len + 1); 44 | if (!tail->str) 45 | return (NULL); 46 | 47 | tail->str[len] = '\0'; 48 | 49 | if (str[len]) 50 | return (_str_to_list(&tail, str + len + 1, delim)); 51 | 52 | return (tail); 53 | } 54 | 55 | 56 | /** 57 | * add_node - insert a string at the beginning of the list 58 | * @headptr: a pointer to the address of the first list node 59 | * @str: the string to add to the list 60 | * Return: If memory allocation fails, return NULL. Otherwise, return the 61 | * address of the new no 62 | */ 63 | list_t *add_node(list_t **headptr, const char *str) 64 | { 65 | list_t *new; 66 | 67 | if (!headptr) 68 | return (NULL); 69 | 70 | new = malloc(sizeof(list_t)); 71 | if (!new) 72 | return (NULL); 73 | 74 | new->str = _strdup(str); 75 | new->next = *headptr; 76 | 77 | *headptr = new; 78 | 79 | return (new); 80 | } 81 | 82 | 83 | /** 84 | * add_node_end - add a string at the end of the list 85 | * @headptr: a pointer to the address of the first list node 86 | * @str: the string to add to the list 87 | * Return: If memory allocation fails, return NULL. Otherwise, return the 88 | * address of the new no 89 | */ 90 | list_t *add_node_end(list_t **headptr, const char *str) 91 | { 92 | list_t *new; 93 | 94 | if (!headptr) 95 | return (NULL); 96 | 97 | if (*headptr) 98 | return (add_node_end(&((*headptr)->next), str)); 99 | 100 | new = malloc(sizeof(list_t)); 101 | if (!new) 102 | return (NULL); 103 | 104 | new->str = _strdup(str); 105 | new->next = *headptr; 106 | 107 | *headptr = new; 108 | 109 | return (new); 110 | } 111 | 112 | 113 | /** 114 | * free_list - free a linked list and and set head to NULL 115 | * @headptr: the first list node 116 | */ 117 | void free_list(list_t **headptr) 118 | { 119 | if (!*headptr) 120 | return; 121 | 122 | free_list(&((*headptr)->next)); 123 | free((*headptr)->str); 124 | free(*headptr); 125 | *headptr = NULL; 126 | } 127 | -------------------------------------------------------------------------------- /list.h: -------------------------------------------------------------------------------- 1 | #ifndef LIST_H 2 | #define LIST_H 3 | 4 | #include 5 | #include "string.h" 6 | #include "types.h" 7 | 8 | /** 9 | * struct list - singly linked list 10 | * @str: dynamically-allocated string 11 | * @next: pointer to the next node 12 | */ 13 | struct list 14 | { 15 | char *str; 16 | struct list *next; 17 | }; 18 | 19 | list_t *str_to_list(const char *str, char delim); 20 | list_t *_str_to_list(list_t **tailptr, const char *str, char delim); 21 | list_t *add_node(list_t **headptr, const char *str); 22 | list_t *add_node_end(list_t **headptr, const char *str); 23 | void free_list(list_t **headptr); 24 | 25 | #endif /* LIST_H */ 26 | -------------------------------------------------------------------------------- /man_1_simple_shell: -------------------------------------------------------------------------------- 1 | .\" Man page for hsh 2 | .TH HSH 1 "28 August 2019" "Holberton" "Holberton Shell Man Page" 3 | .SH NAME 4 | .B hsh 5 | \- a simple command interpreter 6 | .SH SYNOPSIS 7 | .B hsh 8 | .RI [ FILENAME ] 9 | .SH DESCRIPTION 10 | hsh executes commands read from standard input or from a file. Lines are read 11 | one at a time. Each line is first split into tokens and dequoted. The first 12 | word is then checked for aliases. If an alias is found, it is expanded. The 13 | first word of the resulting command is checked to see if it is a builtin. If 14 | it is, then hsh executes the builtin before reading the next line of input. 15 | If it is not a builtin, a path search is performed. If the command is not 16 | found, the status is set to 127. If the command is found but not executable, 17 | the status is set to 126. Otherwise, hsh forks and executes the given command 18 | before reading the next line of input. 19 | mimic the behavior of other POSIX-compliant shells (e.g. dash) 20 | .SS BUILTINS 21 | .TP 22 | .B alias 23 | .RI [ NAME [=' VALUE '] 24 | .R ...] 25 | Print and define command aliases. 26 | .TP 27 | .B cd 28 | .RI [ DIRECTORY ] 29 | Change the current working directory. 30 | .TP 31 | .B env 32 | Print the environment. 33 | .TP 34 | .B exit 35 | .RI [ STATUS ] 36 | .Exit the shell. 37 | .TP 38 | .B help 39 | .R [ 40 | .I BUILTIN 41 | .R ...] 42 | Show a help message. 43 | .TP 44 | .B setenv 45 | .I VARIABLE VALUE 46 | Set an environment variable. 47 | .TP 48 | .B unsetenv 49 | .I VARIABLE 50 | .R ... 51 | Unset an environment variable. 52 | .SH EXIT STATUS 53 | If the exit builtin is called with a status, 54 | .B hsh 55 | .R exits with the specified value. Otherwise, 56 | .B hsh 57 | .R exits with the exit status of the previous command. 58 | .SH ENVIRONMENT 59 | .TP 60 | PATH 61 | The default search path for executables 62 | .TP 63 | HOME 64 | The default argument for the cd builtin 65 | .TP 66 | PWD 67 | Set by the cd builtin upon changing directories 68 | .TP 69 | OLDPWD 70 | Set by the cd builtin and used as it's argument it receives ``-'' 71 | .SH EXAMPLES 72 | .PP 73 | hsh 74 | .PP 75 | echo ls | hsh 76 | .SH SEE ALSO 77 | .BR sh(1) , bash(1) , csh(1) , dash(1) 78 | .SH BUGS 79 | No known bugs. 80 | Report bugs to ... 81 | .SH AUTHORS 82 | .PP 83 | Banu Sapakova (banuaksom@gmail.com) 84 | .PP 85 | Patrick DeYoreo (pdeyoreo@gmail.com) 86 | -------------------------------------------------------------------------------- /mem.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | /** 4 | * _memchr - get the index of the first matching value 5 | * @src: start of the memory area to search 6 | * @chr: value to find 7 | * @n: size of the search area 8 | * Return: If chr does not occur in the first n elements of src, return -1. 9 | * Otherwise, return the index of the first occurence of chr. 10 | */ 11 | ssize_t _memchr(const void *src, unsigned char chr, size_t n) 12 | { 13 | const unsigned char *mem = src; 14 | ssize_t i = 0; 15 | 16 | if (src) 17 | { 18 | while (n--) 19 | { 20 | if (mem[i] == chr) 21 | return (i); 22 | i += 1; 23 | } 24 | } 25 | return (-1); 26 | } 27 | 28 | /** 29 | * _memcpy - copy a memory area 30 | * @dest: a pointer to the start of the target area 31 | * @src: a pointer to the start of the source area 32 | * @n: the number of bytes to copy 33 | * 34 | * Description: This function copies n bytes from the memory area at src 35 | * to the memory area at dest. These memory areas must not overlap. 36 | * 37 | * Return: a pointer to dest 38 | */ 39 | void *_memcpy(void *dest, const void *src, size_t n) 40 | { 41 | unsigned char *w_pos = dest; 42 | const unsigned char *r_pos = src; 43 | 44 | if (dest && src) 45 | { 46 | while (n--) 47 | *w_pos++ = *r_pos++; 48 | } 49 | return (dest); 50 | } 51 | 52 | /** 53 | * _memdup - duplicate a memory area 54 | * @src: a pointer to the start of the source area 55 | * @n: the number of bytes to duplicate 56 | * Return: If memory allocation fails, return NULL. Otherwise, return a 57 | * pointer to the start of the duplicated memory. 58 | */ 59 | void *_memdup(const void *src, size_t n) 60 | { 61 | void *dup = malloc(n); 62 | unsigned char *w_pos = dup; 63 | const unsigned char *r_pos = src; 64 | 65 | if (dup) 66 | { 67 | while (n--) 68 | *w_pos++ = *r_pos++; 69 | } 70 | return (dup); 71 | } 72 | 73 | /** 74 | * _memset - fill a region of memory with a given value 75 | * @dest: pointer to the beginning of the region 76 | * @chr: value to write to the region 77 | * @n: number of bytes to write 78 | * Return: dest 79 | */ 80 | void *_memset(void *dest, unsigned char chr, size_t n) 81 | { 82 | unsigned char *mem = dest; 83 | 84 | if (dest) 85 | { 86 | while (n--) 87 | *mem++ = chr; 88 | } 89 | return (dest); 90 | } 91 | -------------------------------------------------------------------------------- /num_to_str.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | /** 4 | * _num_to_str - converts number to string 5 | * @buf: string 6 | * @n: number passed 7 | */ 8 | static void _num_to_str(char **buf, size_t n) 9 | { 10 | if (n > 9) 11 | _num_to_str(buf, n / 10); 12 | **buf = '0' + n % 10; 13 | *buf += 1; 14 | **buf = '\0'; 15 | } 16 | 17 | /** 18 | * num_to_str - converts number to string 19 | * @n: number passed 20 | * Return: pointer to string 21 | */ 22 | char *num_to_str(size_t n) 23 | { 24 | size_t tmp = n, len = 1; 25 | char *buf; 26 | 27 | while (tmp /= 10) 28 | len++; 29 | buf = malloc(len + 1); 30 | if (!buf) 31 | return (NULL); 32 | _num_to_str(&buf, n); 33 | return (buf - len); 34 | } 35 | -------------------------------------------------------------------------------- /parse.c: -------------------------------------------------------------------------------- 1 | #include "hsh.h" 2 | 3 | /** 4 | * parse - parse input 5 | * @info: shell information 6 | * 7 | * Description: This function expands aliases, variables, and word splitting 8 | * 9 | * Return: the final number of tokens 10 | */ 11 | int parse(info_t *info) 12 | { 13 | char **tokens, *tok; 14 | size_t n = 0; 15 | cmdlist_t *cmd = info->commands = cmd_to_list(info->line); 16 | 17 | while (cmd) 18 | { 19 | remove_comments(cmd); 20 | if (!cmd->tokens) 21 | { 22 | cmd = cmd->next; 23 | del_cmd(&(info->commands), n); 24 | continue; 25 | } 26 | expand_aliases(info->aliases, &(cmd->tokens)); 27 | if (!cmd->tokens) 28 | { 29 | cmd = cmd->next; 30 | del_cmd(&(info->commands), n); 31 | continue; 32 | } 33 | expand_vars(info, &(cmd->tokens)); 34 | if (!cmd->tokens) 35 | { 36 | cmd = cmd->next; 37 | del_cmd(&(info->commands), n); 38 | continue; 39 | } 40 | tokens = cmd->tokens; 41 | for (tok = *tokens; tok; tok = *(++tokens)) 42 | { 43 | *tokens = dequote(tok); 44 | free(tok); 45 | } 46 | cmd = cmd->next; 47 | ++n; 48 | } 49 | return (n); 50 | } 51 | -------------------------------------------------------------------------------- /path.c: -------------------------------------------------------------------------------- 1 | #include "path.h" 2 | 3 | /** 4 | * search_path - searches for the directory with the executable program 5 | * @info: argument passed 6 | * @path: argument passed 7 | * Return: pointer to directory string 8 | */ 9 | char *search_path(info_t *info, list_t *path) 10 | { 11 | char *pathname, *command = *info->tokens; 12 | struct stat sb; 13 | 14 | while (path) 15 | { 16 | if (*path->str == '\0') 17 | pathname = strjoin(NULL, "/", info->cwd, command); 18 | else 19 | pathname = strjoin(NULL, "/", path->str, command); 20 | if (stat(pathname, &sb) == 0) 21 | { 22 | if ((sb.st_mode & S_IFMT) != S_IFDIR) 23 | return (pathname); 24 | } 25 | free(pathname); 26 | path = path->next; 27 | } 28 | return (NULL); 29 | } 30 | -------------------------------------------------------------------------------- /path.h: -------------------------------------------------------------------------------- 1 | #ifndef PATH_H 2 | #define PATH_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "info.h" 9 | #include "list.h" 10 | #include "string.h" 11 | #include "types.h" 12 | 13 | char *search_path(info_t *info, list_t *path); 14 | 15 | #endif /* PATH_H */ 16 | -------------------------------------------------------------------------------- /quote.c: -------------------------------------------------------------------------------- 1 | #include "quote.h" 2 | 3 | /** 4 | * quote_state - get the state associated with a given character 5 | * @c: character 6 | * 7 | * Return: the state associated with c 8 | */ 9 | quote_state_t quote_state(char c) 10 | { 11 | if (_isspace(c)) 12 | return (QUOTE_NONE); 13 | if (c == '"') 14 | return (QUOTE_DOUBLE); 15 | if (c == '\'') 16 | return (QUOTE_SINGLE); 17 | if (c == '\\') 18 | return (QUOTE_ESCAPE); 19 | return (QUOTE_WORD); 20 | } 21 | 22 | 23 | /** 24 | * quote_state_f - get the function associated with a given state 25 | * @s: state 26 | * 27 | * Return: the state associated with c 28 | */ 29 | quote_state_fp quote_state_f(quote_state_t s) 30 | { 31 | switch (s) 32 | { 33 | case QUOTE_NONE: 34 | return (_quote_state_none); 35 | case QUOTE_WORD: 36 | return (_quote_state_word); 37 | case QUOTE_DOUBLE: 38 | return (_quote_state_double); 39 | case QUOTE_SINGLE: 40 | return (_quote_state_single); 41 | case QUOTE_ESCAPE: 42 | return (_quote_state_escape); 43 | } 44 | return (NULL); 45 | } 46 | 47 | 48 | /** 49 | * quote_state_len - get the length of a given state 50 | * @str: string 51 | * @state: state 52 | * 53 | * Return: the state associated with c 54 | */ 55 | size_t quote_state_len(const char *str, quote_state_t state) 56 | { 57 | return (quote_state_f(state)(str, NULL)); 58 | } 59 | -------------------------------------------------------------------------------- /quote.h: -------------------------------------------------------------------------------- 1 | #ifndef _QUOTE_H_ 2 | #define _QUOTE_H_ 3 | 4 | #include 5 | 6 | #include "ctype.h" 7 | #include "string.h" 8 | 9 | /** 10 | * enum quote_state - a quote state mnemonic 11 | * @QUOTE_NONE: In an unquoted sequence of blanks 12 | * @QUOTE_WORD: In an unquoted sequence of non-blanks 13 | * @QUOTE_DOUBLE: In double quotes 14 | * @QUOTE_SINGLE: In single quotes 15 | * @QUOTE_ESCAPE: Following a backslash 16 | */ 17 | typedef enum quote_state 18 | { 19 | QUOTE_NONE = 0x0, 20 | QUOTE_WORD = 0x1, 21 | QUOTE_DOUBLE = 0x2, 22 | QUOTE_SINGLE = 0x4, 23 | QUOTE_ESCAPE = 0x8 24 | } quote_state_t; 25 | 26 | typedef size_t (*quote_state_fp)(const char *, quote_state_t *); 27 | 28 | quote_state_t quote_state(char c); 29 | 30 | quote_state_fp quote_state_f(quote_state_t s); 31 | 32 | size_t quote_state_len(const char *str, quote_state_t state); 33 | size_t _quote_state_none(const char *str, quote_state_t *state); 34 | size_t _quote_state_word(const char *str, quote_state_t *state); 35 | size_t _quote_state_double(const char *str, quote_state_t *state); 36 | size_t _quote_state_single(const char *str, quote_state_t *state); 37 | size_t _quote_state_escape(const char *str, quote_state_t *state); 38 | 39 | int _isspecial_double(char c); 40 | 41 | char *dequote(const char *str); 42 | size_t dequote_len(const char *str); 43 | 44 | #endif /* _QUOTE_H_ */ 45 | -------------------------------------------------------------------------------- /remove_comments.c: -------------------------------------------------------------------------------- 1 | #include "hsh.h" 2 | 3 | /** 4 | * remove_comments - remove comments (#) from a command 5 | * @cmd: pointer to the cmd to process 6 | */ 7 | void remove_comments(cmdlist_t *cmd) 8 | { 9 | char **tokens, **new, *tmp; 10 | 11 | for (tokens = cmd->tokens; *tokens; ++tokens) 12 | { 13 | if (**tokens == '#') 14 | { 15 | tmp = *tokens; 16 | *tokens = NULL; 17 | new = arrdup(cmd->tokens); 18 | *tokens = tmp; 19 | free_tokens(&(cmd->tokens)); 20 | cmd->tokens = new; 21 | free_cmdlist(&(cmd->next)); 22 | return; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /split_cmd.c: -------------------------------------------------------------------------------- 1 | #include "command.h" 2 | 3 | /** 4 | * split_cmd - split a command with NULL-bytes on unquoted semicolons 5 | * @cmd: the command to split 6 | * 7 | * Return: The total number of commands resulting from the split 8 | */ 9 | size_t split_cmd(char *cmd) 10 | { 11 | size_t count = 1, state_len; 12 | ssize_t sep_index; 13 | quote_state_t state; 14 | 15 | do { 16 | while (*cmd && (state = quote_state(*cmd)) != QUOTE_NONE) 17 | { 18 | if (state & QUOTE_WORD) 19 | { 20 | state_len = _quote_state_word(cmd, NULL); 21 | sep_index = _strnchr(cmd, ';', state_len); 22 | if (sep_index != -1) 23 | { 24 | state_len = sep_index; 25 | *(cmd++ + state_len) = '\0'; 26 | ++count; 27 | } 28 | cmd += state_len; 29 | } 30 | else if (state & QUOTE_ESCAPE) 31 | { 32 | if (*(cmd + 1) == '\n') 33 | _strcpy(cmd, cmd + 2); 34 | else if (*(++cmd)) 35 | ++cmd; 36 | } 37 | else 38 | { 39 | cmd += quote_state_len(cmd + 1, state) + 1; 40 | if (*cmd && (state & (QUOTE_SINGLE | QUOTE_DOUBLE))) 41 | ++cmd; 42 | } 43 | } 44 | } while (*(cmd += quote_state_len(cmd, QUOTE_NONE))); 45 | 46 | return (count); 47 | } 48 | 49 | -------------------------------------------------------------------------------- /str.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | /** 4 | * _strchr - get the index of the first matching character 5 | * @str: string passed 6 | * @chr: character passed 7 | * Return: index 8 | */ 9 | ssize_t _strchr(const char *str, char chr) 10 | { 11 | ssize_t index; 12 | 13 | if (!str) 14 | return (-1); 15 | 16 | for (index = 0; str[index]; ++index) 17 | { 18 | if (str[index] == chr) 19 | return (index); 20 | } 21 | 22 | return (-1); 23 | } 24 | 25 | 26 | /** 27 | * _strdup - create a new array containing a copy of the given string 28 | * @str: a pointer to the string to copy 29 | * Return: If str is NULL or if memory allocation fails, return NULL. 30 | * Otherwise a return a pointer to the new copy 31 | */ 32 | char *_strdup(const char *str) 33 | { 34 | char *dup; 35 | size_t size = 0; 36 | 37 | if (!str) 38 | return (NULL); 39 | 40 | while (str[size++]) 41 | ; 42 | 43 | dup = malloc(sizeof(char) * size); 44 | if (!dup) 45 | return (NULL); 46 | 47 | while (size--) 48 | dup[size] = str[size]; 49 | 50 | return (dup); 51 | } 52 | 53 | 54 | /** 55 | * _strlen - calculate the length of a string 56 | * @str: the string to calculate the length of 57 | * Return: the length of the string 58 | */ 59 | ssize_t _strlen(const char *str) 60 | { 61 | const char *pos = str; 62 | 63 | if (!str) 64 | return (-1); 65 | 66 | while (*pos) 67 | ++pos; 68 | 69 | return (pos - str); 70 | } 71 | 72 | 73 | /** 74 | * _strcmp - compare two strings 75 | * @s1: a string to compare 76 | * @s2: the other string to compare 77 | * Return: 0 if s1 matches s2, 78 | * otherwise an integer less than 0 if s1 is less than s2, 79 | * otherwise an integer greater than 0 if s1 is greater than s2. 80 | */ 81 | int _strcmp(const char *s1, const char *s2) 82 | { 83 | for (; *s1 && *s2; ++s1, ++s2) 84 | { 85 | if (*s1 != *s2) 86 | return (*s1 - *s2); 87 | } 88 | 89 | if (*s1) 90 | return (1); 91 | if (*s2) 92 | return (-1); 93 | 94 | return (0); 95 | } 96 | 97 | 98 | /** 99 | * _strcpy - copy a string 100 | * @dest: destination 101 | * @src: source 102 | * Return: a pointer to dest 103 | */ 104 | char *_strcpy(char *dest, const char *src) 105 | { 106 | char *pos = dest; 107 | 108 | while (*src) 109 | *pos++ = *src++; 110 | *pos = '\0'; 111 | 112 | return (dest); 113 | } 114 | -------------------------------------------------------------------------------- /string.h: -------------------------------------------------------------------------------- 1 | #ifndef _STRING_H_ 2 | #define _STRING_H_ 3 | 4 | #include 5 | #include 6 | 7 | ssize_t _memchr(const void *src, unsigned char chr, size_t n); 8 | void *_memcpy(void *dest, const void *src, size_t n); 9 | void *_memdup(const void *src, size_t n); 10 | void *_memset(void *dest, unsigned char chr, size_t n); 11 | 12 | ssize_t _strchr(const char *str, char chr); 13 | ssize_t _strnchr(const char *str, char chr, size_t n); 14 | 15 | int _strcmp(const char *s1, const char *s2); 16 | int _strncmp(const char *s1, const char *s2, size_t n); 17 | 18 | char *_strcpy(char *dest, const char *src); 19 | char *_strncpy(char *dest, const char *src, size_t n); 20 | 21 | char *_strdup(const char *str); 22 | char *_strndup(const char *str, size_t n); 23 | 24 | ssize_t _strlen(const char *str); 25 | ssize_t _strnlen(const char *str, size_t n); 26 | 27 | char *strjoin(size_t *n, const char *sep, const char *pre, const char *suf); 28 | char *strjoina(size_t *n, const char *sep, const char **array); 29 | char *strjoinl(size_t *n, const char *sep, ...); 30 | 31 | unsigned int atou(char *s); 32 | char *num_to_str(size_t n); 33 | 34 | #endif /* _STRING_H_ */ 35 | -------------------------------------------------------------------------------- /strjoin.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | /** 4 | * strjoin - joins two strings with another string 5 | * @n: address at which to store the size of the new string 6 | * @sep: joining string 7 | * @pre: prefix string 8 | * @suf: suffix string 9 | * Return: pointer to the new string 10 | */ 11 | char *strjoin(size_t *n, const char *sep, const char *pre, const char *suf) 12 | { 13 | char *dest = NULL; 14 | size_t sep_len = _strlen(sep ? sep : ""); 15 | size_t pre_len = _strlen(pre ? pre : ""); 16 | size_t suf_len = _strlen(suf ? suf : ""); 17 | 18 | dest = malloc(sizeof(char) * (pre_len + sep_len + suf_len + 1)); 19 | if (dest) 20 | { 21 | _strcpy(dest, pre ? pre : ""); 22 | _strcpy(dest + pre_len, sep ? sep : ""); 23 | _strcpy(dest + pre_len + sep_len, suf ? suf : ""); 24 | if (n) 25 | *n = pre_len + sep_len + suf_len + 1; 26 | } 27 | return (dest); 28 | } 29 | 30 | 31 | /** 32 | * strjoina - join strings from a NULL-terminated array 33 | * @n: address at which to store the length of the new string 34 | * @sep: joining string 35 | * @array: array of strings 36 | * Return: pointer to the new string 37 | */ 38 | char *strjoina(size_t *n, const char *sep, const char **array) 39 | { 40 | char *dest = NULL; 41 | size_t len = 0, idx = 0, sep_len = _strlen(sep ? sep : ""); 42 | 43 | while (array[idx]) 44 | len += _strlen(array[idx++]); 45 | 46 | if (idx--) 47 | { 48 | dest = malloc(sizeof(char) * (len + idx * sep_len + 1)); 49 | if (dest) 50 | { 51 | len = 0; 52 | while (*array) 53 | { 54 | _strcpy(dest + len, *array); 55 | len += _strlen(*array); 56 | if (sep_len && idx--) 57 | { 58 | _strcpy(dest + len, sep); 59 | len += sep_len; 60 | } 61 | } 62 | if (n) 63 | *n = len + 1; 64 | } 65 | } 66 | return (dest); 67 | } 68 | 69 | 70 | /** 71 | * strjoinl - joins a NULL terminated list of strings with a character 72 | * @n: address at which to store the length of the new string 73 | * @sep: joining string 74 | * @...: strings 75 | * Return: pointer to the new string 76 | */ 77 | char *strjoinl(size_t *n, const char *sep, ...) 78 | { 79 | char *dest = NULL; 80 | const char *temp = NULL; 81 | size_t idx = 0, len = 0, sep_len = _strlen(sep ? sep : ""); 82 | va_list ap; 83 | 84 | for (va_start(ap, sep); (temp = va_arg(ap, char *)); idx += 1) 85 | len += _strlen(temp); 86 | va_end(ap); 87 | 88 | if (idx--) 89 | { 90 | dest = malloc(sizeof(char) * (len + idx * sep_len + 1)); 91 | if (dest) 92 | { 93 | len = 0; 94 | va_start(ap, sep); 95 | while ((temp = va_arg(ap, char *))) 96 | { 97 | _strcpy(dest + len, temp); 98 | len += _strlen(temp); 99 | if (sep_len && idx--) 100 | { 101 | _strcpy(dest + len, sep); 102 | len += sep_len; 103 | } 104 | } 105 | va_end(ap); 106 | if (n) 107 | *n = len + 1; 108 | } 109 | 110 | } 111 | return (dest); 112 | } 113 | -------------------------------------------------------------------------------- /strn.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | /** 4 | * _strnchr - get the index of the first matching character 5 | * @str: string passed 6 | * @chr: character passed 7 | * @n: max number of characters to check 8 | * Return: Index of the first occurence, or -1 chr is not found 9 | */ 10 | ssize_t _strnchr(const char *str, char chr, size_t n) 11 | { 12 | ssize_t index; 13 | 14 | if (!str) 15 | return (-1); 16 | 17 | for (index = 0; n && str[index]; --n, ++index) 18 | { 19 | if (str[index] == chr) 20 | return (index); 21 | } 22 | 23 | return (-1); 24 | } 25 | 26 | 27 | /** 28 | * _strndup - duplicate the given string 29 | * @str: the string to duplicate 30 | * @n: the max number of bytes to copy 31 | * 32 | * Description: This function copies at most n bytes. If str is longer 33 | * than n, only n bytes are copied, and a terminating null byte is added. 34 | * 35 | * Return: If str is NULL or if memory allocation fails, return NULL. 36 | * Otherwise a return a pointer to the dynamically-allocated duplicate. 37 | */ 38 | char *_strndup(const char *str, size_t n) 39 | { 40 | char *dup; 41 | size_t len = 0; 42 | 43 | if (!str) 44 | return (NULL); 45 | 46 | while (n && str[len]) 47 | --n, ++len; 48 | 49 | dup = malloc(sizeof(char) * (len + 1)); 50 | if (!dup) 51 | return (NULL); 52 | 53 | dup[len] = '\0'; 54 | 55 | while (len--) 56 | dup[len] = str[len]; 57 | 58 | return (dup); 59 | } 60 | 61 | 62 | /** 63 | * _strnlen - calculate the length of a string 64 | * @str: the string to measure 65 | * @n: the max number of characters to check 66 | * Return: the lesser of n and the length of the string 67 | */ 68 | ssize_t _strnlen(const char *str, size_t n) 69 | { 70 | const char *pos = str; 71 | 72 | if (!str) 73 | return (-1); 74 | 75 | while (n && *pos) 76 | --n, ++pos; 77 | 78 | return (pos - str); 79 | } 80 | 81 | 82 | 83 | /** 84 | * _strncmp - compare two strings 85 | * @s1: a string to compare 86 | * @s2: the other string to compare 87 | * @n: the max number of bytes to compare 88 | * Return: 0 if s1 matches s2, 89 | * otherwise an integer less than 0 if s1 is less than s2, 90 | * otherwise an integer greater than 0 if s1 is greater than s2. 91 | */ 92 | int _strncmp(const char *s1, const char *s2, size_t n) 93 | { 94 | for (; n && *s1 && *s2; --n, ++s1, ++s2) 95 | { 96 | if (*s1 != *s2) 97 | return (*s1 - *s2); 98 | } 99 | 100 | if (n) 101 | { 102 | if (*s1) 103 | return (1); 104 | if (*s2) 105 | return (-1); 106 | } 107 | 108 | return (0); 109 | } 110 | 111 | 112 | /** 113 | * _strncpy - copy the string 114 | * @dest: destination 115 | * @src: source 116 | * @n: the max number of bytes to copy 117 | * 118 | * Description: This function copies at most n bytes from src to dest. A 119 | * null byte will NOT be written if not found in the first n bytes 120 | * 121 | * Return: a pointer to dest 122 | */ 123 | 124 | char *_strncpy(char *dest, const char *src, size_t n) 125 | { 126 | char *pos = dest; 127 | 128 | for ( ; n && *src; --n) 129 | *pos++ = *src++; 130 | if (n) 131 | *pos = '\0'; 132 | 133 | return (dest); 134 | } 135 | -------------------------------------------------------------------------------- /tokens.c: -------------------------------------------------------------------------------- 1 | #include "tokens.h" 2 | 3 | /** 4 | * tokenize - split a string into words (tokens) and dequote 5 | * @str: the string to tokenize 6 | * Return: If malloc fails or if str is 0 or contains no tokens, return NULL. 7 | * Otherwise, return an array containing the tokens in str, terminated by NULL. 8 | */ 9 | char **tokenize(const char *str) 10 | { 11 | char **tokens; 12 | const char *tok; 13 | size_t count; 14 | quote_state_t state; 15 | 16 | if (!str) 17 | return (NULL); 18 | 19 | tokens = malloc(sizeof(char *) * (count_tokens(str) + 1)); 20 | if (!tokens) 21 | return (NULL); 22 | 23 | for (count = 0; *(str += quote_state_len(str, QUOTE_NONE)); ++count) 24 | { 25 | tok = str; 26 | 27 | while (*str && (state = quote_state(*str)) != QUOTE_NONE) 28 | { 29 | if (state & (QUOTE_DOUBLE | QUOTE_SINGLE | QUOTE_ESCAPE)) 30 | str += quote_state_len(str + 1, state) + 1; 31 | else 32 | str += quote_state_len(str, state); 33 | 34 | if (*str && (state & (QUOTE_DOUBLE | QUOTE_SINGLE))) 35 | ++str; 36 | } 37 | 38 | tokens[count] = _memdup(tok, str - tok + 1); 39 | if (!tokens[count]) 40 | { 41 | free_tokens(&tokens); 42 | return (NULL); 43 | } 44 | tokens[count][str - tok] = '\0'; 45 | } 46 | tokens[count] = NULL; 47 | 48 | return (tokens); 49 | } 50 | 51 | 52 | /** 53 | * count_tokens - compute the length of a string after dequoting 54 | * @str: the string to evaluate 55 | * Return: Return the length of str after dequoting 56 | */ 57 | size_t count_tokens(const char *str) 58 | { 59 | size_t count; 60 | quote_state_t state; 61 | 62 | for (count = 0; *(str += quote_state_len(str, QUOTE_NONE)); ++count) 63 | { 64 | while (*str && (state = quote_state(*str)) != QUOTE_NONE) 65 | { 66 | if (state & (QUOTE_DOUBLE | QUOTE_SINGLE | QUOTE_ESCAPE)) 67 | str += quote_state_len(str + 1, state) + 1; 68 | else 69 | str += quote_state_len(str, state); 70 | 71 | if (*str && (state & (QUOTE_DOUBLE | QUOTE_SINGLE))) 72 | ++str; 73 | } 74 | } 75 | return (count); 76 | } 77 | 78 | 79 | /** 80 | * tokenize_noquote - split a string into words (tokens) 81 | * @str: the string to tokenize 82 | * Return: If malloc fails or if str is 0 or contains no tokens, return NULL. 83 | * Otherwise, return an array containing the tokens in str, terminated by NULL. 84 | */ 85 | char **tokenize_noquote(const char *str) 86 | { 87 | char **tokens; 88 | const char *tok; 89 | size_t count; 90 | 91 | if (!str) 92 | return (NULL); 93 | 94 | tokens = malloc(sizeof(char *) * (count_tokens_noquote(str) + 1)); 95 | if (!tokens) 96 | return (NULL); 97 | 98 | for (count = 0; *str; ++count) 99 | { 100 | while (_isspace(*str)) 101 | ++str; 102 | if (!*str) 103 | break; 104 | 105 | tok = str; 106 | do { 107 | ++str; 108 | } while (*str && !_isspace(*str)); 109 | 110 | tokens[count] = _memdup(tok, str - tok + 1); 111 | if (!tokens[count]) 112 | { 113 | free_tokens(&tokens); 114 | return (NULL); 115 | } 116 | tokens[count][str - tok] = '\0'; 117 | } 118 | tokens[count] = NULL; 119 | 120 | return (tokens); 121 | } 122 | 123 | 124 | /** 125 | * count_tokens_noquote - count the words in a string 126 | * @str: the string to evaluate 127 | * Return: If str is NULL, return -1. 128 | * Otherwise, return the number of words in str. 129 | */ 130 | size_t count_tokens_noquote(const char *str) 131 | { 132 | size_t tok_count; 133 | 134 | for (tok_count = 0; *str; ++tok_count) 135 | { 136 | while (_isspace(*str)) 137 | ++str; 138 | if (!*str) 139 | break; 140 | do { 141 | ++str; 142 | } while (*str && !_isspace(*str)); 143 | } 144 | return (tok_count); 145 | } 146 | 147 | 148 | /** 149 | * free_tokens - free & nullify an array of strings 150 | * @tokens: pointer to an array of tokens 151 | */ 152 | void free_tokens(char ***tokens) 153 | { 154 | char **tok; 155 | 156 | if (!tokens) 157 | return; 158 | 159 | tok = *tokens; 160 | if (!tok) 161 | return; 162 | 163 | while (*tok) 164 | free(*tok++); 165 | free(*tokens); 166 | 167 | *tokens = NULL; 168 | } 169 | -------------------------------------------------------------------------------- /tokens.h: -------------------------------------------------------------------------------- 1 | #ifndef TOKENS_H 2 | #define TOKENS_H 3 | 4 | #include 5 | #include "quote.h" 6 | #include "string.h" 7 | 8 | char **tokenize(const char *str); 9 | char **tokenize_noquote(const char *str); 10 | 11 | size_t count_tokens(const char *str); 12 | size_t count_tokens_noquote(const char *str); 13 | 14 | void free_tokens(char ***tokens); 15 | 16 | char **arrdup(char **arr); 17 | 18 | char **arrjoin(char **arr1, char **arr2); 19 | 20 | #endif /* TOKENS_H */ 21 | -------------------------------------------------------------------------------- /types.h: -------------------------------------------------------------------------------- 1 | #ifndef _TYPES_H_ 2 | #define _TYPES_H_ 3 | 4 | typedef struct builtin builtin_t; 5 | typedef struct cmdlist cmdlist_t; 6 | typedef struct cmdtree cmdtree_t; 7 | typedef struct dict dict_t; 8 | typedef struct history history_t; 9 | typedef struct info info_t; 10 | typedef struct list list_t; 11 | 12 | #endif /* _TYPES_H_ */ 13 | --------------------------------------------------------------------------------