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