/*	Copyright (C) 1990 Free Software Foundation, Inc.

This file is part of Oleo, the GNU Spreadsheet.

Oleo is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

Oleo is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Oleo; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

#include "funcdef.h"

#include <stdio.h>
#include <ctype.h>

#include "sysdef.h"

#include "global.h"
#include "cell.h"

extern VOIDSTAR ck_malloc EXT1(size_t);
extern VOIDSTAR ck_realloc EXT2(void *,size_t);
extern double astof EXT1(char **);
extern long astol EXT1(char **);

extern unsigned short default_width;

extern CELLREF curow,cucol;

extern struct rng screen;

extern int default_fmt;
extern char numb_oflo[];

#ifdef TEST

extern void panic EXT1N(const char *);
#endif

char *bname[] = {
	"#FALSE","#TRUE"
};

#ifndef ASM_INF
#ifdef __GNUC__
__asm(".globl ___plinf");
__asm("___plinf: .double 0rinf");
__asm(".globl ___neinf");
__asm("___neinf: .double 0r-inf");
__asm(".globl ___nan");
__asm("___nan: .double 0rnan");
#else
double __plinf = 1e500;
double __neinf = -1e500;
double __nan = 1e500/ -1e500;
#endif
#endif

const char nname[] = "#NOT_A_NUMBER";
const char iname[] = "#INFINITY";
const char mname[] = "#MINUS_INFINITY";


/* Slightly larger than the maximum exponent we ever expect to see */
#define BIGFLT 309
#ifdef TEST
char print_buf[1024*8];
#else
char print_buf[BIGFLT+20];
#endif


/* Functions, etc for dealing with cell contents being displayed
   on top of other cells. */

struct slops {
	int s_alloc,s_used;
	struct s {
		CELLREF row,clo,chi;
	} s_b[1];
};

void
flush_slops FUN1(VOIDSTAR,where)
{
	struct slops *s;

	s=where;
	if(s)
		s->s_used=0;
}

int
find_slop FUN5(VOIDSTAR, where, CELLREF,r, CELLREF,c, CELLREF *,cclp, CELLREF *,cchp)
{
	int n;
	struct slops *s;

	s= where;
	if(!s)
		return 0;
	for(n=0;n<s->s_used;n++) {
		if(s->s_b[n].row==r && s->s_b[n].clo<=c && s->s_b[n].chi>=c) {
			*cclp=s->s_b[n].clo;
			*cchp=s->s_b[n].chi;
			return 1;
		}
	}
	return 0;
}

void
kill_slop FUN4(VOIDSTAR,where, CELLREF,r, CELLREF,clo, CELLREF,chi)
{
	int n;
	struct slops *s;

	s=where;
	for(n=0;n<s->s_used;n++) {
		if(s->s_b[n].row==r && s->s_b[n].clo==clo && s->s_b[n].chi==chi) {
			--(s->s_used);
			s->s_b[n]=s->s_b[s->s_used];
			return;
		}
	}
}

void
set_slop FUN4(VOIDSTAR *,wherep, CELLREF,r, CELLREF,clo, CELLREF,chi)
{
	int n;
	struct slops **sp;

	sp= (struct slops **)wherep;
	if(!*sp) {
		(*sp)=ck_malloc(sizeof(struct slops)+2*sizeof(struct s));
		(*sp)->s_alloc=2;
		(*sp)->s_used=1;
		n=0;
	} else {
		n= (*sp)->s_used++;
		if((*sp)->s_alloc==n) {
			(*sp)->s_alloc=n*2;

			(*sp)=ck_realloc((*sp),sizeof(struct slops)+n*2*sizeof(struct s));
		}
	}
	(*sp)->s_b[n].row=r;
	(*sp)->s_b[n].clo=clo;
	(*sp)->s_b[n].chi=chi;
}

void
change_slop FUN6(VOIDSTAR,where, CELLREF,r, CELLREF,olo, CELLREF,ohi, CELLREF,lo, CELLREF,hi)
{
	int n;
	struct slops *s;

	s= where;
	for(n=0;n<s->s_used;n++) {
		if(s->s_b[n].row==r && s->s_b[n].clo==olo && s->s_b[n].chi==ohi) {
			s->s_b[n].clo=lo;
			s->s_b[n].chi=hi;
			return;
		}
	}
}

/* Stuff for dealing with width of columns */
/* This isn't the best place for it, but here it is. . . */

struct width {
	struct width *prev,*next;
	CELLREF w_low,w_high;
	unsigned short wids[1];
};

