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

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.37 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> (was hurtta+elm@ozone.FMI.FI)
 *****************************************************************************/

#include "headers.h"

DEBUG_VAR(Debug,__FILE__,"ui");

static unsigned char *s2us P_((char *str));
static unsigned char *s2us(str) 
     char *str;
{
    return (unsigned char *)str;
}
static char *us2s P_((unsigned char *str));
static char *us2s(str) 
     unsigned char *str;
{
    return (char *)str;
}

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


static int def_err_handler P_((CONST char *str));
static int def_err_handler (str) 
     CONST char *str;
{
  int ret;
  int l = strlen(str);
  ret = fprintf(stderr,"%s",str);
  if (ret > 0 && l && str[l-1] != '\n')
    ret += fprintf(stderr,"\n");
  return ret;
}

static err_handler *H = def_err_handler;
extern void set_error_handler (h)
     err_handler *h;
{
    H = h;
}

static err_handler *T = def_err_handler;
void set_transient_handler(h)
     err_handler *h;
{
    T = h;
}

static char * def_prompt_handler P_((const char *str, int pass));
static char * def_prompt_handler (str,pass) 
     CONST char *str;
     int pass;
{
  int l = strlen(str);
  char buffer[200], *x;
  
  FILE * f = fopen("/dev/tty","r+");
  if (!f)
      return NULL;
  if (pass)
      x = getpass(str);
  else {
      int ret = fprintf(f,"%s",str);
      char *y;
      fflush(f);
      x = fgets(buffer,sizeof buffer,f);
      if (x && (y = strchr(x,'\n')))
	  *y = '\0';
      if (ret > 0 && l && str[l-1] != '\n')
	  ret += fprintf(f,"\n");
  }
  fclose(f);
  if (!x)
      return NULL;
  return safe_strdup(x);
}

static prompt_handler *P = def_prompt_handler;
void set_prompt_handler(h)
     prompt_handler *h;
{
    P = h;
}

/* Returns number of args parsed: -1 is parse error,
   - this routine can be called from interrupt handler,
     so this does not malloc anything or call charset routines ...
   - this routine is also part of debugging output routine,
     so do not call debugging output on here...
 */

int parse_format_args (elems,max_elems,format,args,format_error)
     struct  format_elem *elems;
     int              max_elems;
     CONST char        * format;
     va_list               args;
     char        **format_error;
{
    CONST char * s;
    int idx;


#define INC(p) { (p)++; if (!*(p)) break; }

    *format_error = NULL;
    idx = 0;

    for (s = format; *s; s++) {

	/* It is better that program code
	   is ascii only. 

	   Non ascii text may be given on catalog,
	   but it should be on 'msg' parameter'
	*/

	if (!isascii(*s)) {
	    *format_error = "NONASCII: Non ascii characters on format";
	}
	
	if (*s != '%') {
	    continue;
	}
	INC(s);
	
	if ('%' == *s) {
	    continue;
	}

	if (idx >= max_elems) {
	    *format_error = "OVERFLOW: Too many format elements";
	    break;
	}
	elems[idx].fill   = ' ';
	elems[idx].left   = 0;
	elems[idx].plus   = 0;
	elems[idx].val1   = 0;
	elems[idx].val2   = 0;
	elems[idx].long_f = 0;
	
	if ('0' == *s)      { elems[idx].fill = '0'; INC(s); }
	else if ('-' == *s) { elems[idx].left = 1;   INC(s); }
	else if ('+' == *s) { elems[idx].plus = 1;   INC(s); }

	if (*s >= '1' && *s <= '9') {
	    while (*s >= '0' && *s <= '9') {
		elems[idx].val1 = elems[idx].val1 * 10 + *s - '0';
		INC(s);
	    }
	    
	    if (*s == '$') {
		elems[idx].val1 = 0;
		INC(s);
		*format_error = "Positional args are not allowed in format";
			   
		return -1;
	    }
	    
	} else if ('*' == *s) { 
	    elems[idx].val1 = va_arg(args,int);
	    INC(s);
	}
	
	if ('.' == *s) {
	    INC(s);
	    if (*s >= '1' && *s <= '9') {
		while (*s >= '0' && *s <= '9') {
		    elems[idx].val2 = elems[idx].val2 * 10 + *s - '0';
		    INC(s);
		}
	    } else if ('*' == *s) { 
		elems[idx].val2 = va_arg(args,int);
		INC(s);
	    }      
	}
	
	if ('l' == *s) { 
	    elems[idx].long_f = 1;
	    INC(s);
	}
	
	elems[idx].format_chr = *s;
	switch(*s) {
	case 'c':
	    elems[idx].type = V_chr_val;
	    elems[idx].value.chr_val = va_arg(args, int);    
	    break;
	case 'C':
	    elems[idx].type         = V_cs_val;
	    elems[idx].value.cs_val = va_arg(args, struct charset_state *);
	    break;
	case 'S':
	    elems[idx].type = V_string_val;
	    elems[idx].value.string_val = va_arg(args, struct string *);
	    break;
	case 'p':
	    elems[idx].type = V_ptr_val;
	    elems[idx].value.ptr_val = va_arg(args, void *);
	    break;
	case 's': case 'Q':
	    elems[idx].type = V_str_val;
	    elems[idx].value.str_val = va_arg(args, char *);
	    break;
	case 'f':
	    elems[idx].type = V_double_val;
	    elems[idx].value.double_val = va_arg(args, double);
	    break;	    
	case 'd':  case 'i':
	    elems[idx].type = V_signed_val;
	    if (elems[idx].long_f) 
		elems[idx].value.signed_val = va_arg(args, long);
	    else
		elems[idx].value.signed_val = va_arg(args, int);
	    break;
	case 'x': case 'X': case 'u': case 'o':
	    elems[idx].type = V_unsigned_val;
	    if (elems[idx].long_f) 
		elems[idx].value.unsigned_val = va_arg(args, unsigned long);
	    else
		elems[idx].value.unsigned_val = va_arg(args, unsigned int);
	    break;	    
	default:
	    elems[idx].type = V_none;
	    *format_error = "Bad format character in format";
	    return -1;
	}	
	idx++;
    }

    return idx;
}

