home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 35 Internet / 35-Internet.zip / snews-20.zip / ARTICLE.C < prev    next >
C/C++ Source or Header  |  1992-08-02  |  23KB  |  837 lines

  1. /*
  2.  
  3.     SNEWS 2.0
  4.  
  5.     news - routines to read and display an article
  6.  
  7.  
  8.     Copyright (C) 1991  John McCombs, Christchurch, NEW ZEALAND
  9.                         john@ahuriri.gen.nz
  10.                         PO Box 2708, Christchurch, NEW ZEALAND
  11.  
  12.     This program is free software; you can redistribute it and/or modify
  13.     it under the terms of the GNU General Public License, version 1, as
  14.     published by the Free Software Foundation.
  15.  
  16.     This program is distributed in the hope that it will be useful,
  17.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.     GNU General Public License for more details.
  20.  
  21.     See the file COPYING, which contains a copy of the GNU General
  22.     Public License.
  23.  
  24.  */
  25.  
  26.  
  27. #include <process.h>
  28.  
  29. #include "defs.h"
  30. #include "snews.h"
  31.  
  32.  
  33. /*------------------------- recognize address ------------------------------*/
  34. char *plain_address(char *raw)
  35. {
  36.   char *ptr = raw, *end;
  37.  
  38.   while (*ptr && !isalnum(*ptr)) {
  39.  
  40.     while (isspace(*ptr)) ptr++;
  41.  
  42.     if (isalnum(*ptr))
  43.       break;
  44.  
  45.     if (*ptr == '(') {
  46.       for (ptr++; *ptr && *ptr != ')'; ptr++);
  47.       if (*ptr == ')') ptr++;
  48.     }
  49.     else if (*ptr == '"') {
  50.       for (ptr++; *ptr && *ptr != '"'; ptr++);
  51.       if (*ptr == '"') ptr++;
  52.     }
  53.     else if (*ptr == '<') {
  54.       ptr++;
  55.       if ((end = strchr(ptr, '>')) != NULL)
  56.     *end = 0;
  57.     }
  58.     else
  59.       ptr++;
  60.   }
  61.  
  62.   if ((end = strchr(ptr, ' ')) != NULL)
  63.     *end = 0;
  64.   if ((end = strchr(ptr, '\t')) != NULL)
  65.     *end = 0;
  66.   if ((end = strchr(ptr, '\n')) != NULL)
  67.     *end = 0;
  68.  
  69.   return ptr;
  70. }
  71.  
  72.  
  73. /*------------------------- read in an article -----------------------------*/
  74. TEXT *load_article(char *fnx, long offset)
  75. {
  76.     /*
  77.      *  Open the file and read it.  Save the author and organisation
  78.      *  fill in the structures
  79.      */
  80.  
  81.     FILE *tmp_file;
  82.     char lnbuf[MAXLINE];
  83.     TEXT *tx;
  84.     LINE *ln, *lz;
  85.     int  ct, i;
  86.  
  87.     tx = NULL;
  88.     ct = 0;
  89.  
  90.     if ((tmp_file = flockopen(fnx, "rb")) != NULL) {
  91.  
  92.         setvbuf(tmp_file, iobuf, _IOFBF, IOBUFSIZE);
  93.         fseek(tmp_file, offset, SEEK_SET);
  94.  
  95.         tx = xmalloc(sizeof(TEXT));
  96.         tx->top = NULL;
  97.         tx->start = NULL;
  98.         tx->subject = NULL;
  99.         tx->follow_up = NULL;
  100.         tx->author = NULL;
  101.         tx->organisation = NULL;
  102.  
  103.         while (fgets(lnbuf, sizeof(lnbuf) - 1, tmp_file) != NULL) {
  104.  
  105.             lnbuf[sizeof(lnbuf) -1] = 0;
  106.  
  107.             if (strncmp(lnbuf, "@@@@END", 7) == 0) break;
  108.  
  109.             /* is it the first line - if so init the TEXT structure */
  110.             if (ct == 0) {
  111.                 ln = xmalloc(sizeof(LINE));
  112.                 ln->last = NULL;
  113.                 tx->top = ln;
  114.             } else {
  115.                 lz = ln;
  116.                 ln->next = xmalloc(sizeof(LINE));
  117.                 ln = ln->next;
  118.                 ln->last = lz;
  119.             }
  120.  
  121.             ln->index = ct;
  122.             ln->data = xmalloc(strlen(lnbuf) + 1);
  123.             strcpy(ln->data, lnbuf);
  124.  
  125.             if ((strlen(lnbuf) == 1) && (tx->start == NULL))
  126.                 tx->start = ln;
  127.  
  128.             ct++;
  129.  
  130.             /* save the header info */
  131.             if ((tx->start == NULL) && (strncmp("From:", lnbuf, 5) == 0)) {
  132.                 strcpy(lnbuf, plain_address(lnbuf + 5));
  133.                 tx->author = xmalloc(strlen(lnbuf) + 1);
  134.                 strcpy(tx->author, lnbuf);
  135.             }
  136.             if ((tx->start == NULL) &&
  137.               ((strncmp("Organisation:", lnbuf, 13) == 0) ||
  138.                (strncmp("Organization:", lnbuf, 13) == 0))) {
  139.                 lnbuf[strlen(lnbuf) - 1] = 0;
  140.                 tx->organisation = xmalloc(strlen(lnbuf + 14) + 1);
  141.                 strcpy(tx->organisation, lnbuf + 14);
  142.             }
  143.  
  144.             if ((tx->start == NULL) && (strncmp("Subject:", lnbuf, 8) == 0)) {
  145.                 lnbuf[strlen(lnbuf) - 1] = 0;
  146.                 tx->subject = xmalloc(strlen(lnbuf + 9) + 1);
  147.                 strcpy(tx->subject, lnbuf + 9);
  148.             }
  149.  
  150.             if ((tx->start == NULL) && (strncmp("Followup-To:", lnbuf, 12) == 0)) {
  151.                 lnbuf[strlen(lnbuf) - 1] = 0;
  152.                 tx->follow_up = xmalloc(strlen(lnbuf + 13) + 1);
  153.                 strcpy(tx->follow_up, lnbuf + 13);
  154.             }
  155.  
  156.         }
  157.  
  158.         ln->next = NULL;
  159.         tx->lines = ct;
  160.  
  161.         fclose(tmp_file);
  162.     }
  163.  
  164.     return(tx);
  165. }
  166.  
  167.  
  168.  
  169. /*---------------------- deallocate article memory ------------------------*/
  170. void free_article(TEXT *t)
  171. {
  172.  
  173.     LINE *l, *k;
  174.  
  175.     l = t->top;
  176.     while (l != NULL) {
  177.         k = l;
  178.         l = l->next;
  179.         free(k->data);
  180.         free(k);
  181.     }
  182.  
  183.     if (t->subject)
  184.         free(t->subject);
  185.     if (t->follow_up)
  186.         free(t->follow_up);
  187.     if (t->author)
  188.         free(t->author);
  189.     if (t->organisation)
  190.         free(t->organisation);
  191.     free(t);
  192. }
  193.  
  194.  
  195. /*---------------------------- display a line -----------------------------*/
  196. void putline(char *line, int margin)
  197. {
  198.     char buf[MAXLINE];
  199.     int length;
  200.  
  201.     strcpy(buf, line);
  202.     expand_tabs(buf, MAXLINE);
  203.     length = strlen(buf);
  204.  
  205.     if ( length > margin ) {
  206.         if ( length > margin + _columns )
  207.             printf("%-*.*s$", _columns - 1, _columns - 1, buf + margin);
  208.         else {
  209.             puts(buf + margin);
  210.         if ( length - margin < _columns )
  211.           clreol();
  212.         }
  213.     }
  214.     else
  215.         clreol();
  216. }
  217.  
  218. /*---------------------------- read an article ----------------------------*/
  219. int read_article(ACTIVE *gp, TEXT *tx, int a_ct, int of_ct)
  220. {
  221.     /*
  222.      *  This routine alloas the user to read an article
  223.      */
  224.  
  225.     LINE   *this, *tmp;   /* current thread                    */
  226.     int    exit_code;     /* why we are exiting the loop      */
  227.     char   buf[MAXLINE];
  228.     int    ch, i, maxx, margin, newmargin;
  229.  
  230.     this = tx->start;
  231.     if (this->next != NULL)
  232.         this = this->next;
  233.  
  234.     exit_code = 0;
  235.     margin = 0;
  236.     show_article(gp, tx, this, a_ct, of_ct, margin);
  237.  
  238.     while ((exit_code == 0) || (exit_code == EX_DUMMY)) {
  239.  
  240.         exit_code = 0;
  241.         ch = getch();
  242.         switch (ch) {
  243.  
  244.             case 0      :
  245.             case 0xE0   :
  246.             
  247.                 ch = getch();
  248.                 switch (ch) {
  249.  
  250.                     case F1     :
  251.                         show_help(HELP_ARTICLES);
  252.                         break;
  253.  
  254.                     case L_ARR  :
  255.                         if ( margin == 0 )
  256.                             exit_code = EX_DUMMY;
  257.                         else
  258.                             margin = max(0, margin - 4);
  259.                         break;
  260.  
  261.                     case C_L_ARR  :
  262.                         if ( margin == 0 )
  263.                             exit_code = EX_DUMMY;
  264.                         else
  265.                             margin = 0;
  266.                         break;
  267.  
  268.                     case R_ARR  :
  269.                         tmp = this;
  270.                         newmargin = margin;
  271.                         for (i = 0; i < PAGE_LENGTH-1; i++) {
  272.                             strcpy(buf, tmp->data);
  273.                             expand_tabs(buf, MAXLINE);
  274.                             newmargin = max(newmargin, strlen(buf));
  275.                             tmp = tmp->next;
  276.                             if (tmp == NULL) break;
  277.                         }
  278.                         if ( margin >= newmargin - _columns )
  279.                             exit_code = EX_DUMMY;
  280.                         else
  281.                             margin += 4;
  282.                         break;
  283.  
  284.                     case C_R_ARR  :
  285.                         tmp = this;
  286.                         newmargin = margin;
  287.                         for (i = 0; i < PAGE_LENGTH-1; i++) {
  288.                             strcpy(buf, tmp->data);
  289.                             expand_tabs(buf, MAXLINE);
  290.                             newmargin = max(newmargin, strlen(buf));
  291.                             tmp = tmp->next;
  292.                             if (tmp == NULL) break;
  293.                         }
  294.                         newmargin = ((newmargin - 1) / 4 + 1) * 4;
  295.                         if ( newmargin == margin + _columns )
  296.                             exit_code = EX_DUMMY;
  297.                         else
  298.                             margin = newmargin - _columns;
  299.                         break;
  300.  
  301.                     case UP_ARR :
  302.                         if (this->last != NULL) {
  303.                             this = this->last;
  304.                             gotoxy(1,TEXT_LINE);
  305.                             insline();
  306.                             putline(this->data, margin);
  307.                         }
  308.                         exit_code = EX_DUMMY;
  309.                         break;
  310.  
  311.                     case DN_ARR :
  312.                         if (this->next != NULL &&
  313.                             (this->index + PAGE_LENGTH < tx->lines ||
  314.                              this->index <= tx->start->index)) {
  315.                             this = this->next;
  316.                             gotoxy(1,TEXT_LINE);
  317.                             delline();
  318.                             gotoxy(1,TEXT_LINE+PAGE_LENGTH-1);
  319.  
  320.                             tmp = this;
  321.                             for (i = 0; i < PAGE_LENGTH-1; i++) {
  322.                                 tmp = tmp->next;
  323.                                 if (tmp == NULL) break;
  324.                             }
  325.  
  326.                             if (tmp)
  327.                                 putline(tmp->data, margin);
  328.                         }
  329.                         exit_code = EX_DUMMY;
  330.                         break;
  331.  
  332.                     case HOME   :
  333.                         tmp = this;
  334.                         this = tx->start;
  335.                         if (this->next != NULL)
  336.                             this = this->next;
  337.                         if (this == tmp)
  338.                             exit_code = EX_DUMMY;
  339.                         break;
  340.  
  341.                     case END    :
  342.                         tmp = this;
  343.                         this = tx->start;
  344.                         while (this->next != NULL)
  345.                             this = this->next;
  346.                         for (i = 0; i < PAGE_LENGTH-1; i++) {
  347.                             if (this->last == NULL) break;
  348.                             if (tx->start->next && this == tx->start->next)
  349.                                 break;
  350.                             this = this->last;
  351.                         }
  352.                         if (this == tmp)
  353.                             exit_code = EX_DUMMY;
  354.                         break;
  355.  
  356.                     case PGUP   :
  357.                         if ( this->last == NULL )
  358.                             exit_code = EX_DUMMY;
  359.                         else
  360.                             for (i = 0; i < PAGE_LENGTH-1; i++) {
  361.                                 if (this->last == NULL) break;
  362.                                 this = this->last;
  363.                             }
  364.                         break;
  365.  
  366.                     case PGDN   :
  367.                         if ( this->next == NULL )
  368.                             exit_code = EX_DUMMY;
  369.                         else {
  370.                             maxx = tx->lines - this->index - PAGE_LENGTH;
  371.                             maxx = min(maxx, PAGE_LENGTH - 1);
  372.                             maxx = max(maxx, tx->start->index + 1 - this->index);
  373.  
  374.                             if ( maxx <= 0 )
  375.                                 exit_code = EX_DUMMY;
  376.                             else
  377.                                 for (i = 0; i < maxx; i++) {
  378.                                     if (this->next == NULL) break;
  379.                                     this = this->next;
  380.                                 }
  381.                         }
  382.                         break;
  383.  
  384.                     default:
  385.                         exit_code = EX_DUMMY;
  386.                         break;
  387.                 }
  388.                 break;
  389.  
  390.             case 'p'    :
  391.             case 'P'    :
  392.                 post(NULL, gp->group);
  393.                 break;
  394.  
  395.             case 'f'    :
  396.             case 'F'    :
  397.                 if (!tx->follow_up || strcmp(tx->follow_up, "") == 0)
  398.                     post(tx, gp->group);
  399.                 else
  400.                     post(tx, tx->follow_up);
  401.                 break;
  402.  
  403.             case 'r'    :
  404.             case 'R'    :
  405.                 reply_to_article(tx);
  406.                 break;
  407.  
  408.             case 'm'    :
  409.             case 'M'    :
  410.                 mail_to_someone(tx);
  411.                 break;
  412.  
  413.             case 's'    :
  414.             case 'S'    :
  415.                 save_to_disk(tx);
  416.                 break;
  417.  
  418.             case 'x'    :
  419.             case 'X'    :
  420.                 rot13(tx);
  421.                 break;
  422.  
  423.             case 'h'    :
  424.             case 'H'    :
  425.                 show_help(HELP_ARTICLES);
  426.                 break;
  427.  
  428.             case TAB    :
  429.                 exit_code = EX_NEXT_UNREAD;
  430.                 break;
  431.  
  432.             case ENTER  :
  433.                 exit_code = EX_NEXT;
  434.                 break;
  435.  
  436.             case BACKSP :
  437.                 exit_code = EX_PREVIOUS;
  438.                 break;
  439.  
  440.         case '+'    :
  441.         case '/'    :
  442.                 exit_code = EX_SEARCH_FORW;
  443.         break;
  444.  
  445.         case '-'    :
  446.         case '?'    :
  447.                 exit_code = EX_SEARCH_BACKW;
  448.         break;
  449.  
  450.             case ESCAPE :
  451.                 exit_code = EX_QUIT;
  452.                 break;
  453.  
  454.             default:
  455.                 exit_code = EX_DUMMY;
  456.                 break;
  457.         };
  458.  
  459.         if (exit_code == 0)
  460.             show_article(gp, tx, this, a_ct, of_ct, margin);
  461.         else {
  462.             gotoxy(_columns - 16,2);
  463.             textbackground(LIGHTGRAY);  textcolor(BLACK);
  464.             sprintf(buf, "Line %d of %d", this->index + 1, tx->lines);
  465.             printf("%17s", buf);
  466.             textbackground(BLACK);  textcolor(LIGHTGRAY);
  467.         }
  468.     }
  469.  
  470.     return(exit_code);
  471. }
  472.  
  473.  
  474.  
  475. /*-------------------- show the list of active groups -----------------------*/
  476. void show_article(ACTIVE *gp, TEXT *tx, LINE *this, int a_ct,
  477.                   int of_ct, int margin)
  478. {
  479.     /*
  480.      *  This routine show a page of an article
  481.      */
  482.  
  483.     int    i, length;
  484.     char   buf[256], buf2[32];
  485.  
  486.     gotoxy(1,1);
  487.     textbackground(LIGHTGRAY);  textcolor(BLACK);
  488.     sprintf(buf2, "Article %d of %d", a_ct, of_ct);
  489.     printf("Group: %-*.*s  %18s", _columns - 27, _columns - 27,
  490.            gp->group, buf2);
  491.     gotoxy(1,2);
  492.     strcpy(buf, tx->author ? tx->author : "<unknown>");
  493.     if (tx->organisation) {
  494.         strcat(buf, "; ");
  495.         strcat(buf, tx->organisation);
  496.     }
  497.     sprintf(buf2, "Line %d of %d", this->index + 1, tx->lines);
  498.     printf("From: %-*.*s  %17s", _columns - 25, _columns - 25, buf, buf2);
  499.     gotoxy(1,3);
  500.     sprintf(buf2, margin ? ">%d" : "", margin);
  501.     printf("Subject: %-*.*s %4s", _columns - 14, _columns - 14,
  502.            tx->subject, buf2);
  503.     textbackground(BLACK);  textcolor(LIGHTGRAY);
  504.  
  505.     for (i = 0; i < PAGE_LENGTH; i++) {
  506.         if (this == NULL) break;
  507.         gotoxy(1, i+TEXT_LINE);
  508.         putline(this->data, margin);
  509.         this = this->next;
  510.     }
  511.  
  512.     if ( i < PAGE_LENGTH ) {
  513.         gotoxy(1, i+TEXT_LINE);
  514.         clreos();
  515.     }
  516.  
  517.     message("ESC=select thread   TAB=next unread   ENTER=next   F1=help");
  518. }
  519.  
  520.  
  521. /*-------------------------- save article --------------------------------*/
  522. void save_to_disk(TEXT *tx)
  523. {
  524.     /*
  525.      *  This routine saves an article to disk, appending if necessary
  526.      */
  527.  
  528.     FILE *tmp = NULL;
  529.     LINE *ln;
  530.     char fn[256];
  531.     int  ch;
  532.     time_t now;
  533.     struct tm *tmnow;
  534.     char timestr[64];
  535.  
  536.     lmessage("Enter filename? ");
  537.     gets(fn);
  538.  
  539.     if (access(fn, 0) == 0) {
  540.  
  541.         message("File exists - append(y/n)? ");
  542.         while (((ch = getch()) != 'y') && (ch != 'n'));
  543.         if (ch == 'y') {
  544.             if ((tmp = fopen(fn, "at")) == NULL) {
  545.                 message("*** cannot open file for appending - "
  546.                         "press any key ***");
  547.                 getch();
  548.             }
  549.         }
  550.  
  551.     } else {
  552.  
  553.         if ((tmp = fopen(fn, "wt")) == NULL) {
  554.             message("*** cannot open file for output - press any key  ***");
  555.             getch();
  556.         }
  557.  
  558.     setvbuf(tmp, iobuf, _IOFBF, IOBUFSIZE);
  559.     }
  560.  
  561.     if (tmp != NULL) {
  562.  
  563.         time(&now);
  564.     tmnow = localtime(&now);
  565.     strftime(timestr, sizeof(timestr), "%a %b %d %H:%M:%S %z %Y", tmnow);
  566.  
  567.         ln = tx->top;
  568.         while (ln != NULL && strncmp(ln->data, "Path: ", 6)) {
  569.             ln = ln->next;
  570.         }
  571.     strcpy(fn, ln->data + 6);
  572.     for ( ch = strlen(fn); fn[ch - 1] == '\n' || fn[ch - 1] == '\r'; ch--)
  573.         fn[ch - 1] = 0;
  574.     fprintf(tmp, "From %s %s\n", fn, timestr);
  575.  
  576.         ln = tx->top;
  577.         while (ln != NULL) {
  578.             fputs(ln->data, tmp);
  579.             ln = ln->next;
  580.         }
  581.  
  582.         fclose(tmp);
  583.     }
  584.  
  585. }
  586.  
  587.  
  588.  
  589.  
  590. /*-------------------------- reply to article ---------------------------*/
  591. void reply_to_article(TEXT *tx)
  592. {
  593.     /*
  594.      *  Mail reply to article
  595.      */
  596.  
  597. #ifdef INCLUDE_SIG
  598.     FILE *sig;
  599.     char sig_fn[80];
  600. #endif
  601.  
  602.     FILE *tmp;
  603.     LINE *ln;
  604.     int  ch;
  605.     char fn[256];
  606.     char buf[256];
  607.     char author[80], msg_id[MAXLINE];
  608.     char *mailer = getenv("MAILER");
  609.  
  610.     if (mailer == NULL)
  611.         mailer = "mail";
  612.  
  613.     strcpy(fn, my_stuff.news_dir);
  614.     strcat(fn, "letter");
  615.     unlink(fn);
  616.  
  617.     if ((tmp = fopen(fn, "wt")) != NULL) {
  618.  
  619.         get_his_stuff(tx, author, msg_id);
  620.  
  621.         /* add the quoted message */
  622.         message("Quote article (y/n)? ");
  623.         while (((ch = getch()) != 'y') && (ch != 'n'));
  624.  
  625.         if (ch == 'y') {
  626.             fprintf(tmp, "In article %s you write:\n", msg_id);
  627.             ln = tx->start;
  628.             while (ln != NULL) {
  629.                 fprintf(tmp, "> %s", ln->data);
  630.                 ln = ln->next;
  631.             }
  632.         }
  633.  
  634. #ifdef INCLUDE_SIG
  635.  
  636.         /* append the signature if there is one */
  637.         strcpy(sig_fn, my_stuff.home);
  638.         strcat(sig_fn, my_stuff.signature);
  639.         if ((sig= fopen(sig_fn, "rt")) != NULL) {
  640.             while (fgets(buf, 79, sig) != NULL)
  641.                 fputs(buf, tmp);
  642.             fclose(sig);
  643.         }
  644. #endif
  645.         fclose(tmp);
  646.  
  647.         sprintf(buf, my_stuff.editor, fn);
  648.         system(buf);
  649.  
  650.         sprintf(buf, "Send message to %s (y/n)? ", author);
  651.         message(buf);
  652.         while (((ch = getch()) != 'y') && (ch != 'n'));
  653.         gotoxy(1,PAGE_SIZE);
  654.         clreol();
  655.  
  656.         strcpy(msg_id, tx->subject);
  657.         eat_gunk(msg_id);
  658.  
  659.         if (ch == 'y') {
  660.             sprintf(buf, "%s -s \"Re: %s\" %s <%s >nul 2>&1",
  661.                     mailer, msg_id, author, fn);
  662.             system(buf);
  663.         }
  664.  
  665.         /* unlink(fn); /* keep it around like rn */
  666.  
  667.     } else {
  668.         message("*** cannot open temp file - press any key ***");
  669.         getch();
  670.     }
  671.  
  672. }
  673.  
  674.  
  675.  
  676.  
  677. /*-------------------------- reply to article ---------------------------*/
  678. void mail_to_someone(TEXT *tx)
  679. {
  680.     /*
  681.      *  Mail this article to someone
  682.      */
  683.  
  684.  
  685.     FILE *tmp;
  686.     LINE *ln;
  687.     int  ch;
  688.     char fn[256];
  689.     char buf[256], who[80];
  690.     char *subj = tx->subject ? tx->subject : "No subject";
  691.  
  692.     sprintf(fn, "%s\\snews.tmp", my_stuff.temp_name);
  693.  
  694.     if ((tmp= fopen(fn, "wt")) != NULL) {
  695.  
  696.         lmessage("Mail this article to? ");
  697.         gets(who);
  698.  
  699.     fprintf(tmp, "Resent-From: %s@%s (%s)\n",
  700.         my_stuff.user, my_stuff.my_domain, my_stuff.my_name);
  701.     fprintf(tmp, "Resent-To: %s\n", who);
  702.  
  703.         ln = tx->top;
  704.         while (ln != NULL) {
  705.             fputs(ln->data, tmp);
  706.             ln = ln->next;
  707.         }
  708.  
  709.         fclose(tmp);
  710.  
  711.         message("Edit outgoing message (y/n)? ");
  712.         while (((ch = getch()) != 'y') && (ch != 'n'));
  713.  
  714.         if (ch == 'y') {
  715.       sprintf(buf, my_stuff.editor, fn);
  716.       system(buf);
  717.     }
  718.  
  719.         sprintf(buf, "Mail article to %s (y/n)? ", who);
  720.         message(buf);
  721.         while (((ch = getch()) != 'y') && (ch != 'n'));
  722.         if (ch == 'y') {
  723.             sprintf(buf, "rmail -t <%s 2>nul", fn);
  724.             system(buf);
  725.         }
  726.  
  727.         unlink(fn);
  728.  
  729.     } else {
  730.         message("*** cannot open temp file - press any key ***");
  731.         getch();
  732.     }
  733.  
  734. }
  735.  
  736.  
  737.  
  738. /*----------------------- get stuf off article header --------------------*/
  739. void get_his_stuff(TEXT *tx, char *author, char *msg_id)
  740. {
  741.     /*
  742.      *  Retrieve the author and msg_id from the article
  743.      */
  744.  
  745.     LINE *ln;
  746.     char *p;
  747.     char buf[MAXLINE];
  748.     char *null_name = {" ** none ** "};
  749.  
  750.     strcpy(author, null_name);
  751.     strcpy(msg_id, " <none> ");
  752.  
  753.     ln = tx->top;
  754.     while (ln != NULL) {
  755.         if (strlen(ln->data) < 2) break;
  756.  
  757.         strcpy(buf, ln->data);
  758.         p = strtok(buf, " :\n\r");
  759.         p = strtok(NULL, " :\n\r");
  760.         if (strnicmp(ln->data, "Message-ID:", 11) == 0) {
  761.             strcpy(msg_id, p);
  762.         }
  763.  
  764.         if ((strnicmp(ln->data, "From:", 5) == 0) &&
  765.           (strcmp(author, null_name) == 0)) {
  766.             strcpy(author, plain_address(ln->data+5));
  767.         }
  768.  
  769.         if (strnicmp(ln->data, "Reply-to:", 9) == 0) {
  770.             strcpy(author, plain_address(ln->data+9));
  771.         }
  772.  
  773.         ln = ln->next;
  774.     }
  775. }
  776.  
  777.  
  778. /*--------------------------- rot 13 the article ------------------------*/
  779. void rot13(TEXT *tx)
  780. {
  781.     LINE *ln;
  782.     int  i, c;
  783.  
  784.  
  785.     ln = tx->start;
  786.  
  787.     while (ln != NULL) {
  788.         for (i = 0; i < strlen(ln->data); i++) {
  789.             c = *((ln->data)+i);
  790.             if ((c >= 'A') && (c <= 'Z')) {
  791.                 *((ln->data)+i) = (((c-'A') + 13) % 26) + 'A';
  792.             } else {
  793.                 if ((c >= 'a') && (c <= 'z')) {
  794.                     *((ln->data)+i) = (((c-'a') + 13) % 26) + 'a';
  795.                 }
  796.             }
  797.         }
  798.         ln = ln->next;
  799.     }
  800. }
  801.  
  802.  
  803. /*--------------------------- expand the tabs ----------------------------*/
  804. void expand_tabs(char *buf, int max_len)
  805. {
  806.     int  l, k;
  807.     char tmp[MAXLINE], *p, *t;
  808.  
  809.     p = buf;
  810.     t = &tmp[0];
  811.     l = 0;
  812.  
  813.     while ((*p != '\x00') && (l < max_len)) {
  814.         if (*p != '\x09') {
  815.             *t = *p;
  816.             t++;
  817.             p++;
  818.             l++;
  819.         } else {
  820.             p++;
  821.             k = ((l / 8) + 1) * 8;
  822.             for ( ; l < k ; l++) {
  823.                 *t = ' ';
  824.                 t++;
  825.                 if (l >= max_len) break;
  826.             }
  827.         }
  828.     }
  829.  
  830.     *t = '\x00';
  831.     strcpy(buf, tmp);
  832.     l = strlen(buf);
  833.     
  834.     if (buf[l - 1] == '\n')
  835.       buf[l - 1] = 0;
  836. }
  837.