Re: [DRAFT PATCH] modified notmuch-emacs-mua

Subject: Re: [DRAFT PATCH] modified notmuch-emacs-mua

Date: Wed, 29 Oct 2014 22:14:07 +0200

To: Tomi Ollila, notmuch@notmuchmail.org, david@tethera.net, jrollins@finestructure.net

Cc: tomi.ollila@iki.fi

From: Jani Nikula


On Fri, 11 Jul 2014, Tomi Ollila <tomi.ollila@iki.fi> wrote:
> 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
> +	    ;;

How about generalizing this into letting the user pass arguments after a
"--" separator to emacs(client)? Would that work?

Alternatively, why support -nw which is neither part of the mutt
interface I was originally aiming at emulating nor conforms to notmuch
style? Just go with --nw or the more verbose --no-window-system (which
is also compatible with emacs).

> +	mailto:*)
> +	    oIFS=$IFS; IFS=; OPTARG="$*" IFS=$oIFS
> +	    escape_optarg
> +	    exec_mua "(progn (require 'notmuch) (browse-url-mail \"$OPTARG\"))"
> +	    exit
> +    esac

Why does mailto: need to be handled here? I think you could either make
the usage have a special mailto: case where you only have mailto: at $1,
or you handle mailto: as a positional parameter at the end.

With these, you could drop this one extra case here, and go back to the
more natural "while getopts" argument parsing loop.

> +
> +    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  )

This may be git-ish, but I don't think we should support this in
notmuch.

> +		    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}\")"

This needs the (cd \"${PWD}\") bit because --body given here may be
relative to the script, while emacs likely has a totally different cwd.

> +	    IB=${IB}$'\n'"  (if /= (point) (line-beginning-position) (insert \"\\n\"))"
> +	    ;;
> +	--body)
> +	    escape_body_optarg
> +	    IB=${IB}$'\n'"  (insert \"${OPTARG}\\n\")"

Why should --body and -i be different?

> +	    ;;
> +	--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))"

I think the newlines with $NL hurt readability in the script more than
they help readability in the output... which is generally interpreted by
emacs which doesn't really care about the newlines! ;)

BR,
Jani.


> +fi
> -- 
> 1.9.0

Thread: