home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume13 / vn.jan.88 / part05 / session.c < prev   
Encoding:
C/C++ Source or Header  |  1988-01-30  |  19.0 KB  |  1,033 lines

  1. /*
  2. ** vn news reader.
  3. **
  4. ** session.c - top session loop
  5. **
  6. ** see copyright disclaimer / history in vn.c source file
  7. */
  8. #include <stdio.h>
  9. #include <setjmp.h>
  10. #include "config.h"
  11. #include "tty.h"
  12. #include "brk.h"
  13. #include "head.h"
  14. #include "tune.h"
  15. #include "node.h"
  16. #include "page.h"
  17. #include "vn.h"
  18.  
  19. extern NODE **Newsorder;
  20. extern char Erasekey, Killkey;
  21. extern int Rot;
  22. extern char *Ps1,*Printer;
  23. extern char *Orgdir,*Savefile;
  24. extern int Ncount, Cur_page, Lrec, L_allow, C_allow;
  25. extern int Headflag;
  26. extern PAGE Page;
  27. extern int Digest;
  28. extern char *No_msg;
  29. extern char *Hdon_msg;
  30. extern char *Hdoff_msg;
  31. extern char *Roton_msg;
  32. extern char *Rotoff_msg;
  33. extern char Cxitop[], Cxptoi[];
  34. extern char *Aformat;
  35. extern char *Contstr;
  36. extern char *Kl,*Kr,*Ku,*Kd;
  37. extern int Nounsub, Listfirst;
  38. extern char *List_sep;
  39. extern char *Version, *Vns_version;
  40.  
  41. extern int (*Headedit)();
  42.  
  43. static int C_info;
  44. static int Dskip, Drec;
  45.  
  46. static char *Unsub_msg = "Unsubscribed";
  47. static char *Egroup_msg = "Entire newsgroup";
  48.  
  49. static int Crec;
  50. static int Highrec;
  51.  
  52. /*
  53.     main session handler processing input commands
  54.  
  55.     NOTE: this is where a setjmp call is made to set the break reentry
  56.         location.  Keep the possible user states in mind.
  57. */
  58. session ()
  59. {
  60.     char alist [RECLEN], c;
  61.     int newg, i, count;
  62.     jmp_buf brkbuf;
  63.  
  64.     newg = new_groups();
  65.     tty_set (RAWMODE);
  66.     find_page (0);
  67.     Digest = 0;
  68.  
  69.     /* reentry point for break from within session interaction */
  70.     setjmp (brkbuf);
  71.     sig_set (BRK_SESS,brkbuf);
  72.     Headflag = FALSE;
  73.     Rot = 0;
  74.  
  75.     /* done this way so that user gets "really quit?" break treatment */
  76.     if (newg > 0)
  77.     {
  78.         printf ("\n%s",Contstr);
  79.         getnoctl();
  80.         newg = 0;
  81.     }
  82.  
  83.     /* list preview option - clear after first time for long jumps */
  84.     if (Listfirst)
  85.     {
  86.         /* tot_list settings will be overwritten in this case */
  87.         tot_list();
  88.         Listfirst = 0;
  89.     }
  90.  
  91.     /* if breaking from a digest, recover original page */
  92.     if (Digest)
  93.     {
  94.         find_page(Cur_page);
  95.         Digest = 0;
  96.     }
  97.     show ();
  98.     Crec = RECBIAS;
  99.     Highrec = Page.h.artnum + RECBIAS;
  100.     term_set (MOVE,0,Crec);
  101.  
  102.     /*
  103.         handle commands until QUIT, update global/local status
  104.         and display for each.
  105.      */
  106.     for (count = getkey(&c); c != QUIT; count = getkey(&c))
  107.     {
  108.         if ( srch_help(c,&i) != 0 || (Digest != 0 && i == 0))
  109.         {
  110.             preinfo (UDKFORM,Cxptoi[HELP]);
  111.             term_set (MOVE, 0, Crec);
  112.             continue;
  113.         }
  114.  
  115.         switch (c)
  116.         {
  117.         case HEADTOG:
  118.             if (Headedit != NULL)
  119.             {
  120.                 term_set(ERASE);
  121.                 (*Headedit)();
  122.                 show();
  123.                 term_set (MOVE,0,Crec);
  124.                 break;
  125.             }
  126.             if (Headflag)
  127.             {
  128.                 Headflag = FALSE;
  129.                 prinfo (Hdoff_msg);
  130.             }
  131.             else
  132.             {
  133.                 Headflag = TRUE;
  134.                 prinfo (Hdon_msg);
  135.             }
  136.             term_set (MOVE,0,Crec);
  137.             break;
  138.         case SETROT:
  139.             if (Rot == 0)
  140.             {
  141.                 Rot = 13;
  142.                 prinfo (Roton_msg);
  143.             }
  144.             else
  145.             {
  146.                 Rot = 0;
  147.                 prinfo (Rotoff_msg);
  148.             }
  149.             term_set (MOVE,0,Crec);
  150.             break;
  151.         case SSTAT:
  152.             count_msg ();
  153.             term_set (MOVE,0,Crec);
  154.             break;
  155.         case GRPLIST:
  156.             tot_list ();
  157.             show();
  158.             term_set (MOVE,0,Crec);
  159.             break;
  160.         case REDRAW:
  161.             show();
  162.             term_set (MOVE,0,Crec);
  163.             break;
  164.         case UNSUBSCRIBE:
  165.             new_sub(Page.h.group,0);
  166.             do_update(Unsub_msg);
  167.             term_set (MOVE,0,Crec);
  168.             break;
  169.  
  170.         case UPDATE:
  171.             new_read(Page.h.group,Page.b[Crec-RECBIAS].art_id);
  172.             wr_show ();
  173.             do_update("Updated to cursor");
  174.             term_set (MOVE,0,Crec);
  175.             break;
  176.         case UPALL:
  177.             new_read(Page.h.group,(Page.h.group)->highnum);
  178.             wr_show();
  179.             do_update(Egroup_msg);
  180.             term_set (MOVE,0,Crec);
  181.             break;
  182.         case ORGGRP:
  183.             new_read(Page.h.group,(Page.h.group)->orgrd);
  184.             wr_show();
  185.             do_update(Egroup_msg);
  186.             term_set (MOVE,0,Crec);
  187.             break;
  188.         case UPSEEN:
  189.             up_seen();
  190.             wr_show();
  191.             do_update("All pages displayed to this point updated");
  192.             term_set (MOVE,0,Crec);
  193.             break;
  194.         case ORGSTAT:
  195.             for (i = 0; i < Ncount; ++i)
  196.                 new_read(Newsorder[i],(Newsorder[i])->orgrd);
  197.             wr_show();
  198.             do_update("Original data recovered");
  199.             term_set (MOVE,0,Crec);
  200.             break;
  201.          case TOPMOVE:
  202.              Crec = RECBIAS;
  203.              term_set (MOVE, 0, Crec);
  204.              break;
  205.          case BOTMOVE:
  206.          case ALTBOTTOM:
  207.              Crec = Highrec - 1;
  208.              term_set (MOVE, 0, Crec);
  209.              break;
  210.          case MIDMOVE:
  211.              Crec = (RECBIAS + Highrec - 1) / 2;
  212.              if (Crec < RECBIAS)
  213.                  Crec = RECBIAS;
  214.              if (Crec >= Highrec)
  215.                  Crec = Highrec - 1;
  216.              term_set (MOVE, 0, Crec);
  217.              break;
  218.         case UP:
  219.             if (Crec != RECBIAS)
  220.             {
  221.                 Crec -= count;
  222.                 if (Crec < RECBIAS)
  223.                     Crec = RECBIAS;
  224.                 term_set (MOVE, 0, Crec);
  225.             }
  226.             else
  227.                 putchar ('\07');
  228.             break;
  229.         case DOWN:
  230.             if (Crec < (Highrec - 1))
  231.             {
  232.                 Crec += count;
  233.                 if (Crec >= Highrec)
  234.                     Crec = Highrec - 1;
  235.                 term_set (MOVE, 0, Crec);
  236.             }
  237.             else
  238.                 putchar ('\07');
  239.             break;
  240.         case MARK:
  241.         case ART_MARK:
  242.             count += Crec - 1;
  243.             if (count >= Highrec)
  244.                 count = Highrec - 1;
  245.             for (i=Crec; i <= count; ++i)
  246.             {
  247.                 if (Page.b[i-RECBIAS].art_mark != ART_MARK)
  248.                     Page.b[i-RECBIAS].art_mark = ART_MARK;
  249.                 else
  250.                     Page.b[i-RECBIAS].art_mark = ' ';
  251.                 if (i != Crec)
  252.                     term_set (MOVE, 0, i);
  253.                 printf ("%c\010",Page.b[i-RECBIAS].art_mark);
  254.             }
  255.             if (count != Crec)
  256.                 term_set (MOVE, 0, Crec);
  257.             write_page ();
  258.             break;
  259.         case UNMARK:
  260.             for (i=0; i < Page.h.artnum; ++i)
  261.             {
  262.                 if (Page.b[i].art_mark == ART_MARK)
  263.                 {
  264.                     Page.b[i].art_mark = ' ';
  265.                     term_set (MOVE, 0, i+RECBIAS);
  266.                     putchar (' ');
  267.                 }
  268.             }
  269.             term_set (MOVE, 0, Crec);
  270.             write_page ();
  271.             break;
  272.         case BACK:
  273.             count *= -1;    /* fall through */
  274.         case FORWARD:
  275.             if (forward (count) >= 0)
  276.                 show();
  277.             else
  278.                 preinfo ("No more pages");
  279.             term_set (MOVE,0,Crec);
  280.             break;
  281.         case DIGEST:
  282.             if (Digest)
  283.             {
  284.                 Digest = 0;
  285.                 find_page (Cur_page);
  286.                 show();
  287.                 Crec = Drec + RECBIAS + 1;
  288.                 Highrec = Page.h.artnum + RECBIAS;
  289.                 if (Crec >= Highrec)
  290.                     Crec = Highrec - 1;
  291.                 term_set (MOVE,0,Crec);
  292.                 break;
  293.             }
  294.             (Page.h.group)->flags |= FLG_ACC;
  295.             Dskip = count - 1;
  296.             Drec = Crec - RECBIAS;
  297.             if (digest_page(Drec,Dskip) >= 0)
  298.             {
  299.                 show();
  300.                 Crec = RECBIAS;
  301.                 Highrec = Page.h.artnum + RECBIAS;
  302.                 term_set (MOVE,0,Crec);
  303.                 break;
  304.             }
  305.             Digest = 0;
  306.             preinfo ("Can't unpack the article");
  307.             term_set (MOVE,0,Crec);
  308.             break;
  309.         case NEWGROUP:
  310.             if ((i = spec_group()) < 0)
  311.             {
  312.                 term_set (MOVE,0,Crec);
  313.                 break;
  314.             }
  315.             Digest = 0;
  316.             show();
  317.             Crec = RECBIAS;
  318.             Highrec = Page.h.artnum + RECBIAS;
  319.             term_set (MOVE,0,Crec);
  320.             break;
  321.  
  322.         case SAVE:
  323.             (Page.h.group)->flags |= FLG_ACC;
  324.             genlist (alist,Crec-RECBIAS,count);
  325.             savestr (alist);
  326.             term_set (MOVE,0,Crec);
  327.             break;
  328.         case SAVEALL:
  329.             (Page.h.group)->flags |= FLG_ACC;
  330.             genlist (alist,0,L_allow);
  331.             savestr (alist);
  332.             term_set (MOVE,0,Crec);
  333.             break;
  334.         case SAVESTRING:
  335.         case ALTSAVE:
  336.             (Page.h.group)->flags |= FLG_ACC;
  337.             userlist (alist);
  338.             savestr (alist);
  339.             term_set (MOVE,0,Crec);
  340.             break;
  341.         case READ:
  342.         case ALTREAD:
  343.             (Page.h.group)->flags |= FLG_ACC;
  344.             genlist (alist,Crec-RECBIAS,count);
  345.             readstr (alist,count);
  346.             break;
  347.         case READALL:
  348.             (Page.h.group)->flags |= FLG_ACC;
  349.             genlist (alist,0,L_allow);
  350.             readstr (alist,0);
  351.             break;
  352.         case READSTRING:
  353.             (Page.h.group)->flags |= FLG_ACC;
  354.             userlist (alist);
  355.             readstr (alist,0);
  356.             break;
  357.         case PRINT:
  358.             (Page.h.group)->flags |= FLG_ACC;
  359.             genlist (alist,Crec-RECBIAS,count);
  360.             printstr (alist);
  361.             term_set (MOVE,0,Crec);
  362.             break;
  363.         case PRINTALL:
  364.             (Page.h.group)->flags |= FLG_ACC;
  365.             genlist (alist,0,L_allow);
  366.             printstr (alist);
  367.             term_set (MOVE, 0, Crec);
  368.             break;
  369.         case PRINTSTRING:
  370.             (Page.h.group)->flags |= FLG_ACC;
  371.             userlist (alist);
  372.             printstr (alist);
  373.             term_set (MOVE, 0, Crec);
  374.             break;
  375.  
  376.         case HELP:
  377.             help ();
  378.             show ();
  379.             term_set (MOVE, 0, Crec);
  380.             break;
  381.         case UNESC:
  382.             user_str (alist,Ps1,1,"");
  383.             term_set (ERASE);
  384.             fflush (stdout);
  385.             tty_set (SAVEMODE);
  386.             if (chdir(Orgdir) < 0)
  387.                 printf ("change to original directory, %s, failed",Orgdir);
  388.             else
  389.             {
  390.                 system (alist);
  391.                 tty_set (RESTORE);
  392.                 term_set (RESTART);
  393.             }
  394.             printf (Contstr);
  395.             getnoctl ();
  396.             vns_gset(Page.h.name);
  397.             show ();
  398.             term_set (MOVE, 0, Crec);
  399.             break;
  400.         case PRTVERSION:
  401.             prinfo("%s %s", Version, Vns_version);
  402.             term_set (MOVE, 0, Crec);
  403.             break;
  404.         default:
  405.             preinfo("Unhandled key: %c", c);
  406.             break;
  407.         }
  408.     }
  409.  
  410.     Digest = 0;
  411.     for (i=0; i < Ncount; ++i)
  412.     {
  413.         if ((Newsorder[i])->rdnum < (Newsorder[i])->pgrd)
  414.             break;
  415.     }
  416.     if (i < Ncount)
  417.     {
  418.         user_str (alist,"Some displayed pages not updated - update ? ",
  419.                             1, QUIT_ANSWER);
  420.         if (alist[0] == 'y')
  421.             up_seen();
  422.     }
  423.     sig_set (BRK_OUT);
  424. }
  425.  
  426. /*
  427. ** update status of Newsgroups to all seen pages
  428. */
  429. up_seen()
  430. {
  431.     int i;
  432.  
  433.     for (i = 0; i < Ncount; ++i)
  434.     {
  435.         if (Nounsub && ((Newsorder[i])->flags & FLG_SUB) == 0)
  436.         {
  437.             new_read(Newsorder[i],(Newsorder[i])->highnum);
  438.             continue;
  439.         }
  440.         if ((Newsorder[i])->rdnum < (Newsorder[i])->pgrd)
  441.             new_read(Newsorder[i],(Newsorder[i])->pgrd);
  442.     }
  443. }
  444.  
  445. /*
  446.     count_msg displays count information
  447. */
  448. count_msg ()
  449. {
  450.     int i, gpnum, gscan, gpage;
  451.     unsigned long mask;
  452.     gpnum = 1;
  453.     for (gscan = gpage = i = 0; i<Ncount; ++i)
  454.     {
  455.         if (((Newsorder[i])->flags & FLG_PAGE) != 0)
  456.         {
  457.             if (((Newsorder[i])->pnum + (Newsorder[i])->pages - 1) < Cur_page)
  458.                 ++gpnum;
  459.             ++gpage;
  460.             for (mask=1; mask != 0L; mask <<= 1)
  461.                 if (((Newsorder[i])->pgshwn & mask) != 0L)
  462.                     ++gscan;
  463.         }
  464.     }
  465.     prinfo (CFORMAT,Cur_page+1,Lrec+1,gscan,gpnum,gpage);
  466. }
  467.  
  468. /*
  469.     forward utility handles paging
  470. */
  471. static
  472. forward (count)
  473. int count;
  474. {
  475.     if (!Digest)
  476.     {
  477.         if ((count < 0 && Cur_page <= 0) || (count > 0 && Cur_page >= Lrec))
  478.             return (-1);
  479.         Cur_page += count;
  480.         if (Cur_page < 0)
  481.             Cur_page = 0;
  482.         if (Cur_page > Lrec)
  483.             Cur_page = Lrec;
  484.         find_page (Cur_page);
  485.         Crec = RECBIAS;
  486.         Highrec = Page.h.artnum + RECBIAS;
  487.         return (0);
  488.     }
  489.     /*
  490.     ** in digests, paging past the end of the digest returns to
  491.     ** page extracted from.
  492.     */
  493.     if (Dskip > 0 && (Dskip + count*L_allow) < 0)
  494.         Dskip = 0;
  495.     else
  496.         Dskip += count * L_allow;
  497.     find_page (Cur_page);
  498.     if (Dskip >= 0)
  499.     {
  500.         if (digest_page(Drec,Dskip) >= 0)
  501.         {
  502.             Crec = RECBIAS;
  503.             Highrec = Page.h.artnum + RECBIAS;
  504.             return (0);
  505.         }
  506.     }
  507.     Digest = 0;
  508.     Crec = Drec + RECBIAS + 1;
  509.     Highrec = Page.h.artnum + RECBIAS;
  510.     if (Crec >= Highrec)
  511.         Crec = Highrec - 1;
  512.     return (0);
  513. }
  514.  
  515. /*
  516.     generate list of articles on current page,
  517.     count articles, starting with first.
  518. */
  519. static
  520. genlist (list,first,count)
  521. char *list;
  522. int first,count;
  523. {
  524.     int i;
  525.     for (i=first; i < Page.h.artnum && count > 0; ++i)
  526.     {
  527.         sprintf (list,"%d ",Page.b[i].art_id);
  528.         list += strlen(list);
  529.         --count;
  530.     }
  531. }
  532.  
  533. /*
  534.     send list of articles to printer
  535. */
  536. static
  537. printstr (s)
  538. char *s;
  539. {
  540.     char cmd [RECLEN];
  541.     char fn[L_tmpnam];
  542.     char dsave[RECLEN];    /* save list for unlinking */
  543.  
  544.     prinfo ("preparing print command ....");
  545.  
  546.     if (Digest)
  547.     {
  548.         dig_list (s);
  549.         strcpy(dsave,s);
  550.     }
  551.  
  552.     if (*s != '\0')
  553.     {
  554.         tmpnam(fn);
  555.         if (art_xfer(fn,s,"w") != 0)
  556.         {
  557.             preinfo("Couldn't open temporary file");
  558.             return;
  559.         }
  560.         sprintf (cmd,"%s %s 2>/dev/null",Printer,fn);
  561.         if (system (cmd) == 0)
  562.             prinfo ("Sent to printer");
  563.         else
  564.             preinfo ("Print failed");
  565.         unlink(fn);
  566.     }
  567.     else
  568.         preinfo (No_msg);
  569.  
  570.     if (Digest)
  571.         dig_ulist (dsave);
  572. }
  573.  
  574. /*
  575.     read a list of articles.
  576. */
  577. readstr (s,count)
  578. char *s;
  579. int count;
  580. {
  581.     char *strtok();
  582.     char *fn[MAXARTLIST+1];
  583.     int pc, num, i;
  584.  
  585.     /* we pre-process tokens to release strtok() for further use */
  586.     fn[0] = strtok(s,List_sep);
  587.     for (num=0; fn[num] != NULL; fn[(++num)] = strtok(NULL,List_sep))
  588.         if (num >= MAXARTLIST)
  589.             break;
  590.     fn[num] = NULL;
  591.  
  592.     if (fn[0] != NULL)
  593.     {
  594.         term_set (ERASE);
  595.         for (i=0; i < num && readfile(fn[i], fn[i+1] ,&pc) >= 0; ++i)
  596.         {
  597.             if (Digest)
  598.                 unlink (fn[i]);
  599.         }
  600.         if (Digest && fn[i] != NULL)
  601.             unlink (fn[i]);
  602.         if (pc != 0)
  603.             forward (pc);
  604.         else
  605.         {
  606.             Crec += count;
  607.             if (Crec >= Highrec)
  608.                 Crec = Highrec - 1;
  609.         }
  610.         show ();
  611.         term_set (MOVE, 0, Crec);
  612.     }
  613.     else
  614.     {
  615.         preinfo ("%s",No_msg);
  616.         term_set (MOVE, 0, Crec);
  617.     }
  618. }
  619.  
  620. /*
  621.     concatenate articles to save file with appropriate infoline messages.
  622.     prompt for save file, giving default.  If save file begins with "|"
  623.     handle as a filter to pipe to.  NOTE - every user specification of
  624.     a new Savefile "loses" some storage, but it shouldn't be a very great
  625.     amount.
  626. */
  627. static
  628. savestr (s)
  629. char *s;
  630. {
  631.     char *ptr, newfile [MAX_C+1], msg[RECLEN];
  632.     char *str_store();
  633.     char dsave[RECLEN];
  634.  
  635.     if (Digest)
  636.     {
  637.         dig_list (s);
  638.         strcpy(dsave,s);
  639.     }
  640.  
  641.     if (*s != '\0')
  642.     {
  643.         user_str (newfile,"save file ? ",1,Savefile);
  644.         ptr = newfile;
  645.         if (*ptr == '|')
  646.         {
  647.             term_set (ERASE);
  648.             fflush (stdout);
  649.             save_art(s,ptr,msg);
  650.             printf ("%s\n%s",msg,Contstr);
  651.             getnoctl ();
  652.             show ();
  653.         }
  654.         else
  655.         {
  656.             prinfo("saving ....");
  657.             if (*ptr == '\0')
  658.                 ptr = Savefile;
  659.             else
  660.                 Savefile = str_store(ptr);
  661.             if (save_art(s,Savefile,msg) != 0)
  662.                 preinfo(msg);
  663.             else
  664.                 prinfo(msg);
  665.         }
  666.     }
  667.     else
  668.         preinfo (No_msg);
  669.  
  670.     if (Digest)
  671.         dig_ulist (dsave);
  672. }
  673.  
  674. /*
  675.     basic page display routine.  erase screen and format current page
  676. */
  677. static
  678. show ()
  679. {
  680.     int i;
  681.     unsigned long mask;
  682.     char helpstr[40]; 
  683.  
  684.     term_set (ERASE);
  685.     C_info = 0;
  686.     i = Cur_page - (Page.h.group)->pnum + 1;
  687.     if (Digest)
  688.         printf (DHFORMAT,Page.h.name);
  689.     else
  690.         printf (HFORMAT,Page.h.name,i,(Page.h.group)->pages);
  691.  
  692.     mask = 1L << (i-1);
  693.     (Page.h.group)->pgshwn |= mask;
  694.     mask = 1;
  695.     for (--i; i > 0 && (mask & (Page.h.group)->pgshwn) != 0 ; --i)
  696.         mask <<= 1;
  697.     if (i <= 0)
  698.         (Page.h.group)->pgrd = Page.b[(Page.h.artnum)-1].art_id;
  699.  
  700.     for (i=0; i < Page.h.artnum; ++i)
  701.     {
  702.         if (Digest)
  703.         {
  704.             printf(Aformat,Page.b[i].art_mark,ART_UNWRITTEN,Page.b[i].art_id);
  705.             printf("%s",Page.b[i].art_t);
  706.             continue;
  707.         }
  708.  
  709.         if ((Page.h.group)->rdnum >= Page.b[i].art_id)
  710.             printf(Aformat,Page.b[i].art_mark,ART_WRITTEN,Page.b[i].art_id);
  711.         else
  712.             printf(Aformat,Page.b[i].art_mark,ART_UNWRITTEN,Page.b[i].art_id);
  713.         printf("%s",Page.b[i].art_t);
  714.     }
  715.  
  716.     sprintf(helpstr,HELPFORM,Cxptoi[HELP]);
  717.     if (!Digest && ((Page.h.group)->flags & FLG_SUB) == 0)
  718.         prinfo ("%s, %s",Unsub_msg,helpstr);
  719.     else
  720.         prinfo (helpstr);
  721. }
  722.  
  723. /*
  724.     update written status marks on screen
  725. */
  726. static
  727. wr_show ()
  728. {
  729.     int i,row;
  730.     char c;
  731.  
  732.     row = RECBIAS;
  733.     for (i=0; i < Page.h.artnum; ++i)
  734.     {
  735.         term_set (MOVE,WRCOL,row);
  736.         if ((Page.h.group)->rdnum >= Page.b[i].art_id)
  737.             c = ART_WRITTEN;
  738.         else
  739.             c = ART_UNWRITTEN;
  740.         printf("%c",c);
  741.         ++row;
  742.     }
  743. }
  744.  
  745. /*
  746.     obtain user input of group name, becomes current page if valid.
  747.     returns -1 or page number.  calling routine does the show, if needed
  748. */
  749. static
  750. spec_group ()
  751. {
  752.     char nbuf [MAX_C + 1];
  753.     NODE *p, *hashfind();
  754.  
  755.     user_str(nbuf,"Newsgroup ? ",1,"");
  756.  
  757.     if (*nbuf == '\0' || (p = hashfind(nbuf)) == NULL)
  758.     {
  759.         preinfo ("Not a newsgroup");
  760.         return (-1);
  761.     }
  762.     if ((p->flags & FLG_PAGE) == 0)
  763.     {
  764.         if ((p->flags & FLG_SUB) == 0)
  765.         {
  766.             new_sub(p,FLG_SUB);
  767.             do_update("Not subscribed: resubscribed for next reading session");
  768.         }
  769.         else
  770.             prinfo ("No news for that group");
  771.         return (-1);
  772.     }
  773.     if ((p->flags & FLG_SUB) == 0)
  774.     {
  775.         new_sub(p,FLG_SUB);
  776.         do_update("Resubscribed");
  777.     }
  778.     find_page (p->pnum);
  779.     return (p->pnum);
  780. }
  781.  
  782. /*
  783.     obtain user input with prompt p.  Optionally on info line.
  784.     handle erase and kill characters, suppresses leading
  785.     white space.  Use defstr as the editable default user input.
  786.     If on info line, cursor is not moved anywhere whe done, otherwise
  787.     a <CR><LF> is done after input.  Should be in raw mode to use
  788.     this routine.  Used from outside this source file so that we
  789.     only have to do erase / kill key stuff one place.
  790. */
  791. user_str (s,p,iline,defstr)
  792. char *s;
  793. char *p;
  794. int iline;
  795. char *defstr;
  796. {
  797.     int i,idx,len;
  798.  
  799.     if (iline)
  800.     {
  801.          prinfo ("%s%s",p,defstr);
  802.         idx = C_info;
  803.     }
  804.     else
  805.     {
  806.          printf ("%s%s",p,defstr);
  807.         idx = strlen(p);
  808.     }
  809.  
  810.     len = strlen(defstr);
  811.      for (i=0; i < len; i++)
  812.          s[i] = defstr[i];
  813.  
  814.     for (i=len; idx < C_allow && (s[i] = getchar() & 0x7f) != '\012' && s[i] != '\015'; ++i)
  815.     {
  816.         if (s[i] == Erasekey)
  817.         {
  818.             if (i > 0)
  819.             {
  820.                 term_set (RUBSEQ);
  821.                 i -= 2;
  822.                 --idx;
  823.             }
  824.             else
  825.                 i = -1;
  826.             continue;
  827.         }
  828.         if (s[i] == Killkey)
  829.         {
  830.              if (iline)
  831.              {
  832.                  prinfo ("%s",p);
  833.                  idx = C_info;
  834.              }
  835.              else
  836.              {
  837.                  printf ("\r%s",p);
  838.                  term_set(ZAP,strlen(p),idx);
  839.                  fflush(stdout);
  840.                  idx = strlen(p);
  841.              }
  842.             i = -1;
  843.             continue;
  844.         }
  845.         /* no leading spaces */
  846.         if (s[i] == ' ' && i == 0)
  847.         {
  848.             i = -1;
  849.             putchar('\07');
  850.             continue;
  851.         }
  852.         /* no controls */
  853.         if (s[i] < ' ' || s[i] == '\177')
  854.         {
  855.             --i;
  856.             putchar('\07');
  857.             continue;
  858.         }
  859.         ++idx;
  860.         putchar (s[i]);
  861.     }
  862.  
  863.     if (iline)
  864.         C_info = idx;
  865.     else
  866.         printf("\r\n");
  867.  
  868.     s[i] = '\0';
  869. }
  870.  
  871. /*
  872.     print something on the information line,
  873.     clearing any characters not overprinted.
  874.     preinfo includes reverse video and a bell for error messages.
  875. */
  876. preinfo (s,a,b,c,d,e,f)
  877. {
  878.     int l;
  879.     char buf[RECLEN];
  880.  
  881.     term_set (MOVE,0,INFOLINE);
  882.     putchar ('\07');
  883.     term_set (ONREVERSE);
  884.     sprintf (buf,s,a,b,c,d,e,f);
  885.     printf (" %s ",buf);
  886.     term_set (OFFREVERSE);
  887.     l = strlen(buf) + 2;
  888.     if (l < C_info)
  889.         term_set (ZAP,l,C_info);
  890.     C_info = l;
  891.     fflush(stdout);
  892. }
  893.  
  894. prinfo (s,a,b,c,d,e,f)
  895. char *s;
  896. long a,b,c,d,e,f;
  897. {
  898.     int l;
  899.     char buf[RECLEN];
  900.     term_set (MOVE,0,INFOLINE);
  901.     sprintf (buf,s,a,b,c,d,e,f);
  902.     printf ("%s",buf);
  903.     l = strlen(buf);
  904.     if (l < C_info)
  905.         term_set (ZAP,l,C_info);
  906.     C_info = l;
  907.     fflush(stdout);
  908. }
  909.  
  910. static
  911. tot_list ()
  912. {
  913.     int i,max,len;
  914.     char c;
  915.     char ff[MAX_C+1];
  916.  
  917.     term_set (ERASE);
  918.  
  919.     for (max=i=0; i < Ncount; ++i)
  920.     {
  921.         if ((Newsorder[i])->pages == 0)
  922.             continue;
  923.         if ((len = strlen((Newsorder[i])->nd_name)) > max)
  924.             max = len;
  925.     }
  926.  
  927.     sprintf (ff,"%%4d %%%ds: %%3d new %%3d updated\n",max);
  928.  
  929.     for (len=i=0; i < Ncount; ++i)
  930.     {
  931.         if ((Newsorder[i])->pages == 0)
  932.             continue;
  933.         printf (ff, i, (Newsorder[i])->nd_name,
  934.                 (Newsorder[i])->highnum - (Newsorder[i])->orgrd,
  935.                 (Newsorder[i])->rdnum - (Newsorder[i])->orgrd);
  936.         ++len;
  937.         if (len == L_allow && i < (Ncount-1))
  938.         {
  939.             printf("\nr - return, n - new group, other to continue ... ");
  940.             if ((c = getnoctl()) == 'r' || c == 'n')
  941.                 break;
  942.             printf ("\n\n");
  943.             len = 0;
  944.         }
  945.     }
  946.     if (i >= Ncount)
  947.     {
  948.         printf("n - new group, other to return ... ");
  949.         c = getnoctl();
  950.     }
  951.  
  952.     /* c will remain 'n' while user chooses bad newsgroups */
  953.     while (c == 'n')
  954.     {
  955.         printf("\n");
  956.         user_str(ff,"Newsgroup number ? ",0,"");
  957.         i = atoi(ff);
  958.          if (i < 0 || i >= Ncount || (Newsorder[i])->pages == 0)
  959.         {
  960.              printf("\nBad newsgroup number\n");
  961.              printf("n - new group, other to return ... ");
  962.             c = getnoctl();
  963.             continue;
  964.          }
  965.          find_page((Newsorder[i])->pnum);
  966.         Crec = RECBIAS;
  967.         Highrec = Page.h.artnum + RECBIAS;
  968.         c = '\0';
  969.     }
  970. }
  971.  
  972. /*
  973. ** call vns_write if anything has changed, then wipe FLG_ECHG bits
  974. ** also produce message(s)
  975. */
  976. static
  977. do_update(msg)
  978. char *msg;
  979. {
  980.     int i;
  981.  
  982.     for (i=0; i < Ncount; ++i)
  983.         if(((Newsorder[i])->flags & FLG_ECHG) != 0)
  984.             break;
  985.     if (i < Ncount)
  986.     {
  987.         prinfo("Writing news status");
  988.         vns_write(Newsorder,Ncount);
  989.         for (i=0; i < Ncount; ++i)
  990.             (Newsorder[i])->flags &= ~FLG_ECHG;
  991.     }
  992.     prinfo(msg);
  993. }
  994.  
  995. /*
  996. ** set a new rdnum value.  If a change, set FLG_ECHG
  997. */
  998. static
  999. new_read(n,rd)
  1000. NODE *n;
  1001. int rd;
  1002. {
  1003.     if (n->rdnum != rd)
  1004.     {
  1005.         n->rdnum = rd;
  1006.         n->flags |= FLG_ECHG;
  1007.     }
  1008. }
  1009.  
  1010. /*
  1011. ** set a new subscription bit.  bit argument is either 0 or FLG_SUB.
  1012. */
  1013. static
  1014. new_sub(n,bit)
  1015. NODE *n;
  1016. unsigned bit;
  1017. {
  1018.     /*
  1019.     ** since bit is 0 or FLG_SUB, we could get tricky with ^
  1020.     ** but this is clearer
  1021.     */
  1022.     if (bit != 0 && (n->flags & FLG_SUB) == 0)
  1023.         n->flags |= FLG_SUB|FLG_ECHG;
  1024.     else
  1025.     {
  1026.         if (bit == 0 && (n->flags & FLG_SUB) != 0)
  1027.         {
  1028.             n->flags &= ~FLG_SUB;
  1029.             n->flags |= FLG_ECHG;
  1030.         }
  1031.     }
  1032. }
  1033.