text: /*
   * 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;
  }
  