#!/bin/sh set -eu escape_name() { printf '%s' "$1" | sed 's|"|\\"|g' | printf '(load "%s")\n' "$(cat -)" } IMPLEMENTATIONS=' abcl allegro clasp clisp clozure cmucl ecl jscl mkcl sbcl ' usage() { cat <<-'EOF' Usage: cl [-e EXP] [-f FILE] [-p] [-M IMAGE] [-I IMPL] [-n] [-v] [FILE...] [-- LISP_OPTIONS] cl -l cl -h EOF } help() { cat <<-'EOF' Options: -e EXP an sexp to be evaluated (can be given more than once) -E EXP an sexp to be executed as a script -f FILE a file to be evaluated (can be given more than once) -p print the value of the last given expression -M IMAGE load the given Lisp image -I IMPL use the given implementation (default: $LISP_CLI_IMPL) -n skip loading the implementation's init file -v verbose mode -l list the known types of implementations -h, --help show this message FILE the file to be executed as a script LISP_OPTIONS options to be forwarrded to the underlying Lisp command Lauch the desired Lisp implementation, properly adapting the given CLI options. When the implementation is not explicited on the command line, and the $LISP_CLI_IMPL environment variable in unset, implementations are searched for alphabetically in $PATH, untill one is found, otherwise an error is emitted. The supported implementations are: EOF for i in $IMPLEMENTATIONS; do printf -- '- %s\n' "$i" done cat <<-'EOF' Examples: Launch CLISP REPL, when $LISP_CLI_IMPL is set to 'clisp': $ cl Lauch SBCL REPL, with the given Lisp image loaded, skipping the loading of the '.sbclrc' file: $ cl -n -Isbcl sbcl.image Run file1.lisp with ABCL: $ cl -Iabcl file1.lisp Process STDIN: $ cat <<-EOS > process.lisp EOS $ cat f1.txt f2.txt | cl -p process.lisp Print a value on all types of implementations: $ for i in `cl -l`; do cl -I$i -pe 'call-arguments-list'; done EOF } for flag in "$@"; do case "$flag" in --) break ;; --help) usage help exit ;; *) ;; esac done SCRIPT="$(mkstemp)" trap 'rm -f "$SCRIPT"' EXIT NO_RC=false LISP_CLI_RC="${XDG_CONFIG_HOME:-$HOME/.config}/lisp-cli/init.lisp" VERBOSE=false IMAGE='' IMPL="${LISP_CLI_IMPL:-}" INTERACTIVE=true while getopts 'e:E:f:pM:I:nvlh' flag; do case "$flag" in e) printf '%s\n' "$OPTARG" >> "$SCRIPT" ;; E) printf '%s\n' "$OPTARG" >> "$SCRIPT" INTERACTIVE=false ;; f) escape_name "$OPTARG" >> "$SCRIPT" ;; M) IMAGE="$OPTARG" ;; I) IMPL="$OPTARG" ;; n) NO_RC=true ;; v) VERBOSE=true ;; l) for i in $IMPLEMENTATIONS; do printf '%s\n' "$i" done exit ;; h) usage help exit ;; *) usage >&2 exit 2 ;; esac done shift $((OPTIND - 2)) if [ "$1" != '--' ]; then shift fi PRESERVE_ARGS=false for f in "$@"; do if [ "$f" = '--' ]; then PRESERVE_ARGS=true shift break fi INTERACTIVE=false escape_name "$f" >> "$SCRIPT" done if [ "$PRESERVE_ARGS" = false ]; then set -- fi MAIN="$(mkstemp)" trap 'rm -f "$MAIN"' EXIT if [ "$NO_RC" = false ] && [ -e "$LISP_CLI_RC" ]; then escape_name "$LISP_CLI_RC" > "$MAIN" fi if [ "$INTERACTIVE" = true ]; then escape_name "$SCRIPT" >> "$MAIN" else cat <<-EOF >> "$MAIN" (handler-case (progn (load "$SCRIPT" :verbose nil :print nil) (uiop:quit 0)) (error (e) (format *error-output* "~&~%error: ~a~%" e) (uiop:quit 1))) EOF fi if [ -z "$IMPL" ]; then for i in $IMPLEMENTATIONS; do if command -v "$i" > /dev/null; then IMPL="$i" break fi done if [ -z "$IMPL" ]; then printf "Could not find any implementation in \$PATH.\n" >&2 exit 2 fi fi case "$IMPL" in abcl) exit 4 ;; allegro) exit 4 ;; clasp) exit 4 ;; clisp) set -- -ansi -i "$MAIN" "$@" if [ -n "$IMAGE" ]; then set -- -M "$IMAGE" "$@" fi if [ "$NO_RC" = true ]; then set -- -norc "$@" fi if [ "$VERBOSE" = false ]; then set -- -q -q "$@" else set -x fi clisp "$@" ;; clozure) set -- -l "$MAIN" "$@" if [ -n "$IMAGE" ]; then set -- -I "$IMAGE" "$@" fi if [ "$NO_RC" = true ]; then set -- -n "$@" fi if [ "$VERBOSE" = false ]; then set -- -Q "$@" else set -x fi ccl "$@" ;; cmucl) exit 4 ;; ecl) exit 4 ;; jscl) exit 4 ;; mkcl) exit 4 ;; sbcl) set -- --load "$MAIN" "$@" if [ -n "$IMAGE" ]; then # The '--core' "C runtime option" must appear before the # other "Lisp options", such as '--load'. set -- --core "$IMAGE" "$@" fi if [ "$NO_RC" = true ]; then set -- --no-sysinit --no-userinit "$@" fi if [ "$VERBOSE" = false ]; then set -- --noinform "$@" else set -x fi sbcl "$@" ;; *) printf 'Unsupported implementation: "%s".\n\n' "$IMPL" >&2 usage >&2 exit 2 ;; esac