/* textfncs.c */

#include "ml.h"

Boolean hex_flag = FALSE;
Boolean found_hex = FALSE;
static unsigned char hex1 = NUL_TERM;
Widget textshell;
int textdone;
int confirm_done;

Menu confirm_menu[] = 
{
  { NULL, "confirm_ok", NUL_TERM,
      confirm_ok, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "confirm_cancel", NUL_TERM,
      confirm_cancel, NULL, 0, NULL, NULL, BTN_ON },
};

Menu confirm_yn_menu[] = 
{
  { NULL, "confirm_yes", NUL_TERM,
      confirm_ok, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "confirm_no", NUL_TERM,
      confirm_cancel, NULL, 0, NULL, NULL, BTN_ON },
};



Menu text_input_menu[] = 
{
  { NULL, "text_input_accept", NUL_TERM,
      text_input_accept, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "text_input_cancel", NUL_TERM,
      text_input_cancel, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "text_input_HELP", NUL_TERM,
      text_input_help, NULL, 0, NULL, NULL, BTN_ON },
};


Menu spell_menu[] = 
{
  { NULL, "spell_next", NUL_TERM,
      spell_next, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "spell_cancel", NUL_TERM,
      spell_cancel, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "spell_remember", NUL_TERM,
      spell_remember, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "spell_HELP", NUL_TERM,
      spell_help, NULL, 0, NULL, NULL, BTN_ON },
};


Menu search_menu[] = 
{
  { NULL, "search_search", NUL_TERM,
      search_search, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "search_replace", NUL_TERM,
      search_replace, NULL, 0, NULL, NULL, BTN_MAILBOX_NOEDIT },
  { NULL, "search_cancel", NUL_TERM,
      search_cancel, NULL, 0, NULL, NULL, BTN_ON },
  { NULL, "search_HELP", NUL_TERM,
      search_help, NULL, 0, NULL, NULL, BTN_ON },
};




/* 
 * Keep from dragging the current selection in a modal list widget into
 * another widget which has no keyboard focus ability, which core dumps.
 *
 */

char GLOBAL_modal_list_translations[] =
"    <Btn2Down>:        ListEndSelect()        \n\
     <Btn2Up>:          ListEndSelect()        \n\
     <Btn2Motion>:      ListEndSelect()        \n";      

char GLOBAL_lview_pop_translations[] =
"    <Btn3Down>:        lview-pop()            \n";
char GLOBAL_read_pop_translations[] =
"    <Btn3Down>:        read-pop()             \n";
char GLOBAL_compose_pop_translations[] =
"    <Btn3Down>:        compose-pop()          \n";
char GLOBAL_filters_pop_translations[] =
"    <Btn3Down>:        filters-pop()          \n";
char GLOBAL_mailboxes_pop_translations[] =
"    <Btn3Down>:        mailboxes-pop()        \n";
char GLOBAL_mailcopy_pop_translations[] =
"    <Btn3Down>:        mailcopy-pop()         \n";
char GLOBAL_addresses_pop_translations[] =
"    <Btn3Down>:        addresses-pop()        \n";
char GLOBAL_note_pop_translations[] =
"    <Btn3Down>:        note-pop()             \n";
char GLOBAL_help_pop_translations[] =
"    <Btn3Down>:        help-pop()             \n";
char GLOBAL_netconf_pop_translations[] =
"    <Btn3Down>:        netconf-pop()          \n";
char GLOBAL_messages_pop_translations[] =
"    <Btn3Down>:        messages-pop()          \n";


char GLOBAL_text_translations[] =
"       ~Meta ~Alt Ctrl<Key>a:          beginning-of-line()             \n\
        ~Meta ~Alt Ctrl<Key>b:          backward-character()            \n\
        ~Meta ~Alt Ctrl<Key>d:          delete-next-character()         \n\
        ~Meta ~Alt Ctrl<Key>e:          end-of-line()                   \n\
        ~Meta ~Alt Ctrl<Key>f:          forward-character()             \n\
        ~Meta ~Alt Ctrl<Key>g:          process-cancel()                \n\
        ~Meta ~Alt Ctrl<Key>h:          delete-previous-character()     \n\
        ~Meta ~Alt Ctrl<Key>k:          delete-to-end-of-line()         \n\
        ~Meta ~Alt Ctrl<Key>n:          next-line()                     \n\
        ~Meta ~Alt Ctrl<Key>p:          previous-line()                 \n\
        ~Meta ~Alt Ctrl<Key>s:          search-text()                   \n\
        ~Meta ~Alt Ctrl<Key>v:          next-page()                     \n\
        ~Meta ~Alt Ctrl<Key>w:          cut-clipboard()                 \n\
        ~Meta ~Alt Ctrl<Key>y:          paste-clipboard()               \n\
        ~Meta ~Alt Ctrl<Key>/:          hex-input()                     \n\
        Meta ~Ctrl<Key>b:               backward-word()                 \n\
         Alt ~Ctrl<Key>b:               backward-word()                 \n\
        Meta ~Ctrl<Key>d:               delete-next-word()              \n\
         Alt ~Ctrl<Key>d:               delete-next-word()              \n\
        Meta ~Ctrl<Key>f:               forward-word()                  \n\
         Alt ~Ctrl<Key>f:               forward-word()                  \n\
        Meta ~Ctrl<Key>w:               copy-clipboard()                \n\
         Alt ~Ctrl<Key>w:               copy-clipboard()                \n\
        Meta ~Ctrl<Key>osfLeft:         backward-word()                 \n\
         Alt ~Ctrl<Key>osfLeft:         backward-word()                 \n\
        Meta ~Ctrl<Key>osfRight:        forward-word()                  \n\
         Alt ~Ctrl<Key>osfRight:        forward-word()                  \n\
        Meta ~Ctrl<Key>osfBackSpace:    delete-previous-word()          \n\
         Alt ~Ctrl<Key>osfBackSpace:    delete-previous-word()          \n\
        Meta ~Ctrl<Key>osfDelete:       delete-next-word()              \n\
         Alt ~Ctrl<Key>osfDelete:       delete-next-word()              \n\
                  <Key>osfPageUp:       previous-page()                 \n\
                  <Key>osfPageDown:     next-page()                     \n\
   ~Meta ~Alt Ctrl<Key>osfPageUp:       previous-page()                 \n\
   ~Meta ~Alt Ctrl<Key>osfPageDown:     next-page()                     \n\
        Meta ~Ctrl<Key>v:               previous-page()                 \n\
         Alt ~Ctrl<Key>v:               previous-page()                 \n  ";

char GLOBAL_text_field_translations[] = 

"	~Meta ~Alt Ctrl<Key>a:		beginning-of-line()		\n\
	~Meta ~Alt Ctrl<Key>b:		backward-character()		\n\
	~Meta ~Alt Ctrl<Key>d:		delete-next-character()		\n\
	~Meta ~Alt Ctrl<Key>e:		end-of-line()			\n\
	~Meta ~Alt Ctrl<Key>f:		forward-character()		\n\
	~Meta ~Alt Ctrl<Key>g:		process-cancel()		\n\
	~Meta ~Alt Ctrl<Key>h:		delete-previous-character()	\n\
	~Meta ~Alt Ctrl<Key>k:		delete-to-end-of-line()		\n\
	~Meta ~Alt Ctrl<Key>s:		search-text()			\n\
	~Meta ~Alt Ctrl<Key>w:		cut-clipboard()			\n\
        ~Meta ~Alt Ctrl<Key>y:          paste-clipboard()               \n\
        ~Meta ~Alt Ctrl<Key>/:          hex-input()                     \n\
	Meta ~Ctrl<Key>b:		backward-word()			\n\
	 Alt ~Ctrl<Key>b:		backward-word()			\n\
	Meta ~Ctrl<Key>d:		delete-next-word()		\n\
	 Alt ~Ctrl<Key>d:		delete-next-word()		\n\
	Meta ~Ctrl<Key>f:		forward-word()			\n\
	 Alt ~Ctrl<Key>f:		forward-word()			\n\
	Meta ~Ctrl<Key>w:		copy-clipboard()		\n\
	 Alt ~Ctrl<Key>w:		copy-clipboard()		\n\
	Meta ~Ctrl<Key>osfLeft:		backward-word()			\n\
	 Alt ~Ctrl<Key>osfLeft:		backward-word()			\n\
	Meta ~Ctrl<Key>osfRight:	forward-word()			\n\
	 Alt ~Ctrl<Key>osfRight:	forward-word()			\n\
	Meta ~Ctrl<Key>osfBackSpace:	delete-previous-word()		\n\
	 Alt ~Ctrl<Key>osfBackSpace:	delete-previous-word()		\n\
	Meta ~Ctrl<Key>osfDelete:	delete-next-word()		\n\
	 Alt ~Ctrl<Key>osfDelete:	delete-next-word()		\n ";


char GLOBAL_nonterminal_text_field_translations[] =
"	!<Key>Return:		next-tab-group()			\n\
	!<Key>Linefeed:		next-tab-group()			\n ";

char GLOBAL_terminal_text_field_translations[] =
"	<Key>Return:		activate()				\n\
	<Key>Linefeed:		activate()				\n ";


#ifdef __STDC__
int ml_strcoll(unsigned char *s1, unsigned char *s2)
#else
int ml_strcoll(s1, s2)
     unsigned char *s1;
     unsigned char *s2;
#endif
{
  return(strcoll((char *) s1, (char *) s2));
}

#ifdef __STDC__
unsigned char *GetUnTextField(Widget w)
#else
unsigned char *GetUnTextField(w)
     Widget w;
#endif
{
  return((unsigned char *) XmTextGetString(w));
}

#ifdef __STDC__
char *GetTextField(Widget w)
#else
char *GetTextField(w)
     Widget w;
#endif
{
  return(XmTextGetString(w));
}

#ifdef __STDC__
void TextSetString(Widget w, unsigned char *str)
#else
void TextSetString(w, str)
     Widget w;
     unsigned char *str;
#endif
{
  XmTextSetString(w,(char *) str);
}



#ifdef __STDC__
void AppendText(Widget w, char *str)
#else
void AppendText(w, str)
     Widget w;
     char *str;
#endif
{
  
  XmTextPosition pos;

  if(!str)
    return;

  pos = XmTextGetLastPosition(w);
  XmTextInsert(w,pos,str);
  pos = XmTextGetLastPosition(w);
  XmTextSetInsertionPosition(w, pos);
  return;
}


#ifdef __STDC__
void InsertText(Widget w, char *str)
#else
void InsertText(w, str)
     Widget w;
     char *str;
#endif
{
  XmTextPosition pos;

  if(!str)
    return;

  pos = XmTextGetInsertionPosition(w);
  XmTextInsert(w,pos,str);
  XmTextSetInsertionPosition(w, pos + strlen(str));
  return;
}


#ifdef __STDC__
void text_nospace_edit(Widget w, XtPointer zilch, XtPointer call_data)
#else
void text_nospace_edit(w,zilch,call_data)
     Widget w;
     XtPointer zilch;
     XtPointer call_data;
#endif
{
  XmTextVerifyCallbackStruct *ptr = (XmTextVerifyCallbackStruct *) call_data;
  register char *n;

  if(ptr == NULL)
    return;
  if(ptr->text->length != 0) {
    for(n = ptr->text->ptr; *n != NUL_TERM; n ++) 
      if((*n == SPACECHAR) || (*n == TABCHAR))
	*n = '-';
    ptr->text->length = strlen(ptr->text->ptr);
    ptr->doit = TRUE;
  }
  return;
}


#ifdef __STDC__
void text_hex_edit(Widget w, XtPointer zilch, XtPointer call_data)
#else
void text_hex_edit(w,zilch,call_data)
     Widget w;
     XtPointer zilch;
     XtPointer call_data;
#endif
{

  unsigned char a;
  unsigned char x;

  XmTextVerifyCallbackStruct *ptr = (XmTextVerifyCallbackStruct *) call_data;

  if(ptr == NULL)
    return;
  if(hex_flag == FALSE) {
    ptr->doit = TRUE;
    found_hex = FALSE;
    hex1 = NUL_TERM;
    return;
  }
  else {
    x = *ptr->text->ptr;

    if(! isxdigit(x)) {
      hex_flag = FALSE;
      ptr->doit = TRUE;
      found_hex = FALSE;
      hex1 = NUL_TERM;
      return;
    }
    if(found_hex == FALSE) {
      found_hex = TRUE;
      if(isdigit(x))
	hex1 = x - '0';
      else
	hex1 = x - (isupper(x) ? 'A' - 10 : 'a' - 10);
      ptr->text->length = 0;
      ptr->doit = TRUE;
      return;
    }
    else {
      if(isdigit(x))
	a = x  - '0';
      else
	a = x - (isupper(x) ? 'A' - 10 : 'a' - 10);
      x = (hex1 << 4);
      x += a;

      *(ptr->text->ptr) = x;
      hex_flag = FALSE;
      found_hex = FALSE;
      ptr->doit = TRUE;
    }
  }
  return;

}

#ifdef __STDC__
void text_field_edit(Widget w, XtPointer zilch, XtPointer call_data)
#else
void text_field_edit(w,zilch,call_data)
     Widget w;
     XtPointer zilch;
     XtPointer call_data;
#endif
{

  unsigned char a;
  unsigned char x;

  XmTextVerifyCallbackStruct *ptr = (XmTextVerifyCallbackStruct *) call_data;
  register char *n;

  if(ptr == NULL)
    return;

  if((hex_flag == FALSE) || (ptr->text->length > 1)) {
    if(ptr->text->length != 0) {
      for(n = ptr->text->ptr; *n != NUL_TERM; n ++) 
	if(*n == LFCHAR)
	  *n = NUL_TERM;
      ptr->text->length = strlen(ptr->text->ptr);
    }
    ptr->doit = TRUE;
    found_hex = FALSE;
    hex1 = NUL_TERM;
    return;
  }
  else {
    x = *ptr->text->ptr;
    if(! isxdigit((unsigned char) x)) {
      hex_flag = FALSE;
      ptr->doit = TRUE;
      found_hex = FALSE;
      hex1 = NUL_TERM;
      return;
    }
    if(found_hex == FALSE) {
      found_hex = TRUE;
      if(isdigit((unsigned char) x))
	hex1 = x - '0';
      else
	hex1 = x - (isupper((unsigned char) x) ? 'A' - 10 : 'a' - 10);
      ptr->text->length = 0;
      ptr->doit = TRUE;
      return;
    }
    else {
      if(isdigit((unsigned char) x))
	a = x  - '0';
      else
	a = x - (isupper((unsigned char) x) ? 'A' - 10 : 'a' - 10);
      x = (hex1 << 4);
      x += a;
      if((x == LFCHAR) || (x == NUL_TERM))
	ptr->doit = FALSE;
      else {
	*(ptr->text->ptr) = x;
	ptr->doit = TRUE;
      }
      hex_flag = FALSE;
      found_hex = FALSE;
    }
  }
  return;

}




#ifdef __STDC__
void passwd_edit(Widget w, Remote_Auth *authst, XtPointer call_data)
#else
void passwd_edit(w,authst,call_data)
     Widget w;
     Remote_Auth *authst;
     XtPointer call_data;
#endif
{
  XmTextVerifyCallbackStruct * ptr;
  int n = 0;
  int num_inserted;

  ptr = (XmTextVerifyCallbackStruct *) call_data;
  if(ptr == NULL)
    return;

  if(ptr->text->length == 0) {
    delete_at(ptr->startPos,
	      ptr->endPos, 
	      authst->workspace);
  }
  else {
   num_inserted = insert_at(ptr->currInsert,
			     ptr->text->ptr,
			     authst->workspace);
    for(n = 0; n < num_inserted; n ++)
      ptr->text->ptr[n] = PASSWD_CHAR;
    ptr->doit = TRUE;
  }
  return;
}

#ifdef __STDC__
void delete_at(int pos1, int pos2, char *str)
#else
void delete_at(pos1,pos2,str)
     int pos1;
     int pos2;
     char *str;
#endif
{
  int i, j;
  char buf[FILEBUFFLEN];
  
  for(i = 0; i < pos1; i ++)
    buf[i] = str[i];

  j = i;
  for(i = pos1; i < pos2; i ++ )
    ;

  while(str[i]) {
    buf[j] = str[i];
    j++;
    i++;
  }

  buf[j] = NUL_TERM;
  strcpy(str,buf);

  for(i = 0; i < FILEBUFFLEN; i ++)
    buf[i] = NUL_TERM;

  return;
}


#ifdef __STDC__
int insert_at(int pos, char *ins, char *str)
#else
int insert_at(pos,ins,str)
     int pos;
     char *ins;
     char *str;
#endif
{
  int i, j, k;
  int base_len = strlen(str);
  int insert_len = strlen(ins);
  char buf[FILEBUFFLEN];

  if(pos >= base_len) {
    strcat(str,ins);
    return(insert_len);
  }
  else {
    for(i = 0; i < pos; i ++) 
      buf[i] = str[i];
    buf[i] = NUL_TERM;

    j = 0;
    for(k = pos; k < (pos + insert_len); k ++) {
      buf[k] = ins[j];
      j ++;
    }
    buf[pos + insert_len] = NUL_TERM;

    strcat(buf,&str[pos]);
    strcpy(str, buf);
  }
  for(i = 0; i < FILEBUFFLEN; i ++)
    buf[i] = NUL_TERM;
  return(insert_len);
}

/* 
 * scramble() produces a non (easily) readable copy of a string, 
 * in allocated space. It also works in reverse to unscramble 
 * previously scrambled strings. This program calls "wipeout" as 
 * soon as it is able to zero our the password strings it uses.
 * This is all just so we don't leave cleartext passwords laying 
 * around in core files. To steal a quote from "Road Warrior", 
 * ... "A clever fella'; a REAL clever fella'... might..."
 * But there's NOTHING we can do to fool a REAL clever fella'.
 * The user is gonna' get annoyed if we ask them for the same password 
 * over and over again. That could be even more dangerous across the 
 * net by means of "XKey snoops" than a core file with scrambled passwords. 
 * Anybody who is able, should make use of the server's autologin feature
 * which uses rsh protocols to exec the server when it's linked to 
 * "/etc/rimapd". PGP and FTP also use the authentication module,
 * so even that's not perfect. We just try and do the best we can.
 * Caller needs to free the returned string.
 */

#ifdef __STDC__
char *scramble(char *str)
#else
char *scramble(str)
     char *str;
#endif
{
  int i;
  char *ret;
  if(! str)
    return(NULL);
  ret = (cpystr(str));
  for(i = 0; (ret[i] != NUL_TERM) ; i ++)
    ret[i] = ret[i] ^ PASSWORD_PATTERN;
  return(ret);
}

/* Zero out a text string. */

#ifdef __STDC__
void wipeout(char *str)
#else
void wipeout(str)
     char *str;
#endif
{
  register char *ptr;

  if(! str)
    return;
  for(ptr = str; *ptr != NUL_TERM; ptr ++)
    *ptr = NUL_TERM;
  return;
}

#ifdef __STDC__
void zap_commas(char *str)
#else
void zap_commas(str)
     char *str;
#endif
{
  register char *ptr;

  if(! str)
    return;
 
  for(ptr = str; *ptr != NUL_TERM; ptr ++)
    if(*ptr == ',')
      *ptr = SPACECHAR;
  return;
}

#ifdef __STDC__
char *stripcr(char *str)
#else
char *stripcr(str)
     char *str;
#endif
{
  register int i,j;
  if (str) {			
    for (i = j = 0; str[i] != NUL_TERM; i++)
      if(str[i] != CRCHAR || str[i+1] != LFCHAR ) 
	str[j++] = str[i];
    str[j] = NUL_TERM;
  }
  return (str);
}


#ifdef __STDC__
char *striplf(char *str)
#else
char *striplf(str)
     char *str;
