static char rcsid[] = "@(#)$Id: getaddr.c,v 1.29 2006/04/09 07:37:06 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.29 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> (was hurtta+elm@ozone.FMI.FI)
 *
 *  Content (originally) partially moved from melib/parse_util.c 
 *  (however rfc822_reap_comments is now on mparse.c)
 *****************************************************************************/

#include "headers.h"

DEBUG_VAR(Debug,__FILE__,"addr");

char **rfc822_tokenize(line) 
     CONST char *line;
{
    CONST char * ptr;
    char **res;
    int len, count = 0,i;
    
    DPRINT(Debug,25,(&Debug, 
		    "rfc822_tokenize(line=\"%s\n\"):\n",line));
    
    for (ptr = line; *ptr; ptr += len) {
	len = rfc822_toklen(ptr);
	count++;
    }
    DPRINT(Debug,25,(&Debug,
		     "rfc822_tokenize: count=%d\n",count));
    res = safe_malloc((count+1) * sizeof (char *));
    
    for (i = 0, ptr = line; 
	 i < count; 
	 i++, ptr += len) {
	len = rfc822_toklen(ptr);
	res[i] = safe_malloc(len+1);
	strncpy(res[i],ptr,len);
	res[i][len] = '\0';
	DPRINT(Debug,25,(&Debug,
			 "             : [%i]=\"%s\"\n",i,res[i]));
    }
    res[i] = NULL;
    DPRINT(Debug,25,(&Debug,
		     "             : [%i]=NULL\n",i));
    return res;
}

void remove_space_tokenized(tokenized)
     char ** tokenized;
{
    int ptr, ptr2 = 0;
    
    for (ptr = 0; tokenized[ptr]; ptr++) {
	if ('(' == tokenized[ptr][0] || whitespace(tokenized[ptr][0]) ||
	    '\n' == tokenized[ptr][0]) {
	    DPRINT(Debug,25,(&Debug, 
			     "remove_space_tokenized: FREE \t<= [%d] = \"%s\"\n",
			     ptr,tokenized[ptr]));
	    free(tokenized[ptr]);
	    tokenized[ptr] = NULL;
	} else {
	    DPRINT(Debug,25,(&Debug,
			     "remove_space_tokenized: [%d] \t<= [%d] = \"%s\"\n",
			     ptr2,ptr,tokenized[ptr]));
	    tokenized[ptr2] = tokenized[ptr];
	    if (ptr2 != ptr)
		tokenized[ptr] = NULL;
	    ptr2++;
	}
    }
    /* Check: */
    while (ptr2 <= ptr) {
	if (!tokenized[ptr2]) {
	    DPRINT(Debug,25,(&Debug,
			     "remove_space_tokenized: [%d] \t<= NULL\n",
			     ptr2));
	} else {
	    DPRINT(Debug,25,(&Debug,
			     "remove_space_tokenized: [%d] \t== \"%s\" ERROR!\n",
			     ptr2,tokenized[ptr2]));
	}
	ptr2++;
    }
}

void free_rfc822tokenized(res)
     char **res; 
{
    int i;
    for (i = 0; res[i]; i++) {
	DPRINT(Debug,100,(&Debug, 
			"free_rfc822tokenized: free(res[%d]=\"%s\")\n",
			i,res[i]));
	free(res[i]);
    }
    DPRINT(Debug,100,(&Debug, 
		    "free_rfc822tokenized: free(res)\n"));
    free(res);
}

static CONST unsigned char * csUs P_((const char *str));
static CONST unsigned char * csUs(str)
     CONST char *str;
{
    return (CONST unsigned char *)str;
}


