1.0.21.17: --script commandline argument
authorNikodemus Siivola <nikodemus@random-state.net>
Sat, 11 Oct 2008 13:34:44 +0000 (13:34 +0000)
committerNikodemus Siivola <nikodemus@random-state.net>
Sat, 11 Oct 2008 13:34:44 +0000 (13:34 +0000)
 * Works as both runtime and toplevel argument (which may imply the
   separation between the two is suspect?):

 * As a runtime argument it implies --noinform and the end of runtime
   arguments.

 * As a toplevel argument it implies --disable-debugger and the end of
   toplevel arguments. It additionally inhibits sysinit and userinit
   processing unless an explicit --userinit or --sysinit option is
   given before it.

   Then SBCL loads the specified specified file with :VERBOSE NIL and
   :PRINT NIL, discarding the first line if it start with #!.

   When the script file has been processed, SBCL exits without
   entering the REPL.

 * Documentation & a test.

 * Based loosely on an earlier patch by Kevin Reid.

NEWS
doc/manual/start-stop.texinfo
doc/sbcl.1
src/code/toplevel.lisp
src/runtime/runtime.c
tests/toplevel.sh
version.lisp-expr

diff --git a/NEWS b/NEWS
index a341dbe..5befbfa 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,9 @@ changes in sbcl-1.0.22 relative to 1.0.21:
   * minor incompatible change: --disable-debugger toplevel option now takes
     effect before processing of initialization files and --eval or --load
     options.
   * minor incompatible change: --disable-debugger toplevel option now takes
     effect before processing of initialization files and --eval or --load
     options.
+  * new feature: new commandline argument: --script, which supports
+    shebang lines. See documentation for details. (based on work by
+    Kevin Reid)
   * enhancement: inoccous calls to EVAL or generic functions dispatching
     on subclasses of eg. STREAM no longer cause compiler notes to appear.
   * enhancement: the system no longer resignals errors from --load and
   * enhancement: inoccous calls to EVAL or generic functions dispatching
     on subclasses of eg. STREAM no longer cause compiler notes to appear.
   * enhancement: the system no longer resignals errors from --load and
index d482992..be2a88a 100644 (file)
@@ -66,12 +66,31 @@ Integration}.
 @node Shebang Scripts
 @comment  node-name,  next,  previous,  up
 @subsection Shebang Scripts
 @node Shebang Scripts
 @comment  node-name,  next,  previous,  up
 @subsection Shebang Scripts
+@vindex sb-ext:*posix-argv*
+@vindex *posix-argv*
+
+Standard Unix tools that are interpreters follow a common command line
+protocol that is necessary to work with ``shebang scripts''. SBCL supports
+this via the @code{--script} command line option.
+
+Example file (@file{hello.lisp}):
+
+@lisp
+#!/usr/local/bin/sbcl --script
+(write-line "Hello, World!")
+@end lisp
 
 
-SBCL doesn't come with built-in support for shebang-line execution,
-but this can be provided with a shell trampoline, or by dispatching
-from initialization files (@pxref{Unix-style Command Line Protocol} for
-an example.)
+Usage examples:
+
+@smallexample
+$ ./hello.lisp
+Hello, World!
+@end smallexample
 
 
+@smallexample
+$ sbcl --script hello.lisp
+Hello, World!
+@end smallexample
 
 @node Stopping SBCL
 @comment  node-name,  next,  previous,  up
 
 @node Stopping SBCL
 @comment  node-name,  next,  previous,  up
@@ -195,6 +214,11 @@ startup. This makes it easier to write Lisp programs which work
 cleanly in Unix pipelines. See also the @code{--noprint} and
 @code{--disable-debugger} options.
 
 cleanly in Unix pipelines. See also the @code{--noprint} and
 @code{--disable-debugger} options.
 
+@item --script @var{filename}
+As a runtime option this is equivalent to @code{--noinform}
+@code{--end-runtime-options} @code{--script} @var{filename}. See the
+description of @code{--script} as a toplevel option below.
+
 @item --help
 Print some basic information about SBCL, then exit.
 
 @item --help
 Print some basic information about SBCL, then exit.
 
