#!/usr/bin/env bash # # notmuch-emacs-mua - start composing a mail on the command line # # Copyright © 2014 Jani Nikula # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/ . # # Authors: Jani Nikula # set -eu # escape: "expand" '\' as '\\' and '"' as '\"' # calling convention: escape -v var "$arg" (like in bash printf). escape () { local __escape_arg__=${3//\\/\\\\} printf -v $2 '%s' "${__escape_arg__//\"/\\\"}" } EMACS=${EMACS-emacs} EMACSCLIENT=${EMACSCLIENT-emacsclient} PRINT_ONLY= NO_WINDOW= USE_EMACSCLIENT= AUTO_DAEMON= CREATE_FRAME= # The crux of it all: construct an elisp progn and eval it. ELISP="(prog1 'done (require 'notmuch) (notmuch-mua-new-mail)" # Short options compatible with mutt(1). while getopts :s:c:b:i:h opt; do # Handle errors and long options. case "${opt}" in :) echo "$0: short option -${OPTARG} requires an argument." >&2 exit 1 ;; \?) opt=$1 if [ "${OPTARG}" != "-" ]; then echo "$0: unknown short option -${OPTARG}." >&2 exit 1 fi case "${opt}" in # Long options with arguments. --subject=*|--to=*|--cc=*|--bcc=*|--body=*) OPTARG=${opt#--*=} opt=${opt%%=*} ;; # Long options without arguments. --help|--print|--no-window-system|--client|--auto-daemon|--create-frame) ;; *) echo "$0: unknown long option ${opt}, or argument mismatch." >&2 exit 1 ;; esac # getopts does not do this for what it considers errors. OPTIND=$((OPTIND + 1)) ;; esac escape -v OPTARG "${OPTARG-none}" case "${opt}" in --help|h) exec man notmuch-emacs-mua ;; --subject|s) ELISP="${ELISP} (message-goto-subject) (insert \"${OPTARG}\")" ;; --to) ELISP="${ELISP} (message-goto-to) (insert \"${OPTARG}, \")" ;; --cc|c) ELISP="${ELISP} (message-goto-cc) (insert \"${OPTARG}, \")" ;; --bcc|b) ELISP="${ELISP} (message-goto-bcc) (insert \"${OPTARG}, \")" ;; --body|i) ELISP="${ELISP} (message-goto-body) (cd \"${PWD}\") (insert-file \"${OPTARG}\")" ;; --print) PRINT_ONLY=1 ;; --no-window-system) NO_WINDOW="-nw" ;; --client) USE_EMACSCLIENT="yes" ;; --auto-daemon) AUTO_DAEMON="--alternate-editor=" CREATE_FRAME="-c" ;; --create-frame) CREATE_FRAME="-c" ;; *) # We should never end up here. echo "$0: internal error (option ${opt})." >&2 exit 1 ;; esac shift $((OPTIND - 1)) OPTIND=1 done # Positional parameters. for arg; do escape -v arg "${arg}" ELISP="${ELISP} (message-goto-to) (insert \"${arg}, \")" done # Kill the terminal/frame if we're creating one. if [ -z "$USE_EMACSCLIENT" -o -n "$CREATE_FRAME" -o -n "$NO_WINDOW" ]; then ELISP="${ELISP} (setq message-exit-actions (list #'save-buffers-kill-terminal))" fi # End progn. ELISP="${ELISP})" if [ -n "$PRINT_ONLY" ]; then echo ${ELISP} exit 0 fi if [ -n "$USE_EMACSCLIENT" ]; then # Evaluate the progn. exec ${EMACSCLIENT} ${NO_WINDOW} ${CREATE_FRAME} ${AUTO_DAEMON} --eval "${ELISP}" else exec ${EMACS} ${NO_WINDOW} --eval "${ELISP}" fi