/* convert_number may be callled form signal handler, therefore
   it must not malloc annything ... 
*/


int convert_number (buffer,buffer_size,elem)
     char * buffer; 
     int buffer_size; 
     struct  format_elem *elem;
{
    int base   = 0;
    char *seq  = NULL;
    int c = 0;
    int x;


    unsigned long valz, valu;
    unsigned long r;
    double   desimal = 0;

    long val;
    int I = 0;
    int l = 0;

    
#define PUT(c) \
   if (I < buffer_size -1) buffer[I++] = (c); \
   else { buffer[I-1] = '*'; buffer[I] = '\0'; return I; }

   if (buffer_size < 2) 
       return 0;   

   switch(elem->format_chr) {
   case 'd': seq = "0123456789"; base = 10; break;
   case 'i': seq = "0123456789"; base = 10; break;
   case 'u': seq = "0123456789"; base = 10; break;
   case 'o': seq = "01234567";   base = 8; break;
   case 'x': seq = "0123456789abcdef"; base = 16; break;
   case 'X': seq = "0123456789ABCDEF"; base = 16; break;
   case 'p': seq = "0123456789ABCDEF"; base = 16; break;
   case 'f': seq = "0123456789"; base = 10; break;  

   default:
       return 0;       
   }
    
   if (V_signed_val == elem->type) {
       val = elem->value.signed_val;
       if (val < 0) {
	   c = '-'; 
	   valu = -val;
	   elem->val1--;
       } else {
	   valu = val;
	   if (elem->plus) {
	       c = '+'; 
	       elem->val1--;			
	   }
       }
   } else if (V_double_val == elem->type) {
       desimal = elem->value.double_val;
       if (desimal < 0) {
	   c = '-';
           desimal = -desimal;
           elem->val1--;
       } else {
	   if (elem->plus) {
	       c = '+'; 
	       elem->val1--;			
	   }
       }

       valu = (unsigned int)desimal;

       desimal -= valu;

       if (desimal < 0)        /* ??? */
	   desimal = 0;

       l++;   /* Desimal point */

       if (elem->val2 > 0)
	   l += elem->val2; /* Desimals */


   } else if (V_unsigned_val == elem->type) {
       valu = elem->value.unsigned_val;		
       if (elem->plus) {
	   c = '+'; 
	   elem->val1--;			
       }
   } else if (V_ptr_val == elem->type) {
       valu = (unsigned long)elem->value.ptr_val;		
   } else {
       return 0;
   }
   
    valz = valu;
    x = buffer_size;
    do {
	unsigned long tmp;

	tmp = (valz % base);
	r   = (valz / base);
	valz = r;
	l++;
	
	x--;

	buffer[x] = seq[tmp];

    } while(r && l < buffer_size -3 && x > I);


    /* This can overwrite buffer ... */

    while (l < elem->val1 && 
	   !elem->left && elem->fill != '0') { 
	PUT(elem->fill); l++; 
    }

    if (c) { /* sign */
	PUT(c);
    }

    while (l < elem->val1 && 
	   ! elem->left && elem->fill == '0') { 
	PUT('0');  l++; 
    }
   
    while(x < buffer_size) {
	if (x < I) {   /* Indicate OVERFLOW */
	    PUT('*');
	} else {
	    PUT(buffer[x]);
	}
	x++;
    } 

    /* Desimal value ...  l */
    if (V_double_val == elem->type) {
	PUT('.');

	while (elem->val2 > 0) {
	    unsigned long tmp;
	    
	    desimal *= 10;

	    tmp = (unsigned long) desimal;

	    desimal -= tmp;

	    if (desimal < 0)        /* ??? */
		desimal = 0;

	    if (tmp > 10) {
		PUT('?');
	    } else {
		PUT(seq[tmp]);
	    }

	    elem->val2--;
	}
    }

    while (l < elem->val1 && elem->left) { 
	PUT(' '); l++; 
    }

    if (0 == I) {
	PUT('?');
    }

#undef PUT
    
    buffer[I] = '\0'; 

    return I;
}

#define MAX_WIDTH 64

#ifndef NOCHARSET   /* Used by nls/gencat */

char *elm_message P_((const char * format, const char *msg, ...));
int elm_sfprintf P_((char *buffer, int size,
		    const char * format, const char *msg, ...));


struct string * elm_smessage P_((int max_alloc,
				 const char *format, const char *msg,
				 va_list args));