struct width *wid_beg = 0;
struct width *wid_end = 0;

#define WID_BUF 1;

unsigned short
get_width FUN1(CELLREF, col)
{
	struct width *my_width;
	unsigned short n;

	if(!wid_beg) {
		n=default_width;
	} else {
		for(my_width=wid_beg;my_width && col>my_width->w_high;my_width=my_width->next)
			;
		if(!my_width || col<my_width->w_low)
			n=default_width;
		else {
			if(col<=my_width->w_high) {
				n=my_width->wids[col-my_width->w_low];
				if(n==0)
					n=default_width;
				else
					--n;
			} else
				n=default_width;
		}
	}
	/* if(n>numc)
		n=numc; */
	return n;
}

/* This is like get_width, but it returns width+1, or zero if the width is default */
unsigned short
get_nodef_width FUN1(CELLREF, col)
{
	struct width *my_width;
	unsigned short n;

	if(!wid_beg)
		return 0;

	for(my_width=wid_beg;my_width && col>my_width->w_high;my_width=my_width->next)
		;
	if(!my_width || col<my_width->w_low)
		return 0;
	if(col<=my_width->w_high) {
		n=my_width->wids[col-my_width->w_low];
		if(n==0)
			return 0;
		return n;
	}
	return 0;
}

void
set_width FUN2(CELLREF, col, unsigned short, wid)
{
	struct width *my_wid;
	struct width *tmp_wid;

	if(!wid_beg) {
		wid_beg=wid_end=(struct width *)ck_malloc(sizeof(struct width));
		wid_beg->prev=0;
		wid_beg->next=0;
		wid_beg->w_low=col;
		wid_beg->w_high=col;
		wid_beg->wids[0]=wid;
		return;
	}

	for(tmp_wid=wid_beg;tmp_wid;tmp_wid=tmp_wid->next)
		if(col<=tmp_wid->w_high)
			break;

	if(!tmp_wid) {
		if(wid_end->w_high==col-1) {
			wid_end->w_high++;

			wid_end=ck_realloc(wid_end,sizeof(struct width)+(wid_end->w_high-wid_end->w_low)*sizeof(unsigned short));
			wid_end->wids[col-wid_end->w_low]=wid;
			if(wid_end->prev)
				wid_end->prev->next=wid_end;
			else
				wid_beg=wid_end;
		} else {
			my_wid=(struct width *)ck_malloc(sizeof(struct width));
			my_wid->w_high=col;
			my_wid->w_low=col;
			my_wid->wids[0]=wid;
			my_wid->next=0;
			my_wid->prev=wid_end;
			wid_end->next=my_wid;
			wid_end=my_wid;
		}
		return;
	}

	if(col>=tmp_wid->w_low) {
		tmp_wid->wids[col-tmp_wid->w_low]=wid;
		return;
	}

	if(tmp_wid->prev && tmp_wid->prev->w_high==col-1) {
		if(col+1==tmp_wid->w_low) {
			struct width *w1,*w2;

			w1=tmp_wid->prev;
			w2=tmp_wid;
			my_wid=(struct width *)ck_malloc(sizeof(struct width)+(w2->w_high-w1->w_low)*sizeof(unsigned short));
			my_wid->prev=w1->prev;
			my_wid->next=w2->next;
			my_wid->w_high=w2->w_high;
			my_wid->w_low=w1->w_low;

			bcopy(w1->wids,&(my_wid->wids[0]),(1+w1->w_high-w1->w_low)*sizeof(unsigned short));
			bcopy(w2->wids,&(my_wid->wids[w2->w_low-my_wid->w_low]),(1+w2->w_high-w2->w_low)*sizeof(unsigned short));
			my_wid->wids[col-my_wid->w_low]=wid;

			if(my_wid->prev)
				my_wid->prev->next=my_wid;
			else
				wid_beg=my_wid;
			if(my_wid->next)
				my_wid->next->prev=my_wid;
			else
				wid_end=my_wid;
			free(w1);
			free(w2);
		} else {
			tmp_wid=tmp_wid->prev;
			tmp_wid->w_high++;

			tmp_wid=(struct width *)ck_realloc(tmp_wid,sizeof(struct width)+(tmp_wid->w_high-tmp_wid->w_low)*sizeof(unsigned short));
			tmp_wid->wids[tmp_wid->w_high-tmp_wid->w_low]=wid;
			tmp_wid->next->prev=tmp_wid;
			if(tmp_wid->prev)
				tmp_wid->prev->next=tmp_wid;
			else
				wid_beg=tmp_wid;
		}
		return;
	}
	if(col+1==tmp_wid->w_low) {
		my_wid=(struct width *)ck_malloc(sizeof(struct width)+(1+tmp_wid->w_high-tmp_wid->w_low)*sizeof(unsigned short));
		my_wid->w_high=tmp_wid->w_high;
		my_wid->w_low=col;
		my_wid->wids[0]=wid;
		bcopy(&(tmp_wid->wids[0]),&(my_wid->wids[1]),(tmp_wid->w_high-tmp_wid->w_low)*sizeof(unsigned short));
		my_wid->prev=tmp_wid->prev;
		my_wid->next=tmp_wid->next;
		if(my_wid->next)
			my_wid->next->prev=my_wid;
		else
			wid_end=my_wid;
		if(my_wid->prev)
			my_wid->prev->next=my_wid;
		else
			wid_beg=my_wid;
		free(tmp_wid);
		return;
	}
	my_wid=(struct width *)ck_malloc(sizeof(struct width));
	my_wid->w_high=col;
	my_wid->w_low=col;
	my_wid->wids[0]=wid;
	my_wid->next=tmp_wid;
	my_wid->prev=tmp_wid->prev;
	tmp_wid->prev=my_wid;
	if(my_wid->prev)
		my_wid->prev->next=my_wid;
	else
		wid_beg=my_wid;
	return;
}

