home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 3 / 3199 < prev    next >
Encoding:
Internet Message Format  |  1991-04-18  |  41.1 KB

  1. From: skrenta@blekko.commodore.com (Rich Skrenta)
  2. Newsgroups: alt.sources
  3. Subject: Tass 3.2 newsreader part 3/3
  4. Message-ID: <161@blekko.commodore.com>
  5. Date: 17 Apr 91 17:02:04 GMT
  6.  
  7.  
  8. # This is a shell archive.  Remove anything before this line,
  9. # then unpack it by saving it in a file and typing "sh file".
  10. #
  11. # This archive contains:
  12. #    page.c        screen.c    select.c    spool_open.c    
  13. #
  14.  
  15. echo x - page.c
  16. cat >page.c <<'@EOF'
  17.  
  18. #include    <stdio.h>
  19. #include    <signal.h>
  20. #include    <sys/types.h>
  21. #include    "tass.h"
  22.  
  23.  
  24. #define        MAX_PAGES    1000
  25. #define        NOTE_UNAVAIL    -1
  26.  
  27. char note_h_path[LEN];            /* Path:    */
  28. char note_h_date[LEN];            /* Date:    */
  29. char note_h_subj[LEN];            /* Subject:    */
  30. char note_h_from[LEN];            /* From:    */
  31. char note_h_org[LEN];            /* Organization: */
  32. char note_h_newsgroups[LEN];        /* Newsgroups:    */
  33. char note_h_messageid[LEN];        /* Message-ID:    */
  34. char note_h_distrib[LEN];        /* Distribution: */
  35. char note_h_followup[LEN];        /* Followup-To: */
  36.  
  37. int    note_line;
  38. int    note_page;        /* what page we're on */
  39. long    note_mark[MAX_PAGES];    /* ftells on beginnings of pages */
  40. FILE    *note_fp;        /* the body of the current article */
  41. int    note_end;        /* we're done showing this article */
  42. int    rotate;            /* 0=normal, 13=rot13 decode */
  43.  
  44. long    note_size;        /* stat size in bytes of article */
  45.  
  46. char    note_full_name[100];
  47. char    note_from_addr[100];
  48.  
  49.  
  50. int last_resp;        /* current & previous article for - command */
  51. int this_resp;
  52.  
  53. int glob_respnum;
  54. char *glob_page_group;
  55. extern int cur_groupnum;
  56.  
  57.  
  58. #ifdef SIGTSTP
  59. void
  60. page_susp(i)
  61. int i;
  62. {
  63.  
  64.     Raw(FALSE);
  65.     putchar('\n');
  66.     signal(SIGTSTP, SIG_DFL);
  67. #ifdef BSD
  68.         sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
  69. #endif
  70.     kill(0, SIGTSTP);
  71.  
  72.     signal(SIGTSTP, page_susp);
  73.     mail_setup();
  74.     Raw(TRUE);
  75.     redraw_page(glob_respnum, glob_page_group);
  76. }
  77. #endif
  78.  
  79.  
  80. show_page(respnum, group, group_path)
  81. int respnum;
  82. char *group;
  83. char *group_path;
  84. {
  85.     char ch;
  86.     int n;
  87.     int i;
  88.     long art;
  89.  
  90. restart:
  91.  
  92.     glob_respnum = respnum;
  93.     glob_page_group = group;
  94.  
  95. #ifdef SIGTSTP
  96.     signal(SIGTSTP, page_susp);
  97. #endif
  98.  
  99.     if (respnum != this_resp) {       /* remember current & previous */
  100.         last_resp = this_resp;       /* articles for - command */
  101.         this_resp = respnum;
  102.     }
  103.  
  104.     rotate = 0;            /* normal mode, not rot13 */
  105.     art = arts[respnum].artnum;
  106.     arts[respnum].unread = 0;    /* mark article as read */
  107.     open_note(art, group_path);
  108.  
  109.     if (note_page == NOTE_UNAVAIL) {
  110.         ClearScreen();
  111.         printf("[Article %ld unvailable]\r\r", art);
  112.         fflush(stdout);
  113.     } else
  114.         show_note_page(respnum, group);
  115.  
  116.     while (1) {
  117.         ch = ReadCh();
  118.  
  119.         if (ch >= '0' && ch <= '9') {
  120.  
  121.             n = prompt_response(ch, respnum);
  122.             if (n != -1) {
  123.                 respnum = n;
  124.                 goto restart;
  125.             }
  126.  
  127.         } else switch (ch) {
  128.             case 'a':    /* author search forward */
  129.             case 'A':    /* author search backward */
  130.                 i = (ch == 'a');
  131.                 n = search_author(respnum, i, group);
  132.                 if (n < 0)
  133.                     break;
  134.  
  135.                 respnum = n;
  136.                 goto restart;
  137.                 break;
  138.  
  139.             case '|':    /* pipe article into command */
  140.                 pipe_article();
  141.                 redraw_page(respnum, group);
  142.                 break;
  143.  
  144.             case 'I':    /* toggle inverse video */
  145.                 inverse_okay = !inverse_okay;
  146.                 if (inverse_okay)
  147.                     info_message("Inverse video enabled");
  148.                 else
  149.                     info_message("Inverse video disabled");
  150.                 goto pager_ctrlr;
  151.                 break;
  152.  
  153.             case 's':
  154.                 save_art_to_file();
  155.                 break;
  156.  
  157.             case 'S':
  158.                 save_thread_to_file(respnum, group_path);
  159.                 break;
  160.  
  161.             case ctrl('X'):
  162.             case '%':    /* toggle rot-13 mode */
  163.                 if (rotate)
  164.                     rotate = 0;
  165.                 else
  166.                     rotate = 13;
  167.                 redraw_page(respnum, group);
  168.         /*        goto pager_ctrlr;    */
  169.                 break;
  170.  
  171.             case 'P':    /* previous unread article */
  172.                 n = prev_unread(prev_response(respnum));
  173.                 if (n == -1)
  174.                     info_message("No previous unread article");
  175.                 else {
  176.                     note_cleanup();
  177.                     respnum = n;
  178.                     goto restart;
  179.                 }
  180.                 break;
  181.  
  182.             case 'F':    /* post a followup to this article */
  183.                 if (post_response(group, TRUE)) {
  184.                     update_newsrc(group,
  185.                         my_group[cur_groupnum]);
  186.                     n = which_base(respnum);
  187.                     note_cleanup();
  188.                     index_group(group, group_path);
  189.                     read_newsrc_line(group);
  190.                     respnum = choose_resp(n, nresp(n));
  191.                     goto restart;
  192.                 } else
  193.                     redraw_page(respnum, group);
  194.                 break;
  195.  
  196.             case 'f':    /* post a followup to this article */
  197.                 if (post_response(group, FALSE)) {
  198.                     update_newsrc(group,
  199.                         my_group[cur_groupnum]);
  200.                     n = which_base(respnum);
  201.                     note_cleanup();
  202.                     index_group(group, group_path);
  203.                     read_newsrc_line(group);
  204.                     respnum = choose_resp(n, nresp(n));
  205.                     goto restart;
  206.                 } else
  207.                     redraw_page(respnum, group);
  208.                 break;
  209.  
  210.             case 'z':    /* mark article as unread (to return) */
  211.                 arts[respnum].unread = 2;
  212.                 info_message("Article marked as unread");
  213.                 break;
  214.  
  215.             case 'K':    /* mark rest of thread as read */
  216.                 for (n = respnum; n >= 0; n = arts[n].thread)
  217.                     arts[n].unread = 0;
  218.                 n = next_unread(next_response(respnum));
  219.                 if (n == -1)
  220.                     goto return_to_index;
  221.                 else {
  222.                     note_cleanup();
  223.                     respnum = n;
  224.                     goto restart;
  225.                 }
  226.                 break;
  227.  
  228.             case 'i':    /* return to index page */
  229. return_to_index:
  230.                 note_cleanup();
  231.                 return( which_base(respnum) );
  232.  
  233.             case 't':    /* return to group selection page */
  234.                 note_cleanup();
  235.                 return -1;
  236.  
  237.             case ctrl('R'):      /* redraw beginning of article */
  238. pager_ctrlr:
  239.                 if (note_page == NOTE_UNAVAIL) {
  240.                     ClearScreen();
  241.                     printf("[Article %ld unvailable]\r\n",
  242.                             arts[respnum].artnum);
  243.                     fflush(stdout);
  244.                 } else {
  245.                     note_page = 0;
  246.                     note_end = FALSE;
  247.                     fseek(note_fp, note_mark[0], 0);
  248.                     show_note_page(respnum, group);
  249.                 }
  250.                 break;
  251.  
  252.             case '!':
  253.                 shell_escape();
  254.                 redraw_page(respnum, group);
  255.                 break;
  256.  
  257.             case '\b':
  258.             case 'b':    /* back a page */
  259.                 if (note_page == NOTE_UNAVAIL
  260.                 ||  note_page <= 1) {
  261.                     note_cleanup();
  262.                     n = prev_response(respnum);
  263.                     if (n == -1)
  264.                         return( which_resp(respnum) );
  265.  
  266.                     respnum = n;
  267.                     goto restart;
  268.  
  269.                 } else {
  270.                     note_page -= 2;
  271.                     note_end = FALSE;
  272.                     fseek(note_fp, note_mark[note_page], 0);
  273.                     show_note_page(respnum, group);
  274.                 }
  275.                 break;
  276.  
  277.             case 'm':    /* mail article to somebody */
  278.                 mail_to_someone();
  279.                 redraw_page(respnum, group);
  280.                 break;
  281.  
  282.             case 'r':    /* reply to author through mail */
  283.                 mail_to_author(FALSE);
  284.                 redraw_page(respnum, group);
  285.                 break;
  286.  
  287.             case 'R':    /* reply to author, copy text */
  288.                 mail_to_author(TRUE);
  289.                 redraw_page(respnum, group);
  290.                 break;
  291.  
  292.             case '-':    /* show last viewed article */
  293.                 if (last_resp < 0) {
  294.                     info_message("No last message");
  295.                     break;
  296.                 }
  297.                 note_cleanup();
  298.                 respnum = last_resp;
  299.                 goto restart;
  300.  
  301.  
  302.             case 'p':    /* previous article */
  303.                 note_cleanup();
  304.                 n = prev_response(respnum);
  305.                 if (n == -1)
  306.                     return( which_resp(respnum) );
  307.  
  308.                 respnum = n;
  309.                 goto restart;
  310.  
  311.             case 'n':    /* skip to next article */
  312.                 note_cleanup();
  313.                 n = next_response(respnum);
  314.                 if (n == -1)
  315.                     return( which_base(respnum) );
  316.  
  317.                 respnum = n;
  318.                 goto restart;
  319.  
  320.             case 'k':
  321.                 if (note_page == NOTE_UNAVAIL) {
  322.                     n = next_unread(next_response(respnum));
  323.                     if (n == -1)
  324.                         return( which_base(respnum) );
  325.  
  326.                     respnum = n;
  327.                     goto restart;
  328.  
  329.                 } else {
  330.                     note_cleanup();
  331.                     n = next_unread(next_response(respnum));
  332.                     if (n == -1)
  333.                         return( which_base(respnum) );
  334.  
  335.                     respnum = n;
  336.                     goto restart;
  337.                 }
  338.                 break;
  339.  
  340.             case ' ':     /* next page or response */
  341.                 if (note_page == NOTE_UNAVAIL) {
  342.                     n = next_response(respnum);
  343.                     if (n == -1)
  344.                         return( which_base(respnum) );
  345.  
  346.                     respnum = n;
  347.                     goto restart;
  348.  
  349.                 } else if (note_end) {
  350.                     note_cleanup();
  351.                     n = next_response(respnum);
  352.                     if (n == -1)
  353.                         return( which_base(respnum) );
  354.  
  355.                     respnum = n;
  356.                     goto restart;
  357.                 } else
  358.                     show_note_page(respnum, group);
  359.                 break;
  360.  
  361.             case '\t':     /* next page or unread response */
  362.                 if (note_page == NOTE_UNAVAIL) {
  363.                     n = next_unread(next_response(respnum));
  364.                     if (n == -1)
  365.                         return( which_base(respnum) );
  366.  
  367.                     respnum = n;
  368.                     goto restart;
  369.  
  370.                 } else if (note_end) {
  371.                     note_cleanup();
  372.                     n = next_unread(next_response(respnum));
  373.                     if (n == -1)
  374.                         return( which_base(respnum) );
  375.  
  376.                     respnum = n;
  377.                     goto restart;
  378.                 } else
  379.                     show_note_page(respnum, group);
  380.                 break;
  381.  
  382.             case 'N':    /* next unread article */
  383.                 n = next_unread(next_response(respnum));
  384.                 if (n == -1)
  385.                     info_message("No next unread article");
  386.                 else {
  387.                     note_cleanup();
  388.                     respnum = n;
  389.                     goto restart;
  390.                 }
  391.                 break;
  392.  
  393.             case '\r':
  394.             case '\n':    /* go to start of next thread */
  395.                 note_cleanup();
  396.                 n = next_basenote(respnum);
  397.                 if (n == -1)
  398.                     return( which_base(respnum) );
  399.  
  400.                 respnum = n;
  401.                 goto restart;
  402.  
  403.             case 'q':    /* quit */
  404.                 return -2;
  405.  
  406.             case 'H':    /* show article headers */
  407.                 if (note_page == NOTE_UNAVAIL) {
  408.                     n = next_response(respnum);
  409.                     if (n == -1)
  410.                         return( which_base(respnum) );
  411.  
  412.                     respnum = n;
  413.                     goto restart;
  414.                 } else {
  415.                     note_page = 0;
  416.                     note_end = FALSE;
  417.                     fseek(note_fp, 0L, 0);
  418.                     show_note_page(respnum, group);
  419.                 }
  420.                 break;
  421.  
  422.  
  423.             case 'h':
  424.                 tass_page_help();
  425.                 redraw_page(respnum, group);
  426.                 break;
  427.  
  428.             default:
  429.                 info_message("Bad command.  Type 'h' for help.");
  430.         }
  431.     }
  432. }
  433.  
  434.  
  435. note_cleanup() {
  436.  
  437.     if (note_page != NOTE_UNAVAIL)
  438.         fclose(note_fp);
  439. }
  440.  
  441.  
  442. redraw_page(respnum, group)
  443. int respnum;
  444. char *group;
  445. {
  446.  
  447.     if (note_page == NOTE_UNAVAIL) {
  448.         ClearScreen();
  449.         printf("[Article %ld unvailable]\r\r", arts[respnum].artnum);
  450.         fflush(stdout);
  451.     } else if (note_page > 0) {
  452.         note_page--;
  453.         fseek(note_fp, note_mark[note_page], 0);
  454.         show_note_page(respnum, group);
  455.     }
  456. }
  457.  
  458.  
  459. show_note_page(respnum, group)
  460. int respnum;
  461. char *group;
  462. {
  463.     char buf[LEN];
  464.     char buf2[LEN+50];
  465.     int percent;
  466.     char *p, *q;
  467.     int i, j;
  468.     int ctrl_L;        /* form feed character detected */
  469.  
  470.     ClearScreen();
  471.  
  472.     note_line = 1;
  473.  
  474.     if (note_page == 0)
  475.         show_first_header(respnum, group);
  476.     else
  477.         show_cont_header(respnum);
  478.  
  479.     ctrl_L = FALSE;
  480.     while (note_line < LINES) {
  481.         if (fgets(buf, LEN, note_fp) == NULL) {
  482.             note_end = TRUE;
  483.             break;
  484.         }
  485.  
  486.         buf[LEN-1] = '\0';
  487.         if (rotate)
  488.             for (p = buf, q = buf2;
  489.                     *p && *p != '\n' && q<&buf2[LEN]; p++) {
  490.                 if (*p == '\b' && q > buf2) {
  491.                     q--;
  492.                 } else if (*p == 12) {        /* ^L */
  493.                     *q++ = '^';
  494.                     *q++ = 'L';
  495.                     ctrl_L = TRUE;
  496.                 } else if (*p == '\t') {
  497.                     i = q - buf2;
  498.                     j = (i|7) + 1;
  499.  
  500.                     while (i++ < j)
  501.                         *q++ = ' ';
  502.                 } else if (((*p)&0x7F) < 32) {
  503.                     *q++ = '^';
  504.                     *q++ = ((*p)&0x7F) + '@';
  505.                 } else if (*p >= 'A' && *p <= 'Z')
  506.                     *q++ = 'A' + (*p - 'A' + rotate) % 26;
  507.                 else if (*p >= 'a' && *p <= 'z')
  508.                     *q++ = 'a' + (*p - 'a' + rotate) % 26;
  509.                 else
  510.                     *q++ = *p;
  511.             }
  512.         else
  513.             for (p = buf, q = buf2;
  514.                     *p && *p != '\n' && q<&buf2[LEN]; p++) {
  515.                 if (*p == '\b' && q > buf2) {
  516.                     q--;
  517.                 } else if (*p == 12) {        /* ^L */
  518.                     *q++ = '^';
  519.                     *q++ = 'L';
  520.                     ctrl_L = TRUE;
  521.                 } else if (*p == '\t') {
  522.                     i = q - buf2;
  523.                     j = (i|7) + 1;
  524.  
  525.                     while (i++ < j)
  526.                         *q++ = ' ';
  527.                 } else if (((*p)&0x7F) < 32) {
  528.                     *q++ = '^';
  529.                     *q++ = ((*p)&0x7F) + '@';
  530.                 } else
  531.                     *q++ = *p;
  532.             }
  533.  
  534.         *q = '\0';
  535.  
  536.         printf("%s\r\n", buf2);
  537.  
  538. #if 1
  539.         note_line += (strlen(buf2) / COLS) + 1;
  540. #else
  541.         if (*buf2)
  542.             note_line += (strlen(buf2) + COLS) / (COLS+1);
  543.         else
  544.             note_line++;
  545. #endif
  546.         if (ctrl_L)
  547.             break;
  548.     }
  549.  
  550.     note_mark[++note_page] = ftell(note_fp);
  551.  
  552.     MoveCursor(LINES, MORE_POS);
  553. /*    StartInverse();    */
  554.     if (note_end) {
  555.         if (arts[respnum].thread != -1)
  556.             printf("-- next response --");
  557.         else
  558.             printf("-- last response --");
  559.     } else {
  560.         if (note_size > 0) {
  561.             percent = note_mark[note_page] * 100 / note_size;
  562.             printf("--More--(%d%%)", percent);
  563.         } else
  564.             printf("--More--");
  565.     }
  566. /*    EndInverse();    */
  567.  
  568.     fflush(stdout);
  569. }
  570.  
  571.  
  572. show_first_header(respnum, group)
  573. int respnum;
  574. char *group;
  575. {
  576.     int whichresp;
  577.     int x_resp;
  578.     char buf[200];
  579.     char tmp[200];
  580.     int pos, i;
  581.     int n;
  582.  
  583.     whichresp = which_resp( respnum );
  584.     x_resp = nresp( which_base(respnum) );
  585.  
  586.     ClearScreen();
  587.  
  588.     strcpy(buf, note_h_date);
  589.     pos = (COLS - strlen(group)) / 2;
  590.     for (i = strlen(buf); i < pos; i++)
  591.         buf[i] = ' ';
  592.     buf[i] = '\0';
  593.  
  594.     strcat(buf, group);
  595.  
  596.     for (i = strlen(buf); i < RIGHT_POS; i++)
  597.         buf[i] = ' ';
  598.     buf[i] = '\0';
  599.  
  600.     printf("%sNote %3d of %3d\r\n", buf, which_base(respnum) + 1, top_base);
  601.  
  602.     sprintf(buf, "Article %ld  ", arts[respnum].artnum);
  603.     n = strlen(buf);
  604.     fputs(buf, stdout);
  605.  
  606.     pos = (COLS - strlen( note_h_subj )) / 2 - 2;
  607.  
  608.     if (pos > n)
  609.         MoveCursor(1, pos);
  610.     else
  611.         MoveCursor(1, n);
  612.  
  613.     StartInverse();
  614.     strcpy(buf, note_h_subj);
  615.     buf[RIGHT_POS - 2 - n] = '\0';
  616.     fputs(buf, stdout);
  617.     EndInverse();
  618.  
  619.     MoveCursor(1, RIGHT_POS);
  620.     if (whichresp)
  621.         printf("Resp %3d of %3d\r\n", whichresp, x_resp);
  622.     else {
  623.         if (x_resp == 0)
  624.             printf("No responses\r\n");
  625.         else if (x_resp == 1)
  626.             printf("1 Response\r\n");
  627.         else
  628.             printf("%d Responses\r\n", x_resp);
  629.     }
  630.  
  631.     if (*note_h_org)
  632.         sprintf(tmp, "%s at %s", note_full_name, note_h_org);
  633.     else
  634.         strcpy(tmp, note_full_name);
  635.  
  636.     tmp[79] = '\0';
  637.  
  638.     sprintf(buf, "%s  ", note_from_addr);
  639.  
  640.     pos = COLS - 1 - strlen(tmp);
  641.     if (strlen(buf) + strlen(tmp) >= COLS - 1) {
  642.         strncat(buf, tmp, COLS - 1 - strlen(buf));
  643.         buf[COLS - 1] = '\0';
  644.     } else {
  645.         for (i = strlen(buf); i < pos; i++)
  646.             buf[i] = ' ';
  647.         buf[i] = '\0';
  648.         strcat(buf, tmp);
  649.     }
  650.     printf("%s\r\n\r\n", buf);
  651.  
  652.     note_line += 4;
  653. }
  654.  
  655.  
  656. show_cont_header(respnum)
  657. int respnum;
  658. {
  659. int whichresp;
  660. int whichbase;
  661. char buf[200];
  662.  
  663.     whichresp = which_resp(respnum);
  664.     whichbase = which_base(respnum);
  665.  
  666.     assert (whichbase < top_base);
  667.  
  668.     if (whichresp)
  669.         sprintf(buf, "Note %d of %d, Resp %d (page %d):  %s",
  670.             whichbase + 1,
  671.             top_base,
  672.             whichresp,
  673.             note_page + 1,
  674.             note_h_subj);
  675.     else
  676.         sprintf(buf, "Note %d of %d (page %d):  %s",
  677.             whichbase + 1,
  678.             top_base,
  679.             note_page + 1,
  680.             note_h_subj);
  681.  
  682.     buf[COLS] = '\0';
  683.     printf("%s\r\n\r\n", buf);
  684.  
  685.     note_line += 2;
  686. }
  687.  
  688.  
  689. open_note(art, group_path)
  690. long art;
  691. char *group_path;
  692. {
  693.     char buf[1025];
  694.     char *p;
  695.     extern FILE *open_art_fp();
  696.  
  697.     note_page = 0;
  698.  
  699.     note_fp = open_art_fp(group_path, art);
  700.     if (note_fp == NULL) {
  701.         note_page = NOTE_UNAVAIL;
  702.         return;
  703.     }
  704.  
  705.     note_h_from[0] = '\0';
  706.     note_h_path[0] = '\0';
  707.     note_h_subj[0] = '\0';
  708.     note_h_org[0] = '\0';
  709.     note_h_date[0] = '\0';
  710.     note_h_newsgroups[0] = '\0';
  711.     note_h_messageid[0] = '\0';
  712.     note_h_distrib[0] = '\0';
  713.     note_h_followup[0] = '\0';
  714.  
  715.     while (fgets(buf, 1024, note_fp) != NULL) {
  716.         buf[1024] = '\0';
  717.  
  718.         for (p = buf; *p && *p != '\n'; p++)
  719.             if (((*p)&0x7F) < 32)
  720.                 *p = ' ';
  721.         *p = '\0';
  722.  
  723.         if (*buf == '\0')
  724.             break;
  725.  
  726.         if (strncmp(buf, "From: ", 6) == 0) {
  727.             strncpy(note_h_from, &buf[6], LEN);
  728.             note_h_from[LEN-1] = '\0';
  729.         } else if (strncmp(buf, "Path: ", 6) == 0) {
  730.             strncpy(note_h_path, &buf[6], LEN);
  731.             note_h_path[LEN-1] = '\0';
  732.         } else if (strncmp(buf, "Subject: ", 9) == 0) {
  733.             strncpy(note_h_subj, &buf[9], LEN);
  734.             note_h_subj[LEN-1] = '\0';
  735.         } else if (strncmp(buf, "Organization: ", 14) == 0) {
  736.             strncpy(note_h_org, &buf[14], LEN);
  737.             note_h_org[LEN-1] = '\0';
  738.         } else if (strncmp(buf, "Date: ", 6) == 0) {
  739.             strncpy(note_h_date, &buf[6], LEN);
  740.             note_h_date[LEN-1] = '\0';
  741.         } else if (strncmp(buf, "Newsgroups: ", 12) == 0) {
  742.             strncpy(note_h_newsgroups, &buf[12], LEN);
  743.             note_h_newsgroups[LEN-1] = '\0';
  744.         } else if (strncmp(buf, "Message-ID: ", 12) == 0) {
  745.             strncpy(note_h_messageid, &buf[12], LEN);
  746.             note_h_messageid[LEN-1] = '\0';
  747.         } else if (strncmp(buf, "Distribution: ", 14) == 0) {
  748.             strncpy(note_h_distrib, &buf[14], LEN);
  749.             note_h_distrib[LEN-1] = '\0';
  750.         } else if (strncmp(buf, "Followup-To: ", 13) == 0) {
  751.             strncpy(note_h_followup, &buf[13], LEN);
  752.             note_h_followup[LEN-1] = '\0';
  753.         }
  754.     }
  755.  
  756.     note_page = 0;
  757.     note_mark[0] = ftell(note_fp);
  758.  
  759.     parse_from(note_h_from, note_from_addr, note_full_name);
  760.     note_end = FALSE;
  761.  
  762.     return;
  763. }
  764.  
  765.  
  766. prompt_response(ch, respnum)
  767. int respnum;
  768. {
  769.     int num;
  770.  
  771.     clear_message();
  772.  
  773.     if ((num = parse_num(ch, "Read response> ")) == -1) {
  774.         clear_message();
  775.         return(-1);
  776.     }
  777.  
  778.     return choose_resp( which_base(respnum), num );
  779. }
  780.  
  781.  
  782. /*
  783.  *  return response number n from thread i
  784.  */
  785.  
  786. choose_resp(i, n)
  787. int i;
  788. int n;
  789. {
  790.     int j;
  791.  
  792.     j = base[i];
  793.  
  794.     while (n-- && arts[j].thread >= 0)
  795.         j = arts[j].thread;
  796.  
  797.     return j;
  798. }
  799.  
  800.  
  801. /*
  802.  *  Parse various From: lines into the component mail addresses and
  803.  *  real names
  804.  */
  805.  
  806. parse_from(str, addr, name)
  807. char *str;
  808. char *addr;
  809. char *name;
  810. {
  811.     char *p;
  812.  
  813.     for (p = str; *p; p++)
  814.         if (((*p) & 0x7F) < 32)
  815.             *p = ' ';
  816.  
  817.     while (*str && *str != ' ')
  818.         *addr++ = *str++;
  819.     *addr = '\0';
  820.     if (*str++ == ' ') {
  821.         if (*str++ == '(') {
  822.             if (*str == '"')
  823.                 str++;  /* Kill "quotes around names"         */
  824.                     /* But don't touch quotes inside the  */
  825.                     /* Name (that's what that nonsense    */
  826.                     /* below is for                  */
  827.             while (*str && *str != ')' && !(*str=='"'&&str[1]==')'))
  828.                 *name++ = *str++;
  829.         }
  830.     }
  831.     *name = '\0';
  832. }
  833.  
  834.  
  835. tass_page_help() {
  836.     char title[100];
  837.  
  838.     sprintf(title, "%s, Article Pager Commands", TASS_HEADER);
  839.  
  840.     ClearScreen();
  841.     center_line(0, title);
  842.  
  843.     MoveCursor(2, 0);
  844.  
  845.     printf("\t4\tRead response 4 in this thread (0 is basenote)\r\n");
  846.     printf("\t<CR>\tSkip to next base article\r\n");
  847.     printf("\t<TAB>\tAdvance to next page or unread article\r\n");
  848.     printf("\taA\tAuthor search forward (A=backward)\r\n");
  849.     printf("\tb\tBack a page\r\n");
  850.     printf("\tfF\tPost a followup (F=include text)\r\n");
  851.     printf("\tH\tShow article headers\r\n");
  852.     printf("\ti\tReturn to index page\r\n");
  853. printf("\tkK\tMark article (K=thread) as read & advance to next unread\r\n");
  854.     printf("\tm\tMail this article to someone\r\n");
  855.     printf("\tnN\tSkip to the next (N=unread) article)\r\n");
  856.     printf("\tpP\tGo to the previous (P=unread) article\r\n");
  857.     printf("\tq\tQuit\r\n");
  858.     printf("\trR\tReply through mail (R=include text) to author\r\n");
  859.     printf("\tsS\tSave article (S=thread) to file\r\n");
  860.     printf("\tt\tReturn to group selection page\r\n");
  861.     printf("\tz\tMark article as unread\r\n");
  862.     printf("\t^R\tRedisplay first page of article\r\n");
  863.     printf("\t%%, ^X\tToggle rot-13 decoding for this article\r\n");
  864.     printf("\t-\tShow last article\r\n");
  865.     printf("\t|\tPipe article into command\r\n");
  866.  
  867.     center_line(LINES, "-- hit any key --");
  868.     ReadCh();
  869. }
  870.  
  871.  
  872. yank_to_addr(orig, addr)
  873. char *orig;
  874. char *addr;
  875. {
  876.     char *p;
  877.  
  878.     for (p = orig; *p; p++)
  879.         if (((*p) & 0x7F) < 32)
  880.             *p = ' ';
  881.  
  882.     while (*addr)
  883.         addr++;
  884.  
  885.     while (*orig) {
  886.         while (*orig && (*orig == ' ' || *orig == '"' || *orig == ','))
  887.             orig++;
  888.         *addr++ = ' ';
  889.         while (*orig && (*orig != ' ' && *orig != ',' && *orig != '"'))
  890.             *addr++ = *orig++;
  891.         while (*orig && (*orig == ' ' || *orig == '"' || *orig == ','))
  892.             orig++;
  893.         if (*orig == '(') {
  894.             while (*orig && *orig != ')')
  895.                 orig++;
  896.             if (*orig == ')')
  897.                 orig++;
  898.         }
  899.     }
  900.     *addr = '\0';
  901. }
  902.  
  903.  
  904. /*
  905.  *  Read a file grabbing the address given for To: and
  906.  *  sticking it in mail_to
  907.  */
  908.  
  909. find_new_to(nam, mail_to)
  910. char *nam;
  911. char *mail_to;
  912. {
  913.     FILE *fp;
  914.     char buf[LEN];
  915.     char buf2[LEN];
  916.     char dummy[LEN];
  917.     char new_mail_to[LEN];
  918.     char *p;
  919.  
  920.     *new_mail_to = '\0';
  921.  
  922.     fp = fopen(nam, "r");
  923.     if (fp == NULL) {
  924.         fprintf(stderr, "reopen of %s failed\n", nam);
  925.         return;
  926.     }
  927.  
  928.     while (fgets(buf, 1024, fp) != NULL) {
  929.         for (p = buf; *p && *p != '\n'; p++) ;
  930.         *p = '\0';
  931.  
  932.         if (*buf == '\0')
  933.             break;
  934.  
  935.         if (strncmp(buf, "To: ", 4) == 0) {
  936.             strncpy(buf2, &buf[4], LEN);
  937.             buf2[LEN-1] = '\0';
  938.             yank_to_addr(buf2, new_mail_to);
  939.         } else if (strncmp(buf, "Cc: ", 4) == 0) {
  940.             strncpy(buf2, &buf[4], LEN);
  941.             buf2[LEN-1] = '\0';
  942.             yank_to_addr(buf2, new_mail_to);
  943.         }
  944.     }
  945.  
  946.     fclose(fp);
  947.     if (new_mail_to[0] == ' ')
  948.         strcpy(mail_to, &new_mail_to[1]);
  949.     else
  950.         strcpy(mail_to, new_mail_to);
  951. }
  952.  
  953.  
  954. mail_to_someone() {
  955.     char nam[100];
  956.     FILE *fp;
  957.     char ch;
  958.     char buf[200];
  959.     char mail_to[LEN+1];
  960.     char subj[LEN+1];
  961.  
  962.     if (!parse_string("Mail article to: ", mail_to))
  963.         return;
  964.     if (mail_to[0] == '\0')
  965.         return;
  966.  
  967.     setuid(real_uid);
  968.     setgid(real_gid);
  969.  
  970.     sprintf(nam, "%s/.letter", homedir);
  971.     if ((fp = fopen(nam, "w")) == NULL) {
  972.         fprintf(stderr, "can't open %s: ", nam);
  973.         perror("");
  974.         setuid(tass_uid);
  975.         setgid(tass_gid);
  976.         return(FALSE);
  977.     }
  978.     chmod(nam, 0600);
  979.  
  980.     fprintf(fp, "To: %s\n", mail_to);
  981.     fprintf(fp, "Subject: %s\n", note_h_subj);
  982.     if (*note_h_followup)
  983.         fprintf(fp, "Newsgroups: %s\n\n", note_h_followup);
  984.     else
  985.         fprintf(fp, "Newsgroups: %s\n", note_h_newsgroups);
  986.     if (*my_org)
  987.         fprintf(fp, "Organization: %s\n", my_org);
  988.     fputs("\n", fp);
  989.  
  990.     fseek(note_fp, 0L, 0);
  991.     copy_fp(note_fp, fp, "");
  992.  
  993.     add_signature(fp, TRUE);
  994.     fclose(fp);
  995.  
  996.     while (1) {
  997.         do {
  998.             MoveCursor(LINES, 0);
  999.             fputs("abort, edit, send: ", stdout);
  1000.             fflush(stdout);
  1001.             ch = ReadCh();
  1002.         } while (ch != 'a' && ch != 'e' && ch != 's');
  1003.  
  1004.         switch (ch) {
  1005.         case 'e':
  1006.             invoke_editor(nam);
  1007.             break;
  1008.  
  1009.         case 'a':
  1010.             setuid(tass_uid);
  1011.             setgid(tass_gid);
  1012.             return FALSE;
  1013.  
  1014.         case 's':
  1015. /*
  1016.  *  Open letter an get the To: line in case they changed it with
  1017.  *  the editor
  1018.  */
  1019.  
  1020.             find_new_to(nam, mail_to);
  1021.             printf("\r\nMailing to %s...", mail_to);
  1022.             fflush(stdout);
  1023.             sprintf(buf, "%s %s < %s", MAILER,
  1024.                             mail_to, nam);
  1025.             if (invoke_cmd(buf)) {
  1026.                 printf("Message sent\r\n");
  1027.                 fflush(stdout);
  1028.                 goto mail_to_someone_done;
  1029.             } else {
  1030.                 printf("Command failed: %s\r\n", buf);
  1031.                 fflush(stdout);
  1032.                 break;
  1033.             }
  1034.         }
  1035.     }
  1036.  
  1037. mail_to_someone_done:
  1038.     setuid(tass_uid);
  1039.     setgid(tass_gid);
  1040.  
  1041.     continue_prompt();
  1042.  
  1043.     return TRUE;
  1044. }
  1045.  
  1046.  
  1047. mail_to_author(copy_text)
  1048. int copy_text;
  1049. {
  1050.     char nam[100];
  1051.     FILE *fp;
  1052.     char ch;
  1053.     char buf[200];
  1054.     char mail_to[LEN+1];
  1055.  
  1056.     setuid(real_uid);
  1057.     setgid(real_gid);
  1058.  
  1059.     printf("\r\nMailing to %s...\r\n\r\n", note_h_from);
  1060.  
  1061.     sprintf(nam, "%s/.letter", homedir);
  1062.     if ((fp = fopen(nam, "w")) == NULL) {
  1063.         fprintf(stderr, "can't open %s: ", nam);
  1064.         perror("");
  1065.         setuid(tass_uid);
  1066.         setgid(tass_gid);
  1067.         return(FALSE);
  1068.     }
  1069.     chmod(nam, 0600);
  1070.  
  1071.     fprintf(fp, "To: %s\n", note_h_from);
  1072.     fprintf(fp, "Subject: Re: %s\n", eat_re(note_h_subj) );
  1073.     fprintf(fp, "Newsgroups: %s\n", note_h_newsgroups);
  1074.     if (*my_org)
  1075.         fprintf(fp, "Organization: %s\n", my_org);
  1076.     fputs("\n", fp);
  1077.  
  1078.     if (copy_text) {        /* if "copy_text" */
  1079.         fprintf(fp, "In article %s you write:\n", note_h_messageid);
  1080.  
  1081.         fseek(note_fp, note_mark[0], 0);
  1082.         copy_fp(note_fp, fp, "> ");
  1083.     }
  1084.  
  1085.     add_signature(fp, TRUE);
  1086.     fclose(fp);
  1087.  
  1088.     ch = 'e';
  1089.     while (1) {
  1090.         switch (ch) {
  1091.         case 'e':
  1092.             invoke_editor(nam);
  1093.             break;
  1094.  
  1095.         case 'a':
  1096.             setuid(tass_uid);
  1097.             setgid(tass_gid);
  1098.             return FALSE;
  1099.  
  1100.         case 's':
  1101.             strcpy(mail_to, note_from_addr);
  1102.             find_new_to(nam, mail_to);
  1103.             printf("\r\nMailing to %s...  ", mail_to);
  1104.             fflush(stdout);
  1105.             sprintf(buf, "%s %s < %s", MAILER, mail_to, nam);
  1106.             if (invoke_cmd(buf)) {
  1107.                 printf("Message sent\r\n");
  1108.                 fflush(stdout);
  1109.                 goto mail_to_author_done;
  1110.             } else {
  1111.                 printf("Command failed: %s\r\n", buf);
  1112.                 fflush(stdout);
  1113.                 break;
  1114.             }
  1115.         }
  1116.  
  1117.         do {
  1118.             MoveCursor(LINES, 0);
  1119.             fputs("abort, edit, send: ", stdout);
  1120.             fflush(stdout);
  1121.             ch = ReadCh();
  1122.         } while (ch != 'a' && ch != 'e' && ch != 's');
  1123.     }
  1124.  
  1125. mail_to_author_done:
  1126.     setuid(tass_uid);
  1127.     setgid(tass_gid);
  1128.  
  1129.     continue_prompt();
  1130.  
  1131.     return TRUE;
  1132. }
  1133.  
  1134.  
  1135. post_response(group, respnum)
  1136. int respnum;
  1137. {
  1138.     FILE *fp;
  1139.     char nam[100];
  1140.     char ch;
  1141.     char buf[200];
  1142.     int post_anyway = FALSE;
  1143.  
  1144.     if (*note_h_followup && strcmp(note_h_followup, "poster") == 0) {
  1145.         clear_message();
  1146.         MoveCursor(LINES,0);
  1147.         printf("Note: Responses have been directed to the poster");
  1148.         if (!prompt_yn("Post anyway? (y/n): "))
  1149.             return FALSE;
  1150.         *note_h_followup = '\0';
  1151.     } else if (*note_h_followup && strcmp(note_h_followup, group) != 0) {
  1152.         clear_message();
  1153.         MoveCursor(LINES,0);
  1154.         printf("Note:  Responses have been directed to %s\r\n\r\n",
  1155.                             note_h_followup);
  1156.         if (!prompt_yn("Continue? (y/n): "))
  1157.         return FALSE;
  1158.     }
  1159.  
  1160.     setuid(real_uid);
  1161.     setgid(real_gid);
  1162.  
  1163.     sprintf(nam, "%s/.article", homedir);
  1164.     if ((fp = fopen(nam, "w")) == NULL) {
  1165.         fprintf(stderr, "can't open %s: ", nam);
  1166.         perror("");
  1167.         setuid(tass_uid);
  1168.         setgid(tass_gid);
  1169.         return FALSE;
  1170.     }
  1171.     chmod(nam, 0600);
  1172.  
  1173.     fprintf(fp, "Subject: Re: %s\n", eat_re(note_h_subj));
  1174.  
  1175.     if (*note_h_followup && strcmp(note_h_followup, "poster") != 0)
  1176.         fprintf(fp, "Newsgroups: %s\n", note_h_followup);
  1177.     else
  1178.         fprintf(fp, "Newsgroups: %s\n", note_h_newsgroups);
  1179.  
  1180.     if (*my_org)
  1181.         fprintf(fp, "Organization: %s\n", my_org);
  1182.  
  1183.     if (note_h_distrib != '\0')
  1184.         fprintf(fp, "Distribution: %s\n", note_h_distrib);
  1185.  
  1186.     fprintf(fp, "References: %s\n", note_h_messageid);
  1187.     fprintf(fp, "\n");
  1188.  
  1189.     if (respnum) {        /* if "copy_text" */
  1190.         fprintf(fp, "%s writes:\n", note_h_from);
  1191.  
  1192.         fseek(note_fp, note_mark[0], 0);
  1193.         copy_fp(note_fp, fp, "> ");
  1194.     }
  1195.  
  1196.     add_signature(fp, FALSE);
  1197.     fclose(fp);
  1198.  
  1199.     ch = 'e';
  1200.     while (1) {
  1201.         switch (ch) {
  1202.         case 'e':
  1203.             invoke_editor(nam);
  1204.             break;
  1205.  
  1206.         case 'a':
  1207.             setuid(tass_uid);
  1208.             setgid(tass_gid);
  1209.             return FALSE;
  1210.  
  1211.         case 'p':
  1212.             printf("Posting...  ");
  1213.             fflush(stdout);
  1214.             sprintf(buf, "%s/inews -h < %s", LIBDIR, nam);
  1215.             if (invoke_cmd(buf)) {
  1216.                 printf("article posted\r\n");
  1217.                 fflush(stdout);
  1218.                 goto post_response_done;
  1219.             } else {
  1220.                 printf("article rejected\r\n");
  1221.                 fflush(stdout);
  1222.                 break;
  1223.             }
  1224.         }
  1225.  
  1226.         do {
  1227.             MoveCursor(LINES, 0);
  1228.             fputs("abort, edit, post: ", stdout);
  1229.             fflush(stdout);
  1230.             ch = ReadCh();
  1231.         } while (ch != 'a' && ch != 'e' && ch != 'p');
  1232.     }
  1233.  
  1234. post_response_done:
  1235.     setuid(tass_uid);
  1236.     setgid(tass_gid);
  1237.  
  1238.     continue_prompt();
  1239.  
  1240.     return TRUE;
  1241. }
  1242.  
  1243.  
  1244. save_art_to_file()
  1245. {
  1246.     char nam[LEN];
  1247.     FILE *fp;
  1248.     char *p;
  1249.  
  1250.     if (!parse_string("Save article to file: ", nam))
  1251.         return;
  1252.     if (nam[0] == '\0')
  1253.         return;
  1254.  
  1255.     for (p = nam; *p && (*p == ' ' || *p == '\t'); p++) ;
  1256.     if (!*p)
  1257.         return;
  1258.  
  1259.     setuid(real_uid);
  1260.     setgid(real_gid);
  1261.  
  1262.     if ((fp = fopen(p, "a+")) == NULL) {
  1263.         fprintf(stderr, "can't open %s: ", nam);
  1264.         perror("");
  1265.         info_message("-- article not saved --");
  1266.         setuid(real_uid);
  1267.         setgid(real_gid);
  1268.         return;
  1269.     }
  1270.  
  1271.     MoveCursor(LINES, 0);
  1272.     fputs("Saving...", stdout);
  1273.     fflush(stdout);
  1274.  
  1275.     fprintf(fp, "From %s %s\n", note_h_path, note_h_date);
  1276.  
  1277.     fseek(note_fp, 0L, 0);
  1278.     copy_fp(note_fp, fp, "");
  1279.     fputs("\n", fp);
  1280.  
  1281.     fclose(fp);
  1282.  
  1283.     setuid(real_uid);
  1284.     setgid(real_gid);
  1285.     info_message("-- article saved --");
  1286. }
  1287.  
  1288.  
  1289. save_thread_to_file(respnum, group_path)
  1290. long respnum;
  1291. char *group_path;
  1292. {
  1293.     char nam[LEN];
  1294.     FILE *fp;
  1295.     FILE *art;
  1296.     int i;
  1297.     char buf[8192];
  1298.     int b;
  1299.     int count = 0;
  1300.     char *p;
  1301.  
  1302.     b = which_base(respnum);
  1303.  
  1304.     if (!parse_string("Save thread to file: ", nam))
  1305.         return;
  1306.     if (nam[0] == '\0')
  1307.         return;
  1308.  
  1309.     for (p = nam; *p && (*p == ' ' || *p == '\t'); p++) ;
  1310.     if (!*p)
  1311.         return;
  1312.  
  1313.     setuid(real_uid);
  1314.     setgid(real_gid);
  1315.  
  1316.     if ((fp = fopen(nam, "a+")) == NULL) {
  1317.         fprintf(stderr, "can't open %s: ", nam);
  1318.         perror("");
  1319.         info_message("-- thread not saved --");
  1320.         setuid(real_uid);
  1321.         setgid(real_gid);
  1322.         return;
  1323.     }
  1324.  
  1325.     MoveCursor(LINES, 0);
  1326.     fputs("Saving...    ", stdout);
  1327.     fflush(stdout);
  1328.  
  1329.     note_cleanup();
  1330.  
  1331.     for (i = base[b]; i >= 0; i = arts[i].thread) {
  1332.         open_note(arts[i].artnum, group_path);
  1333.  
  1334.         fprintf(fp, "From %s %s\n", note_h_path, note_h_date);
  1335.         fseek(note_fp, 0L, 0);
  1336.         copy_fp(note_fp, fp, "");
  1337.         fputs("\n", fp);
  1338.  
  1339.         note_cleanup();
  1340.         printf("\b\b\b\b%4d", ++count);
  1341.         fflush(stdout);
  1342.     }
  1343.  
  1344.     fclose(fp);
  1345.     setuid(real_uid);
  1346.     setgid(real_gid);
  1347.  
  1348.     info_message("-- thread saved --");
  1349.     open_note(arts[respnum].artnum, group_path);
  1350. }
  1351.  
  1352.  
  1353. pipe_article() {
  1354.     char command[LEN];
  1355.     FILE *fp;
  1356.  
  1357.     if (!parse_string("Pipe to command: ", command))
  1358.         return;
  1359.     if (command[0] == '\0')
  1360.         return;
  1361.  
  1362.     fp = popen(command, "w");
  1363.     if (fp == NULL) {
  1364.         fprintf(stderr, "command failed: ");
  1365.         perror("");
  1366.         goto pipe_article_done;
  1367.     }
  1368.  
  1369.     fseek(note_fp, 0L, 0);
  1370.     copy_fp(note_fp, fp, "");
  1371.     pclose(fp);
  1372.  
  1373. pipe_article_done:
  1374.  
  1375.     continue_prompt();
  1376. }
  1377.  
  1378. @EOF
  1379.  
  1380. chmod 644 page.c
  1381.  
  1382. echo x - screen.c
  1383. cat >screen.c <<'@EOF'
  1384.  
  1385. #include    <stdio.h>
  1386. #include    "tass.h"
  1387.  
  1388.  
  1389.  
  1390. info_message(msg)
  1391. char *msg;
  1392. {
  1393.     clear_message();      /* Clear any old messages hanging around */
  1394.     center_line(LINES, msg);  /* center the message at screen bottom  */
  1395.     MoveCursor(LINES, 0);
  1396. }
  1397.  
  1398.  
  1399. clear_message()
  1400. {
  1401.     MoveCursor(LINES, 0);
  1402.     CleartoEOLN();
  1403. }
  1404.  
  1405.  
  1406. center_line(line, str)
  1407. int line;
  1408. char *str;
  1409. {
  1410. int pos;
  1411.  
  1412.     pos = (COLS - strlen(str)) / 2;
  1413.     MoveCursor(line, pos);
  1414.     printf("%s", str);
  1415.     fflush(stdout);
  1416. }
  1417.  
  1418.  
  1419. draw_arrow(line)
  1420. int line;
  1421. {
  1422.     MoveCursor(line, 0);
  1423.     printf("->");
  1424.     fflush(stdout);
  1425.     MoveCursor(LINES, 0);
  1426. }
  1427.  
  1428. erase_arrow(line)
  1429. int line;
  1430. {
  1431.     MoveCursor(line, 0);
  1432.     printf("  ");
  1433.     fflush(stdout);
  1434. }
  1435.  
  1436. @EOF
  1437.  
  1438. chmod 644 screen.c
  1439.  
  1440. echo x - select.c
  1441. cat >select.c <<'@EOF'
  1442.  
  1443. #include    <stdio.h>
  1444. #include    <signal.h>
  1445. #include    "tass.h"
  1446.  
  1447.  
  1448. int first_group_on_screen;
  1449. int last_group_on_screen;
  1450. int cur_groupnum = 0;
  1451. extern int index_point;
  1452. int space_mode;
  1453. extern char *cvers;
  1454.  
  1455. char group_search_string[LEN+1];
  1456.  
  1457.  
  1458.  
  1459. #ifdef SIGTSTP
  1460. void
  1461. select_susp(i)
  1462. int i;
  1463. {
  1464.  
  1465.     Raw(FALSE);
  1466.     putchar('\n');
  1467.     signal(SIGTSTP, SIG_DFL);
  1468. #ifdef BSD
  1469.         sigsetmask(sigblock(0) & ~(1 << (SIGTSTP - 1)));
  1470. #endif
  1471.     kill(0, SIGTSTP);
  1472.  
  1473.     signal(SIGTSTP, select_susp);
  1474.     Raw(TRUE);
  1475.     mail_setup();
  1476.     group_selection_page();
  1477. }
  1478. #endif
  1479.  
  1480.  
  1481. selection_index()
  1482. {
  1483.     char ch;
  1484.     int n;
  1485.     int i;
  1486.     char buf[200];
  1487.  
  1488.     group_selection_page();        /* display group selection page */
  1489.  
  1490.     while (1) {
  1491.         ch = ReadCh();
  1492.  
  1493.         if (ch > '0' && ch <= '9') {
  1494.             prompt_group_num(ch);
  1495.         } else switch (ch) {
  1496.             case 'c':    /* catchup--mark all articles as read */
  1497.                 if (prompt_yn("Mark group as read? (y/n): ")) {
  1498.                 unread[cur_groupnum] = 0;
  1499.                 mark_group_read(
  1500.                         active[my_group[cur_groupnum]].name,
  1501.                         my_group[cur_groupnum]);
  1502.                  MoveCursor(INDEX_TOP + (cur_groupnum -
  1503.                         first_group_on_screen), 47);
  1504.                  printf("     ");
  1505.                 MoveCursor(LINES, 0);
  1506.                  fflush(stdout);
  1507. /*                group_selection_page();        */
  1508.                 }
  1509.                 break;
  1510.  
  1511.             case ctrl('K'):
  1512.                 if (local_top <= 0) {
  1513.                     info_message("No groups to delete");
  1514.                     break;
  1515.                 }
  1516.  
  1517.                 delete_group(
  1518.                     active[my_group[cur_groupnum]].name);
  1519.                 active[my_group[cur_groupnum]].flag = NOTGOT;
  1520.  
  1521.                 local_top--;
  1522.                 for (i = cur_groupnum; i < local_top; i++) {
  1523.                     my_group[i] = my_group[i+1];
  1524.                     unread[i] = unread[i+1];
  1525.                 }
  1526.                 if (cur_groupnum >= local_top)
  1527.                     cur_groupnum = local_top - 1;
  1528.  
  1529.                 group_selection_page();
  1530.                 break;
  1531.  
  1532.             case ctrl('Y'):
  1533.                 undel_group();
  1534.                 group_selection_page();
  1535.                 break;
  1536.  
  1537.             case 'I':        /* toggle inverse video */
  1538.                 inverse_okay = !inverse_okay;
  1539.                 if (inverse_okay)
  1540.                     info_message("Inverse video enabled");
  1541.                 else
  1542.                     info_message("Inverse video disabled");
  1543.                 break;
  1544.  
  1545.             case ctrl('R'):    /* reset .newsrc */
  1546.                 if (prompt_yn("Reset newsrc? (y/n): ")) {
  1547.                 reset_newsrc();
  1548.                 cur_groupnum = 0;
  1549.                 group_selection_page();
  1550.                 }
  1551.                 break;
  1552.  
  1553.             case '$':    /* reread .newsrc, no unsub groups */
  1554.                 cur_groupnum = 0;
  1555.                 local_top = 0;
  1556.                 for (i = 0; i < num_active; i++)
  1557.                     active[i].flag = NOTGOT;
  1558.                 read_newsrc(TRUE);
  1559.                 group_selection_page();
  1560.                 break;
  1561.  
  1562.             case 's':    /* subscribe to current group */
  1563.                 MoveCursor(INDEX_TOP +
  1564.                 (cur_groupnum-first_group_on_screen), 3);
  1565.                 putchar(' ');
  1566.                 fflush(stdout);
  1567.                 MoveCursor(LINES, 0);
  1568.  
  1569.                 subscribe(active[my_group[cur_groupnum]].name,
  1570.                     ':', my_group[cur_groupnum], FALSE);
  1571.                 sprintf(buf, "subscribed to %s",
  1572.                     active[my_group[cur_groupnum]].name);
  1573.                 info_message(buf);
  1574.                 break;
  1575.  
  1576.             case 'u':    /* unsubscribe to current group */
  1577.                 MoveCursor(INDEX_TOP +
  1578.                 (cur_groupnum-first_group_on_screen), 3);
  1579.                 putchar('u');
  1580.                 fflush(stdout);
  1581.                 MoveCursor(LINES, 0);
  1582.  
  1583.                 subscribe(active[my_group[cur_groupnum]].name,
  1584.                     '!', my_group[cur_groupnum], FALSE);
  1585.                 sprintf(buf, "unsubscribed to %s",
  1586.                     active[my_group[cur_groupnum]].name);
  1587.                 info_message(buf);
  1588.                 break;
  1589.  
  1590.             case '\t':
  1591.                 for (i = cur_groupnum; i < local_top; i++)
  1592.                     if (unread[i] != 0)
  1593.                         break;
  1594.                 if (i >= local_top) {
  1595.                     info_message("No more groups to read");
  1596.                     break;
  1597.                 }
  1598.  
  1599.                 erase_group_arrow();
  1600.                 cur_groupnum = i;
  1601.                 if (cur_groupnum >= last_group_on_screen)
  1602.                     group_selection_page();
  1603.                 else
  1604.                     draw_group_arrow();
  1605.                 space_mode = TRUE;
  1606.                 goto go_into_group;
  1607.  
  1608.             case 'g':    /* prompt for a new group name */
  1609.                 n = choose_new_group();
  1610.                 if (n >= 0) {
  1611.                     erase_group_arrow();
  1612.                     cur_groupnum = n;
  1613.                     if (cur_groupnum < first_group_on_screen
  1614.                     || cur_groupnum >= last_group_on_screen)
  1615.                         group_selection_page();
  1616.                     else
  1617.                         draw_group_arrow();
  1618.                 }
  1619.                 break;
  1620.  
  1621.             case 27:    /* (ESC) common arrow keys */
  1622.                 ch = ReadCh();
  1623.                 if (ch == '[' || ch == 'O')
  1624.                     ch = ReadCh();
  1625.                 switch (ch) {
  1626.                 case 'A':
  1627.                 case 'D':
  1628.                 case 'i':
  1629.                     goto select_up;
  1630.  
  1631.                 case 'B':
  1632.                 case 'I':
  1633.                 case 'C':
  1634.                     goto select_down;
  1635.                 }
  1636.                 break;
  1637.  
  1638.             case 'y':    /* pull in rest of groups from active */
  1639.                 n = local_top;
  1640.                 for (i = 0; i < num_active; i++)
  1641.                     active[i].flag = NOTGOT;
  1642.                 read_newsrc(FALSE);
  1643.                 for (i = 0; i < num_active; i++)
  1644.                     if (active[i].flag & NOTGOT) {
  1645.                         active[i].flag &= ~NOTGOT;
  1646.                         my_group[local_top] = i;
  1647.                         unread[local_top] = -1;
  1648.                         local_top++;
  1649.                     }
  1650.                 if (n < local_top) {
  1651.                     sprintf(buf, "Added %d group%s",
  1652.                         local_top - n,
  1653.                         local_top - n == 1 ? "" : "s");
  1654.                     group_selection_page();
  1655.                     info_message(buf);
  1656.                 } else
  1657.                     info_message("No more groups to yank in");
  1658.                 break;
  1659.  
  1660.             case ctrl('U'):        /* page up */
  1661.                 erase_group_arrow();
  1662.                 cur_groupnum -= NOTESLINES / 2;
  1663.                 if (cur_groupnum < 0)
  1664.                     cur_groupnum = 0;
  1665.                 if (cur_groupnum < first_group_on_screen
  1666.                 ||  cur_groupnum >= last_group_on_screen)
  1667.                     group_selection_page();
  1668.                 else
  1669.                     draw_group_arrow();
  1670.                 break;
  1671.  
  1672.             case ctrl('D'):        /* page down */
  1673.                 erase_group_arrow();
  1674.                 cur_groupnum += NOTESLINES / 2;
  1675.                 if (cur_groupnum >= local_top)
  1676.                     cur_groupnum = local_top - 1;
  1677.  
  1678.                 if (cur_groupnum <= first_group_on_screen
  1679.                 ||  cur_groupnum >= last_group_on_screen)
  1680.                     group_selection_page();
  1681.                 else
  1682.                     draw_group_arrow();
  1683.                 break;
  1684.  
  1685.             case '!':
  1686.                 shell_escape();
  1687.                 group_selection_page();
  1688.                 break;
  1689.  
  1690.             case 'v':
  1691.                 info_message(cvers);
  1692.                 break;
  1693.  
  1694.             case ctrl('N'):        /* line down */
  1695.             case 'j':
  1696. select_down:
  1697.                 if (cur_groupnum + 1 >= local_top)
  1698.                     break;
  1699.  
  1700.                 if (cur_groupnum + 1 >= last_group_on_screen) {
  1701.                     cur_groupnum++;
  1702.                     group_selection_page();
  1703.                 } else {
  1704.                     erase_group_arrow();
  1705.                     cur_groupnum++;
  1706.                     draw_group_arrow();
  1707.                 }
  1708.                 break;
  1709.  
  1710.             case ctrl('P'):        /* line up */
  1711.             case 'k':
  1712. select_up:
  1713.                 if (!cur_groupnum)
  1714.                     break;
  1715.  
  1716.                 if (cur_groupnum <= first_group_on_screen) {
  1717.                     cur_groupnum--;
  1718.                     group_selection_page();
  1719.                 } else {
  1720.                     erase_group_arrow();
  1721.                     cur_groupnum--;
  1722.                     draw_group_arrow();
  1723.                 }
  1724.                 break;
  1725.  
  1726.             case 't':        /* redraw */
  1727.             case ctrl('W'):
  1728.             case ctrl('L'):
  1729.                 group_selection_page();
  1730.                 break;
  1731.  
  1732.             case ' ':
  1733.             case '\r':    /* go into group */
  1734.             case '\n':
  1735.                 space_mode = FALSE;
  1736. go_into_group:
  1737.                 clear_message();
  1738.                 index_point = -1;
  1739.                 do {
  1740.                     group_page(
  1741.                       active[my_group[cur_groupnum]].name);
  1742.                 } while (index_point == -3);
  1743.                 group_selection_page();
  1744.                 break;
  1745.  
  1746.             case '/':    /* search forward */
  1747.                 search_group(TRUE);
  1748.                 break;
  1749.  
  1750.             case '?':    /* search backward */
  1751.                 search_group(FALSE);
  1752.                 break;
  1753.  
  1754.             case 'q':    /* quit */
  1755.                 tass_done(0);
  1756.  
  1757.             case 'h':
  1758.                 tass_select_help();
  1759.                 group_selection_page();
  1760.                 break;
  1761.  
  1762.             default:
  1763.                 info_message("Bad command.  Type 'h' for help.");
  1764.         }
  1765.     }
  1766. }
  1767.  
  1768.  
  1769. group_selection_page() {
  1770.     int i;
  1771.     int n;
  1772.     char new[10];
  1773.     char subs;
  1774.  
  1775. #ifdef SIGTSTP
  1776.     signal(SIGTSTP, select_susp);
  1777. #endif
  1778.  
  1779.     ClearScreen();
  1780.     printf("%s\r\n", nice_time());        /* print time in upper left */
  1781.  
  1782.     if (mail_check()) {            /* you have mail message */
  1783.         MoveCursor(0, 66);        /* in upper right */
  1784.         printf("you have mail\n");
  1785.     }
  1786.  
  1787.     center_line(1, "Group Selection");
  1788.     MoveCursor(INDEX_TOP, 0);
  1789.  
  1790.     first_group_on_screen = (cur_groupnum / NOTESLINES) * NOTESLINES;
  1791.  
  1792.     last_group_on_screen = first_group_on_screen + NOTESLINES;
  1793.     if (last_group_on_screen >= local_top)
  1794.         last_group_on_screen = local_top;
  1795.  
  1796.     for (i = first_group_on_screen; i < last_group_on_screen; i++) {
  1797.         switch (unread[i]) {
  1798.         case -2:
  1799.             strcpy(new, "?   ");
  1800.             break;
  1801.  
  1802.         case -1:
  1803.             strcpy(new, "-   ");
  1804.             break;
  1805.  
  1806.         case 0:
  1807.             strcpy(new, "    ");
  1808.             break;
  1809.  
  1810.         default:
  1811.             sprintf(new, "%-4d", unread[i]);
  1812.         }
  1813.  
  1814.         n = my_group[i];
  1815.         if (active[n].flag & SUBS)    /* subscribed? */
  1816.             subs = ' ';
  1817.         else
  1818.             subs = 'u';    /* u next to unsubscribed groups */
  1819.  
  1820.         printf("   %c %4d  %-35s %s\r\n", subs, i+1,
  1821.                             active[n].name, new);
  1822.     }
  1823.  
  1824.     draw_group_arrow();
  1825. }
  1826.  
  1827.  
  1828. prompt_group_num(ch)
  1829. char ch;
  1830. {
  1831. int num;
  1832.  
  1833.     clear_message();
  1834.  
  1835.     if ((num = parse_num(ch, "Select group> ")) == -1) {
  1836.         clear_message();
  1837.         return FALSE;
  1838.     }
  1839.     num--;        /* index from 0 (internal) vs. 1 (user) */
  1840.  
  1841.     if (num >= local_top)
  1842.         num = local_top - 1;
  1843.  
  1844.     if (num >= first_group_on_screen
  1845.     &&  num < last_group_on_screen) {
  1846.         erase_group_arrow();
  1847.         cur_groupnum = num;
  1848.         draw_group_arrow();
  1849.     } else {
  1850.         cur_groupnum = num;
  1851.         group_selection_page();
  1852.     }
  1853.  
  1854.     return TRUE;
  1855. }
  1856.  
  1857. erase_group_arrow() {
  1858.     erase_arrow(INDEX_TOP + (cur_groupnum-first_group_on_screen) );
  1859. }
  1860.  
  1861. draw_group_arrow() {
  1862.     draw_arrow(INDEX_TOP + (cur_groupnum-first_group_on_screen) );
  1863. }
  1864.  
  1865. search_group(forward)
  1866. int forward;
  1867. {
  1868.     char buf[LEN+1];
  1869.     char buf2[LEN+1];
  1870.     int i;
  1871.     int len;
  1872.     char *prompt;
  1873.  
  1874.     clear_message();
  1875.  
  1876.     if (forward)
  1877.         prompt = "/";
  1878.     else
  1879.         prompt = "?";
  1880.  
  1881.     if (!parse_string(prompt, buf))
  1882.         return;
  1883.  
  1884.     if (strlen(buf))
  1885.         strcpy(group_search_string, buf);
  1886.     else if (!strlen(group_search_string)) {
  1887.         info_message("No search string");
  1888.         return;
  1889.     }
  1890.  
  1891.     i = cur_groupnum;
  1892.  
  1893.     make_lower(group_search_string, buf);
  1894.     len = strlen(buf);
  1895.  
  1896.     do {
  1897.         if (forward)
  1898.             i++;
  1899.         else
  1900.             i--;
  1901.  
  1902.         if (i >= local_top)
  1903.             i = 0;
  1904.         if (i < 0)
  1905.             i = local_top - 1;
  1906.  
  1907.         make_lower(active[my_group[i]].name, buf2);
  1908.         if (match(buf, buf2, len)) {
  1909.             if (i >= first_group_on_screen
  1910.             &&  i < last_group_on_screen) {
  1911.                 erase_group_arrow();
  1912.                 cur_groupnum = i;
  1913.                 draw_group_arrow();
  1914.             } else {
  1915.                 cur_groupnum = i;
  1916.                 group_selection_page();
  1917.             }
  1918.             return;
  1919.         }
  1920.     } while (i != cur_groupnum);
  1921.  
  1922.     info_message("No match");
  1923. }
  1924.  
  1925.  
  1926. tass_select_help() {
  1927.     char title[100];
  1928.  
  1929.     sprintf(title, "%s, Group Selection Commands", TASS_HEADER);
  1930.  
  1931.     ClearScreen();
  1932.     center_line(0, title);
  1933.  
  1934.     MoveCursor(2, 0);
  1935.  
  1936.     printf("\t4\tSelect group 4\r\n");
  1937.     printf("\t^D\tPage down\r\n");
  1938.     printf("\t^R\tReset .newsrc\r\n");
  1939.     printf("\t^U\tPage up\r\n");
  1940.     printf("\t^K\tDelete group\r\n");
  1941.     printf("\t^Y\tUndelete group\r\n");
  1942.     printf("\t<CR>\tRead current group\r\n");
  1943.     printf("\t<TAB>\tView next unread group\r\n");
  1944.     printf("\tc\tMark group as all read\r\n");
  1945.     printf("\tg\tChoose a new group by name\r\n");
  1946.     printf("\tj\tDown a line\r\n");
  1947.     printf("\tk\tUp a line\r\n");
  1948.     printf("\tq\tQuit\r\n");
  1949.     printf("\ts\tSubscribe to current group\r\n");
  1950.     printf("\tu\tUnsubscribe to current group\r\n");
  1951.     printf("\ty\tYank in groups that are not in the .newsrc\r\n");
  1952.     printf("\t$\tReread group list from .newsrc\r\n");
  1953.     printf("\t/?\tGroup search forward (?=backward)\r\n");
  1954.  
  1955.     center_line(LINES, "-- hit any key --");
  1956.     ReadCh();
  1957. }
  1958.  
  1959.  
  1960. choose_new_group() {
  1961.     char buf[LEN+1];
  1962.     char *p;
  1963.     int ret;
  1964.  
  1965.     if (!parse_string("Newsgroup> ", buf))
  1966.         return -1;
  1967.  
  1968.     for (p = buf; *p && (*p == ' ' || *p == '\t'); p++) ;
  1969.     if (*p == '\0')
  1970.         return -1;
  1971.  
  1972.     ret = add_group(p, TRUE);
  1973.     if (ret < 0)
  1974.         info_message("Group not found in active file");
  1975.  
  1976.     return ret;
  1977. }
  1978.  
  1979.  
  1980. /*
  1981.  *  Add a group to the selection list (my_group[])
  1982.  *  Return the index of my_group[] if group is added or was already
  1983.  *  there.  Return -1 if named group is not in active[].
  1984.  */
  1985.  
  1986. add_group(s, get_unread)
  1987. char *s;
  1988. int get_unread;            /* look in .newsrc for sequencer unread info? */
  1989. {
  1990.     long h;
  1991.     int i, j;
  1992.     extern long hash_groupname();
  1993.  
  1994.     h = hash_groupname(s);
  1995.  
  1996.     for (i = group_hash[h]; i >= 0; i = active[i].next)
  1997.         if (strcmp(s, active[i].name) == 0) {
  1998.             for (j = 0; j < local_top; j++)
  1999.                 if (my_group[j] == i)
  2000.                     return j;
  2001.  
  2002.             active[i].flag &= ~NOTGOT;   /* mark that we got it */
  2003.             my_group[local_top] = i;
  2004.  
  2005.             if (get_unread)
  2006.                 unread[local_top] = get_line_unread(s, i);
  2007.             else
  2008.                 unread[local_top] = -2;
  2009.  
  2010.             local_top++;
  2011.             return local_top - 1;
  2012.         }
  2013.  
  2014.     return -1;
  2015. }
  2016.  
  2017.  
  2018. @EOF
  2019.  
  2020. chmod 644 select.c
  2021.  
  2022. echo x - spool_open.c
  2023. cat >spool_open.c <<'@EOF'
  2024.  
  2025.  
  2026. #include    <stdio.h>
  2027. #include    <sys/types.h>
  2028. #include    <sys/stat.h>
  2029. #include    "tass.h"
  2030.  
  2031.  
  2032. /* Hopefully one of these is right for you. */
  2033.  
  2034. #ifdef BSD
  2035. #    include <sys/types.h>
  2036. #    include <sys/dir.h>
  2037. #    define        DIR_BUF        struct direct
  2038. #    define        D_LENGTH    d_namlen
  2039. #endif
  2040. #ifdef M_XENIX
  2041. #    include <sys/ndir.h>
  2042. #    define        DIR_BUF        struct direct
  2043. #    define        D_LENGTH    d_namlen
  2044. #endif
  2045. #ifndef DIR_BUF
  2046. #    include    <sys/types.h>
  2047. #    include    <dirent.h>
  2048. #    define        DIR_BUF        struct dirent
  2049. #    define        D_LENGTH    d_reclen
  2050. #endif
  2051.  
  2052.  
  2053.  
  2054. char *
  2055. is_remote() {
  2056.  
  2057.     return "";
  2058. }
  2059.  
  2060. nntp_startup() {
  2061. }
  2062.  
  2063. nntp_finish() {
  2064. }
  2065.  
  2066.  
  2067. FILE *open_active_fp() {
  2068.     FILE *fp;
  2069.  
  2070.     fp = fopen(active_file, "r");
  2071.     if (fp == NULL) {
  2072.         fprintf(stderr, "can't open %s: ", active_file);
  2073.         perror("");
  2074.         exit(1);
  2075.     }
  2076.  
  2077.     return fp;
  2078. }
  2079.  
  2080.  
  2081.  
  2082. FILE *
  2083. open_art_fp(group_path, art)
  2084. char *group_path;
  2085. long art;
  2086. {
  2087.     char buf[LEN];
  2088.     struct stat sb;
  2089.     extern long note_size;
  2090.  
  2091.     sprintf(buf, "%s/%s/%ld", SPOOLDIR, group_path, art);
  2092.  
  2093.     if (stat(buf, &sb) < 0)
  2094.         note_size = 0;
  2095.     else
  2096.         note_size = sb.st_size;
  2097.  
  2098.     return fopen(buf, "r");
  2099. }
  2100.  
  2101.  
  2102. open_header_fd(group_path, art)
  2103. char *group_path;
  2104. long art;
  2105. {
  2106.     char buf[LEN];
  2107.  
  2108.     sprintf(buf, "%s/%s/%ld", SPOOLDIR, group_path, art);
  2109.     return open(buf, 0);
  2110. }
  2111.  
  2112.  
  2113.  
  2114. /*
  2115.  *  Longword comparison routine for the qsort()
  2116.  */
  2117.  
  2118. base_comp(a, b)
  2119. long *a;
  2120. long *b;
  2121. {
  2122.  
  2123.     if (*a < *b)
  2124.         return -1;
  2125.     if (*a > *b)
  2126.         return 1;
  2127.     return 0;
  2128. }
  2129.  
  2130.  
  2131. /*
  2132.  *  Read the article numbers existing in a group's spool directory
  2133.  *  into base[] and sort them.  base_top is one past top.
  2134.  */
  2135.  
  2136. setup_base(group, group_path)
  2137. char *group;
  2138. char *group_path;
  2139. {
  2140.     DIR *d;
  2141.     DIR_BUF *e;
  2142.     long art;
  2143.     char buf[200];
  2144.  
  2145.     top_base = 0;
  2146.  
  2147.     sprintf(buf, "%s/%s", SPOOLDIR, group_path);
  2148.  
  2149.     if (access(buf, 4) != 0)
  2150.         return;
  2151.  
  2152.     d = opendir(buf);
  2153.     if (d != NULL) {
  2154.         while ((e = readdir(d)) != NULL) {
  2155.             art = my_atol(e->d_name, e->D_LENGTH);
  2156.             if (art >= 0) {
  2157.                 if (top_base >= max_art)
  2158.                     expand_art();
  2159.                 base[top_base++] = art;
  2160.             }
  2161.         }
  2162.         closedir(d);
  2163.         qsort(base, top_base, sizeof(long), base_comp);
  2164.     }
  2165. }
  2166.  
  2167. @EOF
  2168.  
  2169. chmod 644 spool_open.c
  2170.  
  2171. exit 0
  2172.