|
|
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.