Builtin.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  1. /*
  2. * Copyright (c) 2020-2021, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "AST.h"
  7. #include "Shell.h"
  8. #include "Shell/Formatter.h"
  9. #include <AK/LexicalPath.h>
  10. #include <AK/ScopeGuard.h>
  11. #include <AK/Statistics.h>
  12. #include <AK/String.h>
  13. #include <LibCore/ArgsParser.h>
  14. #include <LibCore/EventLoop.h>
  15. #include <LibCore/File.h>
  16. #include <errno.h>
  17. #include <inttypes.h>
  18. #include <limits.h>
  19. #include <signal.h>
  20. #include <sys/wait.h>
  21. #include <unistd.h>
  22. extern char** environ;
  23. namespace Shell {
  24. int Shell::builtin_noop(int, const char**)
  25. {
  26. return 0;
  27. }
  28. int Shell::builtin_dump(int argc, const char** argv)
  29. {
  30. if (argc != 2)
  31. return 1;
  32. Parser { argv[1] }.parse()->dump(0);
  33. return 0;
  34. }
  35. int Shell::builtin_alias(int argc, const char** argv)
  36. {
  37. Vector<const char*> arguments;
  38. Core::ArgsParser parser;
  39. parser.add_positional_argument(arguments, "List of name[=values]'s", "name[=value]", Core::ArgsParser::Required::No);
  40. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  41. return 1;
  42. if (arguments.is_empty()) {
  43. for (auto& alias : m_aliases)
  44. printf("%s=%s\n", escape_token(alias.key).characters(), escape_token(alias.value).characters());
  45. return 0;
  46. }
  47. bool fail = false;
  48. for (auto& argument : arguments) {
  49. auto parts = String { argument }.split_limit('=', 2, true);
  50. if (parts.size() == 1) {
  51. auto alias = m_aliases.get(parts[0]);
  52. if (alias.has_value()) {
  53. printf("%s=%s\n", escape_token(parts[0]).characters(), escape_token(alias.value()).characters());
  54. } else {
  55. fail = true;
  56. }
  57. } else {
  58. m_aliases.set(parts[0], parts[1]);
  59. add_entry_to_cache(parts[0]);
  60. }
  61. }
  62. return fail ? 1 : 0;
  63. }
  64. int Shell::builtin_unalias(int argc, const char** argv)
  65. {
  66. bool remove_all { false };
  67. Vector<const char*> arguments;
  68. Core::ArgsParser parser;
  69. parser.set_general_help("Remove alias from the list of aliases");
  70. parser.add_option(remove_all, "Remove all aliases", nullptr, 'a');
  71. parser.add_positional_argument(arguments, "List of aliases to remove", "alias", Core::ArgsParser::Required::No);
  72. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  73. return 1;
  74. if (remove_all) {
  75. m_aliases.clear();
  76. cache_path();
  77. return 0;
  78. }
  79. if (arguments.is_empty()) {
  80. warnln("unalias: not enough arguments");
  81. parser.print_usage(stderr, argv[0]);
  82. return 1;
  83. }
  84. bool failed { false };
  85. for (auto& argument : arguments) {
  86. if (!m_aliases.contains(argument)) {
  87. warnln("unalias: {}: alias not found", argument);
  88. failed = true;
  89. continue;
  90. }
  91. m_aliases.remove(argument);
  92. remove_entry_from_cache(argument);
  93. }
  94. return failed ? 1 : 0;
  95. }
  96. int Shell::builtin_bg(int argc, const char** argv)
  97. {
  98. int job_id = -1;
  99. bool is_pid = false;
  100. Core::ArgsParser parser;
  101. parser.add_positional_argument(Core::ArgsParser::Arg {
  102. .help_string = "Job ID or Jobspec to run in background",
  103. .name = "job-id",
  104. .min_values = 0,
  105. .max_values = 1,
  106. .accept_value = [&](StringView value) -> bool {
  107. // Check if it's a pid (i.e. literal integer)
  108. if (auto number = value.to_uint(); number.has_value()) {
  109. job_id = number.value();
  110. is_pid = true;
  111. return true;
  112. }
  113. // Check if it's a jobspec
  114. if (auto id = resolve_job_spec(value); id.has_value()) {
  115. job_id = id.value();
  116. is_pid = false;
  117. return true;
  118. }
  119. return false;
  120. } });
  121. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  122. return 1;
  123. if (job_id == -1 && !jobs.is_empty())
  124. job_id = find_last_job_id();
  125. auto* job = const_cast<Job*>(find_job(job_id, is_pid));
  126. if (!job) {
  127. if (job_id == -1) {
  128. warnln("bg: No current job");
  129. } else {
  130. warnln("bg: Job with id/pid {} not found", job_id);
  131. }
  132. return 1;
  133. }
  134. job->set_running_in_background(true);
  135. job->set_should_announce_exit(true);
  136. job->set_shell_did_continue(true);
  137. dbgln("Resuming {} ({})", job->pid(), job->cmd());
  138. warnln("Resuming job {} - {}", job->job_id(), job->cmd());
  139. // Try using the PGID, but if that fails, just use the PID.
  140. if (killpg(job->pgid(), SIGCONT) < 0) {
  141. if (kill(job->pid(), SIGCONT) < 0) {
  142. perror("kill");
  143. return 1;
  144. }
  145. }
  146. return 0;
  147. }
  148. int Shell::builtin_type(int argc, const char** argv)
  149. {
  150. Vector<const char*> commands;
  151. bool dont_show_function_source = false;
  152. Core::ArgsParser parser;
  153. parser.set_general_help("Display information about commands.");
  154. parser.add_positional_argument(commands, "Command(s) to list info about", "command");
  155. parser.add_option(dont_show_function_source, "Do not show functions source.", "no-fn-source", 'f');
  156. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  157. return 1;
  158. bool something_not_found = false;
  159. for (auto& command : commands) {
  160. // check if it is an alias
  161. if (auto alias = m_aliases.get(command); alias.has_value()) {
  162. printf("%s is aliased to `%s`\n", escape_token(command).characters(), escape_token(alias.value()).characters());
  163. continue;
  164. }
  165. // check if it is a function
  166. if (auto function = m_functions.get(command); function.has_value()) {
  167. auto fn = function.value();
  168. printf("%s is a function\n", command);
  169. if (!dont_show_function_source) {
  170. StringBuilder builder;
  171. builder.append(fn.name);
  172. builder.append("(");
  173. for (size_t i = 0; i < fn.arguments.size(); i++) {
  174. builder.append(fn.arguments[i]);
  175. if (!(i == fn.arguments.size() - 1))
  176. builder.append(" ");
  177. }
  178. builder.append(") {\n");
  179. if (fn.body) {
  180. auto formatter = Formatter(*fn.body);
  181. builder.append(formatter.format());
  182. printf("%s\n}\n", builder.build().characters());
  183. } else {
  184. printf("%s\n}\n", builder.build().characters());
  185. }
  186. }
  187. continue;
  188. }
  189. // check if its a builtin
  190. if (has_builtin(command)) {
  191. printf("%s is a shell builtin\n", command);
  192. continue;
  193. }
  194. // check if its an executable in PATH
  195. auto fullpath = Core::find_executable_in_path(command);
  196. if (!fullpath.is_empty()) {
  197. printf("%s is %s\n", command, escape_token(fullpath).characters());
  198. continue;
  199. }
  200. something_not_found = true;
  201. printf("type: %s not found\n", command);
  202. }
  203. if (something_not_found)
  204. return 1;
  205. else
  206. return 0;
  207. }
  208. int Shell::builtin_cd(int argc, const char** argv)
  209. {
  210. const char* arg_path = nullptr;
  211. Core::ArgsParser parser;
  212. parser.add_positional_argument(arg_path, "Path to change to", "path", Core::ArgsParser::Required::No);
  213. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  214. return 1;
  215. String new_path;
  216. if (!arg_path) {
  217. new_path = home;
  218. } else {
  219. if (strcmp(arg_path, "-") == 0) {
  220. char* oldpwd = getenv("OLDPWD");
  221. if (oldpwd == nullptr)
  222. return 1;
  223. new_path = oldpwd;
  224. } else {
  225. new_path = arg_path;
  226. }
  227. }
  228. auto real_path = Core::File::real_path_for(new_path);
  229. if (real_path.is_empty()) {
  230. warnln("Invalid path '{}'", new_path);
  231. return 1;
  232. }
  233. if (cd_history.is_empty() || cd_history.last() != real_path)
  234. cd_history.enqueue(real_path);
  235. auto path_relative_to_current_directory = LexicalPath::relative_path(real_path, cwd);
  236. if (path_relative_to_current_directory.is_empty())
  237. path_relative_to_current_directory = real_path;
  238. const char* path = path_relative_to_current_directory.characters();
  239. int rc = chdir(path);
  240. if (rc < 0) {
  241. if (errno == ENOTDIR) {
  242. warnln("Not a directory: {}", path);
  243. } else {
  244. warnln("chdir({}) failed: {}", path, strerror(errno));
  245. }
  246. return 1;
  247. }
  248. setenv("OLDPWD", cwd.characters(), 1);
  249. cwd = move(real_path);
  250. setenv("PWD", cwd.characters(), 1);
  251. return 0;
  252. }
  253. int Shell::builtin_cdh(int argc, const char** argv)
  254. {
  255. int index = -1;
  256. Core::ArgsParser parser;
  257. parser.add_positional_argument(index, "Index of the cd history entry (leave out for a list)", "index", Core::ArgsParser::Required::No);
  258. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  259. return 1;
  260. if (index == -1) {
  261. if (cd_history.is_empty()) {
  262. warnln("cdh: no history available");
  263. return 0;
  264. }
  265. for (ssize_t i = cd_history.size() - 1; i >= 0; --i)
  266. printf("%zu: %s\n", cd_history.size() - i, cd_history.at(i).characters());
  267. return 0;
  268. }
  269. if (index < 1 || (size_t)index > cd_history.size()) {
  270. warnln("cdh: history index out of bounds: {} not in (0, {})", index, cd_history.size());
  271. return 1;
  272. }
  273. const char* path = cd_history.at(cd_history.size() - index).characters();
  274. const char* cd_args[] = { "cd", path, nullptr };
  275. return Shell::builtin_cd(2, cd_args);
  276. }
  277. int Shell::builtin_dirs(int argc, const char** argv)
  278. {
  279. // The first directory in the stack is ALWAYS the current directory
  280. directory_stack.at(0) = cwd.characters();
  281. bool clear = false;
  282. bool print = false;
  283. bool number_when_printing = false;
  284. char separator = ' ';
  285. Vector<const char*> paths;
  286. Core::ArgsParser parser;
  287. parser.add_option(clear, "Clear the directory stack", "clear", 'c');
  288. parser.add_option(print, "Print directory entries one per line", "print", 'p');
  289. parser.add_option(number_when_printing, "Number the directories in the stack when printing", "number", 'v');
  290. parser.add_positional_argument(paths, "Extra paths to put on the stack", "path", Core::ArgsParser::Required::No);
  291. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  292. return 1;
  293. // -v implies -p
  294. print = print || number_when_printing;
  295. if (print) {
  296. if (!paths.is_empty()) {
  297. warnln("dirs: 'print' and 'number' are not allowed when any path is specified");
  298. return 1;
  299. }
  300. separator = '\n';
  301. }
  302. if (clear) {
  303. for (size_t i = 1; i < directory_stack.size(); i++)
  304. directory_stack.remove(i);
  305. }
  306. for (auto& path : paths)
  307. directory_stack.append(path);
  308. if (print || (!clear && paths.is_empty())) {
  309. int index = 0;
  310. for (auto& directory : directory_stack) {
  311. if (number_when_printing)
  312. printf("%d ", index++);
  313. print_path(directory);
  314. fputc(separator, stdout);
  315. }
  316. }
  317. return 0;
  318. }
  319. int Shell::builtin_exec(int argc, const char** argv)
  320. {
  321. if (argc < 2) {
  322. warnln("Shell: No command given to exec");
  323. return 1;
  324. }
  325. Vector<const char*> argv_vector;
  326. argv_vector.append(argv + 1, argc - 1);
  327. argv_vector.append(nullptr);
  328. execute_process(move(argv_vector));
  329. }
  330. int Shell::builtin_exit(int argc, const char** argv)
  331. {
  332. int exit_code = 0;
  333. Core::ArgsParser parser;
  334. parser.add_positional_argument(exit_code, "Exit code", "code", Core::ArgsParser::Required::No);
  335. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  336. return 1;
  337. if (m_is_interactive) {
  338. if (!jobs.is_empty()) {
  339. if (!m_should_ignore_jobs_on_next_exit) {
  340. warnln("Shell: You have {} active job{}, run 'exit' again to really exit.", jobs.size(), jobs.size() > 1 ? "s" : "");
  341. m_should_ignore_jobs_on_next_exit = true;
  342. return 1;
  343. }
  344. }
  345. }
  346. stop_all_jobs();
  347. if (m_is_interactive) {
  348. m_editor->save_history(get_history_path());
  349. printf("Good-bye!\n");
  350. }
  351. exit(exit_code);
  352. }
  353. int Shell::builtin_export(int argc, const char** argv)
  354. {
  355. Vector<const char*> vars;
  356. Core::ArgsParser parser;
  357. parser.add_positional_argument(vars, "List of variable[=value]'s", "values", Core::ArgsParser::Required::No);
  358. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  359. return 1;
  360. if (vars.is_empty()) {
  361. for (size_t i = 0; environ[i]; ++i)
  362. puts(environ[i]);
  363. return 0;
  364. }
  365. for (auto& value : vars) {
  366. auto parts = String { value }.split_limit('=', 2);
  367. if (parts.size() == 1) {
  368. auto value = lookup_local_variable(parts[0]);
  369. if (value) {
  370. auto values = value->resolve_as_list(*this);
  371. StringBuilder builder;
  372. builder.join(" ", values);
  373. parts.append(builder.to_string());
  374. } else {
  375. // Ignore the export.
  376. continue;
  377. }
  378. }
  379. int setenv_return = setenv(parts[0].characters(), parts[1].characters(), 1);
  380. if (setenv_return != 0) {
  381. perror("setenv");
  382. return 1;
  383. }
  384. if (parts[0] == "PATH")
  385. cache_path();
  386. }
  387. return 0;
  388. }
  389. int Shell::builtin_glob(int argc, const char** argv)
  390. {
  391. Vector<const char*> globs;
  392. Core::ArgsParser parser;
  393. parser.add_positional_argument(globs, "Globs to resolve", "glob");
  394. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  395. return 1;
  396. for (auto& glob : globs) {
  397. for (auto& expanded : expand_globs(glob, cwd))
  398. outln("{}", expanded);
  399. }
  400. return 0;
  401. }
  402. int Shell::builtin_fg(int argc, const char** argv)
  403. {
  404. int job_id = -1;
  405. bool is_pid = false;
  406. Core::ArgsParser parser;
  407. parser.add_positional_argument(Core::ArgsParser::Arg {
  408. .help_string = "Job ID or Jobspec to bring to foreground",
  409. .name = "job-id",
  410. .min_values = 0,
  411. .max_values = 1,
  412. .accept_value = [&](StringView value) -> bool {
  413. // Check if it's a pid (i.e. literal integer)
  414. if (auto number = value.to_uint(); number.has_value()) {
  415. job_id = number.value();
  416. is_pid = true;
  417. return true;
  418. }
  419. // Check if it's a jobspec
  420. if (auto id = resolve_job_spec(value); id.has_value()) {
  421. job_id = id.value();
  422. is_pid = false;
  423. return true;
  424. }
  425. return false;
  426. } });
  427. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  428. return 1;
  429. if (job_id == -1 && !jobs.is_empty())
  430. job_id = find_last_job_id();
  431. RefPtr<Job> job = find_job(job_id, is_pid);
  432. if (!job) {
  433. if (job_id == -1) {
  434. warnln("fg: No current job");
  435. } else {
  436. warnln("fg: Job with id/pid {} not found", job_id);
  437. }
  438. return 1;
  439. }
  440. job->set_running_in_background(false);
  441. job->set_shell_did_continue(true);
  442. dbgln("Resuming {} ({})", job->pid(), job->cmd());
  443. warnln("Resuming job {} - {}", job->job_id(), job->cmd());
  444. tcsetpgrp(STDOUT_FILENO, job->pgid());
  445. tcsetpgrp(STDIN_FILENO, job->pgid());
  446. // Try using the PGID, but if that fails, just use the PID.
  447. if (killpg(job->pgid(), SIGCONT) < 0) {
  448. if (kill(job->pid(), SIGCONT) < 0) {
  449. perror("kill");
  450. return 1;
  451. }
  452. }
  453. block_on_job(job);
  454. if (job->exited())
  455. return job->exit_code();
  456. else
  457. return 0;
  458. }
  459. int Shell::builtin_disown(int argc, const char** argv)
  460. {
  461. Vector<int> job_ids;
  462. Vector<bool> id_is_pid;
  463. Core::ArgsParser parser;
  464. parser.add_positional_argument(Core::ArgsParser::Arg {
  465. .help_string = "Job IDs or Jobspecs to disown",
  466. .name = "job-id",
  467. .min_values = 0,
  468. .max_values = INT_MAX,
  469. .accept_value = [&](StringView value) -> bool {
  470. // Check if it's a pid (i.e. literal integer)
  471. if (auto number = value.to_uint(); number.has_value()) {
  472. job_ids.append(number.value());
  473. id_is_pid.append(true);
  474. return true;
  475. }
  476. // Check if it's a jobspec
  477. if (auto id = resolve_job_spec(value); id.has_value()) {
  478. job_ids.append(id.value());
  479. id_is_pid.append(false);
  480. return true;
  481. }
  482. return false;
  483. } });
  484. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  485. return 1;
  486. if (job_ids.is_empty()) {
  487. job_ids.append(find_last_job_id());
  488. id_is_pid.append(false);
  489. }
  490. Vector<const Job*> jobs_to_disown;
  491. for (size_t i = 0; i < job_ids.size(); ++i) {
  492. auto id = job_ids[i];
  493. auto is_pid = id_is_pid[i];
  494. auto job = find_job(id, is_pid);
  495. if (!job)
  496. warnln("disown: Job with id/pid {} not found", id);
  497. else
  498. jobs_to_disown.append(job);
  499. }
  500. if (jobs_to_disown.is_empty()) {
  501. if (job_ids.is_empty())
  502. warnln("disown: No current job");
  503. // An error message has already been printed about the nonexistence of each listed job.
  504. return 1;
  505. }
  506. for (auto job : jobs_to_disown) {
  507. job->deactivate();
  508. if (!job->is_running_in_background())
  509. warnln("disown warning: Job {} is currently not running, 'kill -{} {}' to make it continue", job->job_id(), SIGCONT, job->pid());
  510. jobs.remove(job->pid());
  511. }
  512. return 0;
  513. }
  514. int Shell::builtin_history(int, const char**)
  515. {
  516. for (size_t i = 0; i < m_editor->history().size(); ++i) {
  517. printf("%6zu %s\n", i, m_editor->history()[i].entry.characters());
  518. }
  519. return 0;
  520. }
  521. int Shell::builtin_jobs(int argc, const char** argv)
  522. {
  523. bool list = false, show_pid = false;
  524. Core::ArgsParser parser;
  525. parser.add_option(list, "List all information about jobs", "list", 'l');
  526. parser.add_option(show_pid, "Display the PID of the jobs", "pid", 'p');
  527. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  528. return 1;
  529. Job::PrintStatusMode mode = Job::PrintStatusMode::Basic;
  530. if (show_pid)
  531. mode = Job::PrintStatusMode::OnlyPID;
  532. if (list)
  533. mode = Job::PrintStatusMode::ListAll;
  534. for (auto& it : jobs) {
  535. if (!it.value->print_status(mode))
  536. return 1;
  537. }
  538. return 0;
  539. }
  540. int Shell::builtin_popd(int argc, const char** argv)
  541. {
  542. if (directory_stack.size() <= 1) {
  543. warnln("Shell: popd: directory stack empty");
  544. return 1;
  545. }
  546. bool should_not_switch = false;
  547. Core::ArgsParser parser;
  548. parser.add_option(should_not_switch, "Do not switch dirs", "no-switch", 'n');
  549. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  550. return 1;
  551. auto popped_path = directory_stack.take_last();
  552. if (should_not_switch)
  553. return 0;
  554. auto new_path = LexicalPath::canonicalized_path(popped_path);
  555. if (chdir(new_path.characters()) < 0) {
  556. warnln("chdir({}) failed: {}", new_path, strerror(errno));
  557. return 1;
  558. }
  559. cwd = new_path;
  560. return 0;
  561. }
  562. int Shell::builtin_pushd(int argc, const char** argv)
  563. {
  564. StringBuilder path_builder;
  565. bool should_switch = true;
  566. // From the BASH reference manual: https://www.gnu.org/software/bash/manual/html_node/Directory-Stack-Builtins.html
  567. // With no arguments, pushd exchanges the top two directories and makes the new top the current directory.
  568. if (argc == 1) {
  569. if (directory_stack.size() < 2) {
  570. warnln("pushd: no other directory");
  571. return 1;
  572. }
  573. String dir1 = directory_stack.take_first();
  574. String dir2 = directory_stack.take_first();
  575. directory_stack.insert(0, dir2);
  576. directory_stack.insert(1, dir1);
  577. int rc = chdir(dir2.characters());
  578. if (rc < 0) {
  579. warnln("chdir({}) failed: {}", dir2, strerror(errno));
  580. return 1;
  581. }
  582. cwd = dir2;
  583. return 0;
  584. }
  585. // Let's assume the user's typed in 'pushd <dir>'
  586. if (argc == 2) {
  587. directory_stack.append(cwd.characters());
  588. if (argv[1][0] == '/') {
  589. path_builder.append(argv[1]);
  590. } else {
  591. path_builder.appendff("{}/{}", cwd, argv[1]);
  592. }
  593. } else if (argc == 3) {
  594. directory_stack.append(cwd.characters());
  595. for (int i = 1; i < argc; i++) {
  596. const char* arg = argv[i];
  597. if (arg[0] != '-') {
  598. if (arg[0] == '/') {
  599. path_builder.append(arg);
  600. } else
  601. path_builder.appendff("{}/{}", cwd, arg);
  602. }
  603. if (!strcmp(arg, "-n"))
  604. should_switch = false;
  605. }
  606. }
  607. auto real_path = LexicalPath::canonicalized_path(path_builder.to_string());
  608. struct stat st;
  609. int rc = stat(real_path.characters(), &st);
  610. if (rc < 0) {
  611. warnln("stat({}) failed: {}", real_path, strerror(errno));
  612. return 1;
  613. }
  614. if (!S_ISDIR(st.st_mode)) {
  615. warnln("Not a directory: {}", real_path);
  616. return 1;
  617. }
  618. if (should_switch) {
  619. int rc = chdir(real_path.characters());
  620. if (rc < 0) {
  621. warnln("chdir({}) failed: {}", real_path, strerror(errno));
  622. return 1;
  623. }
  624. cwd = real_path;
  625. }
  626. return 0;
  627. }
  628. int Shell::builtin_pwd(int, const char**)
  629. {
  630. print_path(cwd);
  631. fputc('\n', stdout);
  632. return 0;
  633. }
  634. int Shell::builtin_setopt(int argc, const char** argv)
  635. {
  636. if (argc == 1) {
  637. #define __ENUMERATE_SHELL_OPTION(name, default_, description) \
  638. if (options.name) \
  639. warnln("{}", #name);
  640. ENUMERATE_SHELL_OPTIONS();
  641. #undef __ENUMERATE_SHELL_OPTION
  642. }
  643. Core::ArgsParser parser;
  644. #define __ENUMERATE_SHELL_OPTION(name, default_, description) \
  645. bool name = false; \
  646. bool not_##name = false; \
  647. parser.add_option(name, "Enable: " description, #name, '\0'); \
  648. parser.add_option(not_##name, "Disable: " description, "no_" #name, '\0');
  649. ENUMERATE_SHELL_OPTIONS();
  650. #undef __ENUMERATE_SHELL_OPTION
  651. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  652. return 1;
  653. #define __ENUMERATE_SHELL_OPTION(name, default_, description) \
  654. if (name) \
  655. options.name = true; \
  656. if (not_##name) \
  657. options.name = false;
  658. ENUMERATE_SHELL_OPTIONS();
  659. #undef __ENUMERATE_SHELL_OPTION
  660. return 0;
  661. }
  662. int Shell::builtin_shift(int argc, const char** argv)
  663. {
  664. int count = 1;
  665. Core::ArgsParser parser;
  666. parser.add_positional_argument(count, "Shift count", "count", Core::ArgsParser::Required::No);
  667. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  668. return 1;
  669. if (count < 1)
  670. return 0;
  671. auto argv_ = lookup_local_variable("ARGV");
  672. if (!argv_) {
  673. warnln("shift: ARGV is unset");
  674. return 1;
  675. }
  676. if (!argv_->is_list())
  677. argv_ = adopt_ref(*new AST::ListValue({ argv_.release_nonnull() }));
  678. auto& values = static_cast<AST::ListValue*>(argv_.ptr())->values();
  679. if ((size_t)count > values.size()) {
  680. warnln("shift: shift count must not be greater than {}", values.size());
  681. return 1;
  682. }
  683. for (auto i = 0; i < count; ++i)
  684. (void)values.take_first();
  685. return 0;
  686. }
  687. int Shell::builtin_source(int argc, const char** argv)
  688. {
  689. const char* file_to_source = nullptr;
  690. Vector<const char*> args;
  691. Core::ArgsParser parser;
  692. parser.add_positional_argument(file_to_source, "File to read commands from", "path");
  693. parser.add_positional_argument(args, "ARGV for the sourced file", "args", Core::ArgsParser::Required::No);
  694. if (!parser.parse(argc, const_cast<char**>(argv)))
  695. return 1;
  696. Vector<String> string_argv;
  697. for (auto& arg : args)
  698. string_argv.append(arg);
  699. auto previous_argv = lookup_local_variable("ARGV");
  700. ScopeGuard guard { [&] {
  701. if (!args.is_empty())
  702. set_local_variable("ARGV", move(previous_argv));
  703. } };
  704. if (!args.is_empty())
  705. set_local_variable("ARGV", AST::make_ref_counted<AST::ListValue>(move(string_argv)));
  706. if (!run_file(file_to_source, true))
  707. return 126;
  708. return 0;
  709. }
  710. int Shell::builtin_time(int argc, const char** argv)
  711. {
  712. Vector<const char*> args;
  713. int number_of_iterations = 1;
  714. Core::ArgsParser parser;
  715. parser.add_option(number_of_iterations, "Number of iterations", "iterations", 'n', "iterations");
  716. parser.set_stop_on_first_non_option(true);
  717. parser.add_positional_argument(args, "Command to execute with arguments", "command", Core::ArgsParser::Required::Yes);
  718. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  719. return 1;
  720. if (number_of_iterations < 1)
  721. return 1;
  722. AST::Command command;
  723. for (auto& arg : args)
  724. command.argv.append(arg);
  725. auto commands = expand_aliases({ move(command) });
  726. AK::Statistics iteration_times;
  727. int exit_code = 1;
  728. for (int i = 0; i < number_of_iterations; ++i) {
  729. auto timer = Core::ElapsedTimer::start_new();
  730. for (auto& job : run_commands(commands)) {
  731. block_on_job(job);
  732. exit_code = job.exit_code();
  733. }
  734. iteration_times.add(timer.elapsed());
  735. }
  736. if (number_of_iterations == 1) {
  737. warnln("Time: {} ms", iteration_times.values().first());
  738. } else {
  739. AK::Statistics iteration_times_excluding_first;
  740. for (size_t i = 1; i < iteration_times.size(); i++)
  741. iteration_times_excluding_first.add(iteration_times.values()[i]);
  742. warnln("Timing report: {} ms", iteration_times.sum());
  743. warnln("==============");
  744. warnln("Command: {}", String::join(' ', args));
  745. warnln("Average time: {:.2} ms (median: {}, stddev: {:.2}, min: {}, max:{})",
  746. iteration_times.average(), iteration_times.median(),
  747. iteration_times.standard_deviation(),
  748. iteration_times.min(), iteration_times.max());
  749. warnln("Excluding first: {:.2} ms (median: {}, stddev: {:.2}, min: {}, max:{})",
  750. iteration_times_excluding_first.average(), iteration_times_excluding_first.median(),
  751. iteration_times_excluding_first.standard_deviation(),
  752. iteration_times_excluding_first.min(), iteration_times_excluding_first.max());
  753. }
  754. return exit_code;
  755. }
  756. int Shell::builtin_umask(int argc, const char** argv)
  757. {
  758. const char* mask_text = nullptr;
  759. Core::ArgsParser parser;
  760. parser.add_positional_argument(mask_text, "New mask (omit to get current mask)", "octal-mask", Core::ArgsParser::Required::No);
  761. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  762. return 1;
  763. if (!mask_text) {
  764. mode_t old_mask = umask(0);
  765. printf("%#o\n", old_mask);
  766. umask(old_mask);
  767. return 0;
  768. }
  769. unsigned mask;
  770. int matches = sscanf(mask_text, "%o", &mask);
  771. if (matches == 1) {
  772. umask(mask);
  773. return 0;
  774. }
  775. warnln("umask: Invalid mask '{}'", mask_text);
  776. return 1;
  777. }
  778. int Shell::builtin_wait(int argc, const char** argv)
  779. {
  780. Vector<int> job_ids;
  781. Vector<bool> id_is_pid;
  782. Core::ArgsParser parser;
  783. parser.add_positional_argument(Core::ArgsParser::Arg {
  784. .help_string = "Job IDs or Jobspecs to wait for",
  785. .name = "job-id",
  786. .min_values = 0,
  787. .max_values = INT_MAX,
  788. .accept_value = [&](StringView value) -> bool {
  789. // Check if it's a pid (i.e. literal integer)
  790. if (auto number = value.to_uint(); number.has_value()) {
  791. job_ids.append(number.value());
  792. id_is_pid.append(true);
  793. return true;
  794. }
  795. // Check if it's a jobspec
  796. if (auto id = resolve_job_spec(value); id.has_value()) {
  797. job_ids.append(id.value());
  798. id_is_pid.append(false);
  799. return true;
  800. }
  801. return false;
  802. } });
  803. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  804. return 1;
  805. Vector<NonnullRefPtr<Job>> jobs_to_wait_for;
  806. for (size_t i = 0; i < job_ids.size(); ++i) {
  807. auto id = job_ids[i];
  808. auto is_pid = id_is_pid[i];
  809. auto job = find_job(id, is_pid);
  810. if (!job)
  811. warnln("wait: Job with id/pid {} not found", id);
  812. else
  813. jobs_to_wait_for.append(*job);
  814. }
  815. if (job_ids.is_empty()) {
  816. for (const auto& it : jobs)
  817. jobs_to_wait_for.append(it.value);
  818. }
  819. for (auto& job : jobs_to_wait_for) {
  820. job->set_running_in_background(false);
  821. block_on_job(job);
  822. }
  823. return 0;
  824. }
  825. int Shell::builtin_unset(int argc, const char** argv)
  826. {
  827. Vector<const char*> vars;
  828. Core::ArgsParser parser;
  829. parser.add_positional_argument(vars, "List of variables", "variables", Core::ArgsParser::Required::Yes);
  830. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  831. return 1;
  832. bool did_touch_path = false;
  833. for (auto& value : vars) {
  834. if (!did_touch_path && value == "PATH"sv)
  835. did_touch_path = true;
  836. if (lookup_local_variable(value)) {
  837. unset_local_variable(value);
  838. } else {
  839. unsetenv(value);
  840. }
  841. }
  842. if (did_touch_path)
  843. cache_path();
  844. return 0;
  845. }
  846. int Shell::builtin_not(int argc, const char** argv)
  847. {
  848. // FIXME: Use ArgsParser when it can collect unrelated -arguments too.
  849. if (argc == 1)
  850. return 1;
  851. AST::Command command;
  852. for (size_t i = 1; i < (size_t)argc; ++i)
  853. command.argv.append(argv[i]);
  854. auto commands = expand_aliases({ move(command) });
  855. int exit_code = 1;
  856. auto found_a_job = false;
  857. for (auto& job : run_commands(commands)) {
  858. found_a_job = true;
  859. block_on_job(job);
  860. exit_code = job.exit_code();
  861. }
  862. // In case it was a function.
  863. if (!found_a_job)
  864. exit_code = last_return_code.value_or(0);
  865. return exit_code == 0 ? 1 : 0;
  866. }
  867. int Shell::builtin_kill(int argc, const char** argv)
  868. {
  869. // Simply translate the arguments and pass them to `kill'
  870. Vector<String> replaced_values;
  871. auto kill_path = find_in_path("kill");
  872. if (kill_path.is_empty()) {
  873. warnln("kill: `kill' not found in PATH");
  874. return 126;
  875. }
  876. replaced_values.append(kill_path);
  877. for (auto i = 1; i < argc; ++i) {
  878. if (auto job_id = resolve_job_spec(argv[i]); job_id.has_value()) {
  879. auto job = find_job(job_id.value());
  880. if (job) {
  881. replaced_values.append(String::number(job->pid()));
  882. } else {
  883. warnln("kill: Job with pid {} not found", job_id.value());
  884. return 1;
  885. }
  886. } else {
  887. replaced_values.append(argv[i]);
  888. }
  889. }
  890. // Now just run `kill'
  891. AST::Command command;
  892. command.argv = move(replaced_values);
  893. command.position = m_source_position.has_value() ? m_source_position->position : Optional<AST::Position> {};
  894. auto exit_code = 1;
  895. auto job_result = run_command(command);
  896. if (job_result.is_error()) {
  897. warnln("kill: Failed to run {}: {}", command.argv.first(), job_result.error());
  898. return exit_code;
  899. }
  900. if (auto job = job_result.release_value()) {
  901. block_on_job(job);
  902. exit_code = job->exit_code();
  903. }
  904. return exit_code;
  905. }
  906. bool Shell::run_builtin(const AST::Command& command, const NonnullRefPtrVector<AST::Rewiring>& rewirings, int& retval)
  907. {
  908. if (command.argv.is_empty())
  909. return false;
  910. if (!has_builtin(command.argv.first()))
  911. return false;
  912. Vector<const char*> argv;
  913. for (auto& arg : command.argv)
  914. argv.append(arg.characters());
  915. argv.append(nullptr);
  916. StringView name = command.argv.first();
  917. SavedFileDescriptors fds { rewirings };
  918. for (auto& rewiring : rewirings) {
  919. int rc = dup2(rewiring.old_fd, rewiring.new_fd);
  920. if (rc < 0) {
  921. perror("dup2(run)");
  922. return false;
  923. }
  924. }
  925. Core::EventLoop loop;
  926. setup_signals();
  927. if (name == ":"sv)
  928. name = "noop"sv;
  929. #define __ENUMERATE_SHELL_BUILTIN(builtin) \
  930. if (name == #builtin) { \
  931. retval = builtin_##builtin(argv.size() - 1, argv.data()); \
  932. if (!has_error(ShellError::None)) \
  933. raise_error(m_error, m_error_description, command.position); \
  934. fflush(stdout); \
  935. fflush(stderr); \
  936. return true; \
  937. }
  938. ENUMERATE_SHELL_BUILTINS();
  939. #undef __ENUMERATE_SHELL_BUILTIN
  940. return false;
  941. }
  942. bool Shell::has_builtin(StringView name) const
  943. {
  944. if (name == ":"sv)
  945. return true;
  946. #define __ENUMERATE_SHELL_BUILTIN(builtin) \
  947. if (name == #builtin) { \
  948. return true; \
  949. }
  950. ENUMERATE_SHELL_BUILTINS();
  951. #undef __ENUMERATE_SHELL_BUILTIN
  952. return false;
  953. }
  954. }