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

Revision 12455, 18.5 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 * folder(s).c -- set/list the current message and/or folder
4 *             -- push/pop a folder onto/from the folder stack
5 *             -- list the folder stack
6 *
7 * $Id: folder.c,v 1.1.1.1 1999-02-07 18:14:13 danw Exp $
8 */
9
10#include <h/mh.h>
11#include <errno.h>
12
13static struct swit switches[] = {
14#define ALLSW           0
15    { "all", 0 },
16#define NALLSW          1
17    { "noall", 0 },
18#define CREATSW         2
19    { "create", 0 },
20#define NCREATSW        3
21    { "nocreate", 0 },
22#define FASTSW          4
23    { "fast", 0 },
24#define NFASTSW         5
25    { "nofast", 0 },
26#define HDRSW           6
27    { "header", 0 },
28#define NHDRSW          7
29    { "noheader", 0 },
30#define PACKSW          8
31    { "pack", 0 },
32#define NPACKSW         9
33    { "nopack", 0 },
34#define VERBSW         10
35    { "verbose", 0 },
36#define NVERBSW        11
37    { "noverbose", 0 },
38#define RECURSW        12
39    { "recurse", 0 },
40#define NRECRSW        13
41    { "norecurse", 0 },
42#define TOTALSW        14
43    { "total", 0 },
44#define NTOTLSW        15
45    { "nototal", 0 },
46#define LISTSW         16
47    { "list", 0 },
48#define NLISTSW        17
49    { "nolist", 0 },
50#define PRNTSW         18
51    { "print", 0 },
52#define NPRNTSW        19
53    { "noprint", -4 },
54#define PUSHSW         20
55    { "push", 0 },
56#define POPSW          21
57    { "pop", 0 },
58#define VERSIONSW      22
59    { "version", 0 },
60#define HELPSW         23
61    { "help", 4 },
62    { NULL, 0 }
63};
64
65extern int errno;
66
67static int fshort   = 0;        /* output only folder names                 */
68static int fcreat   = 0;        /* should we ask to create new folders?     */
69static int fpack    = 0;        /* are we packing the folder?               */
70static int fverb    = 0;        /* print actions taken while packing folder */
71static int fheader  = 0;        /* should we output a header?               */
72static int frecurse = 0;        /* recurse through subfolders               */
73static int ftotal   = 0;        /* should we output the totals?             */
74static int all      = 0;        /* should we output all folders             */
75
76static int total_folders = 0;   /* total number of folders                  */
77
78static int start = 0;
79static int foldp = 0;
80
81static char *nmhdir;
82static char *stack = "Folder-Stack";
83static char folder[BUFSIZ];
84
85#define NUMFOLDERS 100
86
87/*
88 * This is how many folders we currently can hold in the array
89 * `folds'.  We increase this amount by NUMFOLDERS at a time.
90 */
91static int maxfolders;
92static char **folds;
93
94/*
95 * Structure to hold information about
96 * folders as we scan them.
97 */
98struct FolderInfo {
99    char *name;
100    int nummsg;
101    int curmsg;
102    int lowmsg;
103    int hghmsg;
104    int others;         /* others == 1 if other files in folder */
105    int error;          /* error == 1 for unreadable folder     */
106};
107
108/*
109 * Dynamically allocated space to hold
110 * all the folder information.
111 */
112static struct FolderInfo *fi;
113static int maxFolderInfo;
114
115/*
116 * static prototypes
117 */
118static void dodir (char *);
119static int get_folder_info (char *, char *);
120static void print_folders (void);
121static int num_digits (int);
122static int sfold (struct msgs *, char *);
123static void addir (char *);
124static void addfold (char *);
125static int compare (char *, char *);
126static void readonly_folders (void);
127
128
129int
130main (int argc, char **argv)
131{
132    int printsw = 0, listsw = 0;
133    int pushsw = 0, popsw = 0;
134    char *cp, *dp, *msg = NULL, *argfolder = NULL;
135    char **ap, **argp, buf[BUFSIZ], **arguments;
136    struct stat st;
137
138#ifdef LOCALE
139    setlocale(LC_ALL, "");
140#endif
141    invo_name = r1bindex (argv[0], '/');
142
143    /* read user profile/context */
144    context_read();
145
146    /*
147     * If program was invoked with name ending
148     * in `s', then add switch `-all'.
149     */
150    if (argv[0][strlen (argv[0]) - 1] == 's')
151        all = 1;
152
153    arguments = getarguments (invo_name, argc, argv, 1);
154    argp = arguments;
155
156    while ((cp = *argp++)) {
157        if (*cp == '-') {
158            switch (smatch (++cp, switches)) {
159                case AMBIGSW:
160                    ambigsw (cp, switches);
161                    done (1);
162                case UNKWNSW:
163                    adios (NULL, "-%s unknown", cp);
164
165                case HELPSW:
166                    snprintf (buf, sizeof(buf), "%s [+folder] [msg] [switches]",
167                        invo_name);
168                    print_help (buf, switches, 1);
169                    done (1);
170                case VERSIONSW:
171                    print_version(invo_name);
172                    done (1);
173
174                case ALLSW:
175                    all = 1;
176                    continue;
177
178                case NALLSW:
179                    all = 0;
180                    continue;
181
182                case CREATSW:
183                    fcreat = 1;
184                    continue;
185                case NCREATSW:
186                    fcreat = -1;
187                    continue;
188
189                case FASTSW:
190                    fshort++;
191                    continue;
192                case NFASTSW:
193                    fshort = 0;
194                    continue;
195
196                case HDRSW:
197                    fheader = 1;
198                    continue;
199                case NHDRSW:
200                    fheader = -1;
201                    continue;
202
203                case PACKSW:
204                    fpack++;
205                    continue;
206                case NPACKSW:
207                    fpack = 0;
208                    continue;
209
210                case VERBSW:
211                    fverb++;
212                    continue;
213                case NVERBSW:
214                    fverb = 0;
215                    continue;
216
217                case RECURSW:
218                    frecurse++;
219                    continue;
220                case NRECRSW:
221                    frecurse = 0;
222                    continue;
223
224                case TOTALSW:
225                    ftotal = 1;
226                    continue;
227                case NTOTLSW:
228                    ftotal = -1;
229                    continue;
230
231                case PRNTSW:
232                    printsw = 1;
233                    continue;
234                case NPRNTSW:
235                    printsw = 0;
236                    continue;
237
238                case LISTSW:
239                    listsw = 1;
240                    continue;
241                case NLISTSW:
242                    listsw = 0;
243                    continue;
244
245                case PUSHSW:
246                    pushsw = 1;
247                    listsw = 1;
248                    popsw  = 0;
249                    continue;
250                case POPSW:
251                    popsw  = 1;
252                    listsw = 1;
253                    pushsw = 0;
254                    continue;
255            }
256        }
257        if (*cp == '+' || *cp == '@') {
258            if (argfolder)
259                adios (NULL, "only one folder at a time!");
260            else
261                argfolder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
262        } else {
263            if (msg)
264                adios (NULL, "only one (current) message at a time!");
265            else
266                msg = cp;
267        }
268    }
269
270    if (!context_find ("path"))
271        free (path ("./", TFOLDER));
272    nmhdir = concat (m_maildir (""), "/", NULL);
273
274    /*
275     * If we aren't working with the folder stack
276     * (-push, -pop, -list) then the default is to print.
277     */
278    if (pushsw == 0 && popsw == 0 && listsw == 0)
279        printsw++;
280
281    /* Pushing a folder onto the folder stack */
282    if (pushsw) {
283        if (!argfolder) {
284            /* If no folder is given, the current folder and */
285            /* the top of the folder stack are swapped.      */
286            if ((cp = context_find (stack))) {
287                dp = getcpy (cp);
288                ap = brkstring (dp, " ", "\n");
289                argfolder = getcpy(*ap++);
290            } else {
291                adios (NULL, "no other folder");
292            }
293            for (cp = getcpy (getfolder (1)); *ap; ap++)
294                cp = add (*ap, add (" ", cp));
295            free (dp);
296            context_replace (stack, cp);        /* update folder stack */
297        } else {
298            /* update folder stack */
299            context_replace (stack,
300                    (cp = context_find (stack))
301                    ? concat (getfolder (1), " ", cp, NULL)
302                    : getcpy (getfolder (1)));
303        }
304    }
305
306    /* Popping a folder off of the folder stack */
307    if (popsw) {
308        if (argfolder)
309            adios (NULL, "sorry, no folders allowed with -pop");
310        if ((cp = context_find (stack))) {
311            dp = getcpy (cp);
312            ap = brkstring (dp, " ", "\n");
313            argfolder = getcpy(*ap++);
314        } else {
315            adios (NULL, "folder stack empty");
316        }
317        if (*ap) {
318            /* if there's anything left in the stack */
319            cp = getcpy (*ap++);
320            for (; *ap; ap++)
321                cp = add (*ap, add (" ", cp));
322            context_replace (stack, cp);        /* update folder stack */
323        } else {
324            context_del (stack);        /* delete folder stack entry from context */
325        }
326        free (dp);
327    }
328    if (pushsw || popsw) {
329        cp = m_maildir(argfolder);
330        if (access (cp, F_OK) == NOTOK)
331            adios (cp, "unable to find folder");
332        context_replace (pfolder, argfolder);   /* update current folder   */
333        context_save ();                /* save the context file   */
334        argfolder = NULL;
335    }
336
337    /* Listing the folder stack */
338    if (listsw) {
339        printf ("%s", argfolder ? argfolder : getfolder (1));
340        if ((cp = context_find (stack))) {
341            dp = getcpy (cp);
342            for (ap = brkstring (dp, " ", "\n"); *ap; ap++)
343                printf (" %s", *ap);
344            free (dp);
345        }
346        printf ("\n");
347
348        if (!printsw)
349            done (0);
350    }
351
352    /* Allocate initial space to record folder names */
353    maxfolders = NUMFOLDERS;
354    if ((folds = malloc (maxfolders * sizeof(char *))) == NULL)
355        adios (NULL, "unable to allocate storage for folder names");
356
357    /* Allocate initial space to record folder information */
358    maxFolderInfo = NUMFOLDERS;
359    if ((fi = malloc (maxFolderInfo * sizeof(*fi))) == NULL)
360        adios (NULL, "unable to allocate storage for folder info");
361
362    /*
363     * Scan the folders
364     */
365    if (all || ftotal > 0) {
366        /*
367         * If no folder is given, do them all
368         */
369        if (!argfolder) {
370            if (msg)
371                admonish (NULL, "no folder given for message %s", msg);
372            readonly_folders (); /* do any readonly folders */
373            strncpy (folder, (cp = context_find (pfolder)) ? cp : "", sizeof(folder));
374            dodir (".");
375        } else {
376            strncpy (folder, argfolder, sizeof(folder));
377            if (get_folder_info (argfolder, msg)) {
378                context_replace (pfolder, argfolder);/* update current folder */
379                context_save ();                     /* save the context file */
380            }
381            /*
382             * Since recurse wasn't done in get_folder_info(),
383             * we still need to list all level-1 sub-folders.
384             */
385            if (!frecurse)
386                dodir (folder);
387        }
388    } else {
389        strncpy (folder, argfolder ? argfolder : getfolder (1), sizeof(folder));
390
391        /*
392         * Check if folder exists.  If not, then see if
393         * we should create it, or just exit.
394         */
395        if (stat (strncpy (buf, m_maildir (folder), sizeof(buf)), &st) == -1) {
396            if (errno != ENOENT)
397                adios (buf, "error on folder");
398            if (fcreat == 0) {
399                /* ask before creating folder */
400                cp = concat ("Create folder \"", buf, "\"? ", NULL);
401                if (!getanswer (cp))
402                    done (1);
403                free (cp);
404            } else if (fcreat == -1) {
405                /* do not create, so exit */
406                done (1);
407            }
408            if (!makedir (buf))
409                adios (NULL, "unable to create folder %s", buf);
410        }
411
412        if (get_folder_info (folder, msg) && argfolder) {
413            /* update current folder */
414            context_replace (pfolder, argfolder);
415            }
416    }
417
418    /*
419     * Print out folder information
420     */
421    print_folders();
422
423    context_save ();    /* save the context file */
424    done (0);
425}
426
427/*
428 * Base routine for scanning a folder
429 */
430
431static void
432dodir (char *dir)
433{
434    int i;
435    int os = start;
436    int of = foldp;
437    char buffer[BUFSIZ];
438
439    start = foldp;
440
441    /* change directory to base of nmh directory */
442    if (chdir (nmhdir) == NOTOK)
443        adios (nmhdir, "unable to change directory to");
444
445    addir (strncpy (buffer, dir, sizeof(buffer)));
446
447    for (i = start; i < foldp; i++) {
448        get_folder_info (folds[i], NULL);
449        fflush (stdout);
450    }
451
452    start = os;
453    foldp = of;
454}
455
456static int
457get_folder_info (char *fold, char *msg)
458{
459    int i, retval = 1;
460    char *mailfile;
461    struct msgs *mp = NULL;
462
463    i = total_folders++;
464
465    /*
466     * if necessary, reallocate the space
467     * for folder information
468     */
469    if (total_folders >= maxFolderInfo) {
470        maxFolderInfo += NUMFOLDERS;
471        if ((fi = realloc (fi, maxFolderInfo * sizeof(*fi))) == NULL)
472            adios (NULL, "unable to re-allocate storage for folder info");
473    }
474
475    fi[i].name   = fold;
476    fi[i].nummsg = 0;
477    fi[i].curmsg = 0;
478    fi[i].lowmsg = 0;
479    fi[i].hghmsg = 0;
480    fi[i].others = 0;
481    fi[i].error  = 0;
482
483    mailfile = m_maildir (fold);
484
485    if (!chdir (mailfile)) {
486        if ((ftotal > 0) || !fshort || msg || fpack) {
487            /*
488             * create message structure and get folder info
489             */
490            if (!(mp = folder_read (fold))) {
491                admonish (NULL, "unable to read folder %s", fold);
492                return 0;
493            }
494
495            /* set the current message */
496            if (msg && !sfold (mp, msg))
497                retval = 0;
498
499            if (fpack) {
500                if (folder_pack (&mp, fverb) == -1)
501                    done (1);
502                seq_save (mp);          /* synchronize the sequences */
503                context_save ();        /* save the context file     */
504            }
505
506            /* record info for this folder */
507            if ((ftotal > 0) || !fshort) {
508                fi[i].nummsg = mp->nummsg;
509                fi[i].curmsg = mp->curmsg;
510                fi[i].lowmsg = mp->lowmsg;
511                fi[i].hghmsg = mp->hghmsg;
512                fi[i].others = other_files (mp);
513            }
514
515            folder_free (mp); /* free folder/message structure */
516        }
517    } else {
518        fi[i].error = 1;
519    }
520
521    if (frecurse && (fshort || fi[i].others) && (fi[i].error == 0))
522        dodir (fold);
523    return retval;
524}
525
526/*
527 * Print folder information
528 */
529
530static void
531print_folders (void)
532{
533    int i, len, hasempty = 0, curprinted;
534    int maxlen = 0, maxnummsg = 0, maxlowmsg = 0;
535    int maxhghmsg = 0, maxcurmsg = 0, total_msgs = 0;
536    int nummsgdigits, lowmsgdigits;
537    int hghmsgdigits, curmsgdigits;
538    char tmpname[BUFSIZ];
539
540    /*
541     * compute a few values needed to for
542     * printing various fields
543     */
544    for (i = 0; i < total_folders; i++) {
545        /* length of folder name */
546        len = strlen (fi[i].name);
547        if (len > maxlen)
548            maxlen = len;
549
550        /* If folder has error, skip the rest */
551        if (fi[i].error)
552            continue;
553
554        /* calculate total number of messages */
555        total_msgs += fi[i].nummsg;
556
557        /* maximum number of messages */
558        if (fi[i].nummsg > maxnummsg)
559            maxnummsg = fi[i].nummsg;
560
561        /* maximum low message */
562        if (fi[i].lowmsg > maxlowmsg)
563            maxlowmsg = fi[i].lowmsg;
564
565        /* maximum high message */
566        if (fi[i].hghmsg > maxhghmsg)
567            maxhghmsg = fi[i].hghmsg;
568
569        /* maximum current message */
570        if (fi[i].curmsg >= fi[i].lowmsg &&
571            fi[i].curmsg <= fi[i].hghmsg &&
572            fi[i].curmsg > maxcurmsg)
573            maxcurmsg = fi[i].curmsg;
574
575        /* check for empty folders */
576        if (fi[i].nummsg == 0)
577            hasempty = 1;
578    }
579    nummsgdigits = num_digits (maxnummsg);
580    lowmsgdigits = num_digits (maxlowmsg);
581    hghmsgdigits = num_digits (maxhghmsg);
582    curmsgdigits = num_digits (maxcurmsg);
583
584    if (hasempty && nummsgdigits < 2)
585        nummsgdigits = 2;
586
587    /*
588     * Print the header
589     */
590    if (fheader > 0 || (all && !fshort && fheader >= 0))
591        printf ("%-*s %*s %-*s; %-*s %*s\n",
592                maxlen+1, "FOLDER",
593                nummsgdigits + 13, "# MESSAGES",
594                lowmsgdigits + hghmsgdigits + 4, " RANGE",
595                curmsgdigits + 4, "CUR",
596                9, "(OTHERS)");
597
598    /*
599     * Print folder information
600     */
601    if (all || fshort || ftotal < 1) {
602        for (i = 0; i < total_folders; i++) {
603            if (fshort) {
604                printf ("%s\n", fi[i].name);
605                continue;
606            }
607
608            /* Add `+' to end of name, if folder is current */
609            if (strcmp (folder, fi[i].name))
610                snprintf (tmpname, sizeof(tmpname), "%s", fi[i].name);
611            else
612                snprintf (tmpname, sizeof(tmpname), "%s+", fi[i].name);
613
614            if (fi[i].error) {
615                printf ("%-*s is unreadable\n", maxlen+1, tmpname);
616                continue;
617            }
618
619            printf ("%-*s ", maxlen+1, tmpname);
620
621            curprinted = 0; /* remember if we print cur */
622            if (fi[i].nummsg == 0) {
623                printf ("has %*s messages%*s",
624                        nummsgdigits, "no",
625                        fi[i].others ? lowmsgdigits + hghmsgdigits + 5 : 0, "");
626            } else {
627                printf ("has %*d message%s  (%*d-%*d)",
628                        nummsgdigits, fi[i].nummsg,
629                        (fi[i].nummsg == 1) ? " " : "s",
630                        lowmsgdigits, fi[i].lowmsg,
631                        hghmsgdigits, fi[i].hghmsg);
632                if (fi[i].curmsg >= fi[i].lowmsg && fi[i].curmsg <= fi[i].hghmsg) {
633                    curprinted = 1;
634                    printf ("; cur=%*d", curmsgdigits, fi[i].curmsg);
635                }
636            }
637
638            if (fi[i].others)
639                printf (";%*s (others)", curprinted ? 0 : curmsgdigits + 6, "");
640            printf (".\n");
641        }
642    }
643
644    /*
645     * Print folder/message totals
646     */
647    if (ftotal > 0 || (all && !fshort && ftotal >= 0)) {
648        if (all)
649            printf ("\n");
650        printf ("TOTAL = %d message%c in %d folder%s.\n",
651                total_msgs, total_msgs != 1 ? 's' : ' ',
652                total_folders, total_folders != 1 ? "s" : "");
653    }
654
655    fflush (stdout);
656}
657
658/*
659 * Calculate the number of digits in a nonnegative integer
660 */
661int
662num_digits (int n)
663{
664    int ndigits = 0;
665
666    /* Sanity check */
667    if (n < 0)
668        adios (NULL, "oops, num_digits called with negative value");
669
670    if (n == 0)
671        return 1;
672
673    while (n) {
674        n /= 10;
675        ndigits++;
676    }
677
678    return ndigits;
679}
680
681/*
682 * Set the current message and sychronize sequences
683 */
684
685static int
686sfold (struct msgs *mp, char *msg)
687{
688    /* parse the message range/sequence/name and set SELECTED */
689    if (!m_convert (mp, msg))
690        return 0;
691
692    if (mp->numsel > 1) {
693        admonish (NULL, "only one message at a time!");
694        return 0;
695    }
696    seq_setprev (mp);           /* set the previous-sequence     */
697    seq_setcur (mp, mp->lowsel);/* set current message           */
698    seq_save (mp);              /* synchronize message sequences */
699    context_save ();            /* save the context file         */
700
701    return 1;
702}
703
704
705static void
706addir (char *name)
707{
708    int nlink;
709    char *base, *cp;
710    struct stat st;
711    struct dirent *dp;
712    DIR * dd;
713
714    cp = name + strlen (name);
715    *cp++ = '/';
716    *cp = '\0';
717
718    /*
719     * A hack to skip over a leading
720     * "./" in folder names.
721     */
722    base = strcmp (name, "./") ? name : name + 2;
723
724   /* short-cut to see if directory has any sub-directories */
725    if (stat (name, &st) != -1 && st.st_nlink == 2)
726        return;
727 
728    if (!(dd = opendir (name))) {
729        admonish (name, "unable to read directory ");
730        return;
731    }
732
733    /*
734     * Keep track of the number of directories we've seen
735     * so we can quit stat'ing early, if we've seen them all.
736     */
737    nlink = st.st_nlink;
738
739    while (nlink && (dp = readdir (dd))) {
740        if (!strcmp (dp->d_name, ".") || !strcmp (dp->d_name, "..")) {
741            nlink--;
742            continue;
743        }
744        if (cp + NLENGTH(dp) + 2 >= name + BUFSIZ)
745            continue;
746        strcpy (cp, dp->d_name);
747        if (stat (name, &st) != -1 && S_ISDIR(st.st_mode)) {
748            /*
749             * Check if this was really a symbolic link pointing at
750             * a directory.  If not, then decrement link count.
751             */
752            if (lstat (name, &st) == -1)
753                nlink--;
754            addfold (base);
755        }
756    }
757
758    closedir (dd);
759    *--cp = '\0';
760}
761
762/*
763 * Add the folder name into the
764 * list in a sorted fashion.
765 */
766
767static void
768addfold (char *fold)
769{
770    register int i, j;
771    register char *cp;
772
773    /* if necessary, reallocate the space for folder names */
774    if (foldp >= maxfolders) {
775        maxfolders += NUMFOLDERS;
776        if ((folds = realloc (folds, maxfolders * sizeof(char *))) == NULL)
777            adios (NULL, "unable to re-allocate storage for folder names");
778    }
779
780    cp = getcpy (fold);
781    for (i = start; i < foldp; i++)
782        if (compare (cp, folds[i]) < 0) {
783            for (j = foldp - 1; j >= i; j--)
784                folds[j + 1] = folds[j];
785            foldp++;
786            folds[i] = cp;
787            return;
788        }
789
790    folds[foldp++] = cp;
791}
792
793
794static int
795compare (char *s1, char *s2)
796{
797    register int i;
798
799    while (*s1 || *s2)
800        if ((i = *s1++ - *s2++))
801            return i;
802
803    return 0;
804}
805
806/*
807 * Do the read only folders
808 */
809
810static void
811readonly_folders (void)
812{
813    int atrlen;
814    char atrcur[BUFSIZ];
815    register struct node *np;
816
817    /* sanity check - check that context has been read */
818    if (defpath == NULL)
819        adios (NULL, "oops, context hasn't been read yet");
820
821    snprintf (atrcur, sizeof(atrcur), "atr-%s-", current);
822    atrlen = strlen (atrcur);
823
824    for (np = m_defs; np; np = np->n_next)
825        if (ssequal (atrcur, np->n_name)
826                && !ssequal (nmhdir, np->n_name + atrlen))
827            get_folder_info (np->n_name + atrlen, NULL);
828}
Note: See TracBrowser for help on using the repository browser.