source: trunk/third/nmh/uip/show.c @ 12455

Revision 12455, 11.7 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12454, which included commits to RCS files with non-trunk default branches.
Line 
1
2/*
3 * show.c -- show/list messages
4 *
5 * $Id: show.c,v 1.1.1.1 1999-02-07 18:14:17 danw Exp $
6 */
7
8#include <h/mh.h>
9#include <h/mime.h>
10
11static struct swit switches[] = {
12#define CHECKMIMESW          0
13    { "checkmime", 0 },
14#define NOCHECKMIMESW        1
15    { "nocheckmime", 0 },
16#define HEADSW               2
17    { "header", 0 },
18#define NHEADSW              3
19    { "noheader", 0 },
20#define FORMSW               4
21    { "form formfile", 0 },
22#define PROGSW               5
23    { "moreproc program", 0 },
24#define NPROGSW              6
25    { "nomoreproc", 0 },
26#define LENSW                7
27    { "length lines", 0 },
28#define WIDTHSW              8
29    { "width columns", 0 },
30#define SHOWSW               9
31    { "showproc program", 0 },
32#define SHOWMIMESW          10
33    { "showmimeproc program", 0 },
34#define NSHOWSW             11
35    { "noshowproc", 0 },
36#define DRFTSW              12
37    { "draft", 0 },
38#define FILESW              13
39    { "file file", -4 },                /* interface from showfile */
40#define VERSIONSW           14
41    { "version", 0 },
42#define HELPSW              15
43    { "help", 0 },
44    { NULL, 0 }
45};
46
47/*
48 * static prototypes
49 */
50static int is_nontext(char *);
51
52#define SHOW  0
53#define NEXT  1
54#define PREV  2
55
56
57int
58main (int argc, char **argv)
59{
60    int draftsw = 0, headersw = 1, msgp = 0;
61    int nshow = 0, checkmime = 1, mime;
62    int vecp = 1, procp = 1, isdf = 0, mode = SHOW, msgnum;
63    char *cp, *maildir, *file = NULL, *folder = NULL, *proc;
64    char buf[BUFSIZ], **argp, **arguments;
65    char *msgs[MAXARGS], *vec[MAXARGS];
66    struct msgs *mp;
67
68#ifdef LOCALE
69    setlocale(LC_ALL, "");
70#endif
71    invo_name = r1bindex (argv[0], '/');
72
73    /* read user profile/context */
74    context_read();
75
76    if (!strcasecmp (invo_name, "next")) {
77        mode = NEXT;
78    } else if (!strcasecmp (invo_name, "prev")) {
79        mode = PREV;
80    }
81    arguments = getarguments (invo_name, argc, argv, 1);
82    argp = arguments;
83
84    while ((cp = *argp++)) {
85        if (*cp == '-') {
86            switch (smatch (++cp, switches)) {
87                case AMBIGSW:
88                    ambigsw (cp, switches);
89                    done (1);
90                case UNKWNSW:
91                case NPROGSW:
92                    vec[vecp++] = --cp;
93                    continue;
94
95                case HELPSW:
96                    snprintf (buf, sizeof(buf),
97                        "%s [+folder] %s[switches] [switches for showproc]",
98                        invo_name, mode == SHOW ? "[msgs] ": "");
99                    print_help (buf, switches, 1);
100                    done (1);
101                case VERSIONSW:
102                    print_version(invo_name);
103                    done (1);
104
105                case DRFTSW:
106                    if (file)
107                        adios (NULL, "only one file at a time!");
108                    draftsw++;
109                    if (mode == SHOW)
110                        continue;
111usage:
112                    adios (NULL,
113                            "usage: %s [+folder] [switches] [switches for showproc]",
114                            invo_name);
115                case FILESW:
116                    if (mode != SHOW)
117                        goto usage;
118                    if (draftsw || file)
119                        adios (NULL, "only one file at a time!");
120                    if (!(cp = *argp++) || *cp == '-')
121                        adios (NULL, "missing argument to %s", argp[-2]);
122                    file = path (cp, TFILE);
123                    continue;
124
125                case HEADSW:
126                    headersw++;
127                    continue;
128                case NHEADSW:
129                    headersw = 0;
130                    continue;
131
132                case FORMSW:
133                    vec[vecp++] = --cp;
134                    if (!(cp = *argp++) || *cp == '-')
135                        adios (NULL, "missing argument to %s", argp[-2]);
136                    vec[vecp++] = getcpy (etcpath(cp));
137                    continue;
138
139                case PROGSW:
140                case LENSW:
141                case WIDTHSW:
142                    vec[vecp++] = --cp;
143                    if (!(cp = *argp++) || *cp == '-')
144                        adios (NULL, "missing argument to %s", argp[-2]);
145                    vec[vecp++] = cp;
146                    continue;
147
148                case SHOWSW:
149                    if (!(showproc = *argp++) || *showproc == '-')
150                        adios (NULL, "missing argument to %s", argp[-2]);
151                    nshow = 0;
152                    continue;
153                case NSHOWSW:
154                    nshow++;
155                    continue;
156
157                case SHOWMIMESW:
158                    if (!(showmimeproc = *argp++) || *showmimeproc == '-')
159                        adios (NULL, "missing argument to %s", argp[-2]);
160                    nshow = 0;
161                    continue;
162                case CHECKMIMESW:
163                    checkmime++;
164                    continue;
165                case NOCHECKMIMESW:
166                    checkmime = 0;
167                    continue;
168            }
169        }
170        if (*cp == '+' || *cp == '@') {
171            if (folder)
172                adios (NULL, "only one folder at a time!");
173            else
174                folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
175        } else {
176            if (mode != SHOW)
177                goto usage;
178            else
179                msgs[msgp++] = cp;
180        }
181    }
182    procp = vecp;
183
184    if (!context_find ("path"))
185        free (path ("./", TFOLDER));
186
187    if (draftsw || file) {
188        if (msgp)
189            adios (NULL, "only one file at a time!");
190        vec[vecp++] = draftsw
191            ? getcpy (m_draft (folder, msgp ? msgs[0] : NULL, 1, &isdf))
192            : file;
193        goto go_to_it;
194    }
195
196#ifdef WHATNOW
197    if (!msgp && !folder && mode == SHOW && (cp = getenv ("mhdraft")) && *cp) {
198        draftsw++;
199        vec[vecp++] = cp;
200        goto go_to_it;
201    }
202#endif /* WHATNOW */
203
204    if (!msgp) {
205        switch (mode) {
206            case NEXT:
207                msgs[msgp++] = "next";
208                break;
209            case PREV:
210                msgs[msgp++] = "prev";
211                break;
212            default:
213                msgs[msgp++] = "cur";
214                break;
215        }
216    }
217
218    if (!folder)
219        folder = getfolder (1);
220    maildir = m_maildir (folder);
221
222    if (chdir (maildir) == NOTOK)
223        adios (maildir, "unable to change directory to");
224
225    /* read folder and create message structure */
226    if (!(mp = folder_read (folder)))
227        adios (NULL, "unable to read folder %s", folder);
228
229    /* check for empty folder */
230    if (mp->nummsg == 0)
231        adios (NULL, "no messages in %s", folder);
232
233    /* parse all the message ranges/sequences and set SELECTED */
234    for (msgnum = 0; msgnum < msgp; msgnum++)
235        if (!m_convert (mp, msgs[msgnum]))
236            done (1);
237
238    /*
239     * Set the SELECT_UNSEEN bit for all the SELECTED messages,
240     * since we will use that as a tag to know which messages
241     * to remove from the "unseen" sequence.
242     */
243    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
244        if (is_selected(mp, msgnum))
245            set_unseen (mp, msgnum);
246
247    seq_setprev (mp);           /* set the Previous-Sequence */
248    seq_setunseen (mp, 1);      /* unset the Unseen-Sequence */
249
250    if (mp->numsel > MAXARGS - 2)
251        adios (NULL, "more than %d messages for show exec", MAXARGS - 2);
252
253    for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
254        if (is_selected(mp, msgnum))
255            vec[vecp++] = getcpy (m_name (msgnum));
256
257    seq_setcur (mp, mp->hghsel);        /* update current message  */
258    seq_save (mp);                      /* synchronize sequences   */
259    context_replace (pfolder, folder);  /* update current folder   */
260    context_save ();                    /* save the context file   */
261
262    if (headersw && vecp == 2)
263        printf ("(Message %s:%s)\n", folder, vec[1]);
264
265go_to_it: ;
266    fflush (stdout);
267
268    vec[vecp] = NULL;
269
270    /*
271     * Decide which "proc" to use
272     */
273    mime = 0;
274    if (nshow) {
275        proc = catproc;
276    } else {
277        /* check if any messages are non-text MIME messages */
278        if (checkmime && !getenv ("NOMHNPROC")) {
279            if (!draftsw && !file) {
280                /* loop through selected messages and check for MIME */
281                for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
282                    if (is_selected (mp, msgnum) && is_nontext (m_name (msgnum))) {
283                        mime = 1;
284                        break;
285                    }
286            } else {
287                /* check the file or draft for MIME */
288                if (is_nontext (vec[vecp - 1]))
289                    mime = 1;
290            }
291        }
292
293        /* Set the "proc" */
294        if (mime)
295            proc = showmimeproc;
296        else
297            proc = showproc;
298    }
299
300    if (folder && !draftsw && !file)
301        m_putenv ("mhfolder", folder);
302
303    /*
304     * For backward compatibility, if the "proc" is mhn,
305     * then add "-show" option.  Add "-file" if showing
306     * file or draft.
307     */
308    if (strcmp (r1bindex (proc, '/'), "mhn") == 0) {
309        if (draftsw || file) {
310            vec[vecp] = vec[vecp - 1];
311            vec[vecp - 1] = "-file";
312            vecp++;
313        }
314        vec[vecp++] = "-show";
315        vec[vecp] = NULL;
316    }
317
318    /*
319     * If "proc" is mhl, then run it internally
320     * rather than exec'ing it.
321     */
322    if (strcmp (r1bindex (proc, '/'), "mhl") == 0) {
323        vec[0] = "mhl";
324        mhl (vecp, vec);
325        done (0);
326    }
327
328    /*
329     * If you are not using a nmh command as your "proc", then
330     * add the path to the message names.  Currently, we are just
331     * checking for mhn here, since we've already taken care of mhl.
332     */
333    if (!strcmp (r1bindex (proc, '/'), "mhl")
334            && !draftsw
335            && !file
336            && chdir (maildir = concat (m_maildir (""), "/", NULL)) != NOTOK) {
337        mp->foldpath = concat (mp->foldpath, "/", NULL);
338        cp = ssequal (maildir, mp->foldpath)
339            ? mp->foldpath + strlen (maildir)
340            : mp->foldpath;
341        for (msgnum = procp; msgnum < vecp; msgnum++)
342            vec[msgnum] = concat (cp, vec[msgnum], NULL);
343    }
344
345    vec[0] = r1bindex (proc, '/');
346    execvp (proc, vec);
347    adios (proc, "unable to exec");
348}
349
350/*
351 * Cheat:  we are loaded with adrparse, which wants a routine called
352 * OfficialName().  We call adrparse:getm() with the correct arguments
353 * to prevent OfficialName() from being called.  Hence, the following
354 * is to keep the loader happy.
355 */
356
357char *
358OfficialName (char *name)
359{
360    return name;
361}
362
363
364/*
365 * Check if a message or file contains any non-text parts
366 */
367static int
368is_nontext (char *msgnam)
369{
370    int result, state;
371    char *bp, *cp, *dp;
372    char buf[BUFSIZ], name[NAMESZ];
373    FILE *fp;
374
375    if ((fp = fopen (msgnam, "r")) == NULL)
376        return 0;
377
378    for (state = FLD;;) {
379        switch (state = m_getfld (state, name, buf, sizeof(buf), fp)) {
380        case FLD:
381        case FLDPLUS:
382        case FLDEOF:
383            /*
384             * Check Content-Type field
385             */
386            if (!strcasecmp (name, TYPE_FIELD)) {
387                int passno;
388                char c;
389
390                cp = add (buf, NULL);
391                while (state == FLDPLUS) {
392                    state = m_getfld (state, name, buf, sizeof(buf), fp);
393                    cp = add (buf, cp);
394                }
395                bp = cp;
396                passno = 1;
397
398again:
399                for (; isspace (*bp); bp++)
400                    continue;
401                if (*bp == '(') {
402                    int i;
403
404                    for (bp++, i = 0;;) {
405                        switch (*bp++) {
406                        case '\0':
407invalid:
408                            result = 0;
409                            goto out;
410                        case '\\':
411                            if (*bp++ == '\0')
412                                goto invalid;
413                            continue;
414                        case '(':
415                            i++;
416                            /* and fall... */
417                        default:
418                            continue;
419                        case ')':
420                            if (--i < 0)
421                                break;
422                            continue;
423                        }
424                        break;
425                    }
426                }
427                if (passno == 2) {
428                    if (*bp != '/')
429                        goto invalid;
430                    bp++;
431                    passno = 3;
432                    goto again;
433                }
434                for (dp = bp; istoken (*dp); dp++)
435                    continue;
436                c = *dp;
437                *dp = '\0';
438                if (!*bp)
439                    goto invalid;
440                if (passno > 1) {
441                    if ((result = (strcasecmp (bp, "plain") != 0)))
442                        goto out;
443                    *dp = c;
444                    for (dp++; isspace (*dp); dp++)
445                        continue;
446                    if (*dp) {
447                        if ((result = !uprf (dp, "charset")))
448                            goto out;
449                        dp += sizeof("charset") - 1;
450                        while (isspace (*dp))
451                            dp++;
452                        if (*dp++ != '=')
453                            goto invalid;
454                        while (isspace (*dp))
455                            dp++;
456                        if (*dp == '"') {
457                            if ((bp = strchr(++dp, '"')))
458                                *bp = '\0';
459                        } else {
460                            for (bp = dp; *bp; bp++)
461                                if (isspace (*bp)) {
462                                    *bp = '\0';
463                                    break;
464                                }
465                        }
466                    } else {
467                        /* Default character set */
468                        dp = "US-ASCII";
469                    }
470                    /* Check the character set */
471                    result = !check_charset (dp, strlen (dp));
472                } else {
473                    if (!(result = (strcasecmp (bp, "text") != 0))) {
474                        *dp = c;
475                        bp = dp;
476                        passno = 2;
477                        goto again;
478                    }
479                }
480out:
481                free (cp);
482                if (result) {
483                    fclose (fp);
484                    return result;
485                }
486                break;
487            }
488
489            /*
490             * Check Content-Transfer-Encoding field
491             */
492            if (!strcasecmp (name, ENCODING_FIELD)) {
493                cp = add (buf, NULL);
494                while (state == FLDPLUS) {
495                    state = m_getfld (state, name, buf, sizeof(buf), fp);
496                    cp = add (buf, cp);
497                }
498                for (bp = cp; isspace (*bp); bp++)
499                    continue;
500                for (dp = bp; istoken (*dp); dp++)
501                    continue;
502                *dp = '\0';
503                result = (strcasecmp (bp, "7bit")
504                       && strcasecmp (bp, "8bit")
505                       && strcasecmp (bp, "binary"));
506
507                free (cp);
508                if (result) {
509                    fclose (fp);
510                    return result;
511                }
512                break;
513            }
514
515            /*
516             * Just skip the rest of this header
517             * field and go to next one.
518             */
519            while (state == FLDPLUS)
520                state = m_getfld (state, name, buf, sizeof(buf), fp);
521            break;
522
523            /*
524             * We've passed the message header,
525             * so message is just text.
526             */
527        default:
528            fclose (fp);
529            return 0;
530        }
531    }
532}
Note: See TracBrowser for help on using the repository browser.