Builtin.cpp 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119
  1. /*
  2. * Copyright (c) 2020-2023, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "AST.h"
  7. #include "Formatter.h"
  8. #include "PosixParser.h"
  9. #include "Shell.h"
  10. #include <AK/DeprecatedString.h>
  11. #include <AK/LexicalPath.h>
  12. #include <AK/ScopeGuard.h>
  13. #include <AK/Statistics.h>
  14. #include <LibCore/ArgsParser.h>
  15. #include <LibCore/EventLoop.h>
  16. #include <LibCore/File.h>
  17. #include <LibCore/System.h>
  18. #include <LibFileSystem/FileSystem.h>
  19. #include <errno.h>
  20. #include <inttypes.h>
  21. #include <limits.h>
  22. #include <signal.h>
  23. #include <sys/wait.h>
  24. #include <unistd.h>
  25. extern char** environ;
  26. namespace Shell {
  27. ErrorOr<int> Shell::builtin_noop(Main::Arguments)
  28. {
  29. return 0;
  30. }
  31. ErrorOr<int> Shell::builtin_dump(Main::Arguments arguments)
  32. {
  33. bool posix = false;
  34. StringView source;
  35. Core::ArgsParser parser;
  36. parser.add_positional_argument(source, "Shell code to parse and dump", "source");
  37. parser.add_option(posix, "Use the POSIX parser", "posix", 'p');
  38. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  39. return 1;
  40. TRY((posix ? Posix::Parser { source }.parse() : Parser { source }.parse())->dump(0));
  41. return 0;
  42. }
  43. enum FollowSymlinks {
  44. Yes,
  45. No
  46. };
  47. static Vector<String> find_matching_executables_in_path(StringView filename, FollowSymlinks follow_symlinks = FollowSymlinks::No)
  48. {
  49. // Edge cases in which there are guaranteed no solutions
  50. if (filename.is_empty() || filename.contains('/'))
  51. return {};
  52. char const* path_str = getenv("PATH");
  53. auto path = DEFAULT_PATH_SV;
  54. if (path_str != nullptr) // maybe && *path_str
  55. path = { path_str, strlen(path_str) };
  56. Vector<String> executables;
  57. auto directories = path.split_view(':');
  58. for (auto directory : directories) {
  59. auto file = String::formatted("{}/{}", directory, filename).release_value_but_fixme_should_propagate_errors();
  60. if (follow_symlinks == FollowSymlinks::Yes) {
  61. auto path_or_error = FileSystem::read_link(file);
  62. if (!path_or_error.is_error())
  63. file = path_or_error.release_value();
  64. }
  65. if (!Core::System::access(file, X_OK).is_error())
  66. executables.append(move(file));
  67. }
  68. return executables;
  69. }
  70. ErrorOr<int> Shell::builtin_where(Main::Arguments arguments)
  71. {
  72. Vector<StringView> values_to_look_up;
  73. bool do_only_path_search { false };
  74. bool do_follow_symlinks { false };
  75. bool do_print_only_type { false };
  76. Core::ArgsParser parser;
  77. parser.add_positional_argument(values_to_look_up, "List of shell builtins, aliases or executables", "arguments");
  78. parser.add_option(do_only_path_search, "Search only for executables in the PATH environment variable", "path-only", 'p');
  79. parser.add_option(do_follow_symlinks, "Follow symlinks and print the symlink free path", "follow-symlink", 's');
  80. parser.add_option(do_print_only_type, "Print the argument type instead of a human readable description", "type", 'w');
  81. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  82. return 1;
  83. auto const look_up_alias = [do_only_path_search, &m_aliases = this->m_aliases](StringView alias) -> Optional<DeprecatedString> {
  84. if (do_only_path_search)
  85. return {};
  86. return m_aliases.get(alias);
  87. };
  88. auto const look_up_builtin = [do_only_path_search](StringView builtin) -> Optional<DeprecatedString> {
  89. if (do_only_path_search)
  90. return {};
  91. for (auto const& _builtin : builtin_names) {
  92. if (_builtin == builtin) {
  93. return builtin;
  94. }
  95. }
  96. return {};
  97. };
  98. bool at_least_one_succeded { false };
  99. for (auto const& argument : values_to_look_up) {
  100. auto const alias = look_up_alias(argument);
  101. if (alias.has_value()) {
  102. if (do_print_only_type)
  103. outln("{}: alias", argument);
  104. else
  105. outln("{}: aliased to {}", argument, alias.value());
  106. at_least_one_succeded = true;
  107. }
  108. auto const builtin = look_up_builtin(argument);
  109. if (builtin.has_value()) {
  110. if (do_print_only_type)
  111. outln("{}: builtin", builtin.value());
  112. else
  113. outln("{}: shell built-in command", builtin.value());
  114. at_least_one_succeded = true;
  115. }
  116. auto const executables = find_matching_executables_in_path(argument, do_follow_symlinks ? FollowSymlinks::Yes : FollowSymlinks::No);
  117. for (auto const& path : executables) {
  118. if (do_print_only_type)
  119. outln("{}: command", argument);
  120. else
  121. outln(path);
  122. at_least_one_succeded = true;
  123. }
  124. if (!at_least_one_succeded)
  125. warnln("{} not found", argument);
  126. }
  127. return at_least_one_succeded ? 0 : 1;
  128. }
  129. ErrorOr<int> Shell::builtin_reset(Main::Arguments)
  130. {
  131. destroy();
  132. initialize(m_is_interactive);
  133. // NOTE: As the last step before returning, clear (flush) the shell text entirely.
  134. fprintf(stderr, "\033[3J\033[H\033[2J");
  135. fflush(stderr);
  136. return 0;
  137. }
  138. ErrorOr<int> Shell::builtin_alias(Main::Arguments arguments)
  139. {
  140. Vector<DeprecatedString> aliases;
  141. Core::ArgsParser parser;
  142. parser.add_positional_argument(aliases, "List of name[=values]'s", "name[=value]", Core::ArgsParser::Required::No);
  143. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  144. return 1;
  145. if (aliases.is_empty()) {
  146. for (auto& alias : m_aliases)
  147. printf("%s=%s\n", escape_token(alias.key).characters(), escape_token(alias.value).characters());
  148. return 0;
  149. }
  150. bool fail = false;
  151. for (auto& argument : aliases) {
  152. auto parts = argument.split_limit('=', 2, SplitBehavior::KeepEmpty);
  153. if (parts.size() == 1) {
  154. auto alias = m_aliases.get(parts[0]);
  155. if (alias.has_value()) {
  156. printf("%s=%s\n", escape_token(parts[0]).characters(), escape_token(alias.value()).characters());
  157. } else {
  158. fail = true;
  159. }
  160. } else {
  161. m_aliases.set(parts[0], parts[1]);
  162. add_entry_to_cache({ RunnablePath::Kind::Alias, parts[0] });
  163. }
  164. }
  165. return fail ? 1 : 0;
  166. }
  167. ErrorOr<int> Shell::builtin_unalias(Main::Arguments arguments)
  168. {
  169. bool remove_all { false };
  170. Vector<DeprecatedString> aliases;
  171. Core::ArgsParser parser;
  172. parser.set_general_help("Remove alias from the list of aliases");
  173. parser.add_option(remove_all, "Remove all aliases", nullptr, 'a');
  174. parser.add_positional_argument(aliases, "List of aliases to remove", "alias", Core::ArgsParser::Required::Yes);
  175. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  176. return 1;
  177. if (remove_all) {
  178. m_aliases.clear();
  179. cache_path();
  180. return 0;
  181. }
  182. bool failed { false };
  183. for (auto& argument : aliases) {
  184. if (!m_aliases.contains(argument)) {
  185. warnln("unalias: {}: alias not found", argument);
  186. failed = true;
  187. continue;
  188. }
  189. m_aliases.remove(argument);
  190. remove_entry_from_cache(argument);
  191. }
  192. return failed ? 1 : 0;
  193. }
  194. ErrorOr<int> Shell::builtin_break(Main::Arguments arguments)
  195. {
  196. unsigned count = 1;
  197. Core::ArgsParser parser;
  198. parser.add_positional_argument(count, "Number of loops to 'break' out of", "count", Core::ArgsParser::Required::No);
  199. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  200. return 1;
  201. if (count != 1) {
  202. raise_error(ShellError::EvaluatedSyntaxError, "break: count must be equal to 1 (NYI)");
  203. return 1;
  204. }
  205. raise_error(ShellError::InternalControlFlowBreak, "POSIX break");
  206. return 0;
  207. }
  208. ErrorOr<int> Shell::builtin_continue(Main::Arguments arguments)
  209. {
  210. unsigned count = 1;
  211. Core::ArgsParser parser;
  212. parser.add_positional_argument(count, "Number of loops to 'continue' out of", "count", Core::ArgsParser::Required::No);
  213. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  214. return 1;
  215. if (count != 1) {
  216. raise_error(ShellError::EvaluatedSyntaxError, "continue: count must be equal to 1 (NYI)");
  217. return 1;
  218. }
  219. raise_error(ShellError::InternalControlFlowContinue, "POSIX continue");
  220. return 0;
  221. }
  222. ErrorOr<int> Shell::builtin_return(Main::Arguments arguments)
  223. {
  224. int return_code = last_return_code.value_or(0);
  225. Core::ArgsParser parser;
  226. parser.add_positional_argument(return_code, "Return code to return to the parent shell", "return-code", Core::ArgsParser::Required::No);
  227. parser.set_general_help("Return from a function or source file");
  228. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  229. return 1;
  230. last_return_code = return_code & 0xff;
  231. raise_error(ShellError::InternalControlFlowReturn, "POSIX return");
  232. return 0;
  233. }
  234. ErrorOr<int> Shell::builtin_bg(Main::Arguments arguments)
  235. {
  236. int job_id = -1;
  237. bool is_pid = false;
  238. Core::ArgsParser parser;
  239. parser.add_positional_argument(Core::ArgsParser::Arg {
  240. .help_string = "Job ID or Jobspec to run in background",
  241. .name = "job-id",
  242. .min_values = 0,
  243. .max_values = 1,
  244. .accept_value = [&](StringView value) -> bool {
  245. // Check if it's a pid (i.e. literal integer)
  246. if (auto number = value.to_uint(); number.has_value()) {
  247. job_id = number.value();
  248. is_pid = true;
  249. return true;
  250. }
  251. // Check if it's a jobspec
  252. if (auto id = resolve_job_spec(value); id.has_value()) {
  253. job_id = id.value();
  254. is_pid = false;
  255. return true;
  256. }
  257. return false;
  258. } });
  259. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  260. return 1;
  261. if (job_id == -1 && !jobs.is_empty())
  262. job_id = find_last_job_id();
  263. auto* job = const_cast<Job*>(find_job(job_id, is_pid));
  264. if (!job) {
  265. if (job_id == -1) {
  266. warnln("bg: No current job");
  267. } else {
  268. warnln("bg: Job with id/pid {} not found", job_id);
  269. }
  270. return 1;
  271. }
  272. job->set_running_in_background(true);
  273. job->set_should_announce_exit(true);
  274. job->set_shell_did_continue(true);
  275. dbgln("Resuming {} ({})", job->pid(), job->cmd());
  276. warnln("Resuming job {} - {}", job->job_id(), job->cmd());
  277. // Try using the PGID, but if that fails, just use the PID.
  278. if (killpg(job->pgid(), SIGCONT) < 0) {
  279. if (kill(job->pid(), SIGCONT) < 0) {
  280. perror("kill");
  281. return 1;
  282. }
  283. }
  284. return 0;
  285. }
  286. ErrorOr<String> Shell::serialize_function_definition(ShellFunction const& fn) const
  287. {
  288. StringBuilder builder;
  289. builder.append(fn.name);
  290. builder.append('(');
  291. for (size_t i = 0; i < fn.arguments.size(); i++) {
  292. builder.append(fn.arguments[i]);
  293. if (i != fn.arguments.size() - 1)
  294. builder.append(' ');
  295. }
  296. builder.append(") {\n"sv);
  297. if (fn.body) {
  298. auto formatter = Formatter(*fn.body);
  299. builder.append(formatter.format());
  300. }
  301. builder.append("\n}"sv);
  302. return builder.to_string();
  303. }
  304. ErrorOr<int> Shell::builtin_type(Main::Arguments arguments)
  305. {
  306. Vector<DeprecatedString> commands;
  307. bool dont_show_function_source = false;
  308. Core::ArgsParser parser;
  309. parser.set_general_help("Display information about commands.");
  310. parser.add_positional_argument(commands, "Command(s) to list info about", "command");
  311. parser.add_option(dont_show_function_source, "Do not show functions source.", "no-fn-source", 'f');
  312. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  313. return 1;
  314. bool something_not_found = false;
  315. for (auto& command : commands) {
  316. // check if it is an alias
  317. if (auto alias = m_aliases.get(command); alias.has_value()) {
  318. printf("%s is aliased to `%s`\n", escape_token(command).characters(), escape_token(alias.value()).characters());
  319. continue;
  320. }
  321. // check if it is a function
  322. if (auto function = m_functions.get(command); function.has_value()) {
  323. auto fn = function.value();
  324. printf("%s is a function\n", command.characters());
  325. if (!dont_show_function_source) {
  326. auto source = TRY(serialize_function_definition(fn));
  327. outln("{}", source);
  328. }
  329. continue;
  330. }
  331. // check if its a builtin
  332. if (has_builtin(command)) {
  333. printf("%s is a shell builtin\n", command.characters());
  334. continue;
  335. }
  336. // check if its an executable in PATH
  337. auto fullpath = Core::System::resolve_executable_from_environment(command);
  338. if (!fullpath.is_error()) {
  339. printf("%s is %s\n", command.characters(), escape_token(fullpath.release_value()).characters());
  340. continue;
  341. }
  342. something_not_found = true;
  343. printf("type: %s not found\n", command.characters());
  344. }
  345. if (something_not_found)
  346. return 1;
  347. else
  348. return 0;
  349. }
  350. ErrorOr<int> Shell::builtin_cd(Main::Arguments arguments)
  351. {
  352. StringView arg_path;
  353. Core::ArgsParser parser;
  354. parser.add_positional_argument(arg_path, "Path to change to", "path", Core::ArgsParser::Required::No);
  355. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  356. return 1;
  357. DeprecatedString new_path;
  358. if (arg_path.is_empty()) {
  359. new_path = home;
  360. } else {
  361. if (arg_path == "-"sv) {
  362. char* oldpwd = getenv("OLDPWD");
  363. if (oldpwd == nullptr)
  364. return 1;
  365. new_path = oldpwd;
  366. } else {
  367. new_path = arg_path;
  368. }
  369. }
  370. auto real_path_or_error = FileSystem::real_path(new_path);
  371. if (real_path_or_error.is_error()) {
  372. warnln("Invalid path '{}'", new_path);
  373. return 1;
  374. }
  375. auto real_path = real_path_or_error.release_value().to_deprecated_string();
  376. if (cd_history.is_empty() || cd_history.last() != real_path)
  377. cd_history.enqueue(real_path);
  378. auto path_relative_to_current_directory = LexicalPath::relative_path(real_path, cwd);
  379. if (path_relative_to_current_directory.is_empty())
  380. path_relative_to_current_directory = real_path;
  381. char const* path = path_relative_to_current_directory.characters();
  382. int rc = chdir(path);
  383. if (rc < 0) {
  384. if (errno == ENOTDIR) {
  385. warnln("Not a directory: {}", path);
  386. } else {
  387. warnln("chdir({}) failed: {}", path, strerror(errno));
  388. }
  389. return 1;
  390. }
  391. setenv("OLDPWD", cwd.characters(), 1);
  392. cwd = move(real_path);
  393. setenv("PWD", cwd.characters(), 1);
  394. return 0;
  395. }
  396. ErrorOr<int> Shell::builtin_cdh(Main::Arguments arguments)
  397. {
  398. int index = -1;
  399. Core::ArgsParser parser;
  400. parser.add_positional_argument(index, "Index of the cd history entry (leave out for a list)", "index", Core::ArgsParser::Required::No);
  401. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  402. return 1;
  403. if (index == -1) {
  404. if (cd_history.is_empty()) {
  405. warnln("cdh: no history available");
  406. return 0;
  407. }
  408. for (ssize_t i = cd_history.size() - 1; i >= 0; --i)
  409. printf("%zu: %s\n", cd_history.size() - i, cd_history.at(i).characters());
  410. return 0;
  411. }
  412. if (index < 1 || (size_t)index > cd_history.size()) {
  413. warnln("cdh: history index out of bounds: {} not in (0, {})", index, cd_history.size());
  414. return 1;
  415. }
  416. StringView path = cd_history.at(cd_history.size() - index);
  417. StringView cd_args[] = { "cd"sv, path };
  418. return Shell::builtin_cd({ .argc = 0, .argv = 0, .strings = cd_args });
  419. }
  420. ErrorOr<int> Shell::builtin_dirs(Main::Arguments arguments)
  421. {
  422. // The first directory in the stack is ALWAYS the current directory
  423. directory_stack.at(0) = cwd.characters();
  424. bool clear = false;
  425. bool print = false;
  426. bool number_when_printing = false;
  427. char separator = ' ';
  428. Vector<DeprecatedString> paths;
  429. Core::ArgsParser parser;
  430. parser.add_option(clear, "Clear the directory stack", "clear", 'c');
  431. parser.add_option(print, "Print directory entries one per line", "print", 'p');
  432. parser.add_option(number_when_printing, "Number the directories in the stack when printing", "number", 'v');
  433. parser.add_positional_argument(paths, "Extra paths to put on the stack", "path", Core::ArgsParser::Required::No);
  434. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  435. return 1;
  436. // -v implies -p
  437. print = print || number_when_printing;
  438. if (print) {
  439. if (!paths.is_empty()) {
  440. warnln("dirs: 'print' and 'number' are not allowed when any path is specified");
  441. return 1;
  442. }
  443. separator = '\n';
  444. }
  445. if (clear) {
  446. for (size_t i = 1; i < directory_stack.size(); i++)
  447. directory_stack.remove(i);
  448. }
  449. for (auto& path : paths)
  450. directory_stack.append(path);
  451. if (print || (!clear && paths.is_empty())) {
  452. int index = 0;
  453. for (auto& directory : directory_stack) {
  454. if (number_when_printing)
  455. printf("%d ", index++);
  456. print_path(directory);
  457. fputc(separator, stdout);
  458. }
  459. }
  460. return 0;
  461. }
  462. ErrorOr<int> Shell::builtin_eval(Main::Arguments arguments)
  463. {
  464. if (!m_in_posix_mode) {
  465. warnln("eval: This shell is not in POSIX mode");
  466. return 1;
  467. }
  468. StringBuilder joined_arguments;
  469. for (size_t i = 1; i < arguments.strings.size(); ++i) {
  470. if (i != 1)
  471. joined_arguments.append(' ');
  472. joined_arguments.append(arguments.strings[i]);
  473. }
  474. auto result = Posix::Parser { TRY(joined_arguments.to_string()) }.parse();
  475. if (!result)
  476. return 1;
  477. auto value = TRY(result->run(*this));
  478. if (value && value->is_job())
  479. block_on_job(static_cast<AST::JobValue*>(value.ptr())->job());
  480. return last_return_code.value_or(0);
  481. }
  482. ErrorOr<int> Shell::builtin_exec(Main::Arguments arguments)
  483. {
  484. if (arguments.strings.size() < 2) {
  485. warnln("Shell: No command given to exec");
  486. return 1;
  487. }
  488. TRY(execute_process(arguments.strings.slice(1)));
  489. // NOTE: Won't get here.
  490. return 0;
  491. }
  492. ErrorOr<int> Shell::builtin_exit(Main::Arguments arguments)
  493. {
  494. int exit_code = 0;
  495. Core::ArgsParser parser;
  496. parser.add_positional_argument(exit_code, "Exit code", "code", Core::ArgsParser::Required::No);
  497. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  498. return 1;
  499. if (m_is_interactive) {
  500. if (!jobs.is_empty()) {
  501. if (!m_should_ignore_jobs_on_next_exit) {
  502. warnln("Shell: You have {} active job{}, run 'exit' again to really exit.", jobs.size(), jobs.size() > 1 ? "s" : "");
  503. m_should_ignore_jobs_on_next_exit = true;
  504. return 1;
  505. }
  506. }
  507. }
  508. stop_all_jobs();
  509. if (m_is_interactive) {
  510. m_editor->save_history(get_history_path());
  511. printf("Good-bye!\n");
  512. }
  513. exit(exit_code);
  514. }
  515. ErrorOr<int> Shell::builtin_export(Main::Arguments arguments)
  516. {
  517. Vector<DeprecatedString> vars;
  518. Core::ArgsParser parser;
  519. parser.add_positional_argument(vars, "List of variable[=value]'s", "values", Core::ArgsParser::Required::No);
  520. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  521. return 1;
  522. if (vars.is_empty()) {
  523. for (size_t i = 0; environ[i]; ++i)
  524. puts(environ[i]);
  525. return 0;
  526. }
  527. for (auto& value : vars) {
  528. auto parts = value.split_limit('=', 2);
  529. if (parts.is_empty()) {
  530. warnln("Shell: Invalid export spec '{}', expected `variable=value' or `variable'", value);
  531. return 1;
  532. }
  533. if (parts.size() == 1) {
  534. auto value = TRY(look_up_local_variable(parts[0]));
  535. if (value) {
  536. auto values = TRY(const_cast<AST::Value&>(*value).resolve_as_list(*this));
  537. StringBuilder builder;
  538. builder.join(' ', values);
  539. parts.append(builder.to_deprecated_string());
  540. } else {
  541. // Ignore the export.
  542. continue;
  543. }
  544. }
  545. int setenv_return = setenv(parts[0].characters(), parts[1].characters(), 1);
  546. if (setenv_return != 0) {
  547. perror("setenv");
  548. return 1;
  549. }
  550. if (parts[0] == "PATH")
  551. cache_path();
  552. }
  553. return 0;
  554. }
  555. ErrorOr<int> Shell::builtin_glob(Main::Arguments arguments)
  556. {
  557. Vector<DeprecatedString> globs;
  558. Core::ArgsParser parser;
  559. parser.add_positional_argument(globs, "Globs to resolve", "glob");
  560. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  561. return 1;
  562. for (auto& glob : globs) {
  563. for (auto& expanded : expand_globs(glob, cwd))
  564. outln("{}", expanded);
  565. }
  566. return 0;
  567. }
  568. ErrorOr<int> Shell::builtin_fg(Main::Arguments arguments)
  569. {
  570. int job_id = -1;
  571. bool is_pid = false;
  572. Core::ArgsParser parser;
  573. parser.add_positional_argument(Core::ArgsParser::Arg {
  574. .help_string = "Job ID or Jobspec to bring to foreground",
  575. .name = "job-id",
  576. .min_values = 0,
  577. .max_values = 1,
  578. .accept_value = [&](StringView value) -> bool {
  579. // Check if it's a pid (i.e. literal integer)
  580. if (auto number = value.to_uint(); number.has_value()) {
  581. job_id = number.value();
  582. is_pid = true;
  583. return true;
  584. }
  585. // Check if it's a jobspec
  586. if (auto id = resolve_job_spec(value); id.has_value()) {
  587. job_id = id.value();
  588. is_pid = false;
  589. return true;
  590. }
  591. return false;
  592. } });
  593. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  594. return 1;
  595. if (job_id == -1 && !jobs.is_empty())
  596. job_id = find_last_job_id();
  597. RefPtr<Job> job = find_job(job_id, is_pid);
  598. if (!job) {
  599. if (job_id == -1) {
  600. warnln("fg: No current job");
  601. } else {
  602. warnln("fg: Job with id/pid {} not found", job_id);
  603. }
  604. return 1;
  605. }
  606. job->set_running_in_background(false);
  607. job->set_shell_did_continue(true);
  608. dbgln("Resuming {} ({})", job->pid(), job->cmd());
  609. warnln("Resuming job {} - {}", job->job_id(), job->cmd());
  610. tcsetpgrp(STDOUT_FILENO, job->pgid());
  611. tcsetpgrp(STDIN_FILENO, job->pgid());
  612. // Try using the PGID, but if that fails, just use the PID.
  613. if (killpg(job->pgid(), SIGCONT) < 0) {
  614. if (kill(job->pid(), SIGCONT) < 0) {
  615. perror("kill");
  616. return 1;
  617. }
  618. }
  619. block_on_job(job);
  620. if (job->exited())
  621. return job->exit_code();
  622. else
  623. return 0;
  624. }
  625. ErrorOr<int> Shell::builtin_disown(Main::Arguments arguments)
  626. {
  627. Vector<int> job_ids;
  628. Vector<bool> id_is_pid;
  629. Core::ArgsParser parser;
  630. parser.add_positional_argument(Core::ArgsParser::Arg {
  631. .help_string = "Job IDs or Jobspecs to disown",
  632. .name = "job-id",
  633. .min_values = 0,
  634. .max_values = INT_MAX,
  635. .accept_value = [&](StringView value) -> bool {
  636. // Check if it's a pid (i.e. literal integer)
  637. if (auto number = value.to_uint(); number.has_value()) {
  638. job_ids.append(number.value());
  639. id_is_pid.append(true);
  640. return true;
  641. }
  642. // Check if it's a jobspec
  643. if (auto id = resolve_job_spec(value); id.has_value()) {
  644. job_ids.append(id.value());
  645. id_is_pid.append(false);
  646. return true;
  647. }
  648. return false;
  649. } });
  650. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  651. return 1;
  652. if (job_ids.is_empty()) {
  653. job_ids.append(find_last_job_id());
  654. id_is_pid.append(false);
  655. }
  656. Vector<Job const*> jobs_to_disown;
  657. for (size_t i = 0; i < job_ids.size(); ++i) {
  658. auto id = job_ids[i];
  659. auto is_pid = id_is_pid[i];
  660. auto job = find_job(id, is_pid);
  661. if (!job)
  662. warnln("disown: Job with id/pid {} not found", id);
  663. else
  664. jobs_to_disown.append(job);
  665. }
  666. if (jobs_to_disown.is_empty()) {
  667. if (job_ids.is_empty())
  668. warnln("disown: No current job");
  669. // An error message has already been printed about the nonexistence of each listed job.
  670. return 1;
  671. }
  672. for (auto job : jobs_to_disown) {
  673. job->deactivate();
  674. if (!job->is_running_in_background())
  675. warnln("disown warning: Job {} is currently not running, 'kill -{} {}' to make it continue", job->job_id(), SIGCONT, job->pid());
  676. jobs.remove(job->pid());
  677. }
  678. return 0;
  679. }
  680. ErrorOr<int> Shell::builtin_history(Main::Arguments)
  681. {
  682. for (size_t i = 0; i < m_editor->history().size(); ++i) {
  683. printf("%6zu %s\n", i + 1, m_editor->history()[i].entry.characters());
  684. }
  685. return 0;
  686. }
  687. ErrorOr<int> Shell::builtin_jobs(Main::Arguments arguments)
  688. {
  689. bool list = false, show_pid = false;
  690. Core::ArgsParser parser;
  691. parser.add_option(list, "List all information about jobs", "list", 'l');
  692. parser.add_option(show_pid, "Display the PID of the jobs", "pid", 'p');
  693. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  694. return 1;
  695. Job::PrintStatusMode mode = Job::PrintStatusMode::Basic;
  696. if (show_pid)
  697. mode = Job::PrintStatusMode::OnlyPID;
  698. if (list)
  699. mode = Job::PrintStatusMode::ListAll;
  700. for (auto& it : jobs) {
  701. if (!it.value->print_status(mode))
  702. return 1;
  703. }
  704. return 0;
  705. }
  706. ErrorOr<int> Shell::builtin_popd(Main::Arguments arguments)
  707. {
  708. if (directory_stack.size() <= 1) {
  709. warnln("Shell: popd: directory stack empty");
  710. return 1;
  711. }
  712. bool should_not_switch = false;
  713. Core::ArgsParser parser;
  714. parser.add_option(should_not_switch, "Do not switch dirs", "no-switch", 'n');
  715. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  716. return 1;
  717. auto popped_path = directory_stack.take_last();
  718. if (should_not_switch)
  719. return 0;
  720. auto new_path = LexicalPath::canonicalized_path(popped_path);
  721. if (chdir(new_path.characters()) < 0) {
  722. warnln("chdir({}) failed: {}", new_path, strerror(errno));
  723. return 1;
  724. }
  725. cwd = new_path;
  726. return 0;
  727. }
  728. ErrorOr<int> Shell::builtin_pushd(Main::Arguments arguments)
  729. {
  730. StringBuilder path_builder;
  731. bool should_switch = true;
  732. // From the BASH reference manual: https://www.gnu.org/software/bash/manual/html_node/Directory-Stack-Builtins.html
  733. // With no arguments, pushd exchanges the top two directories and makes the new top the current directory.
  734. if (arguments.strings.size() == 1) {
  735. if (directory_stack.size() < 2) {
  736. warnln("pushd: no other directory");
  737. return 1;
  738. }
  739. DeprecatedString dir1 = directory_stack.take_first();
  740. DeprecatedString dir2 = directory_stack.take_first();
  741. directory_stack.insert(0, dir2);
  742. directory_stack.insert(1, dir1);
  743. int rc = chdir(dir2.characters());
  744. if (rc < 0) {
  745. warnln("chdir({}) failed: {}", dir2, strerror(errno));
  746. return 1;
  747. }
  748. cwd = dir2;
  749. return 0;
  750. }
  751. // Let's assume the user's typed in 'pushd <dir>'
  752. if (arguments.strings.size() == 2) {
  753. directory_stack.append(cwd.characters());
  754. if (arguments.strings[1].starts_with('/')) {
  755. path_builder.append(arguments.strings[1]);
  756. } else {
  757. path_builder.appendff("{}/{}", cwd, arguments.strings[1]);
  758. }
  759. } else if (arguments.strings.size() == 3) {
  760. directory_stack.append(cwd.characters());
  761. for (size_t i = 1; i < arguments.strings.size(); i++) {
  762. auto arg = arguments.strings[i];
  763. if (arg.starts_with('-')) {
  764. if (arg.starts_with('/')) {
  765. path_builder.append(arg);
  766. } else {
  767. path_builder.appendff("{}/{}", cwd, arg);
  768. }
  769. }
  770. if (arg == "-n"sv)
  771. should_switch = false;
  772. }
  773. }
  774. auto real_path = LexicalPath::canonicalized_path(path_builder.to_deprecated_string());
  775. struct stat st;
  776. int rc = stat(real_path.characters(), &st);
  777. if (rc < 0) {
  778. warnln("stat({}) failed: {}", real_path, strerror(errno));
  779. return 1;
  780. }
  781. if (!S_ISDIR(st.st_mode)) {
  782. warnln("Not a directory: {}", real_path);
  783. return 1;
  784. }
  785. if (should_switch) {
  786. int rc = chdir(real_path.characters());
  787. if (rc < 0) {
  788. warnln("chdir({}) failed: {}", real_path, strerror(errno));
  789. return 1;
  790. }
  791. cwd = real_path;
  792. }
  793. return 0;
  794. }
  795. ErrorOr<int> Shell::builtin_pwd(Main::Arguments)
  796. {
  797. print_path(cwd);
  798. fputc('\n', stdout);
  799. return 0;
  800. }
  801. ErrorOr<int> Shell::builtin_setopt(Main::Arguments arguments)
  802. {
  803. if (arguments.strings.size() == 1) {
  804. #define __ENUMERATE_SHELL_OPTION(name, default_, description) \
  805. if (options.name) \
  806. warnln("{}", #name);
  807. ENUMERATE_SHELL_OPTIONS();
  808. #undef __ENUMERATE_SHELL_OPTION
  809. }
  810. Core::ArgsParser parser;
  811. #define __ENUMERATE_SHELL_OPTION(name, default_, description) \
  812. bool name = false; \
  813. bool not_##name = false; \
  814. parser.add_option(name, "Enable: " description, #name, '\0'); \
  815. parser.add_option(not_##name, "Disable: " description, "no_" #name, '\0');
  816. ENUMERATE_SHELL_OPTIONS();
  817. #undef __ENUMERATE_SHELL_OPTION
  818. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  819. return 1;
  820. #define __ENUMERATE_SHELL_OPTION(name, default_, description) \
  821. if (name) \
  822. options.name = true; \
  823. if (not_##name) \
  824. options.name = false;
  825. ENUMERATE_SHELL_OPTIONS();
  826. #undef __ENUMERATE_SHELL_OPTION
  827. return 0;
  828. }
  829. ErrorOr<int> Shell::builtin_shift(Main::Arguments arguments)
  830. {
  831. int count = 1;
  832. Core::ArgsParser parser;
  833. parser.add_positional_argument(count, "Shift count", "count", Core::ArgsParser::Required::No);
  834. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  835. return 1;
  836. if (count < 1)
  837. return 0;
  838. auto argv_ = TRY(look_up_local_variable("ARGV"sv));
  839. if (!argv_) {
  840. warnln("shift: ARGV is unset");
  841. return 1;
  842. }
  843. if (!argv_->is_list())
  844. argv_ = adopt_ref(*new AST::ListValue({ const_cast<AST::Value&>(*argv_) }));
  845. auto& values = const_cast<AST::ListValue*>(static_cast<AST::ListValue const*>(argv_.ptr()))->values();
  846. if ((size_t)count > values.size()) {
  847. warnln("shift: shift count must not be greater than {}", values.size());
  848. return 1;
  849. }
  850. for (auto i = 0; i < count; ++i)
  851. (void)values.take_first();
  852. return 0;
  853. }
  854. ErrorOr<int> Shell::builtin_source(Main::Arguments arguments)
  855. {
  856. StringView file_to_source;
  857. Vector<StringView> args;
  858. Core::ArgsParser parser;
  859. parser.add_positional_argument(file_to_source, "File to read commands from", "path");
  860. parser.add_positional_argument(args, "ARGV for the sourced file", "args", Core::ArgsParser::Required::No);
  861. if (!parser.parse(arguments))
  862. return 1;
  863. auto previous_argv = TRY(look_up_local_variable("ARGV"sv));
  864. ScopeGuard guard { [&] {
  865. if (!args.is_empty())
  866. set_local_variable("ARGV", const_cast<AST::Value&>(*previous_argv));
  867. } };
  868. if (!args.is_empty()) {
  869. Vector<String> arguments;
  870. arguments.ensure_capacity(args.size());
  871. for (auto& arg : args)
  872. arguments.append(TRY(String::from_utf8(arg)));
  873. set_local_variable("ARGV", AST::make_ref_counted<AST::ListValue>(move(arguments)));
  874. }
  875. if (!run_file(file_to_source, true))
  876. return 126;
  877. return 0;
  878. }
  879. ErrorOr<int> Shell::builtin_time(Main::Arguments arguments)
  880. {
  881. Vector<StringView> args;
  882. int number_of_iterations = 1;
  883. Core::ArgsParser parser;
  884. parser.add_option(number_of_iterations, "Number of iterations", "iterations", 'n', "iterations");
  885. parser.set_stop_on_first_non_option(true);
  886. parser.add_positional_argument(args, "Command to execute with arguments", "command", Core::ArgsParser::Required::Yes);
  887. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  888. return 1;
  889. if (number_of_iterations < 1)
  890. return 1;
  891. AST::Command command;
  892. TRY(command.argv.try_ensure_capacity(args.size()));
  893. for (auto& arg : args)
  894. command.argv.append(TRY(String::from_utf8(arg)));
  895. auto commands = TRY(expand_aliases({ move(command) }));
  896. AK::Statistics iteration_times;
  897. int exit_code = 1;
  898. for (int i = 0; i < number_of_iterations; ++i) {
  899. auto timer = Core::ElapsedTimer::start_new();
  900. for (auto& job : run_commands(commands)) {
  901. block_on_job(job);
  902. exit_code = job->exit_code();
  903. }
  904. iteration_times.add(static_cast<float>(timer.elapsed()));
  905. }
  906. if (number_of_iterations == 1) {
  907. warnln("Time: {} ms", iteration_times.values().first());
  908. } else {
  909. AK::Statistics iteration_times_excluding_first;
  910. for (size_t i = 1; i < iteration_times.size(); i++)
  911. iteration_times_excluding_first.add(iteration_times.values()[i]);
  912. warnln("Timing report: {} ms", iteration_times.sum());
  913. warnln("==============");
  914. warnln("Command: {}", DeprecatedString::join(' ', arguments.strings));
  915. warnln("Average time: {:.2} ms (median: {}, stddev: {:.2}, min: {}, max:{})",
  916. iteration_times.average(), iteration_times.median(),
  917. iteration_times.standard_deviation(),
  918. iteration_times.min(), iteration_times.max());
  919. warnln("Excluding first: {:.2} ms (median: {}, stddev: {:.2}, min: {}, max:{})",
  920. iteration_times_excluding_first.average(), iteration_times_excluding_first.median(),
  921. iteration_times_excluding_first.standard_deviation(),
  922. iteration_times_excluding_first.min(), iteration_times_excluding_first.max());
  923. }
  924. return exit_code;
  925. }
  926. ErrorOr<int> Shell::builtin_umask(Main::Arguments arguments)
  927. {
  928. StringView mask_text;
  929. bool symbolic_output = false;
  930. Core::ArgsParser parser;
  931. parser.add_option(symbolic_output, "Produce symbolic output", "symbolic", 'S');
  932. parser.add_positional_argument(mask_text, "New mask (omit to get current mask)", "octal-mask", Core::ArgsParser::Required::No);
  933. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  934. return 1;
  935. auto parse_symbolic_digit = [](int digit) -> ErrorOr<String> {
  936. StringBuilder builder;
  937. if ((digit & 4) == 0)
  938. TRY(builder.try_append('r'));
  939. if ((digit & 2) == 0)
  940. TRY(builder.try_append('w'));
  941. if ((digit & 1) == 0)
  942. TRY(builder.try_append('x'));
  943. if (builder.is_empty())
  944. TRY(builder.try_append('-'));
  945. return builder.to_string();
  946. };
  947. if (mask_text.is_empty()) {
  948. mode_t old_mask = umask(0);
  949. if (symbolic_output) {
  950. StringBuilder builder;
  951. TRY(builder.try_append("u="sv));
  952. TRY(builder.try_append(TRY(parse_symbolic_digit(old_mask >> 6 & 7)).bytes()));
  953. TRY(builder.try_append(",g="sv));
  954. TRY(builder.try_append(TRY(parse_symbolic_digit(old_mask >> 3 & 7)).bytes()));
  955. TRY(builder.try_append(",o="sv));
  956. TRY(builder.try_append(TRY(parse_symbolic_digit(old_mask >> 0 & 7)).bytes()));
  957. outln("{}", builder.string_view());
  958. } else {
  959. outln("{:#o}", old_mask);
  960. }
  961. umask(old_mask);
  962. return 0;
  963. }
  964. unsigned mask = 0;
  965. auto matches = true;
  966. // FIXME: There's currently no way to parse an StringView as an octal integer,
  967. // when that becomes available, replace this code.
  968. for (auto byte : mask_text.bytes()) {
  969. if (isspace(byte))
  970. continue;
  971. if (!is_ascii_octal_digit(byte)) {
  972. matches = false;
  973. break;
  974. }
  975. mask = (mask << 3) + (byte - '0');
  976. }
  977. if (matches) {
  978. umask(mask);
  979. return 0;
  980. }
  981. warnln("umask: Invalid mask '{}'", mask_text);
  982. return 1;
  983. }
  984. ErrorOr<int> Shell::builtin_wait(Main::Arguments arguments)
  985. {
  986. Vector<int> job_ids;
  987. Vector<bool> id_is_pid;
  988. Core::ArgsParser parser;
  989. parser.add_positional_argument(Core::ArgsParser::Arg {
  990. .help_string = "Job IDs or Jobspecs to wait for",
  991. .name = "job-id",
  992. .min_values = 0,
  993. .max_values = INT_MAX,
  994. .accept_value = [&](StringView value) -> bool {
  995. // Check if it's a pid (i.e. literal integer)
  996. if (auto number = value.to_uint(); number.has_value()) {
  997. job_ids.append(number.value());
  998. id_is_pid.append(true);
  999. return true;
  1000. }
  1001. // Check if it's a jobspec
  1002. if (auto id = resolve_job_spec(value); id.has_value()) {
  1003. job_ids.append(id.value());
  1004. id_is_pid.append(false);
  1005. return true;
  1006. }
  1007. return false;
  1008. } });
  1009. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  1010. return 1;
  1011. Vector<NonnullRefPtr<Job>> jobs_to_wait_for;
  1012. for (size_t i = 0; i < job_ids.size(); ++i) {
  1013. auto id = job_ids[i];
  1014. auto is_pid = id_is_pid[i];
  1015. auto job = find_job(id, is_pid);
  1016. if (!job)
  1017. warnln("wait: Job with id/pid {} not found", id);
  1018. else
  1019. jobs_to_wait_for.append(*job);
  1020. }
  1021. if (job_ids.is_empty()) {
  1022. for (auto const& it : jobs)
  1023. jobs_to_wait_for.append(it.value);
  1024. }
  1025. for (auto& job : jobs_to_wait_for) {
  1026. job->set_running_in_background(false);
  1027. block_on_job(job);
  1028. }
  1029. return 0;
  1030. }
  1031. ErrorOr<int> Shell::builtin_unset(Main::Arguments arguments)
  1032. {
  1033. Vector<DeprecatedString> vars;
  1034. bool unset_only_variables = false; // POSIX only.
  1035. Core::ArgsParser parser;
  1036. parser.add_positional_argument(vars, "List of variables", "variables", Core::ArgsParser::Required::Yes);
  1037. if (m_in_posix_mode)
  1038. parser.add_option(unset_only_variables, "Unset only variables", "variables", 'v');
  1039. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  1040. return 1;
  1041. bool did_touch_path = false;
  1042. for (auto& value : vars) {
  1043. if (!did_touch_path && value == "PATH"sv)
  1044. did_touch_path = true;
  1045. if (TRY(look_up_local_variable(value)) != nullptr) {
  1046. unset_local_variable(value);
  1047. } else if (!unset_only_variables) {
  1048. unsetenv(value.characters());
  1049. }
  1050. }
  1051. if (did_touch_path)
  1052. cache_path();
  1053. return 0;
  1054. }
  1055. ErrorOr<int> Shell::builtin_set(Main::Arguments arguments)
  1056. {
  1057. if (arguments.strings.size() == 1) {
  1058. HashMap<String, String> vars;
  1059. StringBuilder builder;
  1060. for (auto& frame : m_local_frames) {
  1061. for (auto& var : frame->local_variables) {
  1062. builder.join(" "sv, TRY(var.value->resolve_as_list(*this)));
  1063. vars.set(TRY(String::from_deprecated_string(var.key)), TRY(builder.to_string()));
  1064. builder.clear();
  1065. }
  1066. }
  1067. struct Variable {
  1068. StringView name;
  1069. String value;
  1070. };
  1071. Vector<Variable> variables;
  1072. variables.ensure_capacity(vars.size());
  1073. for (auto& var : vars)
  1074. variables.unchecked_append({ var.key, var.value });
  1075. Vector<String> functions;
  1076. functions.ensure_capacity(m_functions.size());
  1077. for (auto& function : m_functions)
  1078. functions.unchecked_append(TRY(serialize_function_definition(function.value)));
  1079. quick_sort(variables, [](auto& a, auto& b) { return a.name < b.name; });
  1080. quick_sort(functions, [](auto& a, auto& b) { return a < b; });
  1081. for (auto& var : variables)
  1082. outln("{}={}", var.name, escape_token(var.value));
  1083. for (auto& fn : functions)
  1084. outln("{}", fn);
  1085. return 0;
  1086. }
  1087. Vector<StringView> argv_to_set;
  1088. Core::ArgsParser parser;
  1089. parser.set_stop_on_first_non_option(true);
  1090. parser.add_positional_argument(argv_to_set, "List of arguments", "arg", Core::ArgsParser::Required::No);
  1091. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
  1092. return 1;
  1093. if (!argv_to_set.is_empty() || arguments.strings.last() == "--"sv) {
  1094. Vector<String> argv;
  1095. argv.ensure_capacity(argv_to_set.size());
  1096. for (auto& arg : argv_to_set)
  1097. argv.unchecked_append(TRY(String::from_utf8(arg)));
  1098. set_local_variable("ARGV", AST::make_ref_counted<AST::ListValue>(move(argv)));
  1099. }
  1100. return 0;
  1101. }
  1102. ErrorOr<int> Shell::builtin_not(Main::Arguments arguments)
  1103. {
  1104. Vector<StringView> args;
  1105. Core::ArgsParser parser;
  1106. parser.set_stop_on_first_non_option(true);
  1107. parser.add_positional_argument(args, "Command to run followed by its arguments", "string");
  1108. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::Ignore))
  1109. return 1;
  1110. AST::Command command;
  1111. TRY(command.argv.try_ensure_capacity(args.size()));
  1112. for (auto& arg : args)
  1113. command.argv.unchecked_append(TRY(String::from_utf8(arg)));
  1114. auto commands = TRY(expand_aliases({ move(command) }));
  1115. int exit_code = 1;
  1116. auto found_a_job = false;
  1117. for (auto& job : run_commands(commands)) {
  1118. found_a_job = true;
  1119. block_on_job(job);
  1120. exit_code = job->exit_code();
  1121. }
  1122. // In case it was a function.
  1123. if (!found_a_job)
  1124. exit_code = last_return_code.value_or(0);
  1125. return exit_code == 0 ? 1 : 0;
  1126. }
  1127. ErrorOr<int> Shell::builtin_kill(Main::Arguments arguments)
  1128. {
  1129. // Simply translate the arguments and pass them to `kill'
  1130. Vector<String> replaced_values;
  1131. auto kill_path_or_error = Core::System::resolve_executable_from_environment("kill"sv);
  1132. if (kill_path_or_error.is_error()) {
  1133. warnln("kill: `kill' not found in PATH");
  1134. return 126;
  1135. }
  1136. replaced_values.append(kill_path_or_error.release_value());
  1137. for (size_t i = 1; i < arguments.strings.size(); ++i) {
  1138. if (auto job_id = resolve_job_spec(arguments.strings[i]); job_id.has_value()) {
  1139. auto job = find_job(job_id.value());
  1140. if (job) {
  1141. replaced_values.append(TRY(String::number(job->pid())));
  1142. } else {
  1143. warnln("kill: Job with pid {} not found", job_id.value());
  1144. return 1;
  1145. }
  1146. } else {
  1147. replaced_values.append(TRY(String::from_utf8(arguments.strings[i])));
  1148. }
  1149. }
  1150. // Now just run `kill'
  1151. AST::Command command;
  1152. command.argv = move(replaced_values);
  1153. command.position = m_source_position.has_value() ? m_source_position->position : Optional<AST::Position> {};
  1154. auto exit_code = 1;
  1155. auto job_result = run_command(command);
  1156. if (job_result.is_error()) {
  1157. warnln("kill: Failed to run {}: {}", command.argv.first(), job_result.error());
  1158. return exit_code;
  1159. }
  1160. if (auto job = job_result.release_value()) {
  1161. block_on_job(job);
  1162. exit_code = job->exit_code();
  1163. }
  1164. return exit_code;
  1165. }
  1166. ErrorOr<bool> Shell::run_builtin(const AST::Command& command, Vector<NonnullRefPtr<AST::Rewiring>> const& rewirings, int& retval)
  1167. {
  1168. if (command.argv.is_empty())
  1169. return false;
  1170. if (!has_builtin(command.argv.first()))
  1171. return false;
  1172. Vector<StringView> arguments;
  1173. TRY(arguments.try_ensure_capacity(command.argv.size()));
  1174. for (auto& arg : command.argv)
  1175. arguments.unchecked_append(arg);
  1176. Main::Arguments arguments_object {
  1177. .argc = 0,
  1178. .argv = nullptr,
  1179. .strings = arguments
  1180. };
  1181. StringView name = command.argv.first();
  1182. SavedFileDescriptors fds { rewirings };
  1183. for (auto& rewiring : rewirings) {
  1184. int rc = dup2(rewiring->old_fd, rewiring->new_fd);
  1185. if (rc < 0) {
  1186. perror("dup2(run)");
  1187. return false;
  1188. }
  1189. }
  1190. Core::EventLoop loop;
  1191. setup_signals();
  1192. if (name == ":"sv)
  1193. name = "noop"sv;
  1194. else if (m_in_posix_mode && name == "."sv)
  1195. name = "source"sv;
  1196. #define __ENUMERATE_SHELL_BUILTIN(builtin, _mode) \
  1197. if (name == #builtin) { \
  1198. retval = TRY(builtin_##builtin(arguments_object)); \
  1199. if (!has_error(ShellError::None)) \
  1200. raise_error(m_error, m_error_description, command.position); \
  1201. fflush(stdout); \
  1202. fflush(stderr); \
  1203. return true; \
  1204. }
  1205. ENUMERATE_SHELL_BUILTINS();
  1206. #undef __ENUMERATE_SHELL_BUILTIN
  1207. return false;
  1208. }
  1209. ErrorOr<int> Shell::builtin_argsparser_parse(Main::Arguments arguments)
  1210. {
  1211. // argsparser_parse
  1212. // --add-option variable [--type (bool | string | i32 | u32 | double | size)] --help-string "" --long-name "" --short-name "" [--value-name "" <if not --type bool>] --list
  1213. // --add-positional-argument variable [--type (bool | string | i32 | u32 | double | size)] ([--min n] [--max n] | [--required]) --help-string "" --value-name ""
  1214. // [--general-help ""]
  1215. // [--stop-on-first-non-option]
  1216. // --
  1217. // $args_to_parse
  1218. Core::ArgsParser parser;
  1219. Core::ArgsParser user_parser;
  1220. Vector<StringView> descriptors;
  1221. Variant<Core::ArgsParser::Option, Core::ArgsParser::Arg, Empty> current;
  1222. DeprecatedString help_string_storage;
  1223. DeprecatedString long_name_storage;
  1224. DeprecatedString value_name_storage;
  1225. DeprecatedString name_storage;
  1226. DeprecatedString current_variable;
  1227. // if max > 1 or min < 1, or explicit `--list`.
  1228. bool treat_arg_as_list = false;
  1229. enum class Type {
  1230. Bool,
  1231. String,
  1232. I32,
  1233. U32,
  1234. Double,
  1235. Size,
  1236. };
  1237. auto type = Type::String;
  1238. auto try_convert = [](StringView value, Type type) -> ErrorOr<Optional<RefPtr<AST::Value>>> {
  1239. switch (type) {
  1240. case Type::Bool:
  1241. return AST::make_ref_counted<AST::StringValue>("true"_string);
  1242. case Type::String:
  1243. return AST::make_ref_counted<AST::StringValue>(TRY(String::from_utf8(value)));
  1244. case Type::I32:
  1245. if (auto number = value.to_int(); number.has_value())
  1246. return AST::make_ref_counted<AST::StringValue>(TRY(String::number(*number)));
  1247. warnln("Invalid value for type i32: {}", value);
  1248. return OptionalNone {};
  1249. case Type::U32:
  1250. case Type::Size:
  1251. if (auto number = value.to_uint(); number.has_value())
  1252. return AST::make_ref_counted<AST::StringValue>(TRY(String::number(*number)));
  1253. warnln("Invalid value for type u32|size: {}", value);
  1254. return OptionalNone {};
  1255. case Type::Double: {
  1256. DeprecatedString string = value;
  1257. char* endptr = nullptr;
  1258. auto number = strtod(string.characters(), &endptr);
  1259. if (endptr != string.characters() + string.length()) {
  1260. warnln("Invalid value for type double: {}", value);
  1261. return OptionalNone {};
  1262. }
  1263. return AST::make_ref_counted<AST::StringValue>(TRY(String::number(number)));
  1264. }
  1265. default:
  1266. VERIFY_NOT_REACHED();
  1267. }
  1268. };
  1269. auto enlist = [&](auto name, auto value) -> ErrorOr<NonnullRefPtr<AST::Value>> {
  1270. auto variable = TRY(look_up_local_variable(name));
  1271. if (variable) {
  1272. auto list = TRY(const_cast<AST::Value&>(*variable).resolve_as_list(*this));
  1273. auto new_value = TRY(value->resolve_as_string(*this));
  1274. list.append(move(new_value));
  1275. return try_make_ref_counted<AST::ListValue>(move(list));
  1276. }
  1277. return *value;
  1278. };
  1279. // FIXME: We cannot return ErrorOr<T> from Core::ArgsParser::Option::accept_value(), fix the MUST's here whenever that function is ErrorOr-aware.
  1280. auto commit = [&] {
  1281. return current.visit(
  1282. [&](Core::ArgsParser::Option& option) {
  1283. if (!option.long_name && !option.short_name) {
  1284. warnln("Defined option must have at least one of --long-name or --short-name");
  1285. return false;
  1286. }
  1287. option.accept_value = [&, current_variable, treat_arg_as_list, type](StringView value) {
  1288. auto result = MUST(try_convert(value, type));
  1289. if (result.has_value()) {
  1290. auto value = result.release_value();
  1291. if (treat_arg_as_list)
  1292. value = MUST(enlist(current_variable, move(value)));
  1293. this->set_local_variable(current_variable, move(value), true);
  1294. return true;
  1295. }
  1296. return false;
  1297. };
  1298. user_parser.add_option(move(option));
  1299. type = Type::String;
  1300. treat_arg_as_list = false;
  1301. return true;
  1302. },
  1303. [&](Core::ArgsParser::Arg& arg) {
  1304. if (!arg.name) {
  1305. warnln("Defined positional argument must have a name");
  1306. return false;
  1307. }
  1308. arg.accept_value = [&, current_variable, treat_arg_as_list, type](StringView value) {
  1309. auto result = MUST(try_convert(value, type));
  1310. if (result.has_value()) {
  1311. auto value = result.release_value();
  1312. if (treat_arg_as_list)
  1313. value = MUST(enlist(current_variable, move(value)));
  1314. this->set_local_variable(current_variable, move(value), true);
  1315. return true;
  1316. }
  1317. return false;
  1318. };
  1319. user_parser.add_positional_argument(move(arg));
  1320. type = Type::String;
  1321. treat_arg_as_list = false;
  1322. return true;
  1323. },
  1324. [&](Empty) {
  1325. return true;
  1326. });
  1327. };
  1328. parser.add_option(Core::ArgsParser::Option {
  1329. .argument_mode = Core::ArgsParser::OptionArgumentMode::None,
  1330. .help_string = "Stop processing descriptors after a non-argument parameter is seen",
  1331. .long_name = "stop-on-first-non-option",
  1332. .accept_value = [&](auto) {
  1333. user_parser.set_stop_on_first_non_option(true);
  1334. return true;
  1335. },
  1336. });
  1337. parser.add_option(Core::ArgsParser::Option {
  1338. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  1339. .help_string = "Set the general help string for the parser",
  1340. .long_name = "general-help",
  1341. .value_name = "string",
  1342. .accept_value = [&](StringView value) {
  1343. VERIFY(strlen(value.characters_without_null_termination()) == value.length());
  1344. user_parser.set_general_help(value.characters_without_null_termination());
  1345. return true;
  1346. },
  1347. });
  1348. parser.add_option(Core::ArgsParser::Option {
  1349. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  1350. .help_string = "Start describing an option",
  1351. .long_name = "add-option",
  1352. .value_name = "variable-name",
  1353. .accept_value = [&](auto name) {
  1354. if (!commit())
  1355. return false;
  1356. current = Core::ArgsParser::Option {};
  1357. current_variable = name;
  1358. if (current_variable.is_empty() || !all_of(current_variable, [](auto ch) { return ch == '_' || isalnum(ch); })) {
  1359. warnln("Option variable name must be a valid identifier");
  1360. return false;
  1361. }
  1362. return true;
  1363. },
  1364. });
  1365. parser.add_option(Core::ArgsParser::Option {
  1366. .argument_mode = Core::ArgsParser::OptionArgumentMode::None,
  1367. .help_string = "Accept multiple of the current option being given",
  1368. .long_name = "list",
  1369. .accept_value = [&](auto) {
  1370. if (!current.has<Core::ArgsParser::Option>()) {
  1371. warnln("Must be defining an option to use --list");
  1372. return false;
  1373. }
  1374. treat_arg_as_list = true;
  1375. return true;
  1376. },
  1377. });
  1378. parser.add_option(Core::ArgsParser::Option {
  1379. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  1380. .help_string = "Define the type of the option or argument being described",
  1381. .long_name = "type",
  1382. .value_name = "type",
  1383. .accept_value = [&](StringView ty) {
  1384. if (current.has<Empty>()) {
  1385. warnln("Must be defining an argument or option to use --type");
  1386. return false;
  1387. }
  1388. if (ty == "bool") {
  1389. if (auto option = current.get_pointer<Core::ArgsParser::Option>()) {
  1390. if (option->value_name != nullptr) {
  1391. warnln("Type 'bool' does not apply to options with a value (value name is set to {})", option->value_name);
  1392. return false;
  1393. }
  1394. }
  1395. type = Type::Bool;
  1396. } else if (ty == "string") {
  1397. type = Type::String;
  1398. } else if (ty == "i32") {
  1399. type = Type::I32;
  1400. } else if (ty == "u32") {
  1401. type = Type::U32;
  1402. } else if (ty == "double") {
  1403. type = Type::Double;
  1404. } else if (ty == "size") {
  1405. type = Type::Size;
  1406. } else {
  1407. warnln("Invalid type '{}', expected one of bool | string | i32 | u32 | double | size", ty);
  1408. return false;
  1409. }
  1410. if (type == Type::Bool) {
  1411. set_local_variable(
  1412. current_variable,
  1413. make_ref_counted<AST::StringValue>("false"_string),
  1414. true);
  1415. }
  1416. return true;
  1417. },
  1418. });
  1419. parser.add_option(Core::ArgsParser::Option {
  1420. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  1421. .help_string = "Set the help string of the option or argument being defined",
  1422. .long_name = "help-string",
  1423. .value_name = "string",
  1424. .accept_value = [&](StringView value) {
  1425. return current.visit(
  1426. [](Empty) {
  1427. warnln("Must be defining an option or argument to use --help-string");
  1428. return false;
  1429. },
  1430. [&](auto& option) {
  1431. help_string_storage = value;
  1432. option.help_string = help_string_storage.characters();
  1433. return true;
  1434. });
  1435. },
  1436. });
  1437. parser.add_option(Core::ArgsParser::Option {
  1438. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  1439. .help_string = "Set the long name of the option being defined",
  1440. .long_name = "long-name",
  1441. .value_name = "name",
  1442. .accept_value = [&](StringView value) {
  1443. auto option = current.get_pointer<Core::ArgsParser::Option>();
  1444. if (!option) {
  1445. warnln("Must be defining an option to use --long-name");
  1446. return false;
  1447. }
  1448. if (option->long_name) {
  1449. warnln("Repeated application of --long-name is not allowed, current option has long name set to \"{}\"", option->long_name);
  1450. return false;
  1451. }
  1452. long_name_storage = value;
  1453. option->long_name = long_name_storage.characters();
  1454. return true;
  1455. },
  1456. });
  1457. parser.add_option(Core::ArgsParser::Option {
  1458. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  1459. .help_string = "Set the short name of the option being defined",
  1460. .long_name = "short-name",
  1461. .value_name = "char",
  1462. .accept_value = [&](StringView value) {
  1463. auto option = current.get_pointer<Core::ArgsParser::Option>();
  1464. if (!option) {
  1465. warnln("Must be defining an option to use --short-name");
  1466. return false;
  1467. }
  1468. if (value.length() != 1) {
  1469. warnln("Option short name ('{}') must be exactly one character long", value);
  1470. return false;
  1471. }
  1472. if (option->short_name) {
  1473. warnln("Repeated application of --short-name is not allowed, current option has short name set to '{}'", option->short_name);
  1474. return false;
  1475. }
  1476. option->short_name = value[0];
  1477. return true;
  1478. },
  1479. });
  1480. parser.add_option(Core::ArgsParser::Option {
  1481. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  1482. .help_string = "Set the value name of the option being defined",
  1483. .long_name = "value-name",
  1484. .value_name = "string",
  1485. .accept_value = [&](StringView value) {
  1486. return current.visit(
  1487. [](Empty) {
  1488. warnln("Must be defining an option or a positional argument to use --value-name");
  1489. return false;
  1490. },
  1491. [&](Core::ArgsParser::Option& option) {
  1492. if (option.value_name) {
  1493. warnln("Repeated application of --value-name is not allowed, current option has value name set to \"{}\"", option.value_name);
  1494. return false;
  1495. }
  1496. if (type == Type::Bool) {
  1497. warnln("Options of type bool cannot have a value name");
  1498. return false;
  1499. }
  1500. value_name_storage = value;
  1501. option.value_name = value_name_storage.characters();
  1502. return true;
  1503. },
  1504. [&](Core::ArgsParser::Arg& arg) {
  1505. if (arg.name) {
  1506. warnln("Repeated application of --value-name is not allowed, current argument has value name set to \"{}\"", arg.name);
  1507. return false;
  1508. }
  1509. name_storage = value;
  1510. arg.name = name_storage.characters();
  1511. return true;
  1512. });
  1513. },
  1514. });
  1515. parser.add_option(Core::ArgsParser::Option {
  1516. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  1517. .help_string = "Start describing a positional argument",
  1518. .long_name = "add-positional-argument",
  1519. .value_name = "variable",
  1520. .accept_value = [&](auto value) {
  1521. if (!commit())
  1522. return false;
  1523. current = Core::ArgsParser::Arg {};
  1524. current_variable = value;
  1525. if (current_variable.is_empty() || !all_of(current_variable, [](auto ch) { return ch == '_' || isalnum(ch); })) {
  1526. warnln("Argument variable name must be a valid identifier");
  1527. return false;
  1528. }
  1529. return true;
  1530. },
  1531. });
  1532. parser.add_option(Core::ArgsParser::Option {
  1533. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  1534. .help_string = "Set the minimum required number of positional descriptors for the argument being described",
  1535. .long_name = "min",
  1536. .value_name = "n",
  1537. .accept_value = [&](StringView value) {
  1538. auto arg = current.get_pointer<Core::ArgsParser::Arg>();
  1539. if (!arg) {
  1540. warnln("Must be describing a positional argument to use --min");
  1541. return false;
  1542. }
  1543. auto number = value.to_uint();
  1544. if (!number.has_value()) {
  1545. warnln("Invalid value for --min: '{}', expected a non-negative number", value);
  1546. return false;
  1547. }
  1548. if (static_cast<unsigned>(arg->max_values) < *number) {
  1549. warnln("Invalid value for --min: {}, min must not be larger than max ({})", *number, arg->max_values);
  1550. return false;
  1551. }
  1552. arg->min_values = *number;
  1553. treat_arg_as_list = arg->max_values > 1 || arg->min_values < 1;
  1554. return true;
  1555. },
  1556. });
  1557. parser.add_option(Core::ArgsParser::Option {
  1558. .argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
  1559. .help_string = "Set the maximum required number of positional descriptors for the argument being described",
  1560. .long_name = "max",
  1561. .value_name = "n",
  1562. .accept_value = [&](StringView value) {
  1563. auto arg = current.get_pointer<Core::ArgsParser::Arg>();
  1564. if (!arg) {
  1565. warnln("Must be describing a positional argument to use --max");
  1566. return false;
  1567. }
  1568. auto number = value.to_uint();
  1569. if (!number.has_value()) {
  1570. warnln("Invalid value for --max: '{}', expected a non-negative number", value);
  1571. return false;
  1572. }
  1573. if (static_cast<unsigned>(arg->min_values) > *number) {
  1574. warnln("Invalid value for --max: {}, max must not be smaller than min ({})", *number, arg->min_values);
  1575. return false;
  1576. }
  1577. arg->max_values = *number;
  1578. treat_arg_as_list = arg->max_values > 1 || arg->min_values < 1;
  1579. return true;
  1580. },
  1581. });
  1582. parser.add_option(Core::ArgsParser::Option {
  1583. .argument_mode = Core::ArgsParser::OptionArgumentMode::None,
  1584. .help_string = "Mark the positional argument being described as required (shorthand for --min 1)",
  1585. .long_name = "required",
  1586. .accept_value = [&](auto) {
  1587. auto arg = current.get_pointer<Core::ArgsParser::Arg>();
  1588. if (!arg) {
  1589. warnln("Must be describing a positional argument to use --required");
  1590. return false;
  1591. }
  1592. arg->min_values = 1;
  1593. if (arg->max_values < arg->min_values)
  1594. arg->max_values = 1;
  1595. treat_arg_as_list = arg->max_values > 1 || arg->min_values < 1;
  1596. return true;
  1597. },
  1598. });
  1599. parser.add_positional_argument(descriptors, "Arguments to parse via the described ArgsParser configuration", "arg", Core::ArgsParser::Required::No);
  1600. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::Ignore))
  1601. return 2;
  1602. if (!commit())
  1603. return 2;
  1604. if (!user_parser.parse(descriptors, Core::ArgsParser::FailureBehavior::Ignore))
  1605. return 1;
  1606. return 0;
  1607. }
  1608. ErrorOr<int> Shell::builtin_read(Main::Arguments arguments)
  1609. {
  1610. bool no_escape = false;
  1611. Vector<DeprecatedString> variables;
  1612. Core::ArgsParser parser;
  1613. parser.add_option(no_escape, "Do not interpret backslash escapes", "no-escape", 'r');
  1614. parser.add_positional_argument(variables, "Variables to read into", "variable", Core::ArgsParser::Required::Yes);
  1615. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::Ignore))
  1616. return 1;
  1617. auto split_by_any_of = " \t\n"_string;
  1618. if (auto const* value_from_env = getenv("IFS"); value_from_env)
  1619. split_by_any_of = TRY(String::from_utf8({ value_from_env, strlen(value_from_env) }));
  1620. else if (auto split_by_variable = TRY(look_up_local_variable("IFS"sv)); split_by_variable)
  1621. split_by_any_of = TRY(const_cast<AST::Value&>(*split_by_variable).resolve_as_string(*this));
  1622. auto file = TRY(Core::File::standard_input());
  1623. auto buffered_stream = TRY(Core::InputBufferedFile::create(move(file)));
  1624. StringBuilder builder;
  1625. ByteBuffer buffer;
  1626. enum class LineState {
  1627. Done,
  1628. EscapedNewline,
  1629. };
  1630. auto read_line = [&]() -> ErrorOr<LineState> {
  1631. if (m_is_interactive && isatty(STDIN_FILENO)) {
  1632. // Show prompt
  1633. warn("read: ");
  1634. }
  1635. size_t attempted_line_size = 32;
  1636. for (;;) {
  1637. auto result = buffered_stream->read_line(TRY(buffer.get_bytes_for_writing(attempted_line_size)));
  1638. if (result.is_error() && result.error().is_errno() && result.error().code() == EMSGSIZE) {
  1639. attempted_line_size *= 2;
  1640. continue;
  1641. }
  1642. auto used_bytes = TRY(move(result));
  1643. if (!no_escape && used_bytes.ends_with("\\\n"sv)) {
  1644. builder.append(used_bytes.substring_view(0, used_bytes.length() - 2));
  1645. return LineState::EscapedNewline;
  1646. }
  1647. if (used_bytes.ends_with("\n"sv))
  1648. used_bytes = used_bytes.substring_view(0, used_bytes.length() - 1);
  1649. builder.append(used_bytes);
  1650. return LineState::Done;
  1651. }
  1652. };
  1653. LineState state;
  1654. do {
  1655. state = TRY(read_line());
  1656. } while (state == LineState::EscapedNewline);
  1657. auto line = builder.string_view();
  1658. if (variables.size() == 1) {
  1659. set_local_variable(variables[0], make_ref_counted<AST::StringValue>(TRY(String::from_utf8(line))));
  1660. return 0;
  1661. }
  1662. auto fields = line.split_view_if(is_any_of(split_by_any_of), SplitBehavior::KeepEmpty);
  1663. for (size_t i = 0; i < variables.size(); ++i) {
  1664. auto& variable = variables[i];
  1665. StringView variable_value;
  1666. if (i >= fields.size())
  1667. variable_value = ""sv;
  1668. else if (i == variables.size() - 1)
  1669. variable_value = line.substring_view_starting_from_substring(fields[i]);
  1670. else
  1671. variable_value = fields[i];
  1672. set_local_variable(variable, make_ref_counted<AST::StringValue>(TRY(String::from_utf8(variable_value))));
  1673. }
  1674. return 0;
  1675. }
  1676. ErrorOr<int> Shell::builtin_run_with_env(Main::Arguments arguments)
  1677. {
  1678. Vector<DeprecatedString> environment_variables;
  1679. Vector<StringView> command_and_arguments;
  1680. Core::ArgsParser parser;
  1681. parser.add_option(environment_variables, "Environment variables to set", "env", 'e', "NAME=VALUE");
  1682. parser.add_positional_argument(command_and_arguments, "Command and arguments to run", "command", Core::ArgsParser::Required::Yes);
  1683. parser.set_stop_on_first_non_option(true);
  1684. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::Ignore))
  1685. return 1;
  1686. if (command_and_arguments.is_empty()) {
  1687. warnln("run_with_env: No command to run");
  1688. return 1;
  1689. }
  1690. AST::Command command;
  1691. TRY(command.argv.try_ensure_capacity(command_and_arguments.size()));
  1692. for (auto& arg : command_and_arguments)
  1693. command.argv.append(TRY(String::from_utf8(arg)));
  1694. auto commands = TRY(expand_aliases({ move(command) }));
  1695. HashMap<DeprecatedString, Optional<DeprecatedString>> old_environment_entries;
  1696. for (auto& variable : environment_variables) {
  1697. auto parts = variable.split_limit('=', 2, SplitBehavior::KeepEmpty);
  1698. if (parts.size() != 2) {
  1699. warnln("run_with_env: Invalid environment variable: '{}'", variable);
  1700. return 1;
  1701. }
  1702. DeprecatedString name = parts[0];
  1703. old_environment_entries.set(name, getenv(name.characters()) ?: Optional<DeprecatedString> {});
  1704. DeprecatedString value = parts[1];
  1705. setenv(name.characters(), value.characters(), 1);
  1706. }
  1707. int exit_code = 0;
  1708. for (auto& job : run_commands(commands)) {
  1709. block_on_job(job);
  1710. exit_code = job->exit_code();
  1711. }
  1712. for (auto& entry : old_environment_entries) {
  1713. if (entry.value.has_value())
  1714. setenv(entry.key.characters(), entry.value->characters(), 1);
  1715. else
  1716. unsetenv(entry.key.characters());
  1717. }
  1718. return exit_code;
  1719. }
  1720. ErrorOr<int> Shell::builtin_shell_set_active_prompt(Main::Arguments arguments)
  1721. {
  1722. StringView new_prompt;
  1723. Core::ArgsParser parser;
  1724. parser.add_positional_argument(new_prompt, "New prompt text", "prompt", Core::ArgsParser::Required::Yes);
  1725. if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::Ignore))
  1726. return 1;
  1727. if (!m_editor) {
  1728. warnln("shell_set_active_prompt: No active prompt");
  1729. return 1;
  1730. }
  1731. if (m_editor->is_editing())
  1732. m_editor->set_prompt(new_prompt);
  1733. else
  1734. m_next_scheduled_prompt_text = new_prompt;
  1735. return 0;
  1736. }
  1737. bool Shell::has_builtin(StringView name) const
  1738. {
  1739. if (name == ":"sv || (m_in_posix_mode && name == "."sv))
  1740. return true;
  1741. #define __ENUMERATE_SHELL_BUILTIN(builtin, mode) \
  1742. if (name == #builtin) { \
  1743. if (POSIXModeRequirement::mode == POSIXModeRequirement::InAllModes) \
  1744. return true; \
  1745. return m_in_posix_mode; \
  1746. }
  1747. ENUMERATE_SHELL_BUILTINS();
  1748. #undef __ENUMERATE_SHELL_BUILTIN
  1749. return false;
  1750. }
  1751. }