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

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

#include <stdio.h>
#include <utils.h>
#include <trie.h>
#include <deviceattr.h>
#include "info.h"

#define WHERE_AM_I "info.c"

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

static char  *i_ptrname;	/* Name of printer */
static char  *i_dir;		/* Name of printer info directory */

static struct dev i_dev;	/* description of printer */

static char  **i_fonts;		/* Printer font names (in order loaded) */
static int     i_fknt;		/* Number we've already seen */
static int     i_fsize;         /* Size of i_fonts */
static short  *i_sizes;		/* Printer sizes */
static char  **i_fspcl;		/* Special fonts */
static int     i_spknt;		/* Number we've already seen */
static int     i_spsize;        /* Size of i_fspcl */
static char  **i_cspcl;		/* Special character names */

static trie   *i_cnames;        /* Special character names and indices */
static trie   *i_finfo;		/* Information about fonts */

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

#define FREAD(buf,sz,num,fil)	(fread(buf,sz,num,fil)==(num))

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

int if_debug=0;

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

   /*\
   |* Sets global printer and directory names
   \*/

static int
_i_SetNames(pname)
register1 char     *pname;
{
   D_ENTRY1(if_debug,"_i_SetNames(%s)\n",pname);
   if (pname) {
       i_ptrname= (char *)u_malloc(strlen(pname)+1);
       strcpy(i_ptrname,pname);
       i_dir=u_malloc(strlen(IP_DIR)+strlen(pname)+1);
       sprintf(i_dir,IP_DIR,pname);
   }
   else {
       if (i_ptrname) {
	   u_free(i_ptrname);
           i_ptrname= NULL;
       }
       if (i_dir) {
	   u_free(i_dir);
           i_dir= NULL;
       }
   }
   RETURN(TRUE);
}

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

    /*\
    |* Frees up all internal data structures
    \*/

static int
_i_Reset()
{
   D_ENTRY(if_debug,"_i_Reset()\n");
   if (i_ptrname)	{u_free(i_ptrname);	i_ptrname= NULL;}
   if (i_dir)		{u_free(i_dir);		i_dir= NULL;}
   if (i_fonts)		{u_free(i_fonts);	i_fonts= NULL;}
   if (i_sizes)		{u_free(i_sizes);	i_sizes= NULL;}
   if (i_fspcl)		{u_free(i_fspcl);	i_fspcl= NULL;}
   if (i_cspcl)		{u_free(i_cspcl);	i_cspcl= NULL;}
   if (i_cnames)	{u_free(i_cnames);	i_cnames= NULL;}
   if (i_finfo)		{u_free(i_finfo);	i_finfo= NULL;}
   i_fknt= i_fsize= i_spknt= i_spsize= 0;
   RETURN(TRUE);
}

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

    /*\
    |* Reads a dvitroff printer description from 'fil' into 'i_dev' (global)
    |* and assorted global font structures.  (i_fonts, etc)
    \*/

static int
_ip_LdDesc(fil)
register2 FILE  *fil;
{
register1 int    knt;
register3 char  *names;
register4 short *indices;
register6 int    ok= TRUE;
register5 char  *tmp_names;


   D_ENTRY1(if_debug,"_ip_LdDesc(%x)\n",fil);

   ok= ok&&FREAD(&i_dev,sizeof(struct dev),1,fil);

   i_sizes= (short *)u_calloc(i_dev.nsizes+1,sizeof(short));
   ok= ok&&FREAD(i_sizes,sizeof(short),(i_dev.nsizes+1),fil);

   i_cnames= tr_New();

   if (i_dev.nchtab) {
       indices= (short *)u_calloc(i_dev.nchtab,sizeof(short));
       ok= ok&&FREAD(indices,sizeof(short),i_dev.nchtab,fil);
   }
   else indices= NULL;

   if (i_dev.lchname) {
       names= (char *)u_malloc(i_dev.lchname);
       ok= ok&&FREAD(names,i_dev.lchname,1,fil);
   }
   else names= NULL;

   i_cspcl= (char **)u_calloc(i_dev.nchtab+1,sizeof(char *));
   tmp_names= (char *)u_calloc(i_dev.nchtab,3);
   for (knt=0;knt<i_dev.nchtab-1;knt++) {
       i_cspcl[knt]= &tmp_names[knt*3];
       strcpy(i_cspcl[knt],&names[indices[knt]]);
					/* 0 is a legal value, so add 1 to */
					/* stored value so 0 can be stored */
       tr_Add(i_cnames,i_cspcl[knt],(char *)(knt+1));
   }

   u_free(indices);
   if (!ok) {
        WSGO("_ip_LdDesc -- DESC file too short\n");
        RETURN(FALSE);
   }
   else RETURN(TRUE);
}

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

     /*\
     |* Described in info.h
     \*/

