300c8ed536ff7c9dbddc4ec223a7bd6dc87b2ff5
5 * (c) 2020 Mark Wooding
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Runlisp, a tool for invoking Common Lisp scripts.
12 * Runlisp is free software: you can redistribute it and/or modify it
13 * under the terms of the GNU General Public License as published by the
14 * Free Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
17 * Runlisp is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 * You should have received a copy of the GNU General Public License
23 * along with Runlisp. If not, see <https://www.gnu.org/licenses/>.
26 /*----- Header files ------------------------------------------------------*/
43 /*----- Common Lisp runes -------------------------------------------------*/
45 /* A common preamble rune to do the necessary things.
47 * We need to ensure that `asdf' (and therefore `uiop') is loaded. And we
48 * should arrange for `:runlisp-script' to find its way into the `*features*'
49 * list so that scripts can notice that they're being invoked from the
50 * command line rather than loaded into a resident session, and actually do
53 #define COMMON_PRELUDE_RUNE \
55 "(setf *load-verbose* nil *compile-verbose* nil) " \
56 "(require \"asdf\") " \
57 "(funcall (intern \"REGISTER-IMMUTABLE-SYSTEM\" " \
58 "(find-package \"ASDF\")) " \
60 "(set-dispatch-macro-character " \
62 "(lambda (#1=#:stream #2=#:char #3=#:arg) " \
63 "(declare (ignore #2# #3#)) " \
64 "(values (read-line #1#)))) " \
65 "(pushnew :runlisp-script *features*))"
67 /* Get `uiop' to re-check the command-line arguments following an image
70 #define IMAGE_RESTORE_RUNE \
71 "(uiop:call-image-restore-hook)"
73 /* Some Lisps leave crud in the `COMMON-LISP-USER' package. Clear it out. */
74 #define CLEAR_CL_USER_RUNE \
75 "(let ((#4=#:pkg (find-package \"COMMON-LISP-USER\"))) " \
76 "(with-package-iterator (#5=#:next #4# :internal) " \
77 "(loop (multiple-value-bind (#6=#:anyp #7=#:sym #8=#:how) " \
79 "(declare (ignore #8#)) " \
80 "(unless #6# (return)) " \
81 "(unintern #7# #4#)))))"
83 /*----- Handy macros ------------------------------------------------------*/
85 #define N(v) (sizeof(v)/sizeof((v)[0]))
88 # define GCC_VERSION_P(maj, min) \
89 (__GNUC__ > (maj) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min)))
91 # define GCC_VERSION_P(maj, min) 0
95 # define CLANG_VERSION_P(maj, min) \
96 (__clang_major__ > (maj) || (__clang_major__ == (maj) && \
97 __clang_minor__ >= (min)))
99 # define CLANG_VERSION_P(maj, min) 0
102 #if GCC_VERSION_P(2, 5) || CLANG_VERSION_P(3, 3)
103 # define NORETURN __attribute__((__noreturn__))
104 # define PRINTF_LIKE(fix, aix) __attribute__((__format__(printf, fix, aix)))
107 #if GCC_VERSION_P(4, 0) || CLANG_VERSION_P(3, 3)
108 # define EXECL_LIKE(ntrail) __attribute__((__sentinel__(ntrail)))
111 #define CTYPE_HACK(func, ch) (func((unsigned char)(ch)))
112 #define ISSPACE(ch) CTYPE_HACK(isspace, ch)
114 #define MEMCMP(x, op, y, n) (memcmp((x), (y), (n)) op 0)
115 #define STRCMP(x, op, y) (strcmp((x), (y)) op 0)
116 #define STRNCMP(x, op, y, n) (strncmp((x), (y), (n)) op 0)
118 #define END ((const char *)0)
120 /*----- The Lisp implementation table -------------------------------------*/
122 /* The systems, in decreasing order of (not quite my personal) preference.
123 * This list is used to initialize various tables and constants.
125 #define LISP_SYSTEMS(_) \
134 #define DEFSYS(sys) sys##_INDEX,
141 #define DEFFLAG(sys) sys##_FLAG = 1 << sys##_INDEX,
142 LISP_SYSTEMS(DEFFLAG
)
145 #define SETFLAG(sys) | sys##_FLAG
146 LISP_SYSTEMS(SETFLAG
)
153 #define DECLENTRY(sys) \
154 static void run_##sys(struct argstate *, const char *);
155 LISP_SYSTEMS(DECLENTRY
)
158 static const struct systab
{
161 void (*run
)(struct argstate
*, const char *);
163 #define SYSENTRY(sys) { #sys, sys##_FLAG, run_##sys },
164 LISP_SYSTEMS(SYSENTRY
)
168 /*----- Diagnostic utilities ----------------------------------------------*/
170 static const char *progname
= "runlisp";
172 static void set_progname(const char *prog
)
176 p
= strrchr(prog
, '/');
177 progname
= p ? p
+ 1 : progname
;
180 static void vmoan(const char *msg
, va_list ap
)
182 fprintf(stderr
, "%s: ", progname
);
183 vfprintf(stderr
, msg
, ap
);
187 static PRINTF_LIKE(1, 2) void moan(const char *msg
, ...)
188 { va_list ap
; va_start(ap
, msg
); vmoan(msg
, ap
); va_end(ap
); }
190 static NORETURN
PRINTF_LIKE(1, 2) void lose(const char *msg
, ...)
191 { va_list ap
; va_start(ap
, msg
); vmoan(msg
, ap
); va_end(ap
); exit(127); }
193 /*----- Memory allocation -------------------------------------------------*/
195 static void *xmalloc(size_t n
)
200 p
= malloc(n
); if (!p
) lose("failed to allocate memory");
204 static void *xrealloc(void *p
, size_t n
)
206 if (!n
) { free(p
); return (0); }
207 else if (!p
) return (xmalloc(n
));
208 p
= realloc(p
, n
); if (!p
) lose("failed to allocate memory");
212 static char *xstrdup(const char *p
)
214 size_t n
= strlen(p
) + 1;
215 char *q
= xmalloc(n
);
221 /*----- Dynamic strings ---------------------------------------------------*/
227 #define DSTR_INIT { 0, 0, 0 }
230 static void dstr_init(struct dstr *d) { d->p = 0; d->len = d->sz = 0; }
233 static void dstr_reset(struct dstr
*d
) { d
->len
= 0; }
235 static void dstr_ensure(struct dstr
*d
, size_t n
)
237 size_t need
= d
->len
+ n
, newsz
;
239 if (need
<= d
->sz
) return;
240 newsz
= d
->sz ?
2*d
->sz
: 16;
241 while (newsz
< need
) newsz
*= 2;
242 d
->p
= xrealloc(d
->p
, newsz
); d
->sz
= newsz
;
245 static void dstr_release(struct dstr
*d
) { free(d
->p
); }
247 static void dstr_putm(struct dstr
*d
, const void *p
, size_t n
)
248 { dstr_ensure(d
, n
); memcpy(d
->p
+ d
->len
, p
, n
); d
->len
+= n
; }
250 static void dstr_puts(struct dstr
*d
, const char *p
)
252 size_t n
= strlen(p
);
254 dstr_ensure(d
, n
+ 1);
255 memcpy(d
->p
+ d
->len
, p
, n
+ 1);
259 static void dstr_putc(struct dstr
*d
, int ch
)
260 { dstr_ensure(d
, 1); d
->p
[d
->len
++] = ch
; }
262 static void dstr_putz(struct dstr
*d
)
263 { dstr_ensure(d
, 1); d
->p
[d
->len
] = 0; }
265 static int dstr_readline(struct dstr
*d
, FILE *fp
)
272 if (!fgets(d
->p
+ d
->len
, d
->sz
- d
->len
, fp
)) break;
273 n
= strlen(d
->p
+ d
->len
); assert(n
> 0); any
= 1;
275 if (d
->p
[d
->len
- 1] == '\n') { d
->p
[--d
->len
] = 0; break; }
278 if (!any
) return (-1);
281 /*----- Dynamic vectors of strings ----------------------------------------*/
287 #define ARGV_INIT { 0, 0, 0, 0 }
290 static void argv_init(struct argv *av)
291 { av->v = 0; av->o = av->n = av->sz = 0; }
295 static void argv_reset(struct argv *av) { av->o = av->n = 0; }
298 static void argv_ensure(struct argv
*av
, size_t n
)
300 size_t need
= av
->n
+ av
->o
+ n
, newsz
;
302 if (need
<= av
->sz
) return;
303 newsz
= av
->sz ?
2*av
->sz
: 8;
304 while (newsz
< need
) newsz
*= 2;
305 av
->v
= xrealloc(av
->v
, newsz
*sizeof(const char *)); av
->sz
= newsz
;
308 static void argv_ensure_offset(struct argv
*av
, size_t n
)
312 /* Stupid version. We won't, in practice, be prepending lots of stuff, so
313 * avoid the extra bookkeeping involved in trying to make a double-ended
314 * extendable array asymptotically efficient.
316 if (av
->o
>= n
) return;
318 while (newoff
< n
) newoff
*= 2;
319 argv_ensure(av
, newoff
- av
->o
);
320 memmove(av
->v
+ newoff
, av
->v
+ av
->o
, av
->n
*sizeof(const char *));
324 static void argv_release(struct argv
*av
) { free(av
->v
); }
326 static void argv_append(struct argv
*av
, const char *p
)
327 { argv_ensure(av
, 1); av
->v
[av
->n
++ + av
->o
] = p
; }
329 static void argv_appendz(struct argv
*av
)
330 { argv_ensure(av
, 1); av
->v
[av
->n
+ av
->o
] = 0; }
332 static void argv_appendn(struct argv
*av
, const char *const *v
, size_t n
)
335 memcpy(av
->v
+ av
->n
+ av
->o
, v
, n
*sizeof(const char *));
340 static void argv_appendav(struct argv *av, const struct argv *bv)
341 { argv_appendn(av, bv->v + bv->o, bv->n); }
345 static void argv_appendv(struct argv *av, va_list ap)
350 { p = va_arg(ap, const char *); if (!p) break; argv_append(av, p); }
355 static EXECL_LIKE(0) void argv_appendl(struct argv *av, ...)
356 { va_list ap; va_start(ap, av); argv_appendv(av, ap); va_end(ap); }
359 static void argv_prepend(struct argv
*av
, const char *p
)
360 { argv_ensure_offset(av
, 1); av
->v
[--av
->o
] = p
; av
->n
++; }
363 static void argv_prependn(struct argv *av, const char *const *v, size_t n)
365 argv_ensure_offset(av, 1);
366 av->o -= n; av->n += n;
367 memcpy(av->v + av->o, v, n*sizeof(const char *));
372 static void argv_prependav(struct argv *av, const struct argv *bv)
373 { argv_prependn(av, bv->v + bv->o, bv->n); }
376 static void argv_prependv(struct argv
*av
, va_list ap
)
382 p
= va_arg(ap
, const char *); if (!p
) break;
383 argv_prepend(av
, p
); n
++;
387 p
= v
[0]; v
[0] = v
[n
- 1]; v
[n
- 1] = p
;
392 static EXECL_LIKE(0) void argv_prependl(struct argv
*av
, ...)
393 { va_list ap
; va_start(ap
, av
); argv_prependv(av
, ap
); va_end(ap
); }
395 /*----- Lisp system table (redux) -----------------------------------------*/
397 static const struct systab
*find_system(const char *name
)
399 const struct systab
*sys
;
402 for (i
= 0; i
< NSYS
; i
++) {
404 if (STRCMP(name
, ==, sys
->name
)) return (sys
);
406 lose("unknown Lisp system `%s'", name
);
409 static void lisp_quote_string(struct dstr
*d
, const char *p
)
414 n
= strcspn(p
, "\"\\");
415 if (n
) { dstr_putm(d
, p
, n
); p
+= n
; }
417 dstr_putc(d
, '\\'); dstr_putc(d
, *p
++);
422 static const char *expand_rune(struct dstr
*d
, const char *rune
, ...)
424 const struct argv
*av
;
430 n
= strcspn(rune
, "%");
431 if (n
) { dstr_putm(d
, rune
, n
); rune
+= n
; }
434 case '%': dstr_putc(d
, '%'); break;
435 case 'e': lisp_quote_string(d
, va_arg(ap
, const char *)); break;
437 av
= va_arg(ap
, const struct argv
*);
438 for (i
= 0; i
< av
->n
; i
++) {
439 if (i
) dstr_putc(d
, ' ');
441 lisp_quote_string(d
, av
->v
[i
]);
445 default: lose("*** BUG unknown expansion `%%%c'", *rune
);
453 /*----- Argument processing -----------------------------------------------*/
456 const struct systab
*sys
[NSYS
];
460 #define SYSLIST_INIT { { 0 }, 0, 0 }
471 struct syslist allow
, pref
;
474 #define ARGSTATE_INIT { 0, 1, 0, SYSLIST_INIT, SYSLIST_INIT, ARGV_INIT }
476 /*----- Running programs --------------------------------------------------*/
479 static int file_exists_p(const struct argstate
*arg
, const char *path
,
484 if (stat(path
, &st
)) {
485 if (arg
&& arg
->verbose
> 2) moan("file `%s' not found", path
);
487 } else if (!(S_ISREG(st
.st_mode
))) {
488 if (arg
&& arg
->verbose
> 2) moan("`%s' is not a regular file", path
);
490 } else if ((f
&FEF_EXEC
) && access(path
, X_OK
)) {
491 if (arg
&& arg
->verbose
> 2) moan("file `%s' is not executable", path
);
494 if (arg
&& arg
->verbose
> 2) moan("found file `%s'", path
);
499 static int found_in_path_p(const struct argstate
*arg
, const char *prog
)
501 struct dstr p
= DSTR_INIT
, d
= DSTR_INIT
;
504 size_t n
, avail
, proglen
;
507 if (strchr(prog
, '/')) return (file_exists_p(arg
, prog
, 0));
508 path
= getenv("PATH");
515 avail
= p
.sz
- p
.len
;
516 n
= confstr(_CS_PATH
, p
.p
+ p
.len
, avail
);
517 if (avail
> n
) { i
++; assert(i
< 2); dstr_ensure(&p
, n
); goto again
; }
520 q
= p
.p
; proglen
= strlen(prog
);
524 if (q
[n
]) dstr_putm(&d
, q
, n
);
525 else dstr_putc(&d
, '.');
527 dstr_putm(&d
, prog
, proglen
);
529 if (file_exists_p(arg
, d
.p
, FEF_EXEC
)) {
530 if (arg
->verbose
== 2) moan("found program `%s'", d
.p
);
533 q
+= n
; if (!*q
) break; else q
++;
538 static void try_exec(const struct argstate
*arg
, struct argv
*av
)
540 struct dstr d
= DSTR_INIT
;
543 assert(av
->n
); argv_appendz(av
);
544 if (arg
->verbose
> 1) {
545 for (i
= 0; i
< av
->n
; i
++) {
546 if (i
) { dstr_putc(&d
, ','); dstr_putc(&d
, ' '); }
548 lisp_quote_string(&d
, av
->v
[av
->o
+ i
]);
552 moan("trying %s...", d
.p
);
555 { if (found_in_path_p(arg
, av
->v
[av
->o
])) exit(0); }
557 execvp(av
->v
[av
->o
], (/*unconst*/ char **)av
->v
+ av
->o
);
559 lose("failed to exec `%s': %s", av
->v
[av
->o
], strerror(errno
));
561 if (arg
->verbose
> 1) moan("`%s' not found", av
->v
[av
->o
]);
565 static const char *getenv_or_default(const char *var
, const char *dflt
)
566 { const char *p
= getenv(var
); return (p ? p
: dflt
); }
568 /*----- Invoking Lisp systems ---------------------------------------------*/
570 /* Steel Bank Common Lisp. */
572 static void run_sbcl(struct argstate
*arg
, const char *script
)
574 struct dstr d
= DSTR_INIT
;
576 argv_prependl(&arg
->av
, "--script", script
, END
);
578 dstr_puts(&d
, arg
->imagedir
);
580 dstr_puts(&d
, "sbcl+asdf.core");
581 if (!(arg
->f
&F_NODUMP
) && file_exists_p(arg
, d
.p
, 0))
582 argv_prependl(&arg
->av
,
584 "--eval", IMAGE_RESTORE_RUNE
,
587 argv_prependl(&arg
->av
, "--eval", COMMON_PRELUDE_RUNE
, END
);
589 argv_prependl(&arg
->av
, getenv_or_default("SBCL", "sbcl"),
592 try_exec(arg
, &arg
->av
);
596 /* Clozure Common Lisp. */
598 #define CCL_QUIT_RUNE \
601 static void run_ccl(struct argstate
*arg
, const char *script
)
603 struct dstr d
= DSTR_INIT
;
605 argv_prependl(&arg
->av
, "-b", "-n", "-Q",
611 dstr_puts(&d
, arg
->imagedir
);
613 dstr_puts(&d
, "ccl+asdf.image");
614 if (!(arg
->f
&F_NODUMP
) && file_exists_p(arg
, d
.p
, 0))
615 argv_prependl(&arg
->av
, "-I", d
.p
, "-e", IMAGE_RESTORE_RUNE
, END
);
617 argv_prependl(&arg
->av
, "-e", COMMON_PRELUDE_RUNE
, END
);
619 argv_prepend(&arg
->av
, getenv_or_default("CCL", "ccl"));
620 try_exec(arg
, &arg
->av
);
626 * CLisp causes much sadness. Superficially, it's the most sensible of all
627 * of the systems supported here: you just run `clisp SCRIPT -- ARGS ...' and
630 * The problems come when you want to do some preparatory work (e.g., load
631 * `asdf') and then run the script. There's a `-x' option to evaluate some
632 * Lisp code, but it has three major deficiencies.
634 * * It insists on printing the values of the forms it evaluates. It
635 * prints a blank line even if the form goes out of its way to produce no
636 * values at all. So the whole thing has to be a single top-level form
637 * which quits the Lisp rather than returning.
639 * * For some idiotic reason, you can have /either/ `-x' forms /or/ a
640 * script, but not both. So we have to include the `load' here
641 * explicitly. I suppose that was inevitable because we have to inhibit
642 * printing of the result forms, but it's still a separate source of
645 * * The icing on the cake: the `-x' forms are collectively concatenated --
646 * without spaces! -- and used to build a string stream, which is then
647 * assigned over the top of `*standard-input*', making the original stdin
648 * somewhat fiddly to track down.
650 * There's an `-i' option which will load a file without any of this
651 * stupidity, but nothing analogous for immediate expressions.
654 #define CLISP_COMMON_STARTUP_RUNES \
655 "(setf *standard-input* (ext:make-stream :input)) " \
656 "(load \"%e\" :verbose nil :print nil) " \
659 #define CLISP_STARTUP_RUNE \
661 COMMON_PRELUDE_RUNE " " \
662 CLISP_COMMON_STARTUP_RUNES ")"
664 #define CLISP_STARTUP_IMAGE_RUNE \
666 IMAGE_RESTORE_RUNE " " \
667 CLISP_COMMON_STARTUP_RUNES ")"
669 static void run_clisp(struct argstate
*arg
, const char *script
)
671 struct dstr d
= DSTR_INIT
, dd
= DSTR_INIT
;
673 dstr_puts(&d
, arg
->imagedir
);
675 dstr_puts(&d
, "clisp+asdf.mem");
676 if (!(arg
->f
&F_NODUMP
) && file_exists_p(arg
, d
.p
, 0))
677 argv_prependl(&arg
->av
, "-M", d
.p
, "-q",
678 "-x", expand_rune(&dd
, CLISP_STARTUP_IMAGE_RUNE
, script
),
682 argv_prependl(&arg
->av
, "-norc", "-q",
683 "-x", expand_rune(&dd
, CLISP_STARTUP_RUNE
, script
),
687 argv_prepend(&arg
->av
, getenv_or_default("CLISP", "clisp"));
688 try_exec(arg
, &arg
->av
);
695 /* Embeddable Common Lisp. *
697 * ECL is changing its command-line option syntax in version 16. I have no
698 * idea why they think the result can ever be worth the pain of a transition.
707 #define ECL_STARTUP_RUNE \
709 COMMON_PRELUDE_RUNE " " \
710 CLEAR_CL_USER_RUNE ")"
712 static void run_ecl(struct argstate
*arg
, const char *script
)
714 struct dstr d
= DSTR_INIT
;
716 dstr_puts(&d
, arg
->imagedir
);
718 dstr_puts(&d
, "ecl+asdf");
719 if (!(arg
->f
&F_NODUMP
) && file_exists_p(arg
, d
.p
, FEF_EXEC
)) {
720 argv_prependl(&arg
->av
, "-s", script
, "--", END
);
721 argv_prependl(&arg
->av
, d
.p
, END
);
723 argv_prependl(&arg
->av
, ECLOPT
"shell", script
, "--", END
);
724 argv_prependl(&arg
->av
, getenv_or_default("ECL", "ecl"), ECLOPT
"norc",
725 ECLOPT
"eval", ECL_STARTUP_RUNE
,
728 try_exec(arg
, &arg
->av
);
731 /* Carnegie--Mellon University Common Lisp. */
733 #define CMUCL_STARTUP_RUNE \
735 "(setf ext:*require-verbose* nil) " \
736 COMMON_PRELUDE_RUNE ")"
737 #define CMUCL_QUIT_RUNE \
740 static void run_cmucl(struct argstate
*arg
, const char *script
)
742 struct dstr d
= DSTR_INIT
;
744 argv_prependl(&arg
->av
,
746 "-eval", CMUCL_QUIT_RUNE
,
750 dstr_puts(&d
, arg
->imagedir
);
752 dstr_puts(&d
, "cmucl+asdf.core");
753 if (!(arg
->f
&F_NODUMP
) && file_exists_p(arg
, d
.p
, 0))
754 argv_prependl(&arg
->av
, "-core", d
.p
, "-eval", IMAGE_RESTORE_RUNE
, END
);
756 argv_prependl(&arg
->av
, "-batch", "-noinit", "-nositeinit", "-quiet",
757 "-eval", CMUCL_STARTUP_RUNE
,
760 argv_prepend(&arg
->av
, getenv_or_default("CMUCL", "cmucl"));
761 try_exec(arg
, &arg
->av
);
765 /* Armed Bear Common Lisp. *
767 * CLisp made a worthy effort, but ABCL still manages to take the price.
769 * * ABCL manages to avoid touching the `stderr' stream at all, ever. Its
770 * startup machinery finds `stdout' (as `java.lang.System.out'), wraps it
771 * up in a Lisp stream, and uses the result as `*standard-output*' and
772 * `*error-output*' (and a goodly number of other things too). So we
773 * must manufacture a working `stderr' the hard way.
775 * * There doesn't appear to be any easy way to prevent toplevel errors
776 * from invoking the interactive debugger. For extra fun, the debugger
777 * reads from `stdin' by default, so an input file which somehow manages
778 * to break the script can then take over its brain by providing Lisp
779 * forms for the debugger to evaluate.
782 #define ABCL_STARTUP_RUNE \
783 "(let ((#9=#:script \"%e\")) " \
784 COMMON_PRELUDE_RUNE " " \
785 CLEAR_CL_USER_RUNE " " \
787 /* Replace the broken `*error-output*' stream with a working \
788 * copy of `stderr'. \
790 "(setf *error-output* " \
791 "(java:jnew \"org.armedbear.lisp.Stream\" " \
792 "'sys::system-stream " \
793 "(java:jfield \"java.lang.System\" \"err\") " \
797 /* Trap errors signalled by the script and arrange for them \
798 * to actually kill the process rather than ending up in the \
799 * interactive debugger. \
801 "(handler-case (load #9# :verbose nil :print nil) " \
803 "(format *error-output* \"~A (unhandled error): ~A~%%\" " \
805 "(ext:quit :status 255))))"
807 static void run_abcl(struct argstate
*arg
, const char *script
)
809 struct dstr d
= DSTR_INIT
;
811 argv_prependl(&arg
->av
, getenv_or_default("ABCL", "abcl"),
812 "--batch", "--noinform", "--noinit", "--nosystem",
813 "--eval", expand_rune(&d
, ABCL_STARTUP_RUNE
, script
),
816 try_exec(arg
, &arg
->av
);
820 /*----- Main code ---------------------------------------------------------*/
822 static void version(FILE *fp
)
823 { fprintf(fp
, "%s, version %s\n", progname
, PACKAGE_VERSION
); }
825 static void usage(FILE *fp
)
827 fprintf(fp
, "usage: %s [-CDEnqv] [-I IMAGEDIR] "
828 "[-L SYS,SYS,...] [-P SYS,SYS,...]\n"
829 "\t[--] SCRIPT [ARGUMENTS ...] |\n"
830 "\t[-e EXPR] [-p EXPR] [-l FILE] [--] [ARGUMENTS ...]\n",
834 static void help(FILE *fp
)
836 version(fp
); fputc('\n', fp
); usage(fp
);
839 --help Show this help text and exit successfully.\n\
840 --version Show the version number and exit successfully.\n\
841 -C Clear the list of preferred Lisp systems.\n\
842 -D Run system Lisp images, rather than custom images.\n\
843 -E Don't read embedded options from the script.\n\
844 -I IMAGEDIR Look for custom images in IMAGEDIR rather than\n\
846 -L SYS,SYS,... Only use the listed Lisp systems.the script.\n\
847 -P SYS,SYS,... Prefer the listed Lisp systems.\n\
848 -e EXPR Evaluate EXPR (can be repeated).\n\
849 -l FILE Load FILE (can be repeated).\n\
850 -n Don't actually run the script (useful with `-v')\n\
851 -p EXPR Print (`prin1') EXPR (can be repeated).\n\
852 -q Don't print warning messages.\n\
853 -v Print informational messages (repeat for even more).\n",
857 /* Parse a comma-separated list of system names SPEC, and add the named
860 static void parse_syslist(const char *spec
, const struct argstate
*arg
,
861 struct syslist
*list
, const char *what
)
863 char *copy
= xstrdup(spec
), *p
= copy
, *q
;
864 const struct systab
*sys
;
869 if (p
[n
]) q
= p
+ n
+ 1;
871 p
[n
] = 0; sys
= find_system(p
);
872 if (list
->f
&sys
->f
) {
873 if (arg
->verbose
> 0)
874 moan("ignoring duplicate system `%s' in %s list", p
, what
);
876 list
->sys
[list
->n
++] = sys
;
885 static void push_eval_op(struct argstate
*arg
, char op
, const char *val
)
891 moan("must use `-e', `-p', or `-l' on command line");
898 p
[0] = op
; memcpy(p
+ 1, val
, n
);
899 argv_append(&arg
->av
, p
);
902 /* Parse a vector ARGS of command-line arguments. Update ARG with the
903 * results. NARG is the number of arguments, and *I_INOUT is the current
904 * index into the vector, to be updated on exit to identify the first
905 * non-option argument (or the end of the vector).
907 static void parse_arguments(struct argstate
*arg
, const char *const *args
,
908 size_t nargs
, size_t *i_inout
)
914 if (*i_inout
>= nargs
) break;
916 if (STRCMP(o
, ==, "--help")) { help(stdout
); exit(0); }
917 else if (STRCMP(o
, ==, "--version")) { version(stdout
); exit(0); }
918 if (!*o
|| *o
!= '-' || !o
[1]) break;
920 if (STRCMP(o
, ==, "--")) break;
926 #define GETARG do { \
930 if (*i_inout >= nargs) goto noarg; \
931 a = args[(*i_inout)++]; \
935 case 'C': arg
->pref
.n
= 0; arg
->pref
.f
= 0; break;
936 case 'D': arg
->f
|= F_NODUMP
; break;
937 case 'E': arg
->f
|= F_NOEMBED
; break;
938 case 'e': GETARG
; push_eval_op(arg
, '!', a
); break;
939 case 'p': GETARG
; push_eval_op(arg
, '?', a
); break;
940 case 'l': GETARG
; push_eval_op(arg
, '<', a
); break;
941 case 'n': arg
->f
|= F_NOACT
; break;
942 case 'q': if (arg
->verbose
) arg
->verbose
--; break;
943 case 'v': arg
->verbose
++; break;
947 GETARG
; arg
->imagedir
= xstrdup(a
);
952 parse_syslist(a
, arg
, &arg
->allow
, "allowed");
957 parse_syslist(a
, arg
, &arg
->pref
, "preferred");
961 moan("unknown option `%c'", opt
);
973 moan("missing argument for `-%c'", opt
);
979 /* Parse a string P into words (destructively), and process them as
980 * command-line options, updating ARG. Non-option arguments are not
981 * permitted. If `SOSF_EMACS' is set in FLAGS, then ignore `-*- ... -*-'
982 * editor turds. If `SOSF_ENDOK' is set, then accept `--' and ignore
983 * whatever comes after; otherwise, reject all positional arguments.
985 #define SOSF_EMACS 1u
986 #define SOSF_ENDOK 2u
987 static void scan_options_from_string(char *p
, struct argstate
*arg
,
989 const char *what
, const char *file
)
991 struct argv av
= ARGV_INIT
;
999 while (ISSPACE(*p
)) p
++;
1001 if ((flags
&SOSF_EMACS
) && p
[0] == '-' && p
[1] == '*' && p
[2] == '-') {
1002 p
= strstr(p
+ 3, "-*-");
1003 if (!p
) lose("unfinished local-variables list in %s `%s'", what
, file
);
1006 if ((flags
&SOSF_ENDOK
) &&
1007 p
[0] == '-' && p
[1] == '-' && (!p
[2] || ISSPACE(p
[2])))
1009 argv_append(&av
, p
); q
= p
;
1012 else if (f
&f_escape
) { *q
++ = *p
; f
&= ~f_escape
; }
1013 else if (st
&& *p
== st
) st
= 0;
1014 else if (st
!= '\'' && *p
== '\\') f
|= f_escape
;
1015 else if (!st
&& (*p
== '"' || *p
== '\'')) st
= *p
;
1016 else if (!st
&& ISSPACE(*p
)) break;
1022 if (f
&f_escape
) lose("unfinished escape in %s `%s'", what
, file
);
1023 if (st
) lose("unfinished `%c' string in %s `%s'", st
, what
, file
);
1026 i
= 0; parse_arguments(arg
, av
.v
, av
.n
, &i
);
1028 lose("positional argument `%s' in %s `%s'", av
.v
[i
], what
, file
);
1034 /* Read SCRIPT, and check for a `@RUNLISP:' marker in the second line. If
1035 * there is one, parse options from it, and update ARG.
1037 static void check_for_embedded_args(const char *script
, struct argstate
*arg
)
1039 struct dstr d
= DSTR_INIT
;
1043 fp
= fopen(script
, "r");
1044 if (!fp
) lose("can't read script `%s': %s", script
, strerror(errno
));
1046 if (dstr_readline(&d
, fp
)) goto end
;
1047 dstr_reset(&d
); if (dstr_readline(&d
, fp
)) goto end
;
1049 p
= strstr(d
.p
, "@RUNLISP:");
1051 scan_options_from_string(p
+ 9, arg
, SOSF_EMACS
| SOSF_ENDOK
,
1052 "embedded options in script", script
);
1057 lose("error reading script `%s': %s", script
, strerror(errno
));
1063 /* Read the file PATH (if it exists) and update ARG with the arguments parsed
1064 * from it. Ignore blank lines and (Unix- or Lisp-style) comments.
1066 static void read_config_file(const char *path
, struct argstate
*arg
)
1069 struct dstr d
= DSTR_INIT
;
1072 fp
= fopen(path
, "r");
1074 if (errno
== ENOENT
) {
1075 if (arg
->verbose
> 2)
1076 moan("ignoring nonexistent configuration file `%s'", path
);
1079 lose("failed to open configuration file `%s': %s",
1080 path
, strerror(errno
));
1082 if (arg
->verbose
> 1)
1083 moan("reading configuration file `%s'", path
);
1086 if (dstr_readline(&d
, fp
)) break;
1088 while (ISSPACE(*p
)) p
++;
1089 if (!*p
|| *p
== ';' || *p
== '#') continue;
1090 scan_options_from_string(p
, arg
, 0, "configuration file `%s'", path
);
1093 lose("invalid options in configuration file `%s'", path
);
1098 lose("error reading configuration file `%s': %s",
1099 path
, strerror(errno
));
1105 int main(int argc
, char *argv
[])
1107 struct dstr d
= DSTR_INIT
;
1108 const char *script
, *p
;
1113 struct argstate arg
= ARGSTATE_INIT
;
1115 /* Scan the command line. This gets low priority, since it's probably
1116 * from the script shebang.
1118 set_progname(argv
[0]); i
= 1;
1119 parse_arguments(&arg
, (const char *const *)argv
, argc
, &i
);
1121 if ((i
>= argc
&& !arg
.av
.n
) || (arg
.f
&F_BOGUS
))
1122 { usage(stderr
); exit(255); }
1124 /* Prepare the argument vector. Keep track of the number of arguments
1125 * here: we'll need to refer to this later.
1129 if (!(arg
.f
&F_NOEMBED
)) check_for_embedded_args(script
, &arg
);
1131 lose("invalid options in `%s' embedded option list", script
);
1133 script
= getenv("RUNLISP_EVAL");
1134 if (!script
) script
= DATADIR
"/eval.lisp";
1135 argv_append(&arg
.av
, "--");
1137 argv_appendn(&arg
.av
, (const char *const *)argv
+ i
, argc
- i
);
1140 /* Find the user's home directory. (Believe them if they set something
1143 home
= getenv("HOME");
1145 pw
= getpwuid(getuid());
1146 if (!pw
) lose("can't find user in password database");
1150 /* Check user configuration file `~/.runlisprc'. */
1152 dstr_puts(&d
, home
); dstr_putc(&d
, '/'); dstr_puts(&d
, ".runlisprc");
1153 read_config_file(d
.p
, &arg
);
1155 /* Check user configuration file `~/.config/runlisprc'. */
1157 p
= getenv("XDG_CONFIG_HOME");
1161 { dstr_puts(&d
, home
); dstr_putc(&d
, '/'); dstr_puts(&d
, ".config"); }
1162 dstr_putc(&d
, '/'); dstr_puts(&d
, "runlisprc");
1163 read_config_file(d
.p
, &arg
);
1165 /* Finally, check the environment variables. */
1166 p
= getenv("RUNLISP_OPTIONS");
1169 scan_options_from_string(t
, &arg
, 0,
1170 "environment variable", "RUNLISP_OPTIONS");
1174 lose("invalid options in environment variable `RUNLISP_OPTIONS'");
1175 if (!arg
.imagedir
) {
1176 arg
.imagedir
= getenv("RUNLISP_IMAGEDIR");
1177 if (!arg
.imagedir
) arg
.imagedir
= IMAGEDIR
;
1180 /* If no systems are listed as acceptable, try them all. */
1182 if (arg
.verbose
> 1)
1183 moan("no explicitly allowed implementations: allowing all");
1184 for (i
= 0; i
< NSYS
; i
++) arg
.allow
.sys
[i
] = &systab
[i
];
1185 arg
.allow
.n
= NSYS
; arg
.allow
.f
= (1u << NSYS
) - 1;
1188 /* Print what we're going to do. */
1189 if (arg
.verbose
> 2) {
1190 dstr_reset(&d
); p
= "";
1191 for (i
= 0; i
< arg
.allow
.n
; i
++)
1192 { dstr_puts(&d
, p
); p
= ", "; dstr_puts(&d
, arg
.allow
.sys
[i
]->name
); }
1193 moan("permitted Lisps: %s", d
.p
);
1195 dstr_reset(&d
); p
= "";
1196 for (i
= 0; i
< arg
.pref
.n
; i
++)
1197 { dstr_puts(&d
, p
); p
= ", "; dstr_puts(&d
, arg
.pref
.sys
[i
]->name
); }
1198 moan("preferred Lisps: %s", d
.p
);
1200 dstr_reset(&d
); p
= "";
1201 for (i
= 0; i
< arg
.pref
.n
; i
++)
1202 if (arg
.pref
.sys
[i
]->f
&arg
.allow
.f
)
1203 { dstr_puts(&d
, p
); p
= ", "; dstr_puts(&d
, arg
.pref
.sys
[i
]->name
); }
1204 for (i
= 0; i
< arg
.allow
.n
; i
++)
1205 if (!(arg
.allow
.sys
[i
]->f
&arg
.pref
.f
))
1206 { dstr_puts(&d
, p
); p
= ", "; dstr_puts(&d
, arg
.allow
.sys
[i
]->name
); }
1207 moan("overall preference order: %s", d
.p
);
1210 /* Inform `uiop' of the script name.
1212 * As an aside, this is a terrible interface. It's too easy to forget to
1213 * set it. (To illustrate this, `cl-launch -x' indeed forgets to set it.)
1214 * If you're lucky, the script just thinks that its argument is `nil', in
1215 * which case maybe it can use `*load-pathname*' as a fallback. If you're
1216 * unlucky, your script was invoked (possibly indirectly) by another
1217 * script, and now you've accidentally inherited the calling script's name.
1219 * It would have been far better simply to repeat the script name as the
1220 * first user argument, if nothing else had come readily to mind.
1222 if (setenv("__CL_ARGV0", script
, 1))
1223 lose("failed to set script-name environment variable");
1225 /* Work through the list of preferred Lisp systems, trying the ones which
1228 for (i
= 0; i
< arg
.pref
.n
; i
++)
1229 if (arg
.pref
.sys
[i
]->f
&arg
.allow
.f
) {
1230 arg
.av
.o
+= arg
.av
.n
- n
; arg
.av
.n
= n
;
1231 arg
.pref
.sys
[i
]->run(&arg
, script
);
1234 /* That didn't work. Try the remaining allowed systems, in the given
1237 for (i
= 0; i
< arg
.allow
.n
; i
++)
1238 if (!(arg
.allow
.sys
[i
]->f
&arg
.pref
.f
)) {
1239 arg
.av
.o
+= arg
.av
.n
- n
; arg
.av
.n
= n
;
1240 arg
.allow
.sys
[i
]->run(&arg
, script
);
1243 /* No joy. Give up. */
1244 argv_release(&arg
.av
);
1245 lose("no supported Lisp systems found");
1248 /*----- That's all, folks -------------------------------------------------*/