struct string * elm_smessage(max_alloc,format,msg,args)
     int max_alloc;
     CONST char *format;
     CONST char *msg;
     va_list args;
{
    CONST unsigned char * s;   /* add_streambyte_to_string requires
				  unsigned char */
    struct string * store1 = NULL, * msg1 = NULL;
    charset_t msg_charset  = display_charset;

    struct string ** store2 = NULL;
    int store2_len = 0,i,pos;

    int max_elems = 0;
    struct  format_elem *elems = NULL;
    int elem_count, idx;
    char * format_error = NULL;

    DPRINT(Debug,62,(&Debug,   
		     "elm_smessage: max_alloc=%d, format='%s', msg='%s'\n",
		     max_alloc,format,msg));
    
    for (s = cs2us(format); *s; s++) 
	if ('%' == *s && '%' != *(s+1))
	    max_elems++;
    DPRINT(Debug,62,(&Debug,   
		     "elm_smessage: max_elems=%d\n",max_elems));

    if (max_elems > 0) 
	elems = safe_malloc(max_elems * sizeof (*elems));
    else
	elems = NULL;

    elem_count = parse_format_args(elems, max_elems, format,args,
				   &format_error);
    
    DPRINT(Debug,62,(&Debug,   
		     "elm_smessage: elem_count=%d\n", elem_count));
    if (format_error) {
	DPRINT(Debug,1,(&Debug,   
			"FORMAT ERROR (%s): %s\n",format,format_error));
    } 
	   
    /* If there is error, elem_count == -1 and therefore loop is not run */

    for (idx = 0; idx < elem_count; idx++) {
	        
	struct string * param_value = NULL;
		     	
	DPRINT(Debug,63,(&Debug,   
			 "#  %d [%d.%d %c]: \n",
			 idx,elems[idx].val1,elems[idx].val2,
			 elems[idx].format_chr));
	    

	switch(elems[idx].format_chr) {
	    CONST char *str, *a;
	    
	    int l,c;

	    int quote,X;
	    struct string * S, *S2, *S3;
	    char width[MAX_WIDTH+1];
	    char * stemp, *sptr;
	    struct charset_state * ch;
	    int I;
	    void * valp;
	    char DUMMY[256];
	    
	case 'c':
	    if (V_chr_val == elems[idx].type)
		c = elems[idx].value.chr_val;
	    else {
		DPRINT(Debug,63,(&Debug,   
				 "   %d [c] ERROR\n",idx));
		c = '?';
	    }
	    param_value = new_string(display_charset);
	    
	    DPRINT(Debug,63,(&Debug,   
			     "-- %d [c] val1=%d, c=%c, param_value=%p\n",
			     idx,elems[idx].val1,c,param_value));
	    l = 1;
	    if (l < elems[idx].val1 && !elems[idx].left) { 
		fill_ascii_to_string(param_value,
				     elems[idx].val1-l,
				     elems[idx].fill);
		l += elems[idx].val1-l; 
	    }
	    add_streambyte_to_string(param_value,c);
	    if (l < elems[idx].val1 && elems[idx].left) { 
		fill_ascii_to_string(param_value,elems[idx].val1-l,' ');
		l += elems[idx].val1-l; 
	    }
	    break;
	case 'C':
	    if (V_cs_val == elems[idx].type)
		ch = elems[idx].value.cs_val;
	    else {
		DPRINT(Debug,63,(&Debug,   
				 "   %d [C] ERROR\n",idx));
		goto error1;
	    }
	    param_value = new_string(ch->charset);

	    DPRINT(Debug,63,(&Debug,   
			     "-- %d [C] val1=%d, param_value=%p\n",
			     idx,elems[idx].val1,param_value));

	    l = 1;
	    if (l < elems[idx].val1 && !elems[idx].left) { 
		fill_ascii_to_string(param_value,
				     elems[idx].val1-l,
				     elems[idx].fill);
		l += elems[idx].val1-l; 
	    }
	    add_state_to_string(param_value,ch);
	    if (l < elems[idx].val1 && elems[idx].left) { 
		fill_ascii_to_string(param_value,elems[idx].val1-l,' ');
		l += elems[idx].val1-l; 
	    }
	    break;

	case 'S':
	    if (V_string_val == elems[idx].type) 
		S = elems[idx].value.string_val;
	    else {
		DPRINT(Debug,63,(&Debug,   
				 "   %d [S] ERROR\n",idx));
		goto error1;		
	    }
	    if (!verify_string(S)) {
		DPRINT(Debug,1,(&Debug,   
				"elm_smessage: BAD %%S argument\n"));
		goto error1;
	    }

	    l = string_len(S);
	    if (elems[idx].val2 <= 0) elems[idx].val2 = l;
	    
	    X = 0;
	    if (max_alloc > 0 && elems[idx].val2 > max_alloc) {
		DPRINT(Debug,62,(&Debug,   
				 "--  adjusting val2 =%d -> %d (max_alloc)\n",
				 elems[idx].val2,max_alloc));
		elems[idx].val2 = max_alloc;
	    }
	    S2  = clip_from_string(S,&X,elems[idx].val2);
	    
	    DPRINT(Debug,63,(&Debug,   
			     "-- %d [S] val1=%d, S=%S, val2=%d,(clip)S2=%S\n",
			     idx,elems[idx].val1,S,elems[idx].val2,S2));
	    S3 = new_string(S->string_type);
	    
	    if (l < elems[idx].val1 && !elems[idx].left) { 
		fill_ascii_to_string(S3,
				     elems[idx].val1-l,
				     elems[idx].fill);
		l += elems[idx].val1-l; 
	    }
	    
	    param_value = cat_strings(S3,S2,0);
	    DPRINT(Debug,63,(&Debug,   
			     "--   param_value=%p\n",param_value)); 
	    
	    free_string(&S2);
	    free_string(&S3);
	    
	    if (l < elems[idx].val1 && elems[idx].left) { 
		fill_ascii_to_string(param_value,elems[idx].val1-l,' ');
		l += elems[idx].val1-l; 
	    }
	    break;
	    
	case 'p':
	    if (V_ptr_val == elems[idx].type)		
		valp = elems[idx].value.ptr_val;
	    else {
		DPRINT(Debug,63,(&Debug,   
				 "   %d [p] ERROR\n",idx));
		goto error1;		
	    }
	    sprintf(DUMMY,"%p",valp);
	    str = DUMMY;
	    goto jump_dummy;

	case 's': case 'Q':
	    if (V_str_val == elems[idx].type)		
		str = elems[idx].value.str_val;
	    else {
		DPRINT(Debug,63,(&Debug,   
				 "   %d [%c] ERROR\n",
				 idx,elems[idx].format_chr));
		goto error1;
	    }
	    if (!str)
		goto error1;

	jump_dummy:
	    quote = ('Q' == elems[idx].format_chr);
	    
	    l = strlen(str);
	    if (quote) {
		l += 2;
		for (a=str; *a; a++) {
		    if (*a == '\\' || *a == '"')
			l++;
		}
	    }

	    if (elems[idx].val2 <= 0) elems[idx].val2 = l;
	    if (quote)
		elems[idx].val2 -= 2;
	    if (max_alloc > 0 && elems[idx].val2 > max_alloc) {
		DPRINT(Debug,62,(&Debug,   
				 "--  adjusting val2 =%d -> %d (max_alloc)\n",
				 elems[idx].val2,max_alloc));
		elems[idx].val2 = max_alloc;
	    }

	    stemp = safe_malloc(l+2);

	    sptr = stemp;
	    for (a = str; 
		 *a && a - str < elems[idx].val2 && sptr - stemp < l; a++) { 
		if (quote && (*a == '\\' || *a == '"')) {
		    *sptr++ = '\\'; 
		}
		*sptr++ = *a;
	    }
	    *sptr = '\0';
	    S2 = new_string2(display_charset,s2us(stemp));
  
	    DPRINT(Debug,63,(&Debug,   
			     "-- %d [%c] val1=%d, val2=%d, str=%s, ",
			     idx,elems[idx].format_chr,
		       elems[idx].val1,elems[idx].val2,str));
	    DPRINT(Debug,63,(&Debug,   
			     "S2=%p, stemp=%s\n",S2,stemp));
	    free(stemp); stemp = NULL;

	    S3 = new_string(display_charset);	    

	    if (l < elems[idx].val1 && !elems[idx].left) { 
		fill_ascii_to_string(S3,elems[idx].val1-l,elems[idx].fill);
		l += elems[idx].val1-l; 
	    }

	    if (quote) {
		add_ascii_to_string(S3,s2us("\"")); 
	    }

	    param_value = cat_strings(S3,S2,0);
	    DPRINT(Debug,63,(&Debug,   
			     "--   param_value=%p\n",param_value)); 

	    free_string(&S2);
	    free_string(&S3);

	    if (quote) {
		add_ascii_to_string(param_value,s2us("\"")); 
	    }

	    if (l < elems[idx].val1 && elems[idx].left) { 
		fill_ascii_to_string(param_value,elems[idx].val1-l,' ');
		l += elems[idx].val1-l; 
	    }
	    break;
	    
	case 'd': case 'i': case 'x': case 'X': case 'o': case 'u':
	case 'f':
	    
	    
	    I = convert_number(width, sizeof width,  & (elems[idx]));
	    
	    if (0 == I) {
		DPRINT(Debug,63,(&Debug,   
				 "-- %d [%c]  I=%d, ERROR\n",
				 idx,elems[idx].format_chr,I));
		goto error1;
	    }
	    
	    DPRINT(Debug,63,(&Debug,   
			     "-- %d [%c]  I=%d, buffer=%s\n",
			     idx,elems[idx].format_chr,I,width));

	    param_value = new_string(display_charset);	    	   	    
	    add_ascii_to_string(param_value,s2us(width));
	    
	    break;
	default:
	    DPRINT(Debug,1,(&Debug,   
			    "elm_smessage: BAD %d [%c] ????\n",
			    idx,elems[idx].format_chr));
	error1:
	    param_value = new_string(display_charset);
	    add_ascii_to_string(param_value,s2us("[?-ERROR-?]"));
	    DPRINT(Debug,63,(&Debug,   
			     " --- error, param_value=%p\n",
			     param_value));
	}
	store2 = safe_realloc(store2, (store2_len+1) * 
			      sizeof (struct string *)); 
	store2[store2_len++] = param_value; 
	DPRINT(Debug,63,(&Debug,   
			 " -- adding param[%d] = %p\n", 
			 store2_len-1,store2[store2_len-1])); 
    }

    if (elems) {
	free(elems);
	elems = NULL;
    }

    for (i = 0; i < store2_len; i++) {
	DPRINT(Debug,63,(&Debug,   
			 "  - %d: %S\n",
			 i,store2[i]));
    }

    pos = -1;
    /* Actual printing */
    for (s = cs2us(msg); *s; s++) {
	int val1 = 0;
	if (*s != '%') {
	add_character:                 /* Jump position for %% */
	    
	    if (!msg1) {
		msg1 = new_string(msg_charset);
		DPRINT(Debug,63,(&Debug,   
				 "** msg_charset={'%s'; type=%p }, msg1=%p\n",
				 msg_charset->MIME_name ? msg_charset->MIME_name : "<none>",
				 msg_charset->charset_type,
				 msg1));		
	    }
	    
	    add_streambyte_to_string(msg1,*s);
	    continue;
	}
	INC(s);
	
	if ('%' == *s) 
	    goto add_character;         /* Back jump */
	
	pos++;
	
	if (msg1) {
	    if (!store1)
		store1 = msg1;
	    else {
		struct string * temp = cat_strings(store1,msg1,0);
		
		free_string(&store1);
		free_string(&msg1);
		store1 = temp;
	    }
	    msg1 = NULL;
	}

    again2:
	if ('0' == *s)      { INC(s); }
	else if ('-' == *s) { INC(s); }
	else if ('+' == *s) { INC(s); }
	
	if (*s >= '1' && *s <= '9') {
	    while (*s >= '0' && *s <= '9') {
		val1 = val1 * 10 + *s - '0';
		INC(s);
	    }
	    if ('$' == *s) {
		pos = val1;
		val1 = 0;
		goto again2;
	    }
	} else if ('*' == *s) { 
	    INC(s);
	}
	
	if ('.' == *s) {
	    INC(s);
	    if (*s >= '1' && *s <= '9') {
		while (*s >= '0' && *s <= '9') {
		    INC(s);
		}
	    } else if ('*' == *s) { 
		INC(s);
	    }      
	}
	
	if ('l' == *s) { 
	    INC(s);
	}
	
	switch(*s) {
	    CONST struct string *str;
	case 's': case 'Q': case 'd': case 'c': case 'C':
	case 'x': case 'X': case 'S': case 'p': case 'o':
	case 'i': case 'u': case 'f':
	    if (pos < 0 || pos >= store2_len)
		goto error2;
	    str = store2[pos];
	    if (!str)
		goto error2;
	    DPRINT(Debug,63,(&Debug,   
			     "-- [%c] str=%p=%S\n",
			     *s,str,str));
	    
	    if (!store1)
		store1 = dup_string(str);
	    else {
		struct string * temp = cat_strings(store1,str,0);
	    
		free_string(&store1);
		store1 = temp;
	    }
	    break;	
	default:
	    DPRINT(Debug,62,(&Debug,   
			     "-- [%c] ????\n",*s));
	error2:
	    DPRINT(Debug,62,(&Debug,   
			     "--- error\n"));
	    
	    if (!store1)
		store1 = new_string(display_charset);
	    add_ascii_to_string(store1,s2us("[???]"));
	}
    }

    for (i = 0; i < store2_len; i++) {
	if (store2[i])
	    free_string(&store2[i]);
    }
    if (store2)
	free(store2);
    
    if (msg1) {
	if (!store1)
	    store1 = msg1;
	else {
	    struct string * temp = cat_strings(store1,msg1,0);
	    
	    free_string(&store1);
	    free_string(&msg1);
	    store1 = temp;
	}
	msg1 = NULL;
    }
    if (!store1)
	store1 = new_string(display_charset);

#undef INC

    DPRINT(Debug,62,(&Debug, "elm_smessage=%p=%S\n",store1,store1));
    return store1;
}