unsigned short
next_widths FUN2(CELLREF *,c1, CELLREF *,c2)
{
	static struct width *wid;
	static int n;
	static int nhi;

	unsigned short ret;
	int m;

	if(c1==0) {
		wid=wid_beg;
		n=0;
		if(wid)
			nhi=wid->w_high-wid->w_low;
		return 0;
	}
	for(;wid;wid=wid->next,n=0,nhi=wid->w_high-wid->w_low) {
		for(;n<=nhi;n++) {
			if(wid->wids[n]==0)
				continue;
			ret=wid->wids[n];
			for(m=n+1;m<=nhi && wid->wids[m]==ret;m++)
				;
			*c1=wid->w_low+n;
			*c2=wid->w_low+m-1;
			n=m;
			if(n>nhi) {
				n=0;
				wid=wid->next;
				if(wid)
					nhi=wid->w_high-wid->w_low;
			}
			return ret;
		}
	}
	return 0;
}

void
flush_widths FUN0()
{
	struct width *w,*wn;

	for(w=wid_beg;w;w=wn) {
		wn=w->next;
		free(w);
	}
	wid_beg=0;
}

void
shift_widths FUN1(int, over)
{
	struct width *w, *wtmp;

	for(w=wid_beg;w;w=w->next) {
		if(over<0 && w->w_low<= -over) {
			if(w->w_high<=MIN_COL-over) {
				if(w->prev)
					w->prev->next=w->next;
				else
					wid_beg=w->next;
				if(w->next)
					w->next=w->prev;
				else
					wid_end=w->prev;
			} else {
				wtmp=ck_malloc(sizeof(struct width)+(w->w_high+over-MIN_COL)*sizeof(unsigned short));

				if(w->next) {
					wtmp->next=w->next;
					w->next->prev=wtmp;
				} else
					wid_end=wtmp;
				if(w->prev) {
					wtmp->prev=w->prev;
					w->prev->next=wtmp;
				} else
					wid_beg=wtmp;
				wtmp->w_low=MIN_COL;
				wtmp->w_high=w->w_high+over;
				bcopy(&(w->wids[MIN_COL-over-w->w_low]),&(wtmp->wids[0]),(wtmp->w_high+1-wtmp->w_low)*sizeof(unsigned short));
			}
			free(w);
		} else if(over>0 && w->w_high>=MAX_COL-over) {
			if(w->w_low>=MAX_COL-over) {
				if(w->prev)
					w->prev->next=w->next;
				else
					wid_beg=w->next;
				if(w->next)
					w->next=w->prev;
				else
					wid_end=w->prev;
				free(w);
			} else {
				w->w_high=MAX_COL;
				w->w_low+=over;

				w=ck_realloc(w,sizeof(struct width)+(w->w_high-w->w_low)*sizeof(unsigned short));
				if(w->next)
					w->next->prev=w;
				else
					wid_end=w;
				if(w->prev)
					w->prev->next=w;
				else
					wid_beg=w;
			}
		} else {
			w->w_low+=over;
			w->w_high+=over;
		}
	}
}

/* Structures/vars/functions for dealing with formatting floating-point
   numbers, etc */

