├── AUTHORS ├── README.md ├── _getline.c ├── alias_management.c ├── builtins_env.c ├── builtins_list.c ├── builtins_more.c ├── env_management.c ├── execute.c ├── expansions.c ├── find_in_path.c ├── helper_1.c ├── helper_num.c ├── helper_p.c ├── helper_str.c ├── macros.h ├── main.c ├── man_1_simple_shell ├── shell.h ├── str_token.c └── tokenize.c /AUTHORS: -------------------------------------------------------------------------------- 1 | #We have 2 contributors. 2 | 3 | 1) Bright Daniel 4 | 2) 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple_shell 2 | A project to wrap up sprint 1 of Alx SE program. Done by by two persons 3 | -------------------------------------------------------------------------------- /_getline.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | /** 4 | * _getline - read one line from the prompt. 5 | * @data: struct for the program's data 6 | * 7 | * Return: reading counting bytes. 8 | */ 9 | int _getline(data_of_program *data) 10 | { 11 | char buff[BUFFER_SIZE] = {'\0'}; 12 | static char *array_commands[10] = {NULL}; 13 | static char array_operators[10] = {'\0'}; 14 | ssize_t bytes_read, i = 0; 15 | 16 | /* check if doesnot exist more commands in the array */ 17 | /* and checks the logical operators */ 18 | if (!array_commands[0] || (array_operators[0] == '&' && errno != 0) || 19 | (array_operators[0] == '|' && errno == 0)) 20 | { 21 | /*free the memory allocated in the array if it exists */ 22 | for (i = 0; array_commands[i]; i++) 23 | { 24 | free(array_commands[i]); 25 | array_commands[i] = NULL; 26 | } 27 | 28 | /* read from the file descriptor int to buff */ 29 | bytes_read = read(data->file_descriptor, &buff, BUFFER_SIZE - 1); 30 | if (bytes_read == 0) 31 | return (-1); 32 | 33 | /* split lines for \n or ; */ 34 | i = 0; 35 | do { 36 | array_commands[i] = str_duplicate(_strtok(i ? NULL : buff, "\n;")); 37 | /*checks and split for && and || operators*/ 38 | i = check_logic_ops(array_commands, i, array_operators); 39 | } while (array_commands[i++]); 40 | } 41 | 42 | /*obtains the next command (command 0) and remove it for the array*/ 43 | data->input_line = array_commands[0]; 44 | for (i = 0; array_commands[i]; i++) 45 | { 46 | array_commands[i] = array_commands[i + 1]; 47 | array_operators[i] = array_operators[i + 1]; 48 | } 49 | 50 | return (str_length(data->input_line)); 51 | } 52 | 53 | 54 | /** 55 | * check_logic_ops - checks and split for && and || operators 56 | * @array_commands: array of the commands. 57 | * @i: index in the array_commands to be checked 58 | * @array_operators: array of the logical operators for each previous command 59 | * 60 | * Return: index of the last command in the array_commands. 61 | */ 62 | int check_logic_ops(char *array_commands[], int i, char array_operators[]) 63 | { 64 | char *temp = NULL; 65 | int j; 66 | 67 | /* checks for the & char in the command line*/ 68 | for (j = 0; array_commands[i] != NULL && array_commands[i][j]; j++) 69 | { 70 | if (array_commands[i][j] == '&' && array_commands[i][j + 1] == '&') 71 | { 72 | /* split the line when chars && was found */ 73 | temp = array_commands[i]; 74 | array_commands[i][j] = '\0'; 75 | array_commands[i] = str_duplicate(array_commands[i]); 76 | array_commands[i + 1] = str_duplicate(temp + j + 2); 77 | i++; 78 | array_operators[i] = '&'; 79 | free(temp); 80 | j = 0; 81 | } 82 | if (array_commands[i][j] == '|' && array_commands[i][j + 1] == '|') 83 | { 84 | /* split the line when chars || was found */ 85 | temp = array_commands[i]; 86 | array_commands[i][j] = '\0'; 87 | array_commands[i] = str_duplicate(array_commands[i]); 88 | array_commands[i + 1] = str_duplicate(temp + j + 2); 89 | i++; 90 | array_operators[i] = '|'; 91 | free(temp); 92 | j = 0; 93 | } 94 | } 95 | return (i); 96 | } 97 | -------------------------------------------------------------------------------- /alias_management.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | /** 4 | * print_alias - add, remove or show aliases 5 | * @data: struct for the program's data 6 | * @alias: name of the alias to be printed 7 | * Return: zero if sucess, or other number if its declared in the arguments 8 | */ 9 | int print_alias(data_of_program *data, char *alias) 10 | { 11 | int i, j, alias_length; 12 | char buffer[250] = {'\0'}; 13 | 14 | if (data->alias_list) 15 | { 16 | alias_length = str_length(alias); 17 | for (i = 0; data->alias_list[i]; i++) 18 | { 19 | if (!alias || (str_compare(data->alias_list[i], alias, alias_length) 20 | && data->alias_list[i][alias_length] == '=')) 21 | { 22 | for (j = 0; data->alias_list[i][j]; j++) 23 | { 24 | buffer[j] = data->alias_list[i][j]; 25 | if (data->alias_list[i][j] == '=') 26 | break; 27 | } 28 | buffer[j + 1] = '\0'; 29 | buffer_add(buffer, "'"); 30 | buffer_add(buffer, data->alias_list[i] + j + 1); 31 | buffer_add(buffer, "'\n"); 32 | _print(buffer); 33 | } 34 | } 35 | } 36 | 37 | return (0); 38 | } 39 | 40 | /** 41 | * get_alias - add, remove or show aliases 42 | * @data: struct for the program's data 43 | * @name: name of the requested alias. 44 | * Return: zero if sucess, or other number if its declared in the arguments 45 | */ 46 | char *get_alias(data_of_program *data, char *name) 47 | { 48 | int i, alias_length; 49 | 50 | /* validate the arguments */ 51 | if (name == NULL || data->alias_list == NULL) 52 | return (NULL); 53 | 54 | alias_length = str_length(name); 55 | 56 | for (i = 0; data->alias_list[i]; i++) 57 | {/* Iterates through the environ and check for coincidence of the varname */ 58 | if (str_compare(name, data->alias_list[i], alias_length) && 59 | data->alias_list[i][alias_length] == '=') 60 | {/* returns the value of the key NAME= when find it */ 61 | return (data->alias_list[i] + alias_length + 1); 62 | } 63 | } 64 | /* returns NULL if did not find it */ 65 | return (NULL); 66 | 67 | } 68 | 69 | /** 70 | * set_alias - add, or override alias 71 | * @alias_string: alias to be seted in the form (name='value') 72 | * @data: struct for the program's data 73 | * Return: zero if sucess, or other number if its declared in the arguments 74 | */ 75 | int set_alias(char *alias_string, data_of_program *data) 76 | { 77 | int i, j; 78 | char buffer[250] = {'0'}, *temp = NULL; 79 | 80 | /* validate the arguments */ 81 | if (alias_string == NULL || data->alias_list == NULL) 82 | return (1); 83 | /* Iterates alias to find = char */ 84 | for (i = 0; alias_string[i]; i++) 85 | if (alias_string[i] != '=') 86 | buffer[i] = alias_string[i]; 87 | else 88 | {/* search if the value of the alias is another alias */ 89 | temp = get_alias(data, alias_string + i + 1); 90 | break; 91 | } 92 | 93 | /* Iterates through the alias list and check for coincidence of the varname */ 94 | for (j = 0; data->alias_list[j]; j++) 95 | if (str_compare(buffer, data->alias_list[j], i) && 96 | data->alias_list[j][i] == '=') 97 | {/* if the alias alredy exist */ 98 | free(data->alias_list[j]); 99 | break; 100 | } 101 | 102 | /* add the alias */ 103 | if (temp) 104 | {/* if the alias already exist */ 105 | buffer_add(buffer, "="); 106 | buffer_add(buffer, temp); 107 | data->alias_list[j] = str_duplicate(buffer); 108 | } 109 | else /* if the alias does not exist */ 110 | data->alias_list[j] = str_duplicate(alias_string); 111 | return (0); 112 | } 113 | -------------------------------------------------------------------------------- /builtins_env.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | /** 4 | * builtin_env - shows the environment where the shell runs 5 | * @data: struct for the program's data 6 | * Return: zero if sucess, or other number if its declared in the arguments 7 | */ 8 | int builtin_env(data_of_program *data) 9 | { 10 | int i; 11 | char cpname[50] = {'\0'}; 12 | char *var_copy = NULL; 13 | 14 | /* if not arguments */ 15 | if (data->tokens[1] == NULL) 16 | print_environ(data); 17 | else 18 | { 19 | for (i = 0; data->tokens[1][i]; i++) 20 | {/* checks if exists a char = */ 21 | if (data->tokens[1][i] == '=') 22 | {/* checks if exists a var with the same name and change its value*/ 23 | /* temporally */ 24 | var_copy = str_duplicate(env_get_key(cpname, data)); 25 | if (var_copy != NULL) 26 | env_set_key(cpname, data->tokens[1] + i + 1, data); 27 | 28 | /* print the environ */ 29 | print_environ(data); 30 | if (env_get_key(cpname, data) == NULL) 31 | {/* print the variable if it does not exist in the environ */ 32 | _print(data->tokens[1]); 33 | _print("\n"); 34 | } 35 | else 36 | {/* returns the old value of the var*/ 37 | env_set_key(cpname, var_copy, data); 38 | free(var_copy); 39 | } 40 | return (0); 41 | } 42 | cpname[i] = data->tokens[1][i]; 43 | } 44 | errno = 2; 45 | perror(data->command_name); 46 | errno = 127; 47 | } 48 | return (0); 49 | } 50 | 51 | /** 52 | * builtin_set_env - .. 53 | * @data: struct for the program's data 54 | * Return: zero if sucess, or other number if its declared in the arguments 55 | */ 56 | int builtin_set_env(data_of_program *data) 57 | { 58 | /* validate args */ 59 | if (data->tokens[1] == NULL || data->tokens[2] == NULL) 60 | return (0); 61 | if (data->tokens[3] != NULL) 62 | { 63 | errno = E2BIG; 64 | perror(data->command_name); 65 | return (5); 66 | } 67 | 68 | env_set_key(data->tokens[1], data->tokens[2], data); 69 | 70 | return (0); 71 | } 72 | 73 | /** 74 | * builtin_unset_env - .. 75 | * @data: struct for the program's data' 76 | * Return: .. 77 | */ 78 | int builtin_unset_env(data_of_program *data) 79 | { 80 | /* validate args */ 81 | if (data->tokens[1] == NULL) 82 | return (0); 83 | if (data->tokens[2] != NULL) 84 | { 85 | errno = E2BIG; 86 | perror(data->command_name); 87 | return (5); 88 | } 89 | env_remove_key(data->tokens[1], data); 90 | 91 | return (0); 92 | } 93 | -------------------------------------------------------------------------------- /builtins_list.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | /** 4 | * builtins_list - search for match and execute the associate builtin 5 | * @data: struct for the program's data 6 | * Return: Returns the return of the function executed is there is a match, 7 | * otherwise returns -1. 8 | **/ 9 | int builtins_list(data_of_program *data) 10 | { 11 | int iterator; 12 | builtins options[] = { 13 | {"exit", builtin_exit}, 14 | {"help", builtin_help}, 15 | {"cd", builtin_cd}, 16 | {"alias", builtin_alias}, 17 | {"env", builtin_env}, 18 | {"setenv", builtin_set_env}, 19 | {"unsetenv", builtin_unset_env}, 20 | {NULL, NULL} 21 | }; 22 | 23 | /*walk through the structure*/ 24 | for (iterator = 0; options[iterator].builtin != NULL; iterator++) 25 | { 26 | /*if there is a match between the given command and a builtin,*/ 27 | if (str_compare(options[iterator].builtin, data->command_name, 0)) 28 | { 29 | /*execute the function, and return the return value of the function*/ 30 | return (options[iterator].function(data)); 31 | } 32 | /*if there is no match return -1 */ 33 | } 34 | return (-1); 35 | } 36 | -------------------------------------------------------------------------------- /builtins_more.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | /** 4 | * builtin_exit - exit of the program with the status 5 | * @data: struct for the program's data 6 | * Return: zero if sucess, or other number if its declared in the arguments 7 | */ 8 | int builtin_exit(data_of_program *data) 9 | { 10 | int i; 11 | 12 | if (data->tokens[1] != NULL) 13 | {/*if exists arg for exit, check if is a number*/ 14 | for (i = 0; data->tokens[1][i]; i++) 15 | if ((data->tokens[1][i] < '0' || data->tokens[1][i] > '9') 16 | && data->tokens[1][i] != '+') 17 | {/*if is not a number*/ 18 | errno = 2; 19 | return (2); 20 | } 21 | errno = _atoi(data->tokens[1]); 22 | } 23 | free_all_data(data); 24 | exit(errno); 25 | } 26 | 27 | /** 28 | * builtin_cd - change the current directory 29 | * @data: struct for the program's data 30 | * Return: zero if sucess, or other number if its declared in the arguments 31 | */ 32 | int builtin_cd(data_of_program *data) 33 | { 34 | char *dir_home = env_get_key("HOME", data), *dir_old = NULL; 35 | char old_dir[128] = {0}; 36 | int error_code = 0; 37 | 38 | if (data->tokens[1]) 39 | { 40 | if (str_compare(data->tokens[1], "-", 0)) 41 | { 42 | dir_old = env_get_key("OLDPWD", data); 43 | if (dir_old) 44 | error_code = set_work_directory(data, dir_old); 45 | _print(env_get_key("PWD", data)); 46 | _print("\n"); 47 | 48 | return (error_code); 49 | } 50 | else 51 | { 52 | return (set_work_directory(data, data->tokens[1])); 53 | } 54 | } 55 | else 56 | { 57 | if (!dir_home) 58 | dir_home = getcwd(old_dir, 128); 59 | 60 | return (set_work_directory(data, dir_home)); 61 | } 62 | return (0); 63 | } 64 | 65 | /** 66 | * set_work_directory - set the work directory 67 | * @data: struct for the program's data 68 | * @new_dir: path to be set as work directory 69 | * Return: zero if sucess, or other number if its declared in the arguments 70 | */ 71 | int set_work_directory(data_of_program *data, char *new_dir) 72 | { 73 | char old_dir[128] = {0}; 74 | int err_code = 0; 75 | 76 | getcwd(old_dir, 128); 77 | 78 | if (!str_compare(old_dir, new_dir, 0)) 79 | { 80 | err_code = chdir(new_dir); 81 | if (err_code == -1) 82 | { 83 | errno = 2; 84 | return (3); 85 | } 86 | env_set_key("PWD", new_dir, data); 87 | } 88 | env_set_key("OLDPWD", old_dir, data); 89 | return (0); 90 | } 91 | 92 | /** 93 | * builtin_help - shows the environment where the shell runs 94 | * @data: struct for the program's data 95 | * Return: zero if sucess, or other number if its declared in the arguments 96 | */ 97 | int builtin_help(data_of_program *data) 98 | { 99 | int i, length = 0; 100 | char *mensajes[6] = {NULL}; 101 | 102 | mensajes[0] = HELP_MSG; 103 | 104 | /* validate args */ 105 | if (data->tokens[1] == NULL) 106 | { 107 | _print(mensajes[0] + 6); 108 | return (1); 109 | } 110 | if (data->tokens[2] != NULL) 111 | { 112 | errno = E2BIG; 113 | perror(data->command_name); 114 | return (5); 115 | } 116 | mensajes[1] = HELP_EXIT_MSG; 117 | mensajes[2] = HELP_ENV_MSG; 118 | mensajes[3] = HELP_SETENV_MSG; 119 | mensajes[4] = HELP_UNSETENV_MSG; 120 | mensajes[5] = HELP_CD_MSG; 121 | 122 | for (i = 0; mensajes[i]; i++) 123 | { 124 | length = str_length(data->tokens[1]); 125 | if (str_compare(data->tokens[1], mensajes[i], length)) 126 | { 127 | _print(mensajes[i] + length + 1); 128 | return (1); 129 | } 130 | } 131 | /*if there is no match, print error and return -1 */ 132 | errno = EINVAL; 133 | perror(data->command_name); 134 | return (0); 135 | } 136 | 137 | /** 138 | * builtin_alias - add, remove or show aliases 139 | * @data: struct for the program's data 140 | * Return: zero if sucess, or other number if its declared in the arguments 141 | */ 142 | int builtin_alias(data_of_program *data) 143 | { 144 | int i = 0; 145 | 146 | /* if there are no arguments, print all environment */ 147 | if (data->tokens[1] == NULL) 148 | return (print_alias(data, NULL)); 149 | 150 | while (data->tokens[++i]) 151 | {/* if there are arguments, set or print each env variable*/ 152 | if (count_characters(data->tokens[i], "=")) 153 | set_alias(data->tokens[i], data); 154 | else 155 | print_alias(data, data->tokens[i]); 156 | } 157 | 158 | return (0); 159 | } 160 | -------------------------------------------------------------------------------- /env_management.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | /** 4 | * env_get_key - gets the value of an environment variable 5 | * @key: the environment variable of interest 6 | * @data: struct of the program's data 7 | * Return: a pointer to the value of the variable or NULL if it doesn't exist 8 | */ 9 | char *env_get_key(char *key, data_of_program *data) 10 | { 11 | int i, key_length = 0; 12 | 13 | /* validate the arguments */ 14 | if (key == NULL || data->env == NULL) 15 | return (NULL); 16 | 17 | /* obtains the leng of the variable requested */ 18 | key_length = str_length(key); 19 | 20 | for (i = 0; data->env[i]; i++) 21 | {/* Iterates through the environ and check for coincidence of the vame */ 22 | if (str_compare(key, data->env[i], key_length) && 23 | data->env[i][key_length] == '=') 24 | {/* returns the value of the key NAME= when find it*/ 25 | return (data->env[i] + key_length + 1); 26 | } 27 | } 28 | /* returns NULL if did not find it */ 29 | return (NULL); 30 | } 31 | 32 | /** 33 | * env_set_key - overwrite the value of the environment variable 34 | * or create it if does not exist. 35 | * @key: name of the variable to set 36 | * @value: new value 37 | * @data: struct of the program's data 38 | * Return: 1 if the parameters are NULL, 2 if there is an erroror 0 if sucess. 39 | */ 40 | 41 | int env_set_key(char *key, char *value, data_of_program *data) 42 | { 43 | int i, key_length = 0, is_new_key = 1; 44 | 45 | /* validate the arguments */ 46 | if (key == NULL || value == NULL || data->env == NULL) 47 | return (1); 48 | 49 | /* obtains the leng of the variable requested */ 50 | key_length = str_length(key); 51 | 52 | for (i = 0; data->env[i]; i++) 53 | {/* Iterates through the environ and check for coincidence of the vame */ 54 | if (str_compare(key, data->env[i], key_length) && 55 | data->env[i][key_length] == '=') 56 | {/* If key already exists */ 57 | is_new_key = 0; 58 | /* free the entire variable, it is new created below */ 59 | free(data->env[i]); 60 | break; 61 | } 62 | } 63 | /* make an string of the form key=value */ 64 | data->env[i] = str_concat(str_duplicate(key), "="); 65 | data->env[i] = str_concat(data->env[i], value); 66 | 67 | if (is_new_key) 68 | {/* if the variable is new, it is create at end of actual list and we need*/ 69 | /* to put the NULL value in the next position */ 70 | data->env[i + 1] = NULL; 71 | } 72 | return (0); 73 | } 74 | 75 | /** 76 | * env_remove_key - remove a key from the environment 77 | * @key: the key to remove 78 | * @data: the sructure of the program's data 79 | * Return: 1 if the key was removed, 0 if the key does not exist; 80 | */ 81 | int env_remove_key(char *key, data_of_program *data) 82 | { 83 | int i, key_length = 0; 84 | 85 | /* validate the arguments */ 86 | if (key == NULL || data->env == NULL) 87 | return (0); 88 | 89 | /* obtains the leng of the variable requested */ 90 | key_length = str_length(key); 91 | 92 | for (i = 0; data->env[i]; i++) 93 | {/* iterates through the environ and checks for coincidences */ 94 | if (str_compare(key, data->env[i], key_length) && 95 | data->env[i][key_length] == '=') 96 | {/* if key already exists, remove them */ 97 | free(data->env[i]); 98 | 99 | /* move the others keys one position down */ 100 | i++; 101 | for (; data->env[i]; i++) 102 | { 103 | data->env[i - 1] = data->env[i]; 104 | } 105 | /* put the NULL value at the new end of the list */ 106 | data->env[i - 1] = NULL; 107 | return (1); 108 | } 109 | } 110 | return (0); 111 | } 112 | 113 | 114 | /** 115 | * print_environ - prints the current environ 116 | * @data: struct for the program's data 117 | * Return: nothing 118 | */ 119 | void print_environ(data_of_program *data) 120 | { 121 | int j; 122 | 123 | for (j = 0; data->env[j]; j++) 124 | { 125 | _print(data->env[j]); 126 | _print("\n"); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /execute.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | /** 3 | * execute - execute a command with its entire path variables. 4 | * @data: a pointer to the program's data 5 | * Return: If sucess returns zero, otherwise, return -1. 6 | */ 7 | int execute(data_of_program *data) 8 | { 9 | int retval = 0, status; 10 | pid_t pidd; 11 | 12 | /* check for program in built ins */ 13 | retval = builtins_list(data); 14 | if (retval != -1)/* if program was found in built ins */ 15 | return (retval); 16 | 17 | /* check for program file system */ 18 | retval = find_program(data); 19 | if (retval) 20 | {/* if program not found */ 21 | return (retval); 22 | } 23 | else 24 | {/* if program was found */ 25 | pidd = fork(); /* create a child process */ 26 | if (pidd == -1) 27 | { /* if the fork call failed */ 28 | perror(data->command_name); 29 | exit(EXIT_FAILURE); 30 | } 31 | if (pidd == 0) 32 | {/* I am the child process, I execute the program*/ 33 | retval = execve(data->tokens[0], data->tokens, data->env); 34 | if (retval == -1) /* if error when execve*/ 35 | perror(data->command_name), exit(EXIT_FAILURE); 36 | } 37 | else 38 | {/* I am the father, I wait and check the exit status of the child */ 39 | wait(&status); 40 | if (WIFEXITED(status)) 41 | errno = WEXITSTATUS(status); 42 | else if (WIFSIGNALED(status)) 43 | errno = 128 + WTERMSIG(status); 44 | } 45 | } 46 | return (0); 47 | } 48 | -------------------------------------------------------------------------------- /expansions.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | /** 4 | * expand_variables - expand variables 5 | * @data: a pointer to a struct of the program's data 6 | * 7 | * Return: nothing, but sets errno. 8 | */ 9 | void expand_variables(data_of_program *data) 10 | { 11 | int i, j; 12 | char line[BUFFER_SIZE] = {0}, expansion[BUFFER_SIZE] = {'\0'}, *temp; 13 | 14 | if (data->input_line == NULL) 15 | return; 16 | buffer_add(line, data->input_line); 17 | for (i = 0; line[i]; i++) 18 | if (line[i] == '#') 19 | line[i--] = '\0'; 20 | else if (line[i] == '$' && line[i + 1] == '?') 21 | { 22 | line[i] = '\0'; 23 | long_to_string(errno, expansion, 10); 24 | buffer_add(line, expansion); 25 | buffer_add(line, data->input_line + i + 2); 26 | } 27 | else if (line[i] == '$' && line[i + 1] == '$') 28 | { 29 | line[i] = '\0'; 30 | long_to_string(getpid(), expansion, 10); 31 | buffer_add(line, expansion); 32 | buffer_add(line, data->input_line + i + 2); 33 | } 34 | else if (line[i] == '$' && (line[i + 1] == ' ' || line[i + 1] == '\0')) 35 | continue; 36 | else if (line[i] == '$') 37 | { 38 | for (j = 1; line[i + j] && line[i + j] != ' '; j++) 39 | expansion[j - 1] = line[i + j]; 40 | temp = env_get_key(expansion, data); 41 | line[i] = '\0', expansion[0] = '\0'; 42 | buffer_add(expansion, line + i + j); 43 | temp ? buffer_add(line, temp) : 1; 44 | buffer_add(line, expansion); 45 | } 46 | if (!str_compare(data->input_line, line, 0)) 47 | { 48 | free(data->input_line); 49 | data->input_line = str_duplicate(line); 50 | } 51 | } 52 | 53 | /** 54 | * expand_alias - expans aliases 55 | * @data: a pointer to a struct of the program's data 56 | * 57 | * Return: nothing, but sets errno. 58 | */ 59 | void expand_alias(data_of_program *data) 60 | { 61 | int i, j, was_expanded = 0; 62 | char line[BUFFER_SIZE] = {0}, expansion[BUFFER_SIZE] = {'\0'}, *temp; 63 | 64 | if (data->input_line == NULL) 65 | return; 66 | 67 | buffer_add(line, data->input_line); 68 | 69 | for (i = 0; line[i]; i++) 70 | { 71 | for (j = 0; line[i + j] && line[i + j] != ' '; j++) 72 | expansion[j] = line[i + j]; 73 | expansion[j] = '\0'; 74 | 75 | temp = get_alias(data, expansion); 76 | if (temp) 77 | { 78 | expansion[0] = '\0'; 79 | buffer_add(expansion, line + i + j); 80 | line[i] = '\0'; 81 | buffer_add(line, temp); 82 | line[str_length(line)] = '\0'; 83 | buffer_add(line, expansion); 84 | was_expanded = 1; 85 | } 86 | break; 87 | } 88 | if (was_expanded) 89 | { 90 | free(data->input_line); 91 | data->input_line = str_duplicate(line); 92 | } 93 | } 94 | 95 | /** 96 | * buffer_add - append string at end of the buffer 97 | * @buffer: buffer to be filled 98 | * @str_to_add: string to be copied in the buffer 99 | * Return: nothing, but sets errno. 100 | */ 101 | int buffer_add(char *buffer, char *str_to_add) 102 | { 103 | int length, i; 104 | 105 | length = str_length(buffer); 106 | for (i = 0; str_to_add[i]; i++) 107 | { 108 | buffer[length + i] = str_to_add[i]; 109 | } 110 | buffer[length + i] = '\0'; 111 | return (length + i); 112 | } 113 | -------------------------------------------------------------------------------- /find_in_path.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | int check_file(char *full_path); 4 | 5 | /** 6 | * find_program - find a program in path 7 | * @data: a pointer to the program's data 8 | * Return: 0 if success, errcode otherwise 9 | */ 10 | 11 | int find_program(data_of_program *data) 12 | { 13 | int i = 0, ret_code = 0; 14 | char **directories; 15 | 16 | if (!data->command_name) 17 | return (2); 18 | 19 | /**if is a full_path or an executable in the same path */ 20 | if (data->command_name[0] == '/' || data->command_name[0] == '.') 21 | return (check_file(data->command_name)); 22 | 23 | free(data->tokens[0]); 24 | data->tokens[0] = str_concat(str_duplicate("/"), data->command_name); 25 | if (!data->tokens[0]) 26 | return (2); 27 | 28 | directories = tokenize_path(data);/* search in the PATH */ 29 | 30 | if (!directories || !directories[0]) 31 | { 32 | errno = 127; 33 | return (127); 34 | } 35 | for (i = 0; directories[i]; i++) 36 | {/* appends the function_name to path */ 37 | directories[i] = str_concat(directories[i], data->tokens[0]); 38 | ret_code = check_file(directories[i]); 39 | if (ret_code == 0 || ret_code == 126) 40 | {/* the file was found, is not a directory and has execute permisions*/ 41 | errno = 0; 42 | free(data->tokens[0]); 43 | data->tokens[0] = str_duplicate(directories[i]); 44 | free_array_of_pointers(directories); 45 | return (ret_code); 46 | } 47 | } 48 | free(data->tokens[0]); 49 | data->tokens[0] = NULL; 50 | free_array_of_pointers(directories); 51 | return (ret_code); 52 | } 53 | 54 | /** 55 | * tokenize_path - tokenize the path in directories 56 | * @data: a pointer to the program's data 57 | * Return: array of path directories 58 | */ 59 | 60 | char **tokenize_path(data_of_program *data) 61 | { 62 | int i = 0; 63 | int counter_directories = 2; 64 | char **tokens = NULL; 65 | char *PATH; 66 | 67 | /* get the PATH value*/ 68 | PATH = env_get_key("PATH", data); 69 | if ((PATH == NULL) || PATH[0] == '\0') 70 | {/*path not found*/ 71 | return (NULL); 72 | } 73 | 74 | PATH = str_duplicate(PATH); 75 | 76 | /* find the number of directories in the PATH */ 77 | for (i = 0; PATH[i]; i++) 78 | { 79 | if (PATH[i] == ':') 80 | counter_directories++; 81 | } 82 | 83 | /* reserve space for the array of pointers */ 84 | tokens = malloc(sizeof(char *) * counter_directories); 85 | 86 | /*tokenize and duplicate each token of path*/ 87 | i = 0; 88 | tokens[i] = str_duplicate(_strtok(PATH, ":")); 89 | while (tokens[i++]) 90 | { 91 | tokens[i] = str_duplicate(_strtok(NULL, ":")); 92 | } 93 | 94 | free(PATH); 95 | PATH = NULL; 96 | return (tokens); 97 | 98 | } 99 | 100 | /** 101 | * check_file - checks if exists a file, if it is not a dairectory and 102 | * if it has excecution permisions for permisions. 103 | * @full_path: pointer to the full file name 104 | * Return: 0 on success, or error code if it exists. 105 | */ 106 | 107 | int check_file(char *full_path) 108 | { 109 | struct stat sb; 110 | 111 | if (stat(full_path, &sb) != -1) 112 | { 113 | if (S_ISDIR(sb.st_mode) || access(full_path, X_OK)) 114 | { 115 | errno = 126; 116 | return (126); 117 | } 118 | return (0); 119 | } 120 | /*if not exist the file*/ 121 | errno = 127; 122 | return (127); 123 | } 124 | -------------------------------------------------------------------------------- /helper_1.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | /** 4 | * free_recurrent_data - free the fields needed each loop 5 | * @data: struct of the program's data 6 | * Return: Nothing 7 | */ 8 | void free_recurrent_data(data_of_program *data) 9 | { 10 | if (data->tokens) 11 | free_array_of_pointers(data->tokens); 12 | if (data->input_line) 13 | free(data->input_line); 14 | if (data->command_name) 15 | free(data->command_name); 16 | 17 | data->input_line = NULL; 18 | data->command_name = NULL; 19 | data->tokens = NULL; 20 | } 21 | 22 | /** 23 | * free_all_data - free all field of the data 24 | * @data: struct of the program's data 25 | * Return: Nothing 26 | */ 27 | void free_all_data(data_of_program *data) 28 | { 29 | if (data->file_descriptor != 0) 30 | { 31 | if (close(data->file_descriptor)) 32 | perror(data->program_name); 33 | } 34 | free_recurrent_data(data); 35 | free_array_of_pointers(data->env); 36 | free_array_of_pointers(data->alias_list); 37 | } 38 | 39 | /** 40 | * free_array_of_pointers - frees each pointer of an array of pointers and the 41 | * array too 42 | * @array: array of pointers 43 | * Return: nothing 44 | */ 45 | void free_array_of_pointers(char **array) 46 | { 47 | int i; 48 | 49 | if (array != NULL) 50 | { 51 | for (i = 0; array[i]; i++) 52 | free(array[i]); 53 | 54 | free(array); 55 | array = NULL; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /helper_num.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | /** 4 | * long_to_string - converts a number to a string. 5 | * @number: number to be converten in an string. 6 | * @string: buffer to save the number as string. 7 | * @base: base to convert number 8 | * 9 | * Return: Nothing. 10 | */ 11 | void long_to_string(long number, char *string, int base) 12 | { 13 | int index = 0, inNegative = 0; 14 | long cociente = number; 15 | char letters[] = {"0123456789abcdef"}; 16 | 17 | if (cociente == 0) 18 | string[index++] = '0'; 19 | 20 | if (string[0] == '-') 21 | inNegative = 1; 22 | 23 | while (cociente) 24 | { 25 | if (cociente < 0) 26 | string[index++] = letters[-(cociente % base)]; 27 | else 28 | string[index++] = letters[cociente % base]; 29 | cociente /= base; 30 | } 31 | if (inNegative) 32 | string[index++] = '-'; 33 | 34 | string[index] = '\0'; 35 | str_reverse(string); 36 | } 37 | 38 | 39 | /** 40 | * _atoi - convert a string to an integer. 41 | * 42 | * @s: pointer to str origen. 43 | * Return: int of string or 0. 44 | */ 45 | int _atoi(char *s) 46 | { 47 | int sign = 1; 48 | unsigned int number = 0; 49 | /*1- analisys sign*/ 50 | while (!('0' <= *s && *s <= '9') && *s != '\0') 51 | { 52 | if (*s == '-') 53 | sign *= -1; 54 | if (*s == '+') 55 | sign *= +1; 56 | s++; 57 | } 58 | 59 | /*2 - extract the number */ 60 | while ('0' <= *s && *s <= '9' && *s != '\0') 61 | { 62 | 63 | number = (number * 10) + (*s - '0'); 64 | s++; 65 | } 66 | return (number * sign); 67 | } 68 | 69 | /** 70 | * count_characters - count the coincidences of character in string. 71 | * 72 | * @string: pointer to str origen. 73 | * @character: string with chars to be counted 74 | * Return: int of string or 0. 75 | */ 76 | int count_characters(char *string, char *character) 77 | { 78 | int i = 0, counter = 0; 79 | 80 | for (; string[i]; i++) 81 | { 82 | if (string[i] == character[0]) 83 | counter++; 84 | } 85 | return (counter); 86 | } 87 | -------------------------------------------------------------------------------- /helper_p.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | /** 4 | * _print - writes a array of chars in the standar output 5 | * @string: pointer to the array of chars 6 | * Return: the number of bytes writed or . 7 | * On error, -1 is returned, and errno is set appropriately. 8 | */ 9 | int _print(char *string) 10 | { 11 | return (write(STDOUT_FILENO, string, str_length(string))); 12 | } 13 | /** 14 | * _printe - writes a array of chars in the standar error 15 | * @string: pointer to the array of chars 16 | * Return: the number of bytes writed or . 17 | * On error, -1 is returned, and errno is set appropriately. 18 | */ 19 | int _printe(char *string) 20 | { 21 | return (write(STDERR_FILENO, string, str_length(string))); 22 | } 23 | 24 | /** 25 | * _print_error - writes a array of chars in the standart error 26 | * @data: a pointer to the program's data' 27 | * @errorcode: error code to print 28 | * Return: the number of bytes writed or . 29 | * On error, -1 is returned, and errno is set appropriately. 30 | */ 31 | int _print_error(int errorcode, data_of_program *data) 32 | { 33 | char n_as_string[10] = {'\0'}; 34 | 35 | long_to_string((long) data->exec_counter, n_as_string, 10); 36 | 37 | if (errorcode == 2 || errorcode == 3) 38 | { 39 | _printe(data->program_name); 40 | _printe(": "); 41 | _printe(n_as_string); 42 | _printe(": "); 43 | _printe(data->tokens[0]); 44 | if (errorcode == 2) 45 | _printe(": Illegal number: "); 46 | else 47 | _printe(": can't cd to "); 48 | _printe(data->tokens[1]); 49 | _printe("\n"); 50 | } 51 | else if (errorcode == 127) 52 | { 53 | _printe(data->program_name); 54 | _printe(": "); 55 | _printe(n_as_string); 56 | _printe(": "); 57 | _printe(data->command_name); 58 | _printe(": not found\n"); 59 | } 60 | else if (errorcode == 126) 61 | { 62 | _printe(data->program_name); 63 | _printe(": "); 64 | _printe(n_as_string); 65 | _printe(": "); 66 | _printe(data->command_name); 67 | _printe(": Permission denied\n"); 68 | } 69 | return (0); 70 | } 71 | -------------------------------------------------------------------------------- /helper_str.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | /** 4 | * str_length - returns the length of a string. 5 | * @string: pointer to string. 6 | * Return: length of string. 7 | */ 8 | int str_length(char *string) 9 | { 10 | int length = 0; 11 | 12 | if (string == NULL) 13 | return (0); 14 | 15 | while (string[length++] != '\0') 16 | { 17 | } 18 | return (--length); 19 | } 20 | 21 | /** 22 | * str_duplicate - duplicates an string 23 | * @string: String to be copied 24 | * Return: pointer to the array 25 | */ 26 | char *str_duplicate(char *string) 27 | { 28 | char *result; 29 | int length, i; 30 | 31 | if (string == NULL) 32 | return (NULL); 33 | 34 | length = str_length(string) + 1; 35 | 36 | result = malloc(sizeof(char) * length); 37 | 38 | if (result == NULL) 39 | { 40 | errno = ENOMEM; 41 | perror("Error"); 42 | return (NULL); 43 | } 44 | for (i = 0; i < length ; i++) 45 | { 46 | result[i] = string[i]; 47 | } 48 | 49 | return (result); 50 | } 51 | 52 | /** 53 | * str_compare - Compare two strings 54 | * @string1: String one, or the shorter 55 | * @string2: String two, or the longer 56 | * @number: number of characters to be compared, 0 if infinite 57 | * Return: 1 if the strings are equals,0 if the strings are different 58 | */ 59 | int str_compare(char *string1, char *string2, int number) 60 | { 61 | int iterator; 62 | 63 | if (string1 == NULL && string2 == NULL) 64 | return (1); 65 | 66 | if (string1 == NULL || string2 == NULL) 67 | return (0); 68 | 69 | if (number == 0) /* infinite longitud */ 70 | { 71 | if (str_length(string1) != str_length(string2)) 72 | return (0); 73 | for (iterator = 0; string1[iterator]; iterator++) 74 | { 75 | if (string1[iterator] != string2[iterator]) 76 | return (0); 77 | } 78 | return (1); 79 | } 80 | else /* if there is a number of chars to be compared */ 81 | { 82 | for (iterator = 0; iterator < number ; iterator++) 83 | { 84 | if (string1[iterator] != string2[iterator]) 85 | return (0); 86 | } 87 | return (1); 88 | } 89 | } 90 | 91 | /** 92 | * str_concat - concatenates two strings. 93 | * @string1: String to be concatenated 94 | * @string2: String to be concatenated 95 | * 96 | * Return: pointer to the array 97 | */ 98 | char *str_concat(char *string1, char *string2) 99 | { 100 | char *result; 101 | int length1 = 0, length2 = 0; 102 | 103 | if (string1 == NULL) 104 | string1 = ""; 105 | length1 = str_length(string1); 106 | 107 | if (string2 == NULL) 108 | string2 = ""; 109 | length2 = str_length(string2); 110 | 111 | result = malloc(sizeof(char) * (length1 + length2 + 1)); 112 | if (result == NULL) 113 | { 114 | errno = ENOMEM; 115 | perror("Error"); 116 | return (NULL); 117 | } 118 | 119 | /* copy of string1 */ 120 | for (length1 = 0; string1[length1] != '\0'; length1++) 121 | result[length1] = string1[length1]; 122 | free(string1); 123 | 124 | /* copy of string2 */ 125 | for (length2 = 0; string2[length2] != '\0'; length2++) 126 | { 127 | result[length1] = string2[length2]; 128 | length1++; 129 | } 130 | 131 | result[length1] = '\0'; 132 | return (result); 133 | } 134 | 135 | 136 | /** 137 | * str_reverse - reverses a string. 138 | * 139 | * @string: pointer to string. 140 | * Return: void. 141 | */ 142 | void str_reverse(char *string) 143 | { 144 | 145 | int i = 0, length = str_length(string) - 1; 146 | char hold; 147 | 148 | while (i < length) 149 | { 150 | hold = string[i]; 151 | string[i++] = string[length]; 152 | string[length--] = hold; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /macros.h: -------------------------------------------------------------------------------- 1 | #ifndef HELPERS_H 2 | #define HELPERS_H 3 | 4 | /* Prompt to be printed */ 5 | #define PROMPT_MSG "$" /* Needed to work with signal */ 6 | 7 | /* Resume from the unused attibute */ 8 | #define UNUSED __attribute__((unused)) 9 | 10 | /* buffer size for each read call in _getline */ 11 | #define BUFFER_SIZE 1024 12 | 13 | /************* FORMATTED STRING FOR HELP BUILT IN **************/ 14 | 15 | #define HELP_CD_MSG "cd=\n"\ 16 | "cd:\tcd [dir]\n\n"\ 17 | " Change the shell working directory.\n\n"\ 18 | " If no argument is given to cd the command will be interpreted\n"\ 19 | " as cd $HOME.\n"\ 20 | " if the argumenthelp is - (cd -), the command will be interpreted\n"\ 21 | " as cd $OLDPWD.\n\n" 22 | 23 | #define HELP_EXIT_MSG "exit=\n"\ 24 | "exit:\texit [STATUS]\n\n"\ 25 | " Exit of the simple-shell.\n\n"\ 26 | " Exits the shell with a status of N. If N is omitted, the exit status\n"\ 27 | " is that of the last command executed.\n\n" 28 | 29 | #define HELP_ENV_MSG "env=\n"\ 30 | "env:\tenv \n\n"\ 31 | " Print environment.\n\n"\ 32 | " The env command will be print a complete list of enviroment variables.\n\n" 33 | 34 | #define HELP_SETENV_MSG "setenv=\n"\ 35 | "setenv:\tsetenv VARIABLE VALUE\n\n"\ 36 | " Change or add an environment variable.\n\n"\ 37 | " initialize a new environment variable, or modify an existing one\n"\ 38 | " When there are not correct numbers of arguments print error message.\n\n" 39 | 40 | #define HELP_UNSETENV_MSG "unsetenv=\n"\ 41 | "unsetenv:\tunsetenv VARIABLE\n\n"\ 42 | " The unsetenv function deletes one variable from the environment.\n\n"\ 43 | " Wen there are not correct numbers of arguments print error message.\n\n" 44 | 45 | #define HELP_MSG "help=\n"\ 46 | "help:\thelp [BUILTIN_NAME]\n\n"\ 47 | " Display information about builtin commands.\n\n"\ 48 | " Displays brief summaries of builtin commands. If BUILTIN_NAME is\n"\ 49 | " specified, gives detailed help on all commands matching BUILTIN_NAME,\n"\ 50 | " otherwise the list of help topics is printed BUILTIN_NAME list.\n"\ 51 | " Arguments:\n\n"\ 52 | " BUILTIN_NAME specifiying a help topic.\n\n"\ 53 | " cd\t[dir]\n"\ 54 | " exit\t[status]\n"\ 55 | " env\n"\ 56 | " setenv\t[variable value]\n"\ 57 | " unset\t[variable]\n"\ 58 | " help\t[built_name]\n\n" 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | /** 3 | * main - initialize the variables of the program 4 | * @argc: number of values received from the command line 5 | * @argv: values received from the command line 6 | * @env: number of values received from the command line 7 | * Return: zero on succes. 8 | */ 9 | int main(int argc, char *argv[], char *env[]) 10 | { 11 | data_of_program data_struct = {NULL}, *data = &data_struct; 12 | char *prompt = ""; 13 | 14 | inicialize_data(data, argc, argv, env); 15 | 16 | signal(SIGINT, handle_ctrl_c); 17 | 18 | if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO) && argc == 1) 19 | {/* We are in the terminal, interactive mode */ 20 | errno = 2;/*???????*/ 21 | prompt = PROMPT_MSG; 22 | } 23 | errno = 0; 24 | sisifo(prompt, data); 25 | return (0); 26 | } 27 | 28 | /** 29 | * handle_ctrl_c - print the prompt in a new line 30 | * when the signal SIGINT (ctrl + c) is send to the program 31 | * @UNUSED: option of the prototype 32 | */ 33 | void handle_ctrl_c(int opr UNUSED) 34 | { 35 | _print("\n"); 36 | _print(PROMPT_MSG); 37 | } 38 | 39 | /** 40 | * inicialize_data - inicialize the struct with the info of the program 41 | * @data: pointer to the structure of data 42 | * @argv: array of arguments pased to the program execution 43 | * @env: environ pased to the program execution 44 | * @argc: number of values received from the command line 45 | */ 46 | void inicialize_data(data_of_program *data, int argc, char *argv[], char **env) 47 | { 48 | int i = 0; 49 | 50 | data->program_name = argv[0]; 51 | data->input_line = NULL; 52 | data->command_name = NULL; 53 | data->exec_counter = 0; 54 | /* define the file descriptor to be readed*/ 55 | if (argc == 1) 56 | data->file_descriptor = STDIN_FILENO; 57 | else 58 | { 59 | data->file_descriptor = open(argv[1], O_RDONLY); 60 | if (data->file_descriptor == -1) 61 | { 62 | _printe(data->program_name); 63 | _printe(": 0: Can't open "); 64 | _printe(argv[1]); 65 | _printe("\n"); 66 | exit(127); 67 | } 68 | } 69 | data->tokens = NULL; 70 | data->env = malloc(sizeof(char *) * 50); 71 | if (env) 72 | { 73 | for (; env[i]; i++) 74 | { 75 | data->env[i] = str_duplicate(env[i]); 76 | } 77 | } 78 | data->env[i] = NULL; 79 | env = data->env; 80 | 81 | data->alias_list = malloc(sizeof(char *) * 20); 82 | for (i = 0; i < 20; i++) 83 | { 84 | data->alias_list[i] = NULL; 85 | } 86 | } 87 | /** 88 | * sisifo - its a infinite loop that shows the prompt 89 | * @prompt: prompt to be printed 90 | * @data: its a infinite loop that shows the prompt 91 | */ 92 | void sisifo(char *prompt, data_of_program *data) 93 | { 94 | int error_code = 0, string_len = 0; 95 | 96 | while (++(data->exec_counter)) 97 | { 98 | _print(prompt); 99 | error_code = string_len = _getline(data); 100 | 101 | if (error_code == EOF) 102 | { 103 | free_all_data(data); 104 | exit(errno); /* if EOF is the fisrt Char of string, exit*/ 105 | } 106 | if (string_len >= 1) 107 | { 108 | expand_alias(data); 109 | expand_variables(data); 110 | tokenize(data); 111 | if (data->tokens[0]) 112 | { /* if a text is given to prompt, execute */ 113 | error_code = execute(data); 114 | if (error_code != 0) 115 | _print_error(error_code, data); 116 | } 117 | free_recurrent_data(data); 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /man_1_simple_shell: -------------------------------------------------------------------------------- 1 | .TH SHELLBY 1 "August 2022" "ALX School" "0x16. C - Simple Shell" 2 | .SH NAME 3 | .B shellby\fR \- simple UNIX command interpreter 4 | .SH SYNOPSIS 5 | .B shellby\fR [\fIfilename\fR] 6 | .SH DESCRIPTION 7 | .B Shellby\fR is a simple UNIX command language interpreter that reads commands from either a file or standard input and executes them. 8 | 9 | .B Invocation 10 | .in +2n 11 | \fBShellby\fR can be invoked both interactively and non-interactively. 12 | If \fBshellby\fR is invoked with standard input not connected to a terminal, it reads and executes received commands in order. 13 | 14 | If \fBshellby\fR is invoked with standard input connected to a terminal (determined by \fIisatty(3)\fR), an \fIinteractive\fR shell is opened. 15 | When executing interactively, \fBshellby\fR displays the prompt \fI$ \fR when it is ready to read a command. 16 | 17 | Alternatively, if command line arguments are supplied upon invocation, \fBshellby\fR treats the first argument as a file from which to read commands. 18 | The supplied file should contain one command per line. 19 | .B Shellby\fR runs each of the commands contained in the file in order before exiting. 20 | .in 21 | 22 | .B Environment 23 | .in +2n 24 | Upon invocation, \fBshellby\fR receives and copies the environment of the parent process in which it was exeucted. 25 | This \fBenvironment\fR is an array of \fIname-value\fR strings describing variables in the format \fINAME=VALUE\fR. 26 | .in 27 | 28 | .B Command Execution 29 | .in +2n 30 | After receiving a command, \fBshellby\fR tokenizes it into words using \fB" "\fR as a delimiter. 31 | The first word is considered the command and all remaining words are considered arguments to that command. 32 | .B Shellby\fR then proceeds with the following actions: 33 | 34 | .RS 35 | 1. If the first character of the command is neither a slash (\fI\\\fR) nor dot (\fI.\fR), the shell searches for it in the list of shell builtins. 36 | If there exists a shell builtin by that name, the builtin is invoked. 37 | .RE 38 | 39 | .RS 40 | 2. If the first character of the command is none of a slash (\fI\\\fR), dot (\fI.\fR), nor builtin, \fBshellby\fR searches each element of the 41 | \fBPATH\fR environmental variable for a directory containing an executable file by that name. 42 | .RE 43 | 44 | .RS 45 | 3. If the first character of the command is a slash (\fI\\\fR) or dot (\fI.\fR) or either of the above searches was successful, 46 | the shell executes the named program with any remaining arguments given in a separate execution environment. 47 | .RE 48 | 49 | .B Exit Status 50 | .in +2n 51 | .B Shellby\fR returns the exit status of the last command executed, unless a syntax error occurs, with zero indicating success and non-zero indicating failure. 52 | 53 | If a command is not found, the return status is 127; if a command is found but is not executable, the return status is 126. 54 | 55 | All builtins return zero on success and one or two on incorrect usage (indicated by a corresponding error message). 56 | .in 57 | 58 | .B Signals 59 | .in +2n 60 | While running in interactive mode, \fBshellby\fR ignores the keyboard input \fBCtrl+c\fR. 61 | Alternatively, an input of end-of-file (\fBCtrl+d\fR) will exit the program. 62 | .in 63 | 64 | .B Variable Replacement 65 | .in +2n 66 | .B Shellby\fR inerprets the \fI$\fR character for variable replacement: 67 | 68 | .RS 69 | .B $ENV_VARIABLE\fR \fIENV_VARIABLE\fR is subsituted with its value. 70 | .RE 71 | 72 | .RS 73 | .B $?\fR \fI?\fR is substituted with the return value of the last program executed. 74 | .RE 75 | 76 | .RS 77 | .B $$\FR The second \fI$\fR is substituted with the current process ID. 78 | .RE 79 | 80 | .B Comments 81 | .in +2n 82 | .B Shellby\fR ignores all words and characters preceeded by a \fI#\fR character on a line. 83 | .in 84 | 85 | .B Operators 86 | .in +2n 87 | 88 | .RS 89 | .B ;\fR - Command separator 90 | .RS 91 | Commands separated by a \fI;\fR are executed sequentially. 92 | .RE 93 | 94 | .B &&\fR - AND logical operator 95 | .RS 96 | .I command1\fR && \fIcommand2\fR: \fIcommand2\fR is executed if, and only if, \fIcommand1\fR returns an exit status of zero. 97 | .RE 98 | 99 | .B ||\fR - OR logical operator 100 | .RS 101 | .I command1\fR || \fIcommand2\fR: \fIcommand2\fR is executed if, and only if, \fIcommand1\fR returns a non-zero exit status. 102 | .RE 103 | 104 | The operators \fI&&\fR and \fI||\fR have equal precedence, followed by \fI;\fR. 105 | .RE 106 | 107 | .B Shellby Builtin Commands 108 | .in +2n 109 | 110 | .RS 111 | .B cd 112 | .RS 113 | Usage: .B cd [DIRECTORY] 114 | 115 | Changes the current directory of the process to \fBDIRECTORY\fR. 116 | 117 | If no argument is given, the command is interpreted as \fBcd $HOME\fR. 118 | 119 | If the argument \fB-\fR is given, the command is interpreted as \fBcd $OLDPWD\fR. 120 | 121 | The environment variables \fBPWD\fR and \fBOLDPWD\fR are updated after a change of directory. 122 | .RE 123 | 124 | .B alias 125 | .RS 126 | Usage: \fBalias [NAME[='VALUE'] ...]\fR 127 | 128 | Handles aliases. 129 | 130 | .I alias\fR: Prints a list of all aliases, one per line, in the form \fINAME='VALUE'\fR. 131 | 132 | .I alias NAME [NAME2 ...]\fR: Prints the aliases \fINAME\fR, \fINAME2\fR, etc. one per line, in the form \fINAME='VALUE'\fR. 133 | 134 | .I alias NAME='VALUE' [...]\fR: Defines an alias for each \fINAME\fR whose \fIVALUE\fR is given. 135 | If \fIname\fR is already an alias, its value is replaced with \fIVALUE\fR. 136 | .RE 137 | 138 | .B exit 139 | .RS 140 | Usage: \fBexit [STATUS]\fR 141 | 142 | Exits the shell. 143 | 144 | The \fBSTATUS\fR argument is the integer used to exit the shell. 145 | 146 | If no argument is given, the command is interpreted as \fBexit 0\fR. 147 | .RE 148 | 149 | .B env 150 | .RS 151 | Usage: \fBenv\fR 152 | 153 | Prints the current environment. 154 | .RE 155 | 156 | .B setenv 157 | .RS 158 | Usage: \fBsetenv [VARIABLE] [VALUE]\fR 159 | 160 | Initializes a new environment variable, or modifies an existing one. 161 | 162 | Upon failure, prints a message to \fBstderr\fR 163 | .RE 164 | 165 | .B unsetenv 166 | .RS 167 | Usage: \fBunsetenv [VARIABLE]\fR 168 | 169 | Removes an environmental variable. 170 | 171 | Upon failure, prints a message to \fBstderr\fR. 172 | .RE 173 | .RE 174 | .in 175 | 176 | .SH SEE ALSO 177 | access(2), chdir(2), execve(2), _exit(2), exit(3), fflush(3), fork(2), free(3), isatty(3), getcwd(3), malloc(3), open(2), read(2), sh(1), signal(2), stat(2), wait(2), write(2) 178 | 179 | .B Shellby\fR emulates basic functionality of the \fBsh\fR shell. 180 | This man page borrows from the corresponding man pages sh(1) and dash(1). 181 | -------------------------------------------------------------------------------- /shell.h: -------------------------------------------------------------------------------- 1 | #ifndef SHELL_H 2 | #define SHELL_H 3 | 4 | #include /* for printf*/ 5 | #include /* for fork, execve*/ 6 | #include 7 | #include /* for strtok*/ 8 | #include 9 | #include /* for errno and perror */ 10 | #include /* for type pid */ 11 | #include /* for wait */ 12 | #include /* for use of stat function */ 13 | #include /* for signal management */ 14 | #include /* for open files*/ 15 | 16 | /************* MACROS **************/ 17 | 18 | #include "macros.h" /* for msg help and prompt */ 19 | 20 | /************* STRUCTURES **************/ 21 | 22 | /** 23 | * struct info- struct for the program's data 24 | * @program_name: the name of the executable 25 | * @input_line: pointer to the input read for _getline 26 | * @command_name: pointer to the first command typed by the user 27 | * @exec_counter: number of excecuted comands 28 | * @file_descriptor: file descriptor to the input of commands 29 | * @tokens: pointer to array of tokenized input 30 | * @env: copy of the environ 31 | * @alias_list: array of pointers with aliases. 32 | */ 33 | typedef struct info 34 | { 35 | char *program_name; 36 | char *input_line; 37 | char *command_name; 38 | int exec_counter; 39 | int file_descriptor; 40 | char **tokens; 41 | char **env; 42 | char **alias_list; 43 | } data_of_program; 44 | 45 | /** 46 | * struct builtins - struct for the builtins 47 | * @builtin: the name of the builtin 48 | * @function: the associated function to be called for each builtin 49 | */ 50 | typedef struct builtins 51 | { 52 | char *builtin; 53 | int (*function)(data_of_program *data); 54 | } builtins; 55 | 56 | 57 | /************* MAIN FUNCTIONS *************/ 58 | 59 | 60 | /*======== shell.c ========*/ 61 | 62 | /* Inicialize the struct with the info of the program */ 63 | void inicialize_data(data_of_program *data, int arc, char *argv[], char **env); 64 | 65 | /* Makes the infinite loop that shows the prompt*/ 66 | void sisifo(char *prompt, data_of_program *data); 67 | 68 | /* Print the prompt in a new line */ 69 | void handle_ctrl_c(int opr UNUSED); 70 | 71 | 72 | /*======== _getline.c ========*/ 73 | 74 | /* Read one line of the standar input*/ 75 | int _getline(data_of_program *data); 76 | 77 | /* split the each line for the logical operators if it exist */ 78 | int check_logic_ops(char *array_commands[], int i, char array_operators[]); 79 | 80 | 81 | /*======== expansions.c ========*/ 82 | 83 | /* expand variables */ 84 | void expand_variables(data_of_program *data); 85 | 86 | /* expand aliases */ 87 | void expand_alias(data_of_program *data); 88 | 89 | /* append the string to the end of the buffer*/ 90 | int buffer_add(char *buffer, char *str_to_add); 91 | 92 | 93 | /*======== str_tok.c ========*/ 94 | 95 | /* Separate the string in tokens using a designed delimiter */ 96 | void tokenize(data_of_program *data); 97 | 98 | /* Creates a pointer to a part of a string */ 99 | char *_strtok(char *line, char *delim); 100 | 101 | 102 | /*======== execute.c ========*/ 103 | 104 | /* Execute a command with its entire path */ 105 | int execute(data_of_program *data); 106 | 107 | 108 | /*======== builtins_list.c ========*/ 109 | 110 | /* If match a builtin, executes it */ 111 | int builtins_list(data_of_program *data); 112 | 113 | 114 | /*======== find_in_path.c ========*/ 115 | 116 | /* Creates an array of the path directories */ 117 | char **tokenize_path(data_of_program *data); 118 | 119 | /* Search for program in path */ 120 | int find_program(data_of_program *data); 121 | 122 | 123 | /************** HELPERS FOR MEMORY MANAGEMENT **************/ 124 | 125 | 126 | /*======== helpers_free.c ========*/ 127 | 128 | /* Frees the memory for directories */ 129 | void free_array_of_pointers(char **directories); 130 | 131 | /* Free the fields needed each loop */ 132 | void free_recurrent_data(data_of_program *data); 133 | 134 | /* Free all field of the data */ 135 | void free_all_data(data_of_program *data); 136 | 137 | 138 | /************** BUILTINS **************/ 139 | 140 | 141 | /*======== builtins_more.c ========*/ 142 | 143 | /* Close the shell */ 144 | int builtin_exit(data_of_program *data); 145 | 146 | /* Change the current directory */ 147 | int builtin_cd(data_of_program *data); 148 | 149 | /* set the work directory */ 150 | int set_work_directory(data_of_program *data, char *new_dir); 151 | 152 | /* show help information */ 153 | int builtin_help(data_of_program *data); 154 | 155 | /* set, unset and show alias */ 156 | int builtin_alias(data_of_program *data); 157 | 158 | 159 | /*======== builtins_env.c ========*/ 160 | 161 | /* Shows the environment where the shell runs */ 162 | int builtin_env(data_of_program *data); 163 | 164 | /* create or override a variable of environment */ 165 | int builtin_set_env(data_of_program *data); 166 | 167 | /* delete a variable of environment */ 168 | int builtin_unset_env(data_of_program *data); 169 | 170 | 171 | /************** HELPERS FOR ENVIRONMENT VARIABLES MANAGEMENT **************/ 172 | 173 | 174 | /*======== env_management.c ========*/ 175 | 176 | /* Gets the value of an environment variable */ 177 | char *env_get_key(char *name, data_of_program *data); 178 | 179 | /* Overwrite the value of the environment variable */ 180 | int env_set_key(char *key, char *value, data_of_program *data); 181 | 182 | /* Remove a key from the environment */ 183 | int env_remove_key(char *key, data_of_program *data); 184 | 185 | /* prints the current environ */ 186 | void print_environ(data_of_program *data); 187 | 188 | 189 | /************** HELPERS FOR PRINTING **************/ 190 | 191 | 192 | /*======== helpers_print.c ========*/ 193 | 194 | /* Prints a string in the standar output */ 195 | int _print(char *string); 196 | 197 | /* Prints a string in the standar error */ 198 | int _printe(char *string); 199 | 200 | /* Prints a string in the standar error */ 201 | int _print_error(int errorcode, data_of_program *data); 202 | 203 | 204 | /************** HELPERS FOR STRINGS MANAGEMENT **************/ 205 | 206 | 207 | /*======== helpers_string.c ========*/ 208 | 209 | /* Counts the number of characters of a string */ 210 | int str_length(char *string); 211 | 212 | /* Duplicates an string */ 213 | char *str_duplicate(char *string); 214 | 215 | /* Compares two strings */ 216 | int str_compare(char *string1, char *string2, int number); 217 | 218 | /* Concatenates two strings */ 219 | char *str_concat(char *string1, char *string2); 220 | 221 | /* Reverse a string */ 222 | void str_reverse(char *string); 223 | 224 | 225 | /*======== helpers_numbers.c ========*/ 226 | 227 | /* Cast from int to string */ 228 | void long_to_string(long number, char *string, int base); 229 | 230 | /* convert an string in to a number */ 231 | int _atoi(char *s); 232 | 233 | /* count the coincidences of character in string */ 234 | int count_characters(char *string, char *character); 235 | 236 | 237 | /*======== alias_management.c ========*/ 238 | 239 | /* print the list of alias */ 240 | int print_alias(data_of_program *data, char *alias); 241 | 242 | /* get the alias name */ 243 | char *get_alias(data_of_program *data, char *alias); 244 | 245 | /* set the alias name */ 246 | int set_alias(char *alias_string, data_of_program *data); 247 | 248 | 249 | #endif /* SHELL_H */ 250 | -------------------------------------------------------------------------------- /str_token.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | /** 3 | * _strtok - separates strings with delimiters 4 | * @line: It´s pointer to array we receive in getline. 5 | * @delim: It´s characters we mark off string in parts. 6 | * Return: A pointer to the created token 7 | */ 8 | char *_strtok(char *line, char *delim) 9 | { 10 | int j; 11 | static char *str; 12 | char *copystr; 13 | 14 | if (line != NULL) 15 | str = line; 16 | for (; *str != '\0'; str++) 17 | { 18 | for (j = 0; delim[j] != '\0'; j++) 19 | { 20 | if (*str == delim[j]) 21 | break; 22 | } 23 | if (delim[j] == '\0') 24 | break; 25 | } 26 | copystr = str; 27 | if (*copystr == '\0') 28 | return (NULL); 29 | for (; *str != '\0'; str++) 30 | { 31 | for (j = 0; delim[j] != '\0'; j++) 32 | { 33 | if (*str == delim[j]) 34 | { 35 | *str = '\0'; 36 | str++; 37 | return (copystr); 38 | } 39 | } 40 | } 41 | return (copystr); 42 | } 43 | -------------------------------------------------------------------------------- /tokenize.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | /** 3 | * tokenize - this function separate the string using a designed delimiter 4 | * @data: a pointer to the program's data 5 | * Return: an array of the different parts of the string 6 | */ 7 | void tokenize(data_of_program *data) 8 | { 9 | char *delimiter = " \t"; 10 | int i, j, counter = 2, length; 11 | 12 | length = str_length(data->input_line); 13 | if (length) 14 | { 15 | if (data->input_line[length - 1] == '\n') 16 | data->input_line[length - 1] = '\0'; 17 | } 18 | 19 | for (i = 0; data->input_line[i]; i++) 20 | { 21 | for (j = 0; delimiter[j]; j++) 22 | { 23 | if (data->input_line[i] == delimiter[j]) 24 | counter++; 25 | } 26 | } 27 | 28 | data->tokens = malloc(counter * sizeof(char *)); 29 | if (data->tokens == NULL) 30 | { 31 | perror(data->program_name); 32 | exit(errno); 33 | } 34 | i = 0; 35 | data->tokens[i] = str_duplicate(_strtok(data->input_line, delimiter)); 36 | data->command_name = str_duplicate(data->tokens[0]); 37 | while (data->tokens[i++]) 38 | { 39 | data->tokens[i] = str_duplicate(_strtok(NULL, delimiter)); 40 | } 41 | } 42 | --------------------------------------------------------------------------------