int look_special_tokens(tokenized,tok_chars,
			start,ended,demime,set,
			comments,scanned) 
     char **tokenized;
     CONST char *tok_chars;
     int start; 
     int *ended;
     int demime;     
     charset_t set;
     struct string **comments;
     char ***scanned;
{
    int end;
    
    DPRINT(Debug,25,(&Debug, 
		     "look_special_tokens: start=%d, tok_chars=%s,*comments=%p\n", 
		     start,tok_chars,*comments));
  
    *scanned = NULL;
    
    DPRINT(Debug,25,(&Debug,
		     "look_special_tokens: "));
    
    for (end = start; tokenized[end]; end++) {
	if (NULL != strchr(tok_chars,tokenized[end][0]) &&
	    '\0' == tokenized[end][1])
	    break;
	DPRINT(Debug,25,(&Debug," [%d]=%s",end,tokenized[end]));
    }
    DPRINT(Debug,25,(&Debug,"\n"));

    if (!tokenized[end]) {
	DPRINT(Debug,25,(&Debug,"look_special_tokens: end=%d NO MATCH\n",end));
    } else {
	DPRINT(Debug,25,(&Debug,"look_special_tokens: end=%d MATCH=%s\n",
			 end,tokenized[end]));
    }

    if (end > start) {
	int count = end-start;
	int i, in_ptr,out_ptr;
	char **res;
	DPRINT(Debug,26,(&Debug,"look_special_tokens: count=%d\n",
			 count));
	
	res = safe_malloc((count+1) * sizeof (char *));
	for (i = 0; i <= count; i++)
	    res[i] = NULL;

	for (in_ptr=start, out_ptr=0;
	     in_ptr < end && out_ptr < count;
	     in_ptr++) {
	    if (whitespace(tokenized[in_ptr][0])) {
		if (*comments && string_len(*comments) && in_ptr < end-1)
		    add_ascii_to_string(*comments,csUs(tokenized[in_ptr]));
	    } else if ('(' == tokenized[in_ptr][0]) {
		struct string * tmp;

		char buffer[1000];
		int k, j = 0;

		for (k = 1; tokenized[in_ptr][k] && 
			 (tokenized[in_ptr][k] != ')' || tokenized[in_ptr][k+1]) &&
			 j < sizeof buffer -1; k++)
		    buffer[j++] = tokenized[in_ptr][k];
		buffer[j] = '\0';

		tmp = hdr_to_string(HDR_COMMENT,buffer,set,demime);
		    
		if (!*comments)
		    *comments = tmp;
		else {
		    struct string *new_str = cat_strings(*comments,tmp,1);

		    free_string(comments);
		    *comments = new_str;
		    free_string(&tmp);
		}
	    } else
		res[out_ptr++] = safe_strdup(tokenized[in_ptr]);       
	}

	DPRINT(Debug,26,(&Debug,"look_special_tokens: real count=%d, scanned:",
			 out_ptr));

	for (i = 0; i < out_ptr; i++) {
	    DPRINT(Debug,26,(&Debug," [%d]=%s",i,res[i]));
	}
	*scanned=res;

	DPRINT(Debug,26,(&Debug,
			 "\nlook_special_tokens: *scanned=%p, *comments=%S\n",
			 *scanned,*comments));
    }
    if (ended)
	*ended = end;
    DPRINT(Debug,25,(&Debug,
		     "look_special_tokens=%d\n",end-start));
    return end-start;
}

struct string * scanned_to_phrase P_((char **scanned,
				      int demime, charset_t set));

struct string *scanned_to_phrase(scanned,demime,set)
     char * *scanned;
     int demime;
     charset_t set;
{
    struct string * res = NULL;
    char * buffer = NULL;
    int idx, max_idx;
    CONST char *A,*B;

    DPRINT(Debug,25,(&Debug, "scanned_to_phrase: demime=%d\n",demime));
    DPRINT(Debug,25,(&Debug, 
		    "scanned_to_phrase:"));
    for (max_idx  = 0; scanned[max_idx]; max_idx++) {
	DPRINT(Debug,25,(&Debug, 
			" [%d]=%s",max_idx,scanned[max_idx]));
    }
    DPRINT(Debug,25,(&Debug, 
		    "\n"));

    for (idx  = 0; idx < max_idx; idx++) {
	if (buffer)
	    buffer = strmcat(buffer," ");
	buffer = strmcat(buffer,scanned[idx]);	
    }
    
    if (buffer) {
	res = hdr_to_string(HDR_PHRASE,buffer,set,demime);
	free(buffer);
    } else
	res = new_string(set);

    DPRINT(Debug,25,(&Debug, "scanned_to_phrase=%S\n",res));
    A = get_string_MIME_name(res);
    B = get_string_lang(res);
    DPRINT(Debug,25,(&Debug, "                  cs=%s lang=%s\n",
		     A ? A : "<none>", B ? B : "<none>"));

    return res;
}

static char *scanned_to_str P_((char **scanned));