struct user_fmt {
	char *p_hdr,*n_hdr;
	char *p_trl,*n_trl;
	char *zero,*comma,*decpt;
	unsigned char prec;
	double scale;
};

	/* Routines for formatting cell values */
static char *pr_flt EXT3(double, struct user_fmt *, int);
static char *pr_int EXT3(long, struct user_fmt *, int);

struct user_fmt dol = {
	"$","($",     0,")",  "$0",",", ".", PRC_FLT,   1	};

struct user_fmt cma = {
	  0, "(",     0,")",  "0", ",", ".", PRC_FLT,   1	};

struct user_fmt pct = {
	  0, "-",   "%","%",  "0%",  0, ".", PRC_FLT, 100	};

struct user_fmt fxt = {
	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	};

/* Variables */

struct user_fmt u[16] = {
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
  {	  0, "-",     0,  0,  "0",   0, ".", PRC_FLT,   1	},
};


/* Turn a floating-point number into the canonical text form.  This scribbles
   on print_buf */
char *
flt_to_str FUN1(double, val)
{
	double f;
	extern double fabs();

	if(val==__plinf)
		return iname;
	if(val==__neinf)
		return mname;
	f=fabs(val);
	if(f>=1e6 || (f>0 && f<=9.9999e-6)) {
		sprintf(print_buf,"%e",val);
		return print_buf;
	}
	return pr_flt(val,&fxt,PRC_FLT);
}

/* create the human-readable version of the contents of a cell
   This scribbles on print-buf bigtime */

char *
print_cell FUN1(CELL *,cp)
{
	int j;
	int p;
	long num;
	static char zeroes[] = "000000000000000";

	j=GET_FMT(cp);

	if(j==FMT_DEF)
		j=default_fmt;
	if(j==FMT_HID || GET_TYP(cp)==0)
		return "";

	if(GET_TYP(cp)==TYP_STR)
		return cp->cell_str;
	if(GET_TYP(cp)==TYP_BOL) {
#ifdef TEST
		if(cp->cell_bol<0 || cp->cell_bol>1)
			panic("Bool %d out of range",cp->cell_bol);
#endif
		return bname[cp->cell_bol];
	}
	if(GET_TYP(cp)==TYP_ERR) {
#ifdef TEST
		if(cp->cell_err>ERR_MAX || cp->cell_err<0)
			panic("Error %d out of range",cp->cell_err);
#endif
		return ename[cp->cell_err];
	}
	if(GET_TYP(cp)==TYP_FLT) {
		p=GET_PRC(j);
		switch(j|PRC_FLT) {
		case FMT_GPH:
			if(cp->cell_flt<0) {
				j='-';
				num = -(cp->cell_flt);
			} else if(cp->cell_flt>=1) {
				j='+';
				num= (cp->cell_flt);
			} else {
				j='0';
				num=1;
			}
		graph:
			if(num>=sizeof(print_buf)) {
				error_msg("Cannot graph %d '%c'",p,j);
				num=sizeof(print_buf)-1;
			}
			print_buf[num]='\0';
			while(--num>=0)
				print_buf[num]=j;
			return print_buf;

		case FMT_USR:
			return pr_flt(cp->cell_flt,&u[p],u[p].prec);

		case FMT_GEN:
		{
			double f;
			extern double fabs();

			f=fabs(cp->cell_flt);
			if(f>=1e6 || (f>0 && f<=9.9999e-6))
				goto handle_exp;
			return pr_flt(cp->cell_flt,&fxt,p);
		}

		case FMT_DOL:
			return pr_flt(cp->cell_flt,&dol,p);

		case FMT_CMA:
			return pr_flt(cp->cell_flt,&cma,p);

		case FMT_PCT:
			return pr_flt(cp->cell_flt,&pct,p);

		case FMT_FXT:
			return pr_flt(cp->cell_flt,&fxt,p);

		case FMT_EXP:
		handle_exp:
			if(cp->cell_flt==__plinf)
				return iname;
			if(cp->cell_flt==__neinf)
				return mname;
			if(p==PRC_FLT)
				sprintf(print_buf,"%e",cp->cell_flt);
			else
				sprintf(print_buf,"%.*e",p,cp->cell_flt);
			return print_buf;
#ifdef TEST
		default:
			panic("Unknown format %d",j);
			return 0;
#endif
		}
	}

	if(GET_TYP(cp)==TYP_INT) {
		p=GET_PRC(j);
		switch(j|PRC_FLT) {
		case FMT_GPH:
			if(cp->cell_int<0) {
				j='-';
				num = -(cp->cell_int);
			} else if(cp->cell_int>=1) {
				j='+';
				num = (cp->cell_int);
			} else {
				j='0';
				num = 1;
			}
			goto graph;

		case FMT_USR:
			return pr_int(cp->cell_int,&u[p],u[p].prec);

		case FMT_GEN:
			sprintf(print_buf,"%ld",cp->cell_int);
			return print_buf;

		case FMT_DOL:
			return pr_int(cp->cell_int,&dol,p);

		case FMT_CMA:
			return pr_int(cp->cell_int,&cma,p);

		case FMT_PCT:
			return pr_int(cp->cell_int,&pct,p);

		case FMT_FXT:
			if(p!=PRC_FLT && p!=0)
				sprintf(print_buf,"%ld.%.*s",cp->cell_int,p,zeroes);
			else
				sprintf(print_buf,"%ld",cp->cell_int);
			return print_buf;

		case FMT_EXP:
			if(p!=PRC_FLT)
				sprintf(print_buf,"%.*e",p,(double)(cp->cell_int));
			else
				sprintf(print_buf,"%e",(double)(cp->cell_int));
			return print_buf;
#ifdef TEST
		default:
			panic("Unknown format %d",j);
			return 0;
#endif
		}
	}
#ifdef TEST
	panic("Unknown cell type %d",GET_TYP(cp));
#endif
	return 0;
}