#endif
{
  register int i,j;
  if (str) {			
    for (i = j = 0; str[i] != NUL_TERM; i++)
      if(str[i] != LFCHAR ) 
	str[j++] = str[i];
    str[j] = NUL_TERM;
  }
  return (str);
}

#ifdef __STDC__
char *tabtospace(char *str)
#else
char *tabtospace(str)
     char *str;
#endif
{
  register int i;
  if (str) {			
    for (i = 0; str[i] != NUL_TERM; i++)
      if(str[i] == TABCHAR ) 
	str[i] = SPACECHAR;
  }
  return (str);
}



#ifdef __STDC__
char *unquote(char *s)
#else
char *unquote(s)
     char *s;
#endif
{
  char *ret = NULL;
  char *dst;
  char *src = s;

  if(s == NULL)
    return(cpystr(EMPTYSTR));
  dst = ret = fs_get(strlen(s) + 1);
  while(*src != NUL_TERM) {
    if(*src == DQUOTECHAR) {
      src ++;
      continue;
    }
    *dst++ = *src++;
  }
  *dst = NUL_TERM;
  return(ret);
}





/*
 * first_nonwhite returns a pointer to the first non-whitspace
 * character in a string. Warning: Don't free the result. Keep the
 * original base of the string around if it's in allocated space.
 * All this does is point to the first non-white character therein.
 */

#ifdef __STDC__
char *first_nonwhite(char *str)
#else
char *first_nonwhite(str)
     char *str;
#endif
{
  char *ptr = str;
  if(ptr != NULL)
    while(isspace(*(unsigned char *)ptr))
      ptr ++;
  return((char *) ptr);
}

#ifdef __STDC__
void remove_trailing_white(char *str)
#else
void remove_trailing_white(str)
     char *str;
#endif
{
  char *ptr = NULL;

  if((str == NULL) || (*str == NUL_TERM))
    return;
  ptr = str + ((strlen(str)) - 1);
  while(isspace(*(unsigned char *)ptr)) {
    *ptr = NUL_TERM;
    ptr --;
  }
  return;
}


#ifdef __STDC__
Binary_Buffer *load_binary_file(char *filename)
#else
Binary_Buffer *load_binary_file(filename)
     char *filename;
#endif
{
  FILE *fp;
  Binary_Buffer *binary_buffer = NULL;
  struct stat st;
  unsigned long bytes_read  = 0L;
  unsigned long bytes_total = 0L;
  char *ptr;

  if(((stat(filename,&st)) != SYSCALL_SUCCESS)
     || (st.st_size == 0)
     || ((fp = fopen(filename,"r")) == NULL))
    return(NULL);

  binary_buffer = (Binary_Buffer *) fs_get(sizeof(Binary_Buffer));
  binary_buffer->length = (unsigned long) st.st_size;
  binary_buffer->data = (unsigned char *) fs_get(st.st_size + 1);

  ptr = (char *) binary_buffer->data;

  do {
    bytes_read = fread(ptr, 1, ((unsigned long) st.st_size) - bytes_total, fp);
    bytes_total += bytes_read;
    ptr += bytes_read;
  } while ((bytes_read != 0) && (bytes_total < (unsigned long) st.st_size));
  
  fclose(fp);

  binary_buffer->data[binary_buffer->length] = NUL_TERM;
  if(bytes_total != (unsigned long) st.st_size) {
    free_binary_buffer(binary_buffer);
    binary_buffer = NULL;
  }
  return(binary_buffer);
}

#ifdef __STDC__
char *wrap_text(char *str, int linelen)
#else
char *wrap_text(str,linelen)
     char *str;
     int linelen;
#endif
{
  int i = 0;
  char *src = str;
  char *dst, *ptr;
  unsigned long dstsize;

  if(! str)
    return(NULL);

  /* allocate source length + overhead to allow expansion  */

  dstsize = (unsigned long) strlen(src) 
    + (((unsigned long) strlen(src)) / (unsigned long) linelen) + 2;
  
  ptr = (char *) fs_get(dstsize);
  dst = ptr;

  while( *src != NUL_TERM ) {
    i ++;
    switch(*src) {
    case TABCHAR:
      if((i + (TABSIZE - 1)) > linelen) {
	*dst = LFCHAR;
	i = 0;
	break;
      }
      *dst = *src;
      i += (TABSIZE - 1);      
      break;
    case LFCHAR:
      *dst = *src;
      i = 0;
      break;
    case SPACECHAR:
      if((i + wordlen(src + 1)) > linelen ) {
	*dst = LFCHAR;
	i = 0;
	break;
      }
      else {
	*dst = *src;
	break;
      }
    default:
      *dst = *src;
      break;
    }
    src ++;
    dst ++;
  }
  *dst = NUL_TERM;
  return(ptr);
}



#ifdef __STDC__
char *wrap_header_text(char *str, int linelen)
#else
char *wrap_header_text(str,linelen)
     char *str;
     int linelen;
#endif
{
  int i = 0;
  char *src = str;
  char *dst, *ptr;
  unsigned long dstsize;

  if(! str)
    return(NULL);

  /* allocate source length + overhead to allow expansion  */

  dstsize = (unsigned long) strlen(src) 
    + (2 * (((unsigned long) strlen(src)) / (unsigned long) linelen)) + 2;
  
  ptr = (char *) fs_get(dstsize);
  dst = ptr;

  while( *src != NUL_TERM ) {
    i ++;
    switch(*src) {
    case TABCHAR:
      if((i + (TABSIZE - 1)) > linelen) {
	*dst = LFCHAR;
	i = 0;
	break;
      }
      *dst = *src;
      i += (TABSIZE - 1);      
      break;
    case LFCHAR:
      *dst = *src;
      i = 0;
      break;
    case SPACECHAR:
      if((i + wordlen(src + 1)) > linelen ) {
	*dst = LFCHAR;
	i = 0;
	break;
      }
      else {
	*dst = *src;
	break;
      }
    default:
      *dst = *src;
      break;
    }
    src ++;
    dst ++;
  }
  *dst = NUL_TERM;
  return(ptr);
}


/* returns the number of chars in the string contained in 's' up
 * to the next whitespace character.
 */

#ifdef __STDC__
int wordlen(char *s)
#else
int wordlen(s)
     char *s;
#endif
{
  register int cnt = 0;
  register char *ptr = s;

  while((*ptr) && (!isspace(*ptr))) {
    ptr ++;
    cnt ++;
  }
  return(cnt);
}


#ifdef __STDC__
char *lftocrlf(char *src)
#else
char *lftocrlf(src)
     char *src;
#endif
{
  char *dst = NULL;
  register char *ptr;
  register unsigned int lines = 0;
  register unsigned int i;
  register unsigned int j;
  
  if(! src)
    return(NULL);

  for(ptr = src; *ptr != NUL_TERM ; ptr ++)
    if(*ptr == LFCHAR || *ptr == CRCHAR)
      lines ++;
  
  dst = (char *) fs_get(strlen(src) + lines + 1);
  
  for (i = j = 0; src[i] != NUL_TERM ; i++) {
    if (src[i] == LFCHAR)
      dst[j++] = CRCHAR;
    else 
      if(src[i] == CRCHAR && src[i+1] == LFCHAR)
	dst[j++] = src[i++];
    
    dst[j++] = src[i];
    
    if (src[i] == CRCHAR)
      dst[j++] = LFCHAR;
  }
    
  dst[j] = NUL_TERM;	

  return(dst);
}


#ifdef __STDC__
char *input_string(Widget w, char *title, 
		   char *default_string, char *help_file)
#else
char *input_string(w,title,default_string,help_file)
     Widget w;
     char *title;
     char *default_string;
     char *help_file;
#endif
{

  Arg args[ARGLISTSIZE];
  char buff[FILEBUFFLEN];
  char *ret = NULL;
  int n = 0;
  Widget form, menubar, label, text;
  XtTranslations translations;
  XmString xstr;
  textdone = 0;

  if(preferences.autoPlace == TRUE) {
    XtSetArg(args[n], XtNx, -1000); n ++;
    XtSetArg(args[n], XtNy, -1000); n ++;
  }

  XtSetArg (args[n], XmNdeleteResponse, XmDO_NOTHING); n++;   
  XtSetArg(args[n], XmNtitle, title); n ++;
  textshell = XtCreatePopupShell("text_input_shell",
				 topLevelShellWidgetClass, w,
				 args, n);
  n = 0;
  AddDestroyCallback (textshell);
  setup_editres(textshell);

  if(pirate_icon != (Pixmap) None)
    XtVaSetValues(textshell,
		  XmNiconPixmap,pirate_icon,
		  NULL);

  form = XmCreateForm(textshell, "text_input_form",    args, n); n = 0;


  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;

  menubar = XmCreateMenuBar(form,"text_input_menubar", args, n); n = 0;
  XtManageChild(menubar);

  create_buttons(NULL, menubar, 
		 text_input_menu, 
		 XtNumber(text_input_menu), BTN_ON, 
		 help_file, ROOTMENULEVEL);

  sprintf(buff,"%s :",title);

  XtSetArg(args[n], XmNtopAttachment,   XmATTACH_WIDGET );      n ++;
  XtSetArg(args[n], XmNtopWidget,       menubar         );      n ++;
  XtSetArg(args[n], XmNleftAttachment,  XmATTACH_FORM   );      n ++;
  XtSetArg(args[n], XmNborderWidth,     0               );      n ++;

  xstr = XmStringCreateSimple(buff);

  XtSetArg(args[n], XmNlabelString,     xstr            );      n ++;
  label = XmCreateLabel(form, "text_input_lbl", args, n );      n = 0;
  XtManageChild(label);
  XmStringFree(xstr);
  
  XtSetArg(args[n], XmNtopAttachment,   XmATTACH_WIDGET );      n ++;
  XtSetArg(args[n], XmNtopWidget,       menubar         );      n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM   );      n ++;
  XtSetArg(args[n], XmNleftAttachment,  XmATTACH_WIDGET );      n ++;
  XtSetArg(args[n], XmNleftWidget,      label           );      n ++;

  text = XmCreateTextField(form,"text_input_textfld",args, n);  n = 0;
  XtManageChild(text);

  if(default_string)
    AppendText(text,default_string);

  translations = XtParseTranslationTable(GLOBAL_text_field_translations);
  XtOverrideTranslations(text,translations);

  translations = 
    XtParseTranslationTable(GLOBAL_terminal_text_field_translations);
    XtOverrideTranslations(text,translations);  
    XtAddCallback(text, XmNactivateCallback, 
		  (XtCallbackProc) text_input_accept, NULL);

  XtAddCallback(text, XmNmodifyVerifyCallback, 
		(XtCallbackProc) text_field_edit, NULL);

  XtManageChild(form);
  push_cursor(PIRATE_CURSOR);
  XtPopup(textshell,XtGrabExclusive); 
  position_popup_widget(textshell, TRUE);
  modal_main_loop(&textdone);

  ret = GetTextField(text);

  XtPopdown(textshell);
  XtDestroyWidget(textshell);
  
  pop_cursor();
  if((textdone > 0) && (*ret != NUL_TERM)) 
    return(ret);

  if(ret)
    fs_give((void **) &ret);

  return(NULL);

}

