/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1986
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:font.c 12.0$ */
/* $ACIS:font.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ibm/lib/pmp/RCS/font.c,v $ */

#ifndef lint
static char *rcsid = "$Header:font.c 12.0$";
#endif

#include <stdio.h>
#include<sys/types.h>
#include <sys/stat.h>
#include <pmp/font3812.h>
#include <utils.h>
#include <hash.h>
#include <char.h>
#include <font.h>
#include <rp.h>

/***============================================================***/

int	fnt_debug;

#define WHERE_AM_I	"font.c"

#define FNT_MAX_TABLE_SIZE	23
#define FNT_LOADMAP_SIZE	(RP_NUMBEROFCHARS/(sizeof(unsigned)*8))
#define FNT_CHUNKSIZE		2
#define FNT_MAX_FILE_KNT	10
#define FNT_UNDEFINED		((u_int32)~0)

static hashtable	*fnt_names= 	NULL;
static FONT		*fnt_opened=	NULL;
static FONT		*fnt_closed=	NULL;

static int		 fnt_fileknt=	0;
static int		 fnt_filemax=	0;

static char		*fnt_pattern=	NULL;
static int		 fnt_ptrnsize=	0;

static CHAR_ops		*fnt_offsetops=	NULL;
static CHAR_ops		*fnt_datops=	NULL;

static char		**fnt_path;

/***============================================================***/
/***			PRIVATE ROUTINES			***/
/***============================================================***/

_fnt_prepend(old,inserted)
register1 FONT	*old;
register2 FONT	*inserted;
{
register3 FONT	*prev;

    D_ENTRY2(fnt_debug,"_fnt_prepend(0x%x,0x%x)\n",old,inserted);
    if (!old) {
	inserted->fnt_prev= inserted->fnt_next= inserted;
    }
    else {
	inserted->fnt_prev= prev= old->fnt_prev;
	inserted->fnt_next= old;
	old->fnt_prev= prev->fnt_next= inserted;
    }
    RETURN(TRUE);
}

/***============================================================***/

static int
_fnt_found(data_name,off_name,format)
register1 char	*data_name;
register2 char	*off_name;
register3 int	 format;
{
    D_ENTRY3(fnt_debug,"_fnt_found(%s,%s,0x%x)\n",data_name,off_name,format);
    switch (format) {
        case FNT_ACIS:	/* ACIS format needs both offset and data files	*/
		RETURN((data_name)&&(off_name));
	case FNT_PMP:	/* PMP format needs just a data file		*/
		RETURN((data_name)!=NULL);
	default:
		error1("_fnt_found -- unknown format 0x%x\n",format);
		action("font not accessible\n");
		break;
    }
    RETURN(FALSE);
}
    
/***============================================================***/

static char *
_fnt_pathname(name)
register1 char	*name;
{
struct stat buf;
register2 char	 *full_name= NULL;
register3 char	**directory;
register4 int	  name_len;
register5 int	  full_len;
register6 int	  tmp;


    D_ENTRY1(fnt_debug,"_fnt_pathname(%s)\n",name);
    if (!name) 
	RETURN(NULL);
    if (!fnt_path) {
	if (!stat(name,&buf))	RETURN(StrDup(name));
	else			RETURN(NULL);
    }
    name_len= strlen(name)+2;	/* space for '/' and trailing '\0'	*/
    directory= fnt_path;
    while (*directory) {
	tmp= strlen(*directory)+name_len;
	if (!full_name)	{
	    full_name=	u_malloc(tmp);
	    full_len=	tmp;
	}
	else if (tmp>full_len) {
	    full_len=	tmp;
	    full_name=	u_realloc(full_name,tmp);
	}
	sprintf(full_name,"%s/%s",*directory,name);
DEBUG1(fnt_debug,1,"looking for %s\n",full_name);
	if (!stat(full_name,&buf)) 
	    RETURN(full_name);
	directory++;
    }
    if (full_name)
	u_free(full_name);
    RETURN(NULL);
}

/***============================================================***/

