kaem.c 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  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. char* numerate_number(int a);
  30. int match(char* a, char* b);
  31. void file_print(char* s, FILE* f);
  32. char** tokens;
  33. int command_done;
  34. int VERBOSE;
  35. int STRICT;
  36. int envp_length;
  37. /* Function for purging line comments */
  38. void collect_comment(FILE* input)
  39. {
  40. int c;
  41. do
  42. {
  43. c = fgetc(input);
  44. if(-1 == c)
  45. {
  46. file_print("IMPROPERLY TERMINATED LINE COMMENT!\nABORTING HARD\n", stderr);
  47. exit(EXIT_FAILURE);
  48. }
  49. } while('\n' != c);
  50. }
  51. /* Function for collecting RAW strings and removing the " that goes with them */
  52. int collect_string(FILE* input, int index, char* target)
  53. {
  54. int c;
  55. do
  56. {
  57. c = fgetc(input);
  58. if(-1 == c)
  59. { /* We never should hit EOF while collecting a RAW string */
  60. file_print("IMPROPERLY TERMINATED RAW string!\nABORTING HARD\n", stderr);
  61. exit(EXIT_FAILURE);
  62. }
  63. else if('"' == c)
  64. { /* Made it to the end */
  65. c = 0;
  66. }
  67. target[index] = c;
  68. index = index + 1;
  69. } while(0 != c);
  70. return index;
  71. }
  72. /* Function to collect an individual argument or purge a comment */
  73. char* collect_token(FILE* input)
  74. {
  75. char* token = calloc(max_string, sizeof(char));
  76. char c;
  77. int i = 0;
  78. do
  79. {
  80. c = fgetc(input);
  81. if(-1 == c)
  82. { /* Deal with end of file */
  83. file_print("execution complete\n", stderr);
  84. exit(EXIT_SUCCESS);
  85. }
  86. else if((' ' == c) || ('\t' == c))
  87. { /* space and tab are token seperators */
  88. c = 0;
  89. }
  90. else if('\n' == c)
  91. { /* Command terminates at end of line */
  92. c = 0;
  93. command_done = 1;
  94. }
  95. else if('"' == c)
  96. { /* RAW strings are everything between a pair of "" */
  97. i = collect_string(input, i, token);
  98. c = 0;
  99. }
  100. else if('#' == c)
  101. { /* Line comments to aid the humans */
  102. collect_comment(input);
  103. c = 0;
  104. command_done = 1;
  105. }
  106. else if('\\' == c)
  107. { /* Support for end of line escapes, drops the char after */
  108. fgetc(input);
  109. c = 0;
  110. }
  111. token[i] = c;
  112. i = i + 1;
  113. } while (0 != c);
  114. if(1 == i)
  115. { /* Nothing worth returning */
  116. free(token);
  117. return NULL;
  118. }
  119. return token;
  120. }
  121. char* copy_string(char* target, char* source)
  122. {
  123. while(0 != source[0])
  124. {
  125. target[0] = source[0];
  126. target = target + 1;
  127. source = source + 1;
  128. }
  129. return target;
  130. }
  131. int string_length(char* a)
  132. {
  133. int i = 0;
  134. while(0 != a[i]) i = i + 1;
  135. return i;
  136. }
  137. char* prepend_string(char* add, char* base)
  138. {
  139. char* ret = calloc(max_string, sizeof(char));
  140. copy_string(copy_string(ret, add), base);
  141. return ret;
  142. }
  143. char* find_char(char* string, char a)
  144. {
  145. if(0 == string[0]) return NULL;
  146. while(a != string[0])
  147. {
  148. string = string + 1;
  149. if(0 == string[0]) return string;
  150. }
  151. return string;
  152. }
  153. char* prematch(char* search, char* field)
  154. {
  155. do
  156. {
  157. if(search[0] != field[0]) return NULL;
  158. search = search + 1;
  159. field = field + 1;
  160. } while(0 != search[0]);
  161. return field;
  162. }
  163. char* env_lookup(char* token, char** envp)
  164. {
  165. if(NULL == envp) return NULL;
  166. int i = 0;
  167. char* ret = NULL;
  168. do
  169. {
  170. ret = prematch(token, envp[i]);
  171. if(NULL != ret) return ret;
  172. i = i + 1;
  173. } while(NULL != envp[i]);
  174. return NULL;
  175. }
  176. char* find_executable(char* name, char* PATH)
  177. {
  178. if(('.' == name[0]) || ('/' == name[0]))
  179. { /* assume names that start with . or / are relative or absolute */
  180. return name;
  181. }
  182. char* next = find_char(PATH, ':');
  183. char* trial;
  184. FILE* t;
  185. while(NULL != next)
  186. {
  187. next[0] = 0;
  188. trial = prepend_string(PATH, prepend_string("/", name));
  189. t = fopen(trial, "r");
  190. if(NULL != t)
  191. {
  192. fclose(t);
  193. return trial;
  194. }
  195. PATH = next + 1;
  196. next = find_char(PATH, ':');
  197. free(trial);
  198. }
  199. return NULL;
  200. }
  201. /* Function to check if the token is an envar and if it is get the pos of = */
  202. int check_envar(char* token)
  203. {
  204. int j;
  205. int equal_found;
  206. equal_found = 0;
  207. int found;
  208. char c;
  209. for(j = 0; j < string_length(token); j = j + 1)
  210. {
  211. if(token[j] == '=')
  212. { /* After = can be anything */
  213. equal_found = 1;
  214. break;
  215. }
  216. else
  217. { /* Should be A-z */
  218. found = 0;
  219. /* Represented numerically; 0 = 48 through 9 = 57 */
  220. for(c = 48; c <= 57; c = c + 1)
  221. {
  222. if(token[j] == c)
  223. {
  224. found = 1;
  225. }
  226. }
  227. /* Represented numerically; A = 65 through z = 122 */
  228. for(c = 65; c <= 122; c = c + 1)
  229. {
  230. if(token[j] == c)
  231. {
  232. found = 1;
  233. }
  234. }
  235. if(found == 0)
  236. { /* In all likelihood this isn't actually an environment variable */
  237. return 1;
  238. }
  239. }
  240. }
  241. if(equal_found == 0)
  242. { /* Not an envar */
  243. return 1;
  244. }
  245. return 0;
  246. }
  247. /* Function for executing our programs with desired arguments */
  248. void execute_commands(FILE* script, char** envp, int envp_length)
  249. {
  250. char* PATH;
  251. char* USERNAME;
  252. int i;
  253. int status;
  254. char* result;
  255. int j;
  256. int is_envar;
  257. char* program;
  258. int f;
  259. while(1)
  260. {
  261. tokens = calloc(max_args, sizeof(char*));
  262. PATH = env_lookup("PATH=", envp);
  263. if(NULL != PATH)
  264. {
  265. PATH = calloc(max_string, sizeof(char));
  266. copy_string(PATH, env_lookup("PATH=", envp));
  267. }
  268. USERNAME = env_lookup("LOGNAME=", envp);
  269. if((NULL == PATH) && (NULL == USERNAME))
  270. {
  271. PATH = calloc(max_string, sizeof(char));
  272. copy_string(PATH, "/root/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
  273. }
  274. else if(NULL == PATH)
  275. {
  276. PATH = prepend_string("/home/", prepend_string(USERNAME,"/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games"));
  277. }
  278. i = 0;
  279. status = 0;
  280. command_done = 0;
  281. do
  282. {
  283. result = collect_token(script);
  284. if(0 != result)
  285. { /* Not a comment string but an actual argument */
  286. tokens[i] = result;
  287. i = i + 1;
  288. }
  289. } while(0 == command_done);
  290. if(VERBOSE && (0 < i))
  291. {
  292. file_print(" +> ", stdout);
  293. for(j = 0; j < i; j = j + 1)
  294. {
  295. file_print(tokens[j], stdout);
  296. fputc(' ', stdout);
  297. }
  298. file_print("\n", stdout);
  299. }
  300. if(0 < i)
  301. { /* Not a line comment */
  302. is_envar = 0;
  303. if(check_envar(tokens[0]) == 0)
  304. { /* It's an envar! */
  305. is_envar = 1;
  306. envp[envp_length] = tokens[0]; /* Since arrays are 0 indexed */
  307. envp_length = envp_length + 1;
  308. }
  309. if(is_envar == 0)
  310. { /* Stuff to exec */
  311. program = find_executable(tokens[0], PATH);
  312. if(NULL == program)
  313. {
  314. file_print(tokens[0], stderr);
  315. file_print("Some weird shit went down with: ", stderr);
  316. file_print("\n", stderr);
  317. exit(EXIT_FAILURE);
  318. }
  319. f = fork();
  320. if (f == -1)
  321. {
  322. file_print("fork() failure", stderr);
  323. exit(EXIT_FAILURE);
  324. }
  325. else if (f == 0)
  326. { /* child */
  327. /* execve() returns only on error */
  328. execve(program, tokens, envp);
  329. /* Prevent infinite loops */
  330. _exit(EXIT_SUCCESS);
  331. }
  332. /* Otherwise we are the parent */
  333. /* And we should wait for it to complete */
  334. waitpid(f, &status, 0);
  335. if(STRICT && (0 != status))
  336. { /* Clearly the script hit an issue that should never have happened */
  337. file_print("Subprocess error ", stderr);
  338. file_print(numerate_number(status), stderr);
  339. file_print("\nABORTING HARD\n", stderr);
  340. /* stop to prevent damage */
  341. exit(EXIT_FAILURE);
  342. }
  343. }
  344. /* Then go again */
  345. }
  346. }
  347. }
  348. int main(int argc, char** argv, char** envp)
  349. {
  350. VERBOSE = FALSE;
  351. STRICT = FALSE;
  352. char* filename = "kaem.run";
  353. FILE* script = NULL;
  354. /* Get envp_length */
  355. envp_length = 1;
  356. while(envp[envp_length] != NULL)
  357. {
  358. envp_length = envp_length + 1;
  359. }
  360. char** nenvp = calloc(envp_length + max_args + 1, sizeof(char*));
  361. int i;
  362. for(i = 0; i < envp_length; i = i + 1)
  363. {
  364. nenvp[i] = envp[i];
  365. }
  366. for(i = envp_length; i < (envp_length + max_args); i = i + 1)
  367. {
  368. nenvp[i] = "";
  369. }
  370. i = 1;
  371. while(i <= argc)
  372. {
  373. if(NULL == argv[i])
  374. {
  375. i = i + 1;
  376. }
  377. else if(match(argv[i], "-h") || match(argv[i], "--help"))
  378. {
  379. file_print("kaem only accepts --help, --version, --file, --verbose, --nightmare-mode or no arguments\n", stdout);
  380. exit(EXIT_SUCCESS);
  381. }
  382. else if(match(argv[i], "-f") || match(argv[i], "--file"))
  383. {
  384. filename = argv[i + 1];
  385. i = i + 2;
  386. }
  387. else if(match(argv[i], "n") || match(argv[i], "--nightmare-mode"))
  388. {
  389. file_print("Begin nightmare", stdout);
  390. envp = NULL;
  391. i = i + 1;
  392. }
  393. else if(match(argv[i], "-V") || match(argv[i], "--version"))
  394. {
  395. file_print("kaem version 0.6.0\n", stdout);
  396. exit(EXIT_SUCCESS);
  397. }
  398. else if(match(argv[i], "--verbose"))
  399. {
  400. VERBOSE = TRUE;
  401. i = i + 1;
  402. }
  403. else if(match(argv[i], "--strict"))
  404. {
  405. STRICT = TRUE;
  406. i = i + 1;
  407. }
  408. else
  409. {
  410. file_print("UNKNOWN ARGUMENT\n", stdout);
  411. exit(EXIT_FAILURE);
  412. }
  413. }
  414. script = fopen(filename, "r");
  415. if(NULL == script)
  416. {
  417. file_print("The file: ", stderr);
  418. file_print(filename, stderr);
  419. file_print(" can not be opened!\n", stderr);
  420. exit(EXIT_FAILURE);
  421. }
  422. execute_commands(script, nenvp, envp_length);
  423. fclose(script);
  424. return EXIT_SUCCESS;
  425. }