home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume23 / trn / part04 / ng.c < prev   
C/C++ Source or Header  |  1991-08-22  |  35KB  |  1,561 lines

  1. /* $Header: ng.c,v 4.3.3.3 91/01/16 03:18:16 davison Trn $
  2.  *
  3.  * $Log:    ng.c,v $
  4.  * Revision 4.3.3.3  91/01/16  03:18:16  davison
  5.  * Integrated rn patches 48-54.  Fixed in_char/verify interaction.
  6.  * 
  7.  * Revision 4.3.3.2  90/08/20  16:03:45  davison
  8.  * Fixed bug in backpage code.
  9.  * 
  10.  * Revision 4.3.3.1  90/07/21  20:27:17  davison
  11.  * Initial Trn Release
  12.  * 
  13.  * Revision 4.3.2.9  91/01/05  14:56:47  sob
  14.  * Removed bogus "fast skip" for NNTP.
  15.  *
  16.  * Revision 4.3.2.8  90/11/22  16:14:17  sob
  17.  * Added changes to accomodate picky C preprocessors
  18.  * 
  19.  * Revision 4.3.2.7  90/04/21  14:44:23  sob
  20.  * Revised previous patch insure that it does not decrement below zero.
  21.  * 
  22.  * Revision 4.3.2.6  90/03/22  23:04:49  sob
  23.  * Fixes provided by Wayne Davison <drivax!davison>
  24.  * 
  25.  * Revision 4.3.2.5  89/12/09  01:18:42  sob
  26.  * Fixed a bad call to nntpopen().
  27.  * 
  28.  * Revision 4.3.2.4  89/11/28  01:51:20  sob
  29.  * Removed redundant #include directive.
  30.  * 
  31.  * Revision 4.3.2.3  89/11/27  01:31:03  sob
  32.  * Altered NNTP code per ideas suggested by Bela Lubkin
  33.  * <filbo@gorn.santa-cruz.ca.us>
  34.  * 
  35.  * Revision 4.3.2.2  89/11/26  22:53:35  sob
  36.  * Add new patches to make RRN be faster.
  37.  * 
  38.  * Revision 4.3.2.1  89/11/06  00:54:27  sob
  39.  * Added RRN support from NNTP 1.5
  40.  * 
  41.  * Revision 4.3.1.6  85/09/10  11:03:42  lwall
  42.  * Improved %m in in_char().
  43.  * 
  44.  * Revision 4.3.1.5  85/09/05  12:34:37  lwall
  45.  * Catchup command could make unread article count too big.
  46.  * 
  47.  * Revision 4.3.1.4  85/07/23  18:19:46  lwall
  48.  * Added MAILCALL environment variable.
  49.  * 
  50.  * Revision 4.3.1.3  85/05/16  16:48:09  lwall
  51.  * Fixed unsubsubscribe.
  52.  * 
  53.  * Revision 4.3.1.2  85/05/13  09:29:28  lwall
  54.  * Added CUSTOMLINES option.
  55.  * 
  56.  * Revision 4.3.1.1  85/05/10  11:36:00  lwall
  57.  * Branch for patches.
  58.  * 
  59.  * Revision 4.3  85/05/01  11:43:43  lwall
  60.  * Baseline for release with 4.3bsd.
  61.  * 
  62.  */
  63.  
  64. #include "EXTERN.h"
  65. #include "common.h"
  66. #include "rn.h"
  67. #include "term.h"
  68. #include "final.h"
  69. #include "util.h"
  70. #include "artsrch.h"
  71. #include "cheat.h"
  72. #include "help.h"
  73. #include "kfile.h"
  74. #include "rcstuff.h"
  75. #include "head.h"
  76. #include "bits.h"
  77. #include "art.h"
  78. #include "artio.h"
  79. #include "ngstuff.h"
  80. #include "intrp.h"
  81. #include "respond.h"
  82. #include "ngdata.h"
  83. #include "backpage.h"
  84. #include "rcln.h"
  85. #include "last.h"
  86. #include "search.h"
  87. #ifdef SERVER
  88. #include "server.h"
  89. #endif
  90. #ifdef USETHREADS
  91. #include "rthreads.h"
  92. #endif
  93. #include "uudecode.h"
  94. #include "INTERN.h"
  95. #include "ng.h"
  96. #include "artstate.h"            /* somebody has to do it */
  97.  
  98. /* art_switch() return values */
  99.  
  100. #define AS_NORM 0
  101. #define AS_INP 1
  102. #define AS_ASK 2
  103. #define AS_CLEAN 3
  104.  
  105. ART_NUM recent_art = -1;    /* previous article # for '-' command */
  106. ART_NUM curr_art = -1;        /* current article # */
  107. int exit_code = NG_NORM;
  108.  
  109. void
  110. ng_init()
  111. {
  112.  
  113. #ifdef KILLFILES
  114.     open_kfile(KF_GLOBAL);
  115. #endif
  116. #ifdef CUSTOMLINES
  117.     init_compex(&hide_compex);
  118.     init_compex(&page_compex);
  119. #endif
  120. }
  121.  
  122. /* do newsgroup on line ng with name ngname */
  123.  
  124. /* assumes that we are chdir'ed to SPOOL, and assures that that is
  125.  * still true upon return, but chdirs to SPOOL/ngname in between
  126.  *
  127.  * If you can understand this routine, you understand most of the program.
  128.  * The basic structure is:
  129.  *    for each desired article
  130.  *        for each desired page
  131.  *            for each line on page
  132.  *                if we need another line from file
  133.  *                    get it
  134.  *                    if it's a header line
  135.  *                        do special things
  136.  *                for each column on page
  137.  *                    put out a character
  138.  *                end loop
  139.  *            end loop
  140.  *        end loop
  141.  *    end loop
  142.  *
  143.  *    (Actually, the pager is in another routine.)
  144.  *
  145.  * The chief problem is deciding what is meant by "desired".  Most of
  146.  * the messiness of this routine is due to the fact that people want
  147.  * to do unstructured things all the time.  I have used a few judicious
  148.  * goto's where I thought it improved readability.  The rest of the messiness
  149.  * arises from trying to be both space and time efficient.  Have fun.
  150.  */
  151.  
  152. int
  153. do_newsgroup(start_command)
  154. char *start_command;            /* command to fake up first */
  155. {
  156. #ifdef SERVER
  157.     char ser_line[256];
  158.     char artname[32];
  159.     static long our_pid=0;
  160. #endif /* SERVER */
  161.     char oldmode = mode;
  162.     register long i;            /* scratch */
  163.     int skipstate;            /* how many unavailable articles */
  164.                     /*   have we skipped already? */
  165.     
  166.     char *whatnext = "%sWhat next? [%s]";
  167.  
  168. #ifdef SERVER
  169.     if (our_pid == 0)           /* Agreed, this is gross */
  170.         our_pid = getpid();
  171. #endif /* SERVER */
  172.  
  173. #ifdef ARTSEARCH
  174.     srchahead = (scanon && ((ART_NUM)toread[ng]) >= scanon ? -1 : 0);
  175.                     /* did they say -S? */
  176. #endif
  177.     
  178.     mode = 'a';
  179. #ifdef USETHREADS
  180.     recent_p_art = curr_p_art = Nullart;
  181. #endif
  182.     recent_art = curr_art = -1;
  183.     exit_code = NG_NORM;
  184.  
  185. #ifdef SERVER
  186.     sprintf(ser_line, "GROUP %s", ngname);
  187.     put_server(ser_line);
  188.     if (get_server(ser_line, sizeof(ser_line)) < 0) {
  189.     fprintf(stderr, "rrn: Unexpected close of server socket.\n");
  190.     finalize(1);
  191.     }
  192.     if (*ser_line != CHAR_OK) {
  193.     if (atoi(ser_line) != ERR_NOGROUP)
  194.         fprintf(stderr, "rrn: server response to GROUP %s:\n%s\n",
  195.             ngname, ser_line);
  196.     return (-1);
  197.     }
  198. #else /* not SERVER */
  199.     if (eaccess(ngdir,5)) {        /* directory read protected? */
  200.     if (eaccess(ngdir,0)) {
  201. #ifdef VERBOSE
  202.         IF(verbose)
  203.         printf("\nNewsgroup %s does not have a spool directory!\n",
  204.             ngname) FLUSH;
  205.         ELSE
  206. #endif
  207. #ifdef TERSE
  208.         printf("\nNo spool for %s!\n",ngname) FLUSH;
  209. #endif
  210. #ifdef CATCHUP
  211.         catch_up(ng);
  212. #endif
  213.         toread[ng] = TR_NONE;
  214.     }
  215.     else {
  216. #ifdef VERBOSE
  217.         IF(verbose)
  218.         printf("\nNewsgroup %s is not currently accessible.\n",
  219.             ngname) FLUSH;
  220.         ELSE
  221. #endif
  222. #ifdef TERSE
  223.         printf("\n%s not readable.\n",ngname) FLUSH;
  224. #endif
  225.         toread[ng] = TR_NONE;    /* make this newsgroup invisible */
  226.                     /* (temporarily) */
  227.     }
  228.     mode = oldmode;
  229.     return -1;
  230.     }
  231.  
  232.     /* chdir to newsgroup subdirectory */
  233.  
  234.     if (chdir(ngdir)) {
  235.     printf(nocd,ngdir) FLUSH;
  236.     mode = oldmode;
  237.     return -1;
  238.     }
  239. #endif /* SERVER */
  240.  
  241. #ifdef CACHESUBJ
  242.     subj_list = Null(char **);        /* no subject list till needed */
  243. #endif
  244.     
  245.     /* initialize control bitmap */
  246.  
  247.     if (initctl()) {
  248.     mode = oldmode;
  249.     return -1;
  250.     }
  251.  
  252.     /* FROM HERE ON, RETURN THRU CLEANUP OR WE ARE SCREWED */
  253.  
  254.     /* grab threaded data */
  255.  
  256. #ifdef USETHREADS
  257.     if (ThreadedGroup && (ThreadedGroup = use_data(thread_name(ngname)))) {
  258.     /* check if thread file is newer than the active2 entry (this is
  259.     ** possible when mthreads is still running.) */
  260.     if (total.last > lastart) {
  261.         grow_ctl(total.last);    /* sets lastart */
  262.     }
  263.     else if (total.last < lastart) {
  264.         /* If the active2 entry is newer than the data file, something
  265.         ** bad is going on.  Forget using the thread data. */
  266.         unuse_data(0);
  267.         ThreadedGroup = FALSE;
  268.     }
  269.     }
  270. #endif
  271.  
  272.     in_ng = TRUE;            /* tell the world we are here */
  273.     forcelast = TRUE;            /* if 0 unread, do not bomb out */
  274.  
  275.     /* remember what newsgroup we were in for sake of posterity */
  276.  
  277.     writelast();
  278.  
  279.     /* see if there are any special searches to do */
  280.  
  281. #ifdef KILLFILES
  282.     open_kfile(KF_LOCAL);
  283. #ifdef VERBOSE
  284.     IF(verbose)
  285.     kill_unwanted(firstart,"Looking for articles to kill...\n\n",TRUE);
  286.     ELSE
  287. #endif
  288. #ifdef TERSE
  289.     kill_unwanted(firstart,"Killing...\n\n",TRUE);
  290. #endif
  291. #endif
  292. #ifdef USETHREADS
  293.     first_art();
  294. #else
  295.     art=firstart;
  296. #endif
  297.     
  298.     /* do they want a special top line? */
  299.  
  300.     firstline = getval("FIRSTLINE",Nullch);
  301.  
  302.     /* custom line suppression, custom page ending */
  303.  
  304. #ifdef CUSTOMLINES
  305.     if (hideline = getval("HIDELINE",Nullch))
  306.     compile(&hide_compex,hideline,TRUE,TRUE);
  307.     if (pagestop = getval("PAGESTOP",Nullch))
  308.     compile(&page_compex,pagestop,TRUE,TRUE);
  309. #endif
  310.  
  311.     /* now read each unread article */
  312.  
  313.     rc_changed = doing_ng = TRUE;    /* enter the twilight zone */
  314.     skipstate = 0;            /* we have not skipped anything (yet) */
  315.     checkcount = 0;            /* do not checkpoint for a while */
  316.     do_fseek = FALSE;            /* start 1st article at top */
  317.     if (art > lastart)
  318. #ifdef USETHREADS
  319.     first_art();
  320. #else
  321.     art=firstart;            /* init the for loop below */
  322. #endif
  323.     for (; art<=lastart+1; ) {        /* for each article */
  324.  
  325.     /* do we need to "grow" the newsgroup? */
  326.  
  327. #ifdef USETHREADS
  328.     if (ThreadedGroup) {
  329.         if ((art > lastart || forcegrow) && getngsize(ng) > total.last) {
  330.         unuse_data(1);        /* free data with selections saved */
  331.         if ((ThreadedGroup = use_data(thread_name(ngname))) != 0) {
  332.             grow_ctl(total.last);    /* sets lastart */
  333.             find_article(art);
  334.             curr_p_art = p_art;
  335.         }
  336.         forcegrow = FALSE;
  337.         }
  338.     }
  339.     else
  340. #endif
  341.     if (art > lastart || forcegrow)
  342.         grow_ctl(getngsize(ng));
  343.     check_first(art);        /* make sure firstart is still 1st */
  344.     if (start_command) {        /* fake up an initial command? */
  345.         prompt = whatnext;
  346.         strcpy(buf,start_command);
  347.         free(start_command);
  348.         start_command = Nullch;
  349. #ifdef USETHREADS
  350.         p_art = Nullart;
  351. #endif
  352.         art = lastart+1;
  353.         goto article_level;
  354.     }
  355.     if (art>lastart) {        /* are we off the end still? */
  356.         ART_NUM ucount = 0;        /* count of unread articles left */
  357.  
  358.         for (i=firstart; i<=lastart; i++)
  359.         if (!(ctl_read(i)))
  360.             ucount++;        /* count the unread articles */
  361. #ifdef DEBUGGING
  362.         /*NOSTRICT*/
  363.         if (debug && ((ART_NUM)toread[ng]) != ucount)
  364.         printf("(toread=%ld sb %ld)",(long)toread[ng],(long)ucount)
  365.           FLUSH;
  366. #endif
  367.         /*NOSTRICT*/
  368.         toread[ng] = (ART_UNREAD)ucount;    /* this is perhaps pointless */
  369.         art = lastart + 1;        /* keep bitmap references sane */
  370.         if (art != curr_art) {
  371. #ifdef USETHREADS
  372.         recent_p_art = curr_p_art;
  373.         find_article(art);
  374.         curr_p_art = p_art;
  375. #endif
  376.         recent_art = curr_art;
  377.                     /* remember last article # (for '-') */
  378.         curr_art = art;      /* remember this article # */
  379.         }
  380.         if (erase_screen)
  381.         clear();            /* clear the screen */
  382.         else
  383.         fputs("\n\n",stdout) FLUSH;
  384. #ifdef VERBOSE
  385.         IF(verbose)
  386.         printf("End of newsgroup %s.",ngname);
  387.                     /* print pseudo-article */
  388.         ELSE
  389. #endif
  390. #ifdef TERSE
  391.         printf("End of %s",ngname);
  392. #endif
  393. #ifdef USETHREADS
  394.         if (ThreadedGroup)
  395.         ucount -= unthreaded;
  396. #endif
  397.         if (ucount) {
  398. #ifdef USETHREADS
  399.         if (selected_root_cnt)
  400.             printf("  (%ld + %ld articles still unread)",
  401.             (long)selected_count,(long)ucount-selected_count);
  402.         else
  403. #endif
  404.             printf("  (%ld article%s still unread)",
  405.             (long)ucount,ucount==1?nullstr:"s");
  406.         }
  407.         else {
  408.         if (!forcelast)
  409.             goto cleanup;    /* actually exit newsgroup */
  410.         }
  411.         prompt = whatnext;
  412. #ifdef ARTSEARCH
  413.         srchahead = 0;        /* no more subject search mode */
  414. #endif
  415.         fputs("\n\n",stdout) FLUSH;
  416.         skipstate = 0;        /* back to none skipped */
  417.     }
  418.     else if (!reread && was_read(art)) {
  419.                     /* has this article been read? */
  420. #ifdef USETHREADS
  421.         follow_thread('n');
  422. #else
  423.         art++;            /* then skip it */
  424. #endif
  425.         continue;
  426.     }
  427.     else if
  428.       (!reread && !was_read(art)
  429. #ifdef SERVER
  430.         && nntpopen(art,GET_HEADER) == Nullfp) { 
  431. #else
  432.         && artopen(art) == Nullfp) { /* never read it, & cannot find it? */
  433.         if (errno != ENOENT) {    /* has it not been deleted? */
  434. #ifdef VERBOSE
  435.         IF(verbose)
  436.             printf("\n(Article %ld exists but is unreadable.)\n",
  437.             (long)art) FLUSH;
  438.         ELSE
  439. #endif
  440. #ifdef TERSE
  441.             printf("\n(%ld unreadable.)\n",(long)art) FLUSH;
  442. #endif
  443.         skipstate = 0;
  444.         sleep(2);
  445.         }
  446. #endif
  447.         switch(skipstate++) {
  448.         case 0:
  449.         clear();
  450. #ifdef VERBOSE
  451.         IF(verbose)
  452.             fputs("Skipping unavailable article",stdout);
  453.         ELSE
  454. #endif
  455. #ifdef TERSE
  456.             fputs("Skipping",stdout);
  457. #endif
  458.         pad(just_a_sec/3);
  459.         sleep(1);
  460.         break;
  461.         case 1:
  462.         fputs("..",stdout);
  463.         fflush(stdout);
  464.         break;
  465.         default:
  466.         putchar('.');
  467.         fflush(stdout);
  468. #ifndef SERVER
  469. #define READDIR
  470. #ifdef READDIR
  471.         {            /* fast skip patch */
  472.             ART_NUM newart;
  473.             
  474.             if (! (newart=getngmin(".",art)))
  475.             newart = lastart+1;
  476.             for (i=art; i<newart; i++)
  477.             oneless(i);
  478.             art = newart - 1;
  479.         }
  480. #endif /* READDIR */
  481. #endif /* SERVER */
  482.         break;
  483.         }
  484.         oneless(art);        /* mark deleted as read */
  485. #ifdef USETHREADS
  486.         count_roots(FALSE);        /* Keep selected_count accurate */
  487.         find_article(art);
  488.         follow_thread('n');
  489. #else
  490.         art++;            /* try next article */
  491. #endif
  492.         continue;
  493.     }
  494.     else {                /* we have a real live article */
  495.         skipstate = 0;        /* back to none skipped */
  496.         if (art != curr_art) {
  497. #ifdef USETHREADS
  498.         recent_p_art = curr_p_art;
  499.         find_article(art);
  500.         curr_p_art = p_art;
  501. #endif
  502.         recent_art = curr_art;
  503.                     /* remember last article # (for '-') */
  504.         curr_art = art;      /* remember this article # */
  505.         }
  506.         if (!do_fseek) {        /* starting at top of article? */
  507.         artline = 0;        /* start at the beginning */
  508.         topline = -1;        /* and remember top line of screen */
  509.                     /*  (line # within article file) */
  510.         }
  511.         clear();            /* clear screen */
  512.         if (!artopen(art)) {    /* make sure article is found & open */
  513. #ifdef USETHREADS
  514.         char tmpbuf[256];
  515.         /* see if we have tree data for this article anyway */
  516.         init_tree();
  517.         sprintf(tmpbuf,"%s #%ld is not available.",ngname,(long)art);
  518.         tree_puts(tmpbuf,0,0);
  519.         vwtary((ART_LINE)0,(ART_POS)0);
  520.         finish_tree(1);
  521.         prompt = whatnext;
  522. #else
  523.         printf("Article %ld of %s is not available.\n\n",
  524.             (long)art,ngname) FLUSH;
  525.         prompt = whatnext;
  526. #endif
  527. #ifdef ARTSEARCH
  528.         srchahead = 0;
  529. #endif
  530.         }
  531.         else {            /* found it, so print it */
  532.         switch (do_article()) {
  533.         case DA_CLEAN:        /* quit newsgroup */
  534.             goto cleanup;
  535.         case DA_TOEND:        /* do not mark as read */
  536.             goto reask_article; 
  537.         case DA_RAISE:        /* reparse command at end of art */
  538.             goto article_level;
  539.         case DA_NORM:        /* normal end of article */
  540.             break;
  541.         }
  542.         }
  543.         if (art >= absfirst)    /* don't mark non-existant articles */
  544.         mark_as_read();        /* mark current article as read */
  545.         reread = FALSE;
  546.         do_hiding = TRUE;
  547. #ifdef ROTATION
  548.         rotate = FALSE;
  549. #endif
  550.     }
  551.  
  552. /* if these gotos bother you, think of this as a little state machine */
  553.  
  554. reask_article:
  555. #ifdef MAILCALL
  556.     setmail();
  557. #endif
  558.     setdfltcmd();
  559. #ifdef CLEAREOL
  560.     if (erase_screen && can_home_clear)
  561.         clear_rest();
  562. #endif /* CLEAREOL */
  563.     unflush_output();        /* disable any ^O in effect */
  564.     standout();            /* enter standout mode */
  565.     printf(prompt,mailcall,dfltcmd);/* print prompt, whatever it is */
  566.     un_standout();            /* leave standout mode */
  567.     putchar(' ');
  568.     fflush(stdout);
  569. reinp_article:
  570.     eat_typeahead();
  571. #ifdef PENDING
  572.     look_ahead();            /* see what we can do in advance */
  573.     if (!input_pending())
  574.         collect_subjects();        /* loads subject cache until */
  575.                     /* input is pending */
  576. #endif
  577.     getcmd(buf);
  578.     if (errno || *buf == '\f') {
  579.         if (LINES < 100 && !int_count)
  580.         *buf = '\f';        /* on CONT fake up refresh */
  581.         else {
  582.         putchar('\n') FLUSH;        /* but only on a crt */
  583.         goto reask_article;
  584.         }
  585.     }
  586. article_level:
  587.  
  588.     /* parse and process article level command */
  589.  
  590.     switch (art_switch()) {
  591.     case AS_INP:            /* multichar command rubbed out */
  592.         goto reinp_article;
  593.     case AS_ASK:            /* reprompt "End of article..." */
  594.         goto reask_article;
  595.     case AS_CLEAN:            /* exit newsgroup */
  596.         goto cleanup;
  597.     case AS_NORM:            /* display article art */
  598.         break;
  599.     }
  600.     }                    /* end of article selection loop */
  601.     
  602. /* shut down newsgroup */
  603.  
  604. cleanup:
  605.     uud_end();
  606. #ifdef KILLFILES
  607.     kill_unwanted(firstart,"\nCleaning up...\n\n",FALSE);
  608.                     /* do cleanup from KILL file, if any */
  609. #endif
  610. #ifdef USETHREADS
  611.     if (ThreadedGroup)
  612.     unuse_data(0);            /* free article thread data */
  613. #endif
  614.     in_ng = FALSE;            /* leave newsgroup state */
  615.     if (artfp != Nullfp) {        /* article still open? */
  616.     fclose(artfp);            /* close it */
  617.     artfp = Nullfp;            /* and tell the world */
  618. #ifdef SERVER
  619.         sprintf(artname, "/tmp/rrn%ld.%ld", (long) openart, our_pid);
  620.         UNLINK(artname);
  621. #endif /* SERVER */
  622.     openart = 0;
  623.     }
  624.     putchar('\n') FLUSH;
  625.     yankback();                /* do a Y command */
  626.     restore_ng();            /* reconstitute .newsrc line */
  627.     doing_ng = FALSE;            /* tell sig_catcher to cool it */
  628.     free(ctlarea);            /* return the control area */
  629. #ifdef CACHESUBJ
  630.     if (subj_list) {
  631.     for (i=OFFSET(lastart); i>=0; --i)
  632.         if (subj_list[i])
  633.         free(subj_list[i]);
  634. #ifndef lint
  635.     free((char*)subj_list);
  636. #endif /* lint */
  637.     }
  638. #endif
  639.     write_rc();                /* and update .newsrc */
  640.     rc_changed = FALSE;            /* tell sig_catcher it is ok */
  641.     if (chdir(spool)) {
  642.     printf(nocd,spool) FLUSH;
  643.     sig_catcher(0);
  644.     }
  645. #ifdef KILLFILES
  646.     if (localkfp) {
  647.     fclose(localkfp);
  648.     localkfp = Nullfp;
  649.     }
  650. #endif
  651.     mode = oldmode;
  652.     return exit_code;
  653. }                    /* Whew! */
  654.  
  655. /* decide what to do at the end of an article */
  656.  
  657. int
  658. art_switch()
  659. {
  660.     register ART_NUM i;
  661.       
  662.     setdef(buf,dfltcmd);
  663. #ifdef VERIFY
  664.     printcmd();
  665. #endif
  666.  
  667.     switch (*buf) {
  668. #ifdef USETHREADS
  669.     case '<':            /* goto previous thread */
  670.     if (!ThreadedGroup) {
  671.         goto group_unthreaded;
  672.     }
  673.     prev_root();
  674.     return AS_NORM;
  675.     case '>':            /* goto next thread */
  676.     if (!ThreadedGroup) {
  677.         goto group_unthreaded;
  678.     }
  679.     next_root();
  680.     return AS_NORM;
  681.     case 'U': {            /* unread some articles */
  682.     char *u_prompt, *u_help_thread;
  683.  
  684.     if (!ThreadedGroup) {
  685.         dfltcmd = "a";
  686.         u_help_thread = nullstr;
  687. #ifdef VERBOSE
  688.         IF(verbose)
  689.         u_prompt = "\nSet unread: all articles? [an] ";
  690.         ELSE
  691. #endif
  692. #ifdef TERSE
  693.         u_prompt = "\nUnread? [an] ";
  694. #endif
  695.     }
  696.     else if (!p_art || art > lastart) {
  697.         dfltcmd = "+";
  698.         u_help_thread = nullstr;
  699. #ifdef VERBOSE
  700.         IF(verbose)
  701.         u_prompt = "\nSet unread: +select or all? [+an] ";
  702.         ELSE
  703. #endif
  704. #ifdef TERSE
  705.         u_prompt = "\nUnread? [+an] ";
  706. #endif
  707.     }
  708.     else {
  709.         dfltcmd = "t";
  710. #ifdef VERBOSE
  711.         IF(verbose) {
  712.         u_prompt = "\n\
  713. Set unread: thread, subthread, +select, or all? [ts+an] ";
  714.         u_help_thread = "\
  715. Type t or SP to mark this thread's articles as unread.\n\
  716. Type s to mark the current article and its descendants as unread.\n";
  717.         }
  718.         ELSE
  719. #endif
  720. #ifdef TERSE
  721.         {
  722.         u_prompt = "\nUnread? [ts+an] ";
  723.         u_help_thread = "\
  724. t or SP to mark thread unread.\n\
  725. s to mark subthread unread.\n";
  726.         }
  727. #endif
  728.     }
  729.       reask_unread:
  730.     in_char(u_prompt,'u');
  731.     setdef(buf,dfltcmd);
  732. #ifdef VERIFY
  733.     printcmd();
  734. #endif
  735.     putchar('\n') FLUSH;
  736.     if (*buf == 'h') {
  737.         fputs(u_help_thread,stdout);
  738. #ifdef VERBOSE
  739.         IF(verbose)
  740.         {
  741.         if (ThreadedGroup)
  742.             fputs("\
  743. Type + to enter select thread mode using all the unread articles.\n\
  744. (The selected threads will be marked as unread and displayed as usual.)\n\
  745. ",stdout) FLUSH;
  746.         fputs("\
  747. Type a to mark all articles in this group as unread.\n\
  748. Type n to change nothing.\n\
  749. ",stdout) FLUSH;
  750.         }
  751.         ELSE
  752. #endif
  753. #ifdef TERSE
  754.         {
  755.         if (ThreadedGroup)
  756.             fputs("\
  757. + to select threads from the unread.\n\
  758. ",stdout) FLUSH;
  759.         fputs("\
  760. a to mark all articles unread.\n\
  761. n to change nothing.\n\
  762. ",stdout) FLUSH;
  763.         }
  764. #endif
  765.         goto reask_unread;
  766.     }
  767.     else if (*buf == 'n' || *buf == 'q') {
  768.         return AS_ASK;
  769.     }
  770.     else if (*buf == 't' && *dfltcmd == 't')
  771.         follow_thread('u');
  772.     else if (*buf == 's' && *dfltcmd == 't') {
  773.         follow_thread('U');
  774.     }
  775.     else if (*buf == 'a') {
  776.         check_first(absfirst);
  777.         for (i = absfirst; i <= lastart; i++) {
  778.         onemore(i);        /* mark as unread */
  779.         }
  780.         scan_all_roots = FALSE;
  781.         count_roots(FALSE);
  782.         if (art > lastart) {
  783.         first_art();
  784.         }
  785.     }
  786.     else if (ThreadedGroup && *buf == '+') {
  787.         *buf = 'U';
  788.         goto select_threads;
  789.     }
  790.     else {
  791.         fputs(hforhelp,stdout) FLUSH;
  792.         settle_down();
  793.         goto reask_unread;
  794.     }
  795.     return AS_NORM;
  796.     }
  797.     case '[':            /* goto parent article */
  798.     case '{':            /* goto thread's root article */
  799.     if (p_art) {
  800.         if (!p_art->parent) {
  801.         if (p_art == p_articles + p_roots[p_art->root].articles) {
  802.             register char *cp = (*buf=='['?"parent":"root");
  803. #ifdef VERBOSE
  804.             IF(verbose)
  805.             fprintf(stdout,"\n\
  806. There is no %s article prior to this one.\n",cp) FLUSH;
  807.             ELSE
  808. #endif
  809. #ifdef TERSE
  810.             fprintf(stdout,"\nNo prior %s.\n",cp) FLUSH;
  811. #endif
  812.             return AS_ASK;
  813.         }
  814.         *buf = '{';
  815.         p_art--;
  816.         }
  817.         else
  818.         p_art += p_art->parent;
  819.  
  820.         if (*buf == '{')
  821.         while (p_art->parent)
  822.             p_art += p_art->parent;
  823.  
  824.         art = p_art->num;
  825.         reread = TRUE;
  826.         return AS_NORM;
  827.     }
  828. not_threaded:
  829.     if (ThreadedGroup) {
  830. #ifdef VERBOSE
  831.         IF(verbose)
  832.         fputs("\nThis article is not threaded.\n",stdout) FLUSH;
  833.         ELSE
  834. #endif
  835. #ifdef TERSE
  836.         fputs("\nUnthreaded article.\n",stdout) FLUSH;
  837. #endif
  838.         return AS_ASK;
  839.     }
  840. group_unthreaded:
  841. #ifdef VERBOSE
  842.     IF(verbose)
  843.         fputs("\nThis group is not threaded.\n",stdout) FLUSH;
  844.     ELSE
  845. #endif
  846. #ifdef TERSE
  847.         fputs("\nUnthreaded group.\n",stdout) FLUSH;
  848. #endif
  849.     return AS_ASK;
  850.     case ']':            /* goto child article */
  851.     case '}':            /* goto thread's leaf article */
  852.     if (p_art) {
  853.         if (!(p_art++)->child_cnt) {
  854.         PACKED_ARTICLE *root_limit = upper_limit(p_art-1,FALSE);
  855.  
  856.         if (p_art == root_limit) {
  857. #ifdef VERBOSE
  858.             IF(verbose)
  859.             fputs("\n\
  860. This is the last leaf in this tree.\n",stdout) FLUSH;
  861.             ELSE
  862. #endif
  863. #ifdef TERSE
  864.             fputs("\nLast leaf.\n",stdout) FLUSH;
  865. #endif
  866.             p_art--;
  867.             return AS_ASK;
  868.         }
  869.         if (*buf == ']')
  870.             *buf = '}';
  871.         else {
  872.             while (++p_art != root_limit && p_art->parent)
  873.             ;
  874.             p_art--;
  875.             *buf = ' ';
  876.         }
  877.         }
  878.         if( *buf == '}' )
  879.         while (p_art->child_cnt)
  880.             p_art++;
  881.  
  882.         art = p_art->num;
  883.         reread = TRUE;
  884.         return AS_NORM;
  885.     }
  886.     goto not_threaded;
  887.     case 'T':
  888.     if (p_art) {
  889.         sprintf(buf,"T%ld\t# %s",(long)p_roots[p_art->root].root_num,
  890.         subject_ptrs[p_art->subject]);
  891.         fputs(buf,stdout);
  892.         kf_append(buf);
  893.         *buf = 'J';
  894.         goto follow_threads;
  895.     }
  896.     goto not_threaded;
  897.     case 'K':
  898.     if (p_art) {
  899.         /* first, write kill-subject command */
  900.         (void)art_search(buf, (sizeof buf), TRUE);
  901.         art = curr_art;
  902.         p_art = curr_p_art;
  903.         *buf = 'k';            /* then take care of any prior subjs */
  904.         goto follow_threads;
  905.     }
  906.     goto normal_search;
  907.     case ',':        /* kill this node and all descendants */
  908.     mark_as_read();
  909.     *buf = 'K';
  910.     case 'k':        /* kill current subject # (e.g. [1]) */
  911.     case 'J':        /* Junk all nodes in this thread */
  912.     if (!ThreadedGroup) {
  913.         *buf = 'k';
  914.         goto normal_search;
  915.     }
  916. follow_threads:
  917.     follow_thread(*buf);
  918.     if (!reread && !toread[ng])
  919.         return AS_CLEAN;
  920.     if (!reread && selected_root_cnt && !selected_count)
  921.         goto select_threads;
  922.     return AS_NORM;
  923.     case 't':
  924.     carriage_return();
  925. #ifndef CLEAREOL
  926.     erase_eol();        /* erase the prompt */
  927. #else
  928.     if (erase_screen && can_home_clear)
  929.         clear_rest();
  930.     else
  931.         erase_eol();    /* erase the prompt */
  932. #endif /* CLEAREOL */
  933.     fflush(stdout);
  934.     page_line = 1;
  935.     p_art = curr_p_art;
  936.     entire_tree();
  937.     return AS_ASK;
  938.     case ':':            /* execute command on selected articles */
  939.     if (!ThreadedGroup) {
  940.         goto group_unthreaded;
  941.     }
  942.     page_line = 1;
  943.     if (!use_selected())
  944.         return AS_INP;
  945.     putchar('\n');
  946.     art = curr_art;
  947.     p_art = curr_p_art;
  948.     return AS_ASK;
  949. #endif /* USETHREADS */
  950.     case 'p':            /* find previous unread article */
  951. #ifdef USETHREADS
  952.     if (ThreadedGroup) {
  953.         goto backtrack_threads;
  954.     }
  955. #endif
  956.     do {
  957.         if (art <= firstart)
  958.         break;
  959.         art--;
  960. #ifdef SERVER
  961.     } while (was_read(art) || nntpopen(art,GET_HEADER) == Nullfp);
  962. #else
  963.     } while (was_read(art) || artopen(art) == Nullfp);
  964. #endif
  965. #ifdef ARTSEARCH
  966.     srchahead = 0;
  967. #endif
  968.     return AS_NORM;
  969.     case 'P':        /* goto previous article */
  970. #ifdef USETHREADS
  971.     if (ThreadedGroup) {
  972. backtrack_threads:
  973.         backtrack_thread( *buf );
  974.         art++;        /* prepare for art-- below */
  975.     }
  976. #endif
  977.     if (art > absfirst)
  978.         art--;
  979.     else {
  980. #ifdef VERBOSE
  981.         IF(verbose)
  982.         fprintf(stdout,"\n\
  983. There are no%s articles prior to this one.\n\
  984. ",*buf=='P'?nullstr:" unread") FLUSH;
  985.         ELSE
  986. #endif
  987. #ifdef TERSE
  988.         fprintf(stdout,"\n\
  989. No previous%s articles\n\
  990. ",*buf=='P'?nullstr:" unread") FLUSH;
  991. #endif
  992.         art = curr_art;
  993. #ifdef USETHREADS
  994.         p_art = curr_p_art;
  995. #endif
  996.         return AS_ASK;
  997.     }
  998.     reread = TRUE;
  999. #ifdef ARTSEARCH
  1000.     srchahead = 0;
  1001. #endif
  1002.     return AS_NORM;
  1003.     case '-':
  1004.     if (recent_art >= 0) {
  1005. #ifdef USETHREADS
  1006.         p_art = recent_p_art;
  1007. #endif
  1008.         art = recent_art;
  1009.         reread = TRUE;
  1010. #ifdef ARTSEARCH
  1011.         srchahead = -(srchahead != 0);
  1012. #endif
  1013.         return AS_NORM;
  1014.     }
  1015.     else {
  1016.         exit_code = NG_MINUS;
  1017.         return AS_CLEAN;
  1018.     }
  1019.     case 'n':        /* find next unread article? */
  1020. #ifdef USETHREADS
  1021.     if (ThreadedGroup) {
  1022.         goto follow_threads;
  1023.     }
  1024. #endif
  1025.     if (art > lastart) {
  1026.         if (!toread[ng])
  1027.         return AS_CLEAN;
  1028.         art = firstart;
  1029.     }
  1030. #ifdef ARTSEARCH
  1031.     else if (scanon && srchahead) {
  1032.         *buf = Ctl('n');
  1033.         goto normal_search;
  1034.     }
  1035. #endif
  1036.     else
  1037.         art++;
  1038.  
  1039. #ifdef ARTSEARCH
  1040.     srchahead = 0;
  1041. #endif
  1042.     return AS_NORM;
  1043.     case 'N':            /* goto next article */
  1044. #ifdef USETHREADS
  1045.     if (ThreadedGroup) {
  1046.         goto follow_threads;
  1047.     }
  1048. #endif
  1049.     if (art > lastart)
  1050.         art = absfirst;
  1051.     else
  1052.         art++;
  1053.     if (art <= lastart)
  1054.         reread = TRUE;
  1055. #ifdef ARTSEARCH
  1056.     srchahead = 0;
  1057. #endif
  1058.     return AS_NORM;
  1059.     case '$':
  1060.     art = lastart+1;
  1061.     forcelast = TRUE;
  1062. #ifdef USETHREADS
  1063.     p_art = Nullart;
  1064. #endif
  1065. #ifdef ARTSEARCH
  1066.     srchahead = 0;
  1067. #endif
  1068.     return AS_NORM;
  1069.     case '1': case '2': case '3':    /* goto specified article */
  1070.     case '4': case '5': case '6':    /* or do something with a range */
  1071.     case '7': case '8': case '9': case '.':
  1072.     forcelast = TRUE;
  1073.     switch (numnum()) {
  1074.     case NN_INP:
  1075.         return AS_INP;
  1076.     case NN_ASK:
  1077.         return AS_ASK;
  1078.     case NN_REREAD:
  1079.         reread = TRUE;
  1080. #ifdef ARTSEARCH
  1081.         if (srchahead)
  1082.         srchahead = -1;
  1083. #endif
  1084.         break;
  1085.     case NN_NORM:
  1086.         if (was_read(art)) {
  1087. #ifdef USETHREADS
  1088.         first_art();
  1089. #else
  1090.         art = firstart;
  1091. #endif
  1092.         pad(just_a_sec/3);
  1093.         }
  1094.         else {
  1095.         putchar('\n');
  1096.         return AS_ASK;
  1097.         }
  1098.         break;
  1099.     }
  1100.     return AS_NORM;
  1101.     case Ctl('k'):
  1102.     edit_kfile();
  1103.     return AS_ASK;
  1104. #ifndef USETHREADS
  1105.     case 'K':
  1106.     case 'k':
  1107. #endif
  1108.     case Ctl('n'):    /* search for next article with same subject */
  1109. #ifdef USETHREADS
  1110.     if (ThreadedGroup) {
  1111.         goto follow_threads;
  1112.     }
  1113. #endif
  1114.     case Ctl('p'):    /* search for previous article with same subject */
  1115. #ifdef USETHREADS
  1116.     if (ThreadedGroup) {
  1117.         goto backtrack_threads;
  1118.     }
  1119. #endif
  1120.     case '/': case '?':
  1121. normal_search:
  1122. #ifdef ARTSEARCH
  1123.     {        /* search for article by pattern */
  1124.     char cmd = *buf;
  1125.     
  1126.     reread = TRUE;        /* assume this */
  1127.     page_line = 1;
  1128.     switch (art_search(buf, (sizeof buf), TRUE)) {
  1129.     case SRCH_ERROR:
  1130.         art = curr_art;
  1131.         return AS_ASK;
  1132.     case SRCH_ABORT:
  1133.         art = curr_art;
  1134.         return AS_INP;
  1135.     case SRCH_INTR:
  1136. #ifdef VERBOSE
  1137.         IF(verbose)
  1138.         printf("\n(Interrupted at article %ld)\n",(long)art) FLUSH;
  1139.         ELSE
  1140. #endif
  1141. #ifdef TERSE
  1142.         printf("\n(Intr at %ld)\n",(long)art) FLUSH;
  1143. #endif
  1144.         art = curr_art;
  1145.                 /* restore to current article */
  1146.         return AS_ASK;
  1147.     case SRCH_DONE:
  1148.         fputs("done\n",stdout) FLUSH;
  1149.         pad(just_a_sec/3);    /* 1/3 second */
  1150.         if (!srchahead) {
  1151.         art = curr_art;
  1152.         return AS_ASK;
  1153.         }
  1154. #ifdef USETHREADS
  1155.         first_art();
  1156. #else
  1157.         art = firstart;
  1158. #endif
  1159.         reread = FALSE;
  1160.         return AS_NORM;
  1161.     case SRCH_SUBJDONE:
  1162. #ifdef UNDEF
  1163.         fputs("\n\n\n\nSubject not found.\n",stdout) FLUSH;
  1164.         pad(just_a_sec/3);    /* 1/3 second */
  1165. #endif
  1166. #ifdef USETHREADS
  1167.         first_art();
  1168. #else
  1169.         art = firstart;
  1170. #endif
  1171.         reread = FALSE;
  1172.         return AS_NORM;
  1173.     case SRCH_NOTFOUND:
  1174.         fputs("\n\n\n\nNot found.\n",stdout) FLUSH;
  1175.         art = curr_art;  /* restore to current article */
  1176.         return AS_ASK;
  1177.     case SRCH_FOUND:
  1178.         if (cmd == Ctl('n') || cmd == Ctl('p'))
  1179.         oldsubject = TRUE;
  1180.         break;
  1181.     }
  1182.     return AS_NORM;
  1183.     }
  1184. #else
  1185.     buf[1] = '\0';
  1186.     notincl(buf);
  1187.     return AS_ASK;
  1188. #endif
  1189.     case 'u':            /* unsubscribe from this newsgroup? */
  1190.     rcchar[ng] = NEGCHAR;
  1191.     return AS_CLEAN;
  1192.     case 'M':
  1193. #ifdef DELAYMARK
  1194.     if (art <= lastart) {
  1195.         delay_unmark(art);
  1196.         printf("\nArticle %ld will return.\n",(long)art) FLUSH;
  1197.     }
  1198. #else
  1199.     notincl("M");
  1200. #endif
  1201.     return AS_ASK;
  1202.     case 'm':
  1203.     if (art <= lastart) {
  1204.         unmark_as_read();
  1205.         printf("\nArticle %ld marked as still unread.\n",(long)art) FLUSH;
  1206.     }
  1207.     return AS_ASK;
  1208.     case 'c':            /* catch up */
  1209.       reask_catchup:
  1210. #ifdef VERBOSE
  1211.     IF(verbose)
  1212.         in_char("\nDo you really want to mark everything as read? [yn] ",
  1213.         'C');
  1214.     ELSE
  1215. #endif
  1216. #ifdef TERSE
  1217.         in_char("\nReally? [ynh] ", 'C');
  1218. #endif
  1219.     setdef(buf,"y");
  1220. #ifdef VERIFY
  1221.     printcmd();
  1222. #endif
  1223.     putchar('\n') FLUSH;
  1224.     if (*buf == 'h') {
  1225. #ifdef VERBOSE
  1226.         IF(verbose)
  1227.         fputs("\
  1228. Type y or SP to mark all articles as read.\n\
  1229. Type n to leave articles marked as they are.\n\
  1230. Type u to mark everything read and unsubscribe.\n\
  1231. ",stdout) FLUSH;
  1232.         ELSE
  1233. #endif
  1234. #ifdef TERSE
  1235.         fputs("\
  1236. y or SP to mark all read.\n\
  1237. n to forget it.\n\
  1238. u to mark all and unsubscribe.\n\
  1239. ",stdout) FLUSH;
  1240. #endif
  1241.         goto reask_catchup;
  1242.     }
  1243.     else if (*buf == 'n' || *buf == 'q') {
  1244.         return AS_ASK;
  1245.     }
  1246.     else if (*buf != 'y' && *buf != 'u') {
  1247.         fputs(hforhelp,stdout) FLUSH;
  1248.         settle_down();
  1249.         goto reask_catchup;
  1250.     }
  1251.     for (i = firstart; i <= lastart; i++) {
  1252.         oneless(i);        /* mark as read */
  1253.     }
  1254. #ifdef USETHREADS
  1255.     selected_root_cnt = selected_count = 0;
  1256. #endif
  1257. #ifdef DELAYMARK
  1258.     if (dmfp)
  1259.         yankback();
  1260. #endif
  1261.     if (*buf == 'u') {
  1262.         rcchar[ng] = NEGCHAR;
  1263.         return AS_CLEAN;
  1264.     }
  1265. #ifdef USETHREADS
  1266.     p_art = Nullart;
  1267.     selected_count = 0;
  1268. #endif
  1269.     art = lastart+1;
  1270.     forcelast = FALSE;
  1271.     return AS_NORM;
  1272.     case 'Q':
  1273.     exit_code = NG_ASK;
  1274.     /* FALL THROUGH */
  1275.     case 'q':            /* go back up to newsgroup level? */
  1276.     return AS_CLEAN;
  1277.     case 'j':
  1278.     putchar('\n') FLUSH;
  1279.     if (art <= lastart)
  1280.         mark_as_read();
  1281.     return AS_ASK;
  1282.     case 'h': {            /* help? */
  1283.     int cmd;
  1284.  
  1285.     if ((cmd = help_art()) > 0)
  1286.         pushchar(cmd);
  1287.     return AS_ASK;
  1288.     }
  1289.     case '&':
  1290.     if (switcheroo()) /* get rest of command */
  1291.         return AS_INP;    /* if rubbed out, try something else */
  1292.     return AS_ASK;
  1293.     case '#':
  1294. #ifdef VERBOSE
  1295.     IF(verbose)
  1296.         printf("\nThe last article is %ld.\n",(long)lastart) FLUSH;
  1297.     ELSE
  1298. #endif
  1299. #ifdef TERSE
  1300.         printf("\n%ld\n",(long)lastart) FLUSH;
  1301. #endif
  1302.     return AS_ASK;
  1303. #ifdef USETHREADS
  1304.     case '+':            /* enter thread selection mode */
  1305.     if (ThreadedGroup) {
  1306. select_threads:
  1307.         *buf = select_thread(*buf);
  1308.         if (*buf == 'q') {
  1309.         putchar( '\n' ) FLUSH;
  1310.         return AS_ASK;
  1311.         }
  1312.         if (*buf == 'Q') {
  1313.         exit_code = NG_ASK;
  1314.         return AS_CLEAN;
  1315.         }
  1316.         if (*buf == 'N' || !toread[ng])
  1317.         return AS_CLEAN;
  1318.         return AS_NORM;
  1319.     }
  1320.     /* FALLTHROUGH */
  1321. #endif
  1322.     case '=': {            /* list subjects */
  1323.     char tmpbuf[256];
  1324.     ART_NUM oldart = art;
  1325.     int cmd;
  1326.     char *subjline = getval("SUBJLINE",Nullch);
  1327. #ifndef CACHESUBJ
  1328.     char *s;
  1329. #endif
  1330.  
  1331.     page_init();
  1332. #ifdef CACHESUBJ
  1333.     if (!subj_list)
  1334.         fetchsubj(art,TRUE,FALSE);
  1335. #endif
  1336.     for (i=firstart; i<=lastart && !int_count; i++) {
  1337. #ifdef CACHESUBJ
  1338.         if (!was_read(i) &&
  1339.           (subj_list[OFFSET(i)] != Nullch || fetchsubj(i,FALSE,FALSE)) &&
  1340.           *subj_list[OFFSET(i)] ) {
  1341.         sprintf(tmpbuf,"%5ld ", i);
  1342.         if (subjline) {
  1343.             art = i;
  1344.             interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline);
  1345.         }
  1346.         else
  1347.             safecpy(tmpbuf + 6, subj_list[OFFSET(i)],
  1348.             (sizeof tmpbuf) - 6);
  1349.         if (cmd = print_lines(tmpbuf,NOMARKING)) {
  1350.             if (cmd > 0)
  1351.             pushchar(cmd);
  1352.             break;
  1353.         }
  1354.         }
  1355. #else
  1356.         if (!was_read(i) && (s = fetchsubj(i,FALSE,FALSE)) && *s) {
  1357.         sprintf(tmpbuf,"%5ld ", i);
  1358.         if (subjline) {    /* probably fetches it again! */
  1359.             art = i;
  1360.             interp(tmpbuf + 6, (sizeof tmpbuf) - 6, subjline);
  1361.         }
  1362.         else
  1363.             safecpy(tmpbuf + 6, s, (sizeof tmpbuf) - 6);
  1364.         if (cmd = print_lines(tmpbuf,NOMARKING)) {
  1365.             if (cmd > 0)
  1366.             pushchar(cmd);
  1367.             break;
  1368.         }
  1369.         }
  1370. #endif
  1371.     }
  1372.     int_count = 0;
  1373.     art = oldart;
  1374.     return AS_ASK;
  1375.     }
  1376.     case '^':
  1377. #ifdef USETHREADS
  1378.     first_art();
  1379. #else
  1380.     art = firstart;
  1381. #endif
  1382. #ifdef ARTSEARCH
  1383.     srchahead = 0;
  1384. #endif
  1385.     return AS_NORM;
  1386. #if defined(CACHESUBJ) && defined(DEBUGGING)
  1387.     case 'D':
  1388.     printf("\nFirst article: %ld\n",(long)firstart) FLUSH;
  1389.     if (!subj_list)
  1390.         fetchsubj(art,TRUE,FALSE);
  1391.     if (subj_list != Null(char **)) {
  1392.         for (i=1; i<=lastart && !int_count; i++) {
  1393.         if (subj_list[OFFSET(i)])
  1394.             printf("%5ld %c %s\n",
  1395.             i, (was_read(i)?'y':'n'), subj_list[OFFSET(i)]) FLUSH;
  1396.         }
  1397.     }
  1398.     int_count = 0;
  1399.     return AS_ASK;
  1400. #endif
  1401.     case 'v':
  1402.     if (art <= lastart) {
  1403.         reread = TRUE;
  1404.         do_hiding = FALSE;
  1405.     }
  1406.     return AS_NORM;
  1407. #ifdef ROTATION
  1408.     case Ctl('x'):
  1409. #endif
  1410.     case Ctl('r'):
  1411. #ifdef ROTATION
  1412.     rotate = (*buf==Ctl('x'));
  1413. #endif
  1414.     if (art <= lastart)
  1415.         reread = TRUE;
  1416.     return AS_NORM;
  1417. #ifdef ROTATION
  1418.     case 'X':
  1419.     rotate = !rotate;
  1420.     /* FALL THROUGH */
  1421. #else
  1422.     case Ctl('x'):
  1423.     case 'x':
  1424.     case 'X':
  1425.     notincl("x");
  1426.     return AS_ASK;
  1427. #endif
  1428.     case 'l': case Ctl('l'):        /* refresh screen */
  1429.     if (art <= lastart) {
  1430.         reread = TRUE;
  1431.         clear();
  1432.         do_fseek = TRUE;
  1433.         artline = topline;
  1434.         if (artline < 0)
  1435.         artline = 0;
  1436.     }
  1437.     return AS_NORM;
  1438.     case 'b': case Ctl('b'):        /* back up a page */
  1439.     if (art <= lastart) {
  1440.         ART_LINE target;
  1441.  
  1442.         reread = TRUE;
  1443.         clear();
  1444.         do_fseek = TRUE;
  1445.         target = topline - (LINES - 2);
  1446.         artline = topline;
  1447.         if (artline >= 0) do {
  1448.         artline--;
  1449.         } while(artline >= 0 && artline > target && vrdary(artline-1) >= 0);
  1450.         topline = artline;
  1451.         if (artline < 0)
  1452.         artline = 0;
  1453.     }
  1454.     return AS_NORM;
  1455.     case '!':            /* shell escape */
  1456.     if (escapade())
  1457.         return AS_INP;
  1458.     return AS_ASK;
  1459.     case 'C': {
  1460.     cancel_article();
  1461.     return AS_ASK;
  1462.     }
  1463.     case 'R':
  1464.     case 'r': {            /* reply? */
  1465.     reply();
  1466.     return AS_ASK;
  1467.     }
  1468.     case 'F':
  1469.     case 'f': {            /* followup command */
  1470.     followup();
  1471.     forcegrow = TRUE;        /* recalculate lastart */
  1472.     return AS_ASK;
  1473.     }
  1474.     case '|':
  1475.     case 'w': case 'W':
  1476.     case 's': case 'S':        /* save command */
  1477.     case 'e':            /* extract command */
  1478.     if (save_article() == SAVE_ABORT)
  1479.         return AS_INP;
  1480.     int_count = 0;
  1481.     return AS_ASK;
  1482.     case 'E':
  1483.     if (uu_out != Nullfp) {
  1484.         uud_end();
  1485.     }
  1486.     return AS_ASK;
  1487. #ifdef DELAYMARK
  1488.     case 'Y':                /* yank back M articles */
  1489.     yankback();
  1490. #ifdef USETHREADS
  1491.     first_art();
  1492. #else
  1493.     art = firstart;            /* from the beginning */
  1494. #endif
  1495.     return AS_NORM;            /* pretend nothing happened */
  1496. #endif
  1497. #ifdef STRICTCR
  1498.     case '\n':
  1499.     fputs(badcr,stdout) FLUSH;
  1500.     return AS_ASK;
  1501. #endif
  1502.     default:
  1503.     printf("\n%s",hforhelp) FLUSH;
  1504.     settle_down();
  1505.     return AS_ASK;
  1506.     }
  1507. }
  1508.  
  1509. #ifdef MAILCALL
  1510. /* see if there is any mail */
  1511.  
  1512. void
  1513. setmail()
  1514. {
  1515.     if (! (mailcount++)) {
  1516.     char *mailfile = filexp(getval("MAILFILE",MAILFILE));
  1517.     
  1518.     if (stat(mailfile,&filestat) < 0 || !filestat.st_size
  1519.         || filestat.st_atime > filestat.st_mtime)
  1520.         mailcall = nullstr;
  1521.     else
  1522.         mailcall = getval("MAILCALL","(Mail) ");
  1523.     }
  1524.     mailcount %= 10;            /* check every 10 articles */
  1525. }
  1526. #endif
  1527.  
  1528. void
  1529. setdfltcmd()
  1530. {
  1531. #ifdef USETHREADS
  1532.     if (toread[ng] == unthreaded) {
  1533. #else
  1534.     if (!toread[ng]) {
  1535. #endif
  1536.     if (art > lastart)
  1537.         dfltcmd = "qnp";
  1538.     else
  1539.         dfltcmd = "npq";
  1540.     }
  1541.     else {
  1542. #ifdef USETHREADS
  1543.     if (selected_root_cnt && !selected_count)
  1544.         dfltcmd = "+npq";
  1545.     else
  1546. # ifdef ARTSEARCH
  1547.     if (!ThreadedGroup && srchahead)
  1548.         dfltcmd = "^Nnpq";
  1549.     else
  1550. # endif
  1551. #else
  1552. # ifdef ARTSEARCH
  1553.     if (srchahead)
  1554.         dfltcmd = "^Nnpq";
  1555.     else
  1556. # endif
  1557. #endif
  1558.         dfltcmd = "npq";
  1559.     }
  1560. }
  1561.