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