static char *
_fnt_datname(name,format)
register2 char	*name;
register3 int	 format;
{
register1 char	*extension;
register4 char	*datname;
register5 char	*tmp;

    D_ENTRY2(fnt_debug,"_fnt_datname(%s,0x%x)\n",name,format);
    switch (format) {
	when FNT_ACIS:
		datname= u_malloc(strlen(name)+strlen(".dat")+1);
		strcpy(datname,name);
		extension= rindex(datname,'.');
		if (extension)	strcpy(extension,".dat");
		else 		strcat(datname,".dat");
		tmp= _fnt_pathname(datname);
		u_free(datname);
	when FNT_PMP:
		tmp= _fnt_pathname(name);
	otherwise:
		WSGO1("_fnt_datname -- unknown format 0x%x\n",format);
		action("font read aborted\n");
		RETURN(NULL);
    }
    RETURN(tmp);
}

/***============================================================***/

static char *
_fnt_offname(name,format)
register1 char	*name;
register2 int	 format;
{

    D_ENTRY2(fnt_debug,"_fnt_offname(%s,0x%x)\n",name,format);
    switch (format) {
	case FNT_ACIS:
		RETURN(_fnt_pathname(name));
	case FNT_PMP:
		RETURN(NULL);
	default:
		WSGO1("_fnt_offname -- unknown format 0x%x\n",format);
		action("font read aborted\n");
		break;
    }
    RETURN(NULL);
}

/***============================================================***/

static int
_fnt_getdatfile(fnt)
register1 FONT	*fnt;
{
register2 FONT	*ftmp;

    D_ENTRY1(fnt_debug,"_fnt_getdatfile(0x%x)\n",fnt);
    if (!fnt->fnt_datfile) {
	if (fnt_fileknt>=fnt_filemax) {
	    if (fnt_opened) {
		ftmp= fnt_opened->fnt_prev;
		while (ftmp) {
		    if (ftmp->fnt_datfile) {
			u_fclose(ftmp->fnt_datfile);
			ftmp->fnt_datfile= NULL;
			fnt_fileknt--;
			if (fnt_fileknt<fnt_filemax) 
			    break;
		    }
		    if (ftmp==fnt_opened) 
			break;
		    else 
			ftmp= ftmp->fnt_prev;
		}
	    }
	    if (fnt_fileknt>=fnt_filemax) {
		WSGO1("_fnt_getdatfile -- more open files than fonts (%d)\n",
								 fnt_fileknt);
		action("resetting fnt_fileknt. good luck\n");
		fnt_fileknt= 0;
	    }
	}
	fnt->fnt_datfile= u_fopen(fnt->fnt_datname,"r");
	if (!fnt->fnt_datfile) {
		RETURN(FALSE);
	}
	fnt_fileknt++;
    }
    if (fnt!=fnt_opened) {
	_fnt_prepend(fnt_opened,fnt);
	fnt_opened= fnt;
    }
    RETURN(TRUE);
}

/***============================================================***/

static int
_fnt_pmpoffsets(fnt,keep)
register5 FONT	*fnt;
register6 int	 keep;
{
register4 long	 where=	0;
	  int	 size=	0;
	  int	 index=	0;
register1 u_int32	*offsets;
register2 CHAR	*tmp;
register3 FILE	*fil;
register4 int	 knt;

   D_ENTRY2(fnt_debug,"_fnt_pmpoffsets(0x%x,%d)\n",fnt,keep);
   if (fnt->fnt_offname) {	/* does offset file exist? */

       fil= u_fopen(fnt->fnt_offname,"r");
       if (fil) {
	   fread(fnt->fnt_offsets,sizeof(u_int32),RP_NUMBEROFCHARS,fil);
	   u_fclose(fil);
	   RETURN(TRUE);
       }
   }
   if (fnt->fnt_datfile)
        fseek(fnt->fnt_datfile,0L,0);
   else if (!_fnt_getdatfile(fnt))
   	RETURN(FALSE);

   offsets= fnt->fnt_offsets;
   for (knt=0;knt<RP_NUMBEROFCHARS;knt++) {
       offsets[knt]=       FNT_UNDEFINED;
       fnt->fnt_data[knt]= NULL;
   }
   fil= fnt->fnt_datfile;
   while (tmp= ch_pmpread(fil,CH_RASTER,&size,&index,fnt_offsetops)) {
       offsets[index]= where;
       where+= size;
       if (keep)	fnt->fnt_data[index]= tmp;
       else		ch_free(tmp);
   }
   RETURN(TRUE);
}

/***============================================================***/

