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