/* Return the value of ROW,COL in a human-readable fashion
   In paticular, strings have "" around them, and are \\ed
 */
char *
cell_value_string FUN2(CELLREF, row, CELLREF, col)
{
	CELL *cp;
	extern char *bname[];
	extern char print_buf[];
	extern char *flt_to_str();
	extern char *backslash_a_string();

	cp=find_cell(row,col);
	if(!cp || !GET_TYP(cp))
		return "";
	switch(GET_TYP(cp)) {
	case TYP_FLT:
		return flt_to_str(cp->cell_flt);

	case TYP_INT:
		sprintf(print_buf,"%ld",cp->cell_int);
		return print_buf;

	case TYP_STR:
		return backslash_a_string(cp->cell_str,1);

	case TYP_BOL:
		return bname[cp->cell_bol];

	case TYP_ERR:
		return ename[cp->cell_err];
#ifdef TEST
	default:
		panic("unknown type %d in cell_value_string",GET_TYP(cp));
#endif
	}
	return 0;
}

static char *
pr_int FUN3(long,val, struct user_fmt *,fmt, int,prec)
{
	char *pf, *pff, *pt;
	long int n;
	int nn=0;

	pt= &print_buf[sizeof(print_buf)-1];
	*pt='\0';

	n= fmt->scale * ((val<0) ? -val : val);
	if(n==0)
		return fmt->zero ? fmt->zero : "";

	pf=pff= (val<0) ? fmt->n_trl : fmt->p_trl;
	if(pf && *pf) {
		while(*pf)
			pf++;
		do
			*--pt= *--pf;
		while(pf!=pff);
	}

	if(prec!=PRC_FLT && prec!=0) {
		while(prec-->0)
			*--pt='0';
		pf=pff=fmt->decpt;
		if(pf) {
			while(*pf)
				pf++;
			do *--pt= *--pf;
			while(pf!=pff);
		}
		/* *--pt='.'; */
	}
	do {
		*--pt= (n%10)+'0';
		n/=10;
		if(nn++==2 && n>0) {
			if(fmt->comma && *(fmt->comma)) {
				for(pf=pff=fmt->comma;*pf;pf++)
					;
				do *--pt= *--pf;
				while(pf!=pff);
			}
			nn=0;
		}
	} while(n>0);

	pf=pff= (val<0) ? fmt->n_hdr : fmt->p_hdr;
	if(pf && *pf) {
		while(*pf)
			pf++;
		do *--pt= *--pf;
		while(pf!=pff);
	}
	return pt;
}