static char *scanned_to_str(scanned)
     char **scanned;
{
    int idx;
    char * res = safe_strdup("");

    DPRINT(Debug,25,(&Debug, 
		    "scanned_to_str:"));

    for (idx = 0; scanned[idx]; idx++) {
	DPRINT(Debug,25,(&Debug, " [%d]=%s",idx,scanned[idx]));
	res = strmcat(res,scanned[idx]);
    }
    DPRINT(Debug,25,(&Debug, "\nscanned_to_str=%p=%s\n",res,res));

    return res;
}

struct addr_item * break_down_address (buffer, demime, defcharset)
     CONST char *buffer;
     int demime;
     charset_t defcharset;
{
    int count=1;
    int i,res_idx = 0;
    struct addr_item *res;
    int outer, inner;
    int tok_end;

    struct {
	int in_group;
	int in_bracket;
    } state;

    char **tokenized = rfc822_tokenize(buffer);

    DPRINT(Debug,11,(&Debug, 
		     "break_down_address: buffer=%.100s\n",
		     buffer));

    if (!tokenized)
	return NULL;

    for (tok_end = 0; tokenized[tok_end]; tok_end++)
	if (tokenized[tok_end][0] == ',')
	    count++;

    DPRINT(Debug,11,(&Debug, 
		     "break_down_address: count=%d, tok_end=%d\n",
		     count,tok_end));

    res = safe_malloc((count+1) * sizeof (struct addr_item));

    for (i =0; i <= count; i++) {
	res[i].addr     = NULL;
	res[i].fullname = NULL;
	res[i].comment = NULL;
    }

    state.in_group   = 0;
    state.in_bracket = 0;

    for (outer = 0; outer < tok_end; outer = inner) {
	char **scanned = NULL;
	struct string *comments = NULL;
	char tok = '\0';
      
	DPRINT(Debug,25,(&Debug, 
			 "break_down_address: [%d]=%.10s... state: in_group=%d in_bracket=%d\n",
			 outer,tokenized[outer],state.in_group,state.in_bracket));
	
	look_special_tokens(tokenized,":<>,;",outer,&inner,demime,
			    defcharset,&comments,&scanned);
	
	if (inner < tok_end) {
	    tok = tokenized[inner][0];
	    DPRINT(Debug,25,(&Debug, 
			     "break_down_address: [%d] token=%c (%s)\n",inner,tok,tokenized[inner]));
	    inner++;
	} else {
	    tok = '\0';
	    DPRINT(Debug,25,(&Debug, 
			     "break_down_address: [%d] token=EOS\n",inner));
	}
      

	if (res_idx < count) {
	    
	  /* state engine */
	    if (!state.in_group) {
		if (!state.in_bracket)
		    switch(tok) {
		    case ':':  
			DPRINT(Debug,25,(&Debug, 
					"... skipping group phrase\n"));
			state.in_group = 1;
			break;
		    case '<': 
			if (scanned && scanned[0]) {
			    struct string *str = scanned_to_phrase(scanned,
								   demime,
								   defcharset);
			    DPRINT(Debug,25,(&Debug, 
					     "... storing address phrase\n"));
			    if (res[res_idx].fullname) {
				struct string *new_str = 
				    cat_strings(res[res_idx].fullname,
						str,1);
				free_string(&res[res_idx].fullname);
				res[res_idx].fullname = new_str;
				free_string(&str);
			    } else
				res[res_idx].fullname = str;
			}
			state.in_bracket = 1;
			break;
		    case '>':
			DPRINT(Debug,25,(&Debug, 
					 "... Parse error, next token '>'\n"));
			break;
		    case ';':
			DPRINT(Debug,25,(&Debug, 
					 "... Parse error, next token ';'\n"));
			break;
		    case ',':
		    default: /* \0 */	  
			if (scanned  && scanned[0]) {
			    char *str = scanned_to_str(scanned);
			    DPRINT(Debug,25,(&Debug, 
					     "... storing address: %s\n",str));
			    res[res_idx].addr = strmcat(res[res_idx].addr,str);
			    free(str);
			    if (comments && convert_comment && 
				!res[res_idx].fullname) {
				DPRINT(Debug,25,(&Debug, "... storing comments (as fullname)\n"));
				res[res_idx].fullname = comments;
				comments = NULL;
			  } else if (comments) {
			      DPRINT(Debug,25,(&Debug,  
					       "... storing comments"));
			      if (!res[res_idx].comment)
				  res[res_idx].comment = comments;
			      else {
				   struct string *new_str =
				       cat_strings(res[res_idx].comment,
						   comments,1);
				   free_string(&res[res_idx].comment);
				   res[res_idx].comment = new_str;
				   free_string(&comments);
			      }
			      comments = NULL;
			  }
			} 
			if (res[res_idx].addr || res[res_idx].fullname || 
			    res[res_idx].comment) {

			    CONST char *A,*B;

			    res[res_idx].addr = strmcat(res[res_idx].addr,"");
			    if (!res[res_idx].fullname)
				res[res_idx].fullname = new_string(defcharset);
			    if (!res[res_idx].comment)
				res[res_idx].comment = new_string(defcharset);
			    DPRINT(Debug,11,(&Debug, 
					     
					     "break_down_address: [%d].addr    =%.100s\n",
					     res_idx,res[res_idx].addr));

			    DPRINT(Debug,11,(&Debug, 
					     "                       .fullname=%.100S\n",
					     res[res_idx].fullname));
			    A = get_string_MIME_name(res[res_idx].fullname);
			    B = get_string_lang(res[res_idx].fullname);
			    DPRINT(Debug,11,(&Debug, 
					     "                         cs=%s lang=%s\n",
					     A ? A : "<none>",
					     B ? B : "<none>"));

			    
			    DPRINT(Debug,11,(&Debug, 
					     "                       .comment =%.100S\n",
					     res[res_idx].comment));

			    A = get_string_MIME_name(res[res_idx].comment);
			    B = get_string_lang(res[res_idx].comment);
			    DPRINT(Debug,11,(&Debug, 
					     "                         cs=%s lang=%s\n",
					     A ? A : "<none>",
					     B ? B : "<none>"));

			    res_idx++;
		        }
			break;
		    } else switch(tok) {    /* state.in_bracket */
		    case ':': 
			DPRINT(Debug,25,(&Debug, 
					 "... skipping route\n"));
			break;
		    case '<':
			DPRINT(Debug,25,(&Debug, 
					 "... Parse error, next token '<'\n"));
			break;
		    case '>': store_addr1:
			if (scanned  && scanned[0]) {
			    char *str = scanned_to_str(scanned);
			    DPRINT(Debug,25,(&Debug, 
					     "... storing address: %s\n",str));
			    res[res_idx].addr = strmcat(res[res_idx].addr,str);
			    free(str);
			}
			if (res[res_idx].addr || res[res_idx].fullname
			    || res[res_idx].comment) {

			    CONST char *A, *B;

			    res[res_idx].addr = strmcat(res[res_idx].addr,"");
			    if (!res[res_idx].fullname)
				res[res_idx].fullname = new_string(defcharset);
			    if (!res[res_idx].comment)
				res[res_idx].comment = new_string(defcharset);

			    DPRINT(Debug,11,(&Debug, 
					     "break_down_address: [%d].addr    =%.100s\n",
					     res_idx,res[res_idx].addr));
			    
			    DPRINT(Debug,11,(&Debug, 
					     "                       .fullname=%.100S\n",
					     res[res_idx].fullname));

			    A = get_string_MIME_name(res[res_idx].fullname);
			    B = get_string_lang(res[res_idx].fullname);
			    DPRINT(Debug,11,(&Debug, 
					     "                         cs=%s lang=%s\n",
					     A ? A : "<none>",
					     B ? B : "<none>"));


			    DPRINT(Debug,11,(&Debug, 
					     
					     "                       .comment =%.100S\n",
					     res[res_idx].comment));

			    A = get_string_MIME_name(res[res_idx].comment);
			    B = get_string_lang(res[res_idx].comment);
			    DPRINT(Debug,11,(&Debug, 
					     "                         cs=%s lang=%s\n",
					     A ? A : "<none>",
					     B ? B : "<none>"));

			    
			    res_idx++;
			}
			state.in_bracket = 0;
			break;
		    case ',': 
			DPRINT(Debug,25,(&Debug, 
					 "... skipping route\n"));
			break;
		    case ';': 
			DPRINT(Debug,25,(&Debug, 
					"... Parse error, next token ';'\n"));
			break;
		    default: /* \0 */	  
			DPRINT(Debug,25,(&Debug, 
					 "... Parse error, missing '>'\n"));
			goto store_addr1;	  
		  }
	    } else { /* state.in_group */
		if (!state.in_bracket)
		    switch(tok) {
		    case ':': 
			DPRINT(Debug,25,(&Debug, 
					 "... Parse error, next token ':'\n"));
			break;
		    case '<':
			if (scanned && scanned[0]) {
			    struct string *str = scanned_to_phrase(scanned,
								   demime,
								   defcharset);
			    DPRINT(Debug,25,(&Debug, 
					     "... storing address phrase\n"));
			    if (res[res_idx].fullname) {
				struct string *new_str = 
				    cat_strings(res[res_idx].fullname,
						str,1);
				free_string(&res[res_idx].fullname);
				res[res_idx].fullname = new_str;
				free_string(&str);
			    } else
				res[res_idx].fullname = str;
		      }
		      state.in_bracket = 1;
		      break;
		  case '>':
		      DPRINT(Debug,25,(&Debug, 
				       "... Parse error, next token '>'\n"));
		      break;
		    case ';': store_addr2:
			state.in_group = 0;
			/* FALLTHRU */
		  case ',': 
		      if (scanned  && scanned[0]) {
			  char *str = scanned_to_str(scanned);
			  DPRINT(Debug,25,(&Debug, 
					   "... storing address: %s\n",str));
			  res[res_idx].addr = strmcat(res[res_idx].addr,str);
			  free(str);
			  if (comments && convert_comment && 
			      !res[res_idx].fullname) {
			      DPRINT(Debug,25,(&Debug, 
					       "... storing comments (as fullname)\n"));
			      res[res_idx].fullname = comments;
			      comments = NULL;			      
			  } else if (comments) {
			      DPRINT(Debug,25,(&Debug, 
					       "... storing comments\n"));
			      if (!res[res_idx].comment)
				  res[res_idx].comment = comments;
			      else {
				  struct string *new_str =
				      cat_strings(res[res_idx].comment,
						  comments,1);
				  free_string(&res[res_idx].comment);
				  res[res_idx].comment = new_str;
				  free_string(&comments);
			      }
			      comments = NULL;
			  }
		      } 
		      if (res[res_idx].addr || res[res_idx].fullname
			  || res[res_idx].comment) {

			  CONST char *A,*B;

			  res[res_idx].addr = strmcat(res[res_idx].addr,"");
			  if (!res[res_idx].fullname)
			      res[res_idx].fullname = new_string(defcharset);
			  if (!res[res_idx].comment)
			      res[res_idx].comment = new_string(defcharset);

			  DPRINT(Debug,11,(&Debug, 
					   "break_down_address: [%d].addr    =%.100s\n",
					   res_idx,res[res_idx].addr));
			  DPRINT(Debug,11,(&Debug, 		       
					    "                       .fullname=%.100S\n",
					   res[res_idx].fullname));
			  A = get_string_MIME_name(res[res_idx].fullname);
			  B = get_string_lang(res[res_idx].fullname);
			  DPRINT(Debug,11,(&Debug, 
					   "                         cs=%s lang=%s\n",
					   A ? A : "<none>",
					   B ? B : "<none>"));

			  DPRINT(Debug,11,(&Debug, 	 
					   "                       .comment =%.100S\n",
					   res[res_idx].comment));
			  
			  A = get_string_MIME_name(res[res_idx].comment);
			  B = get_string_lang(res[res_idx].comment);
			  DPRINT(Debug,11,(&Debug, 
					   "                         cs=%s lang=%s\n",
					   A ? A : "<none>",
					   B ? B : "<none>"));

			  res_idx++;
		      }
		      break;
		  default: /* \0 */
		      DPRINT(Debug,25,(&Debug, 
				       "... Parse error, missing ';'\n"));
		      goto store_addr2;
		    } else switch(tok) { /* state.in_bracket */
		    case ':': 
			DPRINT(Debug,25,(&Debug, 
					 "... skipping route\n"));
			break;
		    case '<':
			DPRINT(Debug,25,(&Debug, 
					"... Parse error, next token '<'\n"));
			break;
		    case ';': 
			DPRINT(Debug,25,(&Debug, 
					 "... Parse error, next token ';', missing '>'\n"));
			state.in_group = 0;
			/* FALLTHRU */
		    case '>': store_addr3:
			if (scanned && scanned[0]) {
			    char *str = scanned_to_str(scanned);
			    DPRINT(Debug,25,(&Debug, 
					    "... storing address: %s\n",str));
			    res[res_idx].addr = strmcat(res[res_idx].addr,str);
			    free(str);
		      }
			if (res[res_idx].addr || res[res_idx].fullname
			    || res[res_idx].comment) {
			    CONST char *A, *B;

			    res[res_idx].addr = strmcat(res[res_idx].addr,"");
			    if (!res[res_idx].fullname)
				res[res_idx].fullname = new_string(defcharset);
			    if (!res[res_idx].comment)
				res[res_idx].comment = new_string(defcharset);

			    DPRINT(Debug,11,(&Debug, 
					     "break_down_address: [%d].addr    =%.100s\n",
					     res_idx,res[res_idx].addr));
			    DPRINT(Debug,11,(&Debug, 			   
					     "                       .fullname=%.100S\n",
					     res[res_idx].fullname));
			    A = get_string_MIME_name(res[res_idx].fullname);
			    B = get_string_lang(res[res_idx].fullname);
			    DPRINT(Debug,11,(&Debug, 
					     "                         cs=%s lang=%s\n",
					     A ? A : "<none>",
					     B ? B : "<none>"));

			    DPRINT(Debug,11,(&Debug, 
					     "                       .comment =%.100S\n",
					     res[res_idx].comment));
			    A = get_string_MIME_name(res[res_idx].comment);
			    B = get_string_lang(res[res_idx].comment);
			    DPRINT(Debug,11,(&Debug, 
					     "                         cs=%s lang=%s\n",
					     A ? A : "<none>",
					     B ? B : "<none>"));

			    
			    res_idx++;
			}
			state.in_bracket = 0;
			break;
		    case ',': 
			DPRINT(Debug,25,(&Debug, 
					"... skipping route\n"));
			break;
		    default: /* \0 */
			DPRINT(Debug,25,(&Debug, 
					 "... Parse error, missing '>', missing ';'\n"));
			state.in_group = 0;
			goto store_addr3;
		    }
	    }
	} else {
	    if (scanned  && scanned[0]) {
		DPRINT(Debug,11,(&Debug, 

				 "break_down_address: OVERFLOW (already %d addresses)\n",
				 count));
		
	    }
	}
      
	/* do not convert here comment to fullname even when convert_comment
	 * is set!
	 */

	if (comments && res_idx > 0 && 
	    !string_len(res[res_idx-1].comment) &&
	    /* Don't store comment it is already stored as fullname
	     * because of convert_comment is set!
	     */
	    0 != string_cmp(res[res_idx-1].fullname,comments,
			    999 /* == Values not comparable */ )) {
	    struct string *new_str = cat_strings(res[res_idx-1].comment,
						 comments,1);
	    DPRINT(Debug,25,(&Debug, 
			     "... storing comments to previous address\n"));
	    free_string(&res[res_idx-1].comment);
	    res[res_idx-1].comment = new_str;	  
	}
	if (comments)
	    free_string(&comments);
	if (scanned)
	    free_rfc822tokenized(scanned);
    }
    
    free_rfc822tokenized(tokenized);
    
    DPRINT(Debug,11,(&Debug, 
		     "break_down_address=%08X (real count=%d)\n",
		     res,res_idx));
    
    return res;
}