#endif /* NOCHARSET */

char *elm_vmessage P_((int max_alloc,
		       const char *format, const char *msg, 
		       va_list args));


char *elm_vmessage (max_alloc,format,msg,vl)
     int max_alloc;
     CONST char *format;
     CONST char *msg;
     va_list vl;
{
    char * store1 = NULL;

#ifndef NOCHARSET   /* Used by nls/gencat */

    if (display_charset &&
	0   /* Alternate implementation used for testing... */
	) {
	
	struct string * R = elm_smessage(max_alloc,format,msg,vl);
	struct string * R1 = convert_string(display_charset,R,1);
	
	store1 = us2s(stream_from_string(R1,0,NULL));
	
	free_string(&R1);
	free_string(&R);
    } else 
#endif /* NOCHARSET    */
	{

	int max_elems = 0;
	struct  format_elem *elems = NULL;
	int elem_count, idx;
	char * format_error = NULL;

	CONST char * s;
	int ptr1 = 0, alloced1 = 0;
	
	struct store2 {
	    char * store;
	    int ptr;
	    int alloced;
	} * store2 = NULL;
	int store2_len = 0;
	int i,pos;
	
	
	DPRINT(Debug,62,(&Debug,   
		   "elm_vmessage: max_alloc=%d, format=%s\n",
		   max_alloc,format));
	DPRINT(Debug,62,(&Debug,   
			 "              msg=%s\n",msg));

	
	for (s = format; *s; s++) 
	    if ('%' == *s && '%' != *(s+1))
		max_elems++;
	DPRINT(Debug,62,(&Debug,   
			 "elm_vmessage: max_elems=%d\n",max_elems));

	if (max_elems > 0) 
	    elems = safe_malloc(max_elems * sizeof (*elems));
	else
	    elems = NULL;

	elem_count = parse_format_args(elems, max_elems, format,vl,
				       &format_error);
    
	DPRINT(Debug,62,(&Debug,   
			 "elm_vmessage: elem_count=%d\n", elem_count));
	if (format_error) {	    
	    DPRINT(Debug,1,(&Debug,   
			    "FORMAT ERROR (%s): %s\n",
			    format,format_error));
	} 
		
#define ENLARGE(ptr,alloced,S) do { \
    if (ptr >= alloced) { \
       DPRINT(Debug,99,(&Debug,   \
	       "ENLARGE(%d,%d,..) -- reallocing\n",ptr,alloced)); \
    S = safe_realloc(S,alloced+10); alloced += 10; } } while(0)
#define PUTC1(c) do { ENLARGE(ptr1,alloced1,store1); store1[ptr1++] = c; } \
  while(0)
#define PUTC2(c) do { ENLARGE(store2[store2_len-1].ptr,\
   store2[store2_len-1].alloced,store2[store2_len-1].store); \
   store2[store2_len-1].store[store2[store2_len-1].ptr++] = c; } \
