home *** CD-ROM | disk | FTP | other *** search
/ Beijing Paradise BBS Backup / PARADISE.ISO / software / BBSDOORW / SNWS191S.ZIP / ARTICLE.C < prev    next >
C/C++ Source or Header  |  1993-08-07  |  32KB  |  1,157 lines

  1. /*
  2.     SNEWS 1.91
  3.  
  4.     article - routines to read and display an article
  5.  
  6.  
  7.     Copyright (C) 1991  John McCombs, Christchurch, NEW ZEALAND
  8.                         john@ahuriri.gen.nz
  9.                         PO Box 2708, Christchurch, NEW ZEALAND
  10.  
  11.     Modifications copyright (C) 1993  Daniel Fandrich
  12.                         <dan@fch.wimsey.bc.ca> or CompuServe 72365,306
  13.  
  14.     This program is free software; you can redistribute it and/or modify
  15.     it under the terms of the GNU General Public License, version 1, as
  16.     published by the Free Software Foundation.
  17.  
  18.     This program is distributed in the hope that it will be useful,
  19.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  20.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21.     GNU General Public License for more details.
  22.  
  23.     See the file COPYING, which contains a copy of the GNU General
  24.     Public License.
  25.  
  26.  
  27.     Source is formatted with a tab size of 4.
  28.  
  29.  */
  30.  
  31.  
  32. #include "defs.h"
  33. #include "snews.h"
  34. #include "pccharst.h"
  35. #include <alloc.h>
  36. #include <dir.h>
  37. #include <io.h>
  38. #include <ctype.h>
  39.  
  40. enum char_sets current_char_set = DEFAULT_CHAR_SET;
  41. int active_code_page;
  42.  
  43. static char *empty_line = "\n";        /* static empty line structure */
  44.  
  45. void ReplyAddress(TEXT *tx, char *subject);
  46.  
  47.  
  48. /*-------------------- set up the global code page vars --------------------*/
  49. void select_code_page(void)
  50. {
  51.     code_page_table = select_cp_table(active_code_page = get_code_page());
  52. }
  53.  
  54. /*------------------------- read in an article -----------------------------*/
  55. TEXT *load_article(char *fnx, long offset)
  56. {
  57.     /*
  58.      *  Open the file and read it.  Save the author and organisation
  59.      *  fill in the structures
  60.      */
  61.  
  62.     FILE *tmp_file;
  63.     char buf[256], lnbuf[256], *p, *s, *token;
  64.     TEXT *tx;
  65.     LINE *ln, *lz;
  66.     int  ct, i;
  67.  
  68.     tx = NULL;
  69.     ct = 0;
  70.     current_char_set = DEFAULT_CHAR_SET;
  71.  
  72.     if ((tmp_file = flockopen(fnx, "rb")) != NULL) {
  73.  
  74.         fseek(tmp_file, offset, SEEK_SET);
  75.  
  76.         tx = xmalloc(sizeof(TEXT));
  77.         tx->top = NULL;
  78.         tx->start = NULL;
  79.         strcpy(tx->follow_up, "");
  80.         strcpy(tx->author, " ** none ** ");
  81.         strcpy(tx->organisation, " ** none ** ");
  82.  
  83.         while (fgets(buf, sizeof(buf)-1, tmp_file) != NULL) {
  84.  
  85.             if (strncmp(buf, "@@@@END", 7) == 0) break;
  86.  
  87.             expand_tabs(buf, sizeof(buf)-1);
  88.  
  89.             /*
  90.              *  We now have a line of input.  If the line is too long
  91.              *  it is wrapped at spaces or '!'.  The lines of text are
  92.              *  stored in LINE structures
  93.              */
  94.             p = &buf[0];
  95.             while (strlen(p) > 0) {
  96.  
  97.                 strcpy(lnbuf, p);
  98.                 if (strlen(p) <= 80) {
  99.                     strcpy(lnbuf, p);
  100.                     *p = '\x00';        /* signal we're ready for a new line */
  101.                 } else {
  102.                     /* can we split the line at a nice spot (space , !) ? */
  103.                     p += 79;
  104.                     for (i = 79; i > 50; i--) {
  105.                         if ((lnbuf[i] == ' ') || (lnbuf[i] == '!') ||
  106.                             (lnbuf[i] == ','))
  107.                             break;
  108.                         p--;
  109.                     }
  110.                     /* can't split nicely -- split at the end of the line */
  111.                     if (i <= 50) {
  112.                         p += (79-i);
  113.                         i = 79;
  114.                     }
  115.                     lnbuf[i] = '\x00';
  116.                 }
  117.  
  118.                 /* is it the first line - if so int the TEXT structure */
  119.                 if (ct == 0) {
  120.                     ln = xmalloc(sizeof(LINE));
  121.                     ln->last = NULL;
  122.                     tx->top = ln;
  123.                 } else {
  124.                     lz = ln;
  125.                     ln->next = xmalloc(sizeof(LINE));
  126.                     ln = ln->next;
  127.                     ln->last = lz;
  128.                 }
  129.  
  130.                 ln->index = ct;
  131.                 if (lnbuf[0] == '\n')    /* is the line empty? */
  132.                     ln->data = empty_line;    /* yes, save malloc() overhead */
  133.                 else {
  134.                     ln->data = xmalloc(strlen(lnbuf)+1);
  135.                     strcpy(ln->data, lnbuf);
  136.                 }
  137.  
  138.                 /* Find first line of body */
  139.                 if ((strlen(lnbuf) == 1) && (tx->start == NULL))
  140.                     tx->start = ln;
  141.  
  142.                 ct++;
  143.  
  144.                 /* save the header info */
  145.                 if ((tx->start == NULL) && (strnicmp("From:", lnbuf, 5) == 0)) {
  146.                     s = lnbuf + 5;
  147.                     while (*s && isspace(*s)) s++;
  148.                     *(s + WHO_LENGTH - 1) = '\0';
  149.                     strtok(strcpy(tx->author, s), "\n");
  150.                 }
  151.  
  152.                 if ((tx->start == NULL) &&  ((strnicmp("Organisation:", lnbuf, 13) == 0) || (strncmp("Organization:", lnbuf, 13) == 0))) {
  153.                     s = lnbuf + 13;
  154.                     while (*s && isspace(*s)) s++;
  155.                     *(s + ORG_LENGTH - 1) = '\0';
  156.                     strtok(strcpy(tx->organisation, s), "\n");
  157.                 }
  158.  
  159.                 if ((tx->start == NULL) && (strnicmp("Followup-To:", lnbuf, 12) == 0)) {
  160.                     s = lnbuf + 12;
  161.                     while (*s && isspace(*s)) s++;
  162.                     *(s + sizeof(tx->follow_up) - 1) = '\0';
  163.                     strtok(strcpy(tx->follow_up, s), "\n");
  164.                 }
  165.  
  166.                 if ((tx->start == NULL) && (strnicmp("Content-Type:", lnbuf, 13) == 0)) {
  167.                     s = lnbuf + 13;
  168.  
  169.                     while (*s && isspace(*s)) s++;    /* kill leading spaces */
  170.                     token = strtok(s, " ;\n");
  171.                     while (token != NULL) {
  172.                         if (strnicmp("charset=", token, 8) == 0) {
  173.                             current_char_set = select_char_set(token+8);
  174.                             break;
  175.                         }
  176.                         token = strtok(NULL, " ;\n");
  177.                     }
  178.                 }
  179.             }
  180.  
  181.         }
  182.  
  183.         ln->next = NULL;
  184.         tx->lines = ct;
  185.  
  186.         fclose(tmp_file);
  187.     }
  188.  
  189.     return(tx);
  190. }
  191.  
  192.  
  193.  
  194. /*---------------------- deallocate article memory ------------------------*/
  195. void free_article(TEXT *t)
  196. {
  197.  
  198.     LINE *l, *k;
  199.  
  200.     l = t->top;
  201.     while (l != NULL) {
  202.         k = l;
  203.         l = l->next;
  204.     if (k->data != empty_line)    /* don't free empty line pointer */
  205.             free(k->data);
  206.         free(k);
  207.     }
  208.  
  209.     free(t);
  210. }
  211.  
  212.  
  213.  
  214. /*---------------------------- read an article ----------------------------*/
  215. enum exit_codes read_article(ACTIVE *gp, TEXT *tx, char *subject, int a_ct, int of_ct)
  216. {
  217.     /*
  218.      *  This routine allows the user to read an article
  219.      */
  220.  
  221.     LINE   *this, *tmp;   /* current thread                    */
  222.     enum exit_codes exit_code;     /* why we are exiting the loop      */
  223.     char   sub_tmp[80];        /* new subject line */
  224.  
  225.     int    i, maxx;
  226.  
  227.     this = tx->start;
  228.     if (this->next != NULL)
  229.         this = this->next;            /* skip over blank line */
  230.     show_article(gp, tx, subject, this, a_ct, of_ct);
  231.  
  232.     exit_code = EX_CONT;
  233.     while ((exit_code == EX_CONT) || (exit_code == EX_DUMMY)) {
  234.  
  235.         exit_code = EX_CONT;
  236.         gotoxy(1,25);
  237.         switch (get_any_key()) {
  238.  
  239.                     case Fn1    :
  240.                     case '?'    :
  241.                     case 'h'    :
  242.                         show_help(HELP_ARTICLES);
  243.                         break;
  244.  
  245.                     case Fn2    :
  246.                         show_values();
  247.                         break;
  248.  
  249.                     case Fn3    :
  250.                         if (current_char_set++ == US_ASCII)
  251.                             current_char_set = 0;
  252.                         break;
  253.  
  254.                     case Fn4    :
  255.                         pipe_article(tx, my_stuff.hotpipe);
  256.                         message("-- Done --");
  257.                         break;
  258.  
  259.                     case UP_ARR :
  260.                         if (this->last != NULL) {
  261.                             this = this->last;
  262.                             gotoxy(1,TEXT_LINE+PAGE_LENGTH-1);
  263.                             delline();
  264.                             gotoxy(1,TEXT_LINE);
  265.                             insline();
  266.                             clreol();
  267.                             cputs(translate_line(this->data, current_char_set));
  268.                             show_percent((int) ((100L * (this==NULL ? tx->lines+1 : min((this->index) + PAGE_LENGTH, tx->lines+1))) / ((tx->lines) + 1)));
  269.                         }
  270.                         exit_code = EX_DUMMY;
  271.                         break;
  272.  
  273.                     case DN_ARR :
  274.                         if (this->next != NULL &&
  275.                             (this->index + PAGE_LENGTH < tx->lines ||
  276.                              this->index <= tx->start->index)) {
  277.                             this = this->next;
  278.                             gotoxy(1,TEXT_LINE);
  279.                             delline();
  280.                             gotoxy(1,TEXT_LINE+PAGE_LENGTH-1);
  281.                             insline();
  282.  
  283.                             tmp = this;
  284.                             for (i = 0; i < PAGE_LENGTH-1; i++) {
  285.                                 tmp = tmp->next;
  286.                                 if (tmp == NULL) break;
  287.                             }
  288.  
  289.                             if (tmp)
  290.                                 cputs(translate_line(tmp->data, current_char_set));
  291.                             show_percent((int) ((100L * (tmp==NULL ? tx->lines+1 : tmp->index)) / ((tx->lines) + 1)));
  292.                         }
  293.                         exit_code = EX_DUMMY;
  294.                         break;
  295.  
  296.                     case 'b'    :
  297.                     case PGUP   :
  298.                         for (i = 0; i < PAGE_LENGTH-1; i++) {
  299.                             if (this->last == NULL) break;
  300.                             this = this->last;
  301.                         }
  302.                         break;
  303.  
  304.                     case ' '    :
  305.                     case PGDN   :
  306.                         if ( this->next == NULL )
  307.                             exit_code = EX_DUMMY;
  308.                         else {
  309. #ifndef ALTPAGING            /* maintain reading place paging method */
  310.                             maxx = 0;
  311.                             if ((tx->lines - (this->index + PAGE_LENGTH)) > 0)
  312.                                 maxx = PAGE_LENGTH - 1;
  313.  
  314. #else                        /* don't show past end of article paging method */
  315.                             maxx = tx->lines - (this->index + PAGE_LENGTH);
  316.                             maxx = min(maxx, PAGE_LENGTH - 1);
  317.                             maxx = max(maxx, tx->start->index + 1 - this->index);
  318. #endif
  319.  
  320.                             if ( maxx <= 0 )
  321.                                 exit_code = EX_DUMMY;
  322.                             else
  323.                                 for (i = 0; i < maxx; i++) {
  324.                                     if (this->next == NULL) break;
  325.                                     this = this->next;
  326.                                 }
  327.                         }
  328.                         break;
  329.  
  330.                     case 'R'-0x40    :
  331.                     case HOME   :
  332.                         tmp = this;
  333.                         this = tx->start;
  334.                         if (this->next != NULL)
  335.                             this = this->next;    /* skip over blank line */
  336.                         if (this == tmp)
  337.                             exit_code = EX_DUMMY;
  338.                         break;
  339.  
  340.                     case END    :
  341.                         tmp = this;
  342.                         this = tx->start;
  343.                         while (this->next != NULL)
  344.                             this = this->next;
  345.                         for (i = 0; i < PAGE_LENGTH-1; i++) {
  346.                             if (this->last == NULL) break;
  347.                             if (tx->start->next && this == tx->start->next)
  348.                                 break;
  349.                             this = this->last;
  350.                         }
  351.                         if (this == tmp)
  352.                             exit_code = EX_DUMMY;
  353.                         break;
  354.  
  355.                     case '['    :
  356.                     case '-'    :
  357.                     case LEFT    :
  358.                         exit_code = EX_PREVIOUS;
  359.                         break;
  360.  
  361.                     case ']'    :
  362.                     case RIGHT  :
  363.                         exit_code = EX_NEXT;
  364.                         break;
  365.  
  366.                     case cLEFT    :
  367.                         exit_code = EX_PREVIOUS10;
  368.                         break;
  369.  
  370.                     case cRIGHT :
  371.                         exit_code = EX_NEXT10;
  372.                         break;
  373.  
  374.             case 'v'    :
  375.                 tmp = this;
  376.                 this = tx->top;
  377.                 if (this == tmp)
  378.                     exit_code = EX_DUMMY;
  379.                 break;
  380.  
  381.             case '/'    :
  382.                 this = search_text(this);
  383.                 break;
  384.  
  385.             case 'p'    :
  386.                 strcpy(sub_tmp, "");
  387.                 post(NULL, gp->group, sub_tmp);
  388.                 break;
  389.  
  390.             case 'f'    :
  391.                 if (strnicmp(subject, "Re:", 3) != 0) {
  392.                     strcpy(sub_tmp, "Re: ");
  393.                     strcat(sub_tmp, subject);
  394.                 } else
  395.                     strcpy(sub_tmp, subject);
  396.                 if (strlen(tx->follow_up) == 0) {
  397.                     post(tx, gp->group, sub_tmp);
  398.                 } else if (stricmp(tx->follow_up, "poster") == 0) {
  399.                     reply_to_article(tx, sub_tmp);
  400.                 } else
  401.                     post(tx, tx->follow_up, sub_tmp);
  402.                 break;
  403.  
  404.             case 'r'    :
  405.                 if (strnicmp(subject, "Re:", 3) != 0) {
  406.                     strcpy(sub_tmp, "Re: ");
  407.                     strcat(sub_tmp, subject);
  408.                 } else
  409.                     strcpy(sub_tmp, subject);
  410.                 reply_to_article(tx, sub_tmp);
  411.                 break;
  412.  
  413.             case 'R'    :
  414.                 if (strnicmp(subject, "Re:", 3) != 0) {
  415.                     strcpy(sub_tmp, "Re: ");
  416.                     strcat(sub_tmp, subject);
  417.                 } else
  418.                     strcpy(sub_tmp, subject);
  419.                 ReplyAddress(tx, sub_tmp);
  420.                 break;
  421.  
  422.             case 'm'    :
  423.                 mail_to_someone(tx);
  424.                 break;
  425.  
  426.             case 's'    :
  427.                 save_to_disk(tx, NULL, FALSE);
  428.                 message("-- Done --");
  429.                 break;
  430.  
  431.             case 'w'    :
  432.                 save_to_disk(tx, my_stuff.extract_file, TRUE);
  433.                 message("-- Done --");
  434.                 break;
  435.  
  436.             case '|'    :
  437.                 pipe_article(tx, NULL);
  438.                 message("-- Done --");
  439.                 break;
  440.  
  441.             case 'x'    :
  442.             case 'X'    :
  443.                 rot13(tx);
  444.                 break;
  445.  
  446.             case 'X'-0x40    :
  447.                 this = tx->start;
  448.                 if (this->next != NULL)
  449.                     this = this->next;    /* skip over blank line */
  450.                 rot13(tx);
  451.                 break;
  452.  
  453.             case '!'    :
  454.                 textbackground(BLACK);    textcolor(LIGHTGRAY);
  455.                 cprintf("\r\n");
  456.                 spawnl(P_WAIT, getenv("COMSPEC"), getenv("COMSPEC"), NULL);
  457.                 break;
  458.  
  459.             case TAB    :
  460.                 exit_code = EX_NEXT_UNREAD;
  461.                 break;
  462.  
  463.             case ENTER  :
  464.                 exit_code = EX_NEXT;
  465.                 break;
  466.  
  467.             case '='    :
  468.             case ESCAPE :
  469.                 exit_code = EX_QUIT;
  470.                 break;
  471.  
  472.             default     :
  473.                 exit_code = EX_DUMMY;
  474.                 break;
  475.         }
  476.         if (exit_code == EX_CONT)
  477.             show_article(gp, tx, subject, this, a_ct, of_ct);
  478.     }
  479.  
  480.     return(exit_code);
  481. }
  482.  
  483.  
  484.  
  485. /*--------------------- display a page of an article ------------------------*/
  486. void show_article(ACTIVE *gp, TEXT *tx, char *subject, LINE *this, int a_ct,
  487.                   int of_ct)
  488. {
  489.     /*
  490.      *  This routine show a page of an article
  491.      */
  492.  
  493.     int    i;
  494.     char   buf[63];
  495.  
  496.     strcpy(buf, subject);
  497.     buf[59] = '\x00';
  498.  
  499.     textbackground(textb);    textcolor(textf);
  500.     clrscr();
  501.     textbackground(headb);    textcolor(headf);
  502.     clreol();
  503.     cprintf("Group: %-53s Article:%3d of %3d\r\n",
  504.            gp->group, a_ct, of_ct);
  505.     clreol();
  506.     cprintf("Subject: %-59s %4d lines\r\n",
  507.         translate_header(buf, current_char_set), tx->lines);
  508.     clreol();
  509. /*    cprintf("From: %s; %s", translate_header(tx->author, current_char_set),
  510.         translate_header(tx->organisation, current_char_set));*/
  511.     cprintf("From: %s", translate_header(tx->author, current_char_set));
  512.     command("ESC=select thread   TAB=next unread   ENTER=next   F1=help");
  513.  
  514.     textbackground(textb);    textcolor(textf);
  515.  
  516.     gotoxy(1, TEXT_LINE);
  517.     for (i = 0; i < PAGE_LENGTH; i++) {
  518.         gotoxy(1, i+TEXT_LINE);
  519.         cputs(translate_line(this->data, current_char_set));
  520.         this = this->next;
  521.         if (this == NULL) break;
  522.     }
  523.  
  524.     show_percent((int) ((100L * (this==NULL ? tx->lines+1 : this->index)) / ((tx->lines) + 1)));
  525. }
  526.  
  527.  
  528. /*-------------------- search for text in article body -------------------*/
  529. LINE *search_text(LINE *this)
  530. {
  531.     char search_text[80];
  532.     static char last_text[80] = {'\0'};
  533.  
  534.     message("Search for? ");
  535.     if (*gets(search_text))
  536.         strcpy(last_text, search_text);
  537.     else
  538.         strcpy(search_text, last_text);
  539. #ifdef AMATCH
  540.     strlwr(search_text);        /* lower case string for case insensitive search */
  541. #endif
  542.  
  543.     if (search_text[0])
  544.         while (this->next) {
  545.             this = this->next;
  546. #ifdef AMATCH
  547.             if (amatch(search_text, strlwr(translate_line(this->data, current_char_set))))
  548. #else
  549.             if (stristr(translate_line(this->data, current_char_set), search_text))
  550. #endif
  551.                 break;
  552.         }
  553.     message("");
  554.     return this;
  555. }
  556.  
  557.  
  558. /*-------------------- show percentage through article -------------------*/
  559. void show_percent(int percent)
  560. {
  561.     /*
  562.      *  This routine displays how far through an article we are
  563.      */
  564.  
  565.     gotoxy(2,25);
  566.     textbackground(msgb);  textcolor(msgf);
  567.     cprintf("%d%%  ", percent);
  568.     textbackground(textb);    textcolor(textf);
  569. }
  570.  
  571.  
  572.  
  573.  
  574. /*-------------------------- save article --------------------------------*/
  575. int save_to_disk(TEXT *tx, char *save_name, int mailbox)
  576. {
  577.     /*
  578.      *  This routine saves an article to disk, appending if necessary
  579.      *  If save_name is NULL, user is asked for a name
  580.      *  If save_name is not NULL, article is saved to the save_name file
  581.      *  If mailbox is nonzero, article is saved in mailbox format
  582.      *  Returns 0 if no error, 1 if error
  583.      *    (one anomaly -- if user elects not to append, 1 is returned)
  584.      */
  585.  
  586.     FILE *tmp = NULL;
  587.     LINE *ln;
  588.     char fn[80];
  589.     int  ch;
  590.  
  591.     if (save_name == NULL) {        /* save function */
  592.         lmessage("Filename? ");
  593.         gets(fn);
  594.     } else                            /* extract function */
  595.         strcpy(fn, save_name);
  596.  
  597.     if (!*fn)        /* abort if no file name given */
  598.         return 0;
  599.  
  600.     expand_filename(fn);            /* expand ~/ in file name */
  601.  
  602.     if (access(fn, 0) == 0) {        /* file exists -- should we append? */
  603.         if (save_name) {
  604.           if ((tmp = fopen(fn, "at")) == NULL) {
  605.             message("*** Cannot open file for appending - "
  606.                 "press any key to continue ***");
  607.                 get_any_key();
  608.           }
  609.         } else {
  610.             message("File exists - append (y/n)? ");
  611.             while (((ch = tolower(getch())) != 'y') && (ch != 'n'));
  612.             if (ch == 'y') {
  613.                 if ((tmp = fopen(fn, "at")) == NULL) {
  614.                     message("*** Cannot open file for appending - "
  615.                          "press any key to continue ***");
  616.                     get_any_key();
  617.                 }
  618.             }
  619.         }
  620.  
  621.     } else {                /* file doesn't exist -- try to create it */
  622.  
  623.     /* make sure the file name given doesn't have bad characters */
  624.         if ((strcspn(fn, " \"*+,;<=>?[]|\x7f\xe5") != strlen(fn)) ||
  625.             ((tmp = fopen(fn, "wt")) == NULL)) {
  626.             message("*** Cannot open file for output - press any key to continue ***");
  627.             get_any_key();
  628.         }
  629.     }
  630.  
  631.     if (tmp != NULL) {
  632.         if (mailbox) {
  633.             fputs("\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
  634.                   "\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\n",tmp);
  635.         }
  636.  
  637.         /* save article contents to disk */
  638.         /* \n are stored in the ln->data structures so we don't need to
  639.            write any others out */
  640.         ln = tx->top;
  641.         while (ln != NULL) {
  642.             fputs(ln->data, tmp);
  643.             ln = ln->next;
  644.         }
  645.         fclose(tmp);
  646.     }
  647.  
  648.     return (tmp == NULL);
  649. }
  650.  
  651.  
  652. /*------------------- pipe article through a program ---------------------*/
  653. void pipe_article(TEXT *tx, char *prog_name)
  654. {
  655.     /*
  656.      *  This routine saves an article to a temporary file.
  657.      *  If prog_name is NULL, user is asked for a program name and the article
  658.      *      is piped through that program.
  659.      *  If prog_name is not NULL, article is piped through that program.
  660.      */
  661.  
  662.     char prog[129], cmd[129], temp[81];
  663.     char *pipe;
  664.  
  665.     if (prog_name == NULL) {            /* save function */
  666.         lmessage("Pipe article through which program? ");
  667.         gets(prog);
  668.     } else                                /* extract function */
  669.         strcpy(prog, prog_name);
  670.  
  671.     if (!*prog)            /* abort if no program given */
  672.         return;
  673.  
  674.     if (strchr(prog, '<')) {
  675.         message("*** Cannot redirect input - press any key to continue ***");
  676.         get_any_key();
  677.     } else {
  678.         sprintf(temp, "%spipe%04x.tmp", my_stuff.temp_str, getpid());
  679.  
  680.         strcpy(cmd, prog);
  681.  
  682.         /* if command contains %s, put file name there */
  683.         if (strstr(cmd, "%s") != NULL) {
  684.             sprintf(cmd, prog, temp);
  685.  
  686.         /* look for a second pipe in command and put the redirect in the right spot */
  687.         } else if ((pipe = strchr(cmd, '|')) != NULL) {
  688.             strcat(strcat(strcat(strcpy(pipe, " <"), temp), " "), strchr(prog, '|'));
  689.  
  690.         /* append file redirect to end of command */
  691.         } else
  692.             strcat(strcat(cmd, " <"), temp);
  693.  
  694.         if (!save_to_disk(tx, temp, FALSE)) {    /* save article to temporary file */
  695.             clrscr();
  696.             system(cmd);
  697.             unlink(temp);
  698.             printf("\n\n");
  699.             message("-- Press any key to continue --");
  700.             get_any_key();
  701.         }
  702.  
  703.     }
  704. }
  705.  
  706.  
  707. /*------------------- expand an alias to the email address ------------------*/
  708. char *expand_alias(char *author)
  709. {
  710.     char buf[256];
  711.     FILE *aliasfile;
  712.     char *s;
  713.  
  714.     /*
  715.      * Given a possible alias in author, search the alias file for a match
  716.      * and return the full email address back in author.
  717.      */
  718.  
  719.     if (*my_stuff.alias_file && (aliasfile = fopen(my_stuff.alias_file,"rt")) != NULL) {
  720.         while (fgets(buf, sizeof(buf)-1, aliasfile) != NULL) {
  721.             /* ignore comment line */
  722.             if (buf[0] == ';' || buf[0] == '#' || isspace(buf[0]))
  723.                    continue;
  724.  
  725.             /* find matching alias */
  726.             if (strnicmp(author, buf, strlen(author)) == 0) {
  727.  
  728.                 /* skip over alias */
  729.                 s = strpbrk(buf," \t");
  730.                 while (*++s && isspace(*s))
  731.                     ;
  732.  
  733.                 /* skip over name in quotes, if any */
  734.                 if (*s == '\"') {
  735.                     ++s;
  736.                     while (*s && (*s != '\"')) ++s;
  737.                     ++s;
  738.                     while (*s && (isspace(*s) || (*s == '<'))) ++s;
  739.                 }
  740.  
  741.                 /* copy full name into the pointer given for the alias */
  742.                 if ((s = strtok(s, " >;\t\r\n")) != NULL)
  743.                     strcpy(author,s);
  744.                 break;
  745.             }
  746.         } /* while */
  747.  
  748.         fclose(aliasfile);
  749.     } /* if */
  750.  
  751.     return author;
  752. }
  753.  
  754.  
  755. /*-------------------------- reply to article ---------------------------*/
  756. void reply_to_article(TEXT *tx, char *subject)
  757. {
  758.     /*
  759.      *  Mail reply to article
  760.      */
  761.  
  762. #ifdef INCLUDE_SIG
  763.     FILE *sig;
  764.     char sig_fn[80];
  765. #endif
  766.  
  767.     FILE *tmp;
  768.     LINE *ln;
  769.     int  ch;
  770.     char fn[81];
  771.     char buf[256];
  772.     char author[WHO_LENGTH], msg_id[MSG_ID_LENGTH];
  773.  
  774.     sprintf(fn, "%srply%04x.tmp", my_stuff.temp_str, getpid());
  775.  
  776.     if ((tmp= fopen(fn, "wt")) != NULL) {
  777.  
  778.         strcpy(author, " ");
  779.         get_his_stuff(tx, author, msg_id);
  780.  
  781.         /* add the quoted message */
  782.         message("Quote article (y/n)? ");
  783.         while (((ch = tolower(getch())) != 'y') && (ch != 'n'));
  784.  
  785.         if (ch == 'y') {
  786.             fprintf(tmp, "In article %s you write:\n", msg_id);
  787.             ln = tx->start;
  788.             while (ln != NULL) {
  789.                 fprintf(tmp, ">%s", ln->data);
  790.                 ln = ln->next;
  791.             }
  792.         }
  793.  
  794. #ifdef INCLUDE_SIG
  795.         /* append the signature if there is one */
  796.         strcpy(sig_fn, my_stuff.home);
  797.         strcat(sig_fn, my_stuff.signature);
  798.         if ((sig= fopen(sig_fn, "rt")) != NULL) {
  799.             fprintf(tmp, "\n--\n");
  800.             while (fgets(buf, 79, sig) != NULL)
  801.                 fputs(buf, tmp);
  802.             fclose(sig);
  803.         }
  804. #endif
  805.         fclose(tmp);
  806.  
  807.         ch = 'e';
  808.  
  809. /* NEW BIT      includes send/abort/edit */
  810.         while ((ch != 's') && (ch != 'a')) {
  811.             if (ch == 'e') {
  812.                sprintf(buf, my_stuff.editor, fn);
  813.                system(buf);
  814.             }
  815.             clrscr();
  816.             gotoxy(1,23);
  817.             cprintf("  Mail reply addressed to: %s",author);
  818.             sprintf(buf,"Mail reply:   (S)end, (A)bort, or (E)dit again? (S/A/E):");
  819.             message(buf);
  820.             while (((ch = tolower(getch())) != 's') && (ch != 'a') && (ch != 'e'));
  821.             gotoxy(1,24);
  822.         }
  823.  
  824.        if (ch == 's') {
  825.             sprintf(buf, "mail -s \"%s\" %s <%s", subject, author, fn);
  826.             system(buf);
  827.         }
  828.         unlink(fn);
  829.         message("-- Press any key to continue --");
  830.         get_any_key();
  831.  
  832.     } else {
  833.         message("*** Cannot open temp file - press any key to continue ***");
  834.         get_any_key();
  835.     }
  836.  
  837. }
  838.  
  839.  
  840.  
  841. /* ---------- (R)eply allowing specified address or alias ---------------- */
  842.  
  843. void ReplyAddress(TEXT *tx, char *subject)
  844. {
  845. #ifdef INCLUDE_SIG
  846.     FILE *sig;
  847.     char sig_fn[80];
  848. #endif
  849.  
  850.     FILE *tmp;
  851.     LINE *ln;
  852.     int  ch;
  853.     char fn[81];
  854.     char buf[256];
  855.     char author[WHO_LENGTH], msg_id[MSG_ID_LENGTH];
  856.  
  857.     sprintf(fn, "%srply%04x.tmp", my_stuff.temp_str, getpid());
  858.  
  859.     if ((tmp= fopen(fn, "wt")) != NULL) {
  860.  
  861.         get_his_stuff(tx, author, msg_id);
  862.  
  863.         /* add the quoted message */
  864.         message("Quote article (y/n)? ");
  865.         while (((ch = tolower(getch())) != 'y') && (ch != 'n'));
  866.  
  867.         if (ch == 'y') {
  868.             fprintf(tmp, "In article %s you write:\n", msg_id);
  869.             ln = tx->start;
  870.             while (ln != NULL) {
  871.                 fprintf(tmp, ">%s", ln->data);
  872.                 ln = ln->next;
  873.             }
  874.         }
  875.  
  876. #ifdef INCLUDE_SIG
  877.         /* append the signature if there is one */
  878.         strcpy(sig_fn, my_stuff.home);
  879.         strcat(sig_fn, my_stuff.signature);
  880.         if ((sig= fopen(sig_fn, "rt")) != NULL) {
  881.             fprintf(tmp, "\n--\n");
  882.             while (fgets(buf, 79, sig) != NULL)
  883.                 fputs(buf, tmp);
  884.             fclose(sig);
  885.         }
  886. #endif
  887.         fclose(tmp);
  888.  
  889.         lmessage("Send reply to whom (aliases allowed)? ");
  890.         gets(author);
  891.  
  892.         if (*author) {
  893.             expand_alias(author);
  894.  
  895.             ch = 'e';
  896.             while ((ch != 's') && (ch != 'a')) {
  897.                 if (ch == 'e') {
  898.                    sprintf(buf, my_stuff.editor, fn);
  899.                    system(buf);
  900.                 }
  901.                 sprintf(buf,"Mail article:   (S)end, (A)bort, or (E)dit again? (S/A/E):");
  902.                 message(buf);
  903.                 while (((ch = tolower(getch())) != 's') && (ch != 'a') && (ch != 'e'));
  904.                 gotoxy(1,24);
  905.             }
  906.             if (ch == 's') {
  907.                 sprintf(buf, "mail -s \"%s\" %s <%s", subject, author, fn);
  908.                 system(buf);
  909.             }
  910.             unlink(fn);
  911.             message("-- Press any key to continue --");
  912.             get_any_key();
  913.  
  914.         } else {
  915.             unlink(fn);
  916.         }
  917.  
  918.     } else {
  919.         message("*** Cannot open temp file - press any key to continue ***");
  920.         get_any_key();
  921.     }
  922.  
  923. }
  924.  
  925.  
  926.  
  927. /*-------------------------- reply to article ---------------------------*/
  928. void mail_to_someone(TEXT *tx)
  929. {
  930.     /*
  931.      *  Mail this article to someone
  932.      */
  933.  
  934.  
  935.     FILE *tmp;
  936.     LINE *ln;
  937.     int  ch;
  938.     char fn[MAXPATH+1];
  939.     char buf[256], who[80];
  940.  
  941.     lmessage("Mail this article to (aliases allowed)? ");
  942.     gets(who);
  943.  
  944.     if (*who) {
  945.  
  946.         sprintf(fn, "%srply%04x.tmp", my_stuff.temp_str, getpid());
  947.         if ((tmp= fopen(fn, "wt")) != NULL) {
  948.  
  949.             expand_alias(who);
  950.  
  951.             fprintf(tmp, "Resent-From: %s@%s (%s)\n",
  952.                 my_stuff.user, my_stuff.my_domain, my_stuff.my_name);
  953.             fprintf(tmp, "Resent-To: %s\n", who);
  954.  
  955.             ln = tx->top;
  956.             while (ln != NULL) {
  957.                 fputs(ln->data, tmp);
  958.                 ln = ln->next;
  959.             }
  960.  
  961.             fclose(tmp);
  962.  
  963.             message("Edit outgoing message (y/n)? ");
  964.             while (((ch = tolower(getch())) != 'y') && (ch != 'n'));
  965.             if (ch == 'y') {
  966.               sprintf(buf, my_stuff.editor, fn);
  967.               system(buf);
  968.             }
  969.  
  970.             sprintf(buf, "Mail article to %s (y/n)? ", who);
  971.             message(buf);
  972.             while (((ch = tolower(getch())) != 'y') && (ch != 'n'));
  973.             if (ch == 'y') {
  974.                 sprintf(buf, "rmail -t <%s", fn);
  975.                 system(buf);
  976.             }
  977.  
  978.             unlink(fn);
  979.  
  980.         } else {
  981.             message("*** cannot open temp file - press any key ***");
  982.             get_any_key();
  983.         }
  984.     }
  985. }
  986.  
  987.  
  988. /*----------------------- find email address in line --------------------*/
  989.  
  990. /*---------------------------------------------------------*/
  991. /* char *get_address()                                     */
  992. /*                                                         */
  993. /* searches for the '@' char in internet addresses and     */
  994. /* then extracts the word the '@' is in.  The terminat-    */
  995. /* ating characters are : ':' '<' '>' '(' ')' '"' '\t'     */
  996. /* '\r' and '\n'; at which point it stops looking.         */
  997. /*                                                         */
  998. /*---------------------------------------------------------*/
  999.  
  1000. char *get_address(char *buf, char *addr)
  1001. {
  1002.     char *p , *c;
  1003.  
  1004.     /* search for the '@' or "at" that is in every Internet address */
  1005.  
  1006.     if ((p = strchr(buf,'@')) != NULL) {
  1007.         c = p;
  1008.         while (*p && (*p !=  ' ' &&
  1009.                       *p != '\t' && 
  1010.                       *p != ':'  && 
  1011.                       *p != '<'  && 
  1012.                       *p != '\"' &&
  1013.                       *p != '('    )) {
  1014.             /* quite a few cases to check for */
  1015.             p--;
  1016.         }
  1017.  
  1018.         while (*c && (*c !=  ' ' &&
  1019.                       *c != '\t' &&
  1020.                       *c != ':'  &&
  1021.                       *c != '>'  &&
  1022.                       *c != '\"' && 
  1023.                       *c != '\n' && 
  1024.                       *c != '\r' &&
  1025.                       *c != ')'    )) {
  1026.             /* quite a few cases to check for */
  1027.             c++;
  1028.         }
  1029.         *c = '\0'; 
  1030.         strcpy(addr, ++p);
  1031.         return addr;
  1032.     }
  1033.  
  1034.     return NULL;
  1035. }
  1036.  
  1037. /*----------------------- get stuff off article header --------------------*/
  1038.  
  1039. /*---------------------------------------------------------*/
  1040. /*  void get_his_stuff()                                   */
  1041. /*                                                         */
  1042. /*  obtains the address of the person in the passed arti-  */
  1043. /*  cle, as well as the msgid.                             */
  1044. /*                                                         */
  1045. /*---------------------------------------------------------*/
  1046. void get_his_stuff(TEXT *tx, char *author, char *msg_id)
  1047. {
  1048.     /*
  1049.      *  Retrieve the author and msg_id from the article
  1050.      */
  1051.  
  1052.     LINE *ln;
  1053.     char *p;
  1054.     char buf[256];
  1055.     char *null_name = " < no name > ";
  1056.  
  1057.     strcpy(author, null_name);
  1058.     strcpy(msg_id, " <none> ");
  1059.  
  1060.     ln = tx->top;
  1061.     while (ln != NULL) {
  1062.         strcpy(buf, ln->data);
  1063.         strtok(buf, " :\n\r");            /* skip over <header>: */
  1064.         p = strtok(NULL, " :\n\r");        /* skip over space */
  1065.         if (p != NULL) {
  1066.  
  1067.             if (strnicmp(ln->data, "Message-ID:", 11) == 0) {
  1068.                 p = ln->data + 11;            /* is this necessary? [df] */
  1069.                 while (*p && isspace(*p))
  1070.                     ++p;
  1071.                 p = strtok(p, " :\n\r");
  1072.                 if (p)
  1073.                     strncpy(msg_id, p, MSG_ID_LENGTH-1);
  1074.                 else
  1075.                     strncpy(msg_id, ln->data + 11, MSG_ID_LENGTH-1);
  1076.                 msg_id[MSG_ID_LENGTH-1] = '\0';
  1077.             }
  1078.  
  1079.             if ((strnicmp(ln->data, "From:", 5) == 0) && (strcmp(author, null_name) == 0)) {
  1080.                 if (get_address(ln->data, author) == NULL) {
  1081.                     strncpy(author, p, WHO_LENGTH-1);
  1082.                     author[WHO_LENGTH-1] = '\0';
  1083.                 }
  1084.             }
  1085.  
  1086.             if (strnicmp(ln->data, "Reply-To:", 5) == 0) {
  1087.                 if (get_address(ln->data, author) == NULL) {
  1088.                     strncpy(author, p, WHO_LENGTH-1);
  1089.                     author[WHO_LENGTH-1] = '\0';
  1090.                 }
  1091.             }
  1092.  
  1093.         } /* if */
  1094.  
  1095.         if (strlen(ln->data) < 2)
  1096.             break;                    /* we've found the start of the body */
  1097.         ln = ln->next;
  1098.     }
  1099. }
  1100.  
  1101. /*--------------------------- rot 13 the article ------------------------*/
  1102. void rot13(TEXT *tx)
  1103. {
  1104.     LINE *ln;
  1105.     int  i, c;
  1106.  
  1107.  
  1108.     ln = tx->start;
  1109.  
  1110.     while (ln != NULL) {
  1111.         for (i = 0; i < strlen(ln->data); i++) {
  1112.             c = *((ln->data)+i);
  1113.             if ((c >= 'A') && (c <= 'Z')) {
  1114.                 *((ln->data)+i) = (((c-'A') + 13) % 26) + 'A';
  1115.             } else {
  1116.                 if ((c >= 'a') && (c <= 'z')) {
  1117.                     *((ln->data)+i) = (((c-'a') + 13) % 26) + 'a';
  1118.                 }
  1119.             }
  1120.         }
  1121.         ln = ln->next;
  1122.     }
  1123. }
  1124.  
  1125.  
  1126. /*--------------------------- expand the tabs ----------------------------*/
  1127. void expand_tabs(char *buf, int max_len)
  1128. {
  1129.     int  l, k;
  1130.     char tmp[256], *p, *t;
  1131.  
  1132.     p = buf;
  1133.     t = &tmp[0];
  1134.     l = 0;
  1135.  
  1136.     while ((*p != '\x00') && (l < max_len)) {
  1137.         if (*p != '\x09') {
  1138.             *t = *p;
  1139.             t++;
  1140.             p++;
  1141.             l++;
  1142.         } else {
  1143.             p++;
  1144.             k = ((l / 8) + 1) * 8;
  1145.             for ( ; l < k ; l++) {
  1146.                 *t = ' ';
  1147.                 t++;
  1148.                 if (l >= max_len) break;
  1149.             }
  1150.         }
  1151.     }
  1152.  
  1153.     *t = '\x00';
  1154.     strcpy(buf, tmp);
  1155. }
  1156.  
  1157.