home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / bbs_ra / msgq160s.arj / MAKEMSG.C < prev    next >
Text File  |  1991-07-28  |  22KB  |  968 lines

  1. /*
  2.  * MAKEMSG.C - Create messages
  3.  *
  4.  * Msged/Q message editor for QuickBBS  Copyright 1990 by P.J. Muller
  5.  *
  6.  */
  7.  
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <ctype.h>
  11. #include <stdlib.h>
  12. #include <time.h>
  13.  
  14. #include "msged.h"
  15. #include "screen.h"
  16. #include "qmsgbase.h"
  17.  
  18. #include <assert.h>
  19.  
  20. #define TEXTLEN 200
  21. #define INPLEN  60
  22.  
  23. static void show_attrib(void);
  24. static int change_from(void);
  25. static int change_attrib(void);
  26. static int change_to(void);
  27. static int change_dest(void);
  28. static int change_orig(void);
  29. static int change_subject(void);
  30. static void doquote(BOOLEAN otherarea);
  31.  
  32. /* static char work[TEXTLEN]; */
  33.  
  34. static int do_lookup = FALSE;
  35.  
  36. static void createmsg(void);
  37.  
  38. /*
  39.  * Initialise the global message fields
  40.  */
  41.  
  42. static void createmsg()
  43. {
  44.   memset(&(message.header), 0, sizeof(message.header));
  45.  
  46.   message.header.msgnum = newmsgnum();
  47.  
  48.   strncpy(message.header.from, username, sizeof(message.header.from));
  49.   timestr(message.header.posttime,message.header.postdate);
  50.   message.header.board = CurBoard;
  51.   memset(&message.to,0, sizeof(message.to));
  52.   memset(&message.from,0,sizeof(message.from));
  53. #ifdef EIDS
  54.   memset(&message.eid,0,sizeof(message.eid));
  55. #endif
  56.   memset(&message.msgid,0,sizeof(message.msgid));
  57.   message.header.origzone = message.header.destzone = thisnode[CurAKA].zone;
  58.   message.header.up = 0;
  59.   message.header.reply = 0;
  60.   clear_attributes(&message.header);
  61.   clearbuffer(&msgbuf);
  62.   message.to.zone = thisnode[CurAKA].zone;
  63.   message.to.domain = NULL;
  64.   message.from = thisnode[CurAKA];
  65.   if (message.from.domain != NULL)
  66.     message.from.domain = strdup(message.from.domain);
  67.   /* message.from.domain = NULL; */
  68. } /* createmsg */
  69.  
  70. void newmsg()
  71. {
  72.   int keep;
  73.  
  74.   keep = curmsg(CurBoard);
  75.  
  76.   createmsg();
  77.   editheader();
  78.   switch (editmsg()) {
  79.     case SAVE:
  80.       save(message);
  81.       break;
  82.  
  83.     case ABORT:
  84.       break;
  85.   } /* switch */
  86.  
  87.   setcur(CurBoard,keep);
  88. } /* newmsg */
  89.  
  90. void reply()
  91. {
  92.   char msgto[sizeof(message.header.to)];
  93.   char subject[sizeof(message.header.subj)];
  94.   int link;
  95.   ADDRESS tmp;
  96.   MSGHEADER hlink;
  97.   int t = curmsg(CurBoard);
  98.  
  99.   memcpy(msgto, message.header.from, sizeof(message.header.to));
  100.   memcpy(subject, message.header.subj, sizeof(message.header.subj));
  101.   tmp = message.from;
  102.   link = message.header.msgnum;
  103.  
  104.   createmsg();
  105.  
  106.   /* do_lookup = FALSE; */
  107.   memcpy(message.header.to, msgto, sizeof(message.header.to));
  108.   memcpy(message.header.subj, subject, sizeof(message.header.subj));
  109.   message.to = tmp;
  110.  
  111.   editheader();
  112.  
  113.   message.header.reply = link;
  114.  
  115.   switch (editmsg()) {
  116.  
  117.     case SAVE:
  118.       readheader(link, &hlink);
  119.       hlink.up = message.header.msgnum;
  120.       writeheader(&hlink);
  121.       save(message);
  122.       break;
  123.  
  124.     case ABORT:
  125.       break;
  126.   } /* switch */
  127.  
  128.   setcur(CurBoard,t);
  129. } /* reply */
  130.  
  131. void quote()
  132. {
  133.   doquote(FALSE);
  134. }
  135.  
  136. void quotemv()
  137. {
  138.   doquote(TRUE);
  139. }
  140.  
  141. /*
  142.  * Expand a quote string, return a malloc'ed pointer
  143.  *   '*' is replaced with first initial
  144.  *   '^' is replaced with last initial
  145.  *   '&' is replaced with all the initials
  146.  */
  147.  
  148. static char *expandquote(char *qs, char *name)
  149. {
  150.   char init[11];        /* initials */
  151.   char exp[TEXTLEN];        /* expanded quote string */
  152.   char *tok, *t, *s, *i = init;
  153.  
  154. /* first parse the name */
  155.  
  156.   t = s = strdup(name);
  157.   if ((t == NULL) || (qs == NULL)) return(NULL);
  158.  
  159.   do {
  160.     tok = strtok(s,"._ \t\n\r");
  161.     s = NULL;            /* for strtok(), keeps internal pointer */
  162.     if ((tok != NULL) && (isalpha(*tok)))
  163.       *i++ = *tok;
  164.   } while ((tok != NULL) && (i-init < 11));
  165.   *i = EOS;
  166.   ptrfree(t);
  167.  
  168. /* now expand the quote string */
  169.  
  170.   t = exp;
  171.   s = qs;
  172.   while ((*s != EOS) && (t-exp < TEXTLEN)) {
  173.     switch (*s) {
  174.       case '*':
  175.     *t++ = init[0];        /* first initial */
  176.     s++;  break;
  177.  
  178.       case '^':
  179.     if (i-init > 1)        /* more than one initial */
  180.       *t++ = *(i-1);    /* last initial */
  181.     else
  182.       *t++ = '.';        /* else add a period */
  183.     s++;  break;
  184.  
  185.       case '&':            /* all initials */
  186.     for (tok = init;  *tok != EOS;  tok++)
  187.       *t++ = *tok;
  188.     s++;  break;
  189.  
  190.       default:
  191.     *t++ = *s++;
  192.     } /* switch */
  193.   } /* while */
  194.   *t = EOS;
  195.  
  196.   return(strdup(exp));
  197. } /* expandquote */
  198.  
  199. /*
  200.  * Expand the attribution line
  201.  * Return a strdup'ed string
  202.  */
  203.  
  204. static char *expandattrib(char *s, char *from, char *node, char *to, char *date)
  205. {
  206.   char exp[TEXTLEN];
  207.   char *t = exp;
  208.  
  209.   while ((*s != EOS) && (t-exp < TEXTLEN-2)) {
  210.     if (*s == '$') {
  211.       char *ts;
  212.  
  213.       switch (*(++s)) {
  214.     case 'd':
  215.       ts = date;    break;
  216.     case 'f':
  217.       ts = from;    break;
  218.     case 't':
  219.       ts = to;    break;
  220.     case 'a':
  221.       ts = node;    break;
  222.     case '$':
  223.       ts = "$";    break;
  224.     default:
  225.       ts = "";
  226.       } /* switch */
  227.  
  228.       while ((*ts != EOS) && (t-exp < TEXTLEN-2))
  229.     *t++ = *ts++;
  230.       s++;
  231.     }
  232. #ifdef ZAPTHISOUT    /* can finish this later */
  233.     else if (*s == '%') {
  234.       switch (*(++s)) {
  235.     case 'f':
  236.       ts = from;    break;
  237.     case 't':
  238.       ts = to;    break;
  239.     case 'a':
  240.       ts = node;    break;
  241.     case 'w':
  242.       ts = jweekday;    break;
  243.     case 'd':
  244.       ts = jmonday;    break;
  245.     case 'm':
  246.       ts = jmonth;    break;
  247.     case 'y':
  248.       ts = jyear;    break;
  249.     case 'h':
  250.       ts = jtime;    break;
  251.     case '%':
  252.       ts = "%";    break;
  253.     default:
  254.       ts = "";
  255.       } /* switch */
  256.  
  257.       while ((*ts != EOS) && (t-exp < TEXTLEN-2))
  258.     *t++ = *ts++;
  259.       s++;
  260.     }
  261. #endif
  262.     else *t++ = *s++;
  263.   } /* while */
  264.   *t++ = '\n';
  265.   *t = EOS;
  266.  
  267.   return(strdup(exp));
  268. } /* expandattrib */
  269.  
  270. static char *scaninternet(BUFFER *msgbuf, MSG *message)
  271. {
  272.   LINE *t;
  273.   static char buffer[128];
  274.   enum {IN_HEAD, IN_TEXT};
  275.   int scan;
  276.  
  277.   if (arealist[area].netmail && (stricmp(message->header.from, "uucp") == 0)) {
  278.     /* message is from the uucp gateway */;
  279.   } else if (arealist[area].echomail) {
  280.     t = msgbuf->first;
  281.     while (t != NULL) {
  282.       if (strncmp(t->text, "\01UFGATE ", 8) == 0)
  283.     break;  /* found marker */
  284.       t = t->next;
  285.     }
  286.     if (t == NULL) return NULL;    /* not found */
  287.   } else return NULL;        /* not a recognized message */
  288.  
  289.   t = msgbuf->first;
  290.   scan = IN_HEAD;
  291.   buffer[0] = EOS;
  292.   while (t != NULL) {
  293.     if (scan == IN_HEAD) {
  294.       if (sscanf(t->text, "From: %127s", buffer) == 1)
  295.     break;
  296.       if ((t->text == NULL) || (*t->text == '\n'))    /* empty line */
  297.     scan = IN_TEXT;
  298.     } /* might add heuristics to recognize signatures */
  299.     t = t->next;
  300.   } /* while */
  301.   if (buffer[0] == EOS) return NULL;
  302.  
  303.   return buffer;
  304.  
  305. } /* scaninternet */
  306.  
  307. static void doquote(BOOLEAN otherarea)    /* new version */
  308. {
  309.   char origfr[TEXTLEN];        /* $f: quoted message from person */
  310.   char origto[TEXTLEN];        /* $t: quoted message to person */
  311.   char repdate[40];        /* $d: date of original message */
  312.   ADDRESS fromaddr;
  313.   int i;
  314.   char *qs;
  315.   LINE *t;
  316.   int link, oldup;
  317.   int curarea = area, newarea;
  318.   int tm;
  319.   char *internet = NULL;
  320.  
  321.   if (areas < 2) otherarea = FALSE;    /* avoid silly stuff */
  322.  
  323.   if (otherarea) {
  324.     newarea = selectarea("SELECT DESTINATION AREA");
  325.     area = curarea;        /* selectarea() might have changed it */
  326.     if (newarea == -1)
  327.       return;
  328.   } else newarea = area;
  329.  
  330.   memset(&message.to,0, sizeof(message.to));
  331.   memset(&message.from,0,sizeof(message.from));
  332.   message.to.zone = message.from.zone = thisnode[arealist[newarea].aka].zone;
  333.  
  334.   i = rm;
  335.   rm = quoteright;            /* right margin for quoting */
  336.   readmsg(&message, message.header.msgnum);
  337.   rm = i;
  338.  
  339.   internet = scaninternet(&msgbuf, &message);    /* look for internet address */
  340.  
  341.   if (otherarea) {
  342.     area = newarea;
  343.     link = oldup = 0;
  344.     message.header.board = CurBoard;
  345.   } else {
  346.     link = message.header.msgnum;
  347.     oldup = message.header.up;
  348.   } /* else */
  349.  
  350.   tm = curmsg(CurBoard);
  351.  
  352.   strcpy(origfr,message.header.from);    /* original from & to */
  353.   strcpy(origto,message.header.to);
  354.                     /* original post date */
  355.   strncpy(repdate, timedate(message.header.posttime, message.header.postdate, NO), sizeof(repdate));
  356.   if (internet != NULL) {
  357.     strncpy(message.header.to, "UUCP", sizeof(message.header.to));
  358.     memset(&message.to, 0, sizeof(message.to));        /* force lookup */
  359.   } else {
  360.     memcpy(message.header.to, message.header.from,sizeof(message.header.to));
  361.     message.to = message.from;
  362.   }
  363.  
  364.   fromaddr = message.from;        /* $a for netmail */
  365.   message.from = thisnode[arealist[newarea].aka];
  366.   if (message.from.domain != NULL)
  367.     message.from.domain = strdup(message.from.domain);
  368.   /* message.from.domain = NULL; */
  369. #ifdef EIDS
  370.   memset(&message.eid,0,sizeof(message.eid));
  371. #endif
  372.   memset(&message.msgid,0,sizeof(message.msgid));
  373.  
  374.   message.header.msgnum = newmsgnum();    /* new message number */
  375.  
  376.   memset(message.header.from,0,sizeof(message.header.from));
  377.   strncpy(message.header.from, username, sizeof(message.header.from));
  378.   timestr(message.header.posttime,message.header.postdate);
  379.  
  380.   clear_attributes(&message.header);    /* reset the bits */
  381.  
  382.   message.header.reply = link;
  383.   message.header.up = oldup;
  384.  
  385. /* now scan the text and insert quote marks  */
  386.  
  387.   t = msgbuf.first;
  388.   /* memset(work,0,sizeof work); */
  389.   qs = expandquote(quotestr, origfr);
  390.   while (t != NULL) {
  391.  
  392.     if (!shownotes && (*t->text == '\01')) { /* remove current hidden lines */
  393.       LINE *temp = t;
  394.       t = t->next;              /* step to next line */
  395.       deleteline(&msgbuf,temp); /* and delete current line */
  396.       continue;
  397.     } /* if */
  398.  
  399.     t->quote = 1;
  400.     if (t->text != NULL) {
  401.       char line[TEXTLEN];        /* temporary quoted line */
  402.       sprintf(line, " %s%s",qs,t->text);  /* could also remove old quotes */
  403.  
  404.       if (strchr(t->text, '\n') == NULL)
  405.     strcat(line, "\n");
  406.  
  407.       ptrfree(t->text);
  408.       t->text = strdup(line);
  409.       assert(t->text);
  410.     } /* if */
  411.     t = t->next;
  412.   } /* while */
  413.   ptrfree(qs);                /* quote string */
  414.  
  415.   if ((attribline != NULL) && (*attribline != EOS)) {
  416.     char origin[30];        /* $a: node address of quoted person */
  417.     char *attr;
  418.  
  419.     *origin = EOS;        /* empty */
  420.     if (fromaddr.net != 0) {
  421.       strcpy(origin,formaddr(fromaddr,Z_O|N_A|P_O|D_O));
  422.     } /* if */
  423.  
  424.     attr = expandattrib(attribline,origfr,origin,origto,repdate);
  425.  
  426.     /* note: attr may be longer than screen line -- should be ok */
  427.  
  428.     prependline("\n");
  429.     prependline(attr);
  430.     ptrfree(attr);
  431.   } /* if */
  432.  
  433.   if (otherarea) {
  434.     char org[TEXTLEN];
  435.  
  436.     sprintf(org, " * Moved from %s (%s) by %s\n",
  437.         arealist[curarea].tag,arealist[curarea].description,username);
  438.     prependline("\n");
  439.     prependline(org);
  440.   } /* if */
  441.  
  442.   if (internet != NULL) {
  443.     char interaddr[140];
  444.  
  445.     sprintf(interaddr, "To: %s\n", internet);
  446.     prependline("\n");
  447.     prependline(interaddr);
  448.   }
  449.  
  450.   editheader();
  451.  
  452.   if ((editmsg()) == SAVE) {
  453.     if (link != 0) {
  454.       MSGHEADER hlink;
  455.  
  456.       readheader(link, &hlink);
  457.       hlink.up = message.header.msgnum;
  458.       writeheader(&hlink);
  459.     } /* if */
  460.     save(message);
  461.   } /* if */                /* else ABORT */
  462.  
  463.   setcur(CurBoard,tm);
  464.   area = curarea;
  465.  
  466. } /* doquote */
  467.  
  468. void change()
  469. {
  470.   LINE *t;
  471.  
  472.   readmsg(&message, message.header.msgnum);
  473.  
  474.   t = msgbuf.first;
  475.   while (t != NULL) {
  476.     if (!shownotes && (*t->text == '\01')) { /* remove current hidden lines */
  477.       LINE *temp = t;
  478.       t = t->next;              /* step to next line */
  479.       deleteline(&msgbuf,temp); /* and delete current line */
  480.     } else {
  481.       t = t->next;
  482.     } /* else */
  483.   } /* while */
  484.  
  485. #ifdef EIDS
  486.   memset(&message.eid,0,sizeof(message.eid));
  487. #endif
  488.   memset(&message.msgid,0,sizeof(message.msgid));
  489.  
  490.   timestr(message.header.posttime,message.header.postdate);
  491.  
  492.   /* probably shouldn't clear 'is_rcvd' bit because of MSGTOIDX.BBS */
  493.   message.header.bits.is_rcvd = message.header.bits.is_sent = 0;
  494.   if (arealist[area].echomail)
  495.     message.header.bits.is_echotr = 1;
  496.   if (arealist[area].netmail)
  497.     message.header.bits.is_transit = 1;
  498.  
  499.   editheader();
  500.  
  501.   if (editmsg() == SAVE)
  502.     writemsg(&message);
  503.  
  504. } /* change */
  505.  
  506. /*
  507.  * Show attributes that can be changed
  508.  */
  509.  
  510. void show_attrib()
  511. {
  512.   gotoxy(9, 5);
  513.   set_color(co_normal);
  514.   if (message.header.bits.is_priv)
  515.       set_color(co_hilite);
  516.   bputs("Private ");
  517.   set_color(co_normal);
  518.   if (message.header.bits.is_crash)
  519.       set_color(co_hilite);
  520.   bputs("Crash ");
  521.   set_color(co_normal);
  522.   if (message.header.bits.is_file)
  523.       set_color(co_hilite);
  524.   bputs("Attach ");
  525.   set_color(co_normal);
  526.   if (message.header.bits.is_kill)
  527.       set_color(co_hilite);
  528.   bputs("Kill/sent ");
  529.   set_color(co_normal);
  530.   gotoxy(1, 5);
  531.   bputs("Attrib: ");
  532. } /* show_attrib */
  533.  
  534. int change_attrib()
  535. {
  536.   int ch;
  537.  
  538.   set_color(co_hilite);
  539.   gotoxy(1, 5);
  540.   video_update();
  541.   bputs("Attrib: ");
  542.   set_color(co_normal);
  543.   ch = getkey();
  544.   if (toupper((ch & 0xff)) == 'P')
  545.       message.header.bits.is_priv ^= 1;
  546.   if (toupper((ch & 0xff)) == 'C')
  547.       message.header.bits.is_crash ^= 1;
  548.   if (toupper((ch & 0xff)) == 'A')
  549.       message.header.bits.is_file ^= 1;
  550.   if (toupper((ch & 0xff)) == 'K')
  551.       message.header.bits.is_kill ^= 1;
  552.   return (ch);
  553. } /* change_attrib */
  554.  
  555. int change_from()
  556. {
  557.   int ch;
  558.  
  559.   set_color(co_hilite);
  560.   gotoxy(1, 2);
  561.   bputs("From:   ");
  562.   set_color(co_normal);
  563.   clreol();
  564.   ch = bgets(message.header.from, sizeof(message.header.from));
  565.   gotoxy(1, 2);
  566.   set_color(co_info);
  567.   bputs("From:   ");
  568.   return (ch);
  569. } /* change_from */
  570.  
  571. int change_orig()
  572. {
  573.   int     ch;
  574.   char    tmp[INPLEN];
  575.  
  576.   gotoxy(9 + strlen(message.header.from), 2);
  577.   set_color(co_hilite);
  578.   bputs(" of ");
  579.   set_color(co_normal);
  580.   strcpy(tmp,formaddr(message.from,Z_A|N_A|P_A|D_O));
  581.  
  582.   ch = bgets(tmp, INPLEN);
  583.   message.from = parsenode(tmp);
  584.   gotoxy(9 + strlen(message.header.from), 2);
  585.   set_color(co_info);
  586.   bputs(" of ");
  587.   return (ch);
  588. } /* change_orig */
  589.  
  590. int change_to()
  591. {
  592.   int ch;
  593.   char tmp[sizeof(message.header.to)];
  594.  
  595.   set_color(co_hilite);
  596.   gotoxy(1, 3);
  597.   bputs("To:     ");
  598.   set_color(co_normal);
  599.   clreol();
  600.   memset(tmp,0,sizeof(tmp));
  601.   strncpy(tmp,message.header.to,sizeof(message.header.to));
  602.   ch = bgets(message.header.to, sizeof(message.header.to));
  603.   do_lookup = (strcmpl(tmp,message.header.to) != 0) ||
  604.           (message.to.node == 0);
  605.   gotoxy(1, 3);
  606.   set_color(co_info);
  607.   bputs("To:     ");
  608.   if ((arealist[area].netmail) && do_lookup) {
  609.     message.to = lookup(message.header.to,fidolist);
  610.     if ((message.to.node == 0) && (userlist != NULL))
  611.       message.to = lookup(message.header.to,userlist);
  612.   } /* if */
  613.   return (ch);
  614. } /* change_to */
  615.  
  616. int change_dest()
  617. {
  618.   int ch;
  619.   char tmp[INPLEN];
  620.  
  621.   gotoxy(9 + strlen(message.header.to), 3);
  622.   set_color(co_hilite);
  623.   bputs(" of ");
  624.   set_color(co_normal);
  625.   strcpy(tmp,formaddr(message.to,Z_A|N_A|P_A|D_O));
  626.  
  627.   if (message.to.node == 0)
  628.     memset(tmp,0,sizeof(tmp));
  629.  
  630.   ch = bgets(tmp, INPLEN);
  631.   message.to = parsenode(tmp);
  632.   if ((message.to.domain != NULL) && (message.from.domain == NULL)) {
  633.     if (thisnode[CurAKA].domain == NULL)
  634.       message.from.domain = strdup("Fidonet");
  635.     else
  636.       message.from.domain = strdup(thisnode[CurAKA].domain);
  637.     assert(message.from.domain);
  638.   } /* if */
  639.  
  640.   gotoxy(9 + strlen(message.header.to), 3);
  641.   set_color(co_info);
  642.   bputs(" of ");
  643.   return (ch);
  644. } /* change_dest */
  645.  
  646. static BOOLEAN exist(char *n)
  647. {
  648.   FILE *f;
  649.  
  650.   if ((f = fopen(n,"r")) != NULL) {
  651.     fclose(f);
  652.     return TRUE;
  653.   } else return FALSE;
  654.  
  655. } /* exist */
  656.  
  657. int change_subject()
  658. {
  659.   int ch;
  660.  
  661.   set_color(co_hilite);
  662.   gotoxy(1, 4);
  663.   if (message.header.bits.is_file)
  664.     bputs("Files:  ");
  665.   else
  666.     bputs("Subj:   ");
  667.   set_color(co_normal);
  668.   ch = bgets(message.header.subj, sizeof(message.header.subj));
  669.  
  670.   if (strlen(message.header.subj) > 3)         /* check if filename */
  671.     if ((message.header.subj[1] == ':') && (isalpha(message.header.subj[0]))
  672.     && (message.header.subj[2] == '\\')) {
  673.       message.header.bits.is_file = 1;        /* set file attach bit */
  674.       show_attrib();
  675.     } /* if */
  676.   gotoxy(1, 4);
  677.   set_color(co_info);
  678.   if (message.header.bits.is_file) {
  679.     char s[74], *f;
  680.  
  681.     bputs("Files:  ");
  682.     strcpy(s,message.header.subj);
  683.     for (f = strtok(s," \t");  f != NULL;  f = strtok(NULL," \t")) {
  684.       if (exist(f)) {
  685.     set_color(co_info);
  686.       } else {
  687.     set_color(co_warn);
  688.     beep(0,0);
  689.       } /* else */
  690.       bprintf("%s ", f);
  691.     } /* for */
  692.     set_color(co_info);
  693.  
  694.   } else
  695.     bputs("Subj:   ");
  696.  
  697.   return(ch);
  698. } /* change_subject */
  699.  
  700. void editheader()
  701. {
  702.   int field = 2;
  703.   int ch = 0;
  704.  
  705.   set_color(co_info);
  706.   cls();
  707.   gotoxy(1, 2);
  708.   bputs("From:   ");
  709.   bputs(message.header.from);
  710.   gotoxy(1, 3);
  711.   bputs("To:     ");
  712.   gotoxy(1, 4);
  713.   if (message.header.bits.is_file)
  714.     bputs("Files:  ");
  715.   else
  716.     bputs("Subj:   ");
  717.   show_attrib();
  718.   gotoxy(1, 6);
  719.   bputs(
  720.     "______________________________________________________________________________"
  721.       );
  722.   gotoxy(1, 6);
  723.   set_color(co_hilite);
  724.   bputs(arealist[area].description);
  725.   set_color(co_normal);
  726.  
  727.   while (ch != DONE) {
  728.     switch (field) {
  729.       case 0:
  730.     ch = change_from();
  731.     break;
  732.       case 1:
  733.     ch = change_orig();
  734.     break;
  735.       case 2:
  736.     ch = change_to();
  737.     break;
  738.       case 3:
  739.     ch = change_dest();
  740.     break;
  741.       case 4:
  742.     ch = change_subject();
  743.     break;
  744.       case 5:
  745.     ch = change_attrib();
  746.     show_attrib();
  747.     break;
  748.     } /* switch */
  749.  
  750.     if (ch == UP) {
  751.       field--;
  752.  
  753.       if (field < 0)
  754.     field = 5;
  755.  
  756.       if ((field == 3) && !arealist[area].netmail)
  757.     field = 2;
  758.  
  759.       if ((field == 1) && !arealist[area].netmail)
  760.     field = 0;
  761.     } /* if */
  762.  
  763.     if ((ch == DOWN) || (ch == ENTER)) {
  764.       if ((field == 5) && (ch == ENTER))
  765.     break;
  766.  
  767.       field++;
  768.  
  769.       if (field > 5)
  770.     field = 0;
  771.  
  772.       if ((field == 3) && !arealist[area].netmail)
  773.     field = 4;
  774.  
  775.       if ((field == 1) && !arealist[area].netmail)
  776.     field = 2;
  777.  
  778.       continue;
  779.     } /* if */
  780.   } /* while */
  781.  
  782. #ifdef EIDS
  783.   memset(&message.eid,0,sizeof(message.eid));
  784. #endif
  785.   memset(&message.msgid,0,sizeof(message.msgid));
  786.  
  787.   showheader(message);
  788. } /* editheader */
  789.  
  790. /*
  791.  * Update the time and date fields of the message header
  792.  */
  793.  
  794. void timestr(char *tim, char *dat)
  795. {
  796.   struct tm *t;
  797.   time_t  ltime;
  798.  
  799.   time(<ime);
  800.   t = localtime(<ime);
  801.   sprintf(tim, "%02d:%02d", t->tm_hour, t->tm_min);
  802.   sprintf(dat, "%02d-%02d-%02d", t->tm_mon+1, t->tm_mday, t->tm_year);
  803. } /* timestr */
  804.  
  805. void clear_attributes(MSGHEADER * h)
  806. {
  807.   h->bits = arealist[area].msgbits;    /* set defaults */
  808.  
  809.   if (arealist[area].netmail) {        /* now modify the bits */
  810.     h->bits.is_netm    = 1;
  811.     h->bits.is_transit    = 1;
  812.     h->bits.is_echotr    = 0;
  813.     h->bits.is_local    = 1;        /* 28/09/90 */
  814.   } else {
  815.     h->bits.is_crash    = 0;
  816.     h->bits.is_priv    = 0;
  817.     h->bits.is_kill    = 0;
  818.     h->bits.is_netm    = 0;
  819.     h->bits.is_transit    = 0;
  820.     if (arealist[area].echomail)
  821.       h->bits.is_echotr    = 1;
  822.     else
  823.       h->bits.is_echotr    = 0;
  824.   } /* else */
  825.  
  826. } /* clear_attributes */
  827.  
  828. void save(MSG message)
  829. {
  830.   LINE *l;
  831.   char **name = NULL;
  832.   ADDRESS *address = NULL;
  833.   char buf[TEXTLEN];
  834.   char o[TEXTLEN];
  835.   char *a = NULL;
  836.   char *s = NULL;
  837.   int  i = 0;
  838.   int  j = 0;
  839.  
  840.   /* write the original message */
  841.  
  842.   writemsg(&message);
  843.   setcur(CurBoard,message.header.msgnum);
  844.  
  845.   /* initialize stuff */
  846.  
  847.   memset(buf,0,TEXTLEN);
  848.   memset(o,0,TEXTLEN);
  849.  
  850.   /* check for a cc: */
  851.  
  852.   l = msgbuf.first;
  853.   while ((l != NULL) && (*l->text == '\01'))
  854.     l = l->next;
  855.   if (l == NULL)
  856.     return;
  857.  
  858.   s = l->text;
  859.   while (isspace(*s))
  860.     s++;
  861.  
  862.   /* if no cc:, then return */
  863.  
  864.   strcpy(buf,s);
  865.   strlwr(buf);
  866.  
  867.   if (strncmp(buf,"cc:",3) != 0)
  868.     return;
  869.  
  870.   name = (char **) calloc(1, sizeof(char **));
  871.   address = (ADDRESS *) calloc(1,sizeof(ADDRESS));
  872.  
  873.   sprintf(o," * Original to %s @ %s\n",message.header.to,
  874.       formaddr(message.to,Z_A|N_A|P_O|D_O));
  875.  
  876.   /* build the address list */
  877.  
  878.   while ((l != NULL) && (l->text != NULL)) {
  879.       if (tolower(*s) == 'c') s = strchr(s,' ');
  880.  
  881.       while (isspace(*s))
  882.           s++;
  883.  
  884.       memset(buf,0,TEXTLEN);
  885.       strcpy(buf,s);
  886.  
  887.       a = strchr(buf,'\n');
  888.       if (a != NULL)
  889.           *a = '\0';
  890.  
  891.       a = strrchr(buf,' ');
  892.  
  893.       if (a == NULL)
  894.           break;
  895.       else
  896.           a++;
  897.  
  898.       if (isdigit(*a)) {
  899.           address[i] = parsenode(a);
  900.           *(a - 1) = '\0';
  901.       }
  902.       else {
  903.           address[i] = lookup(buf,fidolist);
  904.           if ((address[i].net == 0) && (userlist != NULL))
  905.               address[i] = lookup(buf,userlist);
  906.       }
  907.  
  908.       name[i] = strdup(buf);
  909.  
  910.       i++;
  911.  
  912.       if ((address = (ADDRESS *) realloc(address,sizeof(ADDRESS) * (i+1))) == NULL)
  913.           return;
  914.  
  915.       if ((name = (char **) realloc(name,sizeof(char **) * (i+1))) == NULL) {
  916.           ptrfree(address);
  917.           return;
  918.       }
  919.       l = l->next;
  920.       s = l->text;
  921.   }
  922.  
  923.   if ((l = (LINE *) calloc(1,sizeof(LINE))) == NULL) {
  924.       ptrfree(name);
  925.       ptrfree(address);
  926.       return;
  927.   }
  928.  
  929.   l->text = strdup("\n");
  930.   assert(l->text);
  931.   l->next = msgbuf.first;
  932.   l->next->prev = l;
  933.   l->prev = NULL;
  934.   msgbuf.first = l;
  935.  
  936.   if ((l = (LINE *) calloc(1,sizeof(LINE))) == NULL) {
  937.       ptrfree(name);
  938.       ptrfree(address);
  939.       return;
  940.   }
  941.  
  942.   l->text = strdup(o);
  943.   assert(l->text);
  944.   l->next = msgbuf.first;
  945.   l->next->prev = l;
  946.   l->prev = NULL;
  947.   msgbuf.first = l;
  948.  
  949.   for (j = 0; j < i; j++) {
  950.       message.to = address[j];
  951. #ifdef EIDS
  952.       memset(&message.eid,0,sizeof(message.eid));
  953. #endif
  954.       memset(&message.msgid,0,sizeof(message.msgid));
  955.       message.header.bits.is_kill = 1;
  956.       memset(message.header.to,0,sizeof(message.header.to));
  957.       strncpy(message.header.to,name[j],sizeof(message.header.to));
  958.       ptrfree(name[j]);
  959.  
  960.       message.header.msgnum = newmsgnum();
  961.       writemsg(&message);
  962.       setcur(CurBoard,message.header.msgnum);
  963.   }
  964.  
  965.   ptrfree(name);
  966.   ptrfree(address);
  967. } /* save */
  968.