@@ -260,6 +284,14 @@ before loading of initialization files or processing @code{--eval} and
 @code{--load} options. See @code{sb-ext:disable-debugger} for details.
 @xref{Debugger Entry}.
 
 @code{--load} options. See @code{sb-ext:disable-debugger} for details.
 @xref{Debugger Entry}.
 
+@item --script @var{filename}
+Implies @code{--no-userinit} @code{--no-sysinit}
+@code{--disable-debugger} @code{--end-toplevel-options}.
+
+Causes the system to load the specified file instead of entering the
+read-eval-print-loop, and exit afterwards. If the file begins with a
+shebang line, it is ignored.
+
 @end table
 
 
 @end table
 
 
@@ -313,71 +345,9 @@ Some examples of what you may consider doing in the initialization
 files follow.
 
 @menu
 files follow.
 
 @menu
-* Unix-style Command Line Protocol::
 * Automatic Recompilation of Stale Fasls::
 @end menu
 
 * Automatic Recompilation of Stale Fasls::
 @end menu
 
-@node Unix-style Command Line Protocol
-@comment  node-name,  next,  previous,  up
-@subsubsection Unix-style Command Line Protocol
-@vindex sb-ext:*posix-argv*
-@vindex *posix-argv*
-
-Standard Unix tools that are interpreters follow a common command line
-protocol that is necessary to work with ``shebang scripts''. SBCL
-doesn't do this by default, but adding the following snippet to an
-initialization file does the trick:
-
-@lisp
-;;; If the first user-processable command-line argument is a filename,
-;;; disable the debugger, load the file handling shebang-line and quit.
-(let ((script (and (second *posix-argv*)
-                   (probe-file (second *posix-argv*)))))
-   (when script
-      ;; Handle shebang-line
-      (set-dispatch-macro-character #\# #\!
-                                    (lambda (stream char arg)
-                                       (declare (ignore char arg))
-                                       (read-line stream)))
-      ;; Disable debugger
-      (setf *invoke-debugger-hook*
-            (lambda (condition hook)
-              (declare (ignore hook))
-              ;; Uncomment to get backtraces on errors
-              ;; (sb-debug:backtrace 20)
-              (format *error-output* "Error: ~A~%" condition)
-              (quit)))
-      (load script)
-      (quit)))
-@end lisp
-
-Example file (@file{hello.lisp}):
-
-@lisp
-#!/usr/local/bin/sbcl --noinform
-(write-line "Hello, World!")
-@end lisp
-
-Usage examples:
-
-@smallexample
-$ ./hello.lisp
-Hello, World!
-@end smallexample
-
-@smallexample
-$ sbcl hello.lisp
-This is SBCL 0.8.13.70, an implementation of ANSI Common Lisp.
-More information about SBCL is available at <http://www.sbcl.org/>.
-
-SBCL is free software, provided as is, with absolutely no warranty.
-It is mostly in the public domain; some portions are provided under
-BSD-style licenses.  See the CREDITS and COPYING files in the
-distribution for more information.
-Hello, World!
-@end smallexample
-
-
 @node Automatic Recompilation of Stale Fasls
 @comment  node-name,  next,  previous,  up
 @subsubsection Automatic Recompilation of Stale Fasls
 @node Automatic Recompilation of Stale Fasls
 @comment  node-name,  next,  previous,  up
 @subsubsection Automatic Recompilation of Stale Fasls
index e4f4146..5776d61 100644 (file)
@@ -269,6 +269,11 @@ startup. (This makes it easier to write Lisp programs which work
 cleanly in Unix pipelines. See also the "\-\-noprint" and
 "\-\-disable\-debugger" options.)
 .TP 3
 cleanly in Unix pipelines. See also the "\-\-noprint" and
 "\-\-disable\-debugger" options.)
 .TP 3
+.B \-\-script <filename>
+As a runtime option equivalent to \-\-noinform
+\-\-end\-toplevel\-options \-\-script <filename>. See the description
+of \-\-script as a toplevel option below.
+.TP 3
 .B \-\-help
 Print some basic information about SBCL, then exit.
 .TP 3
 .B \-\-help
 Print some basic information about SBCL, then exit.
 .TP 3