while(0)
#define NEW_STORE do { \
      DPRINT(Debug,99,(&Debug,  \
	     "NEW_STORE -- %d\n",store2_len)); \
  store2 = safe_realloc(store2, \
  (store2_len+1) * sizeof (struct store2)); store2_len++; \
  store2[store2_len-1].store = NULL; store2[store2_len-1].alloced = 0; \
  store2[store2_len-1].ptr = 0; } while(0);
#define INC(p) { (p)++; if (!*(p)) break; }

	
	/* If there is error, elem_count == -1 and therefore loop is not run */
	
	for (idx = 0; idx < elem_count; idx++) {
	    
	    /* We should prealloc sufficien number of elements ... */
	    NEW_STORE;
	    
	    DPRINT(Debug,63,(&Debug,   
			     "#  %d [%d.%d %c]: \n",
			     idx,elems[idx].val1,elems[idx].val2,
			     elems[idx].format_chr));
	    
	    switch(elems[idx].format_chr) {
		CONST char *str, *a;
		
		int l,c;
		int quote,X;
#ifndef NOCHARSET   /* Used by nls/gencat */
		struct charset_state *ch;
		struct string * S, *S1;
#endif
		char * temp;

		void * valp;
		char DUMMY[256];
		char width[MAX_WIDTH+1];
		int I;

	    case 'c':
		if (V_chr_val == elems[idx].type)
		    c = elems[idx].value.chr_val;
		else {
		    DPRINT(Debug,63,(&Debug,   
				     "   %d [c] ERROR\n",idx));
		    c = '?';
		}

		DPRINT(Debug,63,(&Debug,   
				 "-- %d [c] val1=%d, c=%c\n",
				 idx,elems[idx].val1,c));
		l = 1;
		while (l < elems[idx].val1 && !elems[idx].left) { 
		    PUTC2(elems[idx].fill); l++; 
		}
		PUTC2(c);
		while (l < elems[idx].val1 && elems[idx].left) { 
		    PUTC2(' '); l++; 
		}
		break;
#ifndef NOCHARSET   /* Used by nls/gencat */
	    case 'C':
		if (V_cs_val == elems[idx].type)
		    ch = elems[idx].value.cs_val;
		else {
		    DPRINT(Debug,63,(&Debug,   
				     "   %d [C] ERROR\n",idx));
		    goto error1;
		}		
		S = new_string(display_charset);
		add_state_to_string(S,ch);
		
		temp = us2s(stream_from_string(S,1,NULL));
		DPRINT(Debug,63,(&Debug,   
				 "-- %d [C] val1=%d, str=%s\n",
				 idx,elems[idx].val1,temp));

		l = 1;
		while (l < elems[idx].val1 && !elems[idx].left) { 
		    PUTC2(elems[idx].fill); l++; 
		}
		for (a=temp; *a; a++) {
		    PUTC2(*a); 
		}
		while (l < elems[idx].val1 && elems[idx].left) { 
		    PUTC2(' '); l++; 
		}
		free(temp);
		free_string(&S);
		break;

	    case 'S':
		if (V_string_val == elems[idx].type) 
		    S = elems[idx].value.string_val;
		else {
		    DPRINT(Debug,63,(&Debug,   
				     "   %d [S] ERROR\n",idx));
		    goto error1;		
		}

		if (!verify_string(S)) {
		    DPRINT(Debug,1,(&Debug,   
				    "elm_vmessage: BAD %%S argument\n"));
		    goto error1;
	        }

		S1 = convert_string(display_charset,S,1);
		
		l = string_len(S1);
		if (elems[idx].val2 <= 0) 
		    elems[idx].val2 = l;
		
		X = 0;
		temp = us2s(streamclip_from_string(S1,&X,elems[idx].val2,
						   NULL,NULL));    /* TODO: Should use printable len? */
				
		DPRINT(Debug,63,(&Debug,   
				 "-- %d [S] val1=%d, val2=%d,(clip)str=%s\n",
				 idx,
				 elems[idx].val1,elems[idx].val2,temp));

		while (l < elems[idx].val1 && !elems[idx].left) { 
		    PUTC2(elems[idx].fill); l++; 
		}

		for (a=temp; *a; a++) {

		    PUTC2(*a); 
		    if (max_alloc > 0 && a - temp > max_alloc) {
			DPRINT(Debug,1,(&Debug,   
					" --- too long string (max=%d): %.30s...\n",
					max_alloc,temp));
			break;
		    }
		}
		
		free(temp);
		free_string(&S1);
		
		while (l < elems[idx].val1 && elems[idx].left) { 
		    PUTC2(' '); l++; 
		}
		
		break;
#endif  /* NOCHARSET */  /* Used by nls/gencat */

	    case 'p':
		if (V_ptr_val == elems[idx].type)		
		    valp = elems[idx].value.ptr_val;
		else {
		    DPRINT(Debug,63,(&Debug,   
				     "   %d [p] ERROR\n",idx));
		    goto error1;		
		}

		sprintf(DUMMY,"%p",valp);
		str = DUMMY;
		goto jump_dummy;

	    case 's': case 'Q':
		if (V_str_val == elems[idx].type)		
		    str = elems[idx].value.str_val;
		else {
		    DPRINT(Debug,63,(&Debug,   
				     "   %d [%c] ERROR\n",
				     idx,elems[idx].format_chr));
		    goto error1;
		}
		if (!str)
		    goto error1;

	    jump_dummy:
		quote = ('Q' == elems[idx].format_chr);
		
		l = strlen(str);
		if (quote) {
		    l += 2;
		    for (a=str; *a; a++) {
			if (*a == '\\' || *a == '"')
			    l++;
		    }
		}
		if (elems[idx].val2 <= 0) 
		    elems[idx].val2 = l;
		if (quote)
		    elems[idx].val2 -= 2;
		DPRINT(Debug,63,(&Debug,   
				 "-- %d [%c] val1=%d, val2=%d, str=%s\n",
				 idx,elems[idx].format_chr,
				 elems[idx].val1,elems[idx].val2,str));
		while (l < elems[idx].val1 && !elems[idx].left) { 
		    PUTC2(elems[idx].fill); l++; 
		}
		
		if (quote) {
		    PUTC2('"'); 
		}
		for (a = str; *a && a - str < elems[idx].val2; a++) { 
		    if (quote && (*a == '\\' || *a == '"')) {
			PUTC2('\\'); 
		    }
		    PUTC2(*a); 
		    if (max_alloc > 0 && a - str > max_alloc) {
			DPRINT(Debug,1,(&Debug,   
					"--- too long string (max=%d): %.30s...\n",
					max_alloc,str));
			break;
		    }
		}
		if (quote) {
		    PUTC2('"'); 
		}
		while (l < elems[idx].val1 && elems[idx].left) { 
		    PUTC2(' '); l++; 
		}
		break;
	    case 'd': case'i': case 'x': case 'X': case 'o': case 'u':
	    case 'f':

		I = convert_number(width, sizeof width,  & (elems[idx]));
		
		if (0 == I) {
		    DPRINT(Debug,63,(&Debug,   
				     "-- %d [%c]  I=%d, ERROR\n",
				     idx,elems[idx].format_chr,I));
		    goto error1;
		}

		for (l = 0; l < I; l++) {
		    PUTC2(width[l]); 
		}
		break; 
	    default:
		DPRINT(Debug,1,(&Debug,   
				"elm_vmessage: BAD %d [%c] ????\n",
				idx,elems[idx].format_chr));
	    error1:
		DPRINT(Debug,63,(&Debug,   
				 "-- error\n"));
		PUTC2('?');
		break;
	    }
	    PUTC2('\0');
	}
	
	if (elems) {
	    free(elems);
	    elems = NULL;
	}

	for (i = 0; i < store2_len; i++) {
	    DPRINT(Debug,63,(&Debug,   
			     " -- %d: %.*s\n",i,
			     store2[i].ptr,NONULL(store2[i].store)));
	}
	
	pos = -1;
	/* Actual printing */
	for (s = msg; *s; s++) {
	    int val1 = 0;
	    if (*s != '%') {
		PUTC1(*s);
		continue;
	    }
	    INC(s);
	    
	    if ('%' == *s) {
		PUTC1(*s);
		continue;
	    }
	    pos++;
	    
	again2:
	    if ('0' == *s)      { INC(s); }
	    else if ('-' == *s) { INC(s); }
	    else if ('+' == *s) { INC(s); }
	    
	    if (*s >= '1' && *s <= '9') {
		while (*s >= '0' && *s <= '9') {
		    val1 = val1 * 10 + *s - '0';
		    INC(s);
		}
		if ('$' == *s) {
		    pos = val1;
		    val1 = 0;
		    goto again2;
		}
	    } else if ('*' == *s) { 
		INC(s);
	    }
	    
	    if ('.' == *s) {
		INC(s);
		if (*s >= '1' && *s <= '9') {
		    while (*s >= '0' && *s <= '9') {
			INC(s);
		    }
		} else if ('*' == *s) { 
		    INC(s);
		}      
	    }
	    
	    if ('l' == *s) { 
		INC(s);
	    }
	    
	    switch(*s) {
		CONST char *str,*a;
	    case 's': case 'Q': case 'd': case 'c': case 'C': case 'x': 
	    case 'X': case 'S': case 'p': case 'o':
	    case 'i': case 'u': case 'f':
		if (pos < 0 || pos >= store2_len)
		    goto error2;
		str = store2[pos].store;
		if (!str)
		    goto error2;
		DPRINT(Debug,63,(&Debug,   
				 "-- [%c] str=%s\n",*s,str));
		for (a = str; *a; a++) { 
		    PUTC1(*a); 
		}
		break;
	    default:
		DPRINT(Debug,63,(&Debug,   
				 "-- [%c] ????\n",*s));
	    error2:
		DPRINT(Debug,63,(&Debug,   
				 "--- error\n"));
		PUTC1('[');
		PUTC1('?');
		PUTC1(']');
	    }
	}
	
	PUTC1('\0');