static char *
pr_flt FUN3(double,val, struct user_fmt *,fmt, int,prec)
{
	char *iptr;
	char *fptr;
	char *pptr;
	char *pf,*pff;
	double fract, integer, tmpval;
	int n;
	int isneg;
	int comlen;

	val*=fmt->scale;

	if(val==__plinf)
		return iname;
	if(val==__neinf)
		return mname;
	if(val!=val)
		return nname;

	iptr= &print_buf[BIGFLT];
	fptr= &print_buf[BIGFLT];


	if(val<0) {
		isneg=1;
		val = -val;
	} else
		isneg=0;

	if(val==0)
		return fmt->zero ? fmt->zero : "";

	comlen=0;
	if(fmt->comma && *(fmt->comma))
		for(pf=fmt->comma;*pf;comlen++,pf++)
			;

	fract=modf(val,&integer);
	n=0;
	do {
		if(iptr< &print_buf[comlen])
			return numb_oflo;
		tmpval=modf(integer/10,&integer);
		*--iptr='0'+(int)((tmpval+.01)*10);
		if(n++==2 && integer) {
			n=0;
			if(comlen) {
				pff=fmt->comma;
				pf=pff+comlen;
				do *--iptr= *--pf;
				while(pf!=pff);
			}
		}
	} while(integer);

	if(prec) {
		int p1;

		p1= (prec==PRC_FLT) ? 15 : (prec>0) ? prec : -prec;
		pf=fmt->decpt;
		while(pf && *pf)
			*fptr++= *pf++;
		/* *fptr++='.'; */
		if(fract) {
			do {
				fract = modf(fract * 10,&tmpval);
				*fptr++ = '0' + (int)tmpval;
			} while (--p1 && fract);
		}
		if(prec>0 && prec!=PRC_FLT)
			while(p1--)
				*fptr++='0';
		else {
			while(fptr[-1]=='0')
				--fptr;
			while(!isdigit(fptr[-1]))
				--fptr;
			*fptr='\0';
		}
	}
	if(fract) {
		(void)modf(fract * 10, &tmpval);
		if(tmpval>4) {
			iptr[-1]='0';
			for(pptr=fptr-1;;--pptr) {
				if(!isdigit(*pptr))
					continue;
				else if(*pptr=='9') {
					if(pptr==fptr-1 && pptr>&print_buf[BIGFLT] && (prec<0 || prec==PRC_FLT)) {
						--fptr;
						while(!isdigit(pptr[-1])) {
							--fptr;
							--pptr;
						}
						*pptr='\0';
					} else
						*pptr='0';
				} else {
					(*pptr)++;
					break;
				}
			}
			if(pptr<iptr) {
				--iptr;
				if(n==3) {
					n= *iptr++;
					for(pf=pff=fmt->comma;*pf;pf++)
						;
					do *--iptr= *--pf;
					while(pf!=pff);
					*--iptr=n;
				}
			}
		}
	}
	pf=pff= (isneg) ? fmt->n_hdr : fmt->p_hdr;
	if(pf && *pf) {
		while(*pf)
			pf++;
		do *--iptr= *--pf;
		while(pf!=pff);
	}

	pf=(isneg) ? fmt->n_trl : fmt->p_trl;
	while(pf && *pf)
		*fptr++ = *pf++;
	*fptr=0;
	return iptr;
}

char *
adjust_prc FUN5(char *,oldp, CELL *,cp, int,width, int,smallwid, int,just)
{
	int fmt;
	int prc;
	struct user_fmt *ufmt;
	char *bptr;
	char *eptr;
	int len;

	fmt=GET_FMT(cp);
	if(fmt==FMT_DEF)
		fmt=default_fmt;
	prc=GET_PRC(fmt);
	switch(fmt|PRC_FLT) {
	case FMT_GPH:
	case FMT_HID:
		return numb_oflo;
	case FMT_DOL:
		ufmt= &dol;
		goto deal_fmt;

	case FMT_CMA:
		ufmt= &cma;
		goto deal_fmt;

	case FMT_PCT:
		ufmt= &cma;
		goto deal_fmt;

	case FMT_FXT:
		ufmt= &fxt;
		goto deal_fmt;

	case FMT_USR:
		ufmt= &u[prc];
		prc=ufmt->prec;
		goto deal_fmt;

	case FMT_GEN:
		if(prc!=PRC_FLT)
			return numb_oflo;
		if(index(oldp,'e') || !index(oldp,'.'))
			goto handle_exp;

		ufmt= &fxt;
		prc=PRC_FLT;
		goto deal_fmt;

	deal_fmt:
		if(prc!=PRC_FLT)
			return numb_oflo;
		len=strlen(oldp);
		bptr=strstr(oldp,ufmt->decpt);
		if(!bptr)
			return numb_oflo;
		while(eptr=strstr(bptr+1,ufmt->decpt))
			bptr=eptr;

		if(width<bptr-oldp)
			return numb_oflo;
		if(bptr-oldp+strlen(ufmt->decpt)>=width)
			prc=0;
		else {
			prc=width-(strlen(ufmt->decpt)+bptr-oldp);
		}
		bptr=pr_flt(cp->cell_flt,ufmt,-prc);
		len=strlen(bptr);
		if(len>width && prc>0) {
			bptr=pr_flt(cp->cell_flt,ufmt,-(prc-1));
			len=strlen(bptr);
		}
		if(len>width)
			return numb_oflo;
		break;

	case FMT_EXP:
	handle_exp:
		{
			double f;
			extern double fabs EXT1(double);

			f=fabs(cp->cell_flt);
			if(f>9.99999e99 || f<1e-99)
				len=width-7;
			else /* if(f>9.9999999e9 || f<1e-9) */
				len=width-6;
			/* else
				len=width-5; */
			if(cp->cell_flt<0)
				--len;
			if(len>0) {
				sprintf(oldp,"%.*e",len,cp->cell_flt);
				len=strlen(oldp);
				if(len<=width) {
					bptr=oldp;
					break;
				}
			}
		}
		return numb_oflo;
#ifdef TEST
	default:
		panic("Unknown format %d in adjust_prc()",fmt);
		bptr=0;
		len=0;
		break;
#endif
	}

	/* If we get here, bptr points to a a string of len characters
	   (len<=width) that we want to output */
	if(len<smallwid) {
		if(just==JST_RGT || just==JST_CNT) {
			int n;

			n= (just==JST_RGT) ? smallwid-len : (1+smallwid-len)/2;
			for(;;) {
				bptr[len+n]=bptr[len];
				if(len--==0)
					break;
			}
			while(n-->=0)
				bptr[n]=' ';
		}
	}
	return bptr;
}


