builtin.cc 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. // The functions that implement each of the builtin executables
  2. //
  3. // Copyright (C) 2006-2019 Samuel Newbold
  4. #include <algorithm>
  5. #include <climits>
  6. #include <cfloat>
  7. #include <cstdlib>
  8. #include <dirent.h>
  9. #include <errno.h>
  10. #include <fcntl.h>
  11. #include <fstream>
  12. #include <iterator>
  13. #include <list>
  14. #include <limits>
  15. #include <map>
  16. #include <set>
  17. #include <stdio.h>
  18. #include <string>
  19. #include <sstream>
  20. #include <sys/stat.h>
  21. #include <sys/stat.h>
  22. #include <sys/types.h>
  23. #include <unistd.h>
  24. #include <vector>
  25. extern char** environ;
  26. #include "arg_spec.h"
  27. #include "rwsh_stream.h"
  28. #include "variable_map.h"
  29. #include "argm.h"
  30. #include "arg_script.h"
  31. #include "builtin.h"
  32. #include "call_stack.h"
  33. #include "clock.h"
  34. #include "command_stream.h"
  35. #include "executable.h"
  36. #include "executable_map.h"
  37. #include "file_stream.h"
  38. #include "pipe_stream.h"
  39. #include "plumber.h"
  40. #include "prototype.h"
  41. #include "read_dir.cc"
  42. #include "rwshlib.h"
  43. #include "selection.h"
  44. #include "substitution_stream.h"
  45. #include "tokenize.cc"
  46. #include "function.h"
  47. namespace {
  48. const double dummy_double = -4.2e+42;
  49. const double dummy_int = -424242424;
  50. std::string fallback_message = "Exception for failed handler. "
  51. "Original exception with call stack:\n";
  52. std::string my_dtostr(double in) {
  53. std::ostringstream ss;
  54. ss <<in;
  55. return ss.str();}
  56. } // end unnamed namespace
  57. double my_strtod(const std::string& val, Error_list& errors) {
  58. try {return my_strtod(val);}
  59. catch (E_generic) {errors.add_error(Exception(Argm::Not_a_number, val));}
  60. catch (E_nan) {errors.add_error(Exception(Argm::Not_a_number, val));}
  61. catch (E_range) {errors.add_error(Exception(Argm::Input_range, val));}
  62. return dummy_double;} // any use of this value is a failure to stop on error
  63. int my_strtoi(const std::string& val, int min, int max, Error_list& errors) {
  64. try {return my_strtoi(val, min, max);}
  65. catch (E_generic) {errors.add_error(Exception(Argm::Not_a_number, val));}
  66. catch (E_nan) {errors.add_error(Exception(Argm::Not_a_number, val));}
  67. catch (E_not_an_integer) {
  68. errors.add_error(Exception(Argm::Number_not_an_integer, val));}
  69. catch (E_range) {errors.add_error(Exception(Argm::Input_range, val));}
  70. return dummy_int;} // any use of this value is a failure to stop on error
  71. // print the number of arguments passed
  72. void b_argc(const Argm& argm, Error_list& exceptions) {
  73. argm.output <<argm.argc()-1;}
  74. // add binary to executable map with name $1
  75. void b_binary(const Argm& argm, Error_list& exceptions) {
  76. Argm lookup(argm.subrange(1), nullptr, argm.parent_map());
  77. struct stat sb;
  78. if (stat(lookup[0].c_str(), &sb))
  79. exceptions.add_error(Exception(Argm::Binary_does_not_exist, lookup[0]));
  80. else if (executable_map.find_second(lookup))
  81. exceptions.add_error(Exception(Argm::Executable_already_exists, lookup[0]));
  82. else executable_map.set(new Binary(lookup[0]));}
  83. // change the current directory to the one given
  84. void b_cd(const Argm& argm, Error_list& exceptions) {
  85. errno = 0;
  86. if (!chdir(argm[1].c_str()));
  87. else if (errno == ENOENT)
  88. exceptions.add_error(Exception(Argm::Directory_not_found, argm[1]));
  89. else if (errno == ENOTDIR)
  90. exceptions.add_error(Exception(Argm::Not_a_directory, argm[1]));
  91. // not tested. time for additional errors to be differentiated
  92. else exceptions.add_error(Exception(Argm::Internal_error, errno));
  93. errno = 0;}
  94. // run the argument function, collecting exceptions to be thrown as a group
  95. // at the end, but terminating immediately if one of the specified exceptions
  96. // are thrown
  97. void b_collect_errors_except(const Argm& argm, Error_list& exceptions) {
  98. global_stack.collect_errors_core(argm, true, exceptions);}
  99. // run the argument function, collecting exceptions to be thrown as a group
  100. // at the end, but only until an exception is not one of the specified
  101. // exceptions
  102. void b_collect_errors_only(const Argm& argm, Error_list& exceptions) {
  103. global_stack.collect_errors_core(argm, false, exceptions);}
  104. // echo arguments to standard output without space separation
  105. void b_combine(const Argm& argm, Error_list& exceptions) {
  106. for (auto i: argm.subrange(1)) argm.output <<i;
  107. argm.output.flush();}
  108. // disable readline regardless of status
  109. void b_disable_readline(const Argm& argm, Error_list& exceptions) {
  110. readline_enabled = false;}
  111. // echo arguments to standard output separated by space
  112. void b_echo(const Argm& argm, Error_list& exceptions) {
  113. for (auto i: argm.subrange(1, 1)) argm.output <<i <<" ";
  114. argm.output <<argm.back();
  115. argm.output.flush();}
  116. // enable readline regardless of status
  117. void b_enable_readline(const Argm& argm, Error_list& exceptions) {
  118. readline_enabled = true;}
  119. // echo arguments to standard error separated by space
  120. void b_error(const Argm& argm, Error_list& exceptions) {
  121. for (auto i: argm.subrange(1, 1)) argm.error <<i <<" ";
  122. argm.error <<argm.back();
  123. argm.error.flush();}
  124. // replace the shell with the given binary
  125. void b_exec(const Argm& argm, Error_list& exceptions) {
  126. int input = argm.input.fd(),
  127. output = argm.output.fd(),
  128. error = argm.error.fd();
  129. if (dup2(input, 0) < 0) argm.error <<"dup2 didn't like changing input\n";
  130. if (dup2(output, 1) < 0) argm.error <<"dup2 didn't like changing output\n";
  131. if (dup2(error, 2) < 0) argm.error <<"dup2 didn't like changing error\n";
  132. Old_argv old_argv(argm.subrange(1));
  133. std::vector<char *>env;
  134. argm.export_env(env);
  135. execve(*old_argv.argv(), old_argv.argv(), &env[0]);
  136. // execve does not return on success, so what's the error?
  137. if (errno == ENOENT) // depends on the error handlers to indicate failure
  138. exceptions.add_error(Exception(Argm::Binary_does_not_exist, argm[1]));
  139. else if (errno == EACCES || errno == ENOEXEC)
  140. exceptions.add_error(Exception(Argm::Not_executable, argm[1]));
  141. else exceptions.add_error(Exception(Argm::Exec_failed, argm[1], errno));}
  142. // print the number of times that the executable in the executable map with
  143. // key $1 has been run
  144. void b_execution_count(const Argm& argm, Error_list& exceptions) {
  145. Argm lookup(argm.subrange(1), argm.argfunction(), argm.parent_map());
  146. Base_executable* focus = executable_map.find_second(lookup);
  147. if (focus) {
  148. argm.output <<focus->execution_count();
  149. argm.output.flush();}
  150. else throw Exception(Argm::Function_not_found, argm[1]);}
  151. // exit the shell with the specified exit value
  152. void b_exit(const Argm& argm, Error_list& exceptions) {
  153. int val = my_strtoi(argm[1], -255, 255, exceptions);
  154. if (global_stack.unwind_stack()) return; // another chance to give status
  155. global_stack.request_exit(val);
  156. if (gc_state.in_if_block) gc_state.exception_thrown = true;}
  157. /* Exception handler for exceptions that trigger exceptions in their exception
  158. handler, without possibly itself triggering exceptions. This prints its
  159. arguments prefixed by a configurable message.*/
  160. void b_fallback_handler(const Argm& argm, Error_list& exceptions) {
  161. argm.output <<fallback_message;
  162. for (auto i: argm.subrange(1, 1)) argm.output <<i <<" ";
  163. argm.output <<argm.back() <<"\n";
  164. argm.output.flush();}
  165. #if 0
  166. argm.error <<*i <<" ";
  167. argm.error <<argm.back() <<"\n";
  168. argm.error.flush();}
  169. #endif
  170. // run the argfunction for each argument, passing that value as the argument
  171. void b_for(const Argm& argm, Error_list& exceptions) {
  172. Argv prototype_argv;
  173. tokenize_words(argm[argm.argc()-1], std::back_inserter(prototype_argv));
  174. Function temp_func(".for(body)", prototype_argv, *argm.argfunction());
  175. for (auto i: argm.subrange(1,1)) {
  176. Argm body(argm.parent_map(), argm.input, argm.output, argm.error);
  177. body.push_back(".for(body)");
  178. tokenize_words(i, std::back_inserter(body));
  179. temp_func(body, exceptions);
  180. (void) global_stack.remove_exceptions(".continue", exceptions);
  181. if (global_stack.remove_exceptions(".break", exceptions) ||
  182. global_stack.unwind_stack()) return;}}
  183. // run the argfunction for line of input, passing that line as the argm
  184. void b_for_each_line(const Argm& argm, Error_list& exceptions) {
  185. Argv prototype_argv(argm.subrange(1));
  186. Function temp_func(".for_each_line(body)", prototype_argv,
  187. *argm.argfunction());
  188. while(!argm.input.fail()) {
  189. std::string line;
  190. // shouldn't interfere with input being consumed by this builtin
  191. Argm body({".for_each_line(body)"}, nullptr, argm.parent_map(),
  192. default_input, argm.output, argm.error);
  193. argm.input.getline(line);
  194. if (argm.input.fail() && !line.size()) break;
  195. tokenize(line, std::back_inserter(body),
  196. std::bind2nd(std::equal_to<char>(), ' '));
  197. temp_func(body, exceptions);
  198. (void) global_stack.remove_exceptions(".continue", exceptions);
  199. if (global_stack.remove_exceptions(".break", exceptions) ||
  200. global_stack.unwind_stack()) return;}}
  201. void b_fork(const Argm& argm, Error_list& exceptions) {
  202. int status = 0;
  203. if (!fork()) {
  204. plumber.after_fork();
  205. Argm lookup(argm.subrange(1), argm.argfunction(),
  206. argm.parent_map(),
  207. argm.input, argm.output.child_stream(), argm.error);
  208. executable_map.run(lookup, exceptions);
  209. if (global_stack.unwind_stack()) {
  210. global_stack.exception_handler(exceptions);
  211. if (!global_stack.exit_value()) global_stack.request_exit(-1);}
  212. executable_map.unused_var_check_at_exit();
  213. std::exit(global_stack.exit_value());}
  214. else plumber.wait(&status);
  215. if (WIFEXITED(status) && WEXITSTATUS(status))
  216. exceptions.add_error(Exception(Argm::Return_code, WEXITSTATUS(status)));}
  217. // add argfunction to executable map with name $1 and arguments $*2
  218. // the arguments must include all flags that can be passed to this function
  219. void b_function(const Argm& argm, Error_list& exceptions) {
  220. Argm lookup(argm.subrange(1, argm.argc()-2), nullptr, argm.parent_map());
  221. Base_executable *e = executable_map.find_second(lookup);
  222. if (is_argfunction_name(argm[1]) || dynamic_cast<Builtin*>(e))
  223. exceptions.add_error(Exception(Argm::Illegal_function_name, argm[1]));
  224. else executable_map.set(new Function(argm[1], argm.subrange(2),
  225. *argm.argfunction()));}
  226. // Get the configurable message for fallback_handler
  227. void b_get_fallback_message(const Argm& argm, Error_list& exceptions) {
  228. argm.output <<fallback_message;}
  229. // Get the number of exceptions that can be thrown inside .collect_errors_*
  230. // before they exit early
  231. void b_get_max_collectible_exceptions(const Argm& argm,Error_list& exceptions) {
  232. argm.output <<global_stack.max_collect;}
  233. // Get the number of exceptions that can be thrown by catch blocks after
  234. // max_collectible_exceptions have already been thrown
  235. void b_get_max_extra_exceptions(const Argm& argm, Error_list& exceptions) {
  236. argm.output <<global_stack.max_extra;}
  237. // Get the maximum number of nesting levels where functions call functions
  238. // before completing.
  239. void b_get_max_nesting(const Argm& argm, Error_list& exceptions) {
  240. argm.output <<global_stack.max_nesting;}
  241. // add a variable to the variable map that will remain after the enclosing
  242. // function terminates
  243. void b_global(const Argm& argm, Error_list& exceptions) {
  244. argm.global(argm[1], argm[2]);}
  245. namespace {
  246. void if_core(const Argm& argm, Error_list& exceptions,
  247. Conditional_state& state, bool logic, bool is_else) {
  248. if (!state.in_if_block) throw Exception(Argm::Else_without_if);
  249. else if (!state.successful_condition) {
  250. Argm lookup(argm.subrange(1), nullptr, argm.parent_map(),
  251. argm.input, argm.output.child_stream(), argm.error);
  252. state.in_if_block = false;
  253. bool run = true;
  254. if (!is_else) {
  255. run = logic == executable_map.run_condition(lookup, exceptions);
  256. if (global_stack.unwind_stack())
  257. run = false, state.exception_thrown = true;
  258. else if (state.in_if_block) {
  259. state.in_if_block = state.exception_thrown = false;
  260. // if only .false was thrown, then ignore any .if without .else
  261. if (logic == run) throw Exception(Argm::Bad_if_nest);}}
  262. if (run) {
  263. if (argm.argfunction()) {
  264. Argm mapped_argm(argm.parent_map(),
  265. argm.input, argm.output.child_stream(), argm.error);
  266. mapped_argm.push_back(".mapped_argfunction");
  267. (*argm.argfunction())(mapped_argm, exceptions);}
  268. if (global_stack.unwind_stack()) state.exception_thrown = ! is_else;
  269. else if (state.in_if_block && !state.exception_thrown)
  270. throw Exception(Argm::Bad_if_nest);
  271. else state.successful_condition = true, state.exception_thrown = false;}
  272. state.in_if_block = true;}}
  273. }
  274. // run argfunction if $* doesn't throw an exception
  275. void b_if(const Argm& argm, Error_list& exceptions) {
  276. try {
  277. if (gc_state.exception_thrown)
  278. gc_state.successful_condition = gc_state.exception_thrown = false;
  279. else if (gc_state.in_if_block) throw Exception(Argm::If_before_else);
  280. gc_state.in_if_block = true;
  281. if_core(argm, exceptions, gc_state, true, false);}
  282. catch (Exception exception) {
  283. gc_state.exception_thrown = true;
  284. throw exception;}}
  285. // run argfunction if successful_condition is false and $* doesn't throw
  286. void b_else_if(const Argm& argm, Error_list& exceptions) {
  287. try {if (!gc_state.exception_thrown)
  288. if_core(argm, exceptions, gc_state, true, false);}
  289. catch (Exception exception) {
  290. gc_state.exception_thrown = true;
  291. throw exception;}}
  292. // run argfunction if successful_condition is false and $* doesn't throw
  293. void b_else_if_not(const Argm& argm, Error_list& exceptions) {
  294. try { if (!gc_state.exception_thrown)
  295. if_core(argm, exceptions, gc_state, false, false);}
  296. catch (Exception exception) {
  297. gc_state.exception_thrown = true;
  298. throw exception;}}
  299. // run argfunction if successful_condition is false
  300. void b_else(const Argm& argm, Error_list& exceptions) {
  301. try {
  302. if (gc_state.exception_thrown) gc_state.exception_thrown = false;
  303. else if_core(argm, exceptions, gc_state, false, true);
  304. gc_state.successful_condition = gc_state.in_if_block = false;}
  305. catch (Exception exception) {
  306. gc_state.successful_condition = gc_state.in_if_block = false;
  307. throw exception;}}
  308. // prints a list of all internal functions
  309. void b_internal_functions(const Argm& argm, Error_list& exceptions) {
  310. for (int i = 1; i < Argm::Exception_count; ++i)
  311. argm.output <<Argm::exception_names[i] <<"\n";}
  312. // throws .false if the input stream is not the default_stream
  313. void b_is_default_input(const Argm& argm, Error_list& exceptions) {
  314. if (!argm.input.is_default())
  315. exceptions.add_error(Exception(Argm::False, "is_default_input"));}
  316. // throws .false if the output stream is not the default_stream
  317. void b_is_default_output(const Argm& argm, Error_list& exceptions) {
  318. if (!argm.output.is_default())
  319. exceptions.add_error(Exception(Argm::False, "is_default_output"));}
  320. // throws .false if the error stream is not the default_stream
  321. void b_is_default_error(const Argm& argm, Error_list& exceptions) {
  322. if (!argm.error.is_default())
  323. exceptions.add_error(Exception(Argm::False, "is_default_error"));}
  324. // print the last exception that was thrown by this function
  325. void b_last_exception(const Argm& argm, Error_list& exceptions) {
  326. Argm lookup(argm.subrange(1), argm.argfunction(), argm.parent_map());
  327. Base_executable* focus = executable_map.find_second(lookup);
  328. if (focus) {
  329. argm.output <<focus->last_exception() <<"\n";
  330. argm.output.flush();}
  331. else throw Exception(Argm::Function_not_found, argm[1]);}
  332. // print the number of times that the executable in the executable map with
  333. // key $1 has been run
  334. void b_last_execution_time(const Argm& argm, Error_list& exceptions) {
  335. Argm lookup(argm.subrange(1), argm.argfunction(), argm.parent_map());
  336. Base_executable* focus = executable_map.find_second(lookup);
  337. if (focus) {
  338. argm.output <<focus->last_execution_time();
  339. argm.output.flush();}
  340. else throw Exception(Argm::Function_not_found, argm[1]);}
  341. // print the environment that the shell started in
  342. void b_list_environment(const Argm& argm, Error_list& exceptions) {
  343. for (char** i=environ; *i; ++i) {
  344. std::string src(*i);
  345. std::string::size_type div = src.find("=");
  346. if (div != std::string::npos) {
  347. if (i != environ) argm.output <<" ";
  348. argm.output <<word_from_value(
  349. word_from_value(src.substr(0, div)) + " " +
  350. word_from_value(src.substr(div+1)));}}}
  351. // prints the binaries that have been called, and all builtins and functions
  352. void b_list_executables(const Argm& argm, Error_list& exceptions) {
  353. for (auto i: executable_map)
  354. argm.output <<(i == *executable_map.begin()? "": " ") <<i.first;}
  355. // prints all variables in the local variable map
  356. void b_list_locals(const Argm& argm, Error_list& exceptions) {
  357. Variable_map* nonempty = argm.nonempty_parent_map();
  358. for (auto j: *nonempty)
  359. argm.output <<(j.first == nonempty->begin()->first? "": " ") <<j.first;
  360. nonempty->locals_listed = true;}
  361. // add a variable to the variable map until the enclosing function terminates
  362. void b_local(const Argm& argm, Error_list& exceptions) {
  363. argm.local(argm[1], argm[2]);}
  364. // add, but don't define a variable until the enclosing function terminates
  365. void b_local_declare(const Argm& argm, Error_list& exceptions) {
  366. for (unsigned i=1; i<argm.argc(); ++i)
  367. argm.local_declare(argm[i], exceptions);}
  368. // list the files specified by the arguments if they exist
  369. void b_ls(const Argm& argm, Error_list& exceptions) {
  370. struct stat sb;
  371. bool found = false;
  372. for (auto i: argm) if (!stat(i.c_str(), &sb)) {
  373. argm.output <<i <<"\n";
  374. found = true;}
  375. argm.output.flush();
  376. if (!found) {
  377. std::string tried(argm[1]);
  378. for (auto i: argm.subrange(2)) tried += " " + i;
  379. exceptions.add_error(Exception(Argm::File_not_found, tried));}}
  380. // ignore arguments, argfunctions, and then do nothing
  381. void b_nop(const Argm& argm, Error_list& exceptions) {}
  382. // print the process id of the shell
  383. void b_getpid(const Argm& argm, Error_list& exceptions) {
  384. argm.output <<(unsigned) getpid();}
  385. // print the parent process id of the shell
  386. void b_getppid(const Argm& argm, Error_list& exceptions) {
  387. argm.output <<(unsigned) getppid();}
  388. // reassign the variables in the prototype according to the arguments
  389. // this will not permit you to declare variables that don't already exist
  390. void b_reinterpret(const Argm& argm, Error_list& exceptions) {
  391. Argv prototype_argv;
  392. tokenize_words(argm[argm.argc()-1], std::back_inserter(prototype_argv));
  393. Prototype prototype(prototype_argv);
  394. prototype.reassign(argm.subrange(0, 1), *argm.parent_map(), exceptions);}
  395. // add the given exception as a replacement for the current one (if nothing
  396. // afterwards fails)
  397. void b_replace_exception(const Argm& argm, Error_list& exceptions) {
  398. if (!global_stack.in_exception_handler())
  399. throw Exception(Argm::Not_catching_exception);
  400. Argm new_exception(argm.subrange(1), argm.argfunction(),
  401. Variable_map::global_map,
  402. argm.input, argm.output.child_stream(), argm.error);
  403. exceptions.replace_error(new_exception);}
  404. // remove executable with name $1 from executable map
  405. void b_rm_executable(const Argm& argm, Error_list& exceptions) {
  406. Argm lookup(argm.subrange(1), nullptr, argm.parent_map());
  407. Base_executable *e = executable_map.find_second(lookup);
  408. if (dynamic_cast<Builtin*>(e))
  409. exceptions.add_error(Exception(Argm::Illegal_function_name, argm[1]));
  410. else if (!executable_map.erase(*(argm.begin()+1)))
  411. exceptions.add_error(Exception(Argm::Function_not_found, argm[1]));
  412. else;} // successfully removed executable
  413. // run the argfunction having set local variables according to the given
  414. // prototype
  415. void b_scope(const Argm& argm, Error_list& exceptions) {
  416. Argv prototype_argv;
  417. tokenize_words(argm[argm.argc()-1], std::back_inserter(prototype_argv));
  418. Argm invoking_argm(argm.subrange(0, 1), nullptr, argm.parent_map(),
  419. argm.input, argm.output, argm.error);
  420. (*argm.argfunction()).prototype_execute(invoking_argm, prototype_argv,
  421. exceptions);}
  422. // modify variable $1 as a selection according to $2
  423. void b_selection_set(const Argm& argm, Error_list& exceptions) {
  424. std::list<Entry_pattern> focus;
  425. str_to_entry_pattern_list(argm.get_var(argm[1]), focus);
  426. std::string change = *(argm.begin()+2);
  427. for (auto i: argm.subrange(3)) change += ' ' + i;
  428. str_to_entry_pattern_list(change, focus);
  429. argm.set_var(argm[1], entry_pattern_list_to_str(focus.begin(),focus.end()));}
  430. // set variable $1 to $*2
  431. // throws exception if the variable does not exist
  432. void b_set(const Argm& argm, Error_list& exceptions) {
  433. if (isargvar(argm[1])) throw Exception(Argm::Illegal_variable_name, argm[1]);
  434. std::string dest("");
  435. for (auto i: argm.subrange(2, 1)) dest += i + ' ';
  436. dest += argm.back();
  437. argm.set_var(argm[1], dest);}
  438. // Set the configurable message for fallback_handler
  439. // When this is given a prototype, just use the collected args directly
  440. void b_set_fallback_message(const Argm& argm, Error_list& exceptions) {
  441. fallback_message = "";
  442. for (auto i: argm.subrange(1, 1)) fallback_message += i + " ";
  443. fallback_message += argm.back();}
  444. // Set the number of exceptions that can be thrown inside .collect_errors_*
  445. // before they exit early
  446. void b_set_max_collectible_exceptions(const Argm& argm, Error_list& exceptions){
  447. int val = my_strtoi(argm[1], 1, INT_MAX, exceptions);
  448. if (!global_stack.unwind_stack()) global_stack.max_collect = val;}
  449. // Set the number of exceptions that can be thrown by catch blocks after
  450. // max_collectible_exceptions have already been thrown
  451. void b_set_max_extra_exceptions(const Argm& argm, Error_list& exceptions) {
  452. int val = my_strtoi(argm[1], 0, INT_MAX, exceptions);
  453. if (!global_stack.unwind_stack()) global_stack.max_extra = val;}
  454. // Set the maximum number of nesting levels where functions call functions
  455. // before completing.
  456. void b_set_max_nesting(const Argm& argm, Error_list& exceptions) {
  457. int val = my_strtoi(argm[1], 0, INT_MAX);
  458. if (!global_stack.unwind_stack()) global_stack.max_nesting = val;}
  459. // run the first argument as if it was a script, passing additional arguments
  460. // to that script
  461. void b_source(const Argm& argm, Error_list& exceptions) {
  462. struct stat sb;
  463. if (stat(argm[1].c_str(), &sb))
  464. throw Exception(Argm::File_open_failure, argm[1]);
  465. if (!(sb.st_mode & S_IXUSR)) throw Exception(Argm::Not_executable, argm[1]);
  466. Rwsh_istream_p src(new File_istream(argm[1]), false, false);
  467. Command_stream command_stream(src, false);
  468. Command_block block;
  469. Prototype prototype(Argv{"--", "[argv", "...]"});
  470. Variable_map locals(argm.parent_map());
  471. prototype.arg_to_param(argm.subrange(1), locals, exceptions);
  472. Argm script_arg(argm.subrange(1), nullptr, &locals,
  473. argm.input, argm.output.child_stream(), argm.error);
  474. try {
  475. while (!command_stream.fail() && !global_stack.unwind_stack()) {
  476. command_stream.getline(block, exceptions);
  477. if (command_stream.fail()) break;
  478. block.execute(script_arg, exceptions);}
  479. prototype.unused_var_check(&locals, exceptions);}
  480. catch (Exception error) {
  481. prototype.unused_var_check(&locals, exceptions);
  482. throw error;}}
  483. // run the argument function once with each command in the specified function
  484. // invocation
  485. void b_stepwise(const Argm& argm, Error_list& exceptions) {
  486. Argm lookup(argm.subrange(1,1), nullptr, argm.parent_map(),
  487. argm.input, argm.output.child_stream(), argm.error);
  488. Base_executable* e = executable_map.find_second(lookup);
  489. if (!e) throw Exception(Argm::Function_not_found, argm[1]);
  490. Function* f = dynamic_cast<Function*>(e);
  491. if (!f) return; //throw Exception(Argm::Not_a_function, argm[1]);
  492. Variable_map locals(argm.parent_map());
  493. f->arg_to_param(argm.subrange(1,1), locals, exceptions);
  494. if (global_stack.unwind_stack())
  495. return f->unused_var_check(&locals, exceptions);
  496. // this must be caught and handled to use .stepwise recursively
  497. Argm params(argm.subrange(1,1), nullptr, &locals,
  498. argm.input, argm.output.child_stream(), argm.error);
  499. Argv prototype_argv;
  500. tokenize_words(argm[argm.argc()-1], std::back_inserter(prototype_argv));
  501. Function temp_func(".stepwise(body)", prototype_argv,
  502. *argm.argfunction());
  503. for (auto j: f->body) {
  504. Argm body_i(j.interpret(params, exceptions));
  505. if (global_stack.unwind_stack()) break;
  506. Argm body(".stepwise(body)", body_i.argv(), nullptr,
  507. body_i.parent_map(), body_i.input, body_i.output, body_i.error);
  508. temp_func(body, exceptions);
  509. (void) global_stack.remove_exceptions(".continue", exceptions);
  510. if (global_stack.remove_exceptions(".break", exceptions) ||
  511. global_stack.unwind_stack()) break;}
  512. f->unused_var_check(&locals, exceptions);}
  513. // run the argfunction and store its output in the variable $1
  514. void b_store_output(const Argm& argm, Error_list& exceptions) {
  515. if (isargvar(argm[1])) throw Exception(Argm::Illegal_variable_name, argm[1]);
  516. Substitution_stream text;
  517. Argm mapped_argm(argm.parent_map(),
  518. argm.input, text.child_stream(), argm.error);
  519. mapped_argm.push_back(".mapped_argfunction");
  520. (*argm.argfunction())(mapped_argm, exceptions);
  521. if (!global_stack.unwind_stack()) argm.set_var(argm[1], text.value());}
  522. // throws .false if the key $1 isn't an executable in the executable map
  523. void b_test_executable_exists(const Argm& argm, Error_list& exceptions) {
  524. Argm lookup(argm.subrange(1), argm.argfunction(), argm.parent_map());
  525. if (lookup[0] == ".argfunction") lookup[0] = ".mapped_argfunction";
  526. if (!executable_map.find_second(lookup))
  527. exceptions.add_error(Exception(Argm::False,
  528. "executable exists: " + argm[1]));}
  529. // throws .false if none of the files specified by the arguments exist
  530. void b_test_file_exists(const Argm& argm, Error_list& exceptions) {
  531. struct stat sb;
  532. for (auto i: argm.subrange(1)) if (!stat(i.c_str(), &sb)) return;
  533. std::string err_val;
  534. for (auto i: argm.subrange(1)) err_val += " -e " + i;
  535. exceptions.add_error(Exception(Argm::False, err_val));}
  536. // throws .false if two strings convert to a doubles and first is not greater
  537. void b_test_greater(const Argm& argm, Error_list& exceptions) {
  538. double lhs = my_strtod(argm[1], exceptions),
  539. rhs = my_strtod(argm[2], exceptions);
  540. if (global_stack.unwind_stack()) return;
  541. if (lhs <= rhs)
  542. exceptions.add_error(Exception(Argm::False, argm[1] +" -gt "+ argm[2]));}
  543. // throws .false if the first string isn't repeated in subsequent arguments
  544. void b_test_in(const Argm& argm, Error_list& exceptions) {
  545. for (auto j: argm.subrange(2)) if (argm[1] == j) return;
  546. auto err_val = argm[1] + " in";
  547. for (auto j: argm.subrange(2)) err_val += " " + j;
  548. exceptions.add_error(Exception(Argm::False, err_val));}
  549. // throws .false if the string doesn't convert to a number
  550. void b_test_is_number(const Argm& argm, Error_list& exceptions) {
  551. try {(void) my_strtod(argm[1]);}
  552. catch (E_generic) {exceptions.add_error(Exception(Argm::False,
  553. "is_number " + argm[1] + " - generic"));}
  554. catch (E_nan) {exceptions.add_error(Exception(Argm::False,
  555. "is_number " + argm[1] + " - NaN"));}
  556. catch (E_range) {exceptions.add_error(Exception(Argm::False,
  557. "is_number " + argm[1] + " - range"));}}
  558. // throws .false if two strings convert to doubles and first isn't less
  559. void b_test_less(const Argm& argm, Error_list& exceptions) {
  560. double lhs = my_strtod(argm[1], exceptions),
  561. rhs = my_strtod(argm[2], exceptions);
  562. if (global_stack.unwind_stack()) return;
  563. if (lhs >= rhs)
  564. exceptions.add_error(Exception(Argm::False, argm[1] +" -lt "+ argm[2]));}
  565. // throws .false if the string is empty
  566. void b_test_not_empty(const Argm& argm, Error_list& exceptions) {
  567. for (unsigned j = 1; j < argm.argc(); ++j) if (argm[j].length()) return;
  568. exceptions.add_error(Exception(Argm::False, " -n "));}
  569. // throw .false if the two strings convert to doubles and are not equal
  570. void b_test_number_equal(const Argm& argm, Error_list& exceptions) {
  571. double lhs = my_strtod(argm[1], exceptions),
  572. rhs = my_strtod(argm[2], exceptions);
  573. if (global_stack.unwind_stack()) return;
  574. if (lhs != rhs)
  575. exceptions.add_error(Exception(Argm::False, argm[1] +" -eq "+ argm[2]));}
  576. // throws .false if the two strings aren't the same
  577. void b_test_string_equal(const Argm& argm, Error_list& exceptions) {
  578. if (argm[1] != argm[2])
  579. exceptions.add_error(Exception(Argm::False, argm[1] + " == " + argm[2]));}
  580. // throws .false if the two strings are the same
  581. void b_test_string_unequal(const Argm& argm, Error_list& exceptions) {
  582. if (argm[1] == argm[2])
  583. exceptions.add_error(Exception(Argm::False, argm[1] + " != " + argm[2]));}
  584. // throw the remaining arguments as an exception
  585. void b_throw(const Argm& argm, Error_list& exceptions) {
  586. Argm new_exception(argm.subrange(1), argm.argfunction(),
  587. Variable_map::global_map, default_input, default_output,
  588. default_error);
  589. exceptions.add_error(new_exception);}
  590. // enable readline if disabled, disable if enabled
  591. void b_toggle_readline(const Argm& argm, Error_list& exceptions) {
  592. readline_enabled = ! readline_enabled;}
  593. // print the number of times that the executable in the executable map with
  594. // key $1 has been run
  595. void b_total_execution_time(const Argm& argm, Error_list& exceptions) {
  596. Argm lookup(argm.subrange(1), argm.argfunction(), argm.parent_map());
  597. Base_executable* focus = executable_map.find_second(lookup);
  598. if (focus) {
  599. struct timeval val = focus->total_execution_time();
  600. argm.output <<val;
  601. argm.output.flush();}
  602. else throw Exception(Argm::Function_not_found, argm[1]);}
  603. // run the handler for specified exceptions
  604. void b_try_catch_recursive(const Argm& argm, Error_list& exceptions) {
  605. Argm mapped_argm(argm.parent_map(),
  606. argm.input, argm.output.child_stream(), argm.error);
  607. mapped_argm.push_back(".try_catch_recursive(body)");
  608. (*argm.argfunction())(mapped_argm, exceptions);
  609. if (global_stack.unwind_stack()) global_stack.catch_blocks(argm,exceptions);}
  610. // print the type of executable with name $1 from executable map
  611. void b_type(const Argm& argm, Error_list& exceptions) {
  612. Argm lookup(argm.subrange(1), argm.argfunction(), argm.parent_map());
  613. if (lookup[0] == ".argfunction") lookup[0] = ".mapped_argfunction";
  614. Base_executable *e = executable_map.find_second(lookup);
  615. if (!e) exceptions.add_error(Exception(Argm::Function_not_found, argm[1]));
  616. else if (dynamic_cast<Function*>(e)) argm.output <<"function\n";
  617. else if (dynamic_cast<Binary*>(e)) argm.output <<"file\n";
  618. else if (dynamic_cast<Builtin*>(e)) argm.output <<"builtin\n";
  619. else if (dynamic_cast<Command_block*>(e)) argm.output <<"argfunction\n";
  620. else std::abort();} // unexpected type of executable
  621. // removes the given variable from the variable map. you could be really
  622. // pedantic and throw an .undefined_variable if it doesn't exist, but the
  623. // fact is that the requested state (one where this variable isn't set) is
  624. // already the case, so it's hard to say what you're protecting people from.
  625. void b_unset(const Argm& argm, Error_list& exceptions) {
  626. argm.unset_var(argm[1]);}
  627. namespace {double sleep_requested = 0.0;}
  628. // sleep for the specified number of microseconds
  629. void b_usleep(const Argm& argm, Error_list& exceptions) {
  630. int usec = my_strtoi(argm[1], 0, INT_MAX, exceptions);
  631. if (global_stack.unwind_stack()) return;
  632. sleep_requested += usec / 1000000.0;
  633. const long mega = (long)1E6;
  634. struct timespec delay = {usec / mega, (usec % mega) * 1000};
  635. if (!nanosleep(&delay, nullptr)) return;
  636. else if (errno == EINTR) //not tested
  637. throw Exception(Argm::Interrupted_sleep);
  638. else throw Exception(Argm::Internal_error, //not tested
  639. "nanosleep failed with code", errno);}
  640. // print the average number of microseconds longer that .usleep takes than it
  641. // is requested to take.
  642. void b_usleep_overhead(const Argm& argm, Error_list& exceptions) {
  643. Argm usleep_argm(".usleep", Argv(), nullptr, Variable_map::global_map,
  644. default_input, default_output, default_error);
  645. Base_executable* focus = executable_map.find_second(Argm(usleep_argm));
  646. unsigned count = focus->execution_count();
  647. if (!count) throw Exception(Argm::Not_a_number, "");
  648. struct timeval slept = focus->total_execution_time();
  649. double total = slept.tv_sec + slept.tv_usec/1000000.0 - sleep_requested;
  650. argm.output <<(total/count);}
  651. // add the specified value to the specified variable
  652. void b_var_add(const Argm& argm, Error_list& exceptions) {
  653. const std::string& var_str = argm.get_var(argm[1]);
  654. double var_term = my_strtod(var_str, exceptions);
  655. double const_term = my_strtod(argm[2], exceptions);
  656. if (global_stack.unwind_stack()) return;
  657. double sum = var_term + const_term;
  658. if (sum == std::numeric_limits<double>::infinity() ||
  659. sum == -std::numeric_limits<double>::infinity())
  660. return exceptions.add_error(Exception(Argm::Result_range,var_str,argm[2]));
  661. std::string result = my_dtostr(sum);
  662. if (result == my_dtostr(var_term) && const_term != 0.0)
  663. return exceptions.add_error(Exception(Argm::Epsilon, var_str, argm[2]));
  664. argm.set_var(argm[1], result);}
  665. // divide the specified variable by the specified value
  666. void b_var_divide(const Argm& argm, Error_list& exceptions) {
  667. const std::string& var_str = argm.get_var(argm[1]);
  668. double var_term = my_strtod(var_str, exceptions);
  669. double const_term = my_strtod(argm[2], exceptions);
  670. if (const_term == 0)
  671. exceptions.add_error(Exception(Argm::Divide_by_zero, var_str));
  672. if (global_stack.unwind_stack()) return;
  673. double quotient = var_term / const_term;
  674. if (quotient == 0 && var_term != 0)
  675. return exceptions.add_error(Exception(Argm::Result_range,var_str,argm[2]));
  676. std::string result = my_dtostr(quotient);
  677. if (result == my_dtostr(var_term) && var_term != 0.0 && const_term != 1.0)
  678. return exceptions.add_error(Exception(Argm::Epsilon, var_str, argm[2]));
  679. argm.set_var(argm[1], result);}
  680. // set the specified variable to its remainder modulo the specified value
  681. void b_var_modulo(const Argm& argm, Error_list& exceptions) {
  682. const std::string& var_str = argm.get_var(argm[1]);
  683. int var_term = my_strtoi(var_str, INT_MIN, INT_MAX, exceptions);
  684. int const_term = my_strtoi(argm[2], INT_MIN, INT_MAX, exceptions);
  685. if (const_term == 0)
  686. exceptions.add_error(Exception(Argm::Divide_by_zero, var_str));
  687. if (global_stack.unwind_stack()) return;
  688. double remainder = var_term % const_term;
  689. std::string result = my_dtostr(remainder);
  690. argm.set_var(argm[1], result);}
  691. // multiply the specified variable by the specified value
  692. void b_var_multiply(const Argm& argm, Error_list& exceptions) {
  693. const std::string& var_str = argm.get_var(argm[1]);
  694. double var_term = my_strtod(var_str, exceptions);
  695. double const_term = my_strtod(argm[2], exceptions);
  696. if (global_stack.unwind_stack()) return;
  697. double product = var_term * const_term;
  698. if (product == std::numeric_limits<double>::infinity() ||
  699. product == -std::numeric_limits<double>::infinity())
  700. return exceptions.add_error(Exception(Argm::Result_range,var_str,argm[2]));
  701. std::string result = my_dtostr(product);
  702. if (result == my_dtostr(var_term) && var_term != 0.0 && const_term != 1.0)
  703. return exceptions.add_error(Exception(Argm::Epsilon, var_str, argm[2]));
  704. argm.set_var(argm[1], result);}
  705. // subtract the specified value from the specified variable
  706. void b_var_subtract(const Argm& argm, Error_list& exceptions) {
  707. const std::string& var_str = argm.get_var(argm[1]);
  708. double var_term = my_strtod(var_str, exceptions);
  709. double const_term = my_strtod(argm[2], exceptions);
  710. if (global_stack.unwind_stack()) return;
  711. double difference = var_term - const_term;
  712. if (difference >= DBL_MAX || difference <= -DBL_MAX)
  713. return exceptions.add_error(Exception(Argm::Result_range,var_str,argm[2]));
  714. std::string result = my_dtostr(difference);
  715. if (result == my_dtostr(var_term) && const_term != 0.0)
  716. return exceptions.add_error(Exception(Argm::Epsilon, var_str, argm[2]));
  717. argm.set_var(argm[1], result);}
  718. // throw .false if none of the specified variables exist
  719. void b_var_exists(const Argm& argm, Error_list& exceptions) {
  720. bool any_exist = false;
  721. // need to go through the whole set to mark all as checked
  722. for (auto i: argm.subrange(1)) if (argm.var_exists(i)) any_exist = true;
  723. if (any_exist) return;
  724. std::string err_val ("var exists:");
  725. for (auto i: argm.subrange(1)) err_val += " " + i;
  726. exceptions.add_error(Exception(Argm::False, err_val));}
  727. static const std::string version_str("0.3+");
  728. // write to standard output the version of rwsh
  729. void b_version(const Argm& argm, Error_list& exceptions) {
  730. argm.output <<version_str;}
  731. // throws Version_incompatibles if the given version string is not compatible
  732. // with the version of this shell
  733. void b_version_compatible(const Argm& argm, Error_list& exceptions) {
  734. if (argm[1] != version_str)
  735. throw Exception(Argm::Version_incompatible, argm[1]);}
  736. // prints the total amount of time the shell has spent in wait() syscall
  737. void b_waiting_for_binary(const Argm& argm, Error_list& exceptions) {
  738. argm.output <<rwsh_clock.waiting_for_binary();
  739. argm.output.flush();}
  740. // prints the total amount of time that has passed and the shell has not been
  741. // waiting for other processes or the user
  742. void b_waiting_for_shell(const Argm& argm, Error_list& exceptions) {
  743. argm.output <<rwsh_clock.waiting_for_shell();
  744. argm.output.flush();}
  745. // prints the total amount of time the shell has been waiting for user input
  746. void b_waiting_for_user(const Argm& argm, Error_list& exceptions) {
  747. argm.output <<rwsh_clock.waiting_for_user();
  748. argm.output.flush();}
  749. // print the string corresponding to the executable in the executable map with
  750. // key $1
  751. void b_whence_function(const Argm& argm, Error_list& exceptions) {
  752. Argm lookup(argm.subrange(1), argm.argfunction(), argm.parent_map());
  753. if (lookup[0] == ".argfunction") lookup[0] = ".mapped_argfunction";
  754. Base_executable* focus = executable_map.find_second(lookup);
  755. if (focus) {
  756. argm.output <<focus->str() <<"\n";
  757. argm.output.flush();}
  758. else exceptions.add_error(Exception(Argm::Function_not_found, argm[1]));}
  759. // if the filename has a leading dot, then check in current directory
  760. // otherwise find the binary in $2 with filename $1
  761. void b_which_path(const Argm& argm, Error_list& exceptions) {
  762. std::vector<std::string> path;
  763. tokenize(argm[2], std::back_inserter(path),
  764. std::bind2nd(std::equal_to<char>(), ':'));
  765. for (auto j: path) {
  766. std::string test;
  767. if (argm[1].substr(0,1) == "/" || argm[1].substr(0,2) == "./" ||
  768. argm[1].substr(0,3) == "../")
  769. if (argm[1].substr(0, j.length()) != j) continue;
  770. else test = argm[1];
  771. else if (j.back() == '/') test = j + argm[1];
  772. else test = j + '/' + argm[1];
  773. struct stat sb;
  774. if (!stat(test.c_str(), &sb)) {
  775. argm.output <<test;
  776. return;}}
  777. exceptions.add_error( // executable does not exist
  778. Exception(Argm::Binary_not_found, argm[1], argm[2]));}
  779. // run the argfunction each time that the arguments don't throw an exception
  780. void b_while(const Argm& argm, Error_list& exceptions) {
  781. Argm lookup(argm.subrange(1), nullptr, argm.parent_map(),
  782. argm.input, argm.output.child_stream(), argm.error);
  783. while (executable_map.run_condition(lookup, exceptions)) {
  784. Argm mapped_argm(argm.parent_map(), argm.input, argm.output.child_stream(),
  785. argm.error);
  786. mapped_argm.push_back(".mapped_argfunction");
  787. (*argm.argfunction())(mapped_argm, exceptions);
  788. (void) global_stack.remove_exceptions(".continue", exceptions);
  789. if (global_stack.remove_exceptions(".break", exceptions) ||
  790. global_stack.unwind_stack()) return;}}