#undef PUTC1
#undef INC
#undef PUTC2
#undef NEW_STORE

	for (i = 0; i < store2_len; i++) {
	    if (store2[i].store)
		free(store2[i].store);
	}
	if (store2)
	    free(store2);

    }

    return store1;
}

int lib_error P_((const char * format, const char *msg, ...));

int lib_error (
#if ANSI_C
			      const char * format, 
			      const char *msg, ...
#else
			      format, msg, va_alist
#endif
			      )
#if !ANSI_C
     CONST char * format;
     CONST char *msg;
     va_dcl
#endif
{
    int ret = 0;
    va_list vl;
    char * store1;
    
    DPRINT(Debug,60,(&Debug,   
		     "lib_error: format=%s\n",format));
    DPRINT(Debug,60,(&Debug,   
		     "              msg=%s\n",msg));
    
    Va_start(vl, msg);           /* defined in defs.h */
    
    store1 = elm_vmessage(0,format,msg,vl);
    
    va_end(vl);

#if DEBUG
    panic_dprint ("error message=%s\n",store1);
#endif

    ret = H(store1);
    free(store1);
    return ret;
}


int lib_transient P_((const char * format, const char *msg, ...));

int lib_transient (
#if ANSI_C
			      const char * format, 
			      const char *msg, ...
#else
			      format, msg, va_alist
#endif
			      )
