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