home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / misc / volume30 / tin / part04 / page.c next >
C/C++ Source or Header  |  1992-05-20  |  22KB  |  1,031 lines

  1. /*
  2.  *  Project   : tin - a threaded Netnews reader
  3.  *  Module    : page.c
  4.  *  Author    : I.Lea & R.Skrenta
  5.  *  Created   : 01-04-91
  6.  *  Updated   : 02-05-92
  7.  *  Notes     :
  8.  *  Copyright : (c) Copyright 1991-92 by Iain Lea & Rich Skrenta
  9.  *              You may  freely  copy or  redistribute  this software,
  10.  *              so  long as there is no profit made from its use, sale
  11.  *              trade or  reproduction.  You may not change this copy-
  12.  *              right notice, and it must be included in any copy made
  13.  */
  14.  
  15. #include    "tin.h"
  16.  
  17. extern int cur_groupnum;
  18.  
  19. char note_h_path[LEN];            /* Path:    */
  20. char note_h_date[LEN];            /* Date:    */
  21. char note_h_subj[LEN];            /* Subject:    */
  22. char note_h_org[LEN];            /* Organization: */
  23. char note_h_newsgroups[LEN];        /* Newsgroups:    */
  24. char note_h_messageid[LEN];        /* Message-ID:    */
  25. char note_h_distrib[LEN];        /* Distribution: */
  26. char note_h_followup[LEN];        /* Followup-To: */
  27.  
  28. char *glob_page_group;
  29.  
  30. FILE *note_fp;                    /* the body of the current article */
  31.  
  32. int glob_respnum;
  33. int last_resp;                    /* current & previous article for - command */
  34. int note_end;                    /* we're done showing this article */
  35. int note_line;
  36. int note_page;                    /* what page we're on */
  37. int rotate;                        /* 0=normal, 13=rot13 decode */
  38. int this_resp;
  39.  
  40. long note_mark[MAX_PAGES];        /* ftells on beginnings of pages */
  41. long note_size;                    /* stat size in bytes of article */
  42.  
  43.  
  44. int show_page (respnum, threadnum, group, group_path)
  45.     int respnum;
  46.     int *threadnum;        /* to allow movement in thread mode */
  47.     char *group;
  48.     char *group_path;
  49. {
  50. #ifndef INDEX_DAEMON
  51.  
  52.     char ch;
  53.     int i, n = 0;
  54.     int copy_text;
  55.     int kill_state = NO_KILLING;
  56.     int old_sort_art_type = sort_art_type;
  57.     int old_top;
  58.     int posted;
  59.     int ret_code;
  60.     long old_artnum;
  61.     long art;
  62.  
  63. restart:
  64.  
  65.     glob_respnum = respnum;
  66.     glob_page_group = group;
  67.  
  68.     set_signals_page ();
  69.     
  70.     if (respnum != this_resp) {       /* remember current & previous */
  71.         last_resp = this_resp;       /* articles for - command */
  72.         this_resp = respnum;
  73.     }
  74.  
  75.     rotate = 0;            /* normal mode, not rot13 */
  76.     art = arts[respnum].artnum;
  77.     arts[respnum].unread = ART_READ;    /* mark article as read */
  78.  
  79.     if ((note_page = art_open (art, group_path)) == ART_UNAVAILABLE) {
  80.         sprintf (msg, txt_art_unavailable, art);
  81.         error_message (msg, "");
  82.         return (which_thread (respnum));
  83.     } else {
  84.         show_note_page (respnum, group);
  85.     }
  86.  
  87.     while (TRUE) {
  88.         ch = (char) ReadCh();
  89.  
  90.         if (ch >= '0' && ch <= '9') {
  91.             n = prompt_response (ch, respnum);
  92.             if (n != -1) {
  93.                 respnum = n;
  94.                 goto restart;
  95.             }
  96.             continue;
  97.         }
  98.         switch (ch) {
  99.             case ESC:
  100.                 switch (get_arrow_key ()) {
  101.                     case KEYMAP_PAGE_UP:
  102.                         goto page_up;
  103.  
  104.                     case KEYMAP_PAGE_DOWN:
  105.                         goto page_down;
  106.  
  107.                     case KEYMAP_HOME:
  108.                         goto begin_of_article;
  109.  
  110.                     case KEYMAP_END:
  111.                         goto end_of_article;
  112.                 }
  113.                 break;
  114.  
  115. #ifndef NO_SHELL_ESCAPE
  116.             case '!':
  117.                 shell_escape ();
  118.                 redraw_page (respnum, group);
  119.                 break;
  120. #endif
  121.  
  122.             case '$':    /* goto end of article */
  123.             case 'G':    /* 'less' compatible */
  124. end_of_article:            
  125.                 if (show_last_page ()) {
  126.                     show_note_page (respnum, group);
  127.                 }
  128.                 break;
  129.  
  130.             case '-':    /* show last viewed article */
  131.                 if (last_resp < 0) {
  132.                     info_message (txt_no_last_message);
  133.                     break;
  134.                 }
  135.                 art_close ();
  136.                 respnum = last_resp;
  137.                 goto restart;
  138.  
  139.             case '|':    /* pipe article/thread/tagged arts to command */
  140.                 feed_articles (FEED_PIPE, PAGE_LEVEL, "Pipe", respnum, group_path);
  141.                 break;
  142.  
  143.             case '/':    /* search forwards in article */
  144.                 if (search_article (TRUE)) {
  145.                     show_note_page (respnum, group);
  146.                 }
  147.                 break;
  148.  
  149.             case '<':    /* goto first article in current thread */
  150.                 if (arts[respnum].inthread) {
  151.                     n = which_thread (respnum);
  152.                     if (n >= 0 && base[n] != respnum) {
  153.                         assert (n < top_base);
  154.                         respnum = base[n];
  155.                         art_close ();
  156.                         goto restart;
  157.                     }
  158.                 }
  159.                 break;
  160.  
  161.             case '>':    /* goto last article in current thread */
  162.                 for (i = respnum; i >= 0; i = arts[i].thread) {
  163.                     n = i;
  164.                 }
  165.                 if (n != respnum) {
  166.                     respnum = n;
  167.                     art_close ();
  168.                     goto restart;
  169.                 }
  170.                 break;
  171.  
  172.             case ' ':             /* next page or response */
  173.             case ctrl('D'):        /* vi style */
  174.             case ctrl('V'):        /* emacs style */
  175. page_down:
  176.                 if (note_page == ART_UNAVAILABLE) {
  177.                     n = next_response (respnum);
  178.                     if (n == -1) {
  179.                         return (which_thread (respnum));
  180.                     }
  181.                     respnum = n;
  182.                     goto restart;
  183.                 } else if (note_end) {
  184.                     art_close ();
  185.                     n = next_response (respnum);
  186.                     if (n == -1) {
  187.                         return (which_thread (respnum));
  188.                     }
  189.                     respnum = n;
  190.                     goto restart;
  191.                 } else
  192.                     show_note_page (respnum, group);
  193.                 break;
  194.  
  195.             case '\r':
  196.             case '\n':    /* go to start of next thread */
  197.                 art_close ();
  198.                 n = next_thread (respnum);
  199.                 if (n == -1)
  200.                     return (which_thread (respnum));
  201.                 respnum = n;
  202.                 goto restart;
  203.  
  204.             case '\t':     /* goto next unread article */
  205. #ifndef TAB_GOTO_NEXT_UNREAD
  206.                 if (note_page == ART_UNAVAILABLE) {
  207.                     n = next_unread (next_response (respnum));
  208.                     if (n == -1) {
  209.                         return (which_thread (respnum));
  210.                     }
  211.                     respnum = n;
  212.                     goto restart;
  213.                 } else if (note_end) {
  214.                     art_close ();
  215.                     n = next_unread (next_response (respnum));
  216.                     if (n == -1) {
  217.                         return (which_thread (respnum));
  218.                     }
  219.                     respnum = n;
  220.                     goto restart;
  221.                 } else {
  222.                     show_note_page (respnum, group);
  223.                 }    
  224. #else
  225.                 if (note_page != ART_UNAVAILABLE) {
  226.                     art_close();
  227.                 }
  228.                 n = next_unread (next_response (respnum));
  229.                 if (n == -1) {
  230.                     return (which_thread (respnum));
  231.                 }
  232.                 respnum = n;
  233.                 goto restart;
  234. #endif
  235.                 break;
  236.  
  237.             case ctrl('H'):    /* show article headers */
  238.                 if (note_page == ART_UNAVAILABLE) {
  239.                     n = next_response (respnum);
  240.                     if (n == -1)
  241.                         return (which_thread (respnum));
  242.                     respnum = n;
  243.                     goto restart;
  244.                 } else {
  245.                     note_page = 0;
  246.                     note_end = FALSE;
  247.                     fseek(note_fp, 0L, 0);
  248.                     show_note_page(respnum, group);
  249.                 }
  250.                 break;
  251.  
  252.             case ctrl('K'):        /* kill article */
  253.                 if (kill_art_menu (group, respnum)) {
  254.                     i = which_thread (respnum);
  255.                     if (kill_any_articles (group)) {
  256.                         make_threads (FALSE);
  257.                         find_base (show_only_unread);
  258.                         if (i >= top_base)
  259.                             i = top_base - 1;
  260.                         respnum = base[i];    
  261.                     }
  262.                 }
  263.                 redraw_page (respnum, group);
  264.                 break;
  265.  
  266.             case ctrl('L'):        /* redraw current page of article */
  267.                 redraw_page (respnum, group);
  268.                 break;
  269.  
  270.             case ctrl('R'):        /* redraw beginning of article */
  271.             case 'g':            /* 'less' compatible */
  272. begin_of_article:            
  273.                 if (note_page == ART_UNAVAILABLE) {
  274.                     ClearScreen ();
  275.                     printf (txt_art_unavailable, arts[respnum].artnum);
  276.                     fflush (stdout);
  277.                 } else {
  278.                     note_page = 0;
  279.                     note_end = FALSE;
  280.                     fseek (note_fp, note_mark[0], 0);
  281.                     show_note_page (respnum, group);
  282.                 }
  283.                 break;
  284.  
  285.             case ctrl('X'):
  286.             case '%':
  287.             case 'd':    /* toggle rot-13 mode */
  288.                 if (rotate)
  289.                     rotate = 0;
  290.                 else
  291.                     rotate = 13;
  292.                 redraw_page (respnum, group);
  293.                 info_message (txt_toggled_rot13);
  294.                 break;
  295.  
  296.             case 'a':    /* author search forward */
  297.             case 'A':    /* author search backward */
  298.                 i = (ch == 'a');
  299.                 n = search_author (show_only_unread, respnum, i);
  300.                 if (n < 0)
  301.                     break;
  302.                 respnum = n;
  303.                 goto restart;
  304.                 /* NOTREACHED */
  305.  
  306.             case ctrl('U'):
  307.             case 'b':        /* back a page */
  308. page_up:
  309.                 if (note_page == ART_UNAVAILABLE) {
  310.                     art_close ();
  311.                     n = prev_response (respnum);
  312.                     if (n == -1)
  313.                         return (which_response (respnum));
  314.                     respnum = n;
  315.                     goto restart;
  316.  
  317.                 } else {
  318.                     if (note_page <= 1) {
  319.                         info_message (txt_begin_of_art);
  320.                     } else {
  321.                         note_page -= 2;
  322.                         note_end = FALSE;
  323.                         fseek (note_fp, note_mark[note_page], 0);
  324.                         show_note_page (respnum, group);
  325.                     }
  326.                 }
  327.                 break;
  328.  
  329.             case 'B':    /* bug/gripe/comment mailed to author */
  330.                 mail_bug_report ();
  331.                 redraw_page (respnum, group);
  332.                 break;
  333.                 
  334.             case 'c':    /* catchup--mark all articles as read */
  335.                 if (!confirm_action || prompt_yn (LINES, txt_mark_all_read, 'y')) {
  336.                     for (n = 0; n < top; n++) {
  337.                         arts[n].unread = ART_READ;
  338.                     }
  339.                     fix_new_highest (cur_groupnum);
  340.                     if (cur_groupnum + 1 < group_top) {
  341.                         cur_groupnum++;
  342.                     }
  343.                     art_close ();
  344.                     return -1;
  345.                 }
  346.                 break;
  347.  
  348.             case 'C':    /* cancel an article */
  349.                 if (cancel_article (group, respnum)) {
  350.                     redraw_page (respnum, group);
  351.                 }
  352.                 break;
  353.     
  354.             case 'f':    /* post a followup to this article */
  355.             case 'F':
  356.                 if (! can_post) {
  357.                     info_message (txt_cannot_post);
  358.                     break;
  359.                 }
  360.                 copy_text = (ch == 'f' ? FALSE : TRUE);
  361.                 ret_code = post_response (group, respnum, copy_text);
  362.                 redraw_page (respnum, group);
  363.                 break;
  364.  
  365.             case 'h':    /* help */
  366.                 show_info_page (HELP_INFO, help_page, txt_art_pager_com);
  367.                 redraw_page (respnum, group);
  368.                 break;
  369.  
  370.             case 'q':    /* return to index page */
  371. return_to_index:
  372.                 art_close ();
  373.                 if (kill_state == NO_KILLING &&
  374.                     sort_art_type != old_sort_art_type) {
  375.                     make_threads (TRUE);
  376.                     find_base (show_only_unread);
  377.                 }
  378.                 i = which_thread (respnum);
  379.                 *threadnum = which_response (respnum);
  380.                 if (kill_state == KILLING) {
  381.                     old_top = top;
  382.                     old_artnum = arts[respnum].artnum;
  383.                     kill_any_articles (group);
  384.                     make_threads (FALSE);
  385.                     find_base (show_only_unread);
  386.                     i = find_new_pos (old_top, old_artnum, i);
  387.                 }
  388.                 return (i);
  389.  
  390.             case 'I':    /* toggle inverse video */
  391.                 toggle_inverse_video ();
  392.                 redraw_page (respnum, group);
  393.                 break;
  394.  
  395.             case 'k':
  396.                 if (note_page == ART_UNAVAILABLE) {
  397.                     n = next_unread (next_response(respnum));
  398.                     if (n == -1)
  399.                         return (which_thread (respnum));
  400.                 } else {
  401.                     art_close ();
  402.                     n = next_unread (next_response (respnum));
  403.                     if (n == -1)
  404.                         return (which_thread (respnum));
  405.                 }
  406.                 respnum = n;
  407.                 goto restart;
  408.                 /* NOTREACHED */
  409.  
  410.             case 'K':    /* mark rest of thread as read */
  411.                 for (n = respnum; n >= 0; n = arts[n].thread)
  412.                     arts[n].unread = ART_READ;
  413.                 n = next_unread (next_response (respnum));
  414.                 if (n == -1)
  415.                     goto return_to_index;
  416.                 art_close ();
  417.                 respnum = n;
  418.                 goto restart;
  419.                 /* NOTREACHED */
  420.  
  421.             case 'm':    /* mail article/thread/tagged articles to somebody */
  422.                 feed_articles (FEED_MAIL, PAGE_LEVEL, "Mail", respnum, group_path);
  423.                 break;
  424.  
  425.             case 'M':    /* options menu */
  426.                 if (change_rcfile (group, FALSE) == KILLING) {
  427.                     kill_state = KILLING;
  428.                 } 
  429.                 redraw_page (respnum, group);
  430.                 break;
  431.  
  432.             case 'n':    /* skip to next article */
  433.                 art_close ();
  434.                 n = next_response (respnum);
  435.                 if (n == -1)
  436.                     return (which_thread(respnum));
  437.                 respnum = n;
  438.                 goto restart;
  439.                 /* NOTREACHED */
  440.                 
  441.             case 'N':    /* next unread article */
  442.                 n = next_unread (next_response (respnum));
  443.                 if (n == -1)
  444.                     info_message (txt_no_next_unread_art);
  445.                 else {
  446.                     art_close ();
  447.                     respnum = n;
  448.                     goto restart;
  449.                 }
  450.                 break;
  451.  
  452.             case 'o':    /* output art/thread/tagged arts to printer */
  453.                 feed_articles (FEED_PRINT, PAGE_LEVEL, "Print", respnum, group_path);
  454.                 break;
  455.  
  456.             case 'p':    /* previous article */
  457.                 art_close ();
  458.                 n = prev_response (respnum);
  459.                 if (n == -1)
  460.                     return (which_response (respnum));
  461.                 respnum = n;
  462.                 goto restart;
  463.  
  464.             case 'P':    /* previous unread article */
  465.                 n = prev_unread (prev_response (respnum));
  466.                 if (n == -1)
  467.                     info_message (txt_no_prev_unread_art);
  468.                 else {
  469.                     art_close ();
  470.                     respnum = n;
  471.                     goto restart;
  472.                 }
  473.                 break;
  474.  
  475.             case 'Q':    /* quit */
  476.                 return -2;
  477.                 break;
  478.     
  479.             case 'r':    /* reply to author through mail */
  480.             case 'R':
  481.                 copy_text = (ch == 'r' ? FALSE : TRUE);
  482.                 mail_to_author (group, respnum, copy_text);
  483.                 redraw_page (respnum, group);
  484.                 break;
  485.  
  486.             case 's':    /* save article/thread/tagged articles */
  487.                 feed_articles (FEED_SAVE, PAGE_LEVEL, "Save", respnum, group_path);
  488.                 break;
  489.  
  490.             case 't':    /* return to group selection page */
  491.                 art_close ();
  492.                 if (kill_state == KILLING) {
  493.                     kill_any_articles (group);
  494.                     make_threads (FALSE);
  495.                     find_base (show_only_unread);
  496.                 }
  497.                 update_newsrc (group, my_group[cur_groupnum], FALSE);
  498.                 fix_new_highest (cur_groupnum);
  499.                 return -1;
  500.  
  501.             case 'T':    /* tag/untag article for saving */
  502.                 if (arts[respnum].tagged) {
  503.                     arts[respnum].tagged = 0;
  504.                     info_message (txt_untagged_art);
  505.                 } else {
  506.                     arts[respnum].tagged = ++num_of_tagged_arts;
  507.                     info_message (txt_tagged_art);
  508.                 }
  509.                 break;
  510.  
  511.             case 'v':
  512.                 info_message (cvers);
  513.                 break;
  514.  
  515.             case 'w':    /* post a basenote */
  516.                 if (! can_post) {
  517.                     info_message (txt_cannot_post);
  518.                     break;
  519.                 }
  520.                 if (post_base (group, &posted)) {
  521.                     redraw_page (respnum, group);
  522.                 }
  523.                 break;
  524.  
  525.             case 'W':    /* display messages posted by user */
  526.                 if (user_posted_messages ()) {
  527.                     redraw_page (respnum, group);
  528.                 }
  529.                 break;
  530.  
  531.             case 'x':    /* crosspost current article */
  532.                 feed_articles (FEED_XPOST, PAGE_LEVEL, "Crosspost", respnum, group_path);
  533.                 break;
  534.  
  535.             case 'z':    /* mark article as unread (to return) */
  536.                 arts[respnum].unread = ART_WILL_RETURN;
  537.                 info_message (txt_art_marked_as_unread);
  538.                 break;
  539.  
  540.             default:
  541.                 info_message(txt_bad_command);
  542.         }
  543.     }
  544.  
  545. #endif /* INDEX_DAEMON */
  546. }
  547.  
  548.  
  549. void redraw_page (respnum, group)
  550.     int respnum;
  551.     char *group;
  552. {
  553.     if (note_page == ART_UNAVAILABLE) {
  554.         ClearScreen ();
  555.         printf (txt_art_unavailable, arts[respnum].artnum);
  556.         fflush (stdout);
  557.     } else if (note_page > 0) {
  558.         note_page--;
  559.         fseek (note_fp, note_mark[note_page], 0);
  560.         show_note_page (respnum, group);
  561.     }
  562. }
  563.  
  564.  
  565. void show_note_page (respnum, group)
  566.     int respnum;
  567.     char *group;
  568. {
  569. #ifndef INDEX_DAEMON
  570.  
  571.     char buf[LEN];
  572.     char buf2[LEN+50];
  573.     char *p, *q;
  574.     int i, j;
  575.     int ctrl_L;        /* form feed character detected */
  576.     long tmp_pos;
  577.  
  578.     ClearScreen ();
  579.  
  580.     note_line = 1;
  581.  
  582.     if (note_size == 0L) {
  583.         tmp_pos = ftell (note_fp);
  584.         fseek (note_fp, 0L, 2);            /* goto end of article */
  585.         note_size = ftell (note_fp);
  586.         fseek (note_fp, tmp_pos, 0);    /* goto old position */
  587.     }
  588.     
  589.     if (note_page == 0)
  590.         show_first_header (respnum, group);
  591.     else
  592.         show_cont_header (respnum);
  593.  
  594.     ctrl_L = FALSE;
  595.     while (note_line < LINES) {
  596.         if (fgets (buf, sizeof (buf), note_fp) == NULL) {
  597.             note_end = TRUE;
  598.             break;
  599.         }
  600.  
  601.         buf[LEN-1] = '\0';
  602.         if (rotate)
  603.             for (p = buf, q = buf2; *p && *p != '\n' && q < &buf2[LEN]; p++) {
  604.                 if (*p == '\b' && q > buf2) {
  605.                     q--;
  606.                 } else if (*p == 12) {        /* ^L */
  607.                     *q++ = '^';
  608.                     *q++ = 'L';
  609.                     ctrl_L = TRUE;
  610.                 } else if (*p == '\t') {
  611.                     i = q - buf2;
  612.                     j = (i|7) + 1;
  613.  
  614.                     while (i++ < j)
  615.                         *q++ = ' ';
  616.                 } else if (((*p) & 0xFF) < ' ') {
  617.                     *q++ = '^';
  618.                     *q++ = ((*p) & 0xFF) + '@';
  619.                 } else if (*p >= 'A' && *p <= 'Z')
  620.                     *q++ = 'A' + (*p - 'A' + rotate) % 26;
  621.                 else if (*p >= 'a' && *p <= 'z')
  622.                     *q++ = 'a' + (*p - 'a' + rotate) % 26;
  623.                 else
  624.                     *q++ = *p;
  625.             }
  626.         else
  627.             for (p = buf, q = buf2; *p && *p != '\n' && q < &buf2[LEN]; p++) {
  628.                 if (*p == '\b' && q > buf2) {
  629.                     q--;
  630.                 } else if (*p == 12) {        /* ^L */
  631.                     *q++ = '^';
  632.                     *q++ = 'L';
  633.                     ctrl_L = TRUE;
  634.                 } else if (*p == '\t') {
  635.                     i = q - buf2;
  636.                     j = (i|7) + 1;
  637.  
  638.                     while (i++ < j)
  639.                         *q++ = ' ';
  640.                 } else if (((*p) & 0xFF) < ' ') {
  641.                     *q++ = '^';
  642.                     *q++ = ((*p) & 0xFF) + '@';
  643.                 } else
  644.                     *q++ = *p;
  645.             }
  646.  
  647.         *q = '\0';
  648.  
  649.         printf("%s\r\n", buf2);
  650.  
  651.         note_line += ((int) strlen (buf2) / COLS) + 1;
  652.  
  653.         if (ctrl_L) {
  654.             break;
  655.         }
  656.     }
  657.  
  658.     note_mark[++note_page] = ftell (note_fp);
  659.  
  660.     if (note_mark[note_page] == note_size) {
  661.         note_end = TRUE;
  662.     }
  663.  
  664.     if (note_end) {
  665.         MoveCursor (LINES, MORE_POS-(5+BLANK_PAGE_COLS));
  666.         StartInverse ();    
  667.         if (arts[respnum].thread != -1) {
  668.             fputs (txt_next_resp, stdout);
  669.             fflush (stdout);
  670.         } else {
  671.             fputs (txt_last_resp, stdout);
  672.             fflush (stdout);
  673.         }
  674.         EndInverse ();
  675.     } else {
  676.         if (note_size > 0) {
  677.             draw_percent_mark ((int) note_mark[note_page], (int) note_size);
  678.         } else {
  679.             MoveCursor (LINES, MORE_POS-BLANK_PAGE_COLS);
  680.             StartInverse ();    
  681.             fputs (txt_more, stdout);
  682.             fflush (stdout);
  683.             EndInverse ();
  684.         }
  685.     }
  686.     MoveCursor (LINES, 0);
  687.  
  688. #endif /* INDEX_DAEMON */
  689. }
  690.  
  691.  
  692. void show_first_header (respnum, group)
  693.     int respnum;
  694.     char *group;
  695. {
  696.     int whichresp;
  697.     int x_resp;
  698.     char buf[LEN];
  699.     char tmp[LEN];
  700.     int pos, i;
  701.     int n;
  702.  
  703.     whichresp = which_response (respnum);
  704.     x_resp = num_of_responses (which_thread (respnum));
  705.  
  706.     ClearScreen ();
  707.  
  708.     strcpy (buf, note_h_date);
  709.     pos = (COLS - (int) strlen (group)) / 2;
  710.     for (i = strlen(buf); i < pos; i++)
  711.         buf[i] = ' ';
  712.     buf[i] = '\0';
  713.  
  714.     strcat (buf, group);
  715.  
  716.     for (i = strlen(buf); i < RIGHT_POS ; i++)
  717.         buf[i] = ' ';
  718.     buf[i] = '\0';
  719.  
  720.     printf (txt_thread_x_of_n, buf, which_thread (respnum) + 1, top_base);
  721.  
  722.     sprintf (buf, txt_art, arts[respnum].artnum);
  723.     n = strlen (buf);
  724.     fputs (buf, stdout);
  725.  
  726.     strcpy (buf, note_h_subj);
  727.     buf[RIGHT_POS - 5 - n] = '\0';
  728.  
  729.     pos = ((COLS - (int) strlen (buf)) / 2) - 2;
  730.  
  731.     if (pos > n) {
  732.         MoveCursor (1, pos);
  733.     } else {
  734.         MoveCursor (1, n);
  735.     }
  736.  
  737.     StartInverse ();
  738.     fputs (buf, stdout);
  739.     EndInverse ();
  740.  
  741.     MoveCursor (1, RIGHT_POS);
  742.     if (whichresp)
  743.         printf (txt_resp_x_of_n, whichresp, x_resp);
  744.     else {
  745.         if (x_resp == 0)
  746.             fputs (txt_no_resp, stdout);
  747.         else if (x_resp == 1)
  748.             fputs (txt_1_resp, stdout);
  749.         else
  750.             printf (txt_x_resp, x_resp);
  751.     }
  752.  
  753.     if (*note_h_org) {
  754.         if (strcmp (arts[respnum].from, arts[respnum].name) == 0) {
  755.             strcpy (tmp, note_h_org);
  756.         } else {
  757.             sprintf (tmp, txt_s_at_s, arts[respnum].name, note_h_org);
  758.         }
  759.     } else {
  760.         strcpy (tmp, arts[respnum].name);
  761.     }
  762.  
  763.     tmp[LEN-1] = '\0';
  764.  
  765.     sprintf (buf, "%s  ", arts[respnum].from);
  766.  
  767.     pos = COLS - 1 - (int) strlen(tmp);
  768.     if ((int) strlen (buf) + (int) strlen (tmp) >= COLS - 1) {
  769.         strncat (buf, tmp, COLS - 1 - (int) strlen(buf));
  770.         buf[COLS-1] = '\0';
  771.     } else {
  772.         for (i = strlen(buf); i < pos; i++)
  773.             buf[i] = ' ';
  774.         buf[i] = '\0';
  775.         strcat (buf, tmp);
  776.     }
  777.     printf ("%s\r\n\r\n", buf);
  778.  
  779.     note_line += 4;
  780. }
  781.  
  782.  
  783. void show_cont_header (respnum)
  784.     int respnum;
  785. {
  786.     int whichresp;
  787.     int whichbase;
  788.     char buf[LEN];
  789.  
  790.     whichresp = which_response (respnum);
  791.     whichbase = which_thread (respnum);
  792.  
  793.     assert (whichbase < top_base);
  794.  
  795.     if (whichresp)
  796.         sprintf(buf, txt_thread_resp_page,
  797.             whichbase + 1,
  798.             top_base,
  799.             whichresp,
  800.             note_page + 1,
  801.             note_h_subj);
  802.     else
  803.         sprintf(buf, txt_thread_page,
  804.             whichbase + 1,
  805.             top_base,
  806.             note_page + 1,
  807.             note_h_subj);
  808.  
  809.     buf[COLS-1] = '\0';
  810.     printf("%s\r\n\r\n", buf);
  811.  
  812.     note_line += 2;
  813. }
  814.  
  815.  
  816. int art_open (art, group_path)
  817.     long art;
  818.     char *group_path;
  819. {
  820.     char buf[1025];
  821.     char *p;
  822.  
  823.     note_page = 0;
  824.  
  825.     art_close ();    /* just in case */
  826.  
  827.     if ((note_fp = open_art_fp (group_path, art)) == NULL) {
  828.         return (ART_UNAVAILABLE);
  829.     }
  830.  
  831.     note_h_path[0] = '\0';
  832.     note_h_subj[0] = '\0';
  833.     note_h_org[0] = '\0';
  834.     note_h_date[0] = '\0';
  835.     note_h_newsgroups[0] = '\0';
  836.     note_h_messageid[0] = '\0';
  837.     note_h_distrib[0] = '\0';
  838.     note_h_followup[0] = '\0';
  839.  
  840.     while (fgets(buf, sizeof buf, note_fp) != NULL) {
  841.         buf[1024] = '\0';
  842.  
  843.         for (p=buf ; *p && *p != '\n' ; p++) {
  844.             if (((*p) & 0xFF) < ' ')
  845.                 *p = ' ';
  846.         }
  847.         *p = '\0';
  848.         
  849.         if (*buf == '\0')
  850.             break;
  851.  
  852.           if (match_header (buf, "Path", note_h_path, LEN))
  853.               continue;
  854.           if (match_header (buf, "Subject", note_h_subj, LEN))
  855.               continue;
  856.           if (match_header (buf, "Organization", note_h_org, LEN))
  857.               continue;
  858.           if (match_header (buf, "Date", note_h_date, LEN))
  859.               continue;
  860.           if (match_header (buf, "Newsgroups", note_h_newsgroups, LEN))
  861.               continue;
  862.           if (match_header (buf, "Message-ID", note_h_messageid, LEN))
  863.               continue;
  864.           if (match_header (buf, "Message-Id", note_h_messageid, LEN))
  865.               continue;
  866.           if (match_header (buf, "Distribution", note_h_distrib, LEN))
  867.               continue;
  868.           if (match_header (buf, "Followup-To", note_h_followup, LEN))
  869.               continue;
  870.     }
  871.  
  872.     note_mark[0] = ftell (note_fp);
  873.     note_end = FALSE;
  874.  
  875.     return (0);
  876. }
  877.  
  878.  
  879. void art_close ()
  880. {
  881.     if (note_fp && note_page != ART_UNAVAILABLE) {
  882.         fclose (note_fp);
  883.         note_fp = (FILE *) 0;
  884.     }
  885. }
  886.  
  887.  
  888. int prompt_response (ch, respnum)
  889.     int ch;
  890.     int respnum;
  891. {
  892.     int num;
  893.  
  894.     clear_message ();
  895.  
  896.     if ((num = prompt_num (ch, txt_read_resp)) == -1) {
  897.         clear_message ();
  898.         return -1;
  899.     }
  900.  
  901.     return choose_response (which_thread (respnum), num);
  902. }
  903.  
  904.  
  905. void yank_to_addr (orig, addr)
  906.     char *orig;
  907.     char *addr;
  908. {
  909.     char *p;
  910.  
  911.     for (p = orig; *p; p++)
  912.         if (((*p) & 0xFF) < ' ')
  913.             *p = ' ';
  914.  
  915.     while (*addr)
  916.         addr++;
  917.  
  918.     while (*orig) {
  919.         while (*orig && (*orig == ' ' || *orig == '"' || *orig == ','))
  920.             orig++;
  921.         *addr++ = ' ';
  922.         while (*orig && (*orig != ' ' && *orig != ',' && *orig != '"'))
  923.             *addr++ = *orig++;
  924.         while (*orig && (*orig == ' ' || *orig == '"' || *orig == ','))
  925.             orig++;
  926.         if (*orig == '(') {
  927.             while (*orig && *orig != ')')
  928.                 orig++;
  929.             if (*orig == ')')
  930.                 orig++;
  931.         }
  932.     }
  933.     *addr = '\0';
  934. }
  935.  
  936.  
  937. int show_last_page ()
  938. {
  939.     char buf[LEN];
  940.     char buf2[LEN+50];
  941.     char *p, *q;
  942.     int ctrl_L;        /* form feed character detected */
  943.     int i, j;
  944.     long tmp_pos;
  945.     
  946.     if (note_end) {
  947.         return FALSE;
  948.     }
  949.  
  950.     if (note_size == 0L) {
  951.         tmp_pos = ftell (note_fp);
  952.         fseek (note_fp, 0L, 2);            /* goto end of article */
  953.         note_size = ftell (note_fp);
  954.         fseek (note_fp, tmp_pos, 0);    /* goto old position */
  955.     }
  956.  
  957.     while (! note_end) {
  958.         note_line = 1;
  959.         ctrl_L = FALSE;
  960.  
  961.         if (note_page == 0) {
  962.             note_line += 4;
  963.         } else {
  964.             note_line += 2;
  965.         }
  966.         while (note_line < LINES) {
  967.             if (fgets (buf, sizeof buf, note_fp) == NULL) {
  968.                 note_end = TRUE;
  969.                 break;
  970.             }
  971.             buf[LEN-1] = '\0';
  972.             for (p = buf, q = buf2;    *p && *p != '\n' && q<&buf2[LEN]; p++) {
  973.                 if (*p == '\b' && q > buf2) {
  974.                     q--;
  975.                 } else if (*p == 12) {        /* ^L */
  976.                     *q++ = '^';
  977.                     *q++ = 'L';
  978.                     ctrl_L = TRUE;
  979.                 } else if (*p == '\t') {
  980.                     i = q - buf2;
  981.                     j = (i|7) + 1;
  982.  
  983.                     while (i++ < j) {
  984.                         *q++ = ' ';
  985.                     }
  986.                 } else if (((*p) & 0xFF) < ' ') {
  987.                     *q++ = '^';
  988.                     *q++ = ((*p) & 0xFF) + '@';
  989.                 } else {
  990.                     *q++ = *p;
  991.                 }
  992.             }
  993.             *q = '\0';
  994.             note_line += ((int) strlen (buf2) / COLS) + 1;
  995.  
  996.             if (ctrl_L) {
  997.                 break;
  998.             }
  999.         }
  1000.         if (note_mark[note_page] == note_size) {
  1001.             note_end = TRUE;
  1002.             note_page--;
  1003.             break;
  1004.         } else if (! note_end) {
  1005.             note_mark[++note_page] = ftell(note_fp);
  1006.         }
  1007.     }
  1008.     fseek (note_fp, note_mark[note_page], 0);
  1009.     return TRUE;
  1010. }
  1011.  
  1012.  
  1013. int match_header (buf, pat, body, len)
  1014.     char *buf;
  1015.     char *pat;
  1016.     char *body;
  1017.     int    len;
  1018. {
  1019.     int    plen = strlen (pat);
  1020.  
  1021.     if(strncmp (buf, pat, plen) == 0 && buf[plen] == ':' && buf[plen + 1] == ' ') {
  1022.         plen += 2;
  1023.         while (buf[plen] == ' ')
  1024.             plen++;
  1025.         strncpy (body, &buf[plen], len);
  1026.         body[len - 1] = '\0';
  1027.         return TRUE;
  1028.     }
  1029.     return FALSE;
  1030. }
  1031.