#if !ANSI_C
     CONST char * format;
     CONST char *msg;
     va_dcl
#endif
{
  int ret = 0;
  va_list vl;
  char * store1;

  DPRINT(Debug,60,(&Debug,   
	     "lib_transient: format=%s\n",format));
  DPRINT(Debug,60,(&Debug,   
	     "              msg=%s\n",msg));
  	      
  Va_start(vl, msg);           /* defined in defs.h */
  
  store1 = elm_vmessage(0,format,msg,vl);

  va_end(vl);

  if (def_err_handler == T)
      ret = H(store1);
  else
      ret = T(store1);

  free(store1);
  return ret;
}
 
char * lib_prompt P_((int prompt, CONST char * format, CONST char *msg, ...));
char * lib_prompt (
#if ANSI_C
		int prompt, CONST char * format, CONST char *msg, ...
#else
		prompt,format,msg,va_alist
#endif
		)
#if !ANSI_C
     int prompt;
     CONST char * format;
     CONST char *msg;
     va_dcl
#endif
{
    va_list vl;
    char * store1, *ret;
    
    DPRINT(Debug,60,(&Debug,   
		     "lib_prompt: prompt=%d, format=%s\n",prompt,format));
    DPRINT(Debug,62,(&Debug,   
		     "               msg=%s\n",msg));
    
    if (getppid() == 1) 	{
	/* we've lost our shell! */
	DPRINT(Debug,4,(&Debug,   
			"lib_prompt=NULL: Lost our shell (getppid=1)\n"));
	return NULL;
    }      
    
    Va_start(vl, msg);           /* defined in defs.h */

    store1 = elm_vmessage(0,format,msg,vl);
    
    va_end(vl);
    
    ret = P(store1,prompt);
    free(store1);
    return ret;
}