#ifdef __STDC__
void text_input_accept(Widget w, XtPointer zilch, XtPointer xp)
#else
void text_input_accept(w,zilch,xp)
     Widget w;
     XtPointer zilch;
     XtPointer xp;
#endif
{
  textdone = 1;
  return;
}


#ifdef __STDC__
void text_input_cancel(Widget w, XtPointer zilch, XtPointer xp)
#else
void text_input_cancel(w,zilch,xp)
     Widget w;
     XtPointer zilch;
     XtPointer xp;
#endif
{
  textdone = (-1);
  return;
}

#ifdef __STDC__
void text_input_help(Widget w, XtPointer zilch, XtPointer xp)
#else
void text_input_help(w,zilch,xp)
     Widget w;
     char *zilch;
     XtPointer xp;
#endif
{
  help(textshell,zilch);
  return;
}

#ifdef __STDC__
char *ML_Strstr(char *cs, char *ct)
#else
char *ML_Strstr(cs,ct)
     char *cs;
     char *ct;
#endif
{
  char *s;
  char *t;
  while (cs = strchr (cs,*ct)) {/* for each occurance of the first character */
				/* see if remainder of string matches */
    for (s = cs + 1, t = ct + 1; *t && *s == *t; s++, t++);
    if (!*t) return cs;		/* if ran out of substring then have match */
    cs++;			/* try from next character */
  }
  return NIL;			/* not found */
}

#ifdef __STDC__
int count_lines(char *s)
#else
int count_lines(s)
     char *s;
#endif
{
  int n = 1;
  char *ptr;
  
  if(s == NULL)
    return(0);

  for(ptr = s; *ptr; ptr ++)
    if(*ptr == LFCHAR)
      n ++;

  return(n);
}


/* 
 * XmTextInsertString() seems to have a bug which screws up the scroll
 * bars when inserting more than a screenful at a time. This is a
 * function to bypass it. We break up the existing text into that before 
 * and after the insert position, and then recombine with our inserted text,
 * using XmTextSetString() to blast it onto the screen. Before finishing up,
 * the new insert position is set.
 */

#ifdef __STDC__
void text_blast(Widget w, char *str)
#else
void text_blast(w,str)
     Widget w;
     char *str;
#endif
{
  char *begin;
  char *end;
  char *existing;

  XmTextPosition pos = XmTextGetInsertionPosition(w);
 
  existing = XmTextGetString(w);

  if(pos) {
    begin = (char *) fs_get(pos + 1);
    strncpy(begin,existing,pos);
    begin[pos] = NUL_TERM;
  }
  else
    begin = cpystr(EMPTYSTR);
  
  end = cpystr(existing + pos);

  fs_give((void **) &existing);      /* free it up for re-use. */

  existing = (char *) fs_get(strlen(begin) + strlen(str) + strlen(end) + 1);
  strcpy(existing,begin);
  strcat(existing,str);
  strcat(existing,end);
  XmTextSetString(w, existing);
  XmTextSetInsertionPosition(w, pos + strlen(str));
  fs_give((void **) &existing);
  fs_give((void **) &begin);
  fs_give((void **) &end);
}
  

#ifdef __STDC__
void search_text(Widget w, XtPointer xp)
#else
void search_text(w,xp)
     Widget w;
     XtPointer xp;
#endif
{
  make_text_search_window(w);
  return;
}

#ifdef __STDC__
void hex_input(Widget w, XtPointer xp)
#else
void hex_input(w,xp)
     Widget w;
     XtPointer xp;
#endif
{
  hex_flag = TRUE;
}




#ifdef __STDC__
void spell_do_search(Widget w, Search_Win *search_win, XtPointer xp)
#else
void spell_do_search(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  Arg args[ARGLISTSIZE];
  int n = 0;

  XmTextPosition new_position, last_position;
  Boolean result = FALSE;

  last_position = XmTextGetLastPosition(search_win->parent);

  XmTextSetHighlight(search_win->parent, 0L, 
		     last_position,
		     XmHIGHLIGHT_NORMAL);

  result = XmTextFindString(search_win->parent,
			    search_win->position,
			    search_win->search_text,
			    XmTEXT_FORWARD,
			    &new_position);
  if(result == TRUE) {
    if(new_position == search_win->position) {
      search_win->position ++;
      spell_do_search(w,search_win,xp);
      return;
    }
    XmTextShowPosition(search_win->parent, new_position);
    search_win->position = new_position;
    XmTextSetHighlight(search_win->parent,new_position,
		       new_position + strlen(search_win->search_text),
		       XmHIGHLIGHT_SELECTED);
    XtSetArg(args[n], XmNsensitive, TRUE); n ++;
    XtSetValues(search_win->sbutton,args, n); n = 0;
    XtSetArg(args[n], XmNsensitive, TRUE); n ++;
    XtSetValues(search_win->rbutton,args, n); n = 0;
  }
  else {
    XtSetArg(args[n], XmNsensitive, FALSE); n ++;
    XtSetValues(search_win->sbutton,args, n); n = 0;
    XtSetArg(args[n], XmNsensitive, FALSE); n ++;
    XtSetValues(search_win->rbutton,args, n); n = 0;
    spell_next(w,search_win,xp);
  }
  return;
}


#ifdef __STDC__
void spell_do_replace(Widget w, Search_Win *search_win, XtPointer xp)
#else
void spell_do_replace(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{

  if(search_win->replace_text != NULL)
    fs_give((void **) &search_win->replace_text);
  search_win->replace_text = XmTextGetString(search_win->replacew);
  if((strcmp(search_win->replace_text, search_win->search_text)) == STRMATCH) {
    search_win->position ++;
    spell_do_search(w,search_win,xp);
    return;
  }
  XmTextReplace(search_win->parent,
		search_win->position,
		search_win->position + strlen(search_win->search_text),
		search_win->replace_text);
  search_win->position ++;   /* step over the current guy */
  spell_do_search(w,search_win,xp);
  return;
}

#ifdef __STDC__
void make_text_spell_window(Widget w, String_List *string_list)
#else
void make_text_spell_window(w, string_list)
     Widget w;
     String_List *string_list;
#endif
{
  Arg args[ARGLISTSIZE];
  int n = 0;
  Widget form, menubar;


  Search_Win *search_win = NULL;

  if((session->compose != NULL) && (session->compose->spellwindow == NULL)) {
    search_win = (Search_Win *) fs_get(sizeof(Search_Win));
    session->compose->spellwindow = search_win;
  }
  else
    search_win = session->compose->spellwindow;
  
  search_win->parent = w;
  search_win->is_realized = FALSE;
  search_win->reverse = FALSE;
  search_win->replace = TRUE;
  search_win->button_state = BTN_ON;
  search_win->search_text = NULL;
  search_win->replace_text = NULL;
  search_win->position = 0L;
  search_win->string_list = string_list;
  search_win->base = string_list;

  if(preferences.autoPlace == TRUE) {
    XtSetArg(args[n], XtNx, -1000); n ++;
    XtSetArg(args[n], XtNy, -1000); n ++;
  }

  XtSetArg (args[n], XmNdeleteResponse, XmDO_NOTHING); n++;  
  search_win->shell = XtCreateWidget("spell_shell",
				     topLevelShellWidgetClass, session->shell,
				     args, n);
  n = 0;
  AddDestroyCallback (search_win->shell);
  setup_editres(search_win->shell);

  if(pirate_icon != (Pixmap) None)
    XtVaSetValues(search_win->shell,
		  XmNiconPixmap,pirate_icon,
		  NULL);

  form = XmCreateForm(search_win->shell, "spell_form", args, n); n = 0;

  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;

  menubar = XmCreateMenuBar(form,"spell_menubar", args, n); n = 0;
  XtManageChild(menubar);

  create_buttons(NULL, menubar, 
		 spell_menu, 
		 XtNumber(spell_menu), BTN_ON, 
		 (XtPointer) search_win, 
		 ROOTMENULEVEL);
  
  XtSetArg(args[n], XmNsensitive, FALSE ); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM ); n ++;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, menubar); n ++;
  search_win->sbutton 
    = XmCreatePushButton(form,"spell_search_button",args,n); n = 0;
  XtAddCallback(search_win->sbutton, XmNactivateCallback,
		(XtCallbackProc) spell_do_search, search_win);
  XtManageChild(search_win->sbutton);

  XtSetArg(args[n], XmNsensitive, FALSE); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNleftWidget,search_win->sbutton); n ++;
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, menubar); n ++;
  search_win->rbutton 
    = XmCreatePushButton(form,"spell_replace_button", args, n); n = 0;
  XtAddCallback(search_win->rbutton, XmNactivateCallback,
		(XtCallbackProc) spell_do_replace, search_win);
  XtManageChild(search_win->rbutton);

  search_win->search_text = cpystr(string_list->string);

  search_win->searchw = create_text_field(form, search_win->sbutton, 
		      "spell_word", string_list->string, 0, NULL, NULL);

  XtSetArg(args[n], XmNeditable, FALSE ); n ++;
  XtSetValues(search_win->searchw, args, n ); n = 0;

  search_win->replacew = 
    create_text_field(form, search_win->searchw, "spell_replace", 
		      string_list->string, 0, 
		      (XtPointer) spell_accept, (XtPointer) search_win);

  XtAddCallback(search_win->shell, XmNdestroyCallback,
		(XtCallbackProc) spell_destroy, search_win);

  XtAddCallback(search_win->parent, XmNdestroyCallback,
		(XtCallbackProc) spell_parent_destroy, search_win);


  search_win->replace_text = cpystr(string_list->string);

  XtManageChild(form);
  XtManageChild(search_win->shell);
  XtRealizeWidget(search_win->shell);
  position_popup_widget(search_win->shell, TRUE);
  search_win->is_realized = TRUE;
  spell_do_search(w,search_win,NULL);
  return;

}