int
ip_Load(name,fil)
register1 char   *name;
register3 FILE   *fil;
{
#ifdef IP_AUTOINIT
register5 int  deflt_fil= (int)(!fil);
#endif /* IP_AUTOINIT */
register2 int  knt;
register4 int  ok= TRUE;

   D_ENTRY2(if_debug,"ip_Load(%s,%x)\n",name,fil);

#ifdef IP_AUTOINIT
   if (!name)
      name= IP_DFLT_NAME;
#else
   if (!name) {
      error1("ip_Load called with name=NULL\n");
      RETURN(FALSE);
   }
   if (!fil) {
      error1("ip_Load called with fil=NULL\n");
      RETURN(FALSE);
   }

#endif /* IP_AUTOINIT */

   if (i_ptrname) {
      if (StrMatch(i_ptrname,name))
          RETURN(TRUE);
      else {
	  warning2("ip_Load Overriding %s with %s\n",i_ptrname,name);
	  _i_Reset();
      }
   }

   _i_SetNames(name);

#ifdef IP_AUTOINIT
   if (deflt_fil) {
      register6 char *dfil= (char *)u_malloc(strlen(i_dir)+strlen(IP_DESC)+1);

      strcpy(dfil,i_dir);
      strcat(dfil,IP_DESC);
      fil= u_fopen(dfil,"r");
      if (!fil) {
	 WSGO1("Error! Can't open %s for printer description (WSGO)\n",dfil);
	 _i_Reset();
	 u_free(dfil);
	 RETURN(FALSE);
      }
      u_free(dfil);
   }
#endif /* IP_AUTOINIT */

   ok= _ip_LdDesc(fil);

   for (knt=i_dev.nfonts;knt>0;knt--) {
      ok= ok&&if_Load(NULL,fil);
   }

#ifdef IP_AUTOINIT
   if (deflt_fil)
      u_fclose(fil);
#endif /* IP_AUTOINIT */
   RETURN(ok);
}

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

   /*\
   |* Given a font description, updates the global font name lists
   |* appropriately
   \*/

i_StoreFont(fnt)
register1 struct fontattr *fnt;
{

   D_ENTRY1(if_debug,"i_StoreFont(%x)\n",fnt);

   if (i_fknt>=(i_fsize-1)) {
      if (i_fonts) {
	 u_free(i_fonts);
         i_fonts= (char **)u_realloc(i_fonts,(i_fsize+5)*sizeof(char *));
         i_fsize+=5;
       }
       else {
	 i_fonts= (char **)u_calloc((i_dev.nfonts+5),sizeof(char *));
	 i_fsize= i_dev.nfonts+5;
       }
   }

   i_fonts[i_fknt++]= fnt->namefont;
   i_fonts[i_fknt]= NULL;

   if (!fnt->specfont)
      RETURN(TRUE);

   if (i_spknt>=(i_spsize-1)) {
      if (i_fspcl) {
	 u_free(i_fspcl);
         i_fspcl= (char **)u_realloc(i_fspcl,(i_spsize+5)*sizeof(char *));
      }
      else i_fspcl= (char **)u_calloc((i_spsize+5),sizeof(char *));
      i_spsize+=5;
   }

   i_fspcl[i_spknt++]= fnt->namefont;
   i_fspcl[i_spknt]= NULL;
   RETURN(TRUE);
}

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

    /*\
    |* Given the name of a font returns a pointer to the font
    |* file containing it's description.
    \*/

static FILE *
if_FontFile(fnt_name)
register2 char   *fnt_name;
{
register1 FILE   *fil;
static char fil_name[100];

   D_ENTRY1(if_debug,"if_FontFile(%s)\n",fnt_name);

   strcpy(fil_name,i_dir);
   strcat(fil_name,IF_PREFIX);
   strcat(fil_name,fnt_name);
   strcat(fil_name,IF_SUFFIX);

   fil= u_fopen(fil_name,"r");
   if (!fil) {
      if (fnt_name) {
	error2("Can't open %s to read info for %s\n",fil_name,fnt_name);
	 RETURN(NULL);
      }
      else {
	 WSGO("if_FontFile -- One of font name or file must be supplied\n");
      }
   }

   RETURN(fil);
}

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

     /*\
     |* Described in info.h
     \*/

