home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / trn / part10 / ng.c < prev   
Encoding:
C/C++ Source or Header  |  1991-12-02  |  34.2 KB  |  1,586 lines

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