#ifdef __STDC__
void spell_accept(Widget w, Search_Win *search_win, XtPointer xp)
#else
void spell_accept(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  spell_do_replace(w,search_win,xp);
  return;
}

#ifdef __STDC__
void spell_next(Widget w, Search_Win *search_win, XtPointer xp)
#else
void spell_next(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  Arg args[ARGLISTSIZE];
  int n = 0;
  search_win->position = 0L;
  search_win->string_list = search_win->string_list->next;

  if((search_win->string_list != NULL)
     && (search_win->string_list->string != NULL)) {
    XmTextSetString(search_win->searchw, search_win->string_list->string);
    XmTextSetString(search_win->replacew,search_win->string_list->string);
    if(search_win->search_text != NULL)
      fs_give((void **) &search_win->search_text);
    search_win->search_text = cpystr(search_win->string_list->string);
    if(search_win->replace_text != NULL)
      fs_give((void **) &search_win->replace_text);
    search_win->replace_text = XmTextGetString(search_win->replacew);
    XtSetArg(args[n], XmNsensitive, FALSE); n ++;
    XtSetValues(search_win->sbutton, args, n ); n = 0;
    XtSetArg(args[n], XmNsensitive, FALSE); n ++;
    XtSetValues(search_win->rbutton, args, n ); n = 0;
    spell_do_search(w,search_win,xp); 
  }
  else
    spell_cancel(w,search_win,xp);
  return;
}


#ifdef __STDC__
void spell_cancel(Widget w, Search_Win *search_win, XtPointer xp)
#else
void spell_cancel(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  char command[FILEBUFFLEN];

  /* This is ridiculous, but it just happens to work. */

  if(access(SPELLOUTFILE,R_OK) == SYSCALL_SUCCESS) {
    sprintf(command,SPELL_SORT_COMMAND,SPELLSTOPFILE,
	    SPELLOUTFILE,SPELLSTOPFILE,SPELLJUNKFILE,
	    SPELLJUNKFILE,SPELLSTOPFILE,SPELLOUTFILE);
    mm_log(MLGetLocalized(XtNmsgUpdatingStopFile,MsgUpdatingStopFile), NIL);
    set_watch_cursors();
    system(command);
    pop_cursor();
  }
  XtDestroyWidget(search_win->shell);
  return;
}


#ifdef __STDC__
void spell_destroy(Widget w, Search_Win *search_win, XtPointer xp)
#else
void spell_destroy(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  if(! search_win->parent_destroyed) {
    XmTextSetHighlight(search_win->parent, 0L, 
		       XmTextGetLastPosition(search_win->parent),
		       XmHIGHLIGHT_NORMAL);
    XtRemoveCallback(search_win->parent, XmNdestroyCallback, 
		     (XtCallbackProc) spell_parent_destroy, search_win);
  }

  if(search_win->search_text != NULL)
    fs_give((void **) &search_win->search_text);
  if(search_win->replace_text != NULL)
    fs_give((void **) &search_win->replace_text);
  search_win->position = 0L;
  free_string_list(search_win->base, TRUE);
  search_win->base = NULL;
  search_win->is_realized = FALSE;
  return;
}

#ifdef __STDC__
void spell_parent_destroy(Widget w, Search_Win *search_win, XtPointer xp)
#else
void spell_parent_destroy(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  search_win->parent_destroyed = TRUE;
  XtDestroyWidget(search_win->shell);
  return;
}



#ifdef __STDC__
void spell_remember(Widget w, Search_Win *search_win, XtPointer xp)
#else
void spell_remember(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  FILE *fp;
  fp = fopen(SPELLOUTFILE,"a");
  if(fp)
    fprintf(fp,"%s\n", search_win->search_text);
  fclose(fp);
  spell_next(w,search_win,xp);
  return;
}

#ifdef __STDC__
void spell_help(Widget w, Search_Win *search_win, XtPointer xp)
#else
void spell_help(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  help(search_win->shell, SPELLHELPFILE);
  return;
}





#ifdef __STDC__
void make_text_search_window(Widget w)
#else
void make_text_search_window(w)
     Widget w;
#endif
{
  Arg args[ARGLISTSIZE];
  int n = 0;
  Widget form, menubar;
  Boolean editable;

  Search_Win *search_win;
  

  search_win = (Search_Win *) fs_get(sizeof(Search_Win));

  search_win->parent = w;
  search_win->parent_destroyed = FALSE;
  search_win->is_realized = FALSE;
  search_win->reverse = FALSE;
  search_win->replace = TRUE;
  search_win->button_state = BTN_ON;
  search_win->search_text = NULL;
  search_win->replace_text = NULL;
  search_win->position = 0L;
  search_win->string_list = NULL;
  search_win->base = NULL;

  if(preferences.autoPlace == TRUE) {
    XtSetArg(args[n], XtNx, -1000); n ++;
    XtSetArg(args[n], XtNy, -1000); n ++;
  }

  XtSetArg (args[n], XmNdeleteResponse, XmDO_NOTHING); n++;    

  search_win->shell = XtCreateWidget("search_shell",
				     topLevelShellWidgetClass, session->shell,
				     args, n);
  n = 0;
  AddDestroyCallback (search_win->shell);
  setup_editres(search_win->shell);

  if(pirate_icon != (Pixmap) None)
    XtVaSetValues(search_win->shell,
		  XmNiconPixmap,pirate_icon,
		  NULL);

  XtSetArg(args[n], XmNeditable, &editable ); n ++;
  XtGetValues(search_win->parent, args, n);
  search_win->editable = editable;


  form = XmCreateForm(search_win->shell, "search_form", args, n); n = 0;

  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;

  menubar = XmCreateMenuBar(form,"search_menubar", args, n); n = 0;
  XtManageChild(menubar);

  create_buttons(NULL, menubar, 
		 search_menu, 
		 XtNumber(search_menu), BTN_ON, 
		 (XtPointer) search_win, 
		 ROOTMENULEVEL);
  
  search_win->searchw = create_text_field(form, menubar, 
					  "search_word", NULL, 0, 
					  (XtPointer)search_search,
					  (XtPointer)search_win);

  search_win->replacew = 
    create_text_field(form, search_win->searchw, "search_replace_word", 
		      NULL, 0, 
		      (XtPointer) search_replace, (XtPointer) search_win);

  if(search_win->editable == FALSE) {
    XtSetArg(args[n], XmNeditable, FALSE); n ++;
    XtSetValues(search_win->replacew, args, n); n = 0;
    search_win->button_state |= BTN_MAILBOX_NOEDIT; /* it'll work */
  }
  

  XtAddCallback(search_win->shell, XmNdestroyCallback,
		(XtCallbackProc) search_destroy, search_win);

  XtAddCallback(search_win->parent, XmNdestroyCallback,
		(XtCallbackProc) search_parent_destroy, search_win);


  check_buttons(search_menu,XtNumber(search_menu), 
		NULL, search_win->button_state);

  XtManageChild(form);
  XtManageChild(search_win->shell);
  XtRealizeWidget(search_win->shell);
  position_popup_widget(search_win->shell, TRUE);
  return;
}




#ifdef __STDC__
void search_search(Widget w, Search_Win *search_win, XtPointer xp)
#else
void search_search(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  char *currword;

  XmTextPosition new_position, last_position;
  Boolean result = FALSE;

  last_position = XmTextGetLastPosition(search_win->parent);

  currword = XmTextGetString(search_win->searchw);
  if(search_win->search_text == NULL) {
    search_win->position = 0L;
    search_win->search_text = cpystr(currword);
  }
  else {
    if((strcmp(currword,search_win->search_text)) != STRMATCH) {
      fs_give((void **) &search_win->search_text);
      search_win->search_text = cpystr(currword);
      search_win->position = 0L;
    }
  }

  XmTextSetHighlight(search_win->parent, 0L, 
		     last_position,
		     XmHIGHLIGHT_NORMAL);

  result = XmTextFindString(search_win->parent,
			    search_win->position,
			    search_win->search_text,
			    XmTEXT_FORWARD,
			    &new_position);

  if(result == TRUE) {
    if(new_position == search_win->position) {
      search_win->position ++;
      search_search(w,search_win,xp);
      return;
    }
    XmTextShowPosition(search_win->parent, new_position);
    search_win->position = new_position;
    XmTextSetHighlight(search_win->parent,new_position,
		       new_position + strlen(search_win->search_text),
		       XmHIGHLIGHT_SELECTED);
  }
  else
    XBell(display,1000);

  return;
}

#ifdef __STDC__
void search_replace(Widget w, Search_Win *search_win, XtPointer xp)
#else
void search_replace(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  if((search_win->editable == FALSE) || (search_win->search_text == NULL))
    return;
  if(search_win->replace_text != NULL)
    fs_give((void **) &search_win->replace_text);
  search_win->replace_text = XmTextGetString(search_win->replacew);
  if((strcmp(search_win->replace_text, search_win->search_text)) == STRMATCH) {
    search_win->position ++;
    search_search(w,search_win,xp);
    return;
  }
  XmTextReplace(search_win->parent,
		search_win->position,
		search_win->position + strlen(search_win->search_text),
		search_win->replace_text);
  search_win->position ++;   /* step over the current guy */
  search_search(w,search_win,xp);
  return;
}


