X-Git-Url: http://git.marmaro.de/?p=mmh;a=blobdiff_plain;f=uip%2Fwhatnow.c;h=be046ac79f632fadc3dfb6838abab206988ed4c7;hp=d9954264631ea78a30337635674f3108ebb5b8bd;hb=6e9577f324bef90765a5edc02044eb111ec48072;hpb=6c42153ad9362cc676ea66563bf400d7511b3b68 diff --git a/uip/whatnow.c b/uip/whatnow.c index d995426..be046ac 100644 --- a/uip/whatnow.c +++ b/uip/whatnow.c @@ -1,25 +1,631 @@ - /* - * whatnow.c -- the nmh `WhatNow' shell - * - * $Id$ - * - * This code is Copyright (c) 2002, by the authors of nmh. See the - * COPYRIGHT file in the root directory of the nmh distribution for - * complete copyright information. - */ +** whatnow.c -- the WhatNow shell +** +** This code is Copyright (c) 2002, by the authors of nmh. See the +** COPYRIGHT file in the root directory of the nmh distribution for +** complete copyright information. +*/ #include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_SYS_PARAM_H +# include +#endif + +static struct swit switches[] = { +#define EDITRSW 0 + { "editor editor", 0 }, +#define PRMPTSW 1 + { "prompt string", 0 }, +#define VERSIONSW 2 + { "Version", 0 }, +#define HELPSW 3 + { "help", 0 }, + { NULL, 0 } +}; -/* from whatnowsbr.c */ -int WhatNow (int, char **); +char *version=VERSION; + +/* +** Options at the "whatnow" prompt +*/ +static struct swit aleqs[] = { +#define EDITSW 0 + { "edit [editor [switches]]", 0 }, +#define LISTSW 1 + { "list", 0 }, +#define DISPSW 2 + { "display", 0 }, +#define WHOMSW 3 + { "whom", 0 }, +#define SENDSW 4 + { "send", 0 }, +#define REFILEOPT 5 + { "refile +folder", 0 }, +#define DELETESW 6 + { "delete", 0 }, +#define QUITSW 7 + { "quit", 0 }, +#define CDCMDSW 8 + { "cd [directory]", 0 }, +#define PWDCMDSW 9 + { "pwd", 0 }, +#define LSCMDSW 10 + { "ls", 0 }, +#define ALISTCMDSW 11 + { "alist", 0 }, +#define ATTACHCMDSW 12 + { "attach files", 0 }, +#define DETACHCMDSW 13 + { "detach numbers", 0 }, + { NULL, 0 } +}; + +static char *myprompt = "\nWhat now? "; + +/* +** static prototypes +*/ +static int editfile(char **, char **, char *); +static int sendfile(char **, char *); +static int refile(char **, char *); +static int removefile(char *); +static void writelscmd(char *, int, char **); +static void writesomecmd(char *, int, char *, char *, char **); +static FILE* popen_in_dir(const char *, const char *, const char *); +static int system_in_dir(const char *, const char *); int -main (int argc, char **argv) +main(int argc, char **argv) { -#ifdef LOCALE - setlocale(LC_ALL, ""); -#endif - return WhatNow (argc, argv); + int use = NOUSE; + char *cp; + char *ed = NULL, *drft = NULL; + char buf[BUFSIZ], prompt[BUFSIZ]; + char **argp, **arguments; + struct stat st; + char cwd[MAXPATHLEN + 1]; /* current working directory */ + char file[MAXPATHLEN + 1]; /* file name buffer */ + char shell[MAXPATHLEN + 1]; /* shell response buffer */ + FILE *f; /* read pointer for bgnd proc */ + + setlocale(LC_ALL, ""); + invo_name = mhbasename(argv[0]); + + /* read user profile/context */ + context_read(); + + arguments = getarguments(invo_name, argc, argv, 1); + argp = arguments; + + /* + ** Get the initial current working directory. + */ + + if (!getcwd(cwd, sizeof (cwd))) { + adios(EX_USAGE, "getcwd", "could not get working directory"); + } + + while ((cp = *argp++)) { + if (*cp == '-') { + switch (smatch(++cp, switches)) { + case AMBIGSW: + ambigsw(cp, switches); + exit(EX_USAGE); + case UNKWNSW: + adios(EX_USAGE, NULL, "-%s unknown", cp); + + case HELPSW: + snprintf(buf, sizeof(buf), + "%s [switches] [file]", + invo_name); + print_help(buf, switches, 1); + exit(argc == 2 ? EX_OK : EX_USAGE); + case VERSIONSW: + print_version(invo_name); + exit(argc == 2 ? EX_OK : EX_USAGE); + + case EDITRSW: + if (!(ed = *argp++) || *ed == '-') + adios(EX_USAGE, NULL, "missing argument to %s", + argp[-2]); + continue; + + case PRMPTSW: + if (!(myprompt = *argp++) || *myprompt == '-') + adios(EX_USAGE, NULL, "missing argument to %s", + argp[-2]); + continue; + + } + } + if (drft) + adios(EX_USAGE, NULL, "only one draft at a time!"); + else + drft = cp; + } + + if ((!drft && !(drft = getenv("mhdraft"))) || !*drft) + drft = mh_xstrdup(m_draft(seq_cur)); + + if ((cp = getenv("mhuse")) && *cp) + use = atoi(cp); + + if (!ed && !(ed = getenv("mheditor"))) { + ed = ""; /* Don't initially edit the draft */ + } + + /* start editing the draft, unless editor is the empty string */ + if (*ed) { + if (editfile(&ed, NULL, drft) <0) { + if (!use) { + unlink(drft); + } + adios(EX_SOFTWARE, NULL, "Try again."); + } + } + + snprintf(prompt, sizeof(prompt), myprompt, invo_name); + while ((argp = getans(prompt, aleqs))) { + switch (smatch(*argp, aleqs)) { + case DISPSW: + /* display the msg being replied to or distributed */ + if ((cp = getenv("mhaltmsg")) && *cp) { + execprogl(listproc, listproc, "-file", cp, + (char *)NULL); + } else { + advise(NULL, "no alternate message to display"); + } + break; + + case EDITSW: + /* Call an editor on the draft file */ + if (*++argp) + ed = *argp++; + editfile(&ed, argp, drft); + break; + + case LISTSW: + /* display the draft file */ + execprogl(listproc, listproc, "-file", drft, + (char *)NULL); + break; + + case QUITSW: + /* quit */ + if (stat(drft, &st) != NOTOK) { + advise(NULL, "draft left on %s", drft); + } + exit(EX_OK); + + case DELETESW: + /* Delete draft and exit */ + removefile(drft); + exit(EX_OK); + + case SENDSW: + /* Send draft */ + sendfile(++argp, drft); + break; + + case REFILEOPT: + /* Refile the draft */ + if (refile(++argp, drft) == 0) { + exit(EX_OK); + } + break; + + case CDCMDSW: + /* + ** Change the working directory for attachments + ** + ** Run the directory through the user's shell + ** so that we can take advantage of any syntax + ** that the user is accustomed to. Read back + ** the absolute path. + */ + + if (*(argp+1) == NULL) { + sprintf(buf, "$SHELL -c \"cd;pwd\""); + } else { + writesomecmd(buf, BUFSIZ, "cd", "pwd", argp); + } + if ((f = popen_in_dir(cwd, buf, "r"))) { + fgets(cwd, sizeof (cwd), f); + + if (strchr(cwd, '\n')) + *strchr(cwd, '\n') = '\0'; + + pclose(f); + } else { + advise("popen", "could not get directory"); + } + + break; + + case PWDCMDSW: + /* Print the working directory for attachments */ + printf("%s\n", cwd); + break; + + case LSCMDSW: + /* + ** List files in the current attachment working + ** directory + ** + ** Use the user's shell so that we can take + ** advantage of any syntax that the user is + ** accustomed to. + */ + writelscmd(buf, sizeof(buf), argp); + system_in_dir(cwd, buf); + break; + + case ALISTCMDSW: + /* + ** List attachments on current draft. + */ + if (execprogl("anno", "anno", "-list", "-comp", + attach_hdr, "-number", drft, + (char *)NULL) != 0) { + advise(NULL, "Could not list attachment headers."); + } + break; + + case ATTACHCMDSW: + /* + ** Attach files to current draft. + */ + + if (*(argp+1) == NULL) { + advise(NULL, "attach command requires file argument(s)."); + break; + } + + /* + ** Build a command line that causes the user's + ** shell to list the file name arguments. + ** This handles and wildcard expansion, tilde + ** expansion, etc. + */ + writelscmd(buf, sizeof(buf), argp); + + /* + ** Read back the response from the shell, + ** which contains a number of lines with one + ** file name per line. Remove off the newline. + ** Determine whether we have an absolute or + ** relative path name. Prepend the current + ** working directory to relative path names. + ** Add the attachment annotation to the draft. + */ + if (!(f = popen_in_dir(cwd, buf, "r"))) { + advise("popen", "could not get file from shell"); + break; + } + + while (fgets(shell, sizeof(shell), f)) { + *(strchr(shell, '\n')) = '\0'; + + if (*shell == '/') + sprintf(file, "%s", shell); + else { + sprintf(file, "%s/%s", cwd, shell); + } + if (execprogl("anno", "anno", + "-nodate", "-append", + "-comp", attach_hdr, + "-text", file, + drft, (char *)NULL) != 0) { + advise(NULL, "Could not add attachment header."); + } + } + pclose(f); + break; + + case DETACHCMDSW: + /* + ** Detach files from current draft. + ** + ** Interpret the arguments as + ** attachment numbers. Decrement any remaining + ** argument number that is greater than the one + ** just processed after processing each one so + ** that the numbering stays correct. + */ + for (arguments=argp+1; *arguments; arguments++) { + int n; + + if (**arguments == '\0') { + continue; + } + + if (execprogl("anno", "anno", "-delete", + "-comp", attach_hdr, + "-number", *arguments, drft, + (char *)NULL) != 0) { + advise(NULL, "Could not delete attachment header."); + } + + n = atoi(*arguments); + for (argp=arguments+1; *argp; argp++) { + if (atoi(*argp) > n) { + if (atoi(*argp) == 1) { + *argp = ""; + } else { + sprintf(*argp, "%d", atoi(*argp) - 1); + } + } + } + } + break; + + case WHOMSW: + /* list recipients */ + execprogl("whom", "whom", drft, (char *)NULL); + break; + + default: + /* Unknown command */ + advise(NULL, "say what?"); + break; + } + } + + exit(EX_IOERR); +} + + + +/* +** Build a command line of the form $SHELL -c "cd 'cwd'; cmd argp ... ; +** trailcmd". +*/ +static void +writesomecmd(char *buf, int bufsz, char *cmd, char *trailcmd, char **argp) +{ + char *cp; + /* + ** Note that we do not quote -- the argp from the user + ** is assumed to be quoted as they desire. (We can't treat + ** it as pure literal as that would prevent them using ~, + ** wildcards, etc.) The buffer produced by this function + ** should be given to popen_in_dir() or system_in_dir() so + ** that the current working directory is set correctly. + */ + int ln = snprintf(buf, bufsz, "$SHELL -c \"%s", cmd); + /* + ** NB that some snprintf() return -1 on overflow rather than the + ** new C99 mandated 'number of chars that would have been written' + */ + /* + ** length checks here and inside the loop allow for the + ** trailing ';', trailcmd, '"' and NUL + */ + int trailln = strlen(trailcmd) + 3; + if (ln < 0 || ln + trailln > bufsz) + adios(EX_USAGE, NULL, "arguments too long"); + + cp = buf + ln; + + while (*++argp) { + ln = strlen(*argp); + /* +1 for leading space */ + if (ln + trailln + 1 > bufsz - (cp-buf)) + adios(EX_USAGE, NULL, "arguments too long"); + *cp++ = ' '; + memcpy(cp, *argp, ln+1); + cp += ln; + } + if (*trailcmd) { + *cp++ = ';'; + strcpy(cp, trailcmd); + cp += trailln - 3; + } + *cp++ = '"'; + *cp = 0; +} + +/* +** Build a command line that causes the user's shell to list the file name +** arguments. This handles and wildcard expansion, tilde expansion, etc. +*/ +static void +writelscmd(char *buf, int bufsz, char **argp) +{ + writesomecmd(buf, bufsz, "ls", "", argp); +} + +/* +** Like system(), but run the command in directory dir. +** This assumes the program is single-threaded! +*/ +static int +system_in_dir(const char *dir, const char *cmd) +{ + char olddir[BUFSIZ]; + int r; + if (getcwd(olddir, sizeof(olddir)) == 0) + adios(EX_OSERR, "getcwd", "could not get working directory"); + if (chdir(dir) != 0) + adios(EX_OSERR, "chdir", "could not change working directory"); + r = system(cmd); + if (chdir(olddir) != 0) + adios(EX_OSERR, "chdir", "could not change working directory"); + return r; +} + +/* ditto for popen() */ +static FILE* +popen_in_dir(const char *dir, const char *cmd, const char *type) +{ + char olddir[BUFSIZ]; + FILE *f; + if (getcwd(olddir, sizeof(olddir)) == 0) + adios(EX_OSERR, "getcwd", "could not get working directory"); + if (chdir(dir) != 0) + adios(EX_OSERR, "chdir", "could not change working directory"); + f = popen(cmd, type); + if (chdir(olddir) != 0) + adios(EX_OSERR, "chdir", "could not change working directory"); + return f; +} + + +/* +** EDIT +*/ + +static char *edsave = NULL; /* the editor we used previously */ + + +static int +editfile(char **ed, char **arg, char *file) +{ + int pid, status, vecp; + char *cp, *vec[MAXARGS]; + + if (!*ed || !**ed) { + /* We have no explicit editor. */ + if (edsave) { + /* Use the previous editor ... */ + *ed = edsave; + if (!(cp = mhbasename(*ed))) + cp = *ed; + + /* but prefer one specified via "editor-next" */ + cp = concat(cp, "-next", NULL); + if ((cp = context_find(cp))) + *ed = cp; + } else { + /* set initial editor */ + *ed = defaulteditor; + } + } + + context_save(); + fflush(stdout); + + switch (pid = fork()) { + case NOTOK: + advise("fork", "unable to"); + status = EX_OSERR; + break; + + case OK: + vecp = 0; + vec[vecp++] = mhbasename(*ed); + while (arg && *arg) { + vec[vecp++] = *arg++; + } + vec[vecp++] = file; + vec[vecp] = NULL; + + execvp(*ed, vec); + fprintf(stderr, "%s: unable to exec ", invo_name); + perror(*ed); + _exit(EX_OSERR); + + default: + if ((status = pidwait(pid, NOTOK))) { + if ((status & 0xff00) == 0xff00) { + /* cmd not found or pidwait() failed */ + status = EX_SOFTWARE; + break; + } + if (status & 0x00ff) { + /* terminated by signal */ + advise(NULL, "%s terminated by signal %d", + *ed, status & 0x7f); + } else { + /* failure exit */ + advise(NULL, "%s exited with return code %d", + *ed, (status & 0xff00) >> 8); + } + status = -1; + break; + } + } + + /* remember which editor we used */ + edsave = mh_xstrdup(*ed); + + *ed = NULL; + + return status; +} + + +/* +** SEND +*/ + +static int +sendfile(char **arg, char *file) +{ + int vecp = 0; + char *vec[MAXARGS]; + + context_save(); + fflush(stdout); + + vec[vecp++] = "send"; + while (arg && *arg) { + vec[vecp++] = *arg++; + } + vec[vecp++] = file; + vec[vecp] = NULL; + execvp(*vec, vec); + fprintf(stderr, "%s: unable to exec ", invo_name); + perror("send"); + _exit(EX_OSERR); +} + + +/* +** refile msg into another folder +*/ +static int +refile(char **arg, char *file) +{ + int vecp = 0; + char *vec[MAXARGS]; + + vec[vecp++] = "refile"; + vec[vecp++] = "-nolink"; /* override bad .mh_profile defaults */ + vec[vecp++] = "-file"; + vec[vecp++] = file; + + while (arg && *arg) { + vec[vecp++] = *arg++; + } + vec[vecp] = NULL; + + context_save(); + fflush(stdout); + + return execprog(*vec, vec); +} + + +/* +** Remove the draft file +*/ + +static int +removefile(char *drft) +{ + if (unlink(drft) == NOTOK) + adios(EX_IOERR, drft, "unable to unlink"); + + return OK; }