Highlights: * notmuch-emacs-mua without arguments runs (notmuch-hello) * runs emacs(1) in case emacsclient(1) fails to connect to running emacs * takes -nw option * handles mailto: * --from option when sending non-mailto: way * -i includes file --body[= ]string inserts string --- notmuch-emacs-mua | 200 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100755 notmuch-emacs-mua diff --git a/notmuch-emacs-mua b/notmuch-emacs-mua new file mode 100755 index 0000000..b1696f7 --- /dev/null +++ b/notmuch-emacs-mua @@ -0,0 +1,200 @@ +#!/usr/bin/env bash +# -*- mode: shell-script; sh-basic-offset: 4; tab-width: 8 -*- +# +# 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 <jani@nikula.org> +# Tomi Ollila <tomi.ollila@iki.fi> +# + +set -eu + +# "expand" '\' to '\\' & '"' to '\"' +escape_optarg () +{ + OPTARG=${OPTARG//\\/\\\\}; OPTARG=${OPTARG//\"/\\\"} +} + +# ditto, in case there is '\n' sequence in the source, otherwise +# "expand" only trailing '\'s to '\\'s +escape_body_optarg () +{ + case ${OPTARG} in + *'\"'*) OPTARG=${OPTARG//\\/\\\\} ;; + *'\') OPTARG=$( printf %s "${OPTARG}" | sed 's/\(\\*\)$/\1\1/' ) + esac + OPTARG=${OPTARG//\"/\\\"} +} + +unset ALTERNATE_EDITOR +exec_mua () +{ + if "${EMACSCLIENT:=emacsclient}" --eval t >/dev/null 2>&1 + then + emacs=$EMACSCLIENT + # close stdout in case no -nw (and no --print) + test -n "$W$X" || exec >/dev/null + else + emacs=${EMACS:-emacs} + fi + ${X:-exec} "$emacs" $W --eval "$*" + exit + ${X:-exec "$emacs" $W --eval} "$*" +} + +X= +W= + +SUBJECT= TO= CC= BCC= BODY= FROM= IB= + +while + # first, handle "long" options which cannot be handled by getopts + case ${1-} in + -nw) + W=-nw + shift + continue + ;; + mailto:*) + oIFS=$IFS; IFS=; OPTARG="$*" IFS=$oIFS + escape_optarg + exec_mua "(progn (require 'notmuch) (browse-url-mail \"$OPTARG\"))" + exit + esac + + 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=*|--from=*) + OPTARG=${opt#--*=} + opt=${opt%%=*} + ;; + # Long options with argument in next arg. + --subject |--to |--cc |--bcc |--body |--from ) + if [[ $# < 2 ]]; then + echo "$0: option '${opt}' requires an argument." >&2 + exit 1 + fi + OPTARG=$2 + OPTIND=$((OPTIND + 1)) + ;; + # Long options without arguments. + --help|--nw|--print) + ;; + *) + 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 + + case ${opt} in + --help|h) + exec man notmuch-emacs-mua + ;; + --from) + escape_optarg + FROM=${OPTARG} + ;; + --subject|s) + escape_optarg + SUBJECT=${SUBJECT:+$SUBJECT }${OPTARG} + ;; + --to) + escape_optarg + TO=${TO:+$TO, }${OPTARG} + ;; + --cc|c) + escape_optarg + CC=${CC:+$CC, }${OPTARG} + ;; + --bcc|b) + escape_optarg + BCC=${BCC:+$BCC, }${OPTARG} + ;; + i) + escape_optarg + if [[ ! -f ${OPTARG} ]]; then + echo "$0: '${OPTARG}': no such file" >&2 + exit 1 + fi + IB=${IB}$'\n'" (insert-file \"${OPTARG}\")" + IB=${IB}$'\n'" (if /= (point) (line-beginning-position) (insert \"\\n\"))" + ;; + --body) + escape_body_optarg + IB=${IB}$'\n'" (insert \"${OPTARG}\\n\")" + ;; + --nw) + W=-nw + ;; + --print) + X=echo + ;; + *) + # 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 + arg=${arg//\\/\\\\}; arg=${arg//\"/\\\"} + TO=${TO:+$TO, }${arg} +done + +NL=$'\n' +ELISP="\ +${CC:+$NL (message-goto-cc) (insert \"$CC\")}\ +${BCC:+$NL (message-goto-bcc) (insert \"$BCC\")}\ +${IB:+$NL (message-goto-body)$IB}" + +if [[ $TO == '' && $SUBJECT == '' && $ELISP == '' ]] +then + exec_mua "(progn (require 'notmuch) (notmuch-hello))" +else + [[ $FROM != '' ]] && OH="(list (cons 'From \"$FROM\"))" || OH=nil + [[ $TO != '' ]] && TO=\"$TO\" || TO=nil + [[ $SUBJECT != '' ]] && SUBJECT=\"$SUBJECT\" || SUBJECT=nil + exec_mua "$NL(progn (require 'notmuch) + (notmuch-mua-mail $TO $SUBJECT + $OH nil (notmuch-mua-get-switch-function))\ +$ELISP$NL (set-buffer-modified-p nil) (message-goto-to))" +fi -- 1.9.0