#ifdef __STDC__
void search_cancel(Widget w, Search_Win *search_win, XtPointer xp)
#else
void search_cancel(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  XtDestroyWidget(search_win->shell);
  return;
}


#ifdef __STDC__
void search_destroy(Widget w, Search_Win *search_win, XtPointer xp)
#else
void search_destroy(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  if(! search_win->parent_destroyed) {
    XmTextSetHighlight(search_win->parent, 0L, 
		       XmTextGetLastPosition(search_win->parent),
		       XmHIGHLIGHT_NORMAL);
    XtRemoveCallback(search_win->parent, XmNdestroyCallback, 
		   (XtCallbackProc) search_parent_destroy, search_win);
  }

  if(search_win->search_text != NULL)
    fs_give((void **) &search_win->search_text);
  if(search_win->replace_text != NULL)
    fs_give((void **) &search_win->replace_text);

  fs_give((void **) &search_win);
  return;
}


#ifdef __STDC__
void search_parent_destroy(Widget w, Search_Win *search_win, XtPointer xp)
#else
void search_parent_destroy(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  search_win->parent_destroyed = TRUE;
  XtDestroyWidget(search_win->shell);
  return;
}


#ifdef __STDC__
void search_help(Widget w, Search_Win *search_win, XtPointer xp)
#else
void search_help(w, search_win, xp)
     Widget w;
     Search_Win *search_win;
     XtPointer xp;
#endif
{
  help(search_win->shell, SEARCHREPHELPFILE);
  return;
}


#ifdef __STDC__
Boolean ml_confirm(Widget w, char *prompt, int type)
#else
Boolean ml_confirm(w,prompt,type)
     Widget w;
     char *prompt;
     int type;
#endif
{
  Arg args[ARGLISTSIZE];
  int n = 0;
  Widget shell, form, menubar, label;
  Boolean result;
  XmString xstr;

  if(preferences.autoPlace == TRUE) {
    XtSetArg(args[n], XtNx, -1000); n ++;
    XtSetArg(args[n], XtNy, -1000); n ++;
  }

  XtSetArg (args[n], XmNdeleteResponse, XmDO_NOTHING); n ++;     
  shell = XtCreatePopupShell("confirm_shell",
			     topLevelShellWidgetClass, w,
			     args, n);
  n = 0;
  AddDestroyCallback(shell);
  setup_editres(shell);

  if(pirate_icon != (Pixmap) None)
    XtVaSetValues(shell,
		  XmNiconPixmap,pirate_icon,
		  NULL);

  form = XmCreateForm(shell, "confirm_form", args, n); n = 0;

  XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n ++;
  XtSetArg(args[n], XmNrightAttachment, XmATTACH_FORM); n ++;

  menubar = XmCreateMenuBar(form,"confirm_menubar", args, n); n = 0;
  XtManageChild(menubar);

  if(type == 0) {   /* OK|Cancel */
    create_buttons(NULL, menubar, 
		   confirm_menu, 
		   XtNumber(confirm_menu), BTN_ON, 
		   &result, 
		   ROOTMENULEVEL);
  }
  if(type == 1) {   /* Yes|No */
    create_buttons(NULL, menubar, 
		   confirm_yn_menu, 
		   XtNumber(confirm_yn_menu), BTN_ON, 
		   &result, 
		   ROOTMENULEVEL);
  }

  xstr = XmStringCreateSimple(prompt);
  
  XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n ++;
  XtSetArg(args[n], XmNtopWidget, menubar);             n ++;
  XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM);  n ++;
  XtSetArg(args[n], XmNborderWidth, 0);                 n ++;

  XtSetArg(args[n], XmNlabelString,xstr);               n ++;
  label = XmCreateLabel(form,"confirm_lbl",args,n);     n = 0;
  XtManageChild(label);
  XmStringFree(xstr);

  XtManageChild(form);
  XtManageChild(shell);
  XtPopup(shell,XtGrabExclusive); 
  position_popup_widget(shell, TRUE);

  push_cursor(PIRATE_CURSOR);
  confirm_done = 0;
  modal_main_loop(&confirm_done);
				       
  XtPopdown(shell);
  XtDestroyWidget(shell);
  pop_cursor();

  return(result);

}

#ifdef __STDC__
void confirm_ok(Widget w, Boolean *result, XtPointer xp)
#else
void confirm_ok(w, result, xp)
     Widget w;
     Boolean *result;
     XtPointer xp;
#endif
{
  *result = TRUE;
  confirm_done = MODAL_LOOP_DONE;
  return;
}


#ifdef __STDC__
void confirm_cancel(Widget w, Boolean *result, XtPointer xp)
#else
void confirm_cancel(w, result, xp)
     Widget w;
     Boolean *result;
     XtPointer xp;
#endif
{
  *result = FALSE;
  confirm_done = MODAL_LOOP_DONE;
  return;
}


#ifdef __STDC__
char *iso_encode_subject(char *s)
#else
char *iso_encode_subject(s)
     char *s;
#endif
{
  unsigned char *ptr;
  Boolean needs_encoding = FALSE;
  char *ret;
  int linelen = 0;
  char buffer[80];

  if((strcasecmp(preferences.charset,US_ASCII_STR)) == STRMATCH)
    return(cpystr(s));
  for(ptr = (unsigned char *) s; *ptr; ptr ++)
    if(*ptr >= 0x80)
      needs_encoding = TRUE;
  if(needs_encoding == FALSE)
    return(cpystr(s));

  ret = (char *) fs_get((strlen(s) * 5) + 128);
  sprintf(buffer,"=?%s?Q?",preferences.charset);
  linelen = strlen(buffer);
  strcpy(ret,buffer);
  ptr = (unsigned char *) s;
  while((*(ptr)) != NUL_TERM) {
    if((linelen > 70) && ((*(ptr + 1)) != NUL_TERM)) {
      strcat(ret,"?=\n ");
      strcat(ret,buffer);
      linelen = strlen(buffer + 1);
    }
    if(*ptr == SPACECHAR) {
      strcat(ret,"_");
      linelen ++;
      ptr ++;
      continue;
    }
    if((*ptr == '?') || (*ptr == '=') || (*ptr == '_') || (*ptr >= 0x80)) {
      strcat(ret,qhexstr((int) *ptr));
      linelen += 3;
      ptr ++;
      continue;
    }
    strncat(ret,(char *) ptr,1);
    linelen ++;
    ptr ++;
  }
  strcat(ret,"?=");
  return(ret);
}


#ifdef __STDC__
void iso_encode_address(ADDRESS *ad)
#else
void iso_encode_address(ad)
     ADDRESS *ad;
#endif
{

  ADDRESS *curr;
  unsigned char *ptr;
  Boolean needs_encoding = FALSE;
  Boolean in_encoding    = FALSE;
  char *ret;
  unsigned char *comment;
  unsigned char *end_comment;

  int linelen = 0;
  char buffer[80];

  if((strcasecmp(preferences.charset,US_ASCII_STR)) == STRMATCH)
    return;

  for(curr = ad; curr; curr = curr->next) {
    if(curr->personal == NULL)
      continue;
    for(ptr = (unsigned char *) curr->personal; *ptr; ptr++)
      if(*ptr > 0x80)
	needs_encoding = TRUE;
    if(needs_encoding == FALSE)
      continue;

    ret = (char *) fs_get((strlen(curr->personal) * 5) + 128);
    sprintf(buffer,"=?%s?Q?",preferences.charset);

    ptr = (unsigned char *) curr->personal;

    comment = (unsigned char *) strchr((char *) ptr,'(');
    if(comment)
      end_comment = (unsigned char *) strchr((char *) comment,')');

    if(comment == ptr) {
      in_encoding = FALSE;
      linelen = 0;
    }    
    else {
      strcpy(ret,buffer);
      in_encoding = TRUE;
      linelen = strlen(buffer);
    }
    
    while((*(ptr)) != NUL_TERM) {
      if(ptr == comment) {
	if(in_encoding) {
	  strcat(ret,"?= ( ");
	  linelen += 5;
	  in_encoding = FALSE;
	}
	else {
	  strcat(ret," ( ");
	  linelen += 3;
	}
      }
      if(ptr == end_comment) {
	if(in_encoding) {
	  strcat(ret, "?=  ) ");
	  linelen += 5;
	  in_encoding = FALSE;
	}
	else {
	  strcat(ret, " ) ");
	  linelen += 3;
	}
      }
      if((linelen > 70) && ((*(ptr +1)) != NUL_TERM)) {
	if(in_encoding) {
	  strcat(ret,"?=\n ");
	  strcat(ret,buffer);
	  linelen = strlen(buffer + 1);
	}
	else {
	  strcat(ret,"\n ");
	  linelen = 2;
	}
      }
      
      if(*ptr == SPACECHAR) {
	strcat(ret,"_");
	linelen ++;
	ptr ++;
	continue;
      }
      
      if((!isalnum(*ptr)) || (*ptr >= 0x80)) {
	strcat(ret,qhexstr((int) *ptr));
	linelen += 3;
	ptr ++;
	continue;
      }
      strncat(ret,(char *) ptr,1);
      linelen ++;
      ptr ++;
    }
    if(in_encoding)
      strcat(ret,"?= ");
    fs_give((void **) &curr->personal);
    curr->personal = ret;
  }
  return;
}


/* Not necessarily efficient, but it does the job. */

static char qhexbuffer[4];

#ifdef __STDC__
char *qhexstr(int c)
#else
char *qhexstr(c)
  int c;
#endif
{
  sprintf(qhexbuffer,"=%02X",c);
  return(qhexbuffer);
}


