Fix uip/whom.c for C89 compatibility
[mmh] / uip / whom.c
index b68e3ae..ef0b684 100644 (file)
 /*
- * whom.c -- report to whom a message would be sent
- *
- * 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.
- */
+** whom.c -- list recipients of message
+**
+** This code is Copyright (c) 2012, by the authors of mmh.  See the
+** COPYRIGHT file in the root directory of the nmh distribution for
+** complete copyright information.
+*/
 
 #include <h/mh.h>
-#include <h/signals.h>
-#include <signal.h>
+#include <h/addrsbr.h>
+#include <h/fmt_scan.h>
+#include <h/utils.h>
+#include <locale.h>
+#include <sysexits.h>
 
 static struct swit switches[] = {
-#define ALIASW  0
-       { "alias aliasfile", 0 },
-#define CHKSW  1
-       { "check", 0 },
-#define NOCHKSW  2
-       { "nocheck", 0 },
-#define DRAFTSW  3
-       { "draft", 0 },
-#define DFOLDSW  4
-       { "draftfolder +folder", 6 },
-#define DMSGSW  5
-       { "draftmessage msg", 6 },
-#define NDFLDSW  6
-       { "nodraftfolder", 0 },
-#define VERSIONSW  7
-       { "version", 0 },
-#define HELPSW  8
+#define VERSIONSW 0
+       { "Version", 0 },
+#define HELPSW 1
        { "help", 0 },
-#define CLIESW  9
-       { "client host", -6 },
-#define SERVSW  10
-       { "server host", -6 },
-#define SNOOPSW  11
-       { "snoop", -5 },
-#define PORTSW  15
-       { "port server port name/number", 4 },
+#define TOCCSW 2
+       { "tocc", 0 },
+#define NTOCCSW 3
+       { "notocc", 2 },
+#define DCCSW 4
+       { "dcc", 0 },
+#define NDCCSW 5
+       { "nodcc", 2 },
+#define BCCSW 6
+       { "bcc", 0 },
+#define NBCCSW 7
+       { "nobcc", 2 },
+#define ALISW 8
+       { "alias", 0 },
+#define NALISW 9
+       { "noalias", 2 },
        { NULL, 0 }
 };
 
+char *version=VERSION;
+
+#define NFILES 32
+
+#define HRESENT (1<<1)
+#define HTO (1<<2)
+#define HCC (1<<3)
+#define HDCC (1<<4)
+#define HBCC (1<<5)
+
+struct mailname head = {0};
+struct mailname *mp = &head;
+
+static char *cmd;
+
+static int resent = 0;  /* consider normal or resent headers */
+static int toccsw = 1;  /* list sighted recipients */
+static int dccsw = 1;  /* list hidden dcc recipients */
+static int bccsw = 1;  /* list hidden bcc recipients */
+static int alisw = 1;  /* expand aliases on rcpt addrs */
+
+static char *dccsep = "\t==DCC==";
+static char *bccsep = "\t==BCC==";
+
+
+/*
+** static prototypes
+*/
+static int process(char *);
+static void proc_hdr(char *, char *);
+static int print(void);
+static int printdcc(void);
+static int printbcc(void);
+static void printone(struct mailname *);
+
 
 int