void
set_usr_stats FUN2(int,usr_n, char **,usr_buf)
{
	int len;
	int i;
	char *p_in,*p_out;

	len=0;
	for(i=0;i<7;i++)
		len+=strlen(usr_buf[i]);
	u[usr_n].p_hdr=ck_malloc(len+7);
	p_out=u[usr_n].p_hdr;
	if(usr_buf[0][0]) {
		p_in=usr_buf[0];
		while(*p_out++= *p_in++)
			;
	} else
		*p_out++='\0';

	if(usr_buf[1][0]) {
		p_in=usr_buf[1];
		u[usr_n].n_hdr=p_out;
		while(*p_out++= *p_in++)
			;
	} else
		u[usr_n].n_hdr=0;

	if(usr_buf[2][0]) {
		p_in=usr_buf[2];
		u[usr_n].p_trl=p_out;
		while(*p_out++= *p_in++)
			;
	} else
		u[usr_n].p_trl=0;

	if(usr_buf[3][0]) {
		p_in=usr_buf[3];
		u[usr_n].n_trl=p_out;
		while(*p_out++= *p_in++)
			;
	} else
		u[usr_n].n_trl=0;

	if(usr_buf[4][0]) {
		p_in=usr_buf[4];
		u[usr_n].zero=p_out;
		while(*p_out++= *p_in++)
			;
	} else
		u[usr_n].zero=0;

	if(usr_buf[5][0]) {
		p_in=usr_buf[5];
		u[usr_n].comma=p_out;
		while(*p_out++= *p_in++)
			;
	} else
		u[usr_n].comma=0;

	if(usr_buf[6][0]) {
		p_in=usr_buf[6];
		u[usr_n].decpt=p_out;
		while(*p_out++= *p_in++)
			;
	} else
		u[usr_n].decpt=0;

	if(!stricmp(usr_buf[7],"float") || !stricmp(usr_buf[7],"f"))
		u[usr_n].prec=15;
	else 
		u[usr_n].prec=astol(&usr_buf[7]);

	u[usr_n].scale=astof(&usr_buf[8]);
}

int
usr_set_fmts FUN0()
{
	int n;
	int ret = 0;

	for(n=0;n<16;n++)
		if(u[n].p_hdr)
			ret|=1<<n;
	return ret;
}

void
get_usr_stats FUN2(int,usr_num, char **,usr_buf)
{
	static char buf1[30];
	static char buf2[30];
	static char NullStr[]="";

	usr_buf[0]= u[usr_num].p_hdr ? u[usr_num].p_hdr : NullStr;
	usr_buf[1]= u[usr_num].n_hdr ? u[usr_num].n_hdr : NullStr;
	usr_buf[2]= u[usr_num].p_trl ? u[usr_num].p_trl : NullStr;
	usr_buf[3]= u[usr_num].n_trl ? u[usr_num].n_trl : NullStr;
	usr_buf[4]= u[usr_num].zero ? u[usr_num].zero : NullStr;
	usr_buf[5]= u[usr_num].comma ? u[usr_num].comma : NullStr;
	usr_buf[6]= u[usr_num].decpt ? u[usr_num].decpt : NullStr;
	if(u[usr_num].prec==15)
		usr_buf[7]="float";
	else {
		sprintf(buf1,"%u",u[usr_num].prec);
		usr_buf[7]=buf1;
	}
	sprintf(buf2,"%.12g",u[usr_num].scale);
	usr_buf[8]=buf2;
}

