/*
* t6.c
*
* width functions, sizes and fonts
*/

#include "tdef.h"
#include "fns.h"
#include "ext.h"

int     fontlab[MAXFONTS+1];
int     cstab[MAXFONTS+1];
int     ccstab[MAXFONTS+1];
int     bdtab[MAXFONTS+1];
int     sbold = 0;

t_width(Tchar j)
{
       int i, k;

       if (iszbit(j))
               return 0;
       if (ismot(j)) {
               if (isvmot(j))
                       return(0);
               k = absmot(j);
               if (isnmot(j))
                       k = -k;
               return(k);
       }
       i = cbits(j);
       if (i < ' ') {
               if (i == '\b')
                       return(-widthp);
               if (i == PRESC)
                       i = eschar;
               else if (i == HX)
                       return(0);
       }
       if (i == ohc)
               return(0);
       i = trtab[i];
       if (i < ' ')
               return(0);
       if (sfbits(j) == oldbits) {
               xfont = pfont;
               xpts = ppts;
       } else
               xbits(j, 0);
       if (i < nchnames + ALPHABET && widcache[i].fontpts == (xfont<<8) + xpts && !setwdf)
               k = widcache[i].width;
       else {
               k = getcw(i);
               if (bd)
                       k += (bd - 1) * HOR;
               if (cs)
                       k = cs;
       }
       widthp = k;
       return(k);
}

/*
* clear width cache-- s means just space
*/
void zapwcache(int s)
{
       int i;

       if (s) {
               widcache[' '].fontpts = 0;
               return;
       }
       for (i=0; i<NWIDCACHE; i++)
               widcache[i].fontpts = 0;
}

onfont(int n, int f)    /* is char n on font f? */
{
       int i;
       Font *fp = &fonts[f];
       Chwid *cp, *ep;
       char *np;

       if (n < ALPHABET) {
               if (fp->wp[n].num == n) /* ascii at front */
                       return n;
               else
                       return -1;
       }
       cp = &fp->wp[ALPHABET];
       ep = &fp->wp[fp->nchars];
       for ( ; cp < ep; cp++)  /* search others */
               if (cp->num == n)
                       return cp - &fp->wp[0];
       /* maybe it was a \N... */
       np = chname(n);
       if (*np == Number) {
               i = atoi(np+1);         /* sscanf(np+1, "%d", &i); */
               cp = &fp->wp[0];
               ep = &fp->wp[fp->nchars];
               for ( ; cp < ep; cp++) {        /* search others */
                       if (cp->code == i)
                               return cp - &fp->wp[0];
               }
               return -2;      /* a \N that doesn't have an entry */
       }
       return -1;      /* vanilla not found */
}

getcw(int i)
{
       int k, n, x;
       Font *fp;
       int nocache = 0;
       if (i < ' ')
               return 0;
       bd = 0;
       fp = &fonts[xfont];
       if (i == ' ') { /* a blank */
               k = (fp->spacewidth * spacesz + 6) / 12;
               /* this nonsense because .ss cmd uses 1/36 em as its units */
               /*      and default is 12 */
       } else if ((n = onfont(i, xfont)) >= 0) {       /* on this font at n */
               k = fp->wp[n].wid;
               if (setwdf)
                       numtabp[CT].val |= fp->wp[n].kern;
       } else if (n == -2) {           /* \N with default width */

               k = fp->defaultwidth;
       } else {                        /* not on current font */
               nocache = 1;
               k = fp->defaultwidth;   /* default-size space */
               if (smnt) {
                       int ii, jj;
                       for (ii=smnt, jj=0; jj < nfonts; jj++, ii=ii % nfonts + 1) {
                               if ((n = onfont(i, ii)) >= 0) {
                                       k = fonts[ii].wp[n].wid;
                                       if (xfont == sbold)
                                               bd = bdtab[ii];
                                       if (setwdf)
                                               numtabp[CT].val |= fonts[ii].wp[n].kern;
                                       break;
                               }
                       }
               }
       }
       if (!bd)
               bd = bdtab[xfont];
       if (cs = cstab[xfont]) {
               nocache = 1;
               if (ccs = ccstab[xfont])
                       x = ccs;
               else
                       x = xpts;
               cs = (cs * EMPTS(x)) / 36;
       }
       /* was (k & BYTEMASK);  since .wid is unsigned, should never happen */
       if (k < 0)
               ERROR "can't happen: negative width %d in getcw %d\n", k, i WARN;
       k = (k * xpts + (Unitwidth / 2)) / Unitwidth;
       if (nocache|bd)
               widcache[i].fontpts = 0;
       else {
               widcache[i].fontpts = (xfont<<8) + xpts;
               widcache[i].width = k;
       }
       return(k);
       /* Unitwidth is Units/Point, where
       /* Units is the fundamental digitization
       /* of the character set widths, and
       /* Point is the number of goobies in a point
       /* e.g., for cat, Units=36, Point=6, so Unitwidth=36/6=6
       /* In effect, it's the size at which the widths
       /* translate directly into units.
       */
}