int
if_Load(name,fil)
register7 char   *name;
register4 FILE   *fil;
{
register1 struct fontattr *fnt;
register2 int   knt;
register3 short *widths;
register5 int   deflt_fil= !fil;
register6 int   ok= TRUE;
register7 int	wrong_name=FALSE;

   D_ENTRY2(if_debug,"if_Load(%s,%x)\n",name,fil);
   fnt= (struct fontattr *)u_malloc(sizeof(struct fontattr));
#ifdef IP_AUTOINIT
    if (!i_ptrname)
       if (!ip_Load(NULL,NULL))
          WSGO("ip_Fonts can't load default DESC file\n");
#endif /* IP_AUTOINIT */

   if (!i_finfo) {
        i_finfo= tr_New();
   }

   if ((name)&&(tr_Ok(i_finfo,name)&TR_VALID))
      RETURN(TRUE);

   if (!fil)
      fil= if_FontFile(name);

   if (!fil) {
       error1("can't open font file for %s\n",name);
       RETURN(FALSE);
   }

   ok= ok&&FREAD(fnt,sizeof(struct fontattr),1,fil);

   if (name&&(!StrMatch(fnt->namefont,name))) {
       warning2("expected font name %s, found %.16s\n",name,fnt->namefont);
       wrong_name= TRUE;
   }

   if (fnt->searchpathcount) {
       fnt->searchpath=(fontindex *)u_calloc(fnt->searchpathcount,
							sizeof(fontindex));
       ok= ok&&FREAD(fnt->searchpath,sizeof(fontindex),fnt->searchpathcount,
       									fil);
   }
   else fnt->searchpath= NULL;

   if (fnt->numberofchars) {
       fnt->info= (struct charinfo *)u_calloc(fnt->numberofchars,
						sizeof(struct charinfo));
       widths= (short *)u_calloc(fnt->numberofchars*i_dev.nsizes,
       							     sizeof(short));
       ok=ok&&FREAD(fnt->info,sizeof(struct charinfo),fnt->numberofchars,fil);

       ok=ok&&FREAD(widths,sizeof(short),fnt->numberofchars*i_dev.nsizes,fil);
   }
   else {
       fnt->info= NULL;
       widths= NULL;
   }

   fnt->charmapping= (unsigned char *)u_calloc(i_dev.nchtab+128-32,
   						     sizeof(unsigned char));
   ok= ok&&FREAD(fnt->charmapping,sizeof(unsigned char),i_dev.nchtab+128-32,
									fil);

   for (knt=0;knt<fnt->numberofchars;knt++) {
	fnt->info[knt].widths= &widths[knt*i_dev.nsizes];
   }

   if (!tr_Add(i_finfo,fnt->namefont,fnt))
	i_StoreFont(fnt);
   if (wrong_name) {
	struct fontattr *dup;

	dup= (struct fontattr *)u_malloc(sizeof(struct fontattr));
	strcpy(dup->namefont,name);
	strcpy(dup->intname,fnt->intname);
	dup->searchpathcount=	fnt->searchpathcount;
	dup->numberofchars=	fnt->numberofchars;
	dup->specfont=		fnt->specfont;
	dup->ligfont=		fnt->ligfont;
	dup->searchpath=	fnt->searchpath;
	dup->info=		fnt->info;
	fnt->charmapping=	fnt->charmapping;
	if (!tr_Add(i_finfo,name,fnt))
	    i_StoreFont(fnt);
   }
   if (deflt_fil)
      u_fclose(fil);
   RETURN(TRUE);
}


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

     /*\
     |* Described in info.h
     \*/

char **
ip_FSpecials()
{
    D_ENTRY(if_debug,"ip_FSpecials()\n");

#ifdef IP_AUTOINIT
    if (!i_ptrname)
       if (!ip_Load(NULL,NULL))
          WSGO("ip_Fonts can't load default DESC file\n");
#endif /* IP_AUTOINIT */

    RETURN(i_fspcl);
}


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

     /*\
     |* Described in info.h
     \*/

char **
ip_CSpecials()
{
    D_ENTRY(if_debug,"ip_CSpecials()\n");

#ifdef IP_AUTOINIT
    if (!i_ptrname)
       if (!ip_Load(NULL,NULL))
          WSGO("ip_Fonts can't load default DESC file\n");
#endif /* IP_AUTOINIT */

    RETURN(i_cspcl);
}


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

     /*\
     |* Described in info.h
     \*/