/* Functions for printing out the names of cells and ranges */

char *
cell_name FUN2(CELLREF,rr, CELLREF,cc)
{
	static char strs[2][20];
	static num = 0;
	char *ptr;

	num = num ? 0 : 1;

#ifdef A0_REFS
	ptr= &strs[num][9];
	sprintf(ptr,"%u",rr);
	if(cc<MIN_COL+26)
		*--ptr='A'-MIN_COL+cc;
	else if(cc<MIN_COL+702) {
		cc-=MIN_COL+26;
		*--ptr='A'+cc%26;
		*--ptr='A'+cc/26;
	}
#if MAX_COL>702
	else if(cc<MIN_COL+18278) {
		cc-=MIN_COL+702;
		*--ptr='A'+cc%26;
		cc/=26;
		*--ptr='A'+cc%26;
		*--ptr='A'+cc/26;
	} else {
		cc-=MIN_COL+18278;
		*--ptr='A'+cc%26;
		cc/=26;
		*--ptr='A'+cc%26;
		cc/=26;
		*--ptr='A'+cc%26;
		*--ptr='A'+cc/26;
	}
#else
	else
		panic("Col %u out of range in cell_name()",cc);
#endif
#else
	ptr= &strs[num][0];
	sprintf(ptr,"r%uc%u",rr,cc);
#endif
	return ptr;
}

char *
range_name FUN1(struct rng *,rng)
{
	CELLREF lr,lc,hr,hc;
	static char buf[2][40];
	static num;
	char *ptr;

	ptr= &buf[num][0];
	num = num ? 0 : 1;

	lr=rng->lr;
	lc=rng->lc;
	hr=rng->hr;
	hc=rng->hc;

#ifdef A0_REFS
	sprintf(ptr,"%s:%s",cell_name(lr,lc),cell_name(hr,hc));
	return ptr;
#else
	if(lr==hr && lc==hc)
		sprintf(ptr,"r%uc%u",lr,lc);
	else if(lr==hr && lc!=hc)
		sprintf(ptr,"r%uc%u:%u",lr,lc,hc);
	else if(lr!=hr && lc==hc)
		sprintf(ptr,"r%u:%uc%u",lr,hr,lc);
	else
		sprintf(ptr,"r%u:%uc%u:%u",lr,hr,lc,hc);
	return ptr;
#endif
}


#ifdef A0_REFS
char *
col_to_str FUN1(CELLREF, col)
{
	static char strs[2][10];
	static num;
	char *ptr;

	ptr= &strs[num][9];
	num = num ? 0 : 1;

	if(col<MIN_COL+26)
		*--ptr='A'-MIN_COL+col;
	else if(col<MIN_COL+702) {
		col-=MIN_COL+26;
		*--ptr='A'+col%26;
		*--ptr='A'+col/26;
	} else if(col<MIN_COL+18278) {
		col-=MIN_COL+702;
		*--ptr='A'+col%26;
		col/=26;
		*--ptr='A'+col%26;
		*--ptr='A'+col/26;
	} else {
		col-=MIN_COL+18278;
		*--ptr='A'+col%26;
		col/=26;
		*--ptr='A'+col%26;
		col/=26;
		*--ptr='A'+col%26;
		*--ptr='A'+col/26;
	}
	return ptr;
}
#endif


void
clear_spreadsheet FUN0()
{
	int n;
	extern int default_jst;
	extern int default_lock;
	extern void flush_widths EXT0();
	extern void flush_everything EXT0();
	extern void flush_all_timers EXT0();

	flush_everything();
	flush_widths();
	flush_all_timers();
	for(n=0;n<16;n++) {
		if(u[n].p_hdr) {
			free(u[n].p_hdr);
			u[n].p_hdr=0;
			u[n].prec=PRC_FLT;
			u[n].scale=1;
		}
	}
	default_width=8;
	default_jst=JST_LFT;
	default_fmt=FMT_GEN;
	default_lock=LCK_UNL;
}

