#!/bin/sh set -eu uuid() { od -xN20 /dev/urandom | head -n1 | awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}' } tmpname() { echo "${TMPDIR:-/tmp}/cl.tmpfile.$(uuid)" } mkstemp() { name="$(tmpname)" touch "$name" echo "$name" } escape_name() { printf '%s' "$1" | sed 's|"|\\"|g' | printf '(load "%s")\n' "$(cat -)" } IMPLEMENTATIONS=' abcl allegro ccl clasp clisp cmucl ecl jscl mkcl sbcl ' usage() { cat <<-'EOF' Usage: cl [-e EXP] [-f FILE] [-p] [-M IMAGE] [-I IMPL] [-n] [-v] [FILE...] cl -l cl -h EOF } help() { cat <<-'EOF' Options: -e EXP an sexp to be evaluated (can be given more than once) -f FILE FIXME -p print the value of the last given expression -M IMAGE load the given Lisp image -m disable looking for the default image under $XDG_DATA_HOME -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 FIXME 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 NO_RC=false SCRIPT="$(mkstemp)" LISP_CLI_RC="${XDG_CONFIG_HOME:-$HOME/.config}/lisp-cli/init.lisp" VERBOSE=false IMAGE='' SKIP_DEFAULT_IMAGE=false IMPL="${LISP_CLI_IMPL:-}" while getopts 'e:f:pM:mI:nvlh' flag; do case "$flag" in e) printf '%s\n' "$OPTARG" >> "$SCRIPT" ;; f) escape_name "$OPTARG" >> "$SCRIPT" ;; M) IMAGE="$OPTARG" ;; m) SKIP_DEFAULT_IMAGE=true ;; 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 - 1)) INTERACTIVE=true for f in "$@"; do INTERACTIVE=false escape_name "$f" >> "$SCRIPT" done set -- MAIN="$(mkstemp)" 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 DEFAULT_IMAGE="${XDG_DATA_HOME:-$HOME/.local/share}/lisp-cli/$IMPL.image" if [ "$SKIP_DEFAULT_IMAGE" = false ] && [ -z "$IMAGE" ] && [ -e "$DEFAULT_IMAGE" ]; then IMAGE="$DEFAULT_IMAGE" fi case "$IMPL" in abcl) exit 4 ;; allegro) exit 4 ;; ccl) 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 exec clisp "$@" ;; 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 exec sbcl "$@" ;; *) printf 'Unsupported implementation: "%s".\n\n' "$IMPL" >&2 usage >&2 exit 2 ;; esac exit # https://www.cliki.net/cl-launch # buildapp # https://github.com/memleaks/clbuild # https://www.cliki.net/roswell # https://github.com/shinmera/cl-all # https://fare.livejournal.com/184127.html # https://github.com/rolpereira/trivial-dump-core # FIXME: enable readline from CL itself when competing with roswell, add completion FIXME: implement the example from help string