void xbits(Tchar i, int bitf)
{
       int k;

       if(TROFF) {
               xfont = fbits(i);
               k = sbits(i);
               if(k) {
                       xpts = pstab[k-1];
                       oldbits = sfbits(i);
                       pfont = xfont;
                       ppts = xpts;
                       return;
               }
               switch(bitf) {
               case 0:
                       xfont = font;
                       xpts = pts;
                       break;
               case 1:
                       xfont = pfont;
                       xpts = ppts;
                       break;
               case 2:
                       xfont = mfont;
                       xpts = mpts;
               }
       }
}


/* these next two functions ought to be the same in troff and nroff, */
/* but the data structures they search are different. */
/* silly historical problem. */


Tchar t_setch(int c)
{
       int j;
       char temp[50];
       char *s;

       s = temp;
       if (c == '(') { /* \(xx */
               if ((*s++ = getach()) == 0 || (*s++ = getach()) == 0)
                       return(0);
       } else {        /* \C'...' */
               c = getach();
               while ((*s = getach()) != c && *s != 0 && s < temp + sizeof(temp) - 1)
                       s++;
       }
       *s = '\0';
#ifdef UNICODE
       return chadd(temp, Troffchar, Install) | chbits; /* add name even if haven't seen it */
#else
       if (NROFF) {
               j = chadd(temp, Troffchar, Lookup);
               if ( j == -1)
                       return 0;
               else
                       return j | chbits;
       } else
               return chadd(temp, Troffchar, Install) | chbits; /* add name even if haven't seen it */

#endif /*UNICODE*/
}

Tchar t_setabs(void)            /* set absolute char from \N'...' */
{
       int n;
       char temp[10];

       getch();        /* delim */
       n = 0;
       n = inumb(&n);
       getch();        /* delim */
       if (nonumb)
               return 0;
       sprintf(temp, "%d", n); /* convert into "#n" */
       n = chadd(temp, Number, Install);
       return n | chbits;
}


/*
* fontlab[] is a cache that contains font information
* for each font.
* fontlab[] contains the 1- or 2-character name of the
* font current associated with that font.
* fonts 1..nfonts correspond to the mounted fonts;
* the last of these are the special fonts.
* If we don't use the (named) font in one of the
* standard positions, we install the name in the next
* free slot of fontlab[] and font[].
* Whenever we need info about the font, we
* read in the data into the next free slot with getfont.
* The ptfont() (t10.c) routine will tell
* the device filter to put the font always at position
* zero if xfont > nfonts, so no need to change these filters.
* Yes, this is a bit kludgy.
*
* This gives the new specs of findft:
*      find the font name i, where i also can be a number.
*      Installs the font(name) i when not present
*      returns -1 on error
*/


t_findft(int i)
{
       int k;
       Uchar *p;

       p = unpair(i);

       if (isdigit(p[0])) {            /* first look for numbers */
               k = p[0] - '0';
               if (p[1] > 0 && isdigit(p[1]))
                       k = 10 * k + p[1] - '0';
               if (k > 0 && k <= nfonts && k < smnt)
                       return(k);      /* mounted font:  .ft 3 */
               if (fontlab[k] && k <= MAXFONTS) {      /* translate */
                       return(k);                      /*number to a name */
               } else {
                       fprintf(stderr, "troff: no font at position %d\n", k);
                       return(-1);     /* wild number */
               }
       }

       /*
        * Now we look for font names
        */
       for (k = 1; fontlab[k] != i; k++) {
               if (k > MAXFONTS)
                       return(-1);     /* running out of fontlab space */
               if (fontlab[k] == 0) {  /* passed all existing names */
                       if (setfp(k, i, (char *) 0, 1) == -1)
                               return(-1);
                       else {
                               fontlab[k] = i; /* install the name */
                               return(k);
                       }
               }
       }
       return(k);                      /* was one of the existing names */
}


