9f67952f3493a99507b00325feef1b14f641cd06
[mmh] / folder.c
1 /* folder(s).c - report on folders */
2 #ifndef lint
3 static char ident[] = "@(#)$Id: folder.c,v 2.11 1993/08/27 23:23:06 jromine Exp $";
4 #endif  /* lint */
5
6 #include "../h/mh.h"
7 #include "../h/local.h"
8 #include <errno.h>
9 #include <stdio.h>
10 #ifdef LOCALE
11 #include        <locale.h>
12 #endif
13
14 static          dodir(), addir(), addfold(), dother();
15 static int      pfold(), sfold(), compare();
16 /* \f */
17
18 static struct swit switches[] = {
19 #define ALLSW   0
20     "all", 0,
21
22 #define CREATSW 1
23     "create", 0,
24 #define NCREATSW 2
25     "nocreate", 0,
26
27 #define FASTSW  3
28     "fast", 0,
29 #define NFASTSW 4
30     "nofast", 0,
31
32 #define HDRSW   5
33     "header", 0,
34 #define NHDRSW  6
35     "noheader", 0,
36
37 #define PACKSW  7
38     "pack", 0,
39 #define NPACKSW 8
40     "nopack", 0,
41 #define VERBSW  9
42     "verbose", 0,
43 #define NVERBSW 10
44     "noverbose", 0,
45
46 #define RECURSW 11
47     "recurse", 0,
48 #define NRECRSW 12
49     "norecurse", 0,
50
51 #define TOTALSW 13
52     "total", 0,
53 #define NTOTLSW 14
54     "nototal", 0,
55
56 #define PRNTSW  15
57     "print", 0,
58 #define NPRNTSW 16
59     "noprint", -4,
60 #define LISTSW  17
61     "list", 0,
62 #define NLISTSW 18
63     "nolist", 0,
64 #define PUSHSW  19
65     "push", 0,
66 #define POPSW   20
67     "pop", 0,
68
69 #define HELPSW  21
70     "help", 4,
71
72     NULL, 0
73 };
74
75 /* \f */
76
77 extern int errno;
78
79 static int  fshort = 0;
80 static int  fcreat = 0;
81 static int  fpack = 0;
82 static int  fverb = 0;
83 static int  fheader = 0;
84 static int  frecurse = 0;
85 static int  ftotonly = 0;
86 static int  msgtot = 0;
87 static int  foldtot = 0;
88 static int  start = 0;
89 static int  foldp = 0;
90
91 static char *mhdir;
92 static char *stack = "Folder-Stack";
93 static char folder[BUFSIZ];
94 static char *folds[NFOLDERS + 1];
95
96 struct msgs *tfold ();
97
98 /* \f */
99
100 /* ARGSUSED */
101
102 main (argc, argv)
103 char   *argv[];
104 {
105     int     all = 0,
106             printsw = 0,
107             listsw = 0,
108             pushsw = 0,
109             popsw = 0;
110     char   *cp,
111            *dp,
112            *msg = NULL,
113            *argfolder = NULL,
114           **ap,
115           **argp,
116             buf[100],
117            *arguments[MAXARGS];
118     struct stat st;
119
120 #ifdef LOCALE
121         setlocale(LC_ALL, "");
122 #endif
123     invo_name = r1bindex (argv[0], '/');
124     if (argv[0][strlen (argv[0]) - 1] == 's')
125         all++;
126     if ((cp = m_find (invo_name)) != NULL) {
127         ap = brkstring (cp = getcpy (cp), " ", "\n");
128         ap = copyip (ap, arguments);
129     }
130     else
131         ap = arguments;
132     (void) copyip (argv + 1, ap);
133     argp = arguments;
134
135 /* \f */
136
137     while (cp = *argp++) {
138         if (*cp == '-')
139             switch (smatch (++cp, switches)) {
140                 case AMBIGSW: 
141                     ambigsw (cp, switches);
142                     done (1);
143                 case UNKWNSW: 
144                     adios (NULLCP, "-%s unknown", cp);
145                 case HELPSW: 
146                     (void) sprintf (buf, "%s [+folder] [msg] [switches]",
147                             invo_name);
148                     help (buf, switches);
149                     done (1);
150
151                 case ALLSW: 
152                     all++;
153                     continue;
154
155                 case CREATSW: 
156                     fcreat = 1;
157                     continue;
158                 case NCREATSW: 
159                     fcreat = -1;
160                     continue;
161
162                 case FASTSW: 
163                     fshort++;
164                     continue;
165                 case NFASTSW: 
166                     fshort = 0;
167                     continue;
168
169                 case HDRSW: 
170                     fheader = -1;
171                     continue;
172                 case NHDRSW: 
173                     fheader++;
174                     continue;
175
176                 case PACKSW: 
177                     fpack++;
178                     continue;
179                 case NPACKSW: 
180                     fpack = 0;
181                     continue;
182
183                 case VERBSW:
184                     fverb++;
185                     continue;
186                 case NVERBSW:
187                     fverb = 0;
188                     continue;
189
190                 case RECURSW: 
191                     frecurse++;
192                     continue;
193                 case NRECRSW: 
194                     frecurse = 0;
195                     continue;
196
197                 case TOTALSW: 
198                     all++;
199                     ftotonly++;
200                     continue;
201                 case NTOTLSW: 
202                     if (ftotonly)
203                         all = 0;
204                     ftotonly = 0;
205                     continue;
206
207                 case PRNTSW: 
208                     printsw++;
209                     continue;
210                 case NPRNTSW: 
211                     printsw = 0;
212                     continue;
213
214                 case LISTSW: 
215                     listsw++;
216                     continue;
217                 case NLISTSW: 
218                     listsw = 0;
219                     continue;
220
221                 case PUSHSW: 
222                     pushsw++;
223                     listsw++;
224                     popsw = 0;
225                     continue;
226                 case POPSW: 
227                     popsw++;
228                     listsw++;
229                     pushsw = 0;
230                     continue;
231             }
232         if (*cp == '+' || *cp == '@')
233             if (argfolder)
234                 adios (NULLCP, "only one folder at a time!");
235             else
236                 argfolder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
237         else
238             if (msg)
239                 adios (NULLCP, "only one (current) message at a time!");
240             else
241                 msg = cp;
242     }
243
244 /* \f */
245
246     if (!m_find ("path"))
247         free (path ("./", TFOLDER));
248     mhdir = concat (m_maildir (""), "/", NULLCP);
249
250     if (pushsw == 0 && popsw == 0 && listsw == 0)
251         printsw++;
252     if (pushsw) {
253         if (!argfolder) {
254             if ((cp = m_find (stack)) == NULL
255                     || (ap = brkstring (dp = getcpy (cp), " ", "\n")) == NULL
256                     || (argfolder = *ap++) == NULL)
257                 adios (NULLCP, "no other folder");
258             for (cp = getcpy (m_getfolder ()); *ap; ap++)
259                 cp = add (*ap, add (" ", cp));
260             free (dp);
261             m_replace (stack, cp);
262         }
263         else
264             m_replace (stack,
265                     (cp = m_find (stack))
266                     ? concat (m_getfolder (), " ", cp, NULLCP)
267                     : getcpy (m_getfolder ()));
268     }
269     if (popsw) {
270         if (argfolder)
271             adios (NULLCP, "sorry, no folders allowed with -pop");
272         if ((cp = m_find (stack)) == NULL
273                 || (ap = brkstring (dp = getcpy (cp), " ", "\n")) == NULL
274                 || (argfolder = *ap++) == NULL)
275             adios (NULLCP, "folder stack empty");
276         for (cp = NULL; *ap; ap++)
277             cp = cp ? add (*ap, add (" ", cp)) : getcpy (*ap);
278         free (dp);
279         if (cp)
280             m_replace (stack, cp);
281         else
282             (void) m_delete (stack);
283     }
284     if (pushsw || popsw) {
285         if (access (cp = m_maildir (argfolder), 0) == NOTOK)
286             adios (cp, "unable to find folder");
287         m_replace (pfolder, argfolder);
288         m_update ();
289         argfolder = NULL;
290     }
291     if (listsw) {
292         printf ("%s", argfolder ? argfolder : m_getfolder ());
293         if (cp = m_find (stack)) {
294             for (ap = brkstring (dp = getcpy (cp), " ", "\n"); *ap; ap++)
295                 printf (" %s", *ap);
296             free (dp);
297         }
298         printf ("\n");
299
300         if (!printsw)
301             done (0);
302     }
303
304 /* \f */
305
306     if (all) {
307         fheader = 0;
308         if (argfolder) {
309             (void) strcpy (folder, argfolder);
310             if (pfold (argfolder, msg)) {
311                 m_replace (pfolder, argfolder);
312                 m_update ();
313             }
314             if (!frecurse)      /* recurse not done in pfold(), */
315                 dodir (folder); /* so just list all level-1 sub-folders */
316         }
317         else {
318             if (msg)
319                 admonish (NULLCP, "no folder given for message %s", msg);
320             dother ();
321
322             (void) strcpy (folder, (cp = m_find (pfolder)) ? cp : "");
323             dodir (".");
324         }
325
326         if (!fshort) {
327             if (!ftotonly)
328                 printf ("\n\t\t     ");
329             printf ("TOTAL= %*d message%c in %d folder%s.\n",
330                     DMAXFOLDER, msgtot, msgtot != 1 ? 's' : ' ',
331                     foldtot, foldtot != 1 ? "s" : "");
332         }
333     }
334     else {
335         fheader++;
336
337         (void) strcpy (folder, argfolder ? argfolder : m_getfolder ());
338         if (stat (strcpy (buf, m_maildir (folder)), &st) == NOTOK) {
339             if (errno != ENOENT)
340                 adios (buf, "error on folder");
341             switch (fcreat) {
342                 case 0:                 /* ask before create */
343                     cp = concat ("Create folder \"", buf, "\"? ", NULLCP);
344                     if (!getanswer (cp))
345                         done (1);
346                     free (cp);
347                     break;
348                 case -1:                /* do not create */
349                     done (1);
350                     break;
351             }
352             if (!makedir (buf))
353                 adios (NULLCP, "unable to create folder %s", buf);
354         }
355
356         if (pfold (folder, msg) && argfolder)
357             m_replace (pfolder, argfolder);
358     }
359
360     m_update ();
361
362     done (0);
363 }
364
365 /* \f */
366
367 static  dodir (dir)
368 register char   *dir;
369 {
370     int     i;
371     int     os = start;
372     int     of = foldp;
373     char    buffer[BUFSIZ];
374
375     start = foldp;
376     if (chdir (mhdir) == NOTOK)
377         adios (mhdir, "unable to change directory to");
378
379     addir (strcpy (buffer, dir));
380     for (i = start; i < foldp; i++)
381         (void) pfold (folds[i], NULLCP), (void) fflush (stdout);
382
383     start = os;
384     foldp = of;
385 }
386
387 /* \f */
388
389 static int  pfold (fold, msg)
390 register char   *fold,
391                 *msg;
392 {
393     int     hack,
394             others,
395             retval = 1;
396     register char *mailfile;
397     register struct msgs   *mp = NULL;
398
399     mailfile = m_maildir (fold);
400     if (chdir (mailfile) == NOTOK) {
401         if (errno != EACCES)
402             admonish (mailfile, "unable to change directory to");
403         else
404             printf ("%22s%c unreadable\n",
405                     fold, strcmp (folder, fold) ? ' ' : '+');
406         return 0;
407     }
408
409     if (fshort) {
410         printf ("%s\n", fold);
411
412         if (!msg && !fpack) {
413             if (frecurse)
414                 dodir (fold);
415             return retval;
416         }
417     }
418
419     if (!(mp = m_gmsg (fold))) {
420         admonish (NULLCP, "unable to read folder %s", fold);
421         return 0;
422     }
423
424     if (msg && !sfold (mp, msg))
425         retval = 0;
426     if (fpack)
427         mp = tfold (mp);
428
429     if (fshort)
430         goto out;
431     foldtot++;
432     msgtot += mp -> nummsg;
433
434     if (ftotonly)
435         goto out;
436
437     if (!fheader++)
438         printf ("\t\tFolder  %*s# of messages (%*srange%*s); cur%*smsg  (other files)\n",
439             DMAXFOLDER, "", DMAXFOLDER - 2, "", DMAXFOLDER - 2, "",
440             DMAXFOLDER - 2, "");
441
442     printf ("%22s%c ", fold, strcmp (folder, fold) ? ' ' : '+');
443
444     hack = 0;
445     if (mp -> hghmsg == 0)
446         printf ("has   no messages%*s",
447                 mp -> msgflags & OTHERS ? DMAXFOLDER * 2 + 4 : 0, "");
448     else {
449         printf ("has %*d message%s (%*d-%*d)",
450                 DMAXFOLDER, mp -> nummsg, (mp -> nummsg == 1) ? " " : "s",
451                 DMAXFOLDER, mp -> lowmsg, DMAXFOLDER, mp -> hghmsg);
452         if (mp -> curmsg >= mp -> lowmsg && mp -> curmsg <= mp -> hghmsg)
453             printf ("; cur=%*d", DMAXFOLDER, hack = mp -> curmsg);
454     }
455
456     if (mp -> msgflags & OTHERS)
457         printf (";%*s (others)", hack ? 0 : DMAXFOLDER + 6, "");
458     printf (".\n");
459
460 out: ;
461     others = mp -> msgflags & OTHERS;
462     m_fmsg (mp);
463
464     if (frecurse && others)
465         dodir (fold);
466
467     return retval;
468 }
469
470 /* \f */
471
472 static int  sfold (mp, msg)
473 register struct msgs   *mp;
474 char   *msg;
475 {
476     if (!m_convert (mp, msg))
477         return 0;
478
479     if (mp -> numsel > 1) {
480         admonish (NULLCP, "only one message at a time!");
481         return 0;
482     }
483     m_setseq (mp);
484     m_setcur (mp, mp -> lowsel);
485     m_sync (mp);
486     m_update ();
487
488     return 1;
489 }
490
491
492 struct msgs *tfold (mp)
493 register struct msgs   *mp;
494 {
495     register int    hole,
496                     msgnum;
497     char    newmsg[BUFSIZ],
498             oldmsg[BUFSIZ];
499
500     if (mp -> lowmsg > 1 && (mp = m_remsg (mp, 1, mp -> hghmsg)) == NULL)
501         adios (NULLCP, "unable to allocate folder storage");
502
503     for (msgnum = mp -> lowmsg, hole = 1; msgnum <= mp -> hghmsg; msgnum++)
504         if (mp -> msgstats[msgnum] & EXISTS) {
505             if (msgnum != hole) {
506                 (void) strcpy (newmsg, m_name (hole));
507                 (void) strcpy (oldmsg, m_name (msgnum));
508                 if (fverb)
509                     printf ("message %s becomes %s\n", oldmsg, newmsg);
510                 if (rename (oldmsg, newmsg) == NOTOK)
511                     adios (newmsg, "unable to rename %s to", oldmsg);
512                 if (msgnum == mp -> curmsg)
513                     m_setcur (mp, mp -> curmsg = hole);
514                 mp -> msgstats[hole] = mp -> msgstats[msgnum];
515                 mp -> msgflags |= SEQMOD;
516                 if (msgnum == mp -> lowsel)
517                     mp -> lowsel = hole;
518                 if (msgnum == mp -> hghsel)
519                     mp -> hghsel = hole;
520             }
521             hole++;
522         }
523     if (mp -> nummsg > 0) {
524         mp -> lowmsg = 1;
525         mp -> hghmsg = hole - 1;
526     }
527     m_sync (mp);
528     m_update ();
529
530     return mp;
531 }
532
533 /* \f */
534
535 static  addir (name)
536 register char   *name;
537 {
538     register char  *base,
539                    *cp;
540     struct stat st;
541 #ifdef SYS5DIR
542     register struct dirent *dp;
543 #else /* SYS5DIR */
544     register struct direct *dp;
545 #endif /* SYS5DIR */
546     register    DIR * dd;
547
548     cp = name + strlen (name);
549     *cp++ = '/';
550     *cp = '\0';
551
552     base = strcmp (name, "./") ? name : name + 2;/* hack */
553
554     if ((dd = opendir (name)) == NULL) {
555         admonish (name, "unable to read directory ");
556         return;
557     }
558     while (dp = readdir (dd))
559         if (strcmp (dp -> d_name, ".") && strcmp (dp -> d_name, "..")) {
560 #ifdef SYS5DIR
561             if (cp + dp -> d_reclen + 2 >= name + BUFSIZ)
562 #else /* SYS5DIR */
563             if (cp + strlen (dp -> d_name) + 2 >= name + BUFSIZ)
564 #endif /* SYS5DIR */
565                 continue;
566             (void) strcpy (cp, dp -> d_name);
567             if (stat (name, &st) != NOTOK && (st.st_mode & S_IFMT) == S_IFDIR)
568                 addfold (base);
569         }
570     closedir (dd);
571
572     *--cp = '\0';
573 }
574
575 /* \f */
576
577 static  addfold (fold)
578 register char   *fold;
579 {
580     register int    i,
581                     j;
582     register char  *cp;
583
584     if (foldp > NFOLDERS)
585         adios (NULLCP, "more than %d folders to report on", NFOLDERS);
586
587     cp = getcpy (fold);
588     for (i = start; i < foldp; i++)
589         if (compare (cp, folds[i]) < 0) {
590             for (j = foldp - 1; j >= i; j--)
591                 folds[j + 1] = folds[j];
592             foldp++;
593             folds[i] = cp;
594             return;
595         }
596
597     folds[foldp++] = cp;
598 }
599
600 /* \f */
601
602 static int  compare (s1, s2)
603 register char   *s1,
604                 *s2;
605 {
606     register int    i;
607
608     while (*s1 || *s2)
609         if (i = *s1++ - *s2++)
610             return i;
611
612     return 0;
613 }
614
615 /* \f */
616
617 static  dother () {
618     int     atrlen;
619     char    atrcur[BUFSIZ];
620     register struct node   *np;
621
622     (void) sprintf (atrcur, "atr-%s-", current);
623     atrlen = strlen (atrcur);
624
625     m_getdefs ();
626     for (np = m_defs; np; np = np -> n_next)
627         if (ssequal (atrcur, np -> n_name)
628                 && !ssequal (mhdir, np -> n_name + atrlen))
629             (void) pfold (np -> n_name + atrlen, NULLCP);
630 }