From a971938946dd61a3468263908988363dbdd87a3d Mon Sep 17 00:00:00 2001 From: Luke Shumaker Date: Thu, 27 Nov 2014 20:06:16 -0500 Subject: initial commit of my emacs scripts, after some cleanup --- .gitignore | 6 +++ Makefile | 36 ++++++++++++++ common.sh | 86 +++++++++++++++++++++++++++++++++ ediff.sh.in | 62 ++++++++++++++++++++++++ emacsmail.sh.in | 57 ++++++++++++++++++++++ emacsterm.sh.in | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 392 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 common.sh create mode 100644 ediff.sh.in create mode 100644 emacsmail.sh.in create mode 100644 emacsterm.sh.in diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..58eb704 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +ediff +emacsmail +emacsterm + +*.sh +!common.sh diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bbb19d1 --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +DESTDIR= +prefix=/usr/local +exec_prefix=$(prefix) +bindir=$(exec_prefix)/bin + +bash=/bin/bash + +EDIT = { m4 -P | sed 's|@bash@|$(bash)|g'; } +INSTALL_PROGRAM = install -Dm755 +RM = rm -f + + + +targets = ediff emacsmail emacsterm + +all: PHONY $(targets) +install: PHONY $(addprefix $(DESTDIR)$(bindir)/,$(targets)) +uninstall: PHONY + $(RM) -- $(addprefix $(DESTDIR)$(bindir)/,$(targets)) +clean: PHONY + $(RM) -- $(addsuffix .sh,$(targets)) +distclean: PHONY clean + $(RM) -- $(targets) + + + +%.sh: %.sh.in common.sh + $(EDIT) < $< > $@ + +$(DESTDIR)$(bindir)/%: % + $(INSTALL_PROGRAM) $< $@ + + + +.PHONY: PHONY +.DELETE_ON_ERROR: diff --git a/common.sh b/common.sh new file mode 100644 index 0000000..93f8851 --- /dev/null +++ b/common.sh @@ -0,0 +1,86 @@ +if type gettext &>/dev/null; then + _() { gettext "$@"; } +else + _() { echo "$@"; } +fi + +print() { + printf -- "$(_ "$1")\n" "${@:2}" +} + +flag() { + if [[ -z "$_flag_indent" ]]; then + local str=$(emacsclient --help | + sed -rn '/^-.*\s\s/{ s/(\s\s)\S.*/\1/p; q; }' | + expand) + declare -gi _flag_indent=${#str} + fi + printf -- "%- ${_flag_indent}s%s\n" "$1" "$(print "${@:2}")" +} + +error() { + printf -- "%s: %s\n" "$0" "$(print "$@")" >&2 +} + +emacs_quote() { + declare -a args=("$@") + args=("${args[@]//\\/\\\\}") # \ -> \\ + args=("${args[@]//\"/\\\"}") # " -> \" + printf -- '"%s" ' "${args[@]}" # wrap them in quotes, return +} + +bash_quote() { + printf -- '%q ' "$@" +} + +version() { + print '%s (Emacs utils) %s, %s' \ + "${0##*/}" 0.9 "$(emacsclient --version)" +} + +# Sets the global variables: +# - emacs_getopt_o +# - emacs_getopt_l +# - emacs_getopt_1 +# - emacs_getopt_2 +emacs_getopt_init() { + declare ifs="$IFS" + + declare -a a_flags + IFS=$'\n' a_flags=($( + LC_ALL=C emacsclient --help | + grep ^- | + sed -e 's/\s\s.*//' -e 's/, /\n/g' | + sed -e 's/[ =].*/:/' -e 's/^-*//' | + grep -vEx 'e|eval')) + + declare -a a_flags_o a_flags_l a_flags_1 a_flags_2 + IFS=$'\n' a_flags_o=($(printf '%s\n' "${a_flags[@]}"|grep -v '^.[^:]')) + IFS=$'\n' a_flags_l=($(printf '%s\n' "${a_flags[@]}"|grep '^.[^:]')) + IFS=$'\n' a_flags_1=($(printf '%s\n' "${a_flags[@]}"|sed -rn -e 's/^(.)$/-\1/p' -e 's/^([^-].*[^:])$/--\1/p')) + IFS=$'\n' a_flags_2=($(printf '%s\n' "${a_flags[@]}"|sed -rn -e 's/^(.):$/-\1/p' -e 's/^([^-].*):$/--\1/p')) + + printf -v emacs_getopt_o -- '%s' "${a_flags_o[@]}" + IFS=',' emacs_getopt_l=${a_flags_l[*]} + IFS='|' emacs_getopt_2="^(${a_flags_2[*]})\$" + + IFS=$ifs +} + +# Sets the global variable: +# - args +emacs_getopt() { + declare o="$1" + declare l="$2" + shift 2 + emacs_getopt_init + args="$(getopt -a \ + -n "$0" \ + -o "${emacs_getopt_o}${o}" \ + -l "${emacs_getopt_l}${l:+,$l}" \ + -- "$@")" +} + +emacs_usage() { + emacsclient --help | grep -E '^(\s|-)' +} diff --git a/ediff.sh.in b/ediff.sh.in new file mode 100644 index 0000000..5ecba70 --- /dev/null +++ b/ediff.sh.in @@ -0,0 +1,62 @@ +#!@bash@ + +m4_include(common.sh) + +usage() { + print 'Usage: %q [OPTIONS] FILE_A FILE_B' "$0" + print 'Usage: %q -3 [OPTIONS] FILE_A FILE_B FILE_C' "$0" + print "Use Emacs' ediff-mode to compare two files" + echo + print 'The following OPTIONS are accepted:' + emacs_usage + flag '-3' 'Do a 3-way diff instead of 2-way' + flag '-r, --recursive' 'Diff directories recursively' +} + +main() { + declare -a flags=() + declare -a files=() + declare -i cnt=2 + declare error=false + declare mode=normal + declare cmd=ediff + + declare args= + emacs_getopt 3r recursive "$@" || error=true + eval set -- "$args" + while true; do + case "$1" in + -V|--version) shift; mode=version;; + -H|--help) shift; mode=usage + -r|--recursive) shift; cmd=edirs;; + -3) shift; cnt=3;; + --) shift; break;; + *) + if [[ $1 =~ $emacs_getopt_2 ]]; then + flags+=("$1" "$2"); shift 2 + else + flags+=("$1"); shift 1 + fi + ;; + esac + done + files=("$@") + if [[ $mode == normal ]]; then + [[ ${#files[@]} = ${cnt} ]] || error=true + fi + + if $error; then + usage >&2 + return 1 + fi + case "$mode" in + usage) usage; return 0;; + version) version; return 0;; + esac + + emacsclient "${flags[@]}" --eval \ + '(select-frame (make-frame))' \ + "(${cmd}${cnt#2} $(emacs_quote "${files[@]}"))" +} + +main "$@" diff --git a/emacsmail.sh.in b/emacsmail.sh.in new file mode 100644 index 0000000..e5eef50 --- /dev/null +++ b/emacsmail.sh.in @@ -0,0 +1,57 @@ +#!@bash@ + +m4_include(common.sh) + +usage() { + print 'Usage: %q [OPTIONS] MAILTO_URL' "$0" + print 'Use Emacs to open RFC 2368 "mailto:" URLs' + echo + print 'Yes, I know that RFC 2368 is obsoleted by RFC 6068.' + print 'emacs-devel@gnu.org would *love* a patch to browse-url.el' + echo + print 'The following OPTIONS are accepted:' + emacs_usage +} + +main() { + declare -a flags=() + declare error=false + declare mode=normal + + declare args= + emacs_getopt '' '' "$@" || error=true + eval set -- "$args" + while true; do + case "$1" in + -V|--version) shift; mode=version;; + -H|--help) shift; mode=usage;; + --) shift; break;; + *) + if [[ $1 =~ $emacs_getopt_2 ]]; then + flags+=("$1" "$2"); shift 2 + else + flags+=("$1"); shift 1 + fi + ;; + esac + done + urls=("$@") + if [[ $mode == normal ]]; then + [[ ${#urls[@]} = 1 ]] || error=true + fi + + if $error; then + usage >&2 + return 1 + fi + case "$mode" in + usage) usage; return 0;; + version) version; return 0;; + esac + + emacsclient "${flags[@]}" --eval \ + '(select-frame (make-frame))' \ + "(browse-url-mail $(emacs_quote "${urls[@]}"))" +} + +main "$@" diff --git a/emacsterm.sh.in b/emacsterm.sh.in new file mode 100644 index 0000000..e199f95 --- /dev/null +++ b/emacsterm.sh.in @@ -0,0 +1,145 @@ +#!@bash@ + +m4_include(common.sh) + +usage() { + print "Usage: %q [OPTIONS] [SHELL]" "$0" + echo + print 'This utility does NOT support option combining.' + echo + print 'The following OPTIONS are accepted:' + emacs_usage + flag '-r' 'Use rxvt-style parsing of CMD for -e (~execve)' + flag '-x' 'Use xterm-style parsing of CMD for -e (~system)' + flag "-e $(_ CMD)" 'Execute CMD instead of ${SHELL:-/bin/sh}' +} + + +main() { + declare mimic + declare cmd + declare -a flags + parse "$@" + + emacsclient "${flags[@]}" --eval \ + '(select-frame (make-frame))' \ + "(term $(emacs_quote "${cmd}"))" \ + '(set-window-dedicated-p (selected-window) t)' +} + +# Sets the 'global' variables: +# - mode +# - mimic +# - cmd +# - flags (array) +parse() { + mimic=rxvt + cmd= + flags=() + + local mode=normal + local error=false + emacs_getopt_init + + while [[ $# -gt 0 ]]; do + case "$1" in + -V|--version) shift; mode=version;; + -H|--help) shift; mode=usage;; + -r) shift; mimic=rxvt;; + -x) shift; mimic=xterm;; + -e) shift; parse_cmd "$@"; set --;; + --) shift; parse_shell "$@"; set --;; + -*) parse_emacs_getopt "$@"; shift $?;; + *) parse_shell "$@"; set --;; + esac + done + + if $error; then + usage >&2 + exit 1 + fi + case "$mode" in + usage) usage; exit 0;; + version) version; exit 0;; + esac + + if [[ -z "$cmd" ]]; then + # This matches rxvt's behavior. + # This is simpler than xterm's behavior. + cmd="${cmd:-${SHELL:-/bin/sh}}" + fi +} + +# Return status is the number of things to shift +parse_emacs_getopt() { + declare -a flag + if eval "flag=($(getopt -a \ + -n "$0" \ + -o "${emacs_getopt_o//:/}" \ + -l "${emacs_getopt_l//:/}" \ + -- "$1" 2>/dev/null))" && + [[ ${#flag[@]} == 2 ]] + then + # getopt worked + if [[ $flag =~ $emacs_getopt_2 ]]; then + # flag takes an argument + if [[ $# -gt 1 ]]; then + flags+=("$flag" "$2") + return 2 + else + # Missing the required 2nd part + getopt -a \ + -n "$0" \ + -o "$emacs_getopt_o" \ + -l "$emacs_getopt_l" \ + -- "$flag" >/dev/null + error=true + return 1 + fi + else + # pass the flag along to emacs + flags+=("$flag") + return 1 + fi + else + # getopt either didn't work, or did combined flags + # Have getopt display its own error message: + getopt -a -n "$0" -o '' -l '' -- "$1" >/dev/null + error=true + return 1 + fi +} + +parse_cmd() { + cmd="$(mktemp -t -- "${0##*/}.XXXXXXXXXX")" + trap "$(printf 'rm -f -- %q' "$cmd")" EXIT + { + echo '#!/bin/sh' + case "$mimic" in + xterm) + if [[ $# == 1 ]]; then + printf '%s' "$1" + else + printf -- '%q ' exec "$@" + fi + ;; + rxvt) + printf -- '%q ' exec "$@" + ;; + esac + echo + } > "$cmd" + chmod 755 "$cmd" +} + +parse_shell() { + if [[ $# == 1 ]]; then + cmd=$1 + else + shift + error "extra arguments: %s" "$*" + error=true + fi +} + +main "$@" -- cgit v1.2.3