Builtin.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929
  1. /*
  2. * Copyright (c) 2020, The SerenityOS developers.
  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 "AST.h"
  27. #include "Shell.h"
  28. #include <AK/LexicalPath.h>
  29. #include <AK/ScopeGuard.h>
  30. #include <LibCore/ArgsParser.h>
  31. #include <LibCore/EventLoop.h>
  32. #include <LibCore/File.h>
  33. #include <inttypes.h>
  34. #include <signal.h>
  35. #include <sys/wait.h>
  36. #include <unistd.h>
  37. extern char** environ;
  38. namespace Shell {
  39. int Shell::builtin_alias(int argc, const char** argv)
  40. {
  41. Vector<const char*> arguments;
  42. Core::ArgsParser parser;
  43. parser.add_positional_argument(arguments, "List of name[=values]'s", "name[=value]", Core::ArgsParser::Required::No);
  44. if (!parser.parse(argc, const_cast<char**>(argv), false))
  45. return 1;
  46. if (arguments.is_empty()) {
  47. for (auto& alias : m_aliases)
  48. printf("%s=%s\n", escape_token(alias.key).characters(), escape_token(alias.value).characters());
  49. return 0;
  50. }
  51. bool fail = false;
  52. for (auto& argument : arguments) {
  53. auto parts = String { argument }.split_limit('=', 2, true);
  54. if (parts.size() == 1) {
  55. auto alias = m_aliases.get(parts[0]);
  56. if (alias.has_value()) {
  57. printf("%s=%s\n", escape_token(parts[0]).characters(), escape_token(alias.value()).characters());
  58. } else {
  59. fail = true;
  60. }
  61. } else {
  62. m_aliases.set(parts[0], parts[1]);
  63. add_entry_to_cache(parts[0]);
  64. }
  65. }
  66. return fail ? 1 : 0;
  67. }
  68. int Shell::builtin_bg(int argc, const char** argv)
  69. {
  70. int job_id = -1;
  71. Core::ArgsParser parser;
  72. parser.add_positional_argument(job_id, "Job ID to run in background", "job-id", Core::ArgsParser::Required::No);
  73. if (!parser.parse(argc, const_cast<char**>(argv), false))
  74. return 1;
  75. if (job_id == -1 && !jobs.is_empty())
  76. job_id = find_last_job_id();
  77. auto* job = const_cast<Job*>(find_job(job_id));
  78. if (!job) {
  79. if (job_id == -1) {
  80. fprintf(stderr, "bg: no current job\n");
  81. } else {
  82. fprintf(stderr, "bg: job with id %d not found\n", job_id);
  83. }
  84. return 1;
  85. }
  86. job->set_running_in_background(true);
  87. job->set_should_announce_exit(true);
  88. job->set_is_suspended(false);
  89. dbgln("Resuming {} ({})", job->pid(), job->cmd());
  90. warnln("Resuming job {} - {}", job->job_id(), job->cmd().characters());
  91. // Try using the PGID, but if that fails, just use the PID.
  92. if (killpg(job->pgid(), SIGCONT) < 0) {
  93. if (kill(job->pid(), SIGCONT) < 0) {
  94. perror("kill");
  95. return 1;
  96. }
  97. }
  98. return 0;
  99. }
  100. int Shell::builtin_cd(int argc, const char** argv)
  101. {
  102. const char* arg_path = nullptr;
  103. Core::ArgsParser parser;
  104. parser.add_positional_argument(arg_path, "Path to change to", "path", Core::ArgsParser::Required::No);
  105. if (!parser.parse(argc, const_cast<char**>(argv), false))
  106. return 1;
  107. String new_path;
  108. if (!arg_path) {
  109. new_path = home;
  110. } else {
  111. if (strcmp(arg_path, "-") == 0) {
  112. char* oldpwd = getenv("OLDPWD");
  113. if (oldpwd == nullptr)
  114. return 1;
  115. new_path = oldpwd;
  116. } else if (arg_path[0] == '/') {
  117. new_path = argv[1];
  118. } else {
  119. StringBuilder builder;
  120. builder.append(cwd);
  121. builder.append('/');
  122. builder.append(arg_path);
  123. new_path = builder.to_string();
  124. }
  125. }
  126. auto real_path = Core::File::real_path_for(new_path);
  127. if (real_path.is_empty()) {
  128. fprintf(stderr, "Invalid path '%s'\n", new_path.characters());
  129. return 1;
  130. }
  131. if (cd_history.is_empty() || cd_history.last() != real_path)
  132. cd_history.enqueue(real_path);
  133. const char* path = real_path.characters();
  134. int rc = chdir(path);
  135. if (rc < 0) {
  136. if (errno == ENOTDIR) {
  137. fprintf(stderr, "Not a directory: %s\n", path);
  138. } else {
  139. fprintf(stderr, "chdir(%s) failed: %s\n", path, strerror(errno));
  140. }
  141. return 1;
  142. }
  143. setenv("OLDPWD", cwd.characters(), 1);
  144. cwd = real_path;
  145. setenv("PWD", cwd.characters(), 1);
  146. return 0;
  147. }
  148. int Shell::builtin_cdh(int argc, const char** argv)
  149. {
  150. int index = -1;
  151. Core::ArgsParser parser;
  152. parser.add_positional_argument(index, "Index of the cd history entry (leave out for a list)", "index", Core::ArgsParser::Required::No);
  153. if (!parser.parse(argc, const_cast<char**>(argv), false))
  154. return 1;
  155. if (index == -1) {
  156. if (cd_history.is_empty()) {
  157. fprintf(stderr, "cdh: no history available\n");
  158. return 0;
  159. }
  160. for (ssize_t i = cd_history.size() - 1; i >= 0; --i)
  161. printf("%lu: %s\n", cd_history.size() - i, cd_history.at(i).characters());
  162. return 0;
  163. }
  164. if (index < 1 || (size_t)index > cd_history.size()) {
  165. fprintf(stderr, "cdh: history index out of bounds: %d not in (0, %zu)\n", index, cd_history.size());
  166. return 1;
  167. }
  168. const char* path = cd_history.at(cd_history.size() - index).characters();
  169. const char* cd_args[] = { "cd", path, nullptr };
  170. return Shell::builtin_cd(2, cd_args);
  171. }
  172. int Shell::builtin_dirs(int argc, const char** argv)
  173. {
  174. // The first directory in the stack is ALWAYS the current directory
  175. directory_stack.at(0) = cwd.characters();
  176. bool clear = false;
  177. bool print = false;
  178. bool number_when_printing = false;
  179. char separator = ' ';
  180. Vector<const char*> paths;
  181. Core::ArgsParser parser;
  182. parser.add_option(clear, "Clear the directory stack", "clear", 'c');
  183. parser.add_option(print, "Print directory entries one per line", "print", 'p');
  184. parser.add_option(number_when_printing, "Number the directories in the stack when printing", "number", 'v');
  185. parser.add_positional_argument(paths, "Extra paths to put on the stack", "path", Core::ArgsParser::Required::No);
  186. if (!parser.parse(argc, const_cast<char**>(argv), false))
  187. return 1;
  188. // -v implies -p
  189. print = print || number_when_printing;
  190. if (print) {
  191. if (!paths.is_empty()) {
  192. fprintf(stderr, "dirs: 'print' and 'number' are not allowed when any path is specified");
  193. return 1;
  194. }
  195. separator = '\n';
  196. }
  197. if (clear) {
  198. for (size_t i = 1; i < directory_stack.size(); i++)
  199. directory_stack.remove(i);
  200. }
  201. for (auto& path : paths)
  202. directory_stack.append(path);
  203. if (print || (!clear && paths.is_empty())) {
  204. int index = 0;
  205. for (auto& directory : directory_stack) {
  206. if (number_when_printing)
  207. printf("%d ", index++);
  208. print_path(directory);
  209. fputc(separator, stdout);
  210. }
  211. }
  212. return 0;
  213. }
  214. int Shell::builtin_exec(int argc, const char** argv)
  215. {
  216. if (argc < 2) {
  217. fprintf(stderr, "Shell: No command given to exec\n");
  218. return 1;
  219. }
  220. Vector<const char*> argv_vector;
  221. argv_vector.append(argv + 1, argc - 1);
  222. argv_vector.append(nullptr);
  223. execute_process(move(argv_vector));
  224. }
  225. int Shell::builtin_exit(int argc, const char** argv)
  226. {
  227. int exit_code = 0;
  228. Core::ArgsParser parser;
  229. parser.add_positional_argument(exit_code, "Exit code", "code", Core::ArgsParser::Required::No);
  230. if (!parser.parse(argc, const_cast<char**>(argv)))
  231. return 1;
  232. if (m_is_interactive) {
  233. if (!jobs.is_empty()) {
  234. if (!m_should_ignore_jobs_on_next_exit) {
  235. fprintf(stderr, "Shell: You have %zu active job%s, run 'exit' again to really exit.\n", jobs.size(), jobs.size() > 1 ? "s" : "");
  236. m_should_ignore_jobs_on_next_exit = true;
  237. return 1;
  238. }
  239. }
  240. }
  241. stop_all_jobs();
  242. m_editor->save_history(get_history_path());
  243. if (m_is_interactive)
  244. printf("Good-bye!\n");
  245. exit(exit_code);
  246. return 0;
  247. }
  248. int Shell::builtin_export(int argc, const char** argv)
  249. {
  250. Vector<const char*> vars;
  251. Core::ArgsParser parser;
  252. parser.add_positional_argument(vars, "List of variable[=value]'s", "values", Core::ArgsParser::Required::No);
  253. if (!parser.parse(argc, const_cast<char**>(argv), false))
  254. return 1;
  255. if (vars.is_empty()) {
  256. for (size_t i = 0; environ[i]; ++i)
  257. puts(environ[i]);
  258. return 0;
  259. }
  260. for (auto& value : vars) {
  261. auto parts = String { value }.split_limit('=', 2);
  262. if (parts.size() == 1) {
  263. auto value = lookup_local_variable(parts[0]);
  264. if (value) {
  265. auto values = value->resolve_as_list(*this);
  266. StringBuilder builder;
  267. builder.join(" ", values);
  268. parts.append(builder.to_string());
  269. } else {
  270. // Ignore the export.
  271. continue;
  272. }
  273. }
  274. int setenv_return = setenv(parts[0].characters(), parts[1].characters(), 1);
  275. if (setenv_return != 0) {
  276. perror("setenv");
  277. return 1;
  278. }
  279. if (parts[0] == "PATH")
  280. cache_path();
  281. }
  282. return 0;
  283. }
  284. int Shell::builtin_glob(int argc, const char** argv)
  285. {
  286. Vector<const char*> globs;
  287. Core::ArgsParser parser;
  288. parser.add_positional_argument(globs, "Globs to resolve", "glob");
  289. if (!parser.parse(argc, const_cast<char**>(argv), false))
  290. return 1;
  291. for (auto& glob : globs) {
  292. for (auto& expanded : expand_globs(glob, cwd))
  293. outln("{}", expanded);
  294. }
  295. return 0;
  296. }
  297. int Shell::builtin_fg(int argc, const char** argv)
  298. {
  299. int job_id = -1;
  300. Core::ArgsParser parser;
  301. parser.add_positional_argument(job_id, "Job ID to bring to foreground", "job-id", Core::ArgsParser::Required::No);
  302. if (!parser.parse(argc, const_cast<char**>(argv), false))
  303. return 1;
  304. if (job_id == -1 && !jobs.is_empty())
  305. job_id = find_last_job_id();
  306. RefPtr<Job> job = find_job(job_id);
  307. if (!job) {
  308. if (job_id == -1) {
  309. fprintf(stderr, "fg: no current job\n");
  310. } else {
  311. fprintf(stderr, "fg: job with id %d not found\n", job_id);
  312. }
  313. return 1;
  314. }
  315. job->set_running_in_background(false);
  316. job->set_is_suspended(false);
  317. dbgln("Resuming {} ({})", job->pid(), job->cmd());
  318. warnln("Resuming job {} - {}", job->job_id(), job->cmd().characters());
  319. tcsetpgrp(STDOUT_FILENO, job->pgid());
  320. tcsetpgrp(STDIN_FILENO, job->pgid());
  321. // Try using the PGID, but if that fails, just use the PID.
  322. if (killpg(job->pgid(), SIGCONT) < 0) {
  323. if (kill(job->pid(), SIGCONT) < 0) {
  324. perror("kill");
  325. return 1;
  326. }
  327. }
  328. block_on_job(job);
  329. if (job->exited())
  330. return job->exit_code();
  331. else
  332. return 0;
  333. }
  334. int Shell::builtin_disown(int argc, const char** argv)
  335. {
  336. Vector<const char*> str_job_ids;
  337. Core::ArgsParser parser;
  338. parser.add_positional_argument(str_job_ids, "Id of the jobs to disown (omit for current job)", "job_ids", Core::ArgsParser::Required::No);
  339. if (!parser.parse(argc, const_cast<char**>(argv), false))
  340. return 1;
  341. Vector<size_t> job_ids;
  342. for (auto& job_id : str_job_ids) {
  343. auto id = StringView(job_id).to_uint();
  344. if (id.has_value())
  345. job_ids.append(id.value());
  346. else
  347. fprintf(stderr, "disown: Invalid job id %s\n", job_id);
  348. }
  349. if (job_ids.is_empty())
  350. job_ids.append(find_last_job_id());
  351. Vector<const Job*> jobs_to_disown;
  352. for (auto id : job_ids) {
  353. auto job = find_job(id);
  354. if (!job)
  355. fprintf(stderr, "disown: job with id %zu not found\n", id);
  356. else
  357. jobs_to_disown.append(job);
  358. }
  359. if (jobs_to_disown.is_empty()) {
  360. if (str_job_ids.is_empty())
  361. fprintf(stderr, "disown: no current job\n");
  362. // An error message has already been printed about the nonexistence of each listed job.
  363. return 1;
  364. }
  365. for (auto job : jobs_to_disown) {
  366. job->deactivate();
  367. if (!job->is_running_in_background())
  368. fprintf(stderr, "disown warning: job %" PRIu64 " is currently not running, 'kill -%d %d' to make it continue\n", job->job_id(), SIGCONT, job->pid());
  369. jobs.remove(job->pid());
  370. }
  371. return 0;
  372. }
  373. int Shell::builtin_history(int, const char**)
  374. {
  375. for (size_t i = 0; i < m_editor->history().size(); ++i) {
  376. printf("%6zu %s\n", i, m_editor->history()[i].entry.characters());
  377. }
  378. return 0;
  379. }
  380. int Shell::builtin_jobs(int argc, const char** argv)
  381. {
  382. bool list = false, show_pid = false;
  383. Core::ArgsParser parser;
  384. parser.add_option(list, "List all information about jobs", "list", 'l');
  385. parser.add_option(show_pid, "Display the PID of the jobs", "pid", 'p');
  386. if (!parser.parse(argc, const_cast<char**>(argv), false))
  387. return 1;
  388. Job::PrintStatusMode mode = Job::PrintStatusMode::Basic;
  389. if (show_pid)
  390. mode = Job::PrintStatusMode::OnlyPID;
  391. if (list)
  392. mode = Job::PrintStatusMode::ListAll;
  393. for (auto& it : jobs) {
  394. if (!it.value->print_status(mode))
  395. return 1;
  396. }
  397. return 0;
  398. }
  399. int Shell::builtin_popd(int argc, const char** argv)
  400. {
  401. if (directory_stack.size() <= 1) {
  402. fprintf(stderr, "Shell: popd: directory stack empty\n");
  403. return 1;
  404. }
  405. bool should_not_switch = false;
  406. String path = directory_stack.take_last();
  407. Core::ArgsParser parser;
  408. parser.add_option(should_not_switch, "Do not switch dirs", "no-switch", 'n');
  409. if (!parser.parse(argc, const_cast<char**>(argv), false))
  410. return 1;
  411. bool should_switch = !should_not_switch;
  412. // When no arguments are given, popd removes the top directory from the stack and performs a cd to the new top directory.
  413. if (argc == 1) {
  414. int rc = chdir(path.characters());
  415. if (rc < 0) {
  416. fprintf(stderr, "chdir(%s) failed: %s\n", path.characters(), strerror(errno));
  417. return 1;
  418. }
  419. cwd = path;
  420. return 0;
  421. }
  422. LexicalPath lexical_path(path.characters());
  423. if (!lexical_path.is_valid()) {
  424. fprintf(stderr, "LexicalPath failed to canonicalize '%s'\n", path.characters());
  425. return 1;
  426. }
  427. const char* real_path = lexical_path.string().characters();
  428. struct stat st;
  429. int rc = stat(real_path, &st);
  430. if (rc < 0) {
  431. fprintf(stderr, "stat(%s) failed: %s\n", real_path, strerror(errno));
  432. return 1;
  433. }
  434. if (!S_ISDIR(st.st_mode)) {
  435. fprintf(stderr, "Not a directory: %s\n", real_path);
  436. return 1;
  437. }
  438. if (should_switch) {
  439. int rc = chdir(real_path);
  440. if (rc < 0) {
  441. fprintf(stderr, "chdir(%s) failed: %s\n", real_path, strerror(errno));
  442. return 1;
  443. }
  444. cwd = lexical_path.string();
  445. }
  446. return 0;
  447. }
  448. int Shell::builtin_pushd(int argc, const char** argv)
  449. {
  450. StringBuilder path_builder;
  451. bool should_switch = true;
  452. // From the BASH reference manual: https://www.gnu.org/software/bash/manual/html_node/Directory-Stack-Builtins.html
  453. // With no arguments, pushd exchanges the top two directories and makes the new top the current directory.
  454. if (argc == 1) {
  455. if (directory_stack.size() < 2) {
  456. fprintf(stderr, "pushd: no other directory\n");
  457. return 1;
  458. }
  459. String dir1 = directory_stack.take_first();
  460. String dir2 = directory_stack.take_first();
  461. directory_stack.insert(0, dir2);
  462. directory_stack.insert(1, dir1);
  463. int rc = chdir(dir2.characters());
  464. if (rc < 0) {
  465. fprintf(stderr, "chdir(%s) failed: %s\n", dir2.characters(), strerror(errno));
  466. return 1;
  467. }
  468. cwd = dir2;
  469. return 0;
  470. }
  471. // Let's assume the user's typed in 'pushd <dir>'
  472. if (argc == 2) {
  473. directory_stack.append(cwd.characters());
  474. if (argv[1][0] == '/') {
  475. path_builder.append(argv[1]);
  476. } else {
  477. path_builder.appendf("%s/%s", cwd.characters(), argv[1]);
  478. }
  479. } else if (argc == 3) {
  480. directory_stack.append(cwd.characters());
  481. for (int i = 1; i < argc; i++) {
  482. const char* arg = argv[i];
  483. if (arg[0] != '-') {
  484. if (arg[0] == '/') {
  485. path_builder.append(arg);
  486. } else
  487. path_builder.appendf("%s/%s", cwd.characters(), arg);
  488. }
  489. if (!strcmp(arg, "-n"))
  490. should_switch = false;
  491. }
  492. }
  493. LexicalPath lexical_path(path_builder.to_string());
  494. if (!lexical_path.is_valid()) {
  495. fprintf(stderr, "LexicalPath failed to canonicalize '%s'\n", path_builder.to_string().characters());
  496. return 1;
  497. }
  498. const char* real_path = lexical_path.string().characters();
  499. struct stat st;
  500. int rc = stat(real_path, &st);
  501. if (rc < 0) {
  502. fprintf(stderr, "stat(%s) failed: %s\n", real_path, strerror(errno));
  503. return 1;
  504. }
  505. if (!S_ISDIR(st.st_mode)) {
  506. fprintf(stderr, "Not a directory: %s\n", real_path);
  507. return 1;
  508. }
  509. if (should_switch) {
  510. int rc = chdir(real_path);
  511. if (rc < 0) {
  512. fprintf(stderr, "chdir(%s) failed: %s\n", real_path, strerror(errno));
  513. return 1;
  514. }
  515. cwd = lexical_path.string();
  516. }
  517. return 0;
  518. }
  519. int Shell::builtin_pwd(int, const char**)
  520. {
  521. print_path(cwd);
  522. fputc('\n', stdout);
  523. return 0;
  524. }
  525. int Shell::builtin_setopt(int argc, const char** argv)
  526. {
  527. if (argc == 1) {
  528. #define __ENUMERATE_SHELL_OPTION(name, default_, description) \
  529. if (options.name) \
  530. fprintf(stderr, #name "\n");
  531. ENUMERATE_SHELL_OPTIONS();
  532. #undef __ENUMERATE_SHELL_OPTION
  533. }
  534. Core::ArgsParser parser;
  535. #define __ENUMERATE_SHELL_OPTION(name, default_, description) \
  536. bool name = false; \
  537. bool not_##name = false; \
  538. parser.add_option(name, "Enable: " description, #name, '\0'); \
  539. parser.add_option(not_##name, "Disable: " description, "no_" #name, '\0');
  540. ENUMERATE_SHELL_OPTIONS();
  541. #undef __ENUMERATE_SHELL_OPTION
  542. if (!parser.parse(argc, const_cast<char**>(argv), false))
  543. return 1;
  544. #define __ENUMERATE_SHELL_OPTION(name, default_, description) \
  545. if (name) \
  546. options.name = true; \
  547. if (not_##name) \
  548. options.name = false;
  549. ENUMERATE_SHELL_OPTIONS();
  550. #undef __ENUMERATE_SHELL_OPTION
  551. return 0;
  552. }
  553. int Shell::builtin_shift(int argc, const char** argv)
  554. {
  555. int count = 1;
  556. Core::ArgsParser parser;
  557. parser.add_positional_argument(count, "Shift count", "count", Core::ArgsParser::Required::No);
  558. if (!parser.parse(argc, const_cast<char**>(argv), false))
  559. return 1;
  560. if (count < 1)
  561. return 0;
  562. auto argv_ = lookup_local_variable("ARGV");
  563. if (!argv_) {
  564. fprintf(stderr, "shift: ARGV is unset\n");
  565. return 1;
  566. }
  567. if (!argv_->is_list())
  568. argv_ = adopt(*new AST::ListValue({ argv_.release_nonnull() }));
  569. auto& values = static_cast<AST::ListValue*>(argv_.ptr())->values();
  570. if ((size_t)count > values.size()) {
  571. fprintf(stderr, "shift: shift count must not be greater than %zu\n", values.size());
  572. return 1;
  573. }
  574. for (auto i = 0; i < count; ++i)
  575. values.take_first();
  576. return 0;
  577. }
  578. int Shell::builtin_source(int argc, const char** argv)
  579. {
  580. const char* file_to_source = nullptr;
  581. Vector<const char*> args;
  582. Core::ArgsParser parser;
  583. parser.add_positional_argument(file_to_source, "File to read commands from", "path");
  584. parser.add_positional_argument(args, "ARGV for the sourced file", "args", Core::ArgsParser::Required::No);
  585. if (!parser.parse(argc, const_cast<char**>(argv)))
  586. return 1;
  587. Vector<String> string_argv;
  588. for (auto& arg : args)
  589. string_argv.append(arg);
  590. auto previous_argv = lookup_local_variable("ARGV");
  591. ScopeGuard guard { [&] {
  592. if (!args.is_empty())
  593. set_local_variable("ARGV", move(previous_argv));
  594. } };
  595. if (!args.is_empty())
  596. set_local_variable("ARGV", AST::create<AST::ListValue>(move(string_argv)));
  597. if (!run_file(file_to_source, true))
  598. return 126;
  599. return 0;
  600. }
  601. int Shell::builtin_time(int argc, const char** argv)
  602. {
  603. Vector<const char*> args;
  604. Core::ArgsParser parser;
  605. parser.add_positional_argument(args, "Command to execute with arguments", "command", Core::ArgsParser::Required::Yes);
  606. if (!parser.parse(argc, const_cast<char**>(argv), false))
  607. return 1;
  608. AST::Command command;
  609. for (auto& arg : args)
  610. command.argv.append(arg);
  611. auto commands = expand_aliases({ move(command) });
  612. Core::ElapsedTimer timer;
  613. int exit_code = 1;
  614. timer.start();
  615. for (auto& job : run_commands(commands)) {
  616. block_on_job(job);
  617. exit_code = job.exit_code();
  618. }
  619. fprintf(stderr, "Time: %d ms\n", timer.elapsed());
  620. return exit_code;
  621. }
  622. int Shell::builtin_umask(int argc, const char** argv)
  623. {
  624. const char* mask_text = nullptr;
  625. Core::ArgsParser parser;
  626. parser.add_positional_argument(mask_text, "New mask (omit to get current mask)", "octal-mask", Core::ArgsParser::Required::No);
  627. if (!parser.parse(argc, const_cast<char**>(argv), false))
  628. return 1;
  629. if (!mask_text) {
  630. mode_t old_mask = umask(0);
  631. printf("%#o\n", old_mask);
  632. umask(old_mask);
  633. return 0;
  634. }
  635. unsigned mask;
  636. int matches = sscanf(mask_text, "%o", &mask);
  637. if (matches == 1) {
  638. umask(mask);
  639. return 0;
  640. }
  641. fprintf(stderr, "umask: Invalid mask '%s'\n", mask_text);
  642. return 1;
  643. }
  644. int Shell::builtin_wait(int argc, const char** argv)
  645. {
  646. Vector<const char*> job_ids;
  647. Core::ArgsParser parser;
  648. parser.add_positional_argument(job_ids, "Job IDs to wait for, defaults to all jobs if missing", "jobs", Core::ArgsParser::Required::No);
  649. if (!parser.parse(argc, const_cast<char**>(argv), false))
  650. return 1;
  651. Vector<NonnullRefPtr<Job>> jobs_to_wait_for;
  652. if (job_ids.is_empty()) {
  653. for (auto it : jobs)
  654. jobs_to_wait_for.append(it.value);
  655. } else {
  656. for (String id_s : job_ids) {
  657. auto id_opt = id_s.to_uint();
  658. if (id_opt.has_value()) {
  659. if (auto job = find_job(id_opt.value())) {
  660. jobs_to_wait_for.append(*job);
  661. continue;
  662. }
  663. }
  664. warnln("wait: invalid or nonexistent job id {}", id_s);
  665. return 1;
  666. }
  667. }
  668. for (auto& job : jobs_to_wait_for) {
  669. job->set_running_in_background(false);
  670. block_on_job(job);
  671. }
  672. return 0;
  673. }
  674. int Shell::builtin_unset(int argc, const char** argv)
  675. {
  676. Vector<const char*> vars;
  677. Core::ArgsParser parser;
  678. parser.add_positional_argument(vars, "List of variables", "variables", Core::ArgsParser::Required::Yes);
  679. if (!parser.parse(argc, const_cast<char**>(argv), false))
  680. return 1;
  681. for (auto& value : vars) {
  682. if (lookup_local_variable(value)) {
  683. unset_local_variable(value);
  684. } else {
  685. unsetenv(value);
  686. }
  687. }
  688. return 0;
  689. }
  690. bool Shell::run_builtin(const AST::Command& command, const NonnullRefPtrVector<AST::Rewiring>& rewirings, int& retval)
  691. {
  692. if (command.argv.is_empty())
  693. return false;
  694. if (!has_builtin(command.argv.first()))
  695. return false;
  696. Vector<const char*> argv;
  697. for (auto& arg : command.argv)
  698. argv.append(arg.characters());
  699. argv.append(nullptr);
  700. StringView name = command.argv.first();
  701. SavedFileDescriptors fds { rewirings };
  702. for (auto& rewiring : rewirings) {
  703. int rc = dup2(rewiring.old_fd, rewiring.new_fd);
  704. if (rc < 0) {
  705. perror("dup2(run)");
  706. return false;
  707. }
  708. }
  709. Core::EventLoop loop;
  710. setup_signals();
  711. #define __ENUMERATE_SHELL_BUILTIN(builtin) \
  712. if (name == #builtin) { \
  713. retval = builtin_##builtin(argv.size() - 1, argv.data()); \
  714. if (!has_error(ShellError::None)) \
  715. raise_error(m_error, m_error_description, command.position); \
  716. return true; \
  717. }
  718. ENUMERATE_SHELL_BUILTINS();
  719. #undef __ENUMERATE_SHELL_BUILTIN
  720. return false;
  721. }
  722. bool Shell::has_builtin(const StringView& name) const
  723. {
  724. #define __ENUMERATE_SHELL_BUILTIN(builtin) \
  725. if (name == #builtin) { \
  726. return true; \
  727. }
  728. ENUMERATE_SHELL_BUILTINS();
  729. #undef __ENUMERATE_SHELL_BUILTIN
  730. return false;
  731. }
  732. }