home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / trn_12.zip / src / rt-select.c < prev    next >
C/C++ Source or Header  |  1993-12-05  |  25KB  |  1,026 lines

  1. /* $Id: rt-select.c,v 2.3 1992/12/14 00:14:12 davison Trn $
  2. */
  3.  
  4. #include "EXTERN.h"
  5. #include "common.h"
  6. #include "rn.h"
  7. #include "rcstuff.h"
  8. #include "term.h"
  9. #include "final.h"
  10. #include "util.h"
  11. #include "help.h"
  12. #include "bits.h"
  13. #include "artsrch.h"
  14. #include "ng.h"
  15. #include "ngdata.h"
  16. #include "ngstuff.h"
  17.  
  18. #ifdef USETHREADS
  19.  
  20. #include "threads.h"
  21. #include "rthreads.h"
  22.  
  23. static int count_subj_lines();
  24. static void display_subj();
  25.  
  26. /* When display mode is 'l', each author gets a separate line; when 'm', up to
  27. ** three authors share a line; when 's', no authors are displayed.
  28. */
  29. static char *display_mode = select_order;
  30. static ART_NUM article_count;
  31. static int author_line;
  32. static char first_two_chars[3] = { ' ', ' ', '\0' }, mask = 1;
  33.  
  34. #define MAX_SEL 64
  35.  
  36. /* Display a menu of roots for the user to choose from.  If cmd is '+'
  37. ** we display all the unread roots and allow the user to mark roots as
  38. ** selected and perform various commands upon the articles.  If cmd is
  39. ** 'U' we display all the previously read roots and allow the user to
  40. ** select which ones should be marked as unread.
  41. */
  42. char
  43. select_thread(cmd)
  44. char_int cmd;
  45. {
  46.     register int i, j, cnt;
  47.     ART_NUM art_hold = art;
  48.     int line_cnt, screen_line, subj_line_cnt;
  49.     int cur_root, page_root, last_root = -1;
  50.     ART_LINE running_total, last_running;
  51.     int last_line, got_dash;
  52.     int max_root;
  53.     int first, last;
  54.     int root_line[MAX_SEL], root_hold[MAX_SEL];
  55.     int ch, action;
  56.     char page_char, end_char;
  57.     char promptbuf[80];
  58.     bool etc, clean_screen, empty_ok, displayed_status;
  59.     char oldmode = mode;
  60. #ifndef CONDSUB
  61.     char tmpbuf[2];
  62. #endif
  63.     char *select_chars, *in_select;
  64.     int max_cnt;
  65.  
  66.     mode = 't';
  67.     unread_selector = (cmd == 'U');
  68.     clear_on_stop = TRUE;
  69.     empty_ok = FALSE;
  70.  
  71.     if (added_articles > 0)
  72.     select_page = 0;
  73.  
  74.   select_threads:
  75.     /* Setup for selecting articles to read or set unread */
  76.     scan_all_roots = FALSE;
  77.     if (unread_selector) {
  78.     page_char = '>';
  79.     end_char = 'Z';
  80.     page_root = 0;
  81.     last_root = -1;
  82.     cmd = 0;
  83.     } else {
  84.     page_char = page_select;
  85.     end_char = end_select;
  86.     page_root = select_page;
  87.     if (curr_p_art) {
  88.         last_root = curr_p_art->root;
  89.     }
  90.     }
  91.     mask = unread_selector+1;
  92.  
  93.     /* Leave empty roots selected for a short time to give them a chance
  94.     ** to Esc out of the selector if they got here by mistake.
  95.     */
  96.     max_root = count_roots(FALSE);
  97.  
  98.     /* If nothing to display, we're done. */
  99.     if (!article_count && !empty_ok) {
  100.      all_empty:
  101.     clear_on_stop = FALSE;
  102.     mode = oldmode;
  103.     putchar('\n');
  104.     if (unread_selector) {
  105. #ifdef VERBOSE
  106.         IF (verbose)
  107.         fputs("\nNo articles to set unread.\n", stdout);
  108.         ELSE
  109. #endif
  110. #ifdef TERSE
  111.         fputs("\nNo articles.\n", stdout) ; FLUSH;
  112. #endif
  113.         unread_selector = 0;
  114.         mask = 1;
  115.     } else {
  116. #ifdef VERBOSE
  117.         IF (verbose)
  118.         fputs("\nNo unread articles to select.", stdout);
  119.         ELSE
  120. #endif
  121. #ifdef TERSE
  122.         fputs("\nNo articles.", stdout);
  123. #endif
  124. #ifndef USETMPTHREAD
  125.         if (tobethreaded) {
  126.         printf("  (%d article%s not yet threaded)",
  127.             tobethreaded, tobethreaded == 1 ? nullstr : "s") ; FLUSH;
  128.         }
  129. #endif
  130.         putchar('\n');    /* let "them" ; FLUSH */
  131.     }
  132.     (void) count_roots(TRUE);
  133.     art = art_hold;
  134.     p_art = curr_p_art;
  135.     return '\033';
  136.     }
  137.     if (unread_selector) {
  138.     for (j = 0; j < total.root; j++) {
  139.         selected_roots[j] |= 4;
  140.     }
  141.     }
  142.     if (page_root >= max_root) {
  143.     ch = '<';
  144.     } else {
  145.     ch = '>';
  146.     }
  147.     cur_root = 0;
  148.     running_total = 0;
  149.     for (i = 0; i < page_root; i++) {
  150.     running_total += root_article_cnts[i];
  151.     }
  152.     do {
  153.     select_chars = getval("SELECTCHARS", SELECTCHARS);
  154.     max_cnt = strlen(select_chars);
  155.     if (max_cnt > MAX_SEL) {
  156.         max_cnt = MAX_SEL;
  157.     }
  158.     if (ch == '<' && i) {
  159.         screen_line = 2;
  160.         cnt = 0;
  161.         /* Scan the roots in reverse to go back a page */
  162.         do {
  163.         if (!root_article_cnts[--i]) {
  164.             continue;
  165.         }
  166.         first = root_subjects[i];
  167.         last = first + p_roots[i].subject_cnt;
  168.         line_cnt = 0;
  169.         for (j = first; j < last; j++) {
  170.             line_cnt += count_subj_lines(i, j);
  171.         }
  172.         if (line_cnt > LINES - 5) {
  173.             line_cnt = LINES - 5;
  174.         }
  175.         screen_line += line_cnt;
  176.         if (screen_line > LINES - 3) {
  177.             i++;
  178.             break;
  179.         }
  180.         running_total -= root_article_cnts[i];
  181.         cnt++;
  182.         } while (i > 0 && cnt < max_cnt);
  183.     }
  184.  
  185.     /* Present a page of subjects to the user */
  186. #ifndef CLEAREOL
  187.     clear();
  188. #else
  189.     if (can_home_clear) {
  190.         home_cursor();
  191.         maybe_eol();
  192.     } else {
  193.         clear();
  194.     }
  195. #endif
  196.     carriage_return();
  197.     page_root = i;
  198.     last_running = running_total;
  199. #ifdef NOFIREWORKS
  200.     no_sofire();
  201. #endif
  202.     standout();
  203.     fputs(ngname, stdout);
  204.     un_standout();
  205.     printf("          %ld %sarticle%s%s\n", (long)article_count,
  206.         unread_selector? "read " : nullstr,
  207.         article_count == 1 ? nullstr : "s", moderated);
  208. #ifdef CLEAREOL
  209.     maybe_eol();
  210. #endif
  211.     putchar('\n') ; FLUSH;
  212.     screen_line = 2;
  213.     for (cnt = 0; i < max_root && cnt < max_cnt; i++) {
  214.         if (last_root == i) {
  215.         cur_root = cnt;
  216.         }
  217.         /* Check each root for articles to list */
  218.         if (!root_article_cnts[i]) {
  219.         continue;
  220.         }
  221.         first = root_subjects[i];
  222.         last = first + p_roots[i].subject_cnt;
  223.  
  224.         /* Compute how many lines we need to display the subjects/authors */
  225.         etc = FALSE;
  226.         line_cnt = 0;
  227.         for (j = first; j < last; j++) {
  228.         subj_line_cnt = count_subj_lines(i, j);
  229.         line_cnt += subj_line_cnt;
  230.         /* If this root is too long to fit on the screen all by
  231.         ** itself, trim it to fit and set the "etc" flag.
  232.         */
  233.         if (line_cnt > LINES - 5) {
  234.             last = j;
  235.             line_cnt -= subj_line_cnt;
  236.             if (line_cnt != LINES - 5) {
  237.             last++;
  238.             line_cnt = LINES - 5;
  239.             }
  240.             if (screen_line == 2) {
  241.             etc = TRUE;
  242.             }
  243.             break;
  244.         }
  245.         }
  246.         /* If it doesn't fit, save it for the next page */
  247.         if (screen_line + line_cnt > LINES - 3) {
  248.         break;
  249.         }
  250.         /* Output the subjects, with optional authors */
  251.         root_line[cnt] = screen_line;
  252.         running_total += root_article_cnts[i];
  253.         first_two_chars[0] = select_chars[cnt];
  254.         first_two_chars[1] = (selected_roots[i] & 4) ? '-' :
  255.                  (selected_roots[i] & mask) ? '+' : ' ';
  256.         author_line = screen_line;
  257.         for (j = first; j < last; j++) {
  258.         display_subj(i, j);
  259.         }
  260.         screen_line += line_cnt;
  261.         root_hold[cnt++] = i;
  262.         if (etc) {
  263.         fputs("      ...etc.", stdout);
  264.         i++;
  265.         break;
  266.         }
  267.     }/* for */
  268.     last_root = -1;
  269.     if (cur_root && cur_root >= cnt) {
  270.         cur_root = cnt - 1;
  271.     }
  272.  
  273.     /* Check if there is really anything left to display. */
  274.     if (!running_total && !empty_ok) {
  275.         goto all_empty;
  276.     }
  277.     empty_ok = FALSE;
  278.  
  279.     last_line = screen_line+1;
  280. #ifdef CLEAREOL
  281.     maybe_eol();
  282. #endif
  283.     putchar('\n') ; FLUSH;
  284.     /* Prompt the user */
  285. #ifdef MAILCALL
  286.     setmail();
  287. #endif
  288.     if (i != max_root) {
  289.         sprintf(promptbuf, "%s-- Select threads -- %s%ld%% [%c%c] --",
  290.         mailcall, (!page_root? "Top " : nullstr),
  291.         (long)(running_total*100 / article_count),
  292.         page_char, end_char);
  293.     } else {
  294.         sprintf(promptbuf, "%s-- Select threads -- %s [%c%c] --",
  295.         mailcall, (!page_root? "All" : "Bot"), end_char, page_char);
  296.     }
  297.     if (cur_root > cnt) {
  298.         cur_root = 0;
  299.     }
  300.     screen_line = root_line[cur_root];
  301. #ifdef CLEAREOL
  302.     if (erase_screen && can_home_clear) {
  303.         clear_rest();
  304.     }
  305. #endif
  306.     if (added_articles > 0) {
  307.         printf("\n** %d article%s been added **", added_articles,
  308.         added_articles == 1 ? " has" : "s have");
  309.         displayed_status = TRUE;
  310.         if (can_home) {
  311.         carriage_return();
  312.         goto_line(screen_line+1, screen_line);
  313.         } else {
  314.         putchar('\n');
  315.         }
  316.     } else {
  317.         displayed_status = FALSE;
  318.     }
  319.     added_articles = 0;
  320.       prompt_select:
  321.     standout();
  322.     fputs(promptbuf, stdout);
  323.     un_standout();
  324.     if (can_home) {
  325.         carriage_return();
  326.         goto_line(last_line, screen_line);
  327.     }
  328.     got_dash = 0;
  329.     /* Grab some commands from the user */
  330.     for (;;) {
  331.         fflush(stdout);
  332.         eat_typeahead();
  333. #ifdef CONDSUB
  334.         getcmd(buf);
  335.         ch = *buf;
  336. #else
  337.         getcmd(tmpbuf);    /* If no conditionals, don't allow macros */ 
  338.         ch = *tmpbuf;
  339.         buf[0] = ch;
  340.         buf[1] = FINISHCMD;
  341. #endif
  342.         if (errno) {
  343.         ch = Ctl('l');
  344.         }
  345.         in_select = index(select_chars, ch);
  346.         /* Plaster any inherited empty roots on first command if not Esc. */
  347.         if (cmd && (in_select || (ch != '\033' && ch != '+'))) {
  348.         max_root = count_roots(TRUE);
  349.         cmd = 0;
  350.         }
  351.         if (displayed_status && can_home) {
  352.         goto_line(screen_line, last_line+1);
  353.         erase_eol();
  354.         screen_line = last_line+1;
  355.         displayed_status = FALSE;
  356.         }
  357.         if (ch == '-') {
  358.         got_dash = 1;
  359.         if (!can_home) {
  360.             putchar('-');
  361.             fflush(stdout);
  362.         }
  363.         continue;
  364.         }
  365.         if (ch == ' ') {
  366.         if (i == max_root) {
  367.             ch = end_char;
  368.         } else {
  369.             ch = page_char;
  370.         }
  371.         }
  372.         if (!in_select && (index("<+>^$!?&:/hDJLNPqQTUXZ\n\r\t\033", ch)
  373.          || ch == Ctl('l') || ch == Ctl('r') || ch == Ctl('k'))) {
  374.         break;
  375.         }
  376.         if (in_select) {
  377.         j = in_select - select_chars;
  378.         if (j >= cnt) {
  379.             dingaling();
  380.             j = -1;
  381.         } else if (got_dash) {
  382.             ;
  383.         } else if (selected_roots[root_hold[j]] & mask) {
  384.             action = (unread_selector ? 'k' : '-');
  385.         } else {
  386.             action = '+';
  387.         }
  388.         } else if (ch == 'y' || ch == '.') {
  389.         j = cur_root;
  390.         if (selected_roots[root_hold[j]] & mask) {
  391.             action = (unread_selector ? 'k' : '-');
  392.         } else {
  393.             action = '+';
  394.         }
  395.         } else if (ch == 'k' || ch == 'j' || ch == ',') {
  396.         j = cur_root;
  397.         action = 'k';
  398.         } else if (ch == 'm' || ch == '\\') {
  399.         j = cur_root;
  400.         action = 'm';
  401.         } else if (ch == '@') {
  402.         cur_root = 0;
  403.         j = cnt-1;
  404.         got_dash = 1;
  405.         action = '@';
  406.         } else if (ch == '[' || ch == 'p') {
  407.         if (--cur_root < 0) {
  408.             cur_root = cnt ? cnt-1 : 0;
  409.         }
  410.         j = -1;
  411.         } else if (ch == ']' || ch == 'n') {
  412.         if (++cur_root >= cnt) {
  413.             cur_root = 0;
  414.         }
  415.         j = -1;
  416.         } else {
  417.         if (can_home) {
  418.             goto_line(screen_line, last_line+1);
  419.             screen_line = last_line+1;
  420.         } else {
  421.             putchar('\n');
  422.         }
  423.         if (ch == 'c') {
  424.             if ((ch = ask_catchup()) == 'y' || ch == 'u') {
  425.             ch = 'q';
  426.             break;
  427.             }
  428.             if (ch != 'N') {
  429.             ch = Ctl('l');
  430.             break;
  431.             }
  432.             if (can_home) {
  433.             carriage_return();
  434.             erase_eol();
  435.             }
  436.             printf("Aborted.");
  437.         } else {
  438.             printf("Type ? for help.");
  439.             settle_down();
  440.         }
  441.         displayed_status = TRUE;
  442.  
  443.         if (can_home) {
  444.             carriage_return();
  445.         } else {
  446.             putchar('\n');
  447.         }
  448.         j = -1;
  449.         }
  450.         if (j >= 0) {
  451.         if (!got_dash) {
  452.             cur_root = j;
  453.         } else {
  454.             got_dash = 0;
  455.             if (j < cur_root) {
  456.             ch = cur_root-1;
  457.             cur_root = j;
  458.             j = ch;
  459.             }
  460.         }
  461.         if (++j == cnt) {
  462.             j = 0;
  463.         }
  464.         do {
  465.           register int r;
  466.           register char maskr = mask;
  467.             r = root_hold[cur_root];
  468.             if (can_home) {
  469.             goto_line(screen_line, root_line[cur_root]);
  470.             screen_line = root_line[cur_root];
  471.             }
  472.             putchar(select_chars[cur_root]);
  473.             if (action == '@') {
  474.             if (selected_roots[r] & 4) {
  475.                 ch = (unread_selector ? '+' : ' ');
  476.             } else if (unread_selector) {
  477.                 ch = 'k';
  478.             } else
  479.             if (selected_roots[r] & maskr) {
  480.                 ch = '-';
  481.             } else {
  482.                 ch = '+';
  483.             }
  484.             } else {
  485.             ch = action;
  486.             }
  487.             switch (ch) {
  488.             case '+':
  489.             if (!(selected_roots[r] & maskr)) {
  490.                 selected_roots[r] |= maskr;
  491.                 selected_root_cnt++;
  492.                 selected_count += root_article_cnts[r];
  493.                 putchar('+');
  494.             }
  495.             /* FALL THROUGH */
  496.             case 'm':
  497.             if (selected_roots[r] & 4) {
  498.                 selected_roots[r] &= ~4;
  499.                 if (ch == 'm') {
  500.                 putchar(' ');
  501.                 }
  502.             } else if (ch == 'm') {
  503.                 goto unsel;
  504.             }
  505.             break;
  506.             case 'k':
  507.             if (!(selected_roots[r] & 4)) {
  508.                 selected_roots[r] |= 4;
  509.                 putchar('-'), fflush(stdout);
  510.                 p_art = p_articles + p_roots[r].articles;
  511.                 art = 0;
  512.             }
  513.             /* FALL THROUGH */
  514.             case '-':
  515.             unsel:
  516.             if (selected_roots[r] & maskr) {
  517.                 selected_roots[r] &= ~maskr;
  518.                 selected_root_cnt--;
  519.                 selected_count -= root_article_cnts[r];
  520.                 if (ch != 'k') {
  521.                 putchar(' ');
  522.                 }
  523.             }
  524.             break;
  525.             }
  526.             fflush(stdout);
  527.             if (++cur_root == cnt) {
  528.             cur_root = 0;
  529.             }
  530.             if (can_home) {
  531.             carriage_return();
  532.             }
  533.         } while (cur_root != j);
  534.         } else {
  535.         got_dash = FALSE;
  536.         }
  537.         if (can_home) {
  538.         goto_line(screen_line, root_line[cur_root]);
  539.         screen_line = root_line[cur_root];
  540.         }
  541.     }/* for */
  542.     if (can_home) {
  543.         goto_line(screen_line, last_line);
  544.     }
  545.     clean_screen = TRUE;
  546.       do_command:
  547.     output_chase_phrase = TRUE;
  548.     if (ch == 'L') {
  549.         if (!*++display_mode) {
  550.         display_mode = select_order;
  551.         }
  552.         ch = Ctl('l');
  553.         cur_root = 0;
  554.     } else if (ch == '$') {
  555.         ch = '<';
  556.         page_root = max_root;
  557.         last_running = article_count;
  558.         cur_root = 0;
  559.     } else if (ch == '^' || ch == Ctl('r')) {
  560.         ch = '>';
  561.         i = 0;
  562.         running_total = 0;
  563.         cur_root = 0;
  564.     } else if (ch == 'h' || ch == '?') {
  565.         putchar('\n');
  566.         if ((ch = help_select()) || (ch = pause_getcmd())) {
  567.         goto got_cmd;
  568.         }
  569.         ch = Ctl('l');
  570.     } else if (index(":/&!", ch)) {
  571.         erase_eol();        /* erase the prompt */
  572.         if (!finish_command(TRUE)) {    /* get rest of command */
  573.         if (clean_screen) {
  574.             screen_line = root_line[cur_root];
  575.             goto prompt_select;
  576.         }
  577.         goto extend_done;
  578.         }
  579.         if (ch == '&' || ch == '!') {
  580.         one_command = TRUE;
  581.         perform(buf, FALSE);
  582.         one_command = FALSE;
  583.  
  584.         putchar('\n') ; FLUSH;
  585.         clean_screen = FALSE;
  586.         } else {
  587.         int selected_save = selected_root_cnt;
  588.  
  589.         if (ch == ':') {
  590.             clean_screen = (use_selected() == 2) && clean_screen;
  591.             if (!unread_selector) {
  592.             for (j = 0; j < total.root; j++) {
  593.                 if (selected_roots[j] & 4) {
  594.                 selected_roots[j] = 0;
  595.                 p_art = p_articles + p_roots[j].articles;
  596.                 art = 0;
  597.                 follow_thread('J');
  598.                 }
  599.             }
  600.             }
  601.         } else {
  602.             /* Force the search to begin at absfirst or firstart,
  603.             ** depending upon whether they specified the 'r' option.
  604.             */
  605.             art = lastart+1;
  606.             page_line = 1;
  607.             switch (art_search(buf, sizeof buf, FALSE)) {
  608.             case SRCH_ERROR:
  609.             case SRCH_ABORT:
  610.             case SRCH_INTR:
  611.             fputs("\nInterrupted\n", stdout) ; FLUSH;
  612.             break;
  613.             case SRCH_DONE:
  614.             case SRCH_SUBJDONE:
  615.             fputs("Done\n", stdout) ; FLUSH;
  616.             break;
  617.             case SRCH_NOTFOUND:
  618.             fputs("\nNot found.\n", stdout) ; FLUSH;
  619.             break;
  620.             case SRCH_FOUND:
  621.             break;
  622.             }
  623.             clean_screen = FALSE;
  624.         }
  625.         /* Recount, in case something has changed. */
  626.         max_root = count_roots(!unread_selector);
  627.  
  628.         running_total = 0;
  629.         last_running = 0;
  630.         if (article_count) {
  631.             for (j = 0; j < page_root; j++) {
  632.             last_running += root_article_cnts[j];
  633.             }
  634.             running_total = last_running;
  635.             for ( ; j < i; j++) {
  636.             running_total += root_article_cnts[j];
  637.             }
  638.         }
  639.         cur_root = 0;
  640.  
  641.         if ((selected_save -= selected_root_cnt) != 0) {
  642.             putchar('\n');
  643.             if (selected_save < 0) {
  644.             fputs("S", stdout);
  645.             selected_save *= -1;
  646.             } else {
  647.             fputs("Des", stdout);
  648.             }
  649.             printf("elected %d thread%s.", selected_save,
  650.             selected_save == 1 ? nullstr : "s");
  651.             clean_screen = FALSE;
  652.         }
  653.         if (!clean_screen) {
  654.             putchar('\n') ; FLUSH;
  655.         }
  656.         }/* if !& or :/ */
  657.  
  658.         if (clean_screen) {
  659.         carriage_return();
  660.         up_line();
  661.         erase_eol();
  662.         screen_line = root_line[cur_root];
  663.         goto prompt_select;
  664.         }
  665.       extend_done:
  666.         if ((ch = pause_getcmd())) {
  667.           got_cmd:
  668.         if (ch > 0) {
  669.             /* try to optimize the screen update for some commands. */
  670.             if (!index(select_chars, ch)
  671.              && (index("<+>^$!?&:/hDJLNPqQTUXZ\n\r\t\033", ch)
  672.               || ch == Ctl('k'))) {
  673.             buf[0] = ch;
  674.             buf[1] = FINISHCMD;
  675.             goto do_command;
  676.             }
  677.             pushchar(ch | 0200);
  678.         }
  679.         }
  680.         ch = Ctl('l');
  681.     } else if (ch == Ctl('k')) {
  682.         edit_kfile();
  683.         ch = Ctl('l');
  684.     } else if (!unread_selector && (ch == 'X' || ch == 'D' || ch == 'J')) {
  685.         if (ch == 'D') {
  686.         j = page_root;
  687.         last = i;
  688.         } else {
  689.         j = 0;
  690.         last = max_root;
  691.         }
  692.         for (; j < last; j++) {
  693.         if (((!(selected_roots[j] & 1) ^ (ch == 'J'))
  694.          && (cnt = root_article_cnts[j])) || (selected_roots[j] & 4)) {
  695.             p_art = p_articles + p_roots[j].articles;
  696.             art = 0;
  697.             follow_thread('J');
  698.         }
  699.         }
  700.         max_root = count_roots(TRUE);
  701.         if (article_count
  702.          && (ch == 'J' || (ch == 'D' && !selected_root_cnt))) {
  703.         last_running = 0;
  704.         for (i = 0; i < page_root; i++) {
  705.             last_running += root_article_cnts[i];
  706.         }
  707.         ch = Ctl('l');
  708.         cur_root = 0;
  709.         } else {
  710.         break;
  711.         }
  712.     } else if (ch == 'J') {
  713.         for (j = 0; j < max_root; j++) {
  714.         selected_roots[j] = (selected_roots[j] & ~2) | 4;
  715.         }
  716.         selected_root_cnt = selected_count = 0;
  717.         ch = Ctl('l');
  718.     } else if (ch == 'T') {
  719.       register int r;
  720.  
  721.         erase_eol();        /* erase the prompt */
  722.         r = root_hold[cur_root];
  723.         p_art = p_articles + p_roots[r].articles;
  724.         art = p_art->num;
  725.         if (p_art->subject == -1) {
  726.         follow_thread('N');
  727.         }
  728.         perform("T", FALSE);
  729.         max_root = count_roots(TRUE);
  730.         if (article_count) {
  731.         ch = Ctl('l');
  732.         }
  733.     }
  734.     if (ch == '>') {
  735.         cur_root = 0;
  736.         page_root = i;
  737.     } else if (ch == '<' || (page_root && page_root >= max_root)) {
  738.         cur_root = 0;
  739.         running_total = last_running;
  740.         if (!(i = page_root) || !max_root) {
  741.         ch = '>';
  742.         } else {
  743.         ch = '<';
  744.         }
  745.     } else if (ch == Ctl('l')) {
  746.         i = page_root;
  747.         running_total = last_running;
  748.         ch = '>';
  749.     } else if (ch == '\r' || ch == '\n') {
  750.         if (!selected_root_cnt) {
  751.             register r = root_hold[cur_root];
  752.         if (unread_selector || !(selected_roots[r] & 4)) {
  753.             selected_roots[r] = mask;
  754.             selected_root_cnt++;
  755.             selected_count += root_article_cnts[r];
  756.         }
  757.         }
  758.     }
  759.     } while ((ch == '>' && i < max_root) || ch == '<');
  760.  
  761.     if (unread_selector) {
  762.     /* Turn selections into unread selected roots.  Let count_roots()
  763.     ** fix the counts after we're through.
  764.     */
  765.     last_root = -1;
  766.     for (j = 0; j < total.root; j++) {
  767.         if (!(selected_roots[j] & 4)) {
  768.         if (selected_roots[j] & 2) {
  769.             selected_roots[j] = 1;
  770.         }
  771.         p_art = p_articles + p_roots[j].articles;
  772.         art = 0;
  773.         follow_thread('u');
  774.         } else {
  775.         selected_roots[j] &= ~4;
  776.         }
  777.     }
  778.     } else {
  779.     select_page = page_root;
  780.     for (j = 0; j < total.root; j++) {
  781.         if (selected_roots[j] & 4) {
  782.         selected_roots[j] = 0;
  783.         p_art = p_articles + p_roots[j].articles;
  784.         art = 0;
  785.         follow_thread('J');
  786.         }
  787.     }
  788.     }
  789.     if (!output_chase_phrase) {
  790.     putchar('\n'); FLUSH;
  791.     }
  792.     if (ch == 'U') {
  793.     unread_selector = !unread_selector;
  794.     empty_ok = TRUE;
  795.     goto select_threads;
  796.     }
  797.  
  798.     if (unread_selector) {
  799.     unread_selector = 0;
  800.     mask = 1;
  801.     (void) count_roots(FALSE);
  802.     }
  803.     if (ch == 'N' || ch == 'P' || Ctl(ch) == Ctl('q') || ch == '\033') {
  804.     art = art_hold;
  805.     p_art = curr_p_art;
  806.     } else {
  807.     first_art();
  808.     }
  809.     clear_on_stop = FALSE;
  810.     mode = oldmode;
  811.     return ch;
  812. }
  813.  
  814. static int author_cnt, first_author;
  815.  
  816. /* Counts the number of lines needed to output a subject, including optional
  817. ** authors.
  818. */
  819. static int
  820. count_subj_lines(root, subj)
  821. int root;
  822. int subj;
  823. {
  824.     PACKED_ARTICLE *artp, *root_limit;
  825.     int author_subj;
  826.  
  827.     author_cnt = 0;
  828.     author_subj = subj;
  829.     first_author = -1;
  830.  
  831.     if (!subject_cnts[subj]) {
  832.     return 0;
  833.     }
  834.     if (*display_mode == 's') {    /* no-author mode takes one line */
  835.     return ++author_cnt;
  836.     }
  837.     bzero(author_cnts, total.author * sizeof (WORD));
  838.  
  839.     /* Count authors associated with this subject.  Increments author_cnts. */
  840.     artp = p_articles + p_roots[root].articles;
  841.     root_limit = upper_limit(artp, FALSE);
  842.     for (; artp != root_limit; artp++) {
  843.     if (artp->subject == author_subj
  844.      && (!was_read(artp->num) ^ unread_selector)) {
  845.         if (artp->author < 0 || artp->author >= total.author) {
  846.         printf("\
  847. Found invalid author (%d) with valid subject (%d)! [%ld]\n",
  848.             artp->author, artp->subject, artp->num);
  849.         artp->author = 0;
  850.         } else {
  851.         if (first_author < 0) {
  852.             first_author = artp->author;
  853.         }
  854.         if (!author_cnts[artp->author]++) {
  855.             author_cnt++;
  856.         }
  857.         }
  858.     }
  859.     }
  860.  
  861.     if (*display_mode == 'm') {
  862.     return (author_cnt+4)/3;
  863.     } else {
  864.     return author_cnt;
  865.     }
  866. }
  867.  
  868. static void
  869. display_subj(root, subj)
  870. int root;
  871. int subj;
  872. {
  873.     PACKED_ARTICLE *artp, *root_limit;
  874.     char *str;
  875.  
  876.     count_subj_lines(root, subj);
  877.     if (!author_cnt) {
  878.     return;
  879.     }
  880.     artp = p_articles + p_roots[root].articles;
  881.     if (artp->subject != -1 && (artp->flags & ROOT_ARTICLE)
  882.      && (!was_read(artp->num) ^ unread_selector)) {
  883.     str = nullstr;
  884.     } else {
  885.     str = ">";
  886.     }
  887. #ifdef CLEAREOL
  888.     maybe_eol();
  889. #endif
  890.     if (*display_mode == 's') {
  891.     printf("%s%3d  %s%.71s\n", first_two_chars,
  892.         subject_cnts[subj], str, subject_ptrs[subj]) ; FLUSH;
  893.     } else {
  894.     printf("%s%-16.16s%3d  %s%.55s", first_two_chars,
  895.         author_ptrs[first_author],
  896.         subject_cnts[subj], str, subject_ptrs[subj]);
  897.     if (author_cnt > 1) {
  898.         author_cnts[first_author] = 0;
  899.         author_cnt = 0;
  900.         root_limit = upper_limit(artp, FALSE);
  901.         for (; artp != root_limit; artp++) {
  902.         if (artp->author >= 0 && author_cnts[artp->author]) {
  903.             switch (author_cnt % 3) {
  904.             case 0:
  905.             putchar('\n') ; FLUSH;
  906.             if (++author_line >= LINES - 3) {
  907.                 return;
  908.             }
  909. #ifdef CLEAREOL
  910.             maybe_eol();
  911. #endif
  912.             putchar(' ');
  913.             putchar(' ');
  914.             break;
  915.             case 1:
  916.             putchar('\t');
  917.             putchar('\t');
  918.             break;
  919.             case 2:
  920.             putchar('\t');
  921.             break;
  922.             }
  923.             author_cnt += (*display_mode == 'm');
  924.             printf("%-16.16s", author_ptrs[artp->author]);
  925.             author_cnts[artp->author] = 0;
  926.         }/* if */
  927.         }/* for */
  928.     }/* if */
  929.     putchar('\n') ; FLUSH;
  930.     author_line++;
  931.     }/* if */
  932.     first_two_chars[0] = first_two_chars[1] = ' ';
  933. }
  934.  
  935. /* Get each root's article count, and subject count(s); count total
  936. ** articles and selected articles (use unread_selector to determine
  937. ** whether to count read or unread articles); deselect any roots we
  938. ** find that are empty (if do_unselect is TRUE); find the last non-
  939. ** empty root, and return its count (the index+1).
  940. */
  941. int
  942. count_roots(do_unselect)
  943. bool_int do_unselect;
  944. {
  945.     register int count;
  946.     register PACKED_ARTICLE *artp, *root_limit, *art_limit;
  947.     int last_root = -1;
  948.  
  949.     article_count = selected_count = selected_root_cnt = 0;
  950.  
  951.     if (!total.article) {
  952.     unthreaded = toread[ng];
  953.     return 0;
  954.     }
  955.     artp = p_articles;
  956.     art_limit = artp + total.article;
  957.     root_limit = upper_limit(artp, 0);
  958.  
  959.     bzero(subject_cnts, total.subject * sizeof (WORD));
  960.     count = 0;
  961.  
  962.     for (;;) {
  963.     if (artp->subject == -1) {
  964.         if (!was_read(artp->num)) {
  965.         oneless(artp->num);
  966.         }
  967.     } else if ((!was_read(artp->num) ^ unread_selector)) {
  968.         count++;
  969.         subject_cnts[artp->subject]++;
  970.     }
  971.     if (++artp == root_limit) {
  972.         register int root_num = artp[-1].root;
  973.         register char maskr = mask;
  974.  
  975.         root_article_cnts[root_num] = count;
  976.         if (count) {
  977.         article_count += count;
  978.         if (selected_roots[root_num] & maskr) {
  979.             selected_roots[root_num] &= ~4;
  980.             selected_root_cnt++;
  981.             selected_count += count;
  982.         }
  983.         last_root = root_num;
  984.         } else if (do_unselect) {
  985.         selected_roots[root_num] &= ~maskr;
  986.         } else if (selected_roots[root_num] & maskr) {
  987.         selected_roots[root_num] &= ~4;
  988.         selected_root_cnt++;
  989.         }
  990.         if (artp == art_limit) {
  991.         break;
  992.         }
  993.         root_limit = upper_limit(artp, 0);
  994.         count = 0;
  995.     }
  996.     }
  997.     if (do_unselect) {
  998.     scan_all_roots = !article_count;
  999.     }
  1000.     unthreaded = toread[ng] - article_count;
  1001.  
  1002.     return last_root+1;
  1003. }
  1004.  
  1005. /* Count the unread articles attached to the given root number.
  1006. */
  1007. int
  1008. count_one_root(root_num)
  1009. int root_num;
  1010. {
  1011.     int last = (root_num == total.root-1 ? total.article
  1012.                      : p_roots[root_num+1].articles);
  1013.     register int count = 0, i;
  1014.  
  1015.     for (i = p_roots[root_num].articles; i < last; i++) {
  1016.     if (p_articles[i].subject != -1 && !was_read(p_articles[i].num)) {
  1017.         count++;
  1018.     }
  1019.     }
  1020.     root_article_cnts[root_num] = count;
  1021.  
  1022.     return count;
  1023. }
  1024.  
  1025. #endif /* USETHREADS */
  1026.