script.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. /* Copyright (C) 1994, 1995, 1996, 1997, 1998, 2000, 2002 Free Software Foundation, Inc.
  2. * This program is free software; you can redistribute it and/or modify
  3. * it under the terms of the GNU General Public License as published by
  4. * the Free Software Foundation; either version 2, or (at your option)
  5. * any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this software; see the file COPYING. If not, write to
  14. * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  15. * Boston, MA 02111-1307 USA
  16. *
  17. * As a special exception, the Free Software Foundation gives permission
  18. * for additional uses of the text contained in its release of GUILE.
  19. *
  20. * The exception is that, if you link the GUILE library with other files
  21. * to produce an executable, this does not by itself cause the
  22. * resulting executable to be covered by the GNU General Public License.
  23. * Your use of that executable is in no way restricted on account of
  24. * linking the GUILE library code into it.
  25. *
  26. * This exception does not however invalidate any other reasons why
  27. * the executable file might be covered by the GNU General Public License.
  28. *
  29. * This exception applies only to the code released by the
  30. * Free Software Foundation under the name GUILE. If you copy
  31. * code from other Free Software Foundation releases into a copy of
  32. * GUILE, as the General Public License permits, the exception does
  33. * not apply to the code that you add in this way. To avoid misleading
  34. * anyone as to the status of such modified files, you must delete
  35. * this exception notice from them.
  36. *
  37. * If you write modifications of your own for GUILE, it is your choice
  38. * whether to permit this exception to apply to your modifications.
  39. * If you do not wish that, delete this exception notice. */
  40. /* "script.c" argv tricks for `#!' scripts.
  41. Authors: Aubrey Jaffer and Jim Blandy */
  42. #include <stdio.h>
  43. #include <ctype.h>
  44. #include "libguile/_scm.h"
  45. #include "libguile/gh.h"
  46. #include "libguile/load.h"
  47. #include "libguile/version.h"
  48. #include "libguile/script.h"
  49. #ifdef HAVE_STRING_H
  50. #include <string.h>
  51. #endif
  52. #ifdef HAVE_UNISTD_H
  53. #include <unistd.h> /* for X_OK define */
  54. #endif
  55. /* Concatentate str2 onto str1 at position n and return concatenated
  56. string if file exists; 0 otherwise. */
  57. static char *
  58. scm_cat_path (char *str1, const char *str2, long n)
  59. {
  60. if (!n)
  61. n = strlen (str2);
  62. if (str1)
  63. {
  64. long len = strlen (str1);
  65. str1 = (char *) realloc (str1, (scm_sizet) (len + n + 1));
  66. if (!str1)
  67. return 0L;
  68. strncat (str1 + len, str2, n);
  69. return str1;
  70. }
  71. str1 = (char *) malloc ((scm_sizet) (n + 1));
  72. if (!str1)
  73. return 0L;
  74. str1[0] = 0;
  75. strncat (str1, str2, n);
  76. return str1;
  77. }
  78. #if 0
  79. static char *
  80. scm_try_path (char *path)
  81. {
  82. FILE *f;
  83. /* fprintf(stderr, "Trying %s\n", path);fflush(stderr); */
  84. if (!path)
  85. return 0L;
  86. SCM_SYSCALL (f = fopen (path, "r");
  87. );
  88. if (f)
  89. {
  90. fclose (f);
  91. return path;
  92. }
  93. free (path);
  94. return 0L;
  95. }
  96. static char *
  97. scm_sep_init_try (char *path, const char *sep, const char *initname)
  98. {
  99. if (path)
  100. path = scm_cat_path (path, sep, 0L);
  101. if (path)
  102. path = scm_cat_path (path, initname, 0L);
  103. return scm_try_path (path);
  104. }
  105. #endif
  106. #ifndef LINE_INCREMENTORS
  107. #define LINE_INCREMENTORS '\n'
  108. #ifdef MSDOS
  109. #define WHITE_SPACES ' ':case '\t':case '\r':case '\f':case 26
  110. #else
  111. #define WHITE_SPACES ' ':case '\t':case '\r':case '\f'
  112. #endif /* def MSDOS */
  113. #endif /* ndef LINE_INCREMENTORS */
  114. #ifndef MAXPATHLEN
  115. #define MAXPATHLEN 80
  116. #endif /* ndef MAXPATHLEN */
  117. #ifndef X_OK
  118. #define X_OK 1
  119. #endif /* ndef X_OK */
  120. char *
  121. scm_find_executable (const char *name)
  122. {
  123. char tbuf[MAXPATHLEN];
  124. int i = 0;
  125. FILE *f;
  126. /* fprintf(stderr, "s_f_e checking access %s ->%d\n", name, access(name, X_OK)); fflush(stderr); */
  127. if (access (name, X_OK))
  128. return 0L;
  129. f = fopen (name, "r");
  130. if (!f)
  131. return 0L;
  132. if ((fgetc (f) == '#') && (fgetc (f) == '!'))
  133. {
  134. while (1)
  135. switch (tbuf[i++] = fgetc (f))
  136. {
  137. case /*WHITE_SPACES */ ' ':
  138. case '\t':
  139. case '\r':
  140. case '\f':
  141. case EOF:
  142. tbuf[--i] = 0;
  143. fclose (f);
  144. return scm_cat_path (0L, tbuf, 0L);
  145. }
  146. }
  147. fclose (f);
  148. return scm_cat_path (0L, name, 0L);
  149. }
  150. /* Read a \nnn-style escape. We've just read the backslash. */
  151. static int
  152. script_get_octal (FILE *f)
  153. {
  154. int i;
  155. int value = 0;
  156. for (i = 0; i < 3; i++)
  157. {
  158. int c = getc (f);
  159. if ('0' <= c && c <= '7')
  160. value = (value * 8) + (c - '0');
  161. else
  162. scm_wta (SCM_UNDEFINED,
  163. "malformed script: bad octal backslash escape",
  164. "script argument parser");
  165. }
  166. return value;
  167. }
  168. static int
  169. script_get_backslash (FILE *f)
  170. {
  171. int c = getc (f);
  172. switch (c)
  173. {
  174. case 'a': return '\a';
  175. case 'b': return '\b';
  176. case 'f': return '\f';
  177. case 'n': return '\n';
  178. case 'r': return '\r';
  179. case 't': return '\t';
  180. case 'v': return '\v';
  181. case '\\':
  182. case ' ':
  183. case '\t':
  184. case '\n':
  185. return c;
  186. case '0': case '1': case '2': case '3':
  187. case '4': case '5': case '6': case '7':
  188. ungetc (c, f);
  189. return script_get_octal (f);
  190. case EOF:
  191. scm_wta (SCM_UNDEFINED,
  192. "malformed script: backslash followed by EOF",
  193. "script argument parser");
  194. return 0; /* not reached? */
  195. default:
  196. scm_wta (SCM_UNDEFINED,
  197. "malformed script: bad backslash sequence",
  198. "script argument parser");
  199. return 0; /* not reached? */
  200. }
  201. }
  202. static char *
  203. script_read_arg (FILE *f)
  204. {
  205. int size = 7;
  206. char *buf = malloc (size + 1);
  207. int len = 0;
  208. if (! buf)
  209. return 0;
  210. for (;;)
  211. {
  212. int c = getc (f);
  213. switch (c)
  214. {
  215. case '\\':
  216. c = script_get_backslash (f);
  217. /* The above produces a new character to add to the argument.
  218. Fall through. */
  219. default:
  220. if (len >= size)
  221. {
  222. size = (size + 1) * 2;
  223. buf = realloc (buf, size);
  224. if (! buf)
  225. return 0;
  226. }
  227. buf[len++] = c;
  228. break;
  229. case '\n':
  230. /* This may terminate an arg now, but it will terminate the
  231. entire list next time through. */
  232. ungetc ('\n', f);
  233. case EOF:
  234. if (len == 0)
  235. {
  236. free (buf);
  237. return 0;
  238. }
  239. /* Otherwise, those characters terminate the argument; fall
  240. through. */
  241. case ' ':
  242. buf[len] = '\0';
  243. return buf;
  244. case '\t':
  245. free (buf);
  246. scm_wta (SCM_UNDEFINED,
  247. "malformed script: TAB in meta-arguments",
  248. "argument parser");
  249. return 0; /* not reached? */
  250. }
  251. }
  252. }
  253. static int
  254. script_meta_arg_P (char *arg)
  255. {
  256. if ('\\' != arg[0])
  257. return 0L;
  258. #ifdef MSDOS
  259. return !arg[1];
  260. #else
  261. switch (arg[1])
  262. {
  263. case 0:
  264. case '%':
  265. case WHITE_SPACES:
  266. return !0;
  267. default:
  268. return 0L;
  269. }
  270. #endif
  271. }
  272. char **
  273. scm_get_meta_args (int argc, char **argv)
  274. {
  275. int nargc = argc, argi = 1, nargi = 1;
  276. char *narg, **nargv;
  277. if (!(argc > 2 && script_meta_arg_P (argv[1])))
  278. return 0L;
  279. if (!(nargv = (char **) malloc ((1 + nargc) * sizeof (char *))))
  280. return 0L;
  281. nargv[0] = argv[0];
  282. while (((argi + 1) < argc) && (script_meta_arg_P (argv[argi])))
  283. {
  284. FILE *f = fopen (argv[++argi], "r");
  285. if (f)
  286. {
  287. nargc--; /* to compensate for replacement of '\\' */
  288. while (1)
  289. switch (getc (f))
  290. {
  291. case EOF:
  292. return 0L;
  293. default:
  294. continue;
  295. case '\n':
  296. goto found_args;
  297. }
  298. found_args:
  299. while ((narg = script_read_arg (f)))
  300. if (!(nargv = (char **) realloc (nargv,
  301. (1 + ++nargc) * sizeof (char *))))
  302. return 0L;
  303. else
  304. nargv[nargi++] = narg;
  305. fclose (f);
  306. nargv[nargi++] = argv[argi++];
  307. }
  308. }
  309. while (argi <= argc)
  310. nargv[nargi++] = argv[argi++];
  311. return nargv;
  312. }
  313. int
  314. scm_count_argv (char **argv)
  315. {
  316. int argc = 0;
  317. while (argv[argc])
  318. argc++;
  319. return argc;
  320. }
  321. /* For use in error messages. */
  322. char *scm_usage_name = 0;
  323. void
  324. scm_shell_usage (int fatal, char *message)
  325. {
  326. if (message)
  327. fprintf (stderr, "%s\n", message);
  328. fprintf (stderr,
  329. "Usage: %s OPTION ...\n"
  330. "Evaluate Scheme code, interactively or from a script.\n"
  331. "\n"
  332. " -s SCRIPT load Scheme source code from FILE, and exit\n"
  333. " -c EXPR evalute Scheme expression EXPR, and exit\n"
  334. " -- stop scanning arguments; run interactively\n"
  335. "The above switches stop argument processing, and pass all\n"
  336. "remaining arguments as the value of (command-line).\n"
  337. "\n"
  338. " -l FILE load Scheme source code from FILE\n"
  339. " -e FUNCTION after reading script, apply FUNCTION to\n"
  340. " command line arguments\n"
  341. " -ds do -s script at this point\n"
  342. " --debug start with debugging evaluator and backtraces\n"
  343. " -q inhibit loading of user init file\n"
  344. " --emacs enable Emacs protocol (experimental)\n"
  345. " -h, --help display this help and exit\n"
  346. " -v, --version display version information and exit\n"
  347. " \\ read arguments from following script lines\n",
  348. scm_usage_name);
  349. if (fatal)
  350. exit (fatal);
  351. }
  352. /* Some symbols used by the command-line compiler. */
  353. SCM_SYMBOL (sym_load, "load");
  354. SCM_SYMBOL (sym_eval_string, "eval-string");
  355. SCM_SYMBOL (sym_command_line, "command-line");
  356. SCM_SYMBOL (sym_begin, "begin");
  357. SCM_SYMBOL (sym_load_user_init, "load-user-init");
  358. SCM_SYMBOL (sym_top_repl, "top-repl");
  359. SCM_SYMBOL (sym_quit, "quit");
  360. /* Given an array of command-line switches, return a Scheme expression
  361. to carry out the actions specified by the switches.
  362. If you told me this should have been written in Scheme, I'd
  363. probably agree. I'd say I didn't feel comfortable doing that in
  364. the present system. You'd say, well, fix the system so you are
  365. comfortable doing that. I'd agree again. *shrug*
  366. We load the ice-9 system from here. It might be nicer if the
  367. libraries initialized from the inner_main function in guile.c (which
  368. will be auto-generated eventually) could assume ice-9 were already
  369. loaded. Then again, it might be nice if ice-9 could assume that
  370. certain libraries were already loaded. The solution is to break up
  371. ice-9 into modules which can be frozen and statically linked like any
  372. other module. Then all the modules can describe their dependencies in
  373. the usual way, and the auto-generated inner_main will do the right
  374. thing. */
  375. static char guile[] = "guile";
  376. SCM
  377. scm_compile_shell_switches (int argc, char **argv)
  378. {
  379. SCM tail = SCM_EOL; /* We accumulate the list backwards,
  380. and then reverse! it before we
  381. return it. */
  382. SCM do_script = SCM_EOL; /* The element of the list containing
  383. the "load" command, in case we get
  384. the "-ds" switch. */
  385. SCM entry_point = SCM_EOL; /* for -e switch */
  386. int interactive = 1; /* Should we go interactive when done? */
  387. int inhibit_user_init = 0; /* Don't load user init file */
  388. int use_emacs_interface = 0;
  389. int i;
  390. char *argv0 = guile;
  391. if (argc > 0)
  392. {
  393. argv0 = argv[0];
  394. scm_usage_name = strrchr (argv[0], '/');
  395. if (! scm_usage_name)
  396. scm_usage_name = argv[0];
  397. else
  398. scm_usage_name++;
  399. }
  400. if (! scm_usage_name)
  401. scm_usage_name = guile;
  402. for (i = 1; i < argc; i++)
  403. {
  404. if (! strcmp (argv[i], "-s")) /* load script */
  405. {
  406. if (++i >= argc)
  407. scm_shell_usage (1, "missing argument to `-s' switch");
  408. /* If we specified the -ds option, do_script points to the
  409. cdr of an expression like (load #f); we replace the car
  410. (i.e., the #f) with the script name. */
  411. if (!SCM_NULLP (do_script))
  412. {
  413. SCM_SETCAR (do_script, scm_makfrom0str (argv[i]));
  414. do_script = SCM_EOL;
  415. }
  416. else
  417. /* Construct an application of LOAD to the script name. */
  418. tail = scm_cons (scm_cons2 (sym_load,
  419. scm_makfrom0str (argv[i]),
  420. SCM_EOL),
  421. tail);
  422. argv0 = argv[i];
  423. i++;
  424. interactive = 0;
  425. break;
  426. }
  427. else if (! strcmp (argv[i], "-c")) /* evaluate expr */
  428. {
  429. if (++i >= argc)
  430. scm_shell_usage (1, "missing argument to `-c' switch");
  431. tail = scm_cons (scm_cons2 (sym_eval_string,
  432. scm_makfrom0str (argv[i]),
  433. SCM_EOL),
  434. tail);
  435. i++;
  436. interactive = 0;
  437. break;
  438. }
  439. else if (! strcmp (argv[i], "--")) /* end args; go interactive */
  440. {
  441. i++;
  442. break;
  443. }
  444. else if (! strcmp (argv[i], "-l")) /* load a file */
  445. {
  446. if (++i < argc)
  447. tail = scm_cons (scm_cons2 (sym_load,
  448. scm_makfrom0str (argv[i]),
  449. SCM_EOL),
  450. tail);
  451. else
  452. scm_shell_usage (1, "missing argument to `-l' switch");
  453. }
  454. else if (! strcmp (argv[i], "-e")) /* entry point */
  455. {
  456. if (++i < argc)
  457. entry_point = gh_symbol2scm (argv[i]);
  458. else
  459. scm_shell_usage (1, "missing argument to `-e' switch");
  460. }
  461. else if (! strcmp (argv[i], "-ds")) /* do script here */
  462. {
  463. /* We put a dummy "load" expression, and let the -s put the
  464. filename in. */
  465. if (!SCM_NULLP (do_script))
  466. scm_shell_usage (1, "the -ds switch may only be specified once");
  467. do_script = scm_cons (SCM_BOOL_F, SCM_EOL);
  468. tail = scm_cons (scm_cons (sym_load, do_script),
  469. tail);
  470. }
  471. else if (! strcmp (argv[i], "--debug")) /* debug eval + backtraces */
  472. {
  473. SCM_DEVAL_P = 1;
  474. SCM_BACKTRACE_P = 1;
  475. SCM_RECORD_POSITIONS_P = 1;
  476. SCM_RESET_DEBUG_MODE;
  477. }
  478. else if (! strcmp (argv[i], "--emacs")) /* use emacs protocol */
  479. use_emacs_interface = 1;
  480. else if (! strcmp (argv[i], "-q")) /* don't load user init */
  481. inhibit_user_init = 1;
  482. else if (! strcmp (argv[i], "-h")
  483. || ! strcmp (argv[i], "--help"))
  484. {
  485. scm_shell_usage (0, 0);
  486. exit (0);
  487. }
  488. else if (! strcmp (argv[i], "-v")
  489. || ! strcmp (argv[i], "--version"))
  490. {
  491. /* Print version number. */
  492. printf ("Guile %s\n"
  493. "Copyright (c) 1995, 1996, 1997, 2000, 2002 Free Software Foundation\n"
  494. "Guile may be distributed under the terms of the GNU General Public Licence;\n"
  495. "certain other uses are permitted as well. For details, see the file\n"
  496. "`COPYING', which is included in the Guile distribution.\n"
  497. "There is no warranty, to the extent permitted by law.\n",
  498. SCM_CHARS (scm_version ()));
  499. exit (0);
  500. }
  501. else
  502. {
  503. fprintf (stderr, "%s: Unrecognized switch `%s'\n",
  504. scm_usage_name, argv[i]);
  505. scm_shell_usage (1, 0);
  506. }
  507. }
  508. /* Check to make sure the -ds got a -s. */
  509. if (!SCM_NULLP (do_script))
  510. scm_shell_usage (1, "the `-ds' switch requires the use of `-s' as well");
  511. /* Make any remaining arguments available to the
  512. script/command/whatever. */
  513. scm_set_program_arguments (argc ? argc - i : 0, argv + i, argv0);
  514. /* If the --emacs switch was set, now is when we process it. */
  515. {
  516. SCM vcell = scm_sysintern0_no_module_lookup ("use-emacs-interface");
  517. SCM_SETCDR (vcell, SCM_BOOL(use_emacs_interface));
  518. }
  519. /* Handle the `-e' switch, if it was specified. */
  520. if (!SCM_NULLP (entry_point))
  521. tail = scm_cons (scm_cons2 (entry_point,
  522. scm_cons (sym_command_line, SCM_EOL),
  523. SCM_EOL),
  524. tail);
  525. /* If we didn't end with a -c or a -s, start the repl. */
  526. if (interactive)
  527. {
  528. tail = scm_cons (scm_cons (sym_top_repl, SCM_EOL), tail);
  529. }
  530. else
  531. {
  532. /* After doing all the other actions prescribed by the command line,
  533. quit. */
  534. tail = scm_cons (scm_cons (sym_quit, SCM_EOL),
  535. tail);
  536. /* Allow asyncs (signal handlers etc.) to be run. */
  537. scm_mask_ints = 0;
  538. }
  539. /* After the following line, actions will be added to the front. */
  540. tail = scm_reverse_x (tail, SCM_UNDEFINED);
  541. /* If we didn't end with a -c or a -s and didn't supply a -q, load
  542. the user's customization file. */
  543. if (interactive && !inhibit_user_init)
  544. {
  545. tail = scm_cons (scm_cons (sym_load_user_init, SCM_EOL), tail);
  546. }
  547. {
  548. SCM val = scm_cons (sym_begin, tail);
  549. #if 0
  550. scm_write (val, SCM_UNDEFINED);
  551. scm_newline (SCM_UNDEFINED);
  552. #endif
  553. return val;
  554. }
  555. }
  556. void
  557. scm_shell (int argc, char **argv)
  558. {
  559. /* If present, add SCSH-style meta-arguments from the top of the
  560. script file to the argument vector. See the SCSH manual: "The
  561. meta argument" for more details. */
  562. {
  563. char **new_argv = scm_get_meta_args (argc, argv);
  564. if (new_argv)
  565. {
  566. argv = new_argv;
  567. argc = scm_count_argv (new_argv);
  568. }
  569. }
  570. exit (scm_exit_status (scm_eval_x (scm_compile_shell_switches (argc,argv))));
  571. }
  572. void
  573. scm_init_script ()
  574. {
  575. #include "libguile/script.x"
  576. }
  577. /*
  578. Local Variables:
  579. c-file-style: "gnu"
  580. End:
  581. */