script.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. /* Copyright (C) 1994-1998, 2000-2011 Free Software Foundation, Inc.
  2. * This library is free software; you can redistribute it and/or
  3. * modify it under the terms of the GNU Lesser General Public License
  4. * as published by the Free Software Foundation; either version 3 of
  5. * the License, or (at your option) any later version.
  6. *
  7. * This library is distributed in the hope that it will be useful, but
  8. * WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. * Lesser General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU Lesser General Public
  13. * License along with this library; if not, write to the Free Software
  14. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. * 02110-1301 USA
  16. */
  17. /* "script.c" argv tricks for `#!' scripts.
  18. Authors: Aubrey Jaffer and Jim Blandy */
  19. #ifdef HAVE_CONFIG_H
  20. # include <config.h>
  21. #endif
  22. #include <localcharset.h>
  23. #include <stdlib.h>
  24. #include <stdio.h>
  25. #include <errno.h>
  26. #include <ctype.h>
  27. #include <uniconv.h>
  28. #include "libguile/_scm.h"
  29. #include "libguile/eval.h"
  30. #include "libguile/feature.h"
  31. #include "libguile/load.h"
  32. #include "libguile/private-gc.h" /* scm_getenv_int */
  33. #include "libguile/read.h"
  34. #include "libguile/script.h"
  35. #include "libguile/strings.h"
  36. #include "libguile/strports.h"
  37. #include "libguile/validate.h"
  38. #include "libguile/version.h"
  39. #include "libguile/vm.h"
  40. #ifdef HAVE_STRING_H
  41. #include <string.h>
  42. #endif
  43. #ifdef HAVE_UNISTD_H
  44. #include <unistd.h> /* for X_OK define */
  45. #endif
  46. #ifdef HAVE_IO_H
  47. #include <io.h>
  48. #endif
  49. /* Concatentate str2 onto str1 at position n and return concatenated
  50. string if file exists; 0 otherwise. */
  51. static char *
  52. scm_cat_path (char *str1, const char *str2, long n)
  53. {
  54. if (!n)
  55. n = strlen (str2);
  56. if (str1)
  57. {
  58. size_t len = strlen (str1);
  59. str1 = (char *) realloc (str1, (size_t) (len + n + 1));
  60. if (!str1)
  61. return 0L;
  62. strncat (str1 + len, str2, n);
  63. return str1;
  64. }
  65. str1 = (char *) scm_malloc ((size_t) (n + 1));
  66. if (!str1)
  67. return 0L;
  68. str1[0] = 0;
  69. strncat (str1, str2, n);
  70. return str1;
  71. }
  72. #if 0
  73. static char *
  74. scm_try_path (char *path)
  75. {
  76. FILE *f;
  77. /* fprintf(stderr, "Trying %s\n", path);fflush(stderr); */
  78. if (!path)
  79. return 0L;
  80. SCM_SYSCALL (f = fopen (path, "r");
  81. );
  82. if (f)
  83. {
  84. fclose (f);
  85. return path;
  86. }
  87. free (path);
  88. return 0L;
  89. }
  90. static char *
  91. scm_sep_init_try (char *path, const char *sep, const char *initname)
  92. {
  93. if (path)
  94. path = scm_cat_path (path, sep, 0L);
  95. if (path)
  96. path = scm_cat_path (path, initname, 0L);
  97. return scm_try_path (path);
  98. }
  99. #endif
  100. #ifndef LINE_INCREMENTORS
  101. #define LINE_INCREMENTORS '\n'
  102. #ifdef MSDOS
  103. #define WHITE_SPACES ' ':case '\t':case '\r':case '\f':case 26
  104. #else
  105. #define WHITE_SPACES ' ':case '\t':case '\r':case '\f'
  106. #endif /* def MSDOS */
  107. #endif /* ndef LINE_INCREMENTORS */
  108. #ifndef MAXPATHLEN
  109. #define MAXPATHLEN 80
  110. #endif /* ndef MAXPATHLEN */
  111. #ifndef X_OK
  112. #define X_OK 1
  113. #endif /* ndef X_OK */
  114. char *
  115. scm_find_executable (const char *name)
  116. {
  117. char tbuf[MAXPATHLEN];
  118. int i = 0, c;
  119. FILE *f;
  120. /* fprintf(stderr, "s_f_e checking access %s ->%d\n", name, access(name, X_OK)); fflush(stderr); */
  121. if (access (name, X_OK))
  122. return 0L;
  123. f = fopen (name, "r");
  124. if (!f)
  125. return 0L;
  126. if ((fgetc (f) == '#') && (fgetc (f) == '!'))
  127. {
  128. while (1)
  129. switch (c = fgetc (f))
  130. {
  131. case /*WHITE_SPACES */ ' ':
  132. case '\t':
  133. case '\r':
  134. case '\f':
  135. case EOF:
  136. tbuf[i] = 0;
  137. fclose (f);
  138. return scm_cat_path (0L, tbuf, 0L);
  139. default:
  140. tbuf[i++] = c;
  141. break;
  142. }
  143. }
  144. fclose (f);
  145. return scm_cat_path (0L, name, 0L);
  146. }
  147. /* Read a \nnn-style escape. We've just read the backslash. */
  148. static int
  149. script_get_octal (FILE *f)
  150. #define FUNC_NAME "script_get_octal"
  151. {
  152. int i;
  153. int value = 0;
  154. for (i = 0; i < 3; i++)
  155. {
  156. int c = getc (f);
  157. if ('0' <= c && c <= '7')
  158. value = (value * 8) + (c - '0');
  159. else
  160. SCM_MISC_ERROR ("malformed script: bad octal backslash escape",
  161. SCM_EOL);
  162. }
  163. return value;
  164. }
  165. #undef FUNC_NAME
  166. static int
  167. script_get_backslash (FILE *f)
  168. #define FUNC_NAME "script_get_backslash"
  169. {
  170. int c = getc (f);
  171. switch (c)
  172. {
  173. case 'a': return '\a';
  174. case 'b': return '\b';
  175. case 'f': return '\f';
  176. case 'n': return '\n';
  177. case 'r': return '\r';
  178. case 't': return '\t';
  179. case 'v': return '\v';
  180. case '\\':
  181. case ' ':
  182. case '\t':
  183. case '\n':
  184. return c;
  185. case '0': case '1': case '2': case '3':
  186. case '4': case '5': case '6': case '7':
  187. ungetc (c, f);
  188. return script_get_octal (f);
  189. case EOF:
  190. SCM_MISC_ERROR ("malformed script: backslash followed by EOF", SCM_EOL);
  191. return 0; /* not reached? */
  192. default:
  193. SCM_MISC_ERROR ("malformed script: bad backslash sequence", SCM_EOL);
  194. return 0; /* not reached? */
  195. }
  196. }
  197. #undef FUNC_NAME
  198. static char *
  199. script_read_arg (FILE *f)
  200. #define FUNC_NAME "script_read_arg"
  201. {
  202. size_t size = 7;
  203. char *buf = scm_malloc (size + 1);
  204. size_t len = 0;
  205. if (! buf)
  206. return 0;
  207. for (;;)
  208. {
  209. int c = getc (f);
  210. switch (c)
  211. {
  212. case '\\':
  213. c = script_get_backslash (f);
  214. /* The above produces a new character to add to the argument.
  215. Fall through. */
  216. default:
  217. if (len >= size)
  218. {
  219. size = (size + 1) * 2;
  220. buf = realloc (buf, size);
  221. if (! buf)
  222. return 0;
  223. }
  224. buf[len++] = c;
  225. break;
  226. case '\n':
  227. /* This may terminate an arg now, but it will terminate the
  228. entire list next time through. */
  229. ungetc ('\n', f);
  230. case EOF:
  231. if (len == 0)
  232. {
  233. free (buf);
  234. return 0;
  235. }
  236. /* Otherwise, those characters terminate the argument; fall
  237. through. */
  238. case ' ':
  239. buf[len] = '\0';
  240. return buf;
  241. case '\t':
  242. free (buf);
  243. SCM_MISC_ERROR ("malformed script: TAB in meta-arguments", SCM_EOL);
  244. return 0; /* not reached? */
  245. }
  246. }
  247. }
  248. #undef FUNC_NAME
  249. static int
  250. script_meta_arg_P (char *arg)
  251. {
  252. if ('\\' != arg[0])
  253. return 0L;
  254. #ifdef MSDOS
  255. return !arg[1];
  256. #else
  257. switch (arg[1])
  258. {
  259. case 0:
  260. case '%':
  261. case WHITE_SPACES:
  262. return !0;
  263. default:
  264. return 0L;
  265. }
  266. #endif
  267. }
  268. char **
  269. scm_get_meta_args (int argc, char **argv)
  270. {
  271. int nargc = argc, argi = 1, nargi = 1;
  272. char *narg, **nargv;
  273. if (!(argc > 2 && script_meta_arg_P (argv[1])))
  274. return 0L;
  275. if (!(nargv = (char **) scm_malloc ((1 + nargc) * sizeof (char *))))
  276. return 0L;
  277. nargv[0] = argv[0];
  278. while (((argi + 1) < argc) && (script_meta_arg_P (argv[argi])))
  279. {
  280. FILE *f = fopen (argv[++argi], "r");
  281. if (f)
  282. {
  283. nargc--; /* to compensate for replacement of '\\' */
  284. while (1)
  285. switch (getc (f))
  286. {
  287. case EOF:
  288. free (nargv);
  289. return 0L;
  290. default:
  291. continue;
  292. case '\n':
  293. goto found_args;
  294. }
  295. found_args:
  296. /* FIXME: we leak the result of calling script_read_arg. */
  297. while ((narg = script_read_arg (f)))
  298. if (!(nargv = (char **) realloc (nargv,
  299. (1 + ++nargc) * sizeof (char *))))
  300. return 0L;
  301. else
  302. nargv[nargi++] = narg;
  303. fclose (f);
  304. nargv[nargi++] = argv[argi++];
  305. }
  306. }
  307. while (argi <= argc)
  308. nargv[nargi++] = argv[argi++];
  309. return nargv;
  310. }
  311. int
  312. scm_count_argv (char **argv)
  313. {
  314. int argc = 0;
  315. while (argv[argc])
  316. argc++;
  317. return argc;
  318. }
  319. /* For use in error messages. */
  320. char *scm_usage_name = 0;
  321. void
  322. scm_shell_usage (int fatal, char *message)
  323. {
  324. scm_call_3 (scm_c_private_ref ("ice-9 command-line",
  325. "shell-usage"),
  326. (scm_usage_name
  327. ? scm_from_locale_string (scm_usage_name)
  328. : scm_from_latin1_string ("guile")),
  329. scm_from_bool (fatal),
  330. (message
  331. ? scm_from_locale_string (message)
  332. : SCM_BOOL_F));
  333. }
  334. /* Return a list of strings from ARGV, which contains ARGC strings
  335. assumed to be encoded in the current locale. Use
  336. `environ_locale_charset' instead of relying on
  337. `scm_from_locale_string' because the user hasn't had a change to call
  338. (setlocale LC_ALL "") yet.
  339. XXX: This hack is for 2.0 and will be removed in the next stable
  340. series where the `setlocale' call will be implicit. See
  341. <http://lists.gnu.org/archive/html/guile-devel/2011-11/msg00040.html>
  342. for details. */
  343. static SCM
  344. locale_arguments_to_string_list (int argc, char **const argv)
  345. {
  346. int i;
  347. SCM lst;
  348. const char *encoding;
  349. encoding = environ_locale_charset ();
  350. for (i = argc - 1, lst = SCM_EOL;
  351. i >= 0;
  352. i--)
  353. lst = scm_cons (scm_from_stringn (argv[i], (size_t) -1, encoding,
  354. SCM_FAILED_CONVERSION_ESCAPE_SEQUENCE),
  355. lst);
  356. return lst;
  357. }
  358. /* Set the value returned by `program-arguments', given ARGC and ARGV. */
  359. void
  360. scm_i_set_boot_program_arguments (int argc, char *argv[])
  361. {
  362. scm_fluid_set_x (scm_program_arguments_fluid,
  363. locale_arguments_to_string_list (argc, argv));
  364. }
  365. /* Given an array of command-line switches, return a Scheme expression
  366. to carry out the actions specified by the switches.
  367. */
  368. SCM
  369. scm_compile_shell_switches (int argc, char **argv)
  370. {
  371. return scm_call_2 (scm_c_public_ref ("ice-9 command-line",
  372. "compile-shell-switches"),
  373. locale_arguments_to_string_list (argc, argv),
  374. (scm_usage_name
  375. ? scm_from_locale_string (scm_usage_name)
  376. : scm_from_latin1_string ("guile")));
  377. }
  378. void
  379. scm_shell (int argc, char **argv)
  380. {
  381. /* If present, add SCSH-style meta-arguments from the top of the
  382. script file to the argument vector. See the SCSH manual: "The
  383. meta argument" for more details. */
  384. {
  385. char **new_argv = scm_get_meta_args (argc, argv);
  386. if (new_argv)
  387. {
  388. argv = new_argv;
  389. argc = scm_count_argv (new_argv);
  390. }
  391. }
  392. exit (scm_exit_status (scm_eval_x (scm_compile_shell_switches (argc, argv),
  393. scm_current_module ())));
  394. }
  395. void
  396. scm_init_script ()
  397. {
  398. #include "libguile/script.x"
  399. }
  400. /*
  401. Local Variables:
  402. c-file-style: "gnu"
  403. End:
  404. */