int
_fnt_acisoffsets(fnt)
register1 FONT	*fnt;
{
register2 u_int32  *offsets;
register3 int       knt;
register4 FILE     *fil;
register5 int       len;
cp_index  indices[RP_NUMBEROFCHARS];

    D_ENTRY1(fnt_debug,"_fnt_acisoffsets(0x%x)\n",fnt);

    fil= u_fopen(fnt->fnt_offname,"r");
    if (!fil) 
	RETURN(FALSE);
    
    len= getw(fil);
    if (len>RP_NUMBEROFCHARS) {
	WSGO1("_fnt_acisoffsets -- count to large (%d)\n",len);
	action1("truncated to %d\n",RP_NUMBEROFCHARS);
	len=RP_NUMBEROFCHARS;
    }

    if (fread(indices,sizeof(cp_index),len,fil)!=len) {
	WSGO1("_fnt_acisoffsets -- bad offsets file %s\n",fnt->fnt_offname);
	action("font change ignored\n");
	RETURN(FALSE);
    }

    offsets= fnt->fnt_offsets;
    for (knt=0;knt<len;knt++) {
	offsets[knt]= indices[knt].offset;
	fnt->fnt_data[knt]= NULL;
    }
    while (knt<RP_NUMBEROFCHARS) {
	fnt->fnt_data[knt]= NULL;
	offsets[knt++]= FNT_UNDEFINED;
    }
    u_fclose(fil);
    RETURN(TRUE);
}

/***============================================================***/

static int
_fnt_getdata(fnt)
register1 FONT	*fnt;
{

    D_ENTRY1(fnt_debug,"_fnt_getdata(0x%x)\n",fnt);
    if (!fnt->fnt_offsets)
    	fnt->fnt_offsets= (u_int32 *)u_calloc(RP_NUMBEROFCHARS,sizeof(u_int32));
    if (!fnt->fnt_data) 
	fnt->fnt_data= (CHAR **)u_calloc(RP_NUMBEROFCHARS,sizeof(CHAR *));
    switch (fnt->fnt_format) {
	case FNT_ACIS:	RETURN(_fnt_acisoffsets(fnt));
	case FNT_PMP:	RETURN(_fnt_pmpoffsets(fnt,FALSE));
    }
    error1("Unknown format 0x%x in _fnt_getdata\n",fnt->fnt_format);
    action("ignored\n");
    RETURN(FALSE);
}

/***============================================================***/

static CHAR *
_fnt_loadchar(fnt,ch)
register1 FONT	*fnt;
register2 int	 ch;
{
register3 FILE	*fil;
register4 CHAR	*fch;
	  int	 index;

    D_ENTRY2(fnt_debug,"_fnt_loadchar(0%x,%d)\n",fnt,ch);
    if ((!fnt->fnt_datfile)&&(!_fnt_getdatfile(fnt))) 
	RETURN(NULL);
    fil= fnt->fnt_datfile;
    fseek(fil,(long)fnt->fnt_offsets[ch],0);
    ch_op_set_info(fnt_datops,(char *)fnt);
    switch (fnt->fnt_format) {
	when FNT_ACIS:	
		fch= ch_acisread(fil,CH_RASTER,NULL,fnt_datops);
		if (!fch) {
		    WSGO3("_fnt_loadchar -- can't read char %d (offset 0x%x) in font %s\n",ch,fnt->fnt_offsets[ch],fnt->fnt_name);
		    action("reporting undefined character\n");
		    RETURN(NULL);
		}
	when FNT_PMP:	
		fch= ch_pmpread(fil,CH_RASTER,NULL,&index,fnt_datops);
		if (index!=ch) {
		    warning2("_fnt_loadchar -- expected index %d, found %d\n",
								   ch,index);
		    action1("ignoring %d\n",index);
		}
	otherwise:
		error1("_fnt_loadchar -- unknown format 0x%x\n",
							    fnt->fnt_format);
		action("character read aborted\n");
		fch= NULL;
    }
    RETURN(fch);
}

/***============================================================***/

typedef struct FC {
    	FONT *fnt_fcfont;
	int   fnt_fcoff;
} fnt_character;

