kaem.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /* Copyright (C) 2016 Jeremiah Orians
  2. * This file is part of mescc-tools.
  3. *
  4. * mescc-tools is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * mescc-tools is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with mescc-tools. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include <stdlib.h>
  18. #include <stdio.h>
  19. #include <unistd.h>
  20. #include <sys/wait.h>
  21. #define FALSE 0
  22. //CONSTANT FALSE 0
  23. #define TRUE 1
  24. //CONSTANT TRUE 1
  25. #define max_string 4096
  26. //CONSTANT max_string 4096
  27. #define max_args 256
  28. //CONSTANT max_args 256
  29. void file_print(char* s, FILE* f);
  30. char* numerate_number(int a);
  31. int match(char* a, char* b);
  32. char** tokens;
  33. int command_done;
  34. int VERBOSE;
  35. int STRICT;
  36. /* Function for purging line comments */
  37. void collect_comment(FILE* input)
  38. {
  39. int c;
  40. do
  41. {
  42. c = fgetc(input);
  43. if(-1 == c)
  44. {
  45. file_print("IMPROPERLY TERMINATED LINE COMMENT!\nABORTING HARD\n", stderr);
  46. exit(EXIT_FAILURE);
  47. }
  48. } while('\n' != c);
  49. }
  50. /* Function for collecting RAW strings and removing the " that goes with them */
  51. int collect_string(FILE* input, int index, char* target)
  52. {
  53. int c;
  54. do
  55. {
  56. c = fgetc(input);
  57. if(-1 == c)
  58. { /* We never should hit EOF while collecting a RAW string */
  59. file_print("IMPROPERLY TERMINATED RAW string!\nABORTING HARD\n", stderr);
  60. exit(EXIT_FAILURE);
  61. }
  62. else if('"' == c)
  63. { /* Made it to the end */
  64. c = 0;
  65. }
  66. target[index] = c;
  67. index = index + 1;
  68. } while(0 != c);
  69. return index;
  70. }
  71. /* Function to collect an individual argument or purge a comment */
  72. char* collect_token(FILE* input)
  73. {
  74. char* token = calloc(max_string, sizeof(char));
  75. char c;
  76. int i = 0;
  77. do
  78. {
  79. c = fgetc(input);
  80. if(-1 == c)
  81. { /* Deal with end of file */
  82. file_print("execution complete\n", stderr);
  83. exit(EXIT_SUCCESS);
  84. }
  85. else if((' ' == c) || ('\t' == c))
  86. { /* space and tab are token seperators */
  87. c = 0;
  88. }
  89. else if('\n' == c)
  90. { /* Command terminates at end of line */
  91. c = 0;
  92. command_done = 1;
  93. }
  94. else if('"' == c)
  95. { /* RAW strings are everything between a pair of "" */
  96. i = collect_string(input, i, token);
  97. c = 0;
  98. }
  99. else if('#' == c)
  100. { /* Line comments to aid the humans */
  101. collect_comment(input);
  102. c = 0;
  103. command_done = 1;
  104. }
  105. else if('\\' == c)
  106. { /* Support for end of line escapes, drops the char after */
  107. fgetc(input);
  108. c = 0;
  109. }
  110. token[i] = c;
  111. i = i + 1;
  112. } while (0 != c);
  113. if(1 == i)
  114. { /* Nothing worth returning */
  115. free(token);
  116. return NULL;
  117. }
  118. return token;
  119. }
  120. char* copy_string(char* target, char* source)
  121. {
  122. while(0 != source[0])
  123. {
  124. target[0] = source[0];
  125. target = target + 1;
  126. source = source + 1;
  127. }
  128. return target;
  129. }
  130. char* prepend_string(char* add, char* base)
  131. {
  132. char* ret = calloc(max_string, sizeof(char));
  133. copy_string(copy_string(ret, add), base);
  134. return ret;
  135. }
  136. char* find_char(char* string, char a)
  137. {
  138. if(0 == string[0]) return NULL;
  139. while(a != string[0])
  140. {
  141. string = string + 1;
  142. if(0 == string[0]) return string;
  143. }
  144. return string;
  145. }
  146. char* prematch(char* search, char* field)
  147. {
  148. do
  149. {
  150. if(search[0] != field[0]) return NULL;
  151. search = search + 1;
  152. field = field + 1;
  153. } while(0 != search[0]);
  154. return field;
  155. }
  156. char* env_lookup(char* token, char** envp)
  157. {
  158. if(NULL == envp) return NULL;
  159. int i = 0;
  160. char* ret = NULL;
  161. do
  162. {
  163. ret = prematch(token, envp[i]);
  164. if(NULL != ret) return ret;
  165. i = i + 1;
  166. } while(NULL != envp[i]);
  167. return NULL;
  168. }
  169. char* find_executable(char* name, char* PATH)
  170. {
  171. if(('.' == name[0]) || ('/' == name[0]))
  172. { /* assume names that start with . or / are relative or absolute */
  173. return name;
  174. }
  175. char* next = find_char(PATH, ':');
  176. char* trial;
  177. FILE* try;
  178. while(NULL != next)
  179. {
  180. next[0] = 0;
  181. trial = prepend_string(PATH, prepend_string("/", name));
  182. try = fopen(trial, "r");
  183. if(NULL != try)
  184. {
  185. fclose(try);
  186. return trial;
  187. }
  188. PATH = next + 1;
  189. next = find_char(PATH, ':');
  190. free(trial);
  191. }
  192. return NULL;
  193. }
  194. /* Function for executing our programs with desired arguments */
  195. void execute_command(FILE* script, char** envp)
  196. {
  197. tokens = calloc(max_args, sizeof(char*));
  198. char* PATH = env_lookup("PATH=", envp);
  199. if(NULL != PATH)
  200. {
  201. PATH = calloc(max_string, sizeof(char));
  202. copy_string(PATH, env_lookup("PATH=", envp));
  203. }
  204. char* USERNAME = env_lookup("LOGNAME=", envp);
  205. if((NULL == PATH) && (NULL == USERNAME))
  206. {
  207. PATH = calloc(max_string, sizeof(char));
  208. copy_string(PATH, "/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
  209. }
  210. else if(NULL == PATH)
  211. {
  212. PATH = prepend_string("/home/", prepend_string(USERNAME,"/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"));
  213. }
  214. int i = 0;
  215. int status = 0;
  216. command_done = 0;
  217. do
  218. {
  219. char* result = collect_token(script);
  220. if(0 != result)
  221. { /* Not a comment string but an actual argument */
  222. tokens[i] = result;
  223. i = i + 1;
  224. }
  225. } while(0 == command_done);
  226. if(VERBOSE && (0 < i))
  227. {
  228. file_print(" +> ", stdout);
  229. int j;
  230. for(j = 0; j < i; j = j + 1)
  231. {
  232. file_print(tokens[j], stdout);
  233. fputc(' ', stdout);
  234. }
  235. file_print("\n", stdout);
  236. }
  237. if(0 < i)
  238. { /* Not a line comment */
  239. char* program = find_executable(tokens[0], PATH);
  240. if(NULL == program)
  241. {
  242. file_print(tokens[0], stderr);
  243. file_print("Some weird shit went down with: ", stderr);
  244. file_print("\n", stderr);
  245. exit(EXIT_FAILURE);
  246. }
  247. int f = fork();
  248. if (f == -1)
  249. {
  250. file_print("fork() failure", stderr);
  251. exit(EXIT_FAILURE);
  252. }
  253. else if (f == 0)
  254. { /* child */
  255. /* execve() returns only on error */
  256. execve(program, tokens, envp);
  257. /* Prevent infinite loops */
  258. _exit(EXIT_SUCCESS);
  259. }
  260. /* Otherwise we are the parent */
  261. /* And we should wait for it to complete */
  262. waitpid(f, &status, 0);
  263. if(STRICT && (0 != status))
  264. { /* Clearly the script hit an issue that should never have happened */
  265. file_print("Subprocess error ", stderr);
  266. file_print(numerate_number(status), stderr);
  267. file_print("\nABORTING HARD\n", stderr);
  268. /* stop to prevent damage */
  269. exit(EXIT_FAILURE);
  270. }
  271. /* Then go again */
  272. }
  273. }
  274. int main(int argc, char** argv, char** envp)
  275. {
  276. VERBOSE = FALSE;
  277. STRICT = FALSE;
  278. char* filename = "kaem.run";
  279. FILE* script = NULL;
  280. int i = 1;
  281. while(i <= argc)
  282. {
  283. if(NULL == argv[i])
  284. {
  285. i = i + 1;
  286. }
  287. else if(match(argv[i], "-h") || match(argv[i], "--help"))
  288. {
  289. file_print("kaem only accepts --help, --version, --file, --verbose, --nightmare-mode or no arguments\n", stdout);
  290. exit(EXIT_SUCCESS);
  291. }
  292. else if(match(argv[i], "-f") || match(argv[i], "--file"))
  293. {
  294. filename = argv[i + 1];
  295. i = i + 2;
  296. }
  297. else if(match(argv[i], "n") || match(argv[i], "--nightmare-mode"))
  298. {
  299. file_print("Begin nightmare", stdout);
  300. envp = NULL;
  301. i = i + 1;
  302. }
  303. else if(match(argv[i], "-V") || match(argv[i], "--version"))
  304. {
  305. file_print("kaem version 0.6.0\n", stdout);
  306. exit(EXIT_SUCCESS);
  307. }
  308. else if(match(argv[i], "--verbose"))
  309. {
  310. VERBOSE = TRUE;
  311. i = i + 1;
  312. }
  313. else if(match(argv[i], "--strict"))
  314. {
  315. STRICT = TRUE;
  316. i = i + 1;
  317. }
  318. else
  319. {
  320. file_print("UNKNOWN ARGUMENT\n", stdout);
  321. exit(EXIT_FAILURE);
  322. }
  323. }
  324. script = fopen(filename, "r");
  325. if(NULL == script)
  326. {
  327. file_print("The file: ", stderr);
  328. file_print(filename, stderr);
  329. file_print(" can not be opened!\n", stderr);
  330. exit(EXIT_FAILURE);
  331. }
  332. while(1)
  333. {
  334. execute_command(script, envp);
  335. }
  336. fclose(script);
  337. return EXIT_SUCCESS;
  338. }