sh.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /* SPDX-License-Identifier: BSD-3-Clause */
  2. /*
  3. * Copyright (C) 2022, 2023 Ferass El Hafidi <vitali64pmemail@protonmail.com>
  4. */
  5. /* Here's the POSIX specification of sh: */
  6. /* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html */
  7. /* Define feature test macro. It doesn't compile with gcc without that for
  8. * some reason.
  9. */
  10. #define _POSIX_C_SOURCE 200809L
  11. /* POSIX Header files */
  12. #include <stdio.h>
  13. #include <unistd.h>
  14. #include <string.h>
  15. #include <stdlib.h>
  16. #include <fcntl.h>
  17. #include <errno.h>
  18. #include <stdint.h>
  19. #include <sys/wait.h>
  20. #include <spawn.h>
  21. #include <libgen.h>
  22. #include <signal.h>
  23. /* Our own header files */
  24. #include "parser.h"
  25. #include "version.h"
  26. #ifndef COMPILETIME
  27. #define COMPILETIME
  28. #endif
  29. void commandLoop();
  30. int getopt(int argc, char *const argv[], const char *optstring);
  31. char *pathSearch(const char *path, const char *name);
  32. void printUsage();
  33. int main(int argc, char *const argv[]) {
  34. int argument;
  35. FILE *file;
  36. struct sigaction signal_action;
  37. signal_action.sa_handler = SIG_IGN;
  38. sigemptyset(&signal_action.sa_mask);
  39. while ((argument = getopt(argc, argv, "c")) != -1) {
  40. if (argument == '?') {
  41. printUsage();
  42. return 1;
  43. }
  44. }
  45. argc -= optind;
  46. argv += optind;
  47. sigaction(SIGINT, &signal_action, NULL);
  48. if (errno) return errno;
  49. if (argv[0]) {
  50. file = fopen(argv[0], "r");
  51. if (errno) return errno;
  52. commandLoop(file);
  53. }
  54. else commandLoop(stdin);
  55. return 0;
  56. }
  57. /* This function is the actual command prompt.
  58. * It basically enters in a for(;;) loop where it reads the user's input.
  59. * When the input is NULL, it exits.
  60. */
  61. void commandLoop(FILE *filstr) {
  62. char *prompt = getenv("PS1");
  63. char *token, *tokenstate;
  64. char *command[4096];
  65. char name[4096], tempstr[4096];
  66. int command_argc;
  67. if (prompt == NULL) prompt = "$ ";
  68. for (;;) {
  69. for (int i = 0; i <= 4096; i++) {
  70. name[i] = 0; /* (Re)Initialise name and command,
  71. * very important. */
  72. command[i] = 0;
  73. }
  74. setvbuf(stdout, NULL, _IONBF, 0);
  75. if (filstr == stdin && isatty(STDIN_FILENO)) printf(prompt);
  76. if (fgets(name, 4096, filstr) == NULL)
  77. break;
  78. //read(fildes, name, 4096);
  79. if (!strcmp(name, "\n"))
  80. continue;
  81. else if (*name == 0 || *name == EOF) {
  82. putchar('\n');
  83. break;
  84. }
  85. name[strlen(name) - 1] = 0;
  86. strcpy(tempstr, name); /* We don't want to override `name`. */
  87. if (strtok(tempstr, ";") != NULL && strtok(NULL, ";") != NULL) {
  88. /* Hacky wacky hack. */
  89. strcpy(tempstr, name);
  90. token = strtok_r(tempstr, ";", &tokenstate);
  91. for (; token != NULL;) {
  92. command_argc = splitCommand(token, command); /* See parser.c */
  93. parseCommand(command_argc, command); /* See parser.c */
  94. token = strtok_r(NULL, ";", &tokenstate);
  95. }
  96. }
  97. else {
  98. command_argc = splitCommand(name, command); /* See parser.c */
  99. parseCommand(command_argc, command); /* See parser.c */
  100. }
  101. }
  102. }
  103. void printUsage() {
  104. printf("Ferass' Base System. (%s)\n\n"
  105. "Usage: sh [-c command_string]\n\n"
  106. "The standard command language interpreter.\n\n"
  107. "\t-c command_string\tExecute command_string and exit.\n", COMPILETIME);
  108. }