-main (int argc, char **argv)
+main(int argc, char **argv)
 {
-       pid_t child_id;
-       int i, status, isdf = 0;
-       int distsw = 0, vecp = 0;
-       char *cp, *dfolder = NULL, *dmsg = NULL;
-       char *msg = NULL, **ap, **argp, backup[BUFSIZ];
-       char buf[BUFSIZ], **arguments, *vec[MAXARGS];
-
-#ifdef LOCALE
+       int naddrs=0, n;
+       char *cp;
+       char buf[BUFSIZ], **argp;
+       char **arguments;
+       char *folder = NULL;
+       FILE *in;
+       struct msgs_array msgs = {0};
+       struct msgs_array files = {0};
+       size_t filep;
+
        setlocale(LC_ALL, "");
-#endif
-       invo_name = r1bindex (argv[0], '/');
+       invo_name = mhbasename(argv[0]);
 
        /* read user profile/context */
        context_read();
 
-       arguments = getarguments (invo_name, argc, argv, 1);
+       arguments = getarguments(invo_name, argc, argv, 1);
        argp = arguments;
 
-       vec[vecp++] = invo_name;
-       vec[vecp++] = "-whom";
-       vec[vecp++] = "-library";
-       vec[vecp++] = getcpy (m_maildir (""));
-
        while ((cp = *argp++)) {
                if (*cp == '-') {
-                       switch (smatch (++cp, switches)) {
-                               case AMBIGSW:
-                                       ambigsw (cp, switches);
-                                       done (1);
-                               case UNKWNSW:
-                                       adios (NULL, "-%s unknown", cp);
-
-                               case HELPSW:
-                                       snprintf (buf, sizeof(buf), "%s [switches] [file]", invo_name);
-                                       print_help (buf, switches, 1);
-                                       done (1);
-                               case VERSIONSW:
-                                       print_version(invo_name);
-                                       done (1);
-
-                               case CHKSW:
-                               case NOCHKSW:
-                               case SNOOPSW:
-                                       vec[vecp++] = --cp;
-                                       continue;
-
-                               case DRAFTSW:
-                                       msg = draft;
-                                       continue;
-
-                               case DFOLDSW:
-                                       if (dfolder)
-                                               adios (NULL, "only one draft folder at a time!");
-                                       if (!(cp = *argp++) || *cp == '-')
-                                               adios (NULL, "missing argument to %s", argp[-2]);
-                                       dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
-                                                       *cp != '@' ? TFOLDER : TSUBCWF);
-                                       continue;
-                               case DMSGSW:
-                                       if (dmsg)
-                                               adios (NULL, "only one draft message at a time!");
-                                       if (!(dmsg = *argp++) || *dmsg == '-')
-                                               adios (NULL, "missing argument to %s", argp[-2]);
-                                       continue;
-                               case NDFLDSW:
-                                       dfolder = NULL;
-                                       isdf = NOTOK;
-                                       continue;
-
-                               case ALIASW:
-                               case CLIESW:
-                               case SERVSW:
-                               case PORTSW:
-                                       vec[vecp++] = --cp;
-                                       if (!(cp = *argp++) || *cp == '-')
-                                               adios (NULL, "missing argument to %s", argp[-2]);
-                                       vec[vecp++] = cp;
-                                       continue;
+                       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 TOCCSW:
+                               toccsw = 1;
+                               continue;
+                       case NTOCCSW:
+                               toccsw = 0;
+                               continue;
+
+                       case DCCSW:
+                               dccsw = 1;
+                               continue;
+                       case NDCCSW:
+                               dccsw = 0;
+                               continue;
+
+                       case BCCSW:
+                               bccsw = 1;
+                               continue;
+                       case NBCCSW:
+                               bccsw = 0;
+                               continue;
+
+                       case ALISW:
+                               alisw = 1;
+                               continue;
+                       case NALISW:
+                               alisw = 0;
+                               continue;
                        }
                }
-               if (msg)
-                       adios (NULL, "only one draft at a time!");
-               else
-                       vec[vecp++] = msg = cp;
+               if (*cp == '+' || *cp == '@') {
+                       if (folder) {
+                               adios(EX_USAGE, NULL, "only one folder at a time!");
+                       } else {
+                               folder = mh_xstrdup(expandfol(cp));
+                       }
+               } else {
+                       app_msgarg(&msgs, cp);
+               }
+       }
+       if (!msgs.size) {
+               adios(EX_USAGE, NULL, "usage: %s [switches] file ...", invo_name);
+       }
+       if (!toccsw && !dccsw && !bccsw) {
+               adios(EX_USAGE, NULL, "use at least one of: -tocc -dcc -bcc");
+       }
+       if (parse_msgs(&msgs, folder, &files) < 0) {
+               exit(EX_IOERR);
+       }
+       for (filep = 0; filep < files.size; filep++) {
+               process(files.msgs[filep]);
        }
 
