#!/usr/bin/env bash # emacs-symbound --- test how symbols are bound in different versions of emacs # Author: Noah Friedman # Created: 2017-06-29 # Public domain # $Id: emacs-symbound,v 1.5 2017/08/02 02:58:06 friedman Exp $ # Commentary: # I have a bunch of emacs versions installed so I can test for portability. # Frequently the most common thing I need to do is just check if a function # or variable actually exists, and how far back. # This command finds all the versions of emacs in your path it can and # prints out a list of binding types each symbol on the command line has. # For example: # # $ emacs-symbound with-current-buffer sit-for # ("26.0.50" # (with-current-buffer macro plist) # (sit-for function)) # # ("25.2.1" # (with-current-buffer macro plist) # (sit-for function)) # # ("24.5.1" # (with-current-buffer macro plist) # (sit-for function)) # # ("23.4.1" # (with-current-buffer macro plist) # (sit-for function)) # # ("22.3.1" # (with-current-buffer macro plist) # (sit-for function)) # # ("21.4.1" # (with-current-buffer macro plist) # (sit-for subr)) # # ("20.7.1" # (with-current-buffer macro plist) # (sit-for subr)) # # ("19.34.1" # (with-current-buffer nil) # (sit-for subr)) # # ("18.59.1" (with-current-buffer nil) (sit-for subr)) # By default this script only looks for commands starting with "emacs". You # can set the environment variable EMACSNAME to xemacs to test that variant. # Code: main() { declare -a emacsen=( yemacs `find_emacsen` ) # This is just for emacsen that don't support the defaults declare -A emacs_args=( [21]='-q' [20]='-q' [19]='-q' [18]='-q -l /dev/stdin -f eval-current-buffer') template=" (let ((outfn '%s) (symlist (nreverse '(%s))) (result nil)) (condition-case nil (mapcar 'require '($FEATURES)) (error nil)) (while symlist (let ((sym (car symlist)) (cells nil)) (setq symlist (cdr symlist)) (if (symbol-plist sym) (setq cells (cons 'plist cells))) (if (fboundp sym) (let* ((symval (symbol-function sym)) (tem (cond ((subrp symval) 'subr) ((and (consp symval) (eq 'macro (car symval))) 'macro) (t 'function)))) (setq cells (cons tem cells)))) (if (boundp sym) (setq cells (cons 'variable cells))) (setq result (cons (cons sym (or cells (list nil))) result)))) (funcall outfn (cons emacs-version result)))" case $# in 1 ) outfn=prin1 ;; * ) outfn=pp ;; esac for emacs in "${emacsen[@]}"; do # bash builtin which assigns result to expr printf -v expr "$template" "$outfn" "$*" vmajor=${emacs#emacs-} args=${emacs_args[${vmajor%%.*}]} $emacs --batch ${args:--Q} --no-site-file --eval "$expr" case $emacs in xemacs* ) : ;; # already adds newline * ) echo ;; esac done echo } find_emacsen() { declare -a dirs=(${PATH//:/ }) declare -A result for dir in "${dirs[@]}"; do for file in "${dir:-.}"/${EMACSNAME-emacs}-[0-9]* ; do test -x "$file" || continue result[${file##*/}]=1 # strip path done done for file in "${!result[@]}"; do echo "$file" done | sort -t - -k2,2rn } # Emacs 18 didn't support -eval function emacs-18.59() { echo "(fset 'pp 'prin1) $expr" | command $emacs -batch $args } main "$@" # eof