|
|
1.1 root 1: /* sortm.c - sort messages in a folder by date/time */
2:
3: #include "../h/mh.h"
4: #include "../zotnet/tws.h"
5: #include <stdio.h>
6: #include <sys/types.h>
7: #include <sys/stat.h>
8: #include <ctype.h>
9:
10: static struct swit switches[] = {
11: #define DATESW 0
12: "datefield field", 0,
13:
14: #define VERBSW 1
15: "verbose", 0,
16: #define NVERBSW 2
17: "noverbose", 0,
18:
19: #define SUBJSW 3
20: "subject", 0,
21:
22: #define LIMSW 4
23: "limit days", 0,
24:
25: #define HELPSW 5
26: "help", 4,
27:
28: NULL, NULL
29: };
30:
31: struct smsg {
32: int s_msg;
33: unsigned long s_clock;
34: char *s_subj;
35: };
36:
37: static struct smsg *smsgs;
38: int nmsgs;
39:
40: int subjsort; /* sort on subject if != 0 */
41: u_long datelimit = ~0;
42: int verbose;
43:
44: static
45: getws (datesw, msg, smsg)
46: register char *datesw;
47: int msg;
48: register struct smsg *smsg;
49: {
50: int compnum;
51: register int state;
52: char *msgnam;
53: char buf[BUFSIZ], nam[NAMESZ];
54: register struct tws *tw;
55: register char *datecomp = NULLCP;
56: register char *subjcomp = NULLCP;
57: register FILE *in;
58:
59: if ((in = fopen (msgnam = m_name (msg), "r")) == NULL) {
60: admonish (msgnam, "unable to read message");
61: return (0);
62: }
63: for (compnum = 1, state = FLD;;) {
64: switch (state = m_getfld (state, nam, buf, sizeof buf, in)) {
65: case FLD:
66: case FLDEOF:
67: case FLDPLUS:
68: compnum++;
69: if (uleq (nam, datesw)) {
70: datecomp = add (buf, datecomp);
71: while (state == FLDPLUS) {
72: state = m_getfld (state, nam, buf, sizeof buf, in);
73: datecomp = add (buf, datecomp);
74: }
75: if (!subjsort || subjcomp)
76: break;
77: } else if (subjsort && uleq (nam, "subject")) {
78: subjcomp = add (buf, subjcomp);
79: while (state == FLDPLUS) {
80: state = m_getfld (state, nam, buf, sizeof buf, in);
81: subjcomp = add (buf, subjcomp);
82: }
83: if (datecomp)
84: break;
85: } else {
86: /* just flush this guy */
87: while (state == FLDPLUS)
88: state = m_getfld (state, nam, buf, sizeof buf, in);
89: }
90: continue;
91:
92: case BODY:
93: case BODYEOF:
94: case FILEEOF:
95: break;
96:
97: case LENERR:
98: case FMTERR:
99: if (state == LENERR || state == FMTERR)
100: admonish (NULLCP,
101: "format error in message %d (header #%d)",
102: msg, compnum);
103: if (datecomp)
104: free (datecomp);
105: if (subjcomp)
106: free (subjcomp);
107: (void) fclose (in);
108: return (0);
109:
110: default:
111: adios (NULLCP, "internal error -- you lose");
112: }
113: break;
114: }
115:
116: if (!datecomp || (tw = dparsetime (datecomp)) == NULL) {
117: struct stat st;
118:
119: admonish (NULLCP, "can't parse %s field in message %d",
120: datesw, msg);
121:
122: /* use the modify time of the file as its date */
123: (void) fstat (fileno (in), &st);
124: smsg->s_clock = st.st_mtime;
125: } else
126: smsg->s_clock = twclock (tw);
127:
128: if (subjsort) {
129: if (subjcomp) {
130: /*
131: * try to make the subject "canonical": delete
132: * leading "re:", everything but letters & smash
133: * letters to lower case.
134: */
135: register char *cp;
136: register char *cp2;
137: register char c;
138:
139: cp = subjcomp;
140: cp2 = subjcomp;
141: while (c = *cp++) {
142: if (! isspace(c)) {
143: if ((c == 'r' || c == 'R') &&
144: (cp[0] == 'e' || cp[0] == 'E') &&
145: cp[1] == ':')
146: cp += 2;
147: else {
148: if (isalpha(c))
149: *cp2++ = islower(c)?
150: c : tolower(c);
151: break;
152: }
153: }
154: }
155: while (c = *cp++) {
156: if (isalpha(c))
157: *cp2++ = islower(c)? c : tolower(c);
158:
159: }
160: *cp2 = '\0';
161: } else
162: subjcomp = "";
163:
164: smsg->s_subj = subjcomp;
165: }
166: (void) fclose (in);
167: if (datecomp)
168: free (datecomp);
169:
170: return (1);
171: }
172:
173: static int
174: read_hdrs (mp, datesw)
175: register struct msgs *mp;
176: register char *datesw;
177: {
178: int msgnum;
179: struct tws tb;
180: register struct smsg *s;
181:
182: twscopy (&tb, dtwstime ());
183:
184: smsgs = (struct smsg *)
185: calloc ((unsigned) (mp->hghsel - mp->lowsel + 2),
186: sizeof *smsgs);
187: if (smsgs == NULL)
188: adios (NULLCP, "unable to allocate sort storage");
189:
190: s = smsgs;
191: for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
192: if (mp->msgstats[msgnum] & SELECTED) {
193: if (getws (datesw, msgnum, s)) {
194: s->s_msg = msgnum;
195: s++;
196: }
197: }
198: }
199: s->s_msg = 0;
200: return(s - smsgs);
201: }
202:
203: /*
204: * sort on dates.
205: */
206: static int
207: dsort (a, b)
208: register struct smsg **a, **b;
209: {
210: if ((*a)->s_clock < (*b)->s_clock)
211: return (-1);
212: else if ((*a)->s_clock > (*b)->s_clock)
213: return (1);
214: else if ((*a)->s_msg < (*b)->s_msg)
215: return (-1);
216: else
217: return (1);
218: }
219:
220: /*
221: * sort on subjects.
222: */
223: static int
224: subsort (a, b)
225: register struct smsg **a, **b;
226: {
227: register int i;
228:
229: if (i = strcmp ((*a)->s_subj, (*b)->s_subj))
230: return (i);
231:
232: return (dsort (a, b));
233: }
234:
235: static void
236: rename_chain (mp, mlist, msg, endmsg)
237: register struct msgs *mp;
238: struct smsg **mlist;
239: int msg, endmsg;
240: {
241: int nxt, old, new;
242: char *newname;
243: char oldname[BUFSIZ];
244:
245: nxt = mlist[msg] - smsgs;
246: mlist[msg] = 0;
247: old = smsgs[nxt].s_msg;
248: new = smsgs[msg].s_msg;
249: (void) strcpy (oldname, m_name (old));
250: newname = m_name (new);
251: if (verbose)
252: printf (" %s becomes %s\n", oldname, newname);
253:
254: if (rename (oldname, newname) == NOTOK)
255: adios (newname, "unable to rename %s to", oldname);
256:
257: mp->msgstats[new] = mp->msgstats[old];
258: if (mp->curmsg == old)
259: m_setcur (mp, new);
260:
261: if (nxt != endmsg)
262: rename_chain (mp, mlist, nxt, endmsg);
263: }
264:
265: static void
266: rename_msgs (mp, mlist)
267: register struct msgs *mp;
268: register struct smsg **mlist;
269: {
270: register int i, j, old, new;
271: register struct smsg *sp;
272: long stats;
273: char f1[BUFSIZ], f2[BUFSIZ], tmpfil[BUFSIZ];
274:
275: (void) strcpy (tmpfil, m_scratch ("", invo_name));
276:
277: for (i = 0; i < nmsgs; i++) {
278: if (! (sp = mlist[i]))
279: continue; /* did this one */
280:
281: j = sp - smsgs;
282: if (j == i)
283: continue; /* this one doesn't move */
284:
285: /*
286: * the guy that was msg j is about to become msg i.
287: * rename 'j' to make a hole, then recursively rename
288: * guys to fill up the hole.
289: */
290: old = smsgs[j].s_msg;
291: new = smsgs[i].s_msg;
292: (void) strcpy (f1, m_name (old));
293:
294: if (verbose)
295: printf ("renaming chain from %d to %d\n", old, new);
296:
297: if (rename (f1, tmpfil) == NOTOK)
298: adios (tmpfil, "unable to rename %s to ", f1);
299: stats = mp->msgstats[old];
300:
301: rename_chain (mp, mlist, j, i);
302: if (rename (tmpfil, m_name(new)) == NOTOK)
303: adios (m_name(new), "unable to rename %s to", tmpfil);
304:
305: mp->msgstats[new] = stats;
306: mp->msgflags |= SEQMOD;
307: }
308: }
309:
310: /* ARGSUSED */
311: main (argc, argv)
312: int argc;
313: char **argv;
314: {
315: int msgp = 0;
316: int i;
317: int msgnum;
318: char *cp;
319: char *maildir;
320: char *datesw = NULL;
321: char *folder = NULL;
322: char buf[100];
323: char **ap;
324: char **argp;
325: char *arguments[MAXARGS];
326: char *msgs[MAXARGS];
327: struct msgs *mp;
328: struct smsg **dlist;
329:
330: invo_name = r1bindex (argv[0], '/');
331: if ((cp = m_find (invo_name)) != NULL) {
332: ap = brkstring (cp = getcpy (cp), " ", "\n");
333: ap = copyip (ap, arguments);
334: } else
335: ap = arguments;
336: (void) copyip (argv + 1, ap);
337: argp = arguments;
338:
339: while (cp = *argp++) {
340: if (*cp == '-')
341: switch (smatch (++cp, switches)) {
342: case AMBIGSW:
343: ambigsw (cp, switches);
344: done (1);
345: case UNKWNSW:
346: adios (NULLCP, "-%s unknown", cp);
347: case HELPSW:
348: (void) sprintf(buf,
349: "%s [+folder] [msgs] [switches]",
350: invo_name);
351: help (buf, switches);
352: done (1);
353:
354: case DATESW:
355: if (datesw)
356: adios (NULLCP,
357: "only one date field at a time");
358: if (!(datesw = *argp++) || *datesw == '-')
359: adios (NULLCP, "missing argument to %s", argp[-2]);
360: continue;
361:
362: case SUBJSW:
363: subjsort = 1;
364: continue;
365:
366: case LIMSW:
367: if (!(cp = *argp++) || *cp == '-')
368: adios (NULLCP, "missing argument to %s", argp[-2]);
369: if (! isdigit(*cp) || !(datelimit = atoi(cp)))
370: adios (NULLCP,
371: "non-zero number must follow %s",
372: argp[-2]);
373: datelimit *= 60*60*24;
374: continue;
375:
376: case VERBSW:
377: verbose++;
378: continue;
379: case NVERBSW:
380: verbose = 0;
381: continue;
382: }
383: if (*cp == '+' || *cp == '@') {
384: if (folder)
385: adios (NULLCP, "only one folder at a time!");
386: else
387: folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
388: } else
389: msgs[msgp++] = cp;
390: }
391:
392: if (!m_find ("path"))
393: free (path ("./", TFOLDER));
394: if (!msgp)
395: msgs[msgp++] = "all";
396: if (!datesw)
397: datesw = "date";
398: if (!folder)
399: folder = m_getfolder ();
400: maildir = m_maildir (folder);
401:
402: if (chdir (maildir) == NOTOK)
403: adios (maildir, "unable to change directory to");
404: if (!(mp = m_gmsg (folder)))
405: adios (NULLCP, "unable to read folder %s", folder);
406: if (mp->hghmsg == 0)
407: adios (NULLCP, "no messages in %s", folder);
408:
409: for (msgnum = 0; msgnum < msgp; msgnum++)
410: if (!m_convert (mp, msgs[msgnum]))
411: done (1);
412: m_setseq (mp);
413:
414: if ((nmsgs = read_hdrs (mp, datesw)) <= 0)
415: adios (NULLCP, "no messages to sort");
416:
417: /*
418: * sort a list of pointers to our "messages to be sorted".
419: */
420: dlist = (struct smsg **) malloc ((nmsgs+1) * sizeof(*dlist));
421: if (! dlist)
422: adios (NULLCP, "couldn't allocate sort memory");
423: for (i = 0; i < nmsgs; i++)
424: dlist[i] = &smsgs[i];
425: dlist[nmsgs] = 0;
426:
427: qsort ((char *) dlist, nmsgs, sizeof(*dlist), dsort);
428:
429: /*
430: * if we're sorting on subject, we need another list
431: * in subject order, then a merge pass to collate the
432: * two sorts.
433: */
434: if (subjsort) {
435: struct smsg **slist;
436: struct smsg **flist;
437: register struct smsg ***il;
438: register struct smsg **fp;
439: register struct smsg **dp;
440:
441: slist = (struct smsg **) malloc ((nmsgs+1) * sizeof(*slist));
442: if (! slist)
443: adios (NULLCP, "couldn't allocate sort memory");
444: bcopy ((char *)dlist, (char *)slist, (nmsgs+1)*sizeof(*slist));
445: qsort ((char *)slist, nmsgs, sizeof(*slist), subsort);
446:
447: /*
448: * make an inversion list so we can quickly find
449: * the collection of messages with the same subj
450: * given a message number.
451: */
452: il = (struct smsg ***) calloc (mp->hghsel+1, sizeof(*il));
453: if (! il)
454: adios (NULLCP, "couldn't allocate msg list");
455: for (i = 0; i < nmsgs; i++)
456: il[slist[i]->s_msg] = &slist[i];
457: /*
458: * make up the final list, chronological but with
459: * all the same subjects grouped together.
460: */
461: flist = (struct smsg **) malloc ((nmsgs+1) * sizeof(*flist));
462: if (! flist)
463: adios (NULLCP, "couldn't allocate msg list");
464: fp = flist;
465: for (dp = dlist; *dp;) {
466: register struct smsg **s = il[(*dp++)->s_msg];
467:
468: /* see if we already did this guy */
469: if (! s)
470: continue;
471:
472: *fp++ = *s++;
473: /*
474: * take the next message(s) if there is one,
475: * its subject isn't null and its subject
476: * is the same as this one and it's not too
477: * far away in time.
478: */
479: while (*s && (*s)->s_subj[0] &&
480: strcmp((*s)->s_subj, s[-1]->s_subj) == 0 &&
481: (*s)->s_clock - s[-1]->s_clock <= datelimit) {
482: il[(*s)->s_msg] = 0;
483: *fp++ = *s++;
484: }
485: }
486: *fp = 0;
487: (void) free (slist);
488: (void) free (dlist);
489: dlist = flist;
490: }
491: rename_msgs (mp, dlist);
492:
493: m_replace (pfolder, folder);
494: m_sync (mp);
495: m_update ();
496: done (0);
497: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.