-       /* allow Aliasfile: profile entry */
-       if ((cp = context_find ("Aliasfile"))) {
-               char *dp = NULL;
-
-               for (ap = brkstring(dp = getcpy(cp), " ", "\n"); ap && *ap; ap++) {
-                       vec[vecp++] = "-alias";
-                       vec[vecp++] = *ap;
+       cmd = add("ali -list", NULL);
+       if ((n=print()) && alisw) {
+               if (!(in = popen(cmd, "r"))) {
+                       adios(EX_IOERR, "popen", "unable to");
+               }
+               while (fgets(buf, sizeof buf, in)) {
+                       fputs(buf, stdout);
                }
+               pclose(in);
        }
+       mh_free0(&cmd);
+       naddrs += n;
 
-       if (msg == NULL) {
-#ifdef WHATNOW
-               if (dfolder || (cp = getenv ("mhdraft")) == NULL || *cp == '\0')
-#endif /* WHATNOW */
-                       cp  = getcpy (m_draft (dfolder, dmsg, 1, &isdf));
-               msg = vec[vecp++] = cp;
+       cmd = add("ali -list", NULL);
+       if ((n=printdcc()) && alisw) {
+               if (!(in = popen(cmd, "r"))) {
+                       adios(EX_IOERR, "popen", "unable to");
+               }
+               while (fgets(buf, sizeof buf, in)) {
+                       fputs(buf, stdout);
+               }
+               pclose(in);
        }
-       if ((cp = getenv ("mhdist"))
-                       && *cp
-                       && (distsw = atoi (cp))
-                       && (cp = getenv ("mhaltmsg"))
-                       && *cp) {
-               if (distout (msg, cp, backup) == NOTOK)
-                       done (1);
-               vec[vecp++] = "-dist";
-               distsw++;
+       mh_free0(&cmd);
+       naddrs += n;
+
+       cmd = add("ali -list", NULL);
+       if ((n=printbcc()) && alisw) {
+               if (!(in = popen(cmd, "r"))) {
+                       adios(EX_IOERR, "popen", "unable to");
+               }
+               while (fgets(buf, sizeof buf, in)) {
+                       fputs(buf, stdout);
+               }
+               pclose(in);
        }
-       vec[vecp] = NULL;
+       mh_free0(&cmd);
+       naddrs += n;
+
+       return naddrs ? 0 : 1;
+}
+
+
+static int
+process(char *file)
+{
+       enum state state;
+       struct field f = {{0}};
+       int compnum;
+       FILE *in;
 
-       closefds (3);
 
-       if (distsw) {
-               for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
-                       sleep (5);
+       if ((in = fopen(file, "r")) == NULL) {
+               adios(EX_IOERR, file, "unable to open");
        }
 
-       switch (distsw ? child_id : OK) {
-               case NOTOK:
-                               advise (NULL, "unable to fork, so checking directly...");
-               case OK:
-                       execvp (postproc, vec);
-                       fprintf (stderr, "unable to exec ");
-                       perror (postproc);
-                       _exit (-1);
+       for (compnum=1, state=FLD2;; compnum++) {
+               switch (state = m_getfld2(state, &f, in)) {
+               case LENERR2:
+                       state = FLD2;
+                       /* FALL */
+               case FLD2:
+                       proc_hdr(f.name, f.value);
+                       continue;
+
+               case BODY2:
+               case FILEEOF2:
+                       break;
+
+               case FMTERR2:
+                       advise(NULL, "message format error in component #%d", compnum);
+                       continue;
+
+               case IOERR2:
+                       adios(EX_DATAERR, NULL, "message format error in component #%d",
+                                       compnum);
 
                default:
-                       SIGNAL (SIGHUP, SIG_IGN);
-                       SIGNAL (SIGINT, SIG_IGN);
-                       SIGNAL (SIGQUIT, SIG_IGN);
-                       SIGNAL (SIGTERM, SIG_IGN);
+                       adios(EX_SOFTWARE, NULL, "getfld() returned %d", state);
+               }
+               break;
+       }
+       fclose(in);
+       return 0;
+}
+
 
-                       status = pidwait(child_id, OK);
+/*
+** Check if the header contains addresses we're interested in.
+** If so, extract the addresses and add them to the global list.
+*/
+static void
+proc_hdr(char *name, char *val)
+{
+       char *cp;
+       int type = 0;
 
-                       unlink (msg);
-                       if (rename (backup, msg) == NOTOK)
-                               adios (msg, "unable to rename %s to", backup);
-                       done (status);
+       while (*val==' ' || *val=='\t' || *val=='\n') {
+               val++;
        }
+       if (strncasecmp(name, "resent-", 7)==0) {
+               resent = HRESENT;
+       }
+       if (mh_strcasecmp(name, "to")==0) {
+               type = HTO;
+       } else if (mh_strcasecmp(name, "cc")==0) {
+               type = HCC;
+       } else if (mh_strcasecmp(name, "dcc")==0) {
+               type = HDCC;
+       } else if (mh_strcasecmp(name, "bcc")==0) {
+               type = HBCC;
+       } else if (mh_strcasecmp(name, "resent-to")==0) {
+               type = (HRESENT | HTO);
+       } else if (mh_strcasecmp(name, "resent-cc")==0) {
+               type = (HRESENT | HCC);
+       } else if (mh_strcasecmp(name, "resent-dcc")==0) {
+               type = (HRESENT | HDCC);
+       } else if (mh_strcasecmp(name, "resent-bcc")==0) {
+               type = (HRESENT | HBCC);
+       }
+       /* ignore non-recpient headers */
+       if (!type) {
+               return;
+       }
+       /* ignore recipient headers we are not interested in */ 
+       if ((type&HTO || type&HCC) && !toccsw) {
+               return;
+       }
+       if ((type&HDCC) && !dccsw) {
+               return;
+       }
+       if ((type&HBCC) && !bccsw) {
+               return;
+       }
+
+       while ((cp = getname(val))) {
+               if (!(mp->m_next = getm(cp, NULL, 0, AD_NAME, NULL))) {
+                       adios(EX_DATAERR, NULL, "illegal address: %s", cp);
+               }
+               mp = mp->m_next;
+               if (mp->m_type == BADHOST) {
+                       admonish(NULL, "bad address `%s'", mp->m_text);
+                       continue;
+               }
+               mp->m_type = type;
+       }
+}
+
+
+/*
+** Walk through the list of addresses and print the right ones.
+*/
+static int
+print(void)
+{
+       int naddrs = 0;
+
+       for (mp=head.m_next; mp; mp=mp->m_next) {
+               /* skip unless both are resent or neither one is */
+               if (resent != (mp->m_type&HRESENT)) {
+                       continue;
+               }
+               if (mp->m_type & (HTO|HCC)) {
+                       naddrs++;
+                       printone(mp);
+               }
+       }
+       return naddrs;
+}
+
+/*
+** Walk through the list of addresses and print the right ones.
+*/
+static int
+printdcc(void)
+{
+       int naddrs = 0;
+
+       for (mp=head.m_next; mp; mp=mp->m_next) {
+               /* skip unless both are resent or neither one is */
+               if (resent != (mp->m_type&HRESENT)) {
+                       continue;
+               }
+               if (mp->m_type & HDCC) {
+                       if (!naddrs && (toccsw || bccsw)) {
+                               puts(dccsep);
+                       }
+                       naddrs++;
+                       printone(mp);
+               }
+       }
+       return naddrs;
+}
+
+/*
+** Walk through the list of addresses and print the right ones.
+*/
+static int
+printbcc(void)
+{
+       int naddrs = 0;
+
+       for (mp=head.m_next; mp; mp=mp->m_next) {
+               /* skip unless both are resent or neither one is */
+               if (resent != (mp->m_type&HRESENT)) {
+                       continue;
+               }
+               if (mp->m_type & HBCC) {
+                       if (!naddrs && (toccsw || dccsw)) {
+                               puts(bccsep);
+                       }
+                       naddrs++;
+                       printone(mp);
+               }
+       }
+       return naddrs;
+}
+
 
-       return 0;  /* dead code to satisfy the compiler */
+/*
+** Print one single address in appropriate form.
+*/
+static void
+printone(struct mailname *mp)
+{
+       char buf[BUFSIZ];
+
+       if (mp->m_host) {
+               snprintf(buf, sizeof buf, " %s@%s", mp->m_mbox, mp->m_host);
+       } else {
+               snprintf(buf, sizeof buf, " %s", mp->m_mbox);
+       }
+       if (alisw) {
+               cmd = add(buf, cmd);
+       } else {
+               puts(buf+1);
+       }
 }