/* 
 * do_any is a flag that can be overriden for reading or printing messages
 * by the preferences variable "decode_all". When we use it to reference the
 * absolute message, we restrict decoding to the native charset or us-ascii.
 */

#ifdef __STDC__
char *iso_decode(char **s, Boolean do_any)
#else
char *iso_decode(s,do_any)
     char **s;
     Boolean do_any;
#endif
{
  char *start;
  char *end;
  char *ret;
  char *encoding;
  char *target;
  char *result;
  char *ptr;
  unsigned long newlen;
  char buffer[128];
  char *recurse_ptr;

  if(*s == NULL)
    return(NULL);

  ret = (char *) fs_get(strlen(*s) + 1);
  *ret = NUL_TERM;

  /* Does it have our special encoding tags? */

  if((wildmat(*s,ENCODING_REGEX)) == WILDNOMATCH) {
    strcpy(ret,*s);
    *s = NULL;
    return(ret);
  }

  /* Hmmm. So far so good. Let's see if it's in our character set. */

  start = ML_Strstr(*s,"=?");
  sprintf(buffer,"=?%s?",preferences.charset);

  if((do_any == FALSE) || (preferences.decode_all == FALSE)) {

    if((strncasecmp(start,buffer,strlen(buffer))) != STRMATCH) {
      /* Nope. But let's try US-ASCII. Let's hope they're using ISO-8859 */
      sprintf(buffer,"=?%s?", US_ASCII_STR);
      if((strncasecmp(start,buffer,strlen(buffer))) != STRMATCH) {

	/* Sorry. Can't display it.... */

	strcpy(ret,*s);
	*s = NULL;
	return(ret);
      }
    }
  }

  /* 
   * OK, we've found something. But was there leading cruft?
   * If so, we have to pass that back, and go through this again.
   */

  if(start != (*s)) {
    strncpy(ret,*s,start - (*s));
    ret[start - (*s)] = NUL_TERM;
    *s = start;
    return(ret);
  }

  /* OK. It looks valid. */

  encoding = (strchr(start + 2,'?')) + 1;

  /* Now, we're more or less committed unless the decode fails. */

  end = ML_Strstr(encoding + 2,"?=");

  target = (char *) fs_get(end - (encoding + 1));
  strncpy(target,encoding + 2,end  - (encoding + 2));
  target[end - (encoding + 2)] = NUL_TERM;
  if((*(encoding) == 'q') || (*(encoding) == 'Q')) {
    for(ptr = target; *ptr != NUL_TERM; ptr ++)
	if(*ptr == '_')
	  *ptr = SPACECHAR;
    result = (char *) rfc822_qprint((unsigned char *) target,
				    strlen(target),&newlen);
  }
  else
    result = (char *) rfc822_base64((unsigned char *) target,
				    strlen(target),&newlen);
  fs_give((void **) &target);  /* Through with this one. */

  if(result == NIL) {
    /* 
     * Ack!! It didn't decode. Must'a been corrupted, 'cause they
     * made it this far without problems.
     */
    fs_give((void **) &target);
    strcpy(ret,*s);
    *s = NULL;
    return(ret);
  }

  /* 
   * Otherwise, we've done our job. Now to clean up.  We have to point
   * at the end so they can come back through and catch any trailing
   * cruft or yet more encodings. Also, to be consistent with the
   * spec, we ignore whitespace up to a subsequent encoding.
   */

  end += 2;
  recurse_ptr = end;

  while((*recurse_ptr != NUL_TERM) && (isspace(*recurse_ptr)))
    recurse_ptr ++;

  if((wildmat(recurse_ptr,CONTINUE_REGEX)) == WILDMATCH)
    *s = recurse_ptr;
  else
    *s = end;
  fs_give((void **) &ret);
  return(result);
}


TagPair HTMLtags[] = {
  "<P>", "\n",
  "<BR>", "\n",
  "<LI>", "\n\t",
  "<UL>", "\n",
  "<TR>", "\n",
  "</BLOCKQUOTE>", "\n-----\n",
  NULL, NULL
};

TagPair AMPtags[] = {
  "&nbsp;", " ",
  "&amp;", "&",
  "&lt;", "<",
  "&gt;", ">",
  "&copy;", "\251",
  NULL, NULL
};
  

#ifdef __STDC__
char * strip_html(char *src)
#else
char *strip_html(src)
char *src;
#endif
{

  char *ret = NULL;
  char *sptr = src;
  char *rptr = NULL;
  int tptr;
  int preformatted = 0;

  if((src == NULL) || (*src == NUL_TERM))
    return ret;

  rptr = ret = fs_get(strlen(src) + FILEBUFFLEN);
  
  while(*sptr  != NUL_TERM) {
    if(*sptr == '\n' && !preformatted) {
      *rptr++ = ' ';
      *sptr++;
      continue;
    }
    if(*sptr != '<' && *sptr != '&') {
      *rptr++ = *sptr++;
      continue;
    }

    if((preformatted) && ((strncasecmp(sptr,"</PRE>",6)) == STRMATCH))
	preformatted = 0;

    if(*sptr == '&') {
      for(tptr = 0; AMPtags[tptr].tag != NULL; tptr ++) {
	if((strncasecmp(sptr,
			AMPtags[tptr].tag,
			strlen(AMPtags[tptr].tag))) == STRMATCH) {
	  strcpy(rptr,AMPtags[tptr].value);
	  rptr += strlen(AMPtags[tptr].value);
	  break;
	}
      }
      /* advance to end of symbol */
      while((*sptr != NUL_TERM) && (*sptr != ';'))
	sptr ++;
      if(*sptr) 
	sptr ++;
      continue;
    }

    if(*sptr == '<') {
      for(tptr = 0; HTMLtags[tptr].tag != NULL; tptr ++) {
	if((strncasecmp(sptr,
			HTMLtags[tptr].tag,
			strlen(HTMLtags[tptr].tag))) == STRMATCH) {
	  strcpy(rptr,HTMLtags[tptr].value);
	  rptr += strlen(HTMLtags[tptr].value);
	  sptr ++; /* advance past tag start so no more matches */
	  break;
	}
      }
      /* fall through to find tag end */

      /* special cases: */

      /* link - retain the href */

      if((strncasecmp(sptr,"<A ",3)) == STRMATCH) {
	while((*sptr != NUL_TERM) && (*sptr != '>')) {
	  if((strncasecmp(sptr," HREF=",6)) == STRMATCH) {
	    sptr += 6;
	    *rptr++ = '<';
	    while((*sptr != NUL_TERM) && (*sptr != ' ') && (*sptr != '>'))
	      *rptr++ = *sptr++;
	    *rptr++ = '>';
	    continue;
	  }
	  else
	    sptr++;
	}
	if(*sptr) sptr ++;
	continue; /* do not fall through */
      }

      /* blockquote - pretty it up a bit */

      if((strncasecmp(sptr,"<BLOCKQUOTE",11)) == STRMATCH) {
	strcpy(rptr,"\n-----\n");
	rptr += 7;
	/* fall through to find tag end */
      }

      /* paragraph tags with args, very common */

      if((strncasecmp(sptr,"<P ",3)) == STRMATCH)
	*rptr++ = '\n';
      /* fall through to find tag end */

      /* preformatted text - flag it for special handling */

      if(((strncasecmp(sptr,"<PRE>",5)) == STRMATCH)
	 || ((strncasecmp(sptr,"<PRE ",5)) == STRMATCH))
	preformatted = 1;


      /* catch fall throughs and all other unrecognized tags */
      while((*sptr != NUL_TERM) && (*sptr != '>'))
	sptr ++;
      if(*sptr) sptr ++;
      continue;
    }
  }

  *rptr = NUL_TERM;
  return ret;  

}




/*
**  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
**  Rich $alz is now <rsalz@osf.org>.
*/


#define MATCH_ABORT			-1

    /* What character marks an inverted character class? */
#define NEGATE_CLASS		'^'
    /* Is "*" a common pattern? */
#define OPTIMIZE_JUST_STAR
    /* Do tar(1) matching rules, which ignore a trailing slash? */
#undef MATCH_TAR_PATTERN


/*
**  Match text and p, return WILDMATCH, WILDNOMATCH, or MATCH_ABORT.
*/

#ifdef __STDC__
int DoMatch(register char *text, register char *p)
#else
int DoMatch(text, p)
    register char	*text;
    register char	*p;
#endif
{
    register int	last;
    register int	matched;
    register int	reverse;

    for ( ; *p; text++, p++) {
	if (*text == '\0' && *p != '*')
	    return MATCH_ABORT;
	switch (*p) {
	case '\\':
	    /* Literal match with following character. */
	    p++;
	    /* FALLTHROUGH */
	default:
	    if (*text != *p)
		return WILDNOMATCH;
	    continue;
	case '?':
	    /* Match anything. */
	    continue;
	case '*':
	    while (*++p == '*')
		/* Consecutive stars act just like one. */
		continue;
	    if (*p == '\0')
		/* Trailing star matches everything. */
		return WILDMATCH;
	    while (*text)
		if ((matched = DoMatch(text++, p)) != WILDNOMATCH)
		    return matched;
	    return MATCH_ABORT;
	case '[':
	    reverse = p[1] == NEGATE_CLASS ? WILDMATCH : WILDNOMATCH;
	    if (reverse)
		/* Inverted character class. */
		p++;
	    matched = WILDNOMATCH;
	    if (p[1] == ']' || p[1] == '-')
		if (*++p == *text)
		    matched = WILDMATCH;
	    for (last = *p; *++p && *p != ']'; last = *p)
		/* This next line requires a good C compiler. */
		if (*p == '-' && p[1] != ']'
		    ? *text <= *++p && *text >= last : *text == *p)
		    matched = WILDMATCH;
	    if (matched == reverse)
		return WILDNOMATCH;
	    continue;
	}
    }

#ifdef	MATCH_TAR_PATTERN
    if (*text == '/')
	return WILDMATCH;
#endif	/* MATCH_TAR_PATTERN */
    return *text == '\0';
}


/*
**  User-level routine.  Returns WILDMATCH or WILDNOMATCH.
*/