@@ -325,6 +330,13 @@ debugger, allowing interactive diagnosis and possible intercession.
 This option disables the debugger, causing errors to print a backtrace
 and exit with status 1 instead -- which is a mode of operation better suited
 for batch processing. See the user manual on \f(CRSB\-EXT:DISABLE\-DEBUGGER\fR for details.
 This option disables the debugger, causing errors to print a backtrace
 and exit with status 1 instead -- which is a mode of operation better suited
 for batch processing. See the user manual on \f(CRSB\-EXT:DISABLE\-DEBUGGER\fR for details.
+.B \-\-script <filename>
+Implies \-\-no-sysinit \-\-no-userinit \-\-disable-debugger
+\-\-end\-toplevel\-options.
+
+Causes the system to load the specified file and exit immediately
+afterwards, instead of entering the readl-eval-print loop. If the file
+begins with a shebang line, it is ignored.
 .PP
 
 Regardless of the order in which toplevel options appear on the command
 .PP
 
 Regardless of the order in which toplevel options appear on the command
@@ -341,7 +353,12 @@ Any user initialization file is loaded, unless prohibited.
 \-\-eval and \-\-load options are processed in the order given.
 .PP
 
 \-\-eval and \-\-load options are processed in the order given.
 .PP
 
-Finally, the read-eval-print loop is entered.
+Finally, either the read-eval-print loop is entered or the file
+specified with \-\-script option is loaded.
+
+When running in the read-eval-print loop the system exits on end of
+file. Similarly, the system exits immediately after processing the
+file specified with \-\-script.
 
 Note that when running SBCL with the \-\-core option, using a core
 file created by a user call to the
 
 Note that when running SBCL with the \-\-core option, using a core
 file created by a user call to the
index e266366..135028c 100644 (file)
@@ -381,6 +381,30 @@ command-line.")
       (dolist (option options)
         (process-1 option)))))
 
       (dolist (option options)
         (process-1 option)))))
 
+;;; Skips past the shebang line on stream, if any.
+(defun maybe-skip-shebang-line (stream)
+  (let ((p (file-position stream)))
+    (flet ((next () (read-byte stream nil)))
+      (unwind-protect
+           (when (and (eq (next) (char-code #\#))
+                      (eq (next) (char-code #\!)))
+             (setf p nil)
+             (loop for x = (next)
+                   until (or (not x) (eq x (char-code #\newline)))))
+        (when p
+          (file-position stream p))))
+    t))
+
+(defun process-script (script)
+  (let ((pathname (native-pathname script))
+        (ok nil))
+    (unwind-protect
+         (with-open-file (f pathname :element-type :default)
+           (maybe-skip-shebang-line f)
+           (load f :verbose nil :print nil)
+           (setf ok t))
+      (quit :unix-status (if ok 0 1)))))
+
 ;; Errors while processing the command line cause the system to QUIT,
 ;; instead of trying to go into the Lisp debugger, because trying to
 ;; go into the Lisp debugger would get into various annoying issues of
 ;; Errors while processing the command line cause the system to QUIT,
 ;; instead of trying to go into the Lisp debugger, because trying to
 ;; go into the Lisp debugger would get into various annoying issues of
@@ -415,6 +439,8 @@ command-line.")
         (reversed-options nil)
         ;; Has a --noprint option been seen?
         (noprint nil)
         (reversed-options nil)
         ;; Has a --noprint option been seen?
         (noprint nil)
+        ;; Has a --script option been seen?
+        (script nil)
         ;; everything in *POSIX-ARGV* except for argv[0]=programname
         (options (rest *posix-argv*)))
 
         ;; everything in *POSIX-ARGV* except for argv[0]=programname
         (options (rest *posix-argv*)))
 
@@ -436,7 +462,14 @@ command-line.")
                         (pop options)
                         (startup-error
                          "unexpected end of command line options"))))
                         (pop options)
                         (startup-error
                          "unexpected end of command line options"))))
-             (cond ((string= option "--sysinit")
+             (cond ((string= option "--script")
+                    (pop-option)
+                    (setf disable-debugger t
+                          no-userinit t
+                          no-sysinit t
+                          script (pop-option))
+                    (return))
+                   ((string= option "--sysinit")
                     (pop-option)
                     (if sysinit
                         (startup-error "multiple --sysinit options")
                     (pop-option)
                     (if sysinit
                         (startup-error "multiple --sysinit options")
@@ -516,13 +549,23 @@ command-line.")
               (process-init-file sysinit :system))
             (unless no-userinit
               (process-init-file userinit :user))
               (process-init-file sysinit :system))
             (unless no-userinit
               (process-init-file userinit :user))
-            (process-eval/load-options (nreverse reversed-options)))
+            (process-eval/load-options (nreverse reversed-options))
+            (when script
+              (process-script script)
+              (bug "PROCESS-SCRIPT returned")))
         (abort ()
         (abort ()
-          :report "Skip to toplevel READ/EVAL/PRINT loop."
+          :report (lambda (s)
+                    (write-string
+                     (if script
+                         ;; In case script calls (enable-debugger)!
+                         "Abort script, exiting lisp."
+                         "Skip to toplevel READ/EVAL/PRINT loop.")
+                     s))
           (/show0 "CONTINUEing from pre-REPL RESTART-CASE")
           (values))                     ; (no-op, just fall through)
         (quit ()
           :report "Quit SBCL (calling #'QUIT, killing the process)."
           (/show0 "CONTINUEing from pre-REPL RESTART-CASE")
           (values))                     ; (no-op, just fall through)
         (quit ()
           :report "Quit SBCL (calling #'QUIT, killing the process)."
+          :test (lambda (c) (declare (ignore c)) (not script))
           (/show0 "falling through to QUIT from pre-REPL RESTART-CASE")
           (quit :unix-status 1))))
 
           (/show0 "falling through to QUIT from pre-REPL RESTART-CASE")
           (quit :unix-status 1))))
 