void caseps(void)
{
       int i;

       if (TROFF) {
               if(skip())
                       i = apts1;
               else {
                       noscale++;
                       i = inumb(&apts);       /* this is a disaster for fractional point sizes */
                       noscale = 0;
                       if(nonumb)
                               i = apts1;
               }
               casps1(i);
       }
}


void casps1(int i)
{

/*
* in olden times, it used to ignore changes to 0 or negative.
* this is meant to allow the requested size to be anything,
* in particular so eqn can generate lots of \s-3's and still
* get back by matching \s+3's.

       if (i <= 0)
               return;
*/
       apts1 = apts;
       apts = i;
       pts1 = pts;
       pts = findps(i);
       mchbits();
}


findps(int i)
{
       int j, k;

       for (j=k=0 ; pstab[j] != 0 ; j++)
               if (abs(pstab[j]-i) < abs(pstab[k]-i))
                       k = j;

       return(pstab[k]);
}


void t_mchbits(void)
{
       int i, j, k;

       i = pts;
       for (j = 0; i > (k = pstab[j]); j++)
               if (!k) {
                       j--;
                       break;
               }
       chbits = 0;
       setsbits(chbits, ++j);
       setfbits(chbits, font);
       sps = width(' ' | chbits);
       zapwcache(1);
}

void t_setps(void)
{
       int i, j;

       i = cbits(getch());
       if (isdigit(i)) {               /* \sd or \sdd */
               i -= '0';
               if (i == 0)             /* \s0 */
                       j = apts1;
               else if (i <= 3 && (ch=getch()) && isdigit(j = cbits(ch))) {    /* \sdd */
                       j = 10 * i + j - '0';
                       ch = 0;
               } else          /* \sd */
                       j = i;
       } else if (i == '(') {          /* \s(dd */
               j = cbits(getch()) - '0';
               j = 10 * j + cbits(getch()) - '0';
               if (j == 0)             /* \s(00 */
                       j = apts1;
       } else if (i == '+' || i == '-') {      /* \s+, \s- */
               j = cbits(getch());
               if (isdigit(j)) {               /* \s+d, \s-d */
                       j -= '0';
               } else if (j == '(') {          /* \s+(dd, \s-(dd */
                       j = cbits(getch()) - '0';
                       j = 10 * j + cbits(getch()) - '0';
               }
               if (i == '-')
                       j = -j;
               j += apts;
       }
       casps1(j);
}


Tchar t_setht(void)             /* set character height from \H'...' */
{
       int n;
       Tchar c;

       getch();
       n = inumb(&apts);
       getch();
       if (n == 0 || nonumb)
               n = apts;       /* does this work? */
       c = CHARHT;
       c |= ZBIT;
       setsbits(c, n);
       setfbits(c, pts);       /* sneaky, CHARHT font bits are size bits */
       return(c);
}

Tchar t_setslant(void)          /* set slant from \S'...' */
{
       int n;
       Tchar c;

       getch();
       n = 0;
       n = inumb(&n);
       getch();
       if (nonumb)
               n = 0;
       c = SLANT;
       c |= ZBIT;
       setsfbits(c, n+180);
       return(c);
}


void caseft(void)
{
       if (!TROFF) {
               n_caseft();
               return;
       }
       skip();
       setfont(1);
}


void t_setfont(int a)
{
       int i, j;

       if (a)
               i = getrq();
       else
               i = getsn();
       if (!i || i == 'P') {
               j = font1;
               goto s0;
       }
       if (/* i == 'S' || */ i == '0') /* an experiment -- why can't we change to it? */
               return;
       if ((j = findft(i)) == -1)
               if ((j = setfp(0, i, (char*) 0, 1)) == -1)      /* try to put it in position 0 */
                       return;
s0:
       font1 = font;
       font = j;
       mchbits();
}