#ifdef __STDC__
int wildmat(char *text, char *p)
#else
int wildmat(text, p)
    char	*text;
    char	*p;
#endif
{
#ifdef	OPTIMIZE_JUST_STAR
    if (p[0] == '*' && p[1] == '\0')
	return WILDMATCH;
#endif	/* OPTIMIZE_JUST_STAR */
    return DoMatch(text, p) == WILDMATCH;
}




#ifdef __STDC__
void free_binary_buffer(Binary_Buffer *binary_buffer)
#else
void free_binary_buffer(binary_buffer)
     Binary_Buffer *binary_buffer;
#endif
{
  if(binary_buffer) {
    if(binary_buffer->data)
      fs_give((void **) &binary_buffer->data);
    fs_give((void **) &binary_buffer);
  }
  return;
}

#ifdef __STDC__
String_List *new_string_list(void)
#else
String_List *new_string_list()
#endif
{
  String_List *string_list = (String_List *) fs_get(sizeof(String_List));

  string_list->string      = NULL;
  string_list->next        = NULL;

  return(string_list);
}

#ifdef __STDC__
void free_string_list(String_List *string_list, Boolean recurse)
#else
void free_string_list(string_list,recurse)
     String_List *string_list;
     Boolean recurse;
#endif
{
  if(string_list == NULL)
    return;
  if(recurse)
    free_string_list(string_list->next, recurse);

  if(string_list->string != NULL)
    fs_give((void **) &string_list->string);

  fs_give((void **) &string_list);
  return;
}


/* One heck of a tough problem. Reformat the original text, 
   preserving reply carets.
*/

#ifdef __STDC__
char *copywrap(char *s, int wrapcols)
#else
char *copywrap(s, wrapcols)
     char * s;
     int wrapcols;
#endif
{
  char *last, *temp;
  char *raw, *dst;
  char **strtab;
  int lines;
  unsigned long dstlen = 0L;
  unsigned long size = 0L;
  Boolean prefixed = FALSE;
  Boolean wrapped = FALSE;
  int tabcount;
  int cnt;
  char *ptr;
  int wraplen = 0;

  if(s == NULL)
    return(cpystr(EMPTYSTR));

  size = strlen(s) + 1024;

  dst = (char *) fs_get(size);
  *dst = NUL_TERM;
  last = (char *) fs_get(wrapcols + 100);
  temp = (char *) fs_get((2 * wrapcols) + 100);
  *last = *temp = NUL_TERM;


  raw = cpystr(s);
  strtab = build_line_table(raw);

  for(lines = 0; strtab[lines]; lines ++)
    strtab[lines] = untabify(strtab[lines]);


  for(lines = 0; strtab[lines]; lines ++) {

    if(dstlen >= size)
      dst = realloc(dst, (size += 1024));
    if(! strlen(strtab[lines])) {
      strcat(dst,LFSTR);
      dstlen ++;
      if(wrapped) {
	strcat(dst,LFSTR);
	dstlen ++;
	*last = NUL_TERM;
	wrapped = 0;
	wraplen = 0;
      }
      continue;
    }
    if((! wrapped) && ((strlen(strtab[lines])) < wrapcols)) {
      strcat(dst,strtab[lines]);
      strcat(dst, LFSTR);
      dstlen += strlen(strtab[lines]) + 1;
      continue;
    }

    /* If we got here, the line needs wrapping */

    if(! wrapped) {
      wrapped = TRUE;
                                /* stash the prefix for comparison */
      strncpy(last,strtab[lines],wrapcols);
      last[wrapcols] = NUL_TERM;
      for(cnt = 0; last[cnt]; cnt ++) {
	if((last[cnt] != '>') && (last[cnt] != SPACECHAR)) {
	  last[cnt] = NUL_TERM;
	  break;
	}
      }

      strncpy(temp,strtab[lines],wrapcols);
      temp[wrapcols] = NUL_TERM;
                           /* find the last useable space */
      for(ptr = temp + (wrapcols - 1); *ptr != SPACECHAR; ptr --)
	;
      if(ptr <= temp) {      /* no spaces found. */
	strcat(dst,strtab[lines]);
	strcat(dst, LFSTR);
	dstlen += strlen(strtab[lines]) + 1;
	wraplen = 0;
	wrapped = FALSE;
	continue;
      }
      else {         /* spit out this line */
	strncat(dst, temp, ptr - temp);
	strcat(dst,LFSTR);
	dstlen += ((ptr - temp) + 1);
	             /* start the next line with a prefix if any */
	             /* We'll ignore a tab (now spaces) as a prefix. */
	if(strcmp(last,"     ")) {
	  strcat(dst,last);
	  dstlen += strlen(last);
	  wraplen = strlen(last);
	}
	else
	  strcpy(last,EMPTYSTR);

	             /* spit out the remaining stuff */
	strcat(dst,&strtab[lines][(ptr - temp) + 1]);
	strcat(dst,SPACESTR);
	dstlen += (strlen(strtab[lines]) - (ptr - temp)) + 1;
	wraplen += (strlen(strtab[lines]) - (ptr - temp)) + 1;
	continue;
      }
    }
    else {      /* already wrapped. second or 'n' line to wrap */
                /* If it doesn't have the same prefix, we're done wrapping. */
      if((strncmp(strtab[lines],last,strlen(last))) != STRMATCH) {
	strcat(dst,LFSTR);
	wrapped = FALSE;
	wraplen = 0;
	dstlen += 1;
	lines --;       /* push this line back to re-evaluate */
	continue;
      }
      else {                                /* keep wrapping */
	                /* we've already got the prefix. */
	strncpy(temp,&strtab[lines][strlen(last)],wrapcols);
	temp[wrapcols] = NUL_TERM;
	                /* is THIS line too  long ? */
	if(((strlen(temp)) + wraplen) >= wrapcols)
	  for(ptr = temp + (wrapcols - wraplen - 1); 
			    *ptr != SPACECHAR; ptr --)
	      ;
	else {    /*  It fits just fine. */ 
	  ptr = temp + strlen(temp);
	  strncat(dst, temp, ptr - temp);
	  strcat(dst,LFSTR);
	  dstlen += ((ptr - temp) + 1);
	  wraplen = 0; /* += ((ptr - temp) + 1); */
	  wrapped = FALSE;
	  continue;
	}
	if(ptr <= temp) {          /* it just won't fit... */
	  strcat(dst,&strtab[lines][strlen(last)]);
	  strcat(dst, LFSTR);
	  dstlen += strlen(strtab[lines]) + strlen(last) + 1;
	  wraplen = 0;
	  wrapped = FALSE;
	  continue;
	}
	else {       /* spit out what we've got and end this line. */
	  strncat(dst, temp, ptr - temp);
	  strcat(dst,LFSTR);
	  dstlen += ((ptr - temp) + 1);
                     /* start a new line. */
	  strcat(dst,last);
	  dstlen += strlen(last);
	  wraplen = strlen(last);
                    /* spit out the rest of this line. */
	  strcat(dst,&strtab[lines][(ptr - temp) + strlen(last) + 1]);
	  strcat(dst,SPACESTR);
	  dstlen += strlen(&strtab[lines][(ptr - temp) + strlen(last) + 1]);
	  wraplen += strlen(&strtab[lines][(ptr - temp) + strlen(last) + 1]);
	  continue;
	}

      }

    }

  }

  /* go home */

  for(lines = 0; strtab[lines]; lines ++)
    fs_give((void **) &strtab[lines]);
  free(strtab);


  fs_give((void **) &last);
  fs_give((void **) &temp);
  fs_give((void **) &raw);
  return(dst);
}


#ifdef __STDC__
char *untabify(char *s)
#else
char *untabify(s)
     char *s;
#endif
{
  int i = 0;
  int y;
  char *src;
  char *ret = fs_get((5 * strlen(s)) + 1);
  char *dst = ret;
  for(src = s; *src; src ++) {
    if(*src == TABCHAR) {
      for(i = 0; i < 5; i ++) {
	*dst = SPACECHAR;
	dst ++;
      }
    }
    else
      *dst++ = *src;
  }
  *dst = NUL_TERM;
  return(ret);
}


#ifdef __STDC__
char **build_line_table(char *s)
#else
char **build_line_table(s)
     char *s;
#endif
{
  int lines = count_lines(s);
  char *start = s;
  char *end = NULL;
  char **ret;
  int count = 0;

  ret = (char **) fs_get(((lines) ? (lines + 1) : 1) * sizeof(char *));
  while((ret[count] = get_next_strline(start, &end)) != NULL) {
    count ++;
    start = end;
  }
  ret[count] = NULL;
  return(ret);
}





#ifdef __STDC__
char *get_next_strline(char *s, char **end)
#else
char *get_next_strline(s,end)
  char *s;
  char **end;
#endif
{
  char *ptr;
  if(*s == NUL_TERM) {
    *end = NULL;
    return(NULL);
  }
  if((ptr = strchr(s,LFCHAR)) != NULL) {
    *ptr = NUL_TERM;
    *end = ptr +1;
    return(s);
  }
  *end = s + strlen(s);
  return(s);
}



#ifdef __STDC__
char *add_prefix(char *s)
#else
char *add_prefix(s)
     char * s;
#endif
{
  int carets = 0;
  unsigned nlength = 0;
  char *temp = NULL;
  char *insert = NULL;
  int  prefixLength = strlen(preferences.reply_prefix);

  carets = count_lines(s);
  nlength = (carets*prefixLength) + strlen(s); 
  insert = XtMalloc(nlength+1);

  strcpy(insert, preferences.reply_prefix);
  temp = insert + strlen(insert);
  while( *s ) {
    *temp++ = *s;
    if( (*s == LFCHAR) && (*(s+1) != NUL_TERM)){
      strncpy(temp, preferences.reply_prefix, prefixLength );
      temp += prefixLength;
    }
    s++;
  }
  *temp = NUL_TERM;
  return(insert);
}









syntax highlighted by Code2HTML, v. 0.9.1