sh.c 21 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013
  1. /* vim: tabstop=4 shiftwidth=4 noexpandtab
  2. * This file is part of ToaruOS and is released under the terms
  3. * of the NCSA / University of Illinois License - see LICENSE.md
  4. * Copyright (C) 2013-2014 Kevin Lange
  5. *
  6. * E-Shell
  7. *
  8. * This is the "experimental shell". It provides
  9. * a somewhat unix-like shell environment, but does
  10. * not include a parser any advanced functionality.
  11. * It simply cuts its input into arguments and executes
  12. * programs.
  13. */
  14. #define _XOPEN_SOURCE
  15. #include <stdio.h>
  16. #include <stdint.h>
  17. #include <string.h>
  18. #include <stdlib.h>
  19. #include <unistd.h>
  20. #include <time.h>
  21. #include <dirent.h>
  22. #include <signal.h>
  23. #include <getopt.h>
  24. #include <termios.h>
  25. #include <errno.h>
  26. #include <sys/time.h>
  27. #include <sys/wait.h>
  28. #include <sys/utsname.h>
  29. #include <sys/stat.h>
  30. #include <_xlog.h>
  31. #include <toaru/list.h>
  32. #include <toaru/kbd.h>
  33. #include <toaru/rline.h>
  34. #define PIPE_TOKEN "\xFF\xFFPIPE\xFF\xFF"
  35. #define STAR_TOKEN "\xFF\xFFSTAR\xFF\xFF"
  36. /* A shell command is like a C program */
  37. typedef uint32_t(*shell_command_t) (int argc, char ** argv);
  38. /* We have a static array that fits a certain number of them. */
  39. #define SHELL_COMMANDS 512
  40. char * shell_commands[SHELL_COMMANDS]; /* Command names */
  41. shell_command_t shell_pointers[SHELL_COMMANDS]; /* Command functions */
  42. char * shell_descript[SHELL_COMMANDS]; /* Command descriptions */
  43. /* This is the number of actual commands installed */
  44. uint32_t shell_commands_len = 0;
  45. int shell_interactive = 1;
  46. int pid; /* Process ID of the shell */
  47. void shell_install_command(char * name, shell_command_t func, char * desc) {
  48. if (shell_commands_len == SHELL_COMMANDS) {
  49. fprintf(stderr, "Ran out of space for static shell commands. The maximum number of commands is %d\n", SHELL_COMMANDS);
  50. return;
  51. }
  52. shell_commands[shell_commands_len] = name;
  53. shell_pointers[shell_commands_len] = func;
  54. shell_descript[shell_commands_len] = desc;
  55. shell_commands_len++;
  56. }
  57. shell_command_t shell_find(char * str) {
  58. for (uint32_t i = 0; i < shell_commands_len; ++i) {
  59. if (!strcmp(str, shell_commands[i])) {
  60. return shell_pointers[i];
  61. }
  62. }
  63. return NULL;
  64. }
  65. void install_commands();
  66. /* Maximum command length */
  67. #define LINE_LEN 4096
  68. /* Current working directory */
  69. char cwd[1024] = {'/',0};
  70. /* Username */
  71. char username[1024];
  72. /* Hostname for prompt */
  73. char _hostname[256];
  74. /* function to update the cached username */
  75. void getuser() {
  76. char * tmp = getenv("USER");
  77. if (tmp) {
  78. _XLOG("Got user:");
  79. _XLOG(tmp);
  80. strcpy(username, tmp);
  81. } else {
  82. sprintf(username, "%d", getuid());
  83. }
  84. }
  85. /* function to update the cached hostname */
  86. void gethost() {
  87. struct utsname buf;
  88. uname(&buf);
  89. int len = strlen(buf.nodename);
  90. memcpy(_hostname, buf.nodename, len+1);
  91. }
  92. /* Draw the user prompt */
  93. void draw_prompt(int ret) {
  94. /* Get the time */
  95. struct tm * timeinfo;
  96. struct timeval now;
  97. gettimeofday(&now, NULL); //time(NULL);
  98. timeinfo = localtime((time_t *)&now.tv_sec);
  99. /* Format the date and time for prompt display */
  100. char date_buffer[80];
  101. strftime(date_buffer, 80, "%m/%d", timeinfo);
  102. char time_buffer[80];
  103. strftime(time_buffer, 80, "%H:%M:%S", timeinfo);
  104. /* Print the working directory in there, too */
  105. getcwd(cwd, 512);
  106. char _cwd[512];
  107. strcpy(_cwd, cwd);
  108. char * home = getenv("HOME");
  109. if (home && strstr(cwd, home) == cwd) {
  110. char * c = cwd + strlen(home);
  111. if (*c == '/' || *c == 0) {
  112. sprintf(_cwd, "~%s", c);
  113. }
  114. }
  115. /* Print the prompt. */
  116. printf("\033]1;%s@%s:%s\007", username, _hostname, _cwd);
  117. printf("\033[s\033[400C\033[16D\033[1m\033[38;5;59m[\033[38;5;173m%s \033[38;5;167m%s\033[38;5;59m]\033[u\033[38;5;221m%s\033[38;5;59m@\033[38;5;81m%s ",
  118. date_buffer, time_buffer,
  119. username, _hostname);
  120. if (ret != 0) {
  121. printf("\033[38;5;167m%d ", ret);
  122. }
  123. printf("\033[0m%s%s\033[0m ", _cwd, getuid() == 0 ? "\033[1;38;5;196m#" : "\033[1;38;5;47m$");
  124. fflush(stdout);
  125. }
  126. uint32_t child = 0;
  127. void sig_pass(int sig) {
  128. /* Interrupt handler */
  129. if (child) {
  130. kill(child, sig);
  131. }
  132. }
  133. void redraw_prompt_func(rline_context_t * context) {
  134. draw_prompt(0);
  135. }
  136. void draw_prompt_c() {
  137. printf("> ");
  138. fflush(stdout);
  139. }
  140. void redraw_prompt_func_c(rline_context_t * context) {
  141. draw_prompt_c();
  142. }
  143. void tab_complete_func(rline_context_t * c) {
  144. char * dup = malloc(LINE_LEN);
  145. memcpy(dup, c->buffer, LINE_LEN);
  146. char *pch, *cmd, *save;
  147. char *argv[1024];
  148. int argc = 0;
  149. int cursor = 0;
  150. pch = strtok_r(dup, " ", &save);
  151. if (!pch) {
  152. argv[0] = "";
  153. argc = 0;
  154. }
  155. while (pch != NULL) {
  156. if (pch - dup <= c->offset) cursor = argc;
  157. argv[argc] = pch;
  158. ++argc;
  159. pch = strtok_r(NULL, " ", &save);
  160. }
  161. argv[argc] = NULL;
  162. if (c->offset && c->buffer[c->offset-1] == ' ' && argc) {
  163. cursor++;
  164. }
  165. char * word = argv[cursor];
  166. int word_offset = word ? (c->offset - (argv[cursor] - dup)) : 0;
  167. char * prefix = malloc(word_offset + 1);
  168. if (word) memcpy(prefix, word, word_offset);
  169. prefix[word_offset] = '\0';
  170. /* Complete file path */
  171. list_t * matches = list_create();
  172. char * match = NULL;
  173. int free_matches = 0;
  174. int no_space_if_only = 0;
  175. if (cursor == 0 && !strchr(prefix,'/')) {
  176. /* Complete binary name */
  177. for (int i = 0; i < shell_commands_len; ++i) {
  178. if (strstr(shell_commands[i], prefix) == shell_commands[i]) {
  179. list_insert(matches, shell_commands[i]);
  180. match = shell_commands[i];
  181. }
  182. }
  183. } else {
  184. free_matches = 1;
  185. char * tmp = strdup(prefix);
  186. char * last_slash = strrchr(tmp, '/');
  187. DIR * dirp;
  188. char * compare = prefix;
  189. if (last_slash) {
  190. *last_slash = '\0';
  191. word = word + (last_slash - tmp) + 1;
  192. word_offset = word_offset - (last_slash - tmp + 1);
  193. compare = word;
  194. if (last_slash == tmp) {
  195. dirp = opendir("/");
  196. } else {
  197. dirp = opendir(tmp);
  198. }
  199. } else {
  200. dirp = opendir(".");
  201. }
  202. if (!dirp) {
  203. free(tmp);
  204. goto finish_tab;
  205. }
  206. struct dirent * ent = readdir(dirp);
  207. while (ent != NULL) {
  208. if (ent->d_name[0] != '.') {
  209. if (!word || strstr(ent->d_name, compare) == ent->d_name) {
  210. struct stat statbuf;
  211. /* stat it */
  212. if (last_slash) {
  213. char * x = malloc(strlen(tmp) + 1 + strlen(ent->d_name) + 1);
  214. sprintf(x,"%s/%s",tmp,ent->d_name);
  215. int t = lstat(x, &statbuf);
  216. } else {
  217. int t = lstat(ent->d_name, &statbuf);
  218. }
  219. char * s;
  220. if (S_ISDIR(statbuf.st_mode)) {
  221. s = malloc(strlen(ent->d_name) + 2);
  222. sprintf(s,"%s/", ent->d_name);
  223. no_space_if_only = 1;
  224. } else {
  225. s = strdup(ent->d_name);
  226. }
  227. list_insert(matches, s);
  228. match = s;
  229. }
  230. }
  231. ent = readdir(dirp);
  232. }
  233. closedir(dirp);
  234. free(tmp);
  235. }
  236. if (matches->length == 1) {
  237. /* Insert */
  238. rline_insert(c, &match[word_offset]);
  239. if (word && word_offset == strlen(word) && !no_space_if_only) {
  240. rline_insert(c, " ");
  241. }
  242. rline_redraw(c);
  243. } else if (matches->length > 1) {
  244. if (!c->tabbed) {
  245. /* see if there is a minimum subset we can fill in */
  246. size_t j = word_offset;
  247. do {
  248. char d = match[j];
  249. int diff = 0;
  250. foreach(node, matches) {
  251. char * match = (char *)node->value;
  252. if (match[j] != d || match[j] == '\0') diff = 1;
  253. }
  254. if (diff) break;
  255. j++;
  256. } while (j < c->requested);
  257. if (j > word_offset) {
  258. char * tmp = strdup(match);
  259. tmp[j] = '\0';
  260. rline_insert(c, &tmp[word_offset]);
  261. rline_redraw(c);
  262. free(tmp);
  263. } else {
  264. c->tabbed = 1;
  265. }
  266. } else {
  267. /* Print matches */
  268. fprintf(stderr,"\n");
  269. size_t j = 0;
  270. foreach(node, matches) {
  271. char * match = (char *)node->value;
  272. fprintf(stderr, "%s", match);
  273. ++j;
  274. if (j < matches->length) {
  275. fprintf(stderr, ", ");
  276. }
  277. }
  278. fprintf(stderr,"\n");
  279. c->callbacks->redraw_prompt(c);
  280. fprintf(stderr, "\033[s");
  281. rline_redraw(c);
  282. }
  283. }
  284. finish_tab:
  285. if (free_matches) list_destroy(matches);
  286. list_free(matches);
  287. free(prefix);
  288. free(dup);
  289. }
  290. void add_argument(list_t * argv, char * buf) {
  291. char * c = malloc(strlen(buf) + 1);
  292. memcpy(c, buf, strlen(buf) + 1);
  293. list_insert(argv, c);
  294. }
  295. int read_entry(char * buffer) {
  296. rline_callbacks_t callbacks = {
  297. tab_complete_func, redraw_prompt_func, NULL,
  298. NULL, NULL, NULL, NULL, NULL
  299. };
  300. int buffer_size = rline((char *)buffer, LINE_LEN, &callbacks);
  301. return buffer_size;
  302. }
  303. int read_entry_continued(char * buffer) {
  304. rline_callbacks_t callbacks = {
  305. tab_complete_func, redraw_prompt_func_c, NULL,
  306. NULL, NULL, NULL, NULL, NULL
  307. };
  308. int buffer_size = rline((char *)buffer, LINE_LEN, &callbacks);
  309. return buffer_size;
  310. }
  311. int variable_char(uint8_t c) {
  312. if (c >= 65 && c <= 90) return 1;
  313. if (c >= 97 && c <= 122) return 1;
  314. if (c >= 48 && c <= 57) return 1;
  315. if (c == 95) return 1;
  316. return 0;
  317. }
  318. void run_cmd(char ** args) {
  319. int i = execvp(*args, args);
  320. shell_command_t func = shell_find(*args);
  321. if (func) {
  322. int argc = 0;
  323. while (args[argc]) {
  324. argc++;
  325. }
  326. i = func(argc, args);
  327. } else {
  328. if (i != 0) {
  329. fprintf(stderr, "%s: Command not found\n", *args);
  330. i = 127;
  331. }
  332. }
  333. exit(i);
  334. }
  335. int shell_exec(char * buffer, int buffer_size) {
  336. /* Read previous history entries */
  337. if (buffer[0] == '!') {
  338. int x = atoi((char *)((uintptr_t)buffer + 1));
  339. if (x > 0 && x <= rline_history_count) {
  340. buffer = rline_history_get(x - 1);
  341. buffer_size = strlen(buffer);
  342. } else {
  343. fprintf(stderr, "esh: !%d: event not found\n", x);
  344. return 0;
  345. }
  346. }
  347. char * history = malloc(strlen(buffer) + 1);
  348. memcpy(history, buffer, strlen(buffer) + 1);
  349. if (buffer[0] != ' ' && buffer[0] != '\n') {
  350. rline_history_insert(history);
  351. } else {
  352. free(history);
  353. }
  354. char * argv[1024];
  355. int tokenid = 0;
  356. char quoted = 0;
  357. char backtick = 0;
  358. char buffer_[512] = {0};
  359. int collected = 0;
  360. list_t * args = list_create();
  361. int have_star = 0;
  362. while (1) {
  363. char * p = buffer;
  364. while (*p) {
  365. switch (*p) {
  366. case '$':
  367. if (quoted == '\'') {
  368. goto _just_add;
  369. } else {
  370. if (backtick) {
  371. goto _just_add;
  372. }
  373. p++;
  374. char var[100];
  375. int coll = 0;
  376. if (*p == '{') {
  377. p++;
  378. while (*p != '}' && *p != '\0' && (coll < 100)) {
  379. var[coll] = *p;
  380. coll++;
  381. var[coll] = '\0';
  382. p++;
  383. }
  384. if (*p == '}') {
  385. p++;
  386. }
  387. } else {
  388. while (*p != '\0' && variable_char(*p) && (coll < 100)) {
  389. var[coll] = *p;
  390. coll++;
  391. var[coll] = '\0';
  392. p++;
  393. }
  394. }
  395. char *c = getenv(var);
  396. if (c) {
  397. backtick = 0;
  398. for (int i = 0; i < strlen(c); ++i) {
  399. buffer_[collected] = c[i];
  400. collected++;
  401. }
  402. buffer_[collected] = '\0';
  403. }
  404. continue;
  405. }
  406. case '\"':
  407. if (quoted == '\"') {
  408. if (backtick) {
  409. goto _just_add;
  410. }
  411. quoted = 0;
  412. goto _next;
  413. } else if (!quoted) {
  414. quoted = *p;
  415. goto _next;
  416. }
  417. goto _just_add;
  418. case '\'':
  419. if (quoted == '\'') {
  420. if (backtick) {
  421. goto _just_add;
  422. }
  423. quoted = 0;
  424. goto _next;
  425. } else if (!quoted) {
  426. quoted = *p;
  427. goto _next;
  428. }
  429. goto _just_add;
  430. case '*':
  431. if (quoted) {
  432. goto _just_add;
  433. }
  434. if (backtick) {
  435. goto _just_add;
  436. }
  437. if (have_star) {
  438. goto _just_add; /* TODO multiple globs */
  439. }
  440. have_star = 1;
  441. collected += sprintf(&buffer_[collected], STAR_TOKEN);
  442. goto _next;
  443. case '\\':
  444. if (quoted == '\'') {
  445. goto _just_add;
  446. }
  447. if (backtick) {
  448. goto _just_add;
  449. }
  450. backtick = 1;
  451. goto _next;
  452. case ' ':
  453. if (backtick) {
  454. goto _just_add;
  455. }
  456. if (!quoted) {
  457. goto _new_arg;
  458. }
  459. goto _just_add;
  460. case '\n':
  461. if (!quoted) {
  462. goto _done;
  463. }
  464. goto _just_add;
  465. case '|':
  466. if (!quoted && !backtick && !collected) {
  467. collected = sprintf(buffer_, "%s", PIPE_TOKEN);
  468. goto _new_arg;
  469. }
  470. default:
  471. if (backtick) {
  472. buffer_[collected] = '\\';
  473. collected++;
  474. buffer_[collected] = '\0';
  475. }
  476. _just_add:
  477. backtick = 0;
  478. buffer_[collected] = *p;
  479. collected++;
  480. buffer_[collected] = '\0';
  481. goto _next;
  482. }
  483. _new_arg:
  484. backtick = 0;
  485. if (collected) {
  486. add_argument(args, buffer_);
  487. buffer_[0] = '\0';
  488. have_star = 0;
  489. collected = 0;
  490. }
  491. _next:
  492. p++;
  493. }
  494. _done:
  495. if (quoted) {
  496. if (shell_interactive) {
  497. draw_prompt_c();
  498. buffer_size = read_entry_continued(buffer);
  499. rline_history_append_line(buffer);
  500. continue;
  501. } else {
  502. fprintf(stderr, "Syntax error: Unterminated quoted string.\n");
  503. return 127;
  504. }
  505. }
  506. if (collected) {
  507. add_argument(args, buffer_);
  508. break;
  509. }
  510. break;
  511. }
  512. int cmdi = 0;
  513. char ** arg_starts[100] = { &argv[0], NULL };
  514. int argcs[100] = {0};
  515. int i = 0;
  516. foreach(node, args) {
  517. char * c = node->value;
  518. if (!strcmp(c, PIPE_TOKEN)) {
  519. argv[i] = 0;
  520. i++;
  521. cmdi++;
  522. arg_starts[cmdi] = &argv[i];
  523. continue;
  524. }
  525. char * glob = strstr(c, STAR_TOKEN);
  526. if (glob) {
  527. /* Globbing */
  528. glob[0] = '\0';
  529. glob[1] = '\0';
  530. char * before = c;
  531. char * after = &glob[8];
  532. int has_before = !!strlen(before);
  533. int has_after = !!strlen(after);
  534. if (!has_before || !strchr(before,'/')) {
  535. /* read current directory, add all */
  536. DIR * dirp = opendir(".");
  537. int before_i = i;
  538. struct dirent * ent = readdir(dirp);
  539. while (ent != NULL) {
  540. if (ent->d_name[0] != '.') {
  541. char * s = malloc(sizeof(char) * (strlen(ent->d_name) + 1));
  542. memcpy(s, ent->d_name, strlen(ent->d_name) + 1);
  543. char * t = s;
  544. if (has_before) {
  545. if (strstr(s,before) != s) {
  546. goto _nope;
  547. }
  548. t = &s[strlen(before)];
  549. }
  550. if (has_after) {
  551. if (strlen(t) >= strlen(after)) {
  552. if (!strcmp(after,&t[strlen(t)-strlen(after)])) {
  553. argv[i] = s;
  554. i++;
  555. argcs[cmdi]++;
  556. }
  557. }
  558. } else {
  559. argv[i] = s;
  560. i++;
  561. argcs[cmdi]++;
  562. }
  563. }
  564. _nope:
  565. ent = readdir(dirp);
  566. }
  567. closedir(dirp);
  568. if (before_i == i) {
  569. /* no matches */
  570. glob[0] = '*';
  571. memmove(&glob[1], after, strlen(after)+1);
  572. argv[i] = c;
  573. i++;
  574. argcs[cmdi]++;
  575. } else {
  576. free(c);
  577. }
  578. } else {
  579. /* directory globs not supported */
  580. glob[0] = '*';
  581. argv[i] = c;
  582. i++;
  583. argcs[cmdi]++;
  584. }
  585. } else {
  586. argv[i] = c;
  587. i++;
  588. argcs[cmdi]++;
  589. }
  590. }
  591. argv[i] = NULL;
  592. if (i == 0) {
  593. return 0;
  594. }
  595. list_free(args);
  596. char * cmd = *arg_starts[0];
  597. tokenid = i;
  598. unsigned int child_pid;
  599. int nowait = (!strcmp(argv[tokenid-1],"&"));
  600. if (nowait) {
  601. argv[tokenid-1] = NULL;
  602. }
  603. if (cmdi > 0) {
  604. int last_output[2];
  605. pipe(last_output);
  606. child_pid = fork();
  607. if (!child_pid) {
  608. dup2(last_output[1], STDOUT_FILENO);
  609. close(last_output[0]);
  610. run_cmd(arg_starts[0]);
  611. }
  612. for (int j = 1; j < cmdi; ++j) {
  613. int tmp_out[2];
  614. pipe(tmp_out);
  615. if (!fork()) {
  616. dup2(tmp_out[1], STDOUT_FILENO);
  617. dup2(last_output[0], STDIN_FILENO);
  618. close(tmp_out[0]);
  619. close(last_output[1]);
  620. run_cmd(arg_starts[j]);
  621. }
  622. close(last_output[0]);
  623. close(last_output[1]);
  624. last_output[0] = tmp_out[0];
  625. last_output[1] = tmp_out[1];
  626. }
  627. if (!fork()) {
  628. dup2(last_output[0], STDIN_FILENO);
  629. close(last_output[1]);
  630. run_cmd(arg_starts[cmdi]);
  631. }
  632. close(last_output[0]);
  633. close(last_output[1]);
  634. /* Now execute the last piece and wait on all of them */
  635. } else {
  636. shell_command_t func = shell_find(*arg_starts[0]);
  637. if (func) {
  638. return func(argcs[0], arg_starts[0]);
  639. } else {
  640. child_pid = fork();
  641. if (!child_pid) {
  642. run_cmd(arg_starts[0]);
  643. }
  644. }
  645. }
  646. tcsetpgrp(STDIN_FILENO, child_pid);
  647. int ret_code = 0;
  648. if (!nowait) {
  649. child = child_pid;
  650. int pid;
  651. do {
  652. pid = waitpid(-1, &ret_code, 0);
  653. } while (pid != -1 || (pid == -1 && errno != ECHILD));
  654. child = 0;
  655. }
  656. tcsetpgrp(STDIN_FILENO, getpid());
  657. free(cmd);
  658. return ret_code;
  659. }
  660. void add_path_contents(char * path) {
  661. DIR * dirp = opendir(path);
  662. if (!dirp) return; /* Failed to load directly */
  663. struct dirent * ent = readdir(dirp);
  664. while (ent != NULL) {
  665. if (ent->d_name[0] != '.') {
  666. char * s = malloc(sizeof(char) * (strlen(ent->d_name) + 1));
  667. memcpy(s, ent->d_name, strlen(ent->d_name) + 1);
  668. shell_install_command(s, NULL, NULL);
  669. }
  670. ent = readdir(dirp);
  671. }
  672. closedir(dirp);
  673. }
  674. struct command {
  675. char * string;
  676. void * func;
  677. char * desc;
  678. };
  679. static int comp_shell_commands(const void *p1, const void *p2) {
  680. return strcmp(((struct command *)p1)->string, ((struct command *)p2)->string);
  681. }
  682. void sort_commands() {
  683. struct command commands[SHELL_COMMANDS];
  684. for (int i = 0; i < shell_commands_len; ++i) {
  685. commands[i].string = shell_commands[i];
  686. commands[i].func = shell_pointers[i];
  687. commands[i].desc = shell_descript[i];
  688. }
  689. qsort(&commands, shell_commands_len, sizeof(struct command), comp_shell_commands);
  690. for (int i = 0; i < shell_commands_len; ++i) {
  691. shell_commands[i] = commands[i].string;
  692. shell_pointers[i] = commands[i].func;
  693. shell_descript[i] = commands[i].desc;
  694. }
  695. }
  696. void show_version(void) {
  697. printf("esh 0.11.0 - experimental shell\n");
  698. }
  699. void show_usage(int argc, char * argv[]) {
  700. printf(
  701. "Esh: The Experimental Shell\n"
  702. "\n"
  703. "usage: %s [-lha] [path]\n"
  704. "\n"
  705. " -c \033[4mcmd\033[0m \033[3mparse and execute cmd\033[0m\n"
  706. //-c cmd \033[...
  707. " -v \033[3mshow version information\033[0m\n"
  708. " -? \033[3mshow this help text\033[0m\n"
  709. "\n", argv[0]);
  710. }
  711. int main(int argc, char ** argv) {
  712. int nowait = 0;
  713. int free_cmd = 0;
  714. int last_ret = 0;
  715. pid = getpid();
  716. signal(SIGINT, sig_pass);
  717. signal(SIGWINCH, sig_pass);
  718. getuser();
  719. gethost();
  720. install_commands();
  721. add_path_contents("/bin");
  722. //add_path_contents("/usr/bin");
  723. sort_commands();
  724. for (int i = 1; i < argc; ++i) {
  725. if (!strcmp(argv[i], "-c")) {
  726. return shell_exec(argv[i+1], strlen(argv[i+1]));
  727. }
  728. if (!strcmp(argv[i], "-v")) {
  729. show_version();
  730. return 0;
  731. }
  732. if (!strcmp(argv[i], "-?")) {
  733. show_usage(argc, argv);
  734. return 0;
  735. }
  736. }
  737. #if 0
  738. if (argc > 1) {
  739. int index, c;
  740. while ((c = getopt(argc, argv, "c:v?")) != -1) {
  741. switch (c) {
  742. case 'c':
  743. shell_interactive = 0;
  744. return shell_exec(optarg, strlen(optarg));
  745. case 'v':
  746. show_version();
  747. return 0;
  748. case '?':
  749. show_usage(argc, argv);
  750. return 0;
  751. }
  752. }
  753. }
  754. #endif
  755. shell_interactive = 1;
  756. while (1) {
  757. draw_prompt(last_ret);
  758. char buffer[LINE_LEN] = {0};
  759. int buffer_size;
  760. buffer_size = read_entry(buffer);
  761. last_ret = shell_exec(buffer, buffer_size);
  762. rline_scroll = 0;
  763. }
  764. exit:
  765. return 0;
  766. }
  767. /*
  768. * cd [path]
  769. */
  770. uint32_t shell_cmd_cd(int argc, char * argv[]) {
  771. if (argc > 1) {
  772. if (chdir(argv[1])) {
  773. goto cd_error;
  774. } /* else success */
  775. } else /* argc < 2 */ {
  776. char * home = getenv("HOME");
  777. if (home) {
  778. if (chdir(home)) {
  779. goto cd_error;
  780. }
  781. } else {
  782. char home_path[512];
  783. sprintf(home_path, "/home/%s", username);
  784. if (chdir(home_path)) {
  785. goto cd_error;
  786. }
  787. }
  788. }
  789. return 0;
  790. cd_error:
  791. fprintf(stderr, "%s: could not cd '%s': no such file or directory\n", argv[0], argv[1]);
  792. return 1;
  793. }
  794. /*
  795. * history
  796. */
  797. uint32_t shell_cmd_history(int argc, char * argv[]) {
  798. for (int i = 0; i < rline_history_count; ++i) {
  799. printf("%d\t%s\n", i + 1, rline_history_get(i));
  800. }
  801. return 0;
  802. }
  803. uint32_t shell_cmd_export(int argc, char * argv[]) {
  804. if (argc > 1) {
  805. //putenv(argv[1]);
  806. }
  807. return 0;
  808. }
  809. uint32_t shell_cmd_exit(int argc, char * argv[]) {
  810. if (argc > 1) {
  811. exit(atoi(argv[1]));
  812. } else {
  813. exit(0);
  814. }
  815. return -1;
  816. }
  817. uint32_t shell_cmd_set(int argc, char * argv[]) {
  818. char * term = getenv("TERM");
  819. if (!term || strstr(term, "toaru") != term) {
  820. fprintf(stderr, "Unrecognized terminal. These commands are for the とある terminal only.\n");
  821. return 1;
  822. }
  823. if (argc < 2) {
  824. fprintf(stderr, "%s: expected argument\n", argv[0]);
  825. return 1;
  826. }
  827. if (!strcmp(argv[1], "alpha")) {
  828. if (argc < 3) {
  829. fprintf(stderr, "%s %s [0 or 1]\n", argv[0], argv[1]);
  830. return 1;
  831. }
  832. int i = atoi(argv[2]);
  833. if (i) {
  834. printf("\033[2001z");
  835. } else {
  836. printf("\033[2000z");
  837. }
  838. fflush(stdout);
  839. return 0;
  840. } else if (!strcmp(argv[1], "scale")) {
  841. if (argc < 3) {
  842. fprintf(stderr, "%s %s [floating point size, 1.0 = normal]\n", argv[0], argv[1]);
  843. return 1;
  844. }
  845. printf("\033[1555;%sz", argv[2]);
  846. fflush(stdout);
  847. return 0;
  848. } else if (!strcmp(argv[1], "size")) {
  849. if (argc < 4) {
  850. fprintf(stderr, "%s %s [width] [height]\n", argv[0], argv[1]);
  851. return 1;
  852. }
  853. printf("\033[3000;%s;%sz", argv[2], argv[3]);
  854. fflush(stdout);
  855. return 0;
  856. } else if (!strcmp(argv[1], "--help")) {
  857. fprintf(stderr, "Available arguments:\n"
  858. " alpha - alpha transparency enabled / disabled\n"
  859. " scale - font scaling\n"
  860. " size - terminal width/height in characters\n"
  861. " force-raw - sets terminal to raw mode before commands\n"
  862. " no-force-raw - disables forced raw mode\n"
  863. );
  864. return 0;
  865. }
  866. fprintf(stderr, "%s: unrecognized argument\n", argv[0]);
  867. return 1;
  868. }
  869. uint32_t shell_cmd_help(int argc, char * argv[]) {
  870. show_version();
  871. printf("\nThis shell is not POSIX-compliant, please be careful.\n\n");
  872. printf("Built-in commands:\n");
  873. for (uint32_t i = 0; i < shell_commands_len; ++i) {
  874. if (!shell_descript[i]) continue;
  875. printf(" %-20s - %s\n", shell_commands[i], shell_descript[i]);
  876. }
  877. return 0;
  878. }
  879. void install_commands() {
  880. shell_install_command("cd", shell_cmd_cd, "change directory");
  881. shell_install_command("exit", shell_cmd_exit, "exit the shell");
  882. shell_install_command("export", shell_cmd_export, "set environment variables");
  883. shell_install_command("help", shell_cmd_help, "display this help text");
  884. shell_install_command("history", shell_cmd_history, "list command history");
  885. shell_install_command("set", shell_cmd_set, "enable special terminal options");
  886. }