static int
_fnt_skip(fil,ch,size)
register1 FILE	*fil;
register4 CHAR	*ch;
register3 int	 size;
{
register2 fnt_character *fc;

    D_ENTRY3(fnt_debug,"_fnt_skip(0x%x,0x%x,%d)\n",fil,ch,size);
    if (ch_info(ch)) {
	fc= (fnt_character *)u_malloc(sizeof(fnt_character));
	fc->fnt_fcfont= (FONT *)ch_info(ch);
	fc->fnt_fcoff=  ftell(fil);
	ch_set_pattern(ch,(char *)fc,CH_RASTER|CH_FREE_PTRN);
    }
    fseek(fil,size,1);
    RETURN(TRUE);
}

static char *
_fnt_ptrn(ch,length,fc)
register1 CHAR		*ch;
register4 int		 length;
register3 fnt_character	*fc;
{
register2 FONT	*fnt;
register5 int	 tmp;

    D_ENTRY3(fnt_debug,"_fnt_ptrn(0x%x,%d,0x%x)\n",ch,length,fc);
    if (fc) {
	fnt= fc->fnt_fcfont;
	if ((!fnt->fnt_datfile)&&(!_fnt_getdatfile(fnt)))
	    RETURN(NULL);
	if ((!fnt->fnt_offsets)&&(!_fnt_getdata(fnt)))
	    RETURN(NULL);
	fseek(fnt->fnt_datfile,fc->fnt_fcoff,0);
	if (length>fnt_ptrnsize) {
	    fnt_ptrnsize= (length*5)/4;
	    if (fnt_pattern) {
		fnt_pattern= u_realloc(fnt_pattern,fnt_ptrnsize);
	    }
	    else {
		fnt_pattern= u_malloc(fnt_ptrnsize);
	    }
	}
	tmp=fread(fnt_pattern,1,length,fnt->fnt_datfile);
	if (tmp!=length) {
	    error1("_fnt_ptrn -- font file %s corrupted (short read)\n",
							fnt->fnt_name);
	    action("ignored. (good luck)\n");
	}
	RETURN(fnt_pattern);
    }
    RETURN(NULL);
}

/***============================================================***/
/***			PUBLIC ROUTINES				***/
/***============================================================***/

static char *fnt_dflpath[] = {
    "/usr/lib/font/dev3812/fonts",
    NULL
};

int
fnt_init(constraint)
register2 int constraint;
{
static int beenhere= FALSE;
register1 table_size;

    D_ENTRY1(fnt_debug,"fnt_init(%d)\n",constraint);
    if (!beenhere) {
	fnt_filemax= table_size= FNT_CHUNKSIZE*constraint;

	fnt_filemax=	min(fnt_filemax,	FNT_MAX_FILE_KNT);
	table_size=	min(table_size,		FNT_MAX_TABLE_SIZE);
	fnt_names=	ht_new(table_size);
	fnt_offsetops=	ch_newops(NULL,_fnt_skip,NULL,NULL,NULL);
	fnt_datops=	ch_newops(NULL,_fnt_skip,NULL,_fnt_ptrn,NULL);
	beenhere=	TRUE;
	fnt_set_path(fnt_dflpath);
    }
    RETURN(TRUE);
}

/***===================================================================***/

FONT	*
fnt_open(name,format)
register2 char *name;
register3 int   format;
{
register1 FONT *fnt;
register4 int knt;

    D_ENTRY2(fnt_debug,"fnt_open(%s,%d)\n",name,format);
    fnt= (FONT *)ht_get(fnt_names,name);
    if (fnt) {
	if (fnt->fnt_format==format) {
	    RETURN(fnt);
	}
	else {
	    fnt->fnt_format&= (~FNT_CLOSED);
	    if (fnt->fnt_format==format) 
		RETURN(fnt);
	    fnt->fnt_format= format;
	    if (fnt->fnt_offsets) {
		u_free(fnt->fnt_offsets);
		fnt->fnt_offsets= NULL;
	    }
	    for (knt=0;knt<RP_NUMBEROFCHARS;knt++) {
		if (fnt->fnt_data[knt]) 
		    ch_free(fnt->fnt_data[knt]);
		fnt->fnt_data[knt]= NULL;
	    }
	    RETURN(fnt);
	}
    }
    else {
	register5 char *datname;
	register6 char *offname;

	datname= _fnt_datname(name,format);
	offname= _fnt_offname(name,format);
	if (_fnt_found(datname,offname,format)) {
		fnt= (FONT *)u_malloc(sizeof(FONT));
		fnt->fnt_name=	StrDup(name);
		fnt->fnt_format=	format&(~FNT_CLOSED);
		fnt->fnt_offsets=	NULL;
		fnt->fnt_data=		NULL;
		fnt->fnt_datfile=	NULL;
		fnt->fnt_datname=	datname;
		fnt->fnt_offname=	offname;
		fnt->fnt_ldinfo=	NULL;
		_fnt_prepend(fnt_opened,fnt);
		fnt_opened= fnt;
		ht_put(fnt_names,name,fnt);
		RETURN(fnt);
	}
    }
    RETURN(NULL);
}

