main.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright notice, this
  9. * list of conditions and the following disclaimer.
  10. *
  11. * 2. Redistributions in binary form must reproduce the above copyright notice,
  12. * this list of conditions and the following disclaimer in the documentation
  13. * and/or other materials provided with the distribution.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  16. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  17. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  18. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  19. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  20. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  21. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  22. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  23. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  24. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. #include "GlobalState.h"
  27. #include "Parser.h"
  28. #include <AK/FileSystemPath.h>
  29. #include <AK/Function.h>
  30. #include <AK/ScopeGuard.h>
  31. #include <AK/StringBuilder.h>
  32. #include <LibCore/DirIterator.h>
  33. #include <LibCore/ElapsedTimer.h>
  34. #include <LibCore/File.h>
  35. #include <LibLine/Editor.h>
  36. #include <errno.h>
  37. #include <fcntl.h>
  38. #include <pwd.h>
  39. #include <signal.h>
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. #include <string.h>
  43. #include <sys/mman.h>
  44. #include <sys/stat.h>
  45. #include <sys/utsname.h>
  46. #include <sys/wait.h>
  47. #include <termios.h>
  48. #include <unistd.h>
  49. //#define SH_DEBUG
  50. GlobalState g;
  51. static Line::Editor editor { Line::Configuration { Line::Configuration::UnescapedSpaces } };
  52. static int run_command(const String&);
  53. void cache_path();
  54. static String prompt()
  55. {
  56. auto* ps1 = getenv("PROMPT");
  57. if (!ps1) {
  58. if (g.uid == 0)
  59. return "# ";
  60. StringBuilder builder;
  61. builder.appendf("\033]0;%s@%s:%s\007", g.username.characters(), g.hostname, g.cwd.characters());
  62. builder.appendf("\033[31;1m%s\033[0m@\033[37;1m%s\033[0m:\033[32;1m%s\033[0m$> ", g.username.characters(), g.hostname, g.cwd.characters());
  63. return builder.to_string();
  64. }
  65. StringBuilder builder;
  66. for (char* ptr = ps1; *ptr; ++ptr) {
  67. if (*ptr == '\\') {
  68. ++ptr;
  69. if (!*ptr)
  70. break;
  71. switch (*ptr) {
  72. case 'X':
  73. builder.append("\033]0;");
  74. break;
  75. case 'a':
  76. builder.append(0x07);
  77. break;
  78. case 'e':
  79. builder.append(0x1b);
  80. break;
  81. case 'u':
  82. builder.append(g.username);
  83. break;
  84. case 'h':
  85. builder.append(g.hostname);
  86. break;
  87. case 'w': {
  88. String home_path = getenv("HOME");
  89. if (g.cwd.starts_with(home_path)) {
  90. builder.append('~');
  91. builder.append(g.cwd.substring_view(home_path.length(), g.cwd.length() - home_path.length()));
  92. } else {
  93. builder.append(g.cwd);
  94. }
  95. break;
  96. }
  97. case 'p':
  98. builder.append(g.uid == 0 ? '#' : '$');
  99. break;
  100. }
  101. continue;
  102. }
  103. builder.append(*ptr);
  104. }
  105. return builder.to_string();
  106. }
  107. static int sh_pwd(int, const char**)
  108. {
  109. printf("%s\n", g.cwd.characters());
  110. return 0;
  111. }
  112. static int sh_exit(int, const char**)
  113. {
  114. printf("Good-bye!\n");
  115. exit(0);
  116. return 0;
  117. }
  118. static int sh_export(int argc, const char** argv)
  119. {
  120. if (argc == 1) {
  121. for (int i = 0; environ[i]; ++i)
  122. puts(environ[i]);
  123. return 0;
  124. }
  125. auto parts = String(argv[1]).split('=');
  126. if (parts.size() != 2) {
  127. fprintf(stderr, "usage: export variable=value\n");
  128. return 1;
  129. }
  130. int setenv_return = setenv(parts[0].characters(), parts[1].characters(), 1);
  131. if (setenv_return == 0 && parts[0] == "PATH")
  132. cache_path();
  133. return setenv_return;
  134. }
  135. static int sh_unset(int argc, const char** argv)
  136. {
  137. if (argc != 2) {
  138. fprintf(stderr, "usage: unset variable\n");
  139. return 1;
  140. }
  141. unsetenv(argv[1]);
  142. return 0;
  143. }
  144. static String expand_tilde(const String& expression)
  145. {
  146. ASSERT(expression.starts_with('~'));
  147. StringBuilder login_name;
  148. size_t first_slash_index = expression.length();
  149. for (size_t i = 1; i < expression.length(); ++i) {
  150. if (expression[i] == '/') {
  151. first_slash_index = i;
  152. break;
  153. }
  154. login_name.append(expression[i]);
  155. }
  156. StringBuilder path;
  157. for (size_t i = first_slash_index; i < expression.length(); ++i)
  158. path.append(expression[i]);
  159. if (login_name.is_empty()) {
  160. const char* home = getenv("HOME");
  161. if (!home) {
  162. auto passwd = getpwuid(getuid());
  163. ASSERT(passwd && passwd->pw_dir);
  164. return String::format("%s/%s", passwd->pw_dir, path.to_string().characters());
  165. }
  166. return String::format("%s/%s", home, path.to_string().characters());
  167. }
  168. auto passwd = getpwnam(login_name.to_string().characters());
  169. if (!passwd)
  170. return expression;
  171. ASSERT(passwd->pw_dir);
  172. return String::format("%s/%s", passwd->pw_dir, path.to_string().characters());
  173. }
  174. static int sh_cd(int argc, const char** argv)
  175. {
  176. if (argc > 2) {
  177. fprintf(stderr, "cd: too many arguments\n");
  178. return 1;
  179. }
  180. String new_path;
  181. if (argc == 1) {
  182. new_path = g.home;
  183. if (g.cd_history.is_empty() || g.cd_history.last() != g.home)
  184. g.cd_history.enqueue(g.home);
  185. } else {
  186. if (g.cd_history.is_empty() || g.cd_history.last() != argv[1])
  187. g.cd_history.enqueue(argv[1]);
  188. if (strcmp(argv[1], "-") == 0) {
  189. char* oldpwd = getenv("OLDPWD");
  190. if (oldpwd == nullptr)
  191. return 1;
  192. new_path = oldpwd;
  193. } else if (argv[1][0] == '/') {
  194. new_path = argv[1];
  195. } else {
  196. StringBuilder builder;
  197. builder.append(g.cwd);
  198. builder.append('/');
  199. builder.append(argv[1]);
  200. new_path = builder.to_string();
  201. }
  202. }
  203. FileSystemPath canonical_path(new_path);
  204. if (!canonical_path.is_valid()) {
  205. printf("FileSystemPath failed to canonicalize '%s'\n", new_path.characters());
  206. return 1;
  207. }
  208. const char* path = canonical_path.string().characters();
  209. struct stat st;
  210. int rc = stat(path, &st);
  211. if (rc < 0) {
  212. printf("stat(%s) failed: %s\n", path, strerror(errno));
  213. return 1;
  214. }
  215. if (!S_ISDIR(st.st_mode)) {
  216. printf("Not a directory: %s\n", path);
  217. return 1;
  218. }
  219. rc = chdir(path);
  220. if (rc < 0) {
  221. printf("chdir(%s) failed: %s\n", path, strerror(errno));
  222. return 1;
  223. }
  224. setenv("OLDPWD", g.cwd.characters(), 1);
  225. g.cwd = canonical_path.string();
  226. setenv("PWD", g.cwd.characters(), 1);
  227. return 0;
  228. }
  229. static int sh_cdh(int argc, const char** argv)
  230. {
  231. if (argc > 2) {
  232. fprintf(stderr, "usage: cdh [index]\n");
  233. return 1;
  234. }
  235. if (argc == 1) {
  236. if (g.cd_history.size() == 0) {
  237. printf("cdh: no history available\n");
  238. return 0;
  239. }
  240. for (int i = g.cd_history.size() - 1; i >= 0; --i)
  241. printf("%lu: %s\n", g.cd_history.size() - i, g.cd_history.at(i).characters());
  242. return 0;
  243. }
  244. bool ok;
  245. size_t cd_history_index = String(argv[1]).to_uint(ok);
  246. if (!ok || cd_history_index < 1 || cd_history_index > g.cd_history.size()) {
  247. fprintf(stderr, "usage: cdh [index]\n");
  248. return 1;
  249. }
  250. const char* path = g.cd_history.at(g.cd_history.size() - cd_history_index).characters();
  251. const char* cd_args[] = { "cd", path };
  252. return sh_cd(2, cd_args);
  253. }
  254. static int sh_history(int, const char**)
  255. {
  256. for (size_t i = 0; i < editor.history().size(); ++i) {
  257. printf("%6zu %s\n", i, editor.history()[i].characters());
  258. }
  259. return 0;
  260. }
  261. static int sh_time(int argc, const char** argv)
  262. {
  263. if (argc == 1) {
  264. printf("usage: time <command>\n");
  265. return 0;
  266. }
  267. StringBuilder builder;
  268. for (int i = 1; i < argc; ++i) {
  269. builder.append(argv[i]);
  270. if (i != argc - 1)
  271. builder.append(' ');
  272. }
  273. Core::ElapsedTimer timer;
  274. timer.start();
  275. int exit_code = run_command(builder.to_string());
  276. printf("Time: %d ms\n", timer.elapsed());
  277. return exit_code;
  278. }
  279. static int sh_umask(int argc, const char** argv)
  280. {
  281. if (argc == 1) {
  282. mode_t old_mask = umask(0);
  283. printf("%#o\n", old_mask);
  284. umask(old_mask);
  285. return 0;
  286. }
  287. if (argc == 2) {
  288. unsigned mask;
  289. int matches = sscanf(argv[1], "%o", &mask);
  290. if (matches == 1) {
  291. umask(mask);
  292. return 0;
  293. }
  294. }
  295. printf("usage: umask <octal-mask>\n");
  296. return 0;
  297. }
  298. static int sh_popd(int argc, const char** argv)
  299. {
  300. if (g.directory_stack.size() <= 1) {
  301. fprintf(stderr, "Shell: popd: directory stack empty\n");
  302. return 1;
  303. }
  304. bool should_switch = true;
  305. String path = g.directory_stack.take_last();
  306. // When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory.
  307. if (argc == 1) {
  308. int rc = chdir(path.characters());
  309. if (rc < 0) {
  310. fprintf(stderr, "chdir(%s) failed: %s", path.characters(), strerror(errno));
  311. return 1;
  312. }
  313. g.cwd = path;
  314. return 0;
  315. }
  316. for (int i = 1; i < argc; i++) {
  317. const char* arg = argv[i];
  318. if (!strcmp(arg, "-n")) {
  319. should_switch = false;
  320. }
  321. }
  322. FileSystemPath canonical_path(path.characters());
  323. if (!canonical_path.is_valid()) {
  324. fprintf(stderr, "FileSystemPath failed to canonicalize '%s'\n", path.characters());
  325. return 1;
  326. }
  327. const char* real_path = canonical_path.string().characters();
  328. struct stat st;
  329. int rc = stat(real_path, &st);
  330. if (rc < 0) {
  331. fprintf(stderr, "stat(%s) failed: %s\n", real_path, strerror(errno));
  332. return 1;
  333. }
  334. if (!S_ISDIR(st.st_mode)) {
  335. fprintf(stderr, "Not a directory: %s\n", real_path);
  336. return 1;
  337. }
  338. if (should_switch) {
  339. int rc = chdir(real_path);
  340. if (rc < 0) {
  341. fprintf(stderr, "chdir(%s) failed: %s\n", real_path, strerror(errno));
  342. return 1;
  343. }
  344. g.cwd = canonical_path.string();
  345. }
  346. return 0;
  347. }
  348. static int sh_pushd(int argc, const char** argv)
  349. {
  350. StringBuilder path_builder;
  351. bool should_switch = true;
  352. // From the BASH reference manual: https://www.gnu.org/software/bash/manual/html_node/Directory-Stack-Builtins.html
  353. // With no arguments, pushd exchanges the top two directories and makes the new top the current directory.
  354. if (argc == 1) {
  355. if (g.directory_stack.size() < 2) {
  356. fprintf(stderr, "pushd: no other directory\n");
  357. return 1;
  358. }
  359. String dir1 = g.directory_stack.take_first();
  360. String dir2 = g.directory_stack.take_first();
  361. g.directory_stack.insert(0, dir2);
  362. g.directory_stack.insert(1, dir1);
  363. int rc = chdir(dir2.characters());
  364. if (rc < 0) {
  365. fprintf(stderr, "chdir(%s) failed: %s", dir2.characters(), strerror(errno));
  366. return 1;
  367. }
  368. g.cwd = dir2;
  369. return 0;
  370. }
  371. // Let's assume the user's typed in 'pushd <dir>'
  372. if (argc == 2) {
  373. g.directory_stack.append(g.cwd.characters());
  374. if (argv[1][0] == '/') {
  375. path_builder.append(argv[1]);
  376. } else {
  377. path_builder.appendf("%s/%s", g.cwd.characters(), argv[1]);
  378. }
  379. } else if (argc == 3) {
  380. g.directory_stack.append(g.cwd.characters());
  381. for (int i = 1; i < argc; i++) {
  382. const char* arg = argv[i];
  383. if (arg[0] != '-') {
  384. if (arg[0] == '/') {
  385. path_builder.append(arg);
  386. } else
  387. path_builder.appendf("%s/%s", g.cwd.characters(), arg);
  388. }
  389. if (!strcmp(arg, "-n"))
  390. should_switch = false;
  391. }
  392. }
  393. FileSystemPath canonical_path(path_builder.to_string());
  394. if (!canonical_path.is_valid()) {
  395. fprintf(stderr, "FileSystemPath failed to canonicalize '%s'\n", path_builder.to_string().characters());
  396. return 1;
  397. }
  398. const char* real_path = canonical_path.string().characters();
  399. struct stat st;
  400. int rc = stat(real_path, &st);
  401. if (rc < 0) {
  402. fprintf(stderr, "stat(%s) failed: %s\n", real_path, strerror(errno));
  403. return 1;
  404. }
  405. if (!S_ISDIR(st.st_mode)) {
  406. fprintf(stderr, "Not a directory: %s\n", real_path);
  407. return 1;
  408. }
  409. if (should_switch) {
  410. int rc = chdir(real_path);
  411. if (rc < 0) {
  412. fprintf(stderr, "chdir(%s) failed: %s\n", real_path, strerror(errno));
  413. return 1;
  414. }
  415. g.cwd = canonical_path.string();
  416. }
  417. return 0;
  418. }
  419. static int sh_dirs(int argc, const char** argv)
  420. {
  421. // The first directory in the stack is ALWAYS the current directory
  422. g.directory_stack.at(0) = g.cwd.characters();
  423. if (argc == 1) {
  424. for (String dir : g.directory_stack)
  425. printf("%s ", dir.characters());
  426. printf("\n");
  427. return 0;
  428. }
  429. bool printed = false;
  430. for (int i = 0; i < argc; i++) {
  431. const char* arg = argv[i];
  432. if (!strcmp(arg, "-c")) {
  433. for (size_t i = 1; i < g.directory_stack.size(); i++)
  434. g.directory_stack.remove(i);
  435. printed = true;
  436. continue;
  437. }
  438. if (!strcmp(arg, "-p") && !printed) {
  439. for (auto& directory : g.directory_stack)
  440. printf("%s\n", directory.characters());
  441. printed = true;
  442. continue;
  443. }
  444. if (!strcmp(arg, "-v") && !printed) {
  445. int idx = 0;
  446. for (auto& directory : g.directory_stack) {
  447. printf("%d %s\n", idx++, directory.characters());
  448. }
  449. printed = true;
  450. continue;
  451. }
  452. }
  453. return 0;
  454. }
  455. static bool handle_builtin(int argc, const char** argv, int& retval)
  456. {
  457. if (argc == 0)
  458. return false;
  459. if (!strcmp(argv[0], "cd")) {
  460. retval = sh_cd(argc, argv);
  461. return true;
  462. }
  463. if (!strcmp(argv[0], "cdh")) {
  464. retval = sh_cdh(argc, argv);
  465. return true;
  466. }
  467. if (!strcmp(argv[0], "pwd")) {
  468. retval = sh_pwd(argc, argv);
  469. return true;
  470. }
  471. if (!strcmp(argv[0], "exit")) {
  472. retval = sh_exit(argc, argv);
  473. return true;
  474. }
  475. if (!strcmp(argv[0], "export")) {
  476. retval = sh_export(argc, argv);
  477. return true;
  478. }
  479. if (!strcmp(argv[0], "unset")) {
  480. retval = sh_unset(argc, argv);
  481. return true;
  482. }
  483. if (!strcmp(argv[0], "history")) {
  484. retval = sh_history(argc, argv);
  485. return true;
  486. }
  487. if (!strcmp(argv[0], "umask")) {
  488. retval = sh_umask(argc, argv);
  489. return true;
  490. }
  491. if (!strcmp(argv[0], "dirs")) {
  492. retval = sh_dirs(argc, argv);
  493. return true;
  494. }
  495. if (!strcmp(argv[0], "pushd")) {
  496. retval = sh_pushd(argc, argv);
  497. return true;
  498. }
  499. if (!strcmp(argv[0], "popd")) {
  500. retval = sh_popd(argc, argv);
  501. return true;
  502. }
  503. if (!strcmp(argv[0], "time")) {
  504. retval = sh_time(argc, argv);
  505. return true;
  506. }
  507. return false;
  508. }
  509. class FileDescriptionCollector {
  510. public:
  511. FileDescriptionCollector() {}
  512. ~FileDescriptionCollector() { collect(); }
  513. void collect()
  514. {
  515. for (auto fd : m_fds)
  516. close(fd);
  517. m_fds.clear();
  518. }
  519. void add(int fd) { m_fds.append(fd); }
  520. private:
  521. Vector<int, 32> m_fds;
  522. };
  523. class CommandTimer {
  524. public:
  525. explicit CommandTimer(const String& command)
  526. : m_command(command)
  527. {
  528. m_timer.start();
  529. }
  530. ~CommandTimer()
  531. {
  532. dbg() << "Command \"" << m_command << "\" finished in " << m_timer.elapsed() << " ms";
  533. }
  534. private:
  535. Core::ElapsedTimer m_timer;
  536. String m_command;
  537. };
  538. static bool is_glob(const StringView& s)
  539. {
  540. for (size_t i = 0; i < s.length(); i++) {
  541. char c = s.characters_without_null_termination()[i];
  542. if (c == '*' || c == '?')
  543. return true;
  544. }
  545. return false;
  546. }
  547. static Vector<StringView> split_path(const StringView& path)
  548. {
  549. Vector<StringView> parts;
  550. size_t substart = 0;
  551. for (size_t i = 0; i < path.length(); i++) {
  552. char ch = path.characters_without_null_termination()[i];
  553. if (ch != '/')
  554. continue;
  555. size_t sublen = i - substart;
  556. if (sublen != 0)
  557. parts.append(path.substring_view(substart, sublen));
  558. parts.append(path.substring_view(i, 1));
  559. substart = i + 1;
  560. }
  561. size_t taillen = path.length() - substart;
  562. if (taillen != 0)
  563. parts.append(path.substring_view(substart, taillen));
  564. return parts;
  565. }
  566. static Vector<String> expand_globs(const StringView& path, const StringView& base)
  567. {
  568. auto parts = split_path(path);
  569. StringBuilder builder;
  570. builder.append(base);
  571. Vector<String> res;
  572. for (size_t i = 0; i < parts.size(); ++i) {
  573. auto& part = parts[i];
  574. if (!is_glob(part)) {
  575. builder.append(part);
  576. continue;
  577. }
  578. // Found a glob.
  579. String new_base = builder.to_string();
  580. StringView new_base_v = new_base;
  581. if (new_base_v.is_empty())
  582. new_base_v = ".";
  583. Core::DirIterator di(new_base_v, Core::DirIterator::SkipParentAndBaseDir);
  584. if (di.has_error()) {
  585. return res;
  586. }
  587. while (di.has_next()) {
  588. String name = di.next_path();
  589. // Dotfiles have to be explicitly requested
  590. if (name[0] == '.' && part[0] != '.')
  591. continue;
  592. if (name.matches(part, CaseSensitivity::CaseSensitive)) {
  593. StringBuilder nested_base;
  594. nested_base.append(new_base);
  595. nested_base.append(name);
  596. StringView remaining_path = path.substring_view_starting_after_substring(part);
  597. Vector<String> nested_res = expand_globs(remaining_path, nested_base.to_string());
  598. for (auto& s : nested_res)
  599. res.append(s);
  600. }
  601. }
  602. return res;
  603. }
  604. // Found no globs.
  605. String new_path = builder.to_string();
  606. if (access(new_path.characters(), F_OK) == 0)
  607. res.append(new_path);
  608. return res;
  609. }
  610. static Vector<String> expand_parameters(const StringView& param)
  611. {
  612. if (!param.starts_with('$'))
  613. return { param };
  614. String variable_name = String(param.substring_view(1, param.length() - 1));
  615. if (variable_name == "?")
  616. return { String::number(g.last_return_code) };
  617. else if (variable_name == "$")
  618. return { String::number(getpid()) };
  619. char* env_value = getenv(variable_name.characters());
  620. if (env_value == nullptr)
  621. return { "" };
  622. Vector<String> res;
  623. String str_env_value = String(env_value);
  624. const auto& split_text = str_env_value.split_view(' ');
  625. for (auto& part : split_text)
  626. res.append(part);
  627. return res;
  628. }
  629. static Vector<String> process_arguments(const Vector<String>& args)
  630. {
  631. Vector<String> argv_string;
  632. for (auto& arg : args) {
  633. // This will return the text passed in if it wasn't a variable
  634. // This lets us just loop over its values
  635. auto expanded_parameters = expand_parameters(arg);
  636. for (auto& exp_arg : expanded_parameters) {
  637. if (exp_arg.starts_with('~'))
  638. exp_arg = expand_tilde(exp_arg);
  639. auto expanded_globs = expand_globs(exp_arg, "");
  640. for (auto& path : expanded_globs)
  641. argv_string.append(path);
  642. if (expanded_globs.is_empty())
  643. argv_string.append(exp_arg);
  644. }
  645. }
  646. return argv_string;
  647. }
  648. static int run_command(const String& cmd)
  649. {
  650. if (cmd.is_empty())
  651. return 0;
  652. if (cmd.starts_with("#"))
  653. return 0;
  654. auto commands = Parser(cmd).parse();
  655. #ifdef SH_DEBUG
  656. for (auto& command : commands) {
  657. for (size_t i = 0; i < command.subcommands.size(); ++i) {
  658. for (size_t j = 0; j < i; ++j)
  659. dbgprintf(" ");
  660. for (auto& arg : command.subcommands[i].args) {
  661. dbgprintf("<%s> ", arg.characters());
  662. }
  663. dbgprintf("\n");
  664. for (auto& redirecton : command.subcommands[i].redirections) {
  665. for (size_t j = 0; j < i; ++j)
  666. dbgprintf(" ");
  667. dbgprintf(" ");
  668. switch (redirecton.type) {
  669. case Redirection::Pipe:
  670. dbgprintf("Pipe\n");
  671. break;
  672. case Redirection::FileRead:
  673. dbgprintf("fd:%d = FileRead: %s\n", redirecton.fd, redirecton.path.characters());
  674. break;
  675. case Redirection::FileWrite:
  676. dbgprintf("fd:%d = FileWrite: %s\n", redirecton.fd, redirecton.path.characters());
  677. break;
  678. case Redirection::FileWriteAppend:
  679. dbgprintf("fd:%d = FileWriteAppend: %s\n", redirecton.fd, redirecton.path.characters());
  680. break;
  681. default:
  682. break;
  683. }
  684. }
  685. }
  686. dbgprintf("\n");
  687. }
  688. #endif
  689. struct termios trm;
  690. tcgetattr(0, &trm);
  691. struct SpawnedProcess {
  692. String name;
  693. pid_t pid;
  694. };
  695. int return_value = 0;
  696. for (auto& command : commands) {
  697. if (command.subcommands.is_empty())
  698. continue;
  699. FileDescriptionCollector fds;
  700. for (size_t i = 0; i < command.subcommands.size(); ++i) {
  701. auto& subcommand = command.subcommands[i];
  702. for (auto& redirection : subcommand.redirections) {
  703. switch (redirection.type) {
  704. case Redirection::Pipe: {
  705. int pipefd[2];
  706. int rc = pipe(pipefd);
  707. if (rc < 0) {
  708. perror("pipe");
  709. return 1;
  710. }
  711. subcommand.rewirings.append({ STDOUT_FILENO, pipefd[1] });
  712. auto& next_command = command.subcommands[i + 1];
  713. next_command.rewirings.append({ STDIN_FILENO, pipefd[0] });
  714. fds.add(pipefd[0]);
  715. fds.add(pipefd[1]);
  716. break;
  717. }
  718. case Redirection::FileWriteAppend: {
  719. int fd = open(redirection.path.characters(), O_WRONLY | O_CREAT | O_APPEND, 0666);
  720. if (fd < 0) {
  721. perror("open");
  722. return 1;
  723. }
  724. subcommand.rewirings.append({ redirection.fd, fd });
  725. fds.add(fd);
  726. break;
  727. }
  728. case Redirection::FileWrite: {
  729. int fd = open(redirection.path.characters(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
  730. if (fd < 0) {
  731. perror("open");
  732. return 1;
  733. }
  734. subcommand.rewirings.append({ redirection.fd, fd });
  735. fds.add(fd);
  736. break;
  737. }
  738. case Redirection::FileRead: {
  739. int fd = open(redirection.path.characters(), O_RDONLY);
  740. if (fd < 0) {
  741. perror("open");
  742. return 1;
  743. }
  744. subcommand.rewirings.append({ redirection.fd, fd });
  745. fds.add(fd);
  746. break;
  747. }
  748. }
  749. }
  750. }
  751. Vector<SpawnedProcess> children;
  752. CommandTimer timer(cmd);
  753. for (size_t i = 0; i < command.subcommands.size(); ++i) {
  754. auto& subcommand = command.subcommands[i];
  755. Vector<String> argv_string = process_arguments(subcommand.args);
  756. Vector<const char*> argv;
  757. argv.ensure_capacity(argv_string.size());
  758. for (const auto& s : argv_string) {
  759. argv.append(s.characters());
  760. }
  761. argv.append(nullptr);
  762. #ifdef SH_DEBUG
  763. for (auto& arg : argv) {
  764. dbgprintf("<%s> ", arg);
  765. }
  766. dbgprintf("\n");
  767. #endif
  768. int retval = 0;
  769. if (handle_builtin(argv.size() - 1, argv.data(), retval))
  770. return retval;
  771. pid_t child = fork();
  772. if (!child) {
  773. setpgid(0, 0);
  774. tcsetpgrp(0, getpid());
  775. tcsetattr(0, TCSANOW, &g.default_termios);
  776. for (auto& rewiring : subcommand.rewirings) {
  777. #ifdef SH_DEBUG
  778. dbgprintf("in %s<%d>, dup2(%d, %d)\n", argv[0], getpid(), rewiring.rewire_fd, rewiring.fd);
  779. #endif
  780. int rc = dup2(rewiring.rewire_fd, rewiring.fd);
  781. if (rc < 0) {
  782. perror("dup2");
  783. return 1;
  784. }
  785. }
  786. fds.collect();
  787. int rc = execvp(argv[0], const_cast<char* const*>(argv.data()));
  788. if (rc < 0) {
  789. if (errno == ENOENT) {
  790. int shebang_fd = open(argv[0], O_RDONLY);
  791. auto close_argv = ScopeGuard([shebang_fd]() { if (shebang_fd >= 0) close(shebang_fd); });
  792. char shebang[256] {};
  793. ssize_t num_read = -1;
  794. if ((shebang_fd >= 0) && ((num_read = read(shebang_fd, shebang, sizeof(shebang))) >= 2) && (StringView(shebang).starts_with("#!"))) {
  795. StringView shebang_path_view(&shebang[2], num_read - 2);
  796. Optional<size_t> newline_pos = shebang_path_view.find_first_of("\n\r");
  797. shebang[newline_pos.has_value() ? newline_pos.value() : num_read] = '\0';
  798. fprintf(stderr, "%s: Invalid interpreter \"%s\": %s\n", argv[0], &shebang[2], strerror(ENOENT));
  799. } else
  800. fprintf(stderr, "%s: Command not found.\n", argv[0]);
  801. } else {
  802. struct stat st;
  803. if (stat(argv[0], &st) == 0 && S_ISDIR(st.st_mode)) {
  804. fprintf(stderr, "Shell: %s: Is a directory\n", argv[0]);
  805. _exit(126);
  806. }
  807. fprintf(stderr, "execvp(%s): %s\n", argv[0], strerror(errno));
  808. }
  809. _exit(126);
  810. }
  811. ASSERT_NOT_REACHED();
  812. }
  813. children.append({ argv[0], child });
  814. }
  815. #ifdef SH_DEBUG
  816. dbgprintf("Closing fds in shell process:\n");
  817. #endif
  818. fds.collect();
  819. #ifdef SH_DEBUG
  820. dbgprintf("Now we gotta wait on children:\n");
  821. for (auto& child : children)
  822. dbgprintf(" %d (%s)\n", child.pid, child.name.characters());
  823. #endif
  824. int wstatus = 0;
  825. for (size_t i = 0; i < children.size(); ++i) {
  826. auto& child = children[i];
  827. do {
  828. int rc = waitpid(child.pid, &wstatus, 0);
  829. if (rc < 0 && errno != EINTR) {
  830. if (errno != ECHILD)
  831. perror("waitpid");
  832. break;
  833. }
  834. if (WIFEXITED(wstatus)) {
  835. if (WEXITSTATUS(wstatus) != 0)
  836. dbg() << "Shell: " << child.name << ":" << child.pid << " exited with status " << WEXITSTATUS(wstatus);
  837. if (i == 0)
  838. return_value = WEXITSTATUS(wstatus);
  839. } else if (WIFSTOPPED(wstatus)) {
  840. fprintf(stderr, "Shell: %s(%d) %s\n", child.name.characters(), child.pid, strsignal(WSTOPSIG(wstatus)));
  841. } else {
  842. if (WIFSIGNALED(wstatus)) {
  843. printf("Shell: %s(%d) exited due to signal '%s'\n", child.name.characters(), child.pid, strsignal(WTERMSIG(wstatus)));
  844. } else {
  845. printf("Shell: %s(%d) exited abnormally\n", child.name.characters(), child.pid);
  846. }
  847. }
  848. } while (errno == EINTR);
  849. }
  850. }
  851. g.last_return_code = return_value;
  852. // FIXME: Should I really have to tcsetpgrp() after my child has exited?
  853. // Is the terminal controlling pgrp really still the PGID of the dead process?
  854. tcsetpgrp(0, getpid());
  855. tcsetattr(0, TCSANOW, &trm);
  856. return return_value;
  857. }
  858. static String get_history_path()
  859. {
  860. StringBuilder builder;
  861. builder.append(g.home);
  862. builder.append("/.history");
  863. return builder.to_string();
  864. }
  865. void load_history()
  866. {
  867. auto history_file = Core::File::construct(get_history_path());
  868. if (!history_file->open(Core::IODevice::ReadOnly))
  869. return;
  870. while (history_file->can_read_line()) {
  871. auto b = history_file->read_line(1024);
  872. // skip the newline and terminating bytes
  873. editor.add_to_history(String(reinterpret_cast<const char*>(b.data()), b.size() - 2));
  874. }
  875. }
  876. void save_history()
  877. {
  878. auto file_or_error = Core::File::open(get_history_path(), Core::IODevice::WriteOnly, 0600);
  879. if (file_or_error.is_error())
  880. return;
  881. auto& file = *file_or_error.value();
  882. for (const auto& line : editor.history()) {
  883. file.write(line);
  884. file.write("\n");
  885. }
  886. }
  887. String escape_token(const String& token)
  888. {
  889. StringBuilder builder;
  890. for (auto c : token) {
  891. switch (c) {
  892. case '\'':
  893. case '"':
  894. case '$':
  895. case '|':
  896. case '>':
  897. case '<':
  898. case '&':
  899. case '\\':
  900. case ' ':
  901. builder.append('\\');
  902. break;
  903. default:
  904. break;
  905. }
  906. builder.append(c);
  907. }
  908. return builder.build();
  909. }
  910. String unescape_token(const String& token)
  911. {
  912. StringBuilder builder;
  913. enum {
  914. Free,
  915. Escaped
  916. } state { Free };
  917. for (auto c : token) {
  918. switch (state) {
  919. case Escaped:
  920. builder.append(c);
  921. state = Free;
  922. break;
  923. case Free:
  924. if (c == '\\')
  925. state = Escaped;
  926. else
  927. builder.append(c);
  928. break;
  929. }
  930. }
  931. if (state == Escaped)
  932. builder.append('\\');
  933. return builder.build();
  934. }
  935. Vector<String, 256> cached_path;
  936. void cache_path()
  937. {
  938. if (!cached_path.is_empty())
  939. cached_path.clear_with_capacity();
  940. String path = getenv("PATH");
  941. if (path.is_empty())
  942. return;
  943. auto directories = path.split(':');
  944. for (const auto& directory : directories) {
  945. Core::DirIterator programs(directory.characters(), Core::DirIterator::SkipDots);
  946. while (programs.has_next()) {
  947. auto program = programs.next_path();
  948. String program_path = String::format("%s/%s", directory.characters(), program.characters());
  949. if (access(program_path.characters(), X_OK) == 0)
  950. cached_path.append(escape_token(program.characters()));
  951. }
  952. }
  953. quick_sort(cached_path);
  954. }
  955. int main(int argc, char** argv)
  956. {
  957. if (pledge("stdio rpath wpath cpath proc exec tty", nullptr) < 0) {
  958. perror("pledge");
  959. return 1;
  960. }
  961. g.uid = getuid();
  962. tcsetpgrp(0, getpgrp());
  963. editor.initialize();
  964. g.termios = editor.termios();
  965. g.default_termios = editor.default_termios();
  966. editor.on_tab_complete_first_token = [&](const String& token_to_complete) -> Vector<Line::CompletionSuggestion> {
  967. auto token = unescape_token(token_to_complete);
  968. auto match = binary_search(cached_path.data(), cached_path.size(), token, [](const String& token, const String& program) -> int {
  969. return strncmp(token.characters(), program.characters(), token.length());
  970. });
  971. if (!match) {
  972. // There is no executable in the $PATH starting with $token
  973. // Suggest local executables and directories
  974. String path;
  975. Vector<Line::CompletionSuggestion> local_suggestions;
  976. bool suggest_executables = true;
  977. ssize_t last_slash = token.length() - 1;
  978. while (last_slash >= 0 && token[last_slash] != '/')
  979. --last_slash;
  980. if (last_slash >= 0) {
  981. // Split on the last slash. We'll use the first part as the directory
  982. // to search and the second part as the token to complete.
  983. path = token.substring(0, last_slash + 1);
  984. if (path[0] != '/')
  985. path = String::format("%s/%s", g.cwd.characters(), path.characters());
  986. path = canonicalized_path(path);
  987. token = token.substring(last_slash + 1, token.length() - last_slash - 1);
  988. } else {
  989. // We have no slashes, so the directory to search is the current
  990. // directory and the token to complete is just the original token.
  991. // In this case, do not suggest executables but directories only.
  992. path = g.cwd;
  993. suggest_executables = false;
  994. }
  995. // the invariant part of the token is actually just the last segment
  996. // e.g. in `cd /foo/bar', 'bar' is the invariant
  997. // since we are not suggesting anything starting with
  998. // `/foo/', but rather just `bar...'
  999. editor.suggest(escape_token(token).length(), 0);
  1000. // only suggest dot-files if path starts with a dot
  1001. Core::DirIterator files(path,
  1002. token.starts_with('.') ? Core::DirIterator::NoFlags : Core::DirIterator::SkipDots);
  1003. while (files.has_next()) {
  1004. auto file = files.next_path();
  1005. // manually skip `.' and `..'
  1006. if (file == "." || file == "..")
  1007. continue;
  1008. auto trivia = " ";
  1009. if (file.starts_with(token)) {
  1010. String file_path = String::format("%s/%s", path.characters(), file.characters());
  1011. struct stat program_status;
  1012. int stat_error = stat(file_path.characters(), &program_status);
  1013. if (stat_error)
  1014. continue;
  1015. if (access(file_path.characters(), X_OK) != 0)
  1016. continue;
  1017. if (S_ISDIR(program_status.st_mode)) {
  1018. if (!suggest_executables)
  1019. continue;
  1020. else
  1021. trivia = "/";
  1022. }
  1023. local_suggestions.append({ escape_token(file), trivia });
  1024. }
  1025. }
  1026. return local_suggestions;
  1027. }
  1028. String completion = *match;
  1029. Vector<Line::CompletionSuggestion> suggestions;
  1030. // Now that we have a program name starting with our token, we look at
  1031. // other program names starting with our token and cut off any mismatching
  1032. // characters.
  1033. int index = match - cached_path.data();
  1034. for (int i = index - 1; i >= 0 && cached_path[i].starts_with(token); --i) {
  1035. suggestions.append({ cached_path[i], " " });
  1036. }
  1037. for (size_t i = index + 1; i < cached_path.size() && cached_path[i].starts_with(token); ++i) {
  1038. suggestions.append({ cached_path[i], " " });
  1039. }
  1040. suggestions.append({ cached_path[index], " " });
  1041. editor.suggest(escape_token(token).length(), 0);
  1042. return suggestions;
  1043. };
  1044. editor.on_tab_complete_other_token = [&](const String& token_to_complete) -> Vector<Line::CompletionSuggestion> {
  1045. auto token = unescape_token(token_to_complete);
  1046. String path;
  1047. Vector<Line::CompletionSuggestion> suggestions;
  1048. ssize_t last_slash = token.length() - 1;
  1049. while (last_slash >= 0 && token[last_slash] != '/')
  1050. --last_slash;
  1051. if (last_slash >= 0) {
  1052. // Split on the last slash. We'll use the first part as the directory
  1053. // to search and the second part as the token to complete.
  1054. path = token.substring(0, last_slash + 1);
  1055. if (path[0] != '/')
  1056. path = String::format("%s/%s", g.cwd.characters(), path.characters());
  1057. path = canonicalized_path(path);
  1058. token = token.substring(last_slash + 1, token.length() - last_slash - 1);
  1059. } else {
  1060. // We have no slashes, so the directory to search is the current
  1061. // directory and the token to complete is just the original token.
  1062. path = g.cwd;
  1063. }
  1064. // the invariant part of the token is actually just the last segment
  1065. // e.g. in `cd /foo/bar', 'bar' is the invariant
  1066. // since we are not suggesting anything starting with
  1067. // `/foo/', but rather just `bar...'
  1068. editor.suggest(escape_token(token).length(), 0);
  1069. // only suggest dot-files if path starts with a dot
  1070. Core::DirIterator files(path,
  1071. token.starts_with('.') ? Core::DirIterator::NoFlags : Core::DirIterator::SkipDots);
  1072. while (files.has_next()) {
  1073. auto file = files.next_path();
  1074. // manually skip `.' and `..'
  1075. if (file == "." || file == "..")
  1076. continue;
  1077. if (file.starts_with(token)) {
  1078. struct stat program_status;
  1079. String file_path = String::format("%s/%s", path.characters(), file.characters());
  1080. int stat_error = stat(file_path.characters(), &program_status);
  1081. if (!stat_error) {
  1082. if (S_ISDIR(program_status.st_mode))
  1083. suggestions.append({ escape_token(file), "/" });
  1084. else
  1085. suggestions.append({ escape_token(file), " " });
  1086. }
  1087. }
  1088. }
  1089. return suggestions;
  1090. };
  1091. signal(SIGINT, [](int) {
  1092. g.was_interrupted = true;
  1093. editor.interrupted();
  1094. });
  1095. signal(SIGWINCH, [](int) {
  1096. g.was_resized = true;
  1097. editor.resized();
  1098. });
  1099. signal(SIGHUP, [](int) {
  1100. save_history();
  1101. });
  1102. int rc = gethostname(g.hostname, sizeof(g.hostname));
  1103. if (rc < 0)
  1104. perror("gethostname");
  1105. rc = ttyname_r(0, g.ttyname, sizeof(g.ttyname));
  1106. if (rc < 0)
  1107. perror("ttyname_r");
  1108. {
  1109. auto* cwd = getcwd(nullptr, 0);
  1110. g.cwd = cwd;
  1111. setenv("PWD", cwd, 1);
  1112. free(cwd);
  1113. }
  1114. {
  1115. auto* pw = getpwuid(getuid());
  1116. if (pw) {
  1117. g.username = pw->pw_name;
  1118. g.home = pw->pw_dir;
  1119. setenv("HOME", pw->pw_dir, 1);
  1120. }
  1121. endpwent();
  1122. }
  1123. if (argc > 2 && !strcmp(argv[1], "-c")) {
  1124. dbgprintf("sh -c '%s'\n", argv[2]);
  1125. run_command(argv[2]);
  1126. return 0;
  1127. }
  1128. if (argc == 2 && argv[1][0] != '-') {
  1129. auto file = Core::File::construct(argv[1]);
  1130. if (!file->open(Core::IODevice::ReadOnly)) {
  1131. fprintf(stderr, "Failed to open %s: %s\n", file->filename().characters(), file->error_string());
  1132. return 1;
  1133. }
  1134. for (;;) {
  1135. auto line = file->read_line(4096);
  1136. if (line.is_null())
  1137. break;
  1138. run_command(String::copy(line, Chomp));
  1139. }
  1140. return 0;
  1141. }
  1142. g.directory_stack.append(g.cwd);
  1143. load_history();
  1144. atexit(save_history);
  1145. cache_path();
  1146. for (;;) {
  1147. auto line = editor.get_line(prompt());
  1148. if (line.is_empty())
  1149. continue;
  1150. run_command(line);
  1151. editor.add_to_history(line);
  1152. }
  1153. return 0;
  1154. }