home *** CD-ROM | disk | FTP | other *** search
/ Source Code 1992 March / Source_Code_CD-ROM_Walnut_Creek_March_1992.iso / usenet / altsrcs / 2 / 2763 < prev    next >
Encoding:
Internet Message Format  |  1991-02-15  |  45.1 KB

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