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

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