void  free_addr_items (list)
     struct addr_item *list;
{
    struct addr_item *ptr;
    
    DPRINT(Debug,100,(&Debug, 
		      "free_addr_items(%08X)\n",list));

    for (ptr=list; ptr->addr && ptr->fullname; ptr++) {
	free(ptr->addr);
	ptr->addr = NULL;
	
	free_string(& (ptr->fullname));
	
	free_string (& (ptr->comment));
    }
    free(list);
}


void  append_addr_items (list, list1)
     struct addr_item **list, *list1;
{
    int count = 0;
    int n     = 0;
    struct addr_item *ptr;

    struct addr_item *ret = *list;

    if (ret) {
	for (ptr=ret; ptr->addr && ptr->fullname; ptr++)
	    n++;   
	count = n;
    }

    for (ptr=list1; ptr->addr && ptr->fullname; ptr++)
	count++;


    ret = safe_realloc(ret, (count + 1) * sizeof (*ret));

    for (ptr=list1; ptr->addr && ptr->fullname && n < count; ptr++) {

	ret[n].addr     = safe_strdup(ptr->addr);
	ret[n].fullname = dup_string (ptr->fullname);
	ret[n].comment  = NULL;
	if (ptr->comment)
	    ret[n].comment  = dup_string(ptr->comment);
	n++;
    }

    ret[n].addr     = NULL;
    ret[n].fullname = NULL;
    ret[n].comment = NULL;

    *list = ret;
}


/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1