void t_setwd(void)
{
       int base, wid;
       Tchar i;
       int delim, emsz, k;
       int savhp, savapts, savapts1, savfont, savfont1, savpts, savpts1;

       base = numtabp[ST].val = numtabp[SB].val = wid = numtabp[CT].val = 0;
       if (ismot(i = getch()))
               return;
       delim = cbits(i);
       savhp = numtabp[HP].val;
       numtabp[HP].val = 0;
       savapts = apts;
       savapts1 = apts1;
       savfont = font;
       savfont1 = font1;
       savpts = pts;
       savpts1 = pts1;
       setwdf++;
       while (cbits(i = getch()) != delim && !nlflg) {
               k = width(i);
               wid += k;
               numtabp[HP].val += k;
               if (!ismot(i)) {
                       emsz = (INCH/72) * xpts;
               } else if (isvmot(i)) {
                       k = absmot(i);
                       if (isnmot(i))
                               k = -k;
                       base -= k;
                       emsz = 0;
               } else
                       continue;
               if (base < numtabp[SB].val)
                       numtabp[SB].val = base;
               if ((k = base + emsz) > numtabp[ST].val)
                       numtabp[ST].val = k;
       }
       setn1(wid, 0, (Tchar) 0);
       numtabp[HP].val = savhp;
       apts = savapts;
       apts1 = savapts1;
       font = savfont;
       font1 = savfont1;
       pts = savpts;
       pts1 = savpts1;
       mchbits();
       setwdf = 0;
}


Tchar t_vmot(void)
{
       dfact = lss;
       vflag++;
       return t_mot();
}


Tchar t_hmot(void)
{
       dfact = EM;
       return t_mot();
}


Tchar t_mot(void)
{
       int j, n;
       Tchar i;

       j = HOR;
       getch(); /*eat delim*/
       if (n = atoi0()) {
               if (vflag)
                       j = VERT;
               i = makem(quant(n, j));
       } else
               i = 0;
       getch();
       vflag = 0;
       dfact = 1;
       return(i);
}


Tchar t_sethl(int k)
{
       int j;
       Tchar i;

       j = EM / 2;
       if (k == 'u')
               j = -j;
       else if (k == 'r')
               j = -2 * j;
       vflag++;
       i = makem(j);
       vflag = 0;
       return(i);
}


Tchar t_makem(int i)
{
       Tchar j;

       if (i >= 0)
               j = i;
       else
               j = -i;
       if (Hor > 1 && !vflag)
               j = (j + Hor/2)/Hor * Hor;
       j |= MOT;
       if (i < 0)
               j |= NMOT;
       if (vflag)
               j |= VMOT;
       return(j);
}


Tchar getlg(Tchar i)
{
       Tchar j, k;
       int lf;

       if (!TROFF)
               return i;
       if ((lf = fonts[fbits(i)].ligfont) == 0) /* font lacks ligatures */
               return(i);
       j = getch0();
       if (cbits(j) == 'i' && (lf & LFI))
               j = LIG_FI;
       else if (cbits(j) == 'l' && (lf & LFL))
               j = LIG_FL;
       else if (cbits(j) == 'f' && (lf & LFF)) {
               if ((lf & (LFFI|LFFL)) && lg != 2) {
                       k = getch0();
                       if (cbits(k)=='i' && (lf&LFFI))
                               j = LIG_FFI;
                       else if (cbits(k)=='l' && (lf&LFFL))
                               j = LIG_FFL;
                       else {
                               *pbp++ = k;
                               j = LIG_FF;
                       }
               } else
                       j = LIG_FF;
       } else {
               *pbp++ = j;
               j = i;
       }
       return(i & SFMASK | j);
}


void caselg(void)
{

       if(TROFF) {
               skip();
               lg = atoi0();
               if (nonumb)
                       lg = 1;
       }
}

void casefp(void)
{
       int i, j;

       if (!TROFF) {
               n_casefp();
               return;
       }
       skip();
       i = cbits(getch());
       if (isdigit(i)) {
               i -= '0';
               j = cbits(getch());
               if (isdigit(j))
                       i = 10 * i + j - '0';
       }
       if (i <= 0 || i > nfonts)
               ERROR "fp: bad font position %d", i WARN;
       else if (skip() || !(j = getrq()))
               ERROR "fp: no font name" WARN;
       else if (skip() || !getname())
               setfp(i, j, (char*) 0, 1);
       else            /* 3rd argument = filename */
               setfp(i, j, nextf, 1);
}