short *
ip_Sizes()
{
    D_ENTRY(if_debug,"ip_Sizes()\n");

#ifdef IP_AUTOINIT
    if (!i_ptrname)
       if (!ip_Load(NULL,NULL))
          WSGO("ip_Fonts can't load default DESC file\n");
#endif /* IP_AUTOINIT */

    RETURN(i_sizes);
}

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

     /*\
     |* Described in info.h
     \*/

char **
ip_Fonts()
{
    D_ENTRY(if_debug,"ip_Fonts()\n");

#ifdef IP_AUTOINIT
    if (!i_ptrname)
       if (!ip_Load(NULL,NULL))
          WSGO("ip_Fonts can't load default DESC file\n");
#endif /* IP_AUTOINIT */

    RETURN(i_fonts);
}


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

     /*\
     |* Described in info.h
     \*/

int
ip_Info(attr,sz_or_buf)
register1 int attr;
register2 union { int size; struct dev *buf; } sz_or_buf;
{
register3 int knt,tsize=IF_ILLEGAL;

   D_ENTRY2(if_debug,"ip_Info(%d,%x)\n",attr,sz_or_buf);

#ifdef IP_AUTOINIT
    if (!i_ptrname)
       if (!ip_Load(NULL,NULL))
          WSGO("ip_Fonts can't load default DESC file\n");
#endif /* IP_AUTOINIT */

   switch (attr) {
      case IP_RES:    RETURN(i_dev.res);
      case IP_HOR:    RETURN(i_dev.hor);
      case IP_VERT:   RETURN(i_dev.vert);
      case IP_WIDTH:  RETURN(i_dev.paperwidth);
      case IP_HEIGHT: RETURN(i_dev.paperlength);
      case IP_UNITW:  RETURN(i_dev.unitwidth);
      case IP_SIZE:   for (knt=0;knt<i_dev.nsizes;knt++) {
	  		  if (i_sizes[knt]==sz_or_buf.size) {
			      tsize= knt;
			  }
		      }
		      RETURN(tsize);
      case IP_ALL:    *sz_or_buf.buf= i_dev;
      		      RETURN(TRUE);
      default:        WSGO1("Unknown printer attribute %d requested\n",attr);
                      RETURN(NULL);
   }
}


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

static
_ic_CharInfo(fnt,index,attr,sz_or_buf)
register1 struct fontattr *fnt;
register2 int          index;
register3 int          attr;
union { int size; struct charinfo *buf; } sz_or_buf;
{
    D_ENTRY4(if_debug,"_ic_CharInfo(0x%x,%d,0x%x,0x%x)\n",fnt,index,attr,
								sz_or_buf);
   if (!fnt->charmapping[index])
      RETURN(IC_NOTFOUND);
   switch (attr) {
      case IC_CODE:   RETURN(fnt->info[fnt->charmapping[index]].code);
      case IC_KERN:   RETURN(fnt->info[fnt->charmapping[index]].kern);
      case IC_WIDTH:  { register5 int tsize= -1;
      			  for (attr=0;attr<i_dev.nsizes;attr++) {
			      if (i_sizes[attr]==sz_or_buf.size) {
				  tsize= attr;
				  break;
			      }
			  }
		          if (tsize!= -1)
		             RETURN(fnt->info[fnt->charmapping[index]].widths[tsize]);
		      }
      case IC_ALL:    { 
			register5 struct charinfo *chr;
			chr= &fnt->info[fnt->charmapping[index]];
		        *sz_or_buf.buf=   *chr;
			RETURN(IC_NOERROR);
		      }
      otherwise:      WSGO1("_ic_CharInfo got bad attribute %d\n",attr);
      		      RETURN(IC_ILLEGAL);
   }
   RETURN(IC_ILLEGAL);
}

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

#define FACE_MATCH   1
#define WEIGHT_MATCH 2

int
_ic_Match(font,special)
register1 struct fontattr *font;
register2 struct fontattr *special;
{
register3 int matchval= 1;
char fface[20],sface[20];
char *fweight;
char *sweight;
char *tmp;

    D_ENTRY2(if_debug,"_ic_Match(%x,%x)\n",font,special);
    if (StrMatch(font->intname,special->intname))
       RETURN(FACE_MATCH+WEIGHT_MATCH);
    strcpy(fface,font->intname);
    fweight= index(fface,'.');
    if (fweight) {
	*fweight++= '\0';
	tmp= index(fweight,'.');
	if (tmp) *tmp++= '\0';
	else     fweight= NULL;
    }
    strcpy(sface,special->intname);
    sweight= index(sface,'.');
    if (sweight) {
	*sweight++= '\0';
	tmp= index(sweight,'.');
	if (tmp) *tmp++=   '\0';
	else      sweight= NULL;
    }
    if (StrMatch(fface,sface))
       matchval+= FACE_MATCH;
    if ((fweight==sweight)||StrMatch(fweight,sweight))
       matchval+= WEIGHT_MATCH;
    RETURN(matchval);
}

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