char *elm_message (
#if ANSI_C
		   CONST char * format, 
		   CONST char *msg, ...
#else
		   format, msg, va_alist
#endif
		)   
#if !ANSI_C
     CONST char * format; 
     CONST char *msg;
     va_dcl
#endif
{
  va_list vl;
  char * store1;

  DPRINT(Debug,61,(&Debug,   
		   "elm_message: format=%s\n",format));
  DPRINT(Debug,61,(&Debug,   
		   "             msg=%s\n",msg));
  	            
  Va_start(vl, msg);           /* defined in defs.h */
  
  store1 = elm_vmessage(0,format,msg,vl);

  va_end(vl);

  return store1;
}

#ifndef NOCHARSET   /* Used by nls/gencat */

struct string * format_string (
#if ANSI_C
			       CONST char * format, 
			       CONST char *msg, ...
#else
			       format, msg, va_alist
#endif
		)   
#if !ANSI_C
     CONST char * format; 
     CONST char *msg;
     va_dcl
#endif
{
  va_list vl;
  struct string * store1;

  DPRINT(Debug,61,(&Debug,   
		   "format_string: format=%s\n",format));
  DPRINT(Debug,61,(&Debug,   
		   "               msg=%s\n",msg));
  	            
  Va_start(vl, msg);           /* defined in defs.h */
  
  store1 = elm_smessage(0,format,msg,vl);

  va_end(vl);

  return store1;
}
#endif /* NOCHARSET */

int elm_sfprintf (
#if ANSI_C
		 char * buffer, int size,
		 CONST char * format, 
		 CONST char *msg, ...
#else
		 buffer, size, format, msg, va_alist
#endif
		)   
#if !ANSI_C
     char * buffer;
     int size;
     CONST char * format; 
     CONST char *msg;
     va_dcl
#endif
{
  va_list vl;
  char * store1;

  DPRINT(Debug,61,(&Debug,   
		   "elm_sfprintf: size=%d, format=%s\n",size, format));
  DPRINT(Debug,61,(&Debug,   
		   "             msg=%s\n",msg));
  	            
  Va_start(vl, msg);           /* defined in defs.h */
  
  store1 = elm_vmessage(size-1,format,msg,vl);

  va_end(vl);

  strfcpy(buffer,store1,size);
  free(store1);
  return strlen(buffer);
}

int elm_fprintf (
#if ANSI_C
		 FILE *f, CONST char * format, CONST char *msg, ...
#else
		 f, format,msg, va_alist
#endif
		 )
#if !ANSI_C
     FILE *f;
     CONST char * format;
     CONST char *msg;
     va_dcl     
#endif
{
    int ret;

    va_list vl;
    char * store1;
    
    DPRINT(Debug,61,(&Debug,   
		     "elm_fprintf: format=%s\n",format));
    DPRINT(Debug,61,(&Debug,   
		     "             msg=%s\n",msg));
    
    Va_start(vl, msg);           /* defined in defs.h */
    
    store1 = elm_vmessage(0,format,msg,vl);
    
    va_end(vl);
    
    ret = fputs(store1,f);    
    if (ret != EOF)
	ret = strlen(store1);
    free(store1);
    return 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