home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume23 / trn / part08 / rt-select.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-22  |  21.8 KB  |  943 lines

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