int
ic_Search(font,found,ch,attr,sz_or_buf)
register1 char         *font;
register5 char        **found;
register6 char         *ch;
register8 int           attr;
union { int size; struct charinfo *buf; } sz_or_buf;
{
register2 struct  fontattr   *fnt;
register4 int             index;
register3 char          **fspcl;
int itmp;
int itmp2;
struct fontattr *basefont= NULL;
int rtrn_val=  IC_NOTFOUND;
int match_val= 0;

    D_ENTRY5(if_debug,"ic_Search(%s,0x%x,%s,0x%x,0x%x)\n",font,found,ch,attr,
								   sz_or_buf);

#ifdef IP_AUTOINIT
    if (!i_ptrname)
       if (!ip_Load(NULL,NULL))
          WSGO("ic_Info can't load default DESC file\n");
#endif /* IP_AUTOINIT */

   if (ch[1]=='\0')	/* ASCII character */
      index= ch[0]-32;
   else  {		/* Named special character */
      index= (int)tr_Find(i_cnames,ch);
      if (!index)
          RETURN(IC_ILLEGAL);
      /* adjust for 96 printable ASCII & 1 added when index was stored */
      index+= 95;
   }
   fspcl= i_fspcl;
   *found= NULL;
   do {
       fnt=(struct fontattr *)tr_Find(i_finfo,font);
       if (!fnt) {
	   if_Load(font,NULL);
	   fnt= (struct fontattr *)tr_Find(i_finfo,font);
       }
       if (!basefont)
          basefont= fnt;
       if (fnt) {
	   itmp= _ic_CharInfo(fnt,index,attr,sz_or_buf);
	   if (itmp!=IC_NOTFOUND) {
	       itmp2= _ic_Match(basefont,fnt);
	       if (itmp2>match_val) {
		   *found= font;
		   match_val= itmp2;
		   rtrn_val=  itmp;
	       }
	   }
       }
       else {
	   WSGO1("Can't read ditroff width tables for %s\n",font);
       }
       font= *fspcl;
       fspcl++;
   } while (font);
   RETURN(rtrn_val);
}

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

     /*\
     |* Described in info.h
     \*/

int
ic_Info(font,ch,attr,sz_or_buf)
register2 char   *font;
register1 char   *ch;
register7 int     attr;
union { int size; struct charinfo *buf; } sz_or_buf;
{
register3 struct  fontattr   *fnt;
register4 int     index;

   D_ENTRY4(if_debug,"ic_Info(%s,%s,%x,%x)\n",font,ch,attr,sz_or_buf);

#ifdef IP_AUTOINIT
    if (!i_ptrname)
       if (!ip_Load(NULL,NULL))
          WSGO("ic_Info can't load default DESC file\n");
#endif /* IP_AUTOINIT */

   if (ch[1]=='\0')	/* ASCII character */
      index= ch[0]-32;
   else  {		/* Named special character */
      index= (int)tr_Find(i_cnames,ch);
      if (!index)
          RETURN(IC_ILLEGAL);
      /* adjust for 96 printable ASCII & 1 added when index was stored */
      index+= 95;
   }
   fnt=(struct fontattr *)tr_Find(i_finfo,font);
   if (!fnt) {
      if_Load(font,NULL);
      fnt= (struct fontattr *)tr_Find(i_finfo,font);
   }
   if (!fnt)
      RETURN(IF_NOTFOUND);
   RETURN(_ic_CharInfo(fnt,index,attr,sz_or_buf));
}

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


struct fontattr *
if_Info(font)
register1 char  *font;
{
register3 struct  fontattr   *fnt;

   D_ENTRY1(if_debug,"if_Info(%s)\n",font);
   fnt= (struct fontattr *)tr_Find(i_finfo,font);
   if (!fnt) {
       if_Load(font,NULL);
       fnt= (struct fontattr *)tr_Find(i_finfo,font);
   }
   if (fnt)
      RETURN(fnt);
   else
      RETURN(NULL);
}
