main.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. // @file main.c ~ main source file for in
  2. // @author Original: Stephen Brennan ~ Maintained and customized
  3. // my Chris Dorman, 2020+ LGPLv2
  4. // @desc CDSH, Chris Dorman's SHell! Simplistic, but functional
  5. // posix unix shell.
  6. #include "main.h"
  7. #include "parse.h"
  8. /*
  9. List of builtin commands, followed by their corresponding functions.
  10. */
  11. char *builtin_str[] = {
  12. "cd",
  13. "help",
  14. "exit"
  15. };
  16. int (*builtin_func[]) (char **) = {
  17. &cdsh_cd,
  18. &cdsh_help,
  19. &cdsh_exit
  20. };
  21. int cdsh_num_builtins() {
  22. return sizeof(builtin_str) / sizeof(char *);
  23. }
  24. /*
  25. Builtin function implementations.
  26. */
  27. /*
  28. @brief Bultin command: change directory.
  29. @param args List of args. args[0] is "cd". args[1] is the directory.
  30. @return Always returns 1, to continue executing.
  31. */
  32. int cdsh_cd(char **args)
  33. {
  34. if (args[1] == NULL) {
  35. fprintf(stderr, "cdsh: expected argument to \"cd\"\n");
  36. } else {
  37. if (chdir(args[1]) != 0) {
  38. perror("cdsh");
  39. }
  40. }
  41. return 1;
  42. }
  43. /*
  44. @brief Builtin command: print help.
  45. @param args List of args. Not examined.
  46. @return Always returns 1, to continue executing.
  47. */
  48. int cdsh_help(char **args)
  49. {
  50. int i;
  51. printf("Chris Dorman's SHell\n");
  52. printf("Type program names and arguments, and hit enter.\n");
  53. printf("The following are built in:\n");
  54. for (i = 0; i < cdsh_num_builtins(); i++) {
  55. printf("=> %s\n", builtin_str[i]);
  56. }
  57. printf("Use the man command for information on other programs.\n");
  58. return 1;
  59. }
  60. /*
  61. @brief Builtin command: exit.
  62. @param args List of args. Not examined.
  63. @return Always returns 0, to terminate execution.
  64. */
  65. int cdsh_exit(char **args)
  66. {
  67. return(0);
  68. }
  69. /*
  70. @brief Launch a program and wait for it to terminate.
  71. @param args Null terminated list of arguments (including program).
  72. @return Always returns 1, to continue execution.
  73. */
  74. int cdsh_launch(char **args)
  75. {
  76. pid_t pid;
  77. int status;
  78. pid = fork();
  79. if (pid == 0) {
  80. // Child process
  81. if (execvp(args[0], args) == -1) {
  82. perror("cdsh");
  83. }
  84. exit(EXIT_FAILURE);
  85. } else if (pid < 0) {
  86. // Error forking
  87. perror("cdsh");
  88. } else {
  89. // Parent process
  90. do {
  91. waitpid(pid, &status, WUNTRACED);
  92. } while (!WIFEXITED(status) && !WIFSIGNALED(status));
  93. }
  94. return 1;
  95. }
  96. /*
  97. @brief Execute shell built-in or launch program.
  98. @param args Null terminated list of arguments.
  99. @return 1 if the shell should continue running, 0 if it should terminate
  100. */
  101. int cdsh_execute(char **args)
  102. {
  103. int i;
  104. if (args[0] == NULL) {
  105. // An empty command was entered.
  106. return 1;
  107. }
  108. for (i = 0; i < cdsh_num_builtins(); i++) {
  109. if (strcmp(args[0], builtin_str[i]) == 0) {
  110. return (*builtin_func[i])(args);
  111. }
  112. }
  113. return cdsh_launch(args);
  114. }
  115. /*
  116. @brief Read a line of input from stdin.
  117. @return The line from stdin.
  118. */
  119. char *cdsh_read_line(void)
  120. {
  121. #ifdef CDSH_USE_STD_GETLINE
  122. char *line = NULL;
  123. ssize_t bufsize = 0; // have getline allocate a buffer for us
  124. if (getline(&line, &bufsize, stdin) == -1) {
  125. if (feof(stdin)) {
  126. exit(EXIT_SUCCESS); // We recieved an EOF
  127. } else {
  128. perror("cdsh: getline\n");
  129. exit(EXIT_FAILURE);
  130. }
  131. }
  132. return line;
  133. #else
  134. #define CDSH_RL_BUFSIZE 1024
  135. int bufsize = CDSH_RL_BUFSIZE;
  136. int position = 0;
  137. char *buffer = malloc(sizeof(char) * bufsize);
  138. int c;
  139. if (!buffer) {
  140. fprintf(stderr, "cdsh: allocation error\n");
  141. exit(EXIT_FAILURE);
  142. }
  143. while (1) {
  144. // Read a character
  145. c = getchar();
  146. if (c == EOF) {
  147. exit(EXIT_SUCCESS);
  148. } else if (c == '\n') {
  149. buffer[position] = '\0';
  150. return buffer;
  151. } else {
  152. buffer[position] = c;
  153. }
  154. position++;
  155. // If we have exceeded the buffer, reallocate.
  156. if (position >= bufsize) {
  157. bufsize += CDSH_RL_BUFSIZE;
  158. buffer = realloc(buffer, bufsize);
  159. if (!buffer) {
  160. fprintf(stderr, "cdsh: allocation error\n");
  161. exit(EXIT_FAILURE);
  162. }
  163. }
  164. }
  165. #endif
  166. }
  167. #define CDSH_TOK_BUFSIZE 64
  168. #define CDSH_TOK_DELIM " \t\r\n\a"
  169. /*
  170. @brief Split a line into tokens (very naively).
  171. @param line The line.
  172. @return Null-terminated array of tokens.
  173. */
  174. char **cdsh_split_line(char *line)
  175. {
  176. int bufsize = CDSH_TOK_BUFSIZE, position = 0;
  177. char **tokens = malloc(bufsize * sizeof(char*));
  178. char *token, **tokens_backup;
  179. if (!tokens) {
  180. fprintf(stderr, "cdsh: allocation error\n");
  181. exit(EXIT_FAILURE);
  182. }
  183. token = strtok(line, CDSH_TOK_DELIM);
  184. while (token != NULL) {
  185. tokens[position] = token;
  186. position++;
  187. if (position >= bufsize) {
  188. bufsize += CDSH_TOK_BUFSIZE;
  189. tokens_backup = tokens;
  190. tokens = realloc(tokens, bufsize * sizeof(char*));
  191. if (!tokens) {
  192. free(tokens_backup);
  193. fprintf(stderr, "cdsh: allocation error\n");
  194. exit(EXIT_FAILURE);
  195. }
  196. }
  197. token = strtok(NULL, CDSH_TOK_DELIM);
  198. }
  199. tokens[position] = NULL;
  200. return tokens;
  201. }
  202. /*
  203. @brief Loop getting input and executing it.
  204. */
  205. void cdsh_loop(void)
  206. {
  207. char *line;
  208. char **args;
  209. int status;
  210. do {
  211. printf("$ ");
  212. line = cdsh_read_line();
  213. args = cdsh_split_line(line);
  214. status = cdsh_execute(args);
  215. free(line);
  216. free(args);
  217. } while (status);
  218. }
  219. /**
  220. @brief Main entry point.
  221. @param argc Argument count.
  222. @param argv Argument vector.
  223. @return status code
  224. */
  225. int main(int argc, char **argv)
  226. {
  227. // Load config files, if any.
  228. if (argc == 1) {
  229. // If cdsh is called by itself, just pull prompt up
  230. cdsh_loop();
  231. } else if(argc > 2 || !strcmp(argv[1], "-?") || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
  232. printf("usage: cdsh [file location]\n"
  233. "Example: cdsh /path/to/script.sh \n");
  234. fflush(stdout);
  235. cdsh_exit(0); // exit
  236. } else if (argc == 2 && file_exists(argv[1])==0) {
  237. parse(argv[1]);
  238. }
  239. // Perform any shutdown/cleanup.
  240. return EXIT_SUCCESS;
  241. }