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