On Wed, Oct 29 2014, Jani Nikula <jani@nikula.org> wrote: Thanks for quick response. There are only one (and half) thing that needs to be resolved before first inclusion, the desire for more versatile options can be discussed later... > 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? That would technically work and be very easy to do. I don't know whether this is good option -- and such can be added later if decided. > 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). The original mutt interface is there -- as this is notmuch-*emacs*-mua this could also have the original emacs -nw option (--nw came later, and is supported by the loop). But, what is the option is not important, as long as there is at least one emacs-compatible option to have this feature (--nw). (for now, Let's see whether I get irritated that notmuch-emacs-mua -nw does not work -- as I still have ESC g bound to goto-line as I cannot get used to ESC g g ;) >> + 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. To handle this here is probably a slip as i started using notmuch-emacs-mua as a drop-in replacement from notmuch-emacs-mailto (where I added possibility to do -nw). I agree that it is better to just check first arg for mailto: prefix and if matches, work accordingly. > With these, you could drop this one extra case here, and go back to the > more natural "while getopts" argument parsing loop. I agree to convert to "while getopts" loop which *apparently* feels more natural to most of the developers ;D >> + >> + 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. This is also emacs-ish, tar-ish, whatnot-ish (with notable exception of google-chrome). But we can go with only supporting --longarg=val option now, and bikeshed that more if anyone wants to -- From some reason when command starts with 'notmuch' I tend to write = to separate long option with arg (and have always done so with this script when not testing), although SPC is easier to press that shift-0 (which produces '=' on finnish keyboard ;) >> + 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. Yes, the drop of that was accidental and not intentional. We could also escape $PWD too, in case user does: mkdir '.")(shell-command "/bin/rm -rf ~'; cd ... although that is probably not accidental... well have to think for cases user's PWD contains just one '"' for some reason.. >> + 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? Now, this is the one that is important to get decided now! The -i is different from other options as it loads a file to be added to the mail buffer, when all other options put the option value to the buffer. an analogous option to that would IMO be --insert (or --include). Also, I desire an option to write mail content lines from command line option directly. To me --body (which is not mutt(1) option) to work as adding the variable to mail buffer is more consistent with the other options -- and we could add this --insert as long option to -i. Anyway, whatever the option to be able to insert body content from command line is desired -- if there is any better choice :) I also think that s/escape_body_optarg/escape_optarg/ (and dropping escape_body_optarg) should be done. escape_body_optarg would have left e.g. "\n"s there and let emacs add newlines there. But this is some hidden emacs-specific magic and might not be compatible with e.g. notmuch-vim-mua (provided that someone(tm) will write such an utility). >> + ;; >> + --nw) >> + W=-nw I could change this to 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! ;) This is half thing left to be decided now. The $NL:s make --print -output (very!) pretty. I can make $NL be empty in case --print is not given -- and and another variable to contain 0/2 spaces depending on --print value to make emacs and human viewer happy... >;) I personally don't think those 4 $NL:s make the readability of the script much worse -- but I can turn those to ${NL}:s if that help. It seems your original code uses those more and I have not -- not because of a style issue but because I am just so accustomed to do so... That said I take more care of those in future versions... > > BR, > Jani. Tomi > > >> +fi >> -- >> 1.9.0