script.c 17 KB

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