Builtin.cpp 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592
  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, char const**)
  25. {
  26. return 0;
  27. }
  28. int Shell::builtin_dump(int argc, char const** 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, char const** argv)
  36. {
  37. Vector<String> 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 = 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, char const** argv)
  65. {
  66. bool remove_all { false };
  67. Vector<String> 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, char const** 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, char const** argv)
  149. {
  150. Vector<String> 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.characters());
  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.characters());
  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.characters(), escape_token(fullpath).characters());
  198. continue;
  199. }
  200. something_not_found = true;
  201. printf("type: %s not found\n", command.characters());
  202. }
  203. if (something_not_found)
  204. return 1;
  205. else
  206. return 0;
  207. }
  208. int Shell::builtin_cd(int argc, char const** argv)
  209. {
  210. char const* 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. char const* 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, char const** 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. char const* path = cd_history.at(cd_history.size() - index).characters();
  274. char const* cd_args[] = { "cd", path, nullptr };
  275. return Shell::builtin_cd(2, cd_args);
  276. }
  277. int Shell::builtin_dirs(int argc, char const** 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<String> 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, char const** argv)
  320. {
  321. if (argc < 2) {
  322. warnln("Shell: No command given to exec");
  323. return 1;
  324. }
  325. Vector<char const*> 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, char const** 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, char const** argv)
  354. {
  355. Vector<String> 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 = 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, char const** argv)
  390. {
  391. Vector<String> 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, char const** 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, char const** 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<Job const*> 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, char const**)
  515. {
  516. for (size_t i = 0; i < m_editor->history().size(); ++i) {
  517. printf("%6zu %s\n", i + 1, m_editor->history()[i].entry.characters());
  518. }
  519. return 0;
  520. }
  521. int Shell::builtin_jobs(int argc, char const** 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, char const** 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, char const** 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. char const* 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, char const**)
  629. {
  630. print_path(cwd);
  631. fputc('\n', stdout);
  632. return 0;
  633. }
  634. int Shell::builtin_setopt(int argc, char const** 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, char const** 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, char const** argv)
  688. {
  689. char const* file_to_source = nullptr;
  690. Vector<String> 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. auto previous_argv = lookup_local_variable("ARGV");
  697. ScopeGuard guard { [&] {
  698. if (!args.is_empty())
  699. set_local_variable("ARGV", move(previous_argv));
  700. } };
  701. if (!args.is_empty())
  702. set_local_variable("ARGV", AST::make_ref_counted<AST::ListValue>(move(args)));
  703. if (!run_file(file_to_source, true))
  704. return 126;
  705. return 0;
  706. }
  707. int Shell::builtin_time(int argc, char const** argv)
  708. {
  709. AST::Command command;
  710. int number_of_iterations = 1;
  711. Core::ArgsParser parser;
  712. parser.add_option(number_of_iterations, "Number of iterations", "iterations", 'n', "iterations");
  713. parser.set_stop_on_first_non_option(true);
  714. parser.add_positional_argument(command.argv, "Command to execute with arguments", "command", Core::ArgsParser::Required::Yes);
  715. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  716. return 1;
  717. if (number_of_iterations < 1)
  718. return 1;
  719. auto commands = expand_aliases({ move(command) });
  720. AK::Statistics iteration_times;
  721. int exit_code = 1;
  722. for (int i = 0; i < number_of_iterations; ++i) {
  723. auto timer = Core::ElapsedTimer::start_new();
  724. for (auto& job : run_commands(commands)) {
  725. block_on_job(job);
  726. exit_code = job.exit_code();
  727. }
  728. iteration_times.add(timer.elapsed());
  729. }
  730. if (number_of_iterations == 1) {
  731. warnln("Time: {} ms", iteration_times.values().first());
  732. } else {
  733. AK::Statistics iteration_times_excluding_first;
  734. for (size_t i = 1; i < iteration_times.size(); i++)
  735. iteration_times_excluding_first.add(iteration_times.values()[i]);
  736. warnln("Timing report: {} ms", iteration_times.sum());
  737. warnln("==============");
  738. warnln("Command: {}", String::join(' ', command.argv));
  739. warnln("Average time: {:.2} ms (median: {}, stddev: {:.2}, min: {}, max:{})",
  740. iteration_times.average(), iteration_times.median(),
  741. iteration_times.standard_deviation(),
  742. iteration_times.min(), iteration_times.max());
  743. warnln("Excluding first: {:.2} ms (median: {}, stddev: {:.2}, min: {}, max:{})",
  744. iteration_times_excluding_first.average(), iteration_times_excluding_first.median(),
  745. iteration_times_excluding_first.standard_deviation(),
  746. iteration_times_excluding_first.min(), iteration_times_excluding_first.max());
  747. }
  748. return exit_code;
  749. }
  750. int Shell::builtin_umask(int argc, char const** argv)
  751. {
  752. char const* mask_text = nullptr;
  753. Core::ArgsParser parser;
  754. parser.add_positional_argument(mask_text, "New mask (omit to get current mask)", "octal-mask", Core::ArgsParser::Required::No);
  755. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  756. return 1;
  757. if (!mask_text) {
  758. mode_t old_mask = umask(0);
  759. printf("%#o\n", old_mask);
  760. umask(old_mask);
  761. return 0;
  762. }
  763. unsigned mask;
  764. int matches = sscanf(mask_text, "%o", &mask);
  765. if (matches == 1) {
  766. umask(mask);
  767. return 0;
  768. }
  769. warnln("umask: Invalid mask '{}'", mask_text);
  770. return 1;
  771. }
  772. int Shell::builtin_wait(int argc, char const** argv)
  773. {
  774. Vector<int> job_ids;
  775. Vector<bool> id_is_pid;
  776. Core::ArgsParser parser;
  777. parser.add_positional_argument(Core::ArgsParser::Arg {
  778. .help_string = "Job IDs or Jobspecs to wait for",
  779. .name = "job-id",
  780. .min_values = 0,
  781. .max_values = INT_MAX,
  782. .accept_value = [&](StringView value) -> bool {
  783. // Check if it's a pid (i.e. literal integer)
  784. if (auto number = value.to_uint(); number.has_value()) {
  785. job_ids.append(number.value());
  786. id_is_pid.append(true);
  787. return true;
  788. }
  789. // Check if it's a jobspec
  790. if (auto id = resolve_job_spec(value); id.has_value()) {
  791. job_ids.append(id.value());
  792. id_is_pid.append(false);
  793. return true;
  794. }
  795. return false;
  796. } });
  797. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  798. return 1;
  799. Vector<NonnullRefPtr<Job>> jobs_to_wait_for;
  800. for (size_t i = 0; i < job_ids.size(); ++i) {
  801. auto id = job_ids[i];
  802. auto is_pid = id_is_pid[i];
  803. auto job = find_job(id, is_pid);
  804. if (!job)
  805. warnln("wait: Job with id/pid {} not found", id);
  806. else
  807. jobs_to_wait_for.append(*job);
  808. }
  809. if (job_ids.is_empty()) {
  810. for (auto const& it : jobs)
  811. jobs_to_wait_for.append(it.value);
  812. }
  813. for (auto& job : jobs_to_wait_for) {
  814. job->set_running_in_background(false);
  815. block_on_job(job);
  816. }
  817. return 0;
  818. }
  819. int Shell::builtin_unset(int argc, char const** argv)
  820. {
  821. Vector<String> vars;
  822. Core::ArgsParser parser;
  823. parser.add_positional_argument(vars, "List of variables", "variables", Core::ArgsParser::Required::Yes);
  824. if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
  825. return 1;
  826. bool did_touch_path = false;
  827. for (auto& value : vars) {
  828. if (!did_touch_path && value == "PATH"sv)
  829. did_touch_path = true;
  830. if (lookup_local_variable(value)) {
  831. unset_local_variable(value);
  832. } else {
  833. unsetenv(value.characters());
  834. }
  835. }
  836. if (did_touch_path)
  837. cache_path();
  838. return 0;
  839. }
  840. int Shell::builtin_not(int argc, char const** argv)
  841. {
  842. // FIXME: Use ArgsParser when it can collect unrelated -arguments too.
  843. if (argc == 1)
  844. return 1;
  845. AST::Command command;
  846. for (size_t i = 1; i < (size_t)argc; ++i)
  847. command.argv.append(argv[i]);
  848. auto commands = expand_aliases({ move(command) });
  849. int exit_code = 1;
  850. auto found_a_job = false;
  851. for (auto& job : run_commands(commands)) {
  852. found_a_job = true;
  853. block_on_job(job);
  854. exit_code = job.exit_code();
  855. }
  856. // In case it was a function.
  857. if (!found_a_job)
  858. exit_code = last_return_code.value_or(0);
  859. return exit_code == 0 ? 1 : 0;
  860. }
  861. int Shell::builtin_kill(int argc, char const** argv)
  862. {
  863. // Simply translate the arguments and pass them to `kill'
  864. Vector<String> replaced_values;
  865. auto kill_path = find_in_path("kill");
  866. if (kill_path.is_empty()) {
  867. warnln("kill: `kill' not found in PATH");
  868. return 126;
  869. }
  870. replaced_values.append(kill_path);
  871. for (auto i = 1; i < argc; ++i) {
  872. if (auto job_id = resolve_job_spec(argv[i]); job_id.has_value()) {
  873. auto job = find_job(job_id.value());
  874. if (job) {
  875. replaced_values.append(String::number(job->pid()));
  876. } else {
  877. warnln("kill: Job with pid {} not found", job_id.value());
  878. return 1;
  879. }
  880. } else {
  881. replaced_values.append(argv[i]);
  882. }
  883. }
  884. // Now just run `kill'
  885. AST::Command command;
  886. command.argv = move(replaced_values);
  887. command.position = m_source_position.has_value() ? m_source_position->position : Optional<AST::Position> {};
  888. auto exit_code = 1;
  889. auto job_result = run_command(command);
  890. if (job_result.is_error()) {
  891. warnln("kill: Failed to run {}: {}", command.argv.first(), job_result.error());
  892. return exit_code;
  893. }
  894. if (auto job = job_result.release_value()) {
  895. block_on_job(job);
  896. exit_code = job->exit_code();
  897. }
  898. return exit_code;
  899. }
  900. bool Shell::run_builtin(const AST::Command& command, NonnullRefPtrVector<AST::Rewiring> const& rewirings, int& retval)
  901. {
  902. if (command.argv.is_empty())
  903. return false;
  904. if (!has_builtin(command.argv.first()))
  905. return false;
  906. Vector<char const*> argv;
  907. for (auto& arg : command.argv)
  908. argv.append(arg.characters());
  909. argv.append(nullptr);
  910. StringView name = command.argv.first();
  911. SavedFileDescriptors fds { rewirings };
  912. for (auto& rewiring : rewirings) {
  913. int rc = dup2(rewiring.old_fd, rewiring.new_fd);
  914. if (rc < 0) {
  915. perror("dup2(run)");
  916. return false;
  917. }
  918. }
  919. Core::EventLoop loop;
  920. setup_signals();
  921. if (name == ":"sv)
  922. name = "noop"sv;
  923. #define __ENUMERATE_SHELL_BUILTIN(builtin) \
  924. if (name == #builtin) { \
  925. retval = builtin_##builtin(argv.size() - 1, argv.data()); \
  926. if (!has_error(ShellError::None)) \
  927. raise_error(m_error, m_error_description, command.position); \
  928. fflush(stdout); \
  929. fflush(stderr); \
  930. return true; \
  931. }
  932. ENUMERATE_SHELL_BUILTINS();
  933. #undef __ENUMERATE_SHELL_BUILTIN
  934. return false;
  935. }
  936. int Shell::builtin_argsparser_parse(int argc, char const** argv)
  937. {
  938. // argsparser_parse
  939. // --add-option variable [--type (bool | string | i32 | u32 | double | size)] --help-string "" --long-name "" --short-name "" [--value-name "" <if not --type bool>] --list
  940. // --add-positional-argument variable [--type (bool | string | i32 | u32 | double | size)] ([--min n] [--max n] | [--required]) --help-string "" --value-name ""
  941. // [--general-help ""]
  942. // [--stop-on-first-non-option]
  943. // --
  944. // $args_to_parse
  945. Core::ArgsParser parser;
  946. Core::ArgsParser user_parser;
  947. Vector<char const*> arguments;
  948. Variant<Core::ArgsParser::Option, Core::ArgsParser::Arg, Empty> current;
  949. String current_variable;
  950. // if max > 1 or min < 1, or explicit `--list`.
  951. bool treat_arg_as_list = false;
  952. enum class Type {
  953. Bool,
  954. String,
  955. I32,
  956. U32,
  957. Double,
  958. Size,
  959. };
  960. auto type = Type::String;
  961. auto try_convert = [](StringView value, Type type) -> Optional<RefPtr<AST::Value>> {
  962. switch (type) {
  963. case Type::Bool:
  964. return AST::make_ref_counted<AST::StringValue>("true");
  965. case Type::String:
  966. return AST::make_ref_counted<AST::StringValue>(value);
  967. case Type::I32:
  968. if (auto number = value.to_int(); number.has_value())
  969. return AST::make_ref_counted<AST::StringValue>(String::number(*number));
  970. warnln("Invalid value for type i32: {}", value);
  971. return {};
  972. case Type::U32:
  973. case Type::Size:
  974. if (auto number = value.to_uint(); number.has_value())
  975. return AST::make_ref_counted<AST::StringValue>(String::number(*number));
  976. warnln("Invalid value for type u32|size: {}", value);
  977. return {};
  978. case Type::Double: {
  979. String string = value;
  980. char* endptr = nullptr;
  981. auto number = strtod(string.characters(), &endptr);
  982. if (endptr != string.characters() + string.length()) {
  983. warnln("Invalid value for type double: {}", value);
  984. return {};
  985. }
  986. return AST::make_ref_counted<AST::StringValue>(String::number(number));
  987. }
  988. default:
  989. VERIFY_NOT_REACHED();
  990. }
  991. };
  992. auto enlist = [&](auto name, auto value) -> NonnullRefPtr<AST::Value> {
  993. auto variable = lookup_local_variable(name);
  994. if (variable) {
  995. auto list = variable->resolve_as_list(*this);
  996. auto new_value = value->resolve_as_string(*this);
  997. list.append(move(new_value));
  998. return make_ref_counted<AST::ListValue>(move(list));
  999. }
  1000. return *value;
  1001. };
  1002. auto commit = [&] {
  1003. return current.visit(
  1004. [&](Core::ArgsParser::Option& option) {
  1005. if (!option.long_name && !option.short_name) {
  1006. warnln("Defined option must have at least one of --long-name or --short-name");
  1007. return false;
  1008. }
  1009. option.accept_value = [&, current_variable, treat_arg_as_list, type](auto value) {
  1010. auto result = try_convert(value, type);
  1011. if (result.has_value()) {
  1012. auto value = result.release_value();
  1013. if (treat_arg_as_list)
  1014. value = enlist(current_variable, move(value));
  1015. this->set_local_variable(current_variable, move(value), true);
  1016. return true;
  1017. }
  1018. return false;
  1019. };
  1020. user_parser.add_option(move(option));
  1021. type = Type::String;
  1022. treat_arg_as_list = false;
  1023. return true;
  1024. },
  1025. [&](Core::ArgsParser::Arg& arg) {
  1026. if (!arg.name) {
  1027. warnln("Defined positional argument must have a name");
  1028. return false;
  1029. }
  1030. arg.accept_value = [&, current_variable, treat_arg_as_list, type](auto value) {
  1031. auto result = try_convert(value, type);
  1032. if (result.has_value()) {
  1033. auto value = result.release_value();
  1034. if (treat_arg_as_list)
  1035. value = enlist(current_variable, move(value));
  1036. this->set_local_variable(current_variable, move(value), true);
  1037. return true;
  1038. }
  1039. return false;
  1040. };
  1041. user_parser.add_positional_argument(move(arg));
  1042. type = Type::String;
  1043. treat_arg_as_list = false;
  1044. return true;
  1045. },
  1046. [&](Empty) {
  1047. return true;
  1048. });
  1049. };
  1050. parser.add_option(Core::ArgsParser::Option {
  1051. .requires_argument = false,
  1052. .help_string = "Stop processing arguments after a non-argument parameter is seen",
  1053. .long_name = "stop-on-first-non-option",
  1054. .accept_value = [&](auto) {
  1055. user_parser.set_stop_on_first_non_option(true);
  1056. return true;
  1057. },
  1058. });
  1059. parser.add_option(Core::ArgsParser::Option {
  1060. .requires_argument = true,
  1061. .help_string = "Set the general help string for the parser",
  1062. .long_name = "general-help",
  1063. .value_name = "string",
  1064. .accept_value = [&](auto value) {
  1065. user_parser.set_general_help(value);
  1066. return true;
  1067. },
  1068. });
  1069. parser.add_option(Core::ArgsParser::Option {
  1070. .requires_argument = true,
  1071. .help_string = "Start describing an option",
  1072. .long_name = "add-option",
  1073. .value_name = "variable-name",
  1074. .accept_value = [&](auto name) {
  1075. if (!commit())
  1076. return false;
  1077. current = Core::ArgsParser::Option {};
  1078. current_variable = name;
  1079. if (current_variable.is_empty() || !all_of(current_variable, [](auto ch) { return ch == '_' || isalnum(ch); })) {
  1080. warnln("Option variable name must be a valid identifier");
  1081. return false;
  1082. }
  1083. return true;
  1084. },
  1085. });
  1086. parser.add_option(Core::ArgsParser::Option {
  1087. .requires_argument = false,
  1088. .help_string = "Accept multiple of the current option being given",
  1089. .long_name = "list",
  1090. .accept_value = [&](auto) {
  1091. if (!current.has<Core::ArgsParser::Option>()) {
  1092. warnln("Must be defining an option to use --list");
  1093. return false;
  1094. }
  1095. treat_arg_as_list = true;
  1096. return true;
  1097. },
  1098. });
  1099. parser.add_option(Core::ArgsParser::Option {
  1100. .requires_argument = true,
  1101. .help_string = "Define the type of the option or argument being described",
  1102. .long_name = "type",
  1103. .value_name = "type",
  1104. .accept_value = [&](auto name) {
  1105. if (current.has<Empty>()) {
  1106. warnln("Must be defining an argument or option to use --type");
  1107. return false;
  1108. }
  1109. StringView ty = name;
  1110. if (ty == "bool") {
  1111. if (auto option = current.get_pointer<Core::ArgsParser::Option>()) {
  1112. if (option->value_name != nullptr) {
  1113. warnln("Type 'bool' does not apply to options with a value (value name is set to {})", option->value_name);
  1114. return false;
  1115. }
  1116. }
  1117. type = Type::Bool;
  1118. } else if (ty == "string") {
  1119. type = Type::String;
  1120. } else if (ty == "i32") {
  1121. type = Type::I32;
  1122. } else if (ty == "u32") {
  1123. type = Type::U32;
  1124. } else if (ty == "double") {
  1125. type = Type::Double;
  1126. } else if (ty == "size") {
  1127. type = Type::Size;
  1128. } else {
  1129. warnln("Invalid type '{}', expected one of bool | string | i32 | u32 | double | size", ty);
  1130. return false;
  1131. }
  1132. if (type == Type::Bool)
  1133. set_local_variable(current_variable, make_ref_counted<AST::StringValue>("false"), true);
  1134. return true;
  1135. },
  1136. });
  1137. parser.add_option(Core::ArgsParser::Option {
  1138. .requires_argument = true,
  1139. .help_string = "Set the help string of the option or argument being defined",
  1140. .long_name = "help-string",
  1141. .value_name = "string",
  1142. .accept_value = [&](auto value) {
  1143. return current.visit(
  1144. [](Empty) {
  1145. warnln("Must be defining an option or argument to use --help-string");
  1146. return false;
  1147. },
  1148. [&](auto& option) {
  1149. option.help_string = value;
  1150. return true;
  1151. });
  1152. },
  1153. });
  1154. parser.add_option(Core::ArgsParser::Option {
  1155. .requires_argument = true,
  1156. .help_string = "Set the long name of the option being defined",
  1157. .long_name = "long-name",
  1158. .value_name = "name",
  1159. .accept_value = [&](auto value) {
  1160. auto option = current.get_pointer<Core::ArgsParser::Option>();
  1161. if (!option) {
  1162. warnln("Must be defining an option to use --long-name");
  1163. return false;
  1164. }
  1165. if (option->long_name) {
  1166. warnln("Repeated application of --long-name is not allowed, current option has long name set to \"{}\"", option->long_name);
  1167. return false;
  1168. }
  1169. option->long_name = value;
  1170. return true;
  1171. },
  1172. });
  1173. parser.add_option(Core::ArgsParser::Option {
  1174. .requires_argument = true,
  1175. .help_string = "Set the short name of the option being defined",
  1176. .long_name = "short-name",
  1177. .value_name = "char",
  1178. .accept_value = [&](auto value) {
  1179. auto option = current.get_pointer<Core::ArgsParser::Option>();
  1180. if (!option) {
  1181. warnln("Must be defining an option to use --short-name");
  1182. return false;
  1183. }
  1184. if (strlen(value) != 1) {
  1185. warnln("Option short name ('{}') must be exactly one character long", value);
  1186. return false;
  1187. }
  1188. if (option->short_name) {
  1189. warnln("Repeated application of --short-name is not allowed, current option has short name set to '{}'", option->short_name);
  1190. return false;
  1191. }
  1192. option->short_name = value[0];
  1193. return true;
  1194. },
  1195. });
  1196. parser.add_option(Core::ArgsParser::Option {
  1197. .requires_argument = true,
  1198. .help_string = "Set the value name of the option being defined",
  1199. .long_name = "value-name",
  1200. .value_name = "string",
  1201. .accept_value = [&](auto value) {
  1202. return current.visit(
  1203. [](Empty) {
  1204. warnln("Must be defining an option or a positional argument to use --value-name");
  1205. return false;
  1206. },
  1207. [&](Core::ArgsParser::Option& option) {
  1208. if (option.value_name) {
  1209. warnln("Repeated application of --value-name is not allowed, current option has value name set to \"{}\"", option.value_name);
  1210. return false;
  1211. }
  1212. if (type == Type::Bool) {
  1213. warnln("Options of type bool cannot have a value name");
  1214. return false;
  1215. }
  1216. option.value_name = value;
  1217. return true;
  1218. },
  1219. [&](Core::ArgsParser::Arg& arg) {
  1220. if (arg.name) {
  1221. warnln("Repeated application of --value-name is not allowed, current argument has value name set to \"{}\"", arg.name);
  1222. return false;
  1223. }
  1224. arg.name = value;
  1225. return true;
  1226. });
  1227. },
  1228. });
  1229. parser.add_option(Core::ArgsParser::Option {
  1230. .requires_argument = true,
  1231. .help_string = "Start describing a positional argument",
  1232. .long_name = "add-positional-argument",
  1233. .value_name = "variable",
  1234. .accept_value = [&](auto value) {
  1235. if (!commit())
  1236. return false;
  1237. current = Core::ArgsParser::Arg {};
  1238. current_variable = value;
  1239. if (current_variable.is_empty() || !all_of(current_variable, [](auto ch) { return ch == '_' || isalnum(ch); })) {
  1240. warnln("Argument variable name must be a valid identifier");
  1241. return false;
  1242. }
  1243. return true;
  1244. },
  1245. });
  1246. parser.add_option(Core::ArgsParser::Option {
  1247. .requires_argument = true,
  1248. .help_string = "Set the minimum required number of positional arguments for the argument being described",
  1249. .long_name = "min",
  1250. .value_name = "n",
  1251. .accept_value = [&](auto value) {
  1252. auto arg = current.get_pointer<Core::ArgsParser::Arg>();
  1253. if (!arg) {
  1254. warnln("Must be describing a positional argument to use --min");
  1255. return false;
  1256. }
  1257. auto number = StringView(value).to_uint();
  1258. if (!number.has_value()) {
  1259. warnln("Invalid value for --min: '{}', expected a non-negative number", value);
  1260. return false;
  1261. }
  1262. if (static_cast<unsigned>(arg->max_values) < *number) {
  1263. warnln("Invalid value for --min: {}, min must not be larger than max ({})", *number, arg->max_values);
  1264. return false;
  1265. }
  1266. arg->min_values = *number;
  1267. treat_arg_as_list = arg->max_values > 1 || arg->min_values < 1;
  1268. return true;
  1269. },
  1270. });
  1271. parser.add_option(Core::ArgsParser::Option {
  1272. .requires_argument = true,
  1273. .help_string = "Set the maximum required number of positional arguments for the argument being described",
  1274. .long_name = "max",
  1275. .value_name = "n",
  1276. .accept_value = [&](auto value) {
  1277. auto arg = current.get_pointer<Core::ArgsParser::Arg>();
  1278. if (!arg) {
  1279. warnln("Must be describing a positional argument to use --max");
  1280. return false;
  1281. }
  1282. auto number = StringView(value).to_uint();
  1283. if (!number.has_value()) {
  1284. warnln("Invalid value for --max: '{}', expected a non-negative number", value);
  1285. return false;
  1286. }
  1287. if (static_cast<unsigned>(arg->min_values) > *number) {
  1288. warnln("Invalid value for --max: {}, max must not be smaller than min ({})", *number, arg->min_values);
  1289. return false;
  1290. }
  1291. arg->max_values = *number;
  1292. treat_arg_as_list = arg->max_values > 1 || arg->min_values < 1;
  1293. return true;
  1294. },
  1295. });
  1296. parser.add_option(Core::ArgsParser::Option {
  1297. .requires_argument = false,
  1298. .help_string = "Mark the positional argument being described as required (shorthand for --min 1)",
  1299. .long_name = "required",
  1300. .accept_value = [&](auto) {
  1301. auto arg = current.get_pointer<Core::ArgsParser::Arg>();
  1302. if (!arg) {
  1303. warnln("Must be describing a positional argument to use --required");
  1304. return false;
  1305. }
  1306. arg->min_values = 1;
  1307. if (arg->max_values < arg->min_values)
  1308. arg->max_values = 1;
  1309. treat_arg_as_list = arg->max_values > 1 || arg->min_values < 1;
  1310. return true;
  1311. },
  1312. });
  1313. parser.add_positional_argument(arguments, "Arguments to parse via the described ArgsParser configuration", "arg", Core::ArgsParser::Required::No);
  1314. if (!parser.parse(argc, const_cast<char* const*>(argv), Core::ArgsParser::FailureBehavior::Ignore))
  1315. return 2;
  1316. if (!commit())
  1317. return 2;
  1318. if (!user_parser.parse(static_cast<int>(arguments.size()), const_cast<char* const*>(arguments.data()), Core::ArgsParser::FailureBehavior::Ignore))
  1319. return 1;
  1320. return 0;
  1321. }
  1322. bool Shell::has_builtin(StringView name) const
  1323. {
  1324. if (name == ":"sv)
  1325. return true;
  1326. #define __ENUMERATE_SHELL_BUILTIN(builtin) \
  1327. if (name == #builtin) { \
  1328. return true; \
  1329. }
  1330. ENUMERATE_SHELL_BUILTINS();
  1331. #undef __ENUMERATE_SHELL_BUILTIN
  1332. return false;
  1333. }
  1334. }