/* sort.c 3.0 (c) copyright 1986,1990 (Dan Heller) */ #include "mush.h" /* #define MYQSORT */ /* The size of this array should really be bounded by * 2 spaces for each possible different sort criteria * (one space for each key letter and one per for 'r'), * but 16 leaves room to add to the current list. */ static char subsort[16]; static int depth, order, ignore_case; static jmp_buf sortbuf; sort(argc, argv, list) register int argc; register char *argv[], list[]; { int msg_cmp(); SIGRET (*oldint)(), (*oldquit)(); int n, offset = -1, range = 0; long curr_msg_off = msg[current_msg].m_offset; depth = 0, order = 1, ignore_case = FALSE; while (argc && *++argv) { n = (argv[0][0] == '-' && argv[0][1] != 0); while (argv[0][n]) { if (depth > sizeof subsort - 2) break; switch(argv[0][n]) { case '-': /* reverse order of next criteria (obsolete) */ argv[0][n] = 'r'; /* fix it and fall through */ case 'r': /* reverse order of next criteria */ case 'd': /* sort by date */ case 'a': /* sort by author (address) */ case 's': /* sort by subject (ignore Re:) */ case 'R': /* sort by subject including Re: */ case 'l': /* sort by length in bytes */ case 'S': /* sort by message status */ case 'p': /* sort by message priority */ /* skip consecutive repeats of the same flag */ if (depth < 1 || subsort[depth-1] != argv[0][n]) subsort[depth++] = argv[0][n]; when 'i': ignore_case = TRUE; otherwise: return help(0, "sort", cmd_help); } n++; } } if (depth == 0 || subsort[depth-1] == 'r') subsort[depth++] = 'S'; /* status sort is the default */ subsort[depth] = 0; depth = 0; /* start at the beginning */ if (msg_cnt <= 1) { print("Not enough messages to sort.\n"); return -1; } turnon(glob_flags, IGN_SIGS); on_intr(); if (list && ison(glob_flags, IS_PIPE)) { int consec = 1; for (n = 0; n < msg_cnt; n++) if (msg_bit(list, n)) { if (!consec) { ok_box("Listed messages not consecutive\n"); turnoff(glob_flags, IGN_SIGS); off_intr(); return -1; } if (offset < 0) offset = n; range++; } else if (offset >= 0) consec = 0; } else offset = 0, range = msg_cnt; if (range < 2) print("Range not broad enough to sort anything\n"); else { Debug("Sorting %d messages starting at message %d\n", range, offset+1); if (setjmp(sortbuf) == 0) qsort((char *)&msg[offset], range, sizeof (struct msg), msg_cmp); else print("WARNING: Sorting interrupted: unpredictable order.\n"); turnon(glob_flags, DO_UPDATE); } for (n = 0; n < msg_cnt; n++) if (msg[n].m_offset == curr_msg_off) break; current_msg = n; turnoff(glob_flags, IGN_SIGS); off_intr(); /* Break pipes because message lists are invalid */ return 0 - in_pipe(); } #ifdef MYQSORT qsort(base, len, siz, compar) register struct msg *base; int (*compar)(); { register int i, swapping; struct msg temp; do { swapping = 0; for (i = 0; i < len-1; ++i) { if (compar(base+i, base+i+1) > 0) { temp = base[i]; base[i] = base[i+1]; base[i+1] = temp; swapping = 1; } } } while (swapping); } #endif /* MYSORT */ status_cmp(msg1, msg2) register struct msg *msg1, *msg2; { if (msg1->m_flags == msg2->m_flags) return msg_cmp(msg1, msg2); if (ison(msg1->m_flags, DELETE) && isoff(msg2->m_flags, DELETE)) return order; if (isoff(msg1->m_flags, DELETE) && ison(msg2->m_flags, DELETE)) return -order; if (isoff(msg1->m_flags, OLD) && ison(msg2->m_flags, OLD)) return -order; if (ison(msg1->m_flags, OLD) && isoff(msg2->m_flags, OLD)) return order; if (ison(msg1->m_flags, UNREAD) && isoff(msg2->m_flags, UNREAD)) return -order; if (isoff(msg1->m_flags, UNREAD) && ison(msg2->m_flags, UNREAD)) return order; if (ison(msg1->m_flags,PRESERVE) && isoff(msg2->m_flags,PRESERVE)) return -order; if (isoff(msg1->m_flags,PRESERVE) && ison(msg2->m_flags,PRESERVE)) return order; if (ison(msg1->m_flags,REPLIED) && isoff(msg2->m_flags,REPLIED)) return -order; if (isoff(msg1->m_flags,REPLIED) && ison(msg2->m_flags,REPLIED)) return order; if (ison(msg1->m_flags,SAVED) && isoff(msg2->m_flags,SAVED)) return -order; if (isoff(msg1->m_flags,SAVED) && ison(msg2->m_flags,SAVED)) return order; if (ison(msg1->m_flags,PRINTED) && isoff(msg2->m_flags,PRINTED)) return -order; if (isoff(msg1->m_flags,PRINTED) && ison(msg2->m_flags,PRINTED)) return order; if (ison(msg1->m_flags,FORWARD) && isoff(msg2->m_flags,FORWARD)) return -order; if (isoff(msg1->m_flags,FORWARD) && ison(msg2->m_flags,FORWARD)) return order; return pri_cmp(msg1, msg2); } author_cmp(msg1, msg2) register struct msg *msg1, *msg2; { char buf1[HDRSIZ], buf2[HDRSIZ]; int retval; (void) reply_to(msg1 - msg, 0, buf1); /* "0" for "author only" */ (void) reply_to(msg2 - msg, 0, buf2); Debug("author: msg %d: %s, msg %d: %s\n", msg1-msg, buf1, msg2-msg, buf2); if (ignore_case) retval = lcase_strncmp(buf1, buf2, -1) * order; else retval = strcmp(buf1, buf2) * order; return retval ? retval : msg_cmp(msg1, msg2); } /* compare messages according to size (length) */ size_cmp(msg1, msg2) register struct msg *msg1, *msg2; { int retval; Debug("sizes: (%d): %d, (%d): %d\"\n", msg1-msg, msg1->m_size, msg2-msg, msg2->m_size); if (retval = (msg1->m_size - msg2->m_size) * order) /* assign and test */ return retval; return msg_cmp(msg1, msg2); } /* * Subject comparison ignoring Re: subject_to() appends an Re: if there is * any subject whatsoever. */ subject_cmp(msg1, msg2) register struct msg *msg1, *msg2; { char buf1[HDRSIZ], buf2[HDRSIZ]; register char *p1, *p2; int retval; p1 = subject_to(msg1 - msg, buf1); p2 = subject_to(msg2 - msg, buf2); if (p1) { p1 += 4; while (isspace(*p1)) p1++; } else p1 = buf1; /* subject_to() makes it an empty string */ if (p2) { p2 += 4; while (isspace(*p2)) p2++; } else p2 = buf2; /* subject_to() makes it an empty string */ Debug("subjects: (%d): \"%s\" (%d): \"%s\"\n", msg1-msg, p1, msg2-msg, p2); if (ignore_case) retval = lcase_strncmp(p1, p2, -1) * order; else retval = strcmp(p1, p2) * order; return retval ? retval : msg_cmp(msg1, msg2); } /* * compare subject strings from two messages. * If Re is appended, so be it -- if user wants to ignore Re: use 'R' flag. */ subj_with_re(msg1, msg2) register struct msg *msg1, *msg2; { char buf1[HDRSIZ], buf2[HDRSIZ], *p; int retval; if (!(p = header_field(msg1 - msg, "subject"))) p = ""; (void) strcpy(buf1, p); if (!(p = header_field(msg2 - msg, "subject"))) p = ""; (void) strcpy(buf2, p); Debug("subjects: (%d): \"%s\" (%d): \"%s\"\n", msg1-msg, buf1, msg2-msg, buf2); if (ignore_case) retval = lcase_strncmp(buf1, buf2, -1) * order; else retval = strcmp(buf1, buf2) * order; return retval ? retval : msg_cmp(msg1, msg2); } date_cmp(msg1, msg2) register struct msg *msg1, *msg2; { long tm1, tm2; if (ison(glob_flags, DATE_RECV)) { (void) sscanf(msg1->m_date_recv, "%ld", &tm1); (void) sscanf(msg2->m_date_recv, "%ld", &tm2); } else { (void) sscanf(msg1->m_date_sent, "%ld", &tm1); (void) sscanf(msg2->m_date_sent, "%ld", &tm2); } return tm1 < tm2 ? -order : (tm1 > tm2) ? order : msg_cmp(msg1, msg2); } pri_cmp(msg1, msg2) register struct msg *msg1, *msg2; { int i; u_long pr1 = 0, pr2 = 0; for (i = 0; pr1 == pr2 && i <= MAX_PRIORITY; i++) { if (ison(msg1->m_flags, M_PRIORITY(i))) turnon(pr1, ULBIT(i)); if (ison(msg2->m_flags, M_PRIORITY(i))) turnon(pr2, ULBIT(i)); } return pr1 > pr2 ? -order : (pr1 < pr2) ? order : msg_cmp(msg1, msg2); } static msg_cmp(msg1, msg2) register struct msg *msg1, *msg2; { int sv_order = order, sv_depth = depth, retval = 0; if (ison(glob_flags, WAS_INTR)) longjmp(sortbuf, 1); if (msg1 < msg || msg2 < msg) { wprint("sort botch trying to sort %d and %d using %s\n", msg1-msg, msg2-msg, subsort); return 0; } if (subsort[depth] == 'r') { order = -1; depth++; } else order = 1; switch(subsort[depth++]) { case '\0': retval = 0; when 'd': retval = date_cmp(msg1, msg2); when 'a': retval = author_cmp(msg1, msg2); when 's': retval = subject_cmp(msg1, msg2); when 'R': retval = subj_with_re(msg1, msg2); when 'l': retval = size_cmp(msg1, msg2); /* length compare */ when 'p': retval = pri_cmp(msg1, msg2); otherwise: retval = status_cmp(msg1, msg2); } depth = sv_depth; order = sv_order; return retval; }