home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / elm / elm2.4 / src / reply.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-04-11  |  19.6 KB  |  688 lines

  1.  
  2. static char rcsid[] = "@(#)$Id: reply.c,v 5.12 1993/04/12 03:02:05 syd Exp $";
  3.  
  4. /*******************************************************************************
  5.  *  The Elm Mail System  -  $Revision: 5.12 $   $State: Exp $
  6.  *
  7.  *            Copyright (c) 1988-1992 USENET Community Trust
  8.  *            Copyright (c) 1986,1987 Dave Taylor
  9.  *******************************************************************************
  10.  * Bug reports, patches, comments, suggestions should be sent to:
  11.  *
  12.  *    Syd Weinstein, Elm Coordinator
  13.  *    elm@DSI.COM            dsinc!elm
  14.  *
  15.  *******************************************************************************
  16.  * $Log: reply.c,v $
  17.  * Revision 5.12  1993/04/12  03:02:05  syd
  18.  * If a To: or Cc: line is split in a comment, that is between ( and ),
  19.  * get_and_expand_everyone won't parse that correctly.
  20.  * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
  21.  *
  22.  * Revision 5.11  1993/04/12  02:34:36  syd
  23.  * I have now added a parameter which controls whether want_to clears the
  24.  * line and centers the question or behaves like it did before. I also
  25.  * added a 0 at the end of the parameter list to all the other calls to
  26.  * want_to where a centered question on a clean line is not desirable.
  27.  * From: Jukka Ukkonen <ukkonen@csc.fi>
  28.  *
  29.  * Revision 5.10  1993/02/06  16:46:06  syd
  30.  * remove outdate include of utsname, no longer needed in reply.c
  31.  * From: Syd
  32.  *
  33.  * Revision 5.9  1993/02/03  19:06:31  syd
  34.  * Remove extra strchr/strcat/strcpy et al declarations
  35.  * From: Syd
  36.  *
  37.  * Revision 5.8  1993/02/03  16:25:45  syd
  38.  * Adresses with double quoted strings that contains comma was parsed
  39.  * wrongly by break_down_tolist() and figure_out_addressee().
  40.  * From: Jan Djarv <Jan.Djarv@sa.erisoft.se>
  41.  *
  42.  * Revision 5.7  1992/12/24  21:42:01  syd
  43.  * Fix messages and nls messages to match.  Plus use want_to
  44.  * where appropriate.
  45.  * From: Syd, via prompting from Jan Djarv <Jan.Djarv@sa.erisoft.se>
  46.  *
  47.  * Revision 5.6  1992/12/11  01:45:04  syd
  48.  * remove sys/types.h include, it is now included by defs.h
  49.  * and this routine includes defs.h or indirectly includes defs.h
  50.  * From: Syd
  51.  *
  52.  * Revision 5.5  1992/11/26  00:46:50  syd
  53.  * Fix how errno is used so err is inited and used instead
  54.  * as errno gets overwritten by print system call
  55.  * From: Syd
  56.  *
  57.  * Revision 5.4  1992/11/07  20:05:52  syd
  58.  * change to use header_cmp to allow for linear white space around the colon
  59.  * From: Syd
  60.  *
  61.  * Revision 5.3  1992/10/24  13:35:39  syd
  62.  * changes found by using codecenter on Elm 2.4.3
  63.  * From: Graham Hudspith <gwh@inmos.co.uk>
  64.  *
  65.  * Revision 5.2  1992/10/11  01:25:58  syd
  66.  * Add undefs of tolower so BSD macro isnt used from ctype.h
  67.  * From: Syd
  68.  *
  69.  * Revision 5.1  1992/10/03  22:58:40  syd
  70.  * Initial checkin as of 2.4 Release at PL0
  71.  *
  72.  *
  73.  ******************************************************************************/
  74.  
  75. /*** routine allows replying to the sender of the current message 
  76.  
  77. ***/
  78.  
  79. #include "headers.h"
  80. #include "s_elm.h"
  81. #include <errno.h>
  82. #include <ctype.h>
  83.  
  84. #ifdef BSD
  85. #undef        tolower
  86. #endif
  87.  
  88. /** Note that this routine generates automatic header information
  89.     for the subject and (obviously) to lines, but that these can
  90.     be altered while in the editor composing the reply message! 
  91. **/
  92.  
  93. char *strip_parens(), *get_token();
  94.  
  95. extern int errno;
  96.  
  97. char *error_description();
  98.  
  99.  
  100. /* Determine the subject to use for a reply.  */
  101. void
  102. get_reply_subj(out_subj,in_subj,dflt_subj)
  103. char *out_subj;        /* store the resulting subject here        */
  104. char *in_subj;        /* subject of the original message        */
  105. char *dflt_subj;    /* default to use if "in_subj" is empty        */
  106. {
  107.     if ( *in_subj == '\0' ) {
  108.       strcpy(out_subj,dflt_subj);
  109.       return;
  110.     }
  111.     if (
  112.       ( in_subj[0] == 'r' || in_subj[0] == 'R' ) &&
  113.       ( in_subj[1] == 'e' || in_subj[1] == 'E' ) &&
  114.       ( in_subj[2] == ':' )
  115.     ) {
  116.       for ( in_subj += 3 ; whitespace(*in_subj) ; ++in_subj ) ;
  117.     }
  118.     strcat( strcpy( out_subj, "Re: " ), in_subj);
  119. }
  120.  
  121. int
  122. optimize_and_add(new_address, full_address)
  123. char *new_address, *full_address;
  124. {
  125.     /** This routine will add the new address to the list of addresses
  126.         in the full address buffer IFF it doesn't already occur.  It
  127.         will also try to fix dumb hops if possible, specifically hops
  128.         of the form ...a!b...!a... and hops of the form a@b@b etc 
  129.     **/
  130.  
  131.     register int len, host_count = 0, i;
  132.     char     hosts[MAX_HOPS][SLEN];    /* array of machine names */
  133.     char     *host, *addrptr;
  134.  
  135.     if (in_list(full_address, new_address))
  136.       return(1);    /* duplicate address */
  137.  
  138.     /** optimize **/
  139.     /*  break down into a list of machine names, checking as we go along */
  140.     
  141.     addrptr = (char *) new_address;
  142.  
  143.     while ((host = get_token(addrptr, "!", 1)) != NULL) {
  144.       for (i = 0; i < host_count && ! equal(hosts[i], host); i++)
  145.           ;
  146.  
  147.       if (i == host_count) {
  148.         strcpy(hosts[host_count++], host);
  149.         if (host_count == MAX_HOPS) {
  150.            dprint(2, (debugfile,
  151.               "Error: hit max_hops limit trying to build return address (%s)\n",
  152.               "optimize_and_add"));
  153.            error(catgets(elm_msg_cat, ElmSet, ElmBuildRAHitMaxHops,
  154.         "Can't build return address. Hit MAX_HOPS limit!"));
  155.            return(1);
  156.         }
  157.       }
  158.       else 
  159.         host_count = i + 1;
  160.       addrptr = NULL;
  161.     }
  162.  
  163.     /** fix the ARPA addresses, if needed **/
  164.     
  165.     if (qchloc(hosts[host_count-1], '@') > -1)
  166.       fix_arpa_address(hosts[host_count-1]);
  167.       
  168.     /** rebuild the address.. **/
  169.  
  170.     new_address[0] = '\0';
  171.  
  172.     for (i = 0; i < host_count; i++)
  173.       sprintf(new_address, "%s%s%s", new_address, 
  174.               new_address[0] == '\0'? "" : "!",
  175.               hosts[i]);
  176.  
  177.     if (full_address[0] == '\0')
  178.       strcpy(full_address, new_address);
  179.     else {
  180.       len = strlen(full_address);
  181.       full_address[len  ] = ',';
  182.       full_address[len+1] = ' ';
  183.       full_address[len+2] = '\0';
  184.       strcat(full_address, new_address);
  185.     }
  186.  
  187.     return(0);
  188. }
  189.  
  190. void
  191. get_and_expand_everyone(return_address, full_address)
  192. char *return_address, *full_address;
  193. {
  194.     /** Read the current message, extracting addresses from the 'To:'
  195.         and 'Cc:' lines.   As each address is taken, ensure that it
  196.         isn't to the author of the message NOR to us.  If neither,
  197.         prepend with current return address and append to the 
  198.         'full_address' string.
  199.     **/
  200.  
  201.     char ret_address[SLEN], buf[VERY_LONG_STRING], new_address[SLEN],
  202.      address[SLEN], comment[SLEN], next_line[SLEN];
  203.     int  lines, iindex, line_pending = 0, line_len, err;
  204.  
  205.     /** First off, get to the first line of the message desired **/
  206.  
  207.     if (fseek(mailfile, headers[current-1]->offset, 0) == -1) {
  208.     err = errno;
  209.     dprint(1,(debugfile,"Error: seek %ld resulted in errno %s (%s)\n", 
  210.          headers[current-1]->offset, error_description(err), 
  211.          "get_and_expand_everyone"));
  212.     error2(catgets(elm_msg_cat, ElmSet, ElmSeekFailedFile,
  213.         "ELM [seek] couldn't read %d bytes into file (%s)."),
  214.         headers[current-1]->offset, error_description(err));
  215.     return;
  216.     }
  217.  
  218.     /** okay!  Now we're there!  **/
  219.  
  220.     /** let's fix the ret_address to reflect the return address of this
  221.     message with '%s' instead of the persons login name... **/
  222.  
  223.     translate_return(return_address, ret_address);
  224.  
  225.     /** now let's parse the actual message! **/
  226.  
  227.     for (lines = headers[current-1]->lines;;) {
  228.  
  229.       /* read in another line if required - break out if end of mbox reached */
  230.       if ( !line_pending && (line_len = mail_gets(buf, SLEN, mailfile)) == 0 )
  231.       return;
  232.       line_pending = 0;
  233.  
  234.       /* break out at end of header or start of next message */
  235.       if ( line_len < 2 )
  236.     return;
  237.       if (buf[line_len - 1] == '\n')
  238.     lines--;
  239.       if (lines <= 0)
  240.     return;
  241.  
  242.       /* we only want lines with addresses */
  243.       if (!header_cmp(buf, "To", NULL) && !header_cmp(buf, "cc", NULL))
  244.     continue;
  245.  
  246.       /* extract the addresses from this line and possible continuation lines */
  247.       next_line[0] = '\0';
  248.  
  249.       /* read in another line - continuation lines start with whitespace */
  250.       while ( (line_len = mail_gets(next_line, SLEN, mailfile)) != 0 &&
  251.           whitespace(next_line[0]) ) {
  252.     no_ret(buf);
  253.     strcat(buf, next_line); /* Append continuation line */
  254.  
  255.     if (next_line[line_len - 1] == '\n')
  256.       lines--;
  257.     next_line[0] = '\0';
  258.       }
  259.  
  260.       no_ret(buf);
  261.       iindex = chloc(buf, ':')+1;        /* point beyond header name */
  262.       dprint(2,(debugfile,"> %s\n",buf));
  263.  
  264.       /* go through all addresses in this line */
  265.       while (break_down_tolist(buf, &iindex, address, comment)) {
  266.     if (okay_address(address, return_address)) {
  267.  
  268.       /**
  269.           Some mailers can emit unqualified addresses in the
  270.           headers, e.g. a Cc to a local user might appear as
  271.           just "user" and not "user@dom.ain".  We do a real
  272.           low-rent check here.  If it looks like a domain
  273.           address then we will pass it through.  Otherwise we
  274.           send it back through the originating host for routing.
  275.       **/
  276.       if (qchloc(address, '@') >= 0)
  277.         strcpy(new_address, address);
  278.       else
  279.         sprintf(new_address, ret_address, address);
  280.       optimize_and_add(new_address, full_address);
  281.  
  282.     }
  283.       }
  284.       if (next_line[0] != '\0') {
  285.     strcpy(buf, next_line);
  286.     line_pending++;
  287.       }
  288.     }
  289. }
  290.  
  291. int
  292. reply()
  293. {
  294.     /** Reply to the current message.  Returns non-zero iff
  295.         the screen has to be rewritten. **/
  296.  
  297.     char return_address[SLEN], subject[SLEN];
  298.     int  return_value, form_letter;
  299.  
  300.     form_letter = (headers[current-1]->status & FORM_LETTER);
  301.  
  302.     if (get_return(return_address, current-1)) {
  303.       strcpy(subject, headers[current-1]->subject);
  304.     } else {
  305.       get_reply_subj( subject, headers[current-1]->subject,
  306.           ( form_letter ?
  307.           catgets(elm_msg_cat, ElmSet, ElmFilledInForm, "Filled in form") :
  308.           catgets(elm_msg_cat, ElmSet, ElmReYourMail, "Re: your mail") ) );
  309.     }
  310.     if (form_letter)
  311.       return_value = mail_filled_in_form(return_address, subject);
  312.     else
  313.       return_value = send_msg(return_address, "", subject, TRUE, NO, TRUE);
  314.     return(return_value);
  315. }
  316.  
  317. int
  318. reply_to_everyone()
  319. {
  320.     /** Reply to everyone who received the current message.  
  321.         This includes other people in the 'To:' line and people
  322.         in the 'Cc:' line too.  Returns non-zero iff the screen 
  323.             has to be rewritten. **/
  324.  
  325.     char return_address[SLEN], subject[SLEN];
  326.     char full_address[VERY_LONG_STRING];
  327.     int  return_value;
  328.  
  329.     get_return(return_address, current-1);
  330.  
  331.     full_address[0] = '\0';            /* no copies yet    */
  332.     get_and_expand_everyone(return_address, full_address);
  333.     dprint(2,(debugfile,
  334.         "reply_to_everyone() - return_addr=\"%s\" full_addr=\"%s\"\n",
  335.         return_address,full_address));
  336.  
  337.     get_reply_subj( subject, headers[current-1]->subject,
  338.           catgets(elm_msg_cat, ElmSet, ElmReYourMail, "Re: your mail"));
  339.  
  340.         return_value = send_msg(return_address, full_address, subject, 
  341.         TRUE, NO, TRUE);
  342.     return(return_value);
  343.  
  344. }
  345.  
  346. int
  347. forward()
  348. {
  349.     /** Forward the current message.  What this actually does is
  350.         to temporarily set forwarding to true, then call 'send' to
  351.         get the address and route the mail.   Modified to also set
  352.         'noheader' to FALSE also, so that the original headers
  353.         of the message sent are included in the message body also.
  354.         Return TRUE if the main part of the screen has been changed
  355.         (useful for knowing whether a redraw is needed.
  356.     **/
  357.  
  358.     char subject[SLEN], address[VERY_LONG_STRING];
  359.     int  results, edit_msg = FALSE;
  360.  
  361.     forwarding = TRUE;
  362.  
  363.     address[0] = '\0';
  364.  
  365.     if (headers[current-1]->status & FORM_LETTER)
  366.       PutLine0(LINES-3,COLUMNS-40, catgets(elm_msg_cat, ElmSet, ElmNoEditingAllowed,
  367.         "<No editing allowed.>"));
  368.     else {
  369.       MCsprintf(subject, catgets(elm_msg_cat, ElmSet, ElmEditOutgoingMessage,
  370.           "Edit outgoing message? (%c/%c) "), *def_ans_yes, *def_ans_no);
  371.       edit_msg = (want_to(subject,
  372.                   *def_ans_yes, LINES-3, 0) != *def_ans_no);
  373.     }
  374.  
  375.     if (strlen(headers[current-1]->subject) > 0) {
  376.  
  377.       strcpy(subject, headers[current-1]->subject); 
  378.  
  379.       /* this next strange compare is to see if the last few chars are
  380.          already '(fwd)' before we tack another on */
  381.  
  382.       if (strlen(subject) < 6 || (strcmp((char *) subject+strlen(subject)-5,
  383.                          "(fwd)") != 0))
  384.         strcat(subject, " (fwd)");
  385.  
  386.       results = send_msg(address, "", subject, edit_msg,
  387.         headers[current-1]->status & FORM_LETTER? 
  388.         PREFORMATTED : allow_forms, FALSE);
  389.     }
  390.     else
  391.       results = send_msg(address, "",
  392.         catgets(elm_msg_cat, ElmSet, ElmForwardedMail, "Forwarded mail..."), edit_msg,
  393.         headers[current-1]->status & FORM_LETTER ? PREFORMATTED : allow_forms, FALSE);
  394.     
  395.     forwarding = FALSE;
  396.  
  397.     return(results);
  398. }
  399.  
  400. int
  401. get_return_name(address, name, trans_to_lowercase)
  402. char *address, *name;
  403. int   trans_to_lowercase;
  404. {
  405.     /** Given the address (either a single address or a combined list 
  406.         of addresses) extract the login name of the first person on
  407.         the list and return it as 'name'.  Modified to stop at
  408.         any non-alphanumeric character. **/
  409.  
  410.     /** An important note to remember is that it isn't vital that this
  411.         always returns just the login name, but rather that it always
  412.         returns the SAME name.  If the persons' login happens to be,
  413.         for example, joe.richards, then it's arguable if the name 
  414.         should be joe, or the full login.  It's really immaterial, as
  415.         indicated before, so long as we ALWAYS return the same name! **/
  416.  
  417.     /** Another note: modified to return the argument as all lowercase
  418.         always, unless trans_to_lowercase is FALSE... **/
  419.  
  420.     /**
  421.      *  Yet another note: Modified to return a reasonable name
  422.      *  even when double quoted addresses and DecNet addresses
  423.      *  are embedded in a domain style address.
  424.      **/
  425.  
  426.     char single_address[SLEN], *sa;
  427.     register int    i, loc, iindex = 0,
  428.             end, first = 0;
  429.     register char    *c;
  430.  
  431.     dprint(6, (debugfile,"get_return_name called with (%s, <>, shift=%s)\n",
  432.            address, onoff(trans_to_lowercase)));
  433.  
  434.     /* First step - copy address up to a comma, space, or EOLN */
  435.  
  436.     for (sa = single_address; *address; ) {
  437.         i = len_next_part(address);
  438.         if (i > 1) {
  439.         while (--i >= 0)
  440.             *sa++ = *address++;
  441.         } else if (*address == ',' || whitespace(*address))
  442.         break;
  443.         else
  444.         *sa++ = *address++;
  445.     }
  446.     *sa = '\0';
  447.  
  448.     /* Now is it an Internet address?? */
  449.  
  450.     if ((loc = qchloc(single_address, '@')) != -1) {      /* Yes */
  451.  
  452.         /*
  453.          *    Is it a double quoted address?
  454.          */
  455.  
  456.         if (single_address[0] == '"') {
  457.         first = 1;
  458.         /*
  459.          *  Notice `end' will really be the index of
  460.          *  the last char in a double quoted address.
  461.          */
  462.         loc = ((end = chloc (&single_address[1], '"')) == -1)
  463.             ? loc
  464.             : end;
  465.         }
  466.         else {
  467.         first = 0;
  468.         }
  469.  
  470.         /*
  471.          *    Hope it is not one of those weird X.400
  472.          *    addresses formatted like
  473.          *    /G=Jukka/S=Ukkonen/O=CSC/@fumail.fi
  474.          */
  475.  
  476.         if (single_address[first] == '/') {
  477.         /* OK then, let's assume it is one of them. */
  478.  
  479.         iindex = 0;
  480.         
  481.         if ((c = strstr (&single_address[first], "/G"))
  482.             || (c = strstr (&single_address[first], "/g"))) {
  483.  
  484.             for (c += 2; *c && (*c++ != '='); );
  485.             for ( ;*c && (*c != '/'); c++) {
  486.             name[iindex++] = trans_to_lowercase
  487.                     ? tolower (*c) : *c;
  488.             }
  489.             if (iindex > 0) {
  490.             name[iindex++] = '.';
  491.             }
  492.         }
  493.         if ((c = strstr (&single_address[first], "/S"))
  494.             || (c = strstr (&single_address[first], "/s"))) {
  495.  
  496.             for (c += 2; *c && (*c++ != '='); );
  497.             for ( ;*c && (*c != '/'); c++) {
  498.             name[iindex++] = trans_to_lowercase
  499.                     ? tolower (*c) : *c;
  500.             }
  501.         }
  502.         name[iindex] = '\0';
  503.  
  504.         for (c = name; *c; c++) {
  505.             *c = ((*c == '.') || (*c == '-') || isalnum (*c))
  506.             ? *c : '_';
  507.         }
  508.  
  509.         if (iindex == 0) {
  510.             strcpy (name, "X.400.John.Doe");
  511.         }
  512.         return 0;
  513.         }
  514.  
  515.         /*
  516.          *    Is it an embedded DecNet address?
  517.          */
  518.  
  519.         while (c = strstr (&single_address[first], "::")) {
  520.         first = c - single_address + 2;
  521.         }
  522.             
  523.  
  524.         /*
  525.          *    At this point the algorithm is to keep shifting our
  526.          *    copy window left until we hit a '!'.  The login name
  527.          *    is then located between the '!' and the first meta-
  528.          *    character to it's right (ie '%', ':', '/' or '@').
  529.          */
  530.  
  531.         for (i=loc; single_address[i] != '!' && i > first-1; i--)
  532.         if (single_address[i] == '%' || 
  533.             single_address[i] == ':' ||
  534.             single_address[i] == '/' ||
  535.             single_address[i] == '@') loc = i-1;
  536.     
  537.         if (i < first || single_address[i] == '!') i++;
  538.  
  539.         for (iindex = 0; iindex < loc - i + 1; iindex++)
  540.         if (trans_to_lowercase)
  541.             name[iindex] = tolower(single_address[iindex+i]);
  542.         else
  543.             name[iindex] = single_address[iindex+i];
  544.         name[iindex] = '\0';
  545.  
  546.     }
  547.     else {    /* easier - standard USENET address */
  548.  
  549.         /*
  550.          *    This really is easier - we just cruise left from
  551.          *    the end of the string until we hit either a '!'
  552.          *    or the beginning of the line.  No sweat.
  553.          */
  554.  
  555.         loc = strlen(single_address)-1;     /* last char */
  556.  
  557.         for (i = loc; i > -1 && single_address[i] != '!'
  558.          && single_address[i] != '.'; i--) {
  559.         if (trans_to_lowercase)
  560.             name[iindex++] = tolower(single_address[i]);
  561.         else
  562.             name[iindex++] = single_address[i];
  563.         }
  564.         name[iindex] = '\0';
  565.         reverse(name);
  566.     }
  567.     return 0;
  568. }
  569.  
  570. int
  571. break_down_tolist(buf, iindex, address, comment)
  572. char *buf, *address, *comment;
  573. int  *iindex;
  574. {
  575.     /** This routine steps through "buf" and extracts a single address
  576.         entry.  This entry can be of any of the following forms;
  577.  
  578.         address (name)
  579.         name <address>
  580.         address
  581.     
  582.         Once it's extracted a single entry, it will then return it as
  583.         two tokens, with 'name' (e.g. comment) surrounded by parens.
  584.         Returns ZERO if done with the string...
  585.     **/
  586.  
  587.     char buffer[LONG_STRING];
  588.     register int i, loc = 0, hold_index, len;
  589.  
  590.     if (*iindex > strlen(buf)) return(FALSE);
  591.  
  592.     while (whitespace(buf[*iindex])) (*iindex)++;
  593.  
  594.     if (*iindex > strlen(buf)) return(FALSE);
  595.  
  596.     /** Now we're pointing at the first character of the token! **/
  597.  
  598.     hold_index = *iindex;
  599.  
  600.     if (buf[*iindex] == '"') {    /* A quoted string */
  601.       buffer[loc++] = buf[(*iindex)++];
  602.       while (buf[*iindex] != '"' && buf[*iindex] != '\0') {
  603.         if (buf[*iindex] == '\\' && buf[(*iindex)+1] != '\0')
  604.           buffer[loc++] = buf[(*iindex)++];    /* Copy backslash */
  605.         buffer[loc++] = buf[(*iindex)++];
  606.       }
  607.     
  608.       if (buf[*iindex] == '"')
  609.         buffer[loc++] = buf[(*iindex)++]; /* Copy final " */
  610.     }
  611.  
  612.  
  613.     while (buf[*iindex] != ',' && buf[*iindex] != '\0')
  614.       buffer[loc++] = buf[(*iindex)++];
  615.  
  616.     (*iindex)++;
  617.     buffer[loc] = '\0';
  618.  
  619.     while (whitespace(buffer[loc]))     /* remove trailing whitespace */
  620.       buffer[--loc] = '\0';
  621.  
  622.     if (strlen(buffer) == 0) return(FALSE);
  623.  
  624.     dprint(5, (debugfile, "\n* got \"%s\"\n", buffer));
  625.  
  626.     if (buffer[loc-1] == ')') {    /*   address (name)  format */
  627.       for (loc = 0, len = strlen(buffer);buffer[loc] != '(' && loc < len; loc++)
  628.         /* get to the opening comment character... */ ;
  629.  
  630.       loc--;    /* back up to just before the paren */
  631.       while (whitespace(buffer[loc])) loc--;    /* back up */
  632.  
  633.       /** get the address field... **/
  634.  
  635.       for (i=0; i <= loc; i++)
  636.         address[i] = buffer[i];
  637.       address[i] = '\0';
  638.  
  639.       /** now get the comment field, en toto! **/
  640.  
  641.       loc = 0;
  642.  
  643.       for (i = chloc(buffer, '('), len = strlen(buffer); i < len; i++)
  644.         comment[loc++] = buffer[i];
  645.       comment[loc] = '\0';
  646.     }
  647.     else if (buffer[loc-1] == '>') {    /*   name <address>  format */
  648.       dprint(7, (debugfile, "\tcomment <address>\n"));
  649.       for (loc = 0, len = strlen(buffer);buffer[loc] != '<' && loc < len; loc++)
  650.         /* get to the opening comment character... */ ;
  651.       while (whitespace(buffer[loc])) loc--;    /* back up */
  652.  
  653.       /** get the comment field... **/
  654.  
  655.       comment[0] = '(';
  656.       for (i=1; i < loc; i++)
  657.         comment[i] = buffer[i-1];
  658.       comment[i++] = ')';
  659.       comment[i] = '\0';
  660.  
  661.       /** now get the address field, en toto! **/
  662.  
  663.       loc = 0;
  664.  
  665.       for (i = chloc(buffer,'<') + 1, len = strlen(buffer); i < len - 1; i++)
  666.         address[loc++] = buffer[i];
  667.     
  668.       address[loc] = '\0';
  669.     }
  670.     else {
  671.       /** the next section is added so that all To: lines have commas
  672.           in them accordingly **/
  673.  
  674.       for (i=0; buffer[i] != '\0'; i++)
  675.         if (whitespace(buffer[i])) break;
  676.       if (i < strlen(buffer)) {    /* shouldn't be whitespace */
  677.         buffer[i] = '\0';
  678.         *iindex = hold_index + strlen(buffer) + 1;
  679.       }
  680.       strcpy(address, buffer);
  681.       comment[0] = '\0';
  682.     }
  683.  
  684.     dprint(5, (debugfile, "-- returning '%s' '%s'\n", address, comment));
  685.  
  686.     return(TRUE);
  687. }
  688.