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.
+  * 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
index d482992..be2a88a 100644 (file)
@@ -66,12 +66,31 @@ Integration}.
 @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
@@ -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.
 
+@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.
 
@@ -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}.
 
+@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
 
 
@@ -313,71 +345,9 @@ Some examples of what you may consider doing in the initialization
 files follow.
 
 @menu
-* Unix-style Command Line Protocol::
 * 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
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
+.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
@@ -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.
+.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
@@ -341,7 +353,12 @@ Any user initialization file is loaded, unless prohibited.
 \-\-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
index e266366..135028c 100644 (file)
@@ -381,6 +381,30 @@ command-line.")
       (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
@@ -415,6 +439,8 @@ command-line.")
         (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*)))
 
@@ -436,7 +462,14 @@ command-line.")
                         (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")
@@ -516,13 +549,23 @@ command-line.")
               (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 ()
-          :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)."
+          :test (lambda (c) (declare (ignore c)) (not script))
           (/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;
+    char *script = 0;
     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];
-            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")) {
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
-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".)
-"1.0.21.16"
+"1.0.21.17"