home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / trn / part07 / rt-select.c < prev   
Encoding:
C/C++ Source or Header  |  1991-12-02  |  22.6 KB  |  993 lines

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