char *strdupl(const char *s)    /* make a copy of s */
{
       char *t;

       t = (char *) malloc(strlen(s) + 1);
       if (t == NULL)
               ERROR "out of space in strdupl(%s)", s FATAL;
       strcpy(t, s);
       return t;
}

setfp(int pos, int f, char *truename, int print)        /* mount font f at position pos[0...nfonts] */
{
       char pathname[NS], shortname[NS], *sl;

       zapwcache(0);
       if (truename)
               strcpy(shortname, truename);
       else
               strcpy(shortname, (char *) unpair(f));
       if (truename && strrchr(truename, '/')) {       /* .fp 1 R dir/file: use verbatim */
               sprintf(pathname, "%s", truename);
               if (fonts[pos].truename)
                       free(fonts[pos].truename);
               fonts[pos].truename = strdupl(truename);
       } else if (truename) {                  /* synonym: .fp 1 R Avant */
               sprintf(pathname, "%s/dev%s/%s", fontdir, devname, truename);
               truename = 0;   /* so doesn't get repeated by ptfpcmd */
       } else                                  /* vanilla: .fp 5 XX */
               sprintf(pathname, "%s/dev%s/%s", fontdir, devname, shortname);
       if (truename == 0 && fonts[pos].truename != 0) {
               free(fonts[pos].truename);
               fonts[pos].truename = 0;
       }
       if (getfont(pathname, pos) < 0) {
               ERROR "Can't open font file %s", pathname WARN;
               return -1;
       }
       if (print && !ascii) {
               ptfpcmd(pos, fonts[pos].longname, truename);
               ptfont();
       }
       if (pos == smnt) {
               smnt = 0;
               sbold = 0;
       }
       fontlab[pos] = f;
       if (smnt == 0 && fonts[pos].specfont)
               smnt = pos;
       bdtab[pos] = cstab[pos] = ccstab[pos] = 0;
       return pos;
}

/*
* .cs request; don't check legality of optional arguments
*/
void casecs(void)
{
       int i, j;

       if (TROFF) {
               int savtr = trace;

               trace = 0;
               noscale++;
               skip();
               if (!(i = getrq()) || (i = findft(i)) < 0)
                       goto rtn;
               skip();
               cstab[i] = atoi0();
               skip();
               j = atoi0();
               if(nonumb)
                       ccstab[i] = 0;
               else
                       ccstab[i] = findps(j);
       rtn:
               zapwcache(0);
               noscale = 0;
               trace = savtr;
       }
}


void casebd(void)
{
       int i, j, k;

       if (!TROFF) {
               n_casebd();
               return;
       }
       zapwcache(0);
       k = 0;
bd0:
       if (skip() || !(i = getrq()) || (j = findft(i)) == -1) {
               if (k)
                       goto bd1;
               else
                       return;
       }
       if (j == smnt) {
               k = smnt;
               goto bd0;
       }
       if (k) {
               sbold = j;
               j = k;
       }
bd1:
       skip();
       noscale++;
       bdtab[j] = atoi0();
       noscale = 0;
}


void casevs(void)
{
       int i;

       if (!TROFF) {
               n_casevs();
               return;
       }
       skip();
       vflag++;
       dfact = INCH; /* default scaling is points! */
       dfactd = 72;
       res = VERT;
       i = inumb(&lss);
       if (nonumb)
               i = lss1;
       if (i < VERT)
               i = VERT;
       lss1 = lss;
       lss = i;
}


void casess(void)
{
       int i;

       if(TROFF) {
               noscale++;
               skip();
               if(i = atoi0()) {
                       spacesz = i & 0177;
                       zapwcache(0);
                       sps = width(' ' | chbits);
               }
               noscale = 0;
       }
}


Tchar t_xlss(void)
{
       /* stores \x'...' into two successive Tchars.
       /* the first contains HX, the second the value,
       /* encoded as a vertical motion.
       /* decoding is done in n2.c by pchar().
       */
       int i;

       getch();
       dfact = lss;
       i = quant(atoi0(), VERT);
       dfact = 1;
       getch();
       if (i >= 0)
               *pbp++ = MOT | VMOT | i;
       else
               *pbp++ = MOT | VMOT | NMOT | -i;
       return(HX);
}

Uchar *unpair(int i)
{
       static Uchar name[3];

       name[0] = i & SHORTMASK;
       name[1] = (i >> SHORT) & SHORTMASK;
       name[2] = 0;
       return name;
}