index 02898e3..79dbcbf 100644 (file)
@@ -219,6 +219,7 @@ main(int argc, char *argv[], char *envp[])
 
     /* other command line options */
     boolean noinform = 0;
 
     /* other command line options */
     boolean noinform = 0;
+    char *script = 0;
     boolean end_runtime_options = 0;
 
     lispobj initial_function;
     boolean end_runtime_options = 0;
 
     lispobj initial_function;
@@ -235,7 +236,15 @@ main(int argc, char *argv[], char *envp[])
         int argi = 1;
         while (argi < argc) {
             char *arg = argv[argi];
         int argi = 1;
         while (argi < argc) {
             char *arg = argv[argi];
-            if (0 == strcmp(arg, "--noinform")) {
+            if (0 == strcmp(arg, "--script")) {
+                /* This is both a runtime and a toplevel option. As a
+                 * runtime option, it is equivalent to --noinform.
+                 * This exits, and does not increment argi, so that
+                 * TOPLEVEL-INIT sees the option. */
+                noinform = 1;
+                end_runtime_options = 1;
+                break;
+            } else if (0 == strcmp(arg, "--noinform")) {
                 noinform = 1;
                 ++argi;
             } else if (0 == strcmp(arg, "--core")) {
                 noinform = 1;
                 ++argi;
             } else if (0 == strcmp(arg, "--core")) {
index a22d436..f7e055b 100644 (file)
@@ -27,4 +27,12 @@ if [ "`grep -c FOO::BAR $TEST_FILESTEM`" != 1 ] ; then
     echo failed DEFPACKAGE-then-PRINT from --eval form
     exit $EXIT_LOSE
 fi
     echo failed DEFPACKAGE-then-PRINT from --eval form
     exit $EXIT_LOSE
 fi
-exit $EXIT_TEST_WIN
\ No newline at end of file
+
+# --script
+run_sbcl --script script-test.lisp --eval foo \
+  < /dev/null > $TEST_FILESTEM
+if [ "`grep -c :SCRIPT-OK $TEST_FILESTEM`" != 1 ] ; then
+   echo "failed --script test"
+   exit $EXIT_LOSE
+fi
+exit $EXIT_TEST_WIN
index ad842bd..b818344 100644 (file)
@@ -17,4 +17,4 @@
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
 ;;; checkins which aren't released. (And occasionally for internal
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
-"1.0.21.16"
+"1.0.21.17"