/***===================================================================***/

int
fnt_close(fnt)
register1 FONT *fnt;
{
    D_ENTRY1(fnt_debug,"fnt_close(0x%x)\n",fnt);
    if (fnt->fnt_format&FNT_CLOSED) {
	RETURN(FALSE);
    }
    else {
	if (fnt->fnt_datfile) {
	    u_fclose(fnt->fnt_datfile);
	    fnt->fnt_datfile= NULL;
	    fnt_fileknt--;
	}
	fnt->fnt_format|= FNT_CLOSED;
	if (fnt_opened==fnt) {
	    fnt_opened= fnt->fnt_next;
	    if (fnt_opened==fnt) 
		fnt_opened= NULL;
	}
	_fnt_prepend(fnt_closed,fnt);
	fnt_closed= fnt;
    }
    RETURN(FALSE);
}

/***===================================================================***/

int
fnt_addch(fnt,ch,chr)
register1 FONT *fnt;
register2 int   ch;
register3 CHAR *chr;
{
    D_ENTRY3(fnt_debug,"fnt_addch(0x%x,%d,0x%x)\n",fnt,ch,chr);
    RETURN(FALSE);
}

/***===================================================================***/

CHAR	*
fnt_char(fnt,ch)
register1 FONT	*fnt;
register3 int	 ch;
{
register2 CHAR	*fch;

    D_ENTRY2(fnt_debug,"fnt_char(0x%x,%d)\n",fnt,ch);
    if ((!fnt)||(!rp_charok(ch))) {	/* fnt and char ok?		*/
	RETURN(NULL);
    }
					/* do we have data for fnt?	*/
    if ((!fnt->fnt_data)&&(!_fnt_getdata(fnt)))
	RETURN(NULL);

    if (fch= fnt->fnt_data[ch])		/* do we have data for char?	*/
	RETURN(fch);			/* yes. return it.		*/
					/* no, is ch defined on font?	*/
    if (fnt->fnt_offsets[ch]==FNT_UNDEFINED) {
	RETURN(NULL);
    }
    else {
	fch= _fnt_loadchar(fnt,ch);	/* yes. get data for char	*/
	fnt->fnt_data[ch]= fch;
	RETURN(fch);
    }
}

/***===================================================================***/

char	**
fnt_get_path()
{
    D_ENTRY(fnt_debug,"fnt_get_path()\n");
    RETURN(fnt_path);
}

/***===================================================================***/

char	**
fnt_set_path(dirs)
register1 char **dirs;
{
register2 char **old;
register3 int	 length=0;

    D_ENTRY1(fnt_debug,"fnt_set_path(0x%x)\n",dirs);
    old= fnt_path;
    while (dirs&&dirs[length]) 
	length++;
    fnt_path= (char **)u_calloc(length+1,sizeof(char *));
    fnt_path[length--]= NULL;
    while (length>=0) {
	fnt_path[length]= dirs[length];
	length--;
    }
    RETURN(old);
}

/***===================================================================***/

int
fnt_add_dir(dir)
register6 char *dir;
{
register1 int	  length=0;
register2 char	**new_path;

    D_ENTRY1(fnt_debug,"fnt_add_dir(%s)\n",dir);
    if (fnt_path) {
	while (fnt_path[length]) {
	    if (StrMatch(fnt_path[length],dir)) 
		RETURN(TRUE);
	    else
		length++;
	}
    }
    length+= 1;	/* make room for new directory */
    new_path= (char **)u_calloc(length+1,sizeof(char *));
    new_path[length--]= NULL;
    new_path[length--]= dir;
    while (length>=0) {
	new_path[length]= fnt_path[length];
	length--;
    }
    if (fnt_path) 
	u_free(fnt_path);
    fnt_path= new_path;
    RETURN(TRUE);
}

/***===================================================================***/
