/* roff.c general register handling code */

#include "nroff.h"

char	*getnreg();

getrname(name, ival)
char	*name;
int	*ival;
{
	int	c;
	c = getm();
	if(c == EOF)
		return(0);
	if(ival){
		if(c == '+' || c == '-'){
			*ival = c;
			if((c = getm()) == EOF)
				return(0);
		}
	}
	if(c == '('){
		if((c = getm()) == EOF)
			return(0);
		name[0] = c;
		if( (c = getm()) == EOF)
			return(0);
		name[1] = c;
	}
	else {
		name[0] = c;
		name[1] = '\0';
	}
	return(1);
}

/* numeric registers */

defreg(buf)
char	*buf;
{
	char	*p;
	char	name[2];
	struct	nregs	*np;
	int	sign = 0;
	int	val;

	if( (p = getnreg(buf, name)) == 0)
		return;
	if(*p == '\0')
		return;
	if(numregs == 0)
		initsvals();
	for(np = numregs ; np ; np = np->n_next)
		if(np->n_name[0] == name[0] && np->n_name[1] == name[1])
			break;
	if(np == 0){
		np = (struct nregs *)mymalloc(sizeof(struct nregs), TRUE);
		np->n_name[0] = name[0];
		np->n_name[1] = name[1];
		np->n_next = numregs;
		numregs = np;
	}
	if(np->n_status & NRO)
		return;
	if(*p == '+' || *p == '-')
		sign = *p++;
	val = eval(&p);
	if(sign == '+')
		np->n_value += val;
	else if(sign == '-')
		np->n_value -= val;
	else
		np->n_value = val;
	np->n_incr = 1;
	if(np->n_status & NSYS)
		return;
	while(*p == ' ')
		p++;
	if(*p != '\0')
		np->n_incr = eval(&p);
}

setnform(buf)
char	*buf;
{
	char	*p;
	char	name[2];
	struct	nregs	*np;

	if( (p = getnreg(buf, name)) == 0)
		return;
	if(*p == '\0')
		return;
	for(np = numregs ; np ; np = np->n_next)
		if(np->n_name[0] == name[0] && np->n_name[1] == name[1])
			break;
	if(np == 0)
		return;
	if(np->n_status & (NRO|NSYS))
		return;
	if(isdigit(*p)){
		for(np->n_digits = 0 ; isdigit(*p) ; p++)
			np->n_digits++;
		np->n_form = '1';
		return;
	}
	switch(*p){
	case 'a':
	case 'A':
	case 'i':
	case 'I':
		np->n_form = *p;
		return;
	}
}

char	*
getnreg(buf, name)
char	*buf, *name;
{
	char	*p = buf;
	char	*ttl = name;

	if(*p == '\0')
		return( (char *) 0);
	for(ttl = name; *p && *p != ' ' ; p++)
		if(ttl < name + 2)
			*ttl++ = *p;
	if(ttl == name + 1)
		*ttl = '\0';
	while(*p == ' ')
		p++;
	return(p);
}

numread()
{
	struct	nregs	*np;
	char	name[2];
	int	sign = 0;
	char	tbuf[20];
	char	*p;
	int	val;
	int	i;
	struct	source	*mp;
	char	*setroman();

	if(getrname(name, &sign) == 0)
		return;

	if(numregs == 0)	/* if not used yet, set up system regs */
		initsvals();

	for(np = numregs ; np ; np = np->n_next)
		if(np->n_name[0] == name[0] && np->n_name[1] == name[1])
			break;
	if(np == 0)	/* register not found */
		return;
	if(np->n_status & NSYS)
		getsvals(np);
	if( (np->n_status & NRO) == 0 && sign){
		if(sign == '+')
			np->n_value += np->n_incr;
		else
			np->n_value -= np->n_incr;
	}
	val = np->n_value;
	switch(np->n_form){
	default:
		np->n_digits = 1;
		np->n_form = '1';
		/* fall through */
	case '1':
		/* arabic */
		i = np->n_digits;
		if(i == 0)
			i = 1;
		else if(i > 15)
			i = 15;
		p = &tbuf[15];
		sprintf(p, "%d", val);
		i -= strlen(p);
		while(i-- > 0)
			*--p = '0';
		break;
	case 'i':
	case 'I':
		p = setroman(tbuf, val, np->n_form == 'I');
		break;
	case 'a':
	case 'A':
		p = &tbuf[19];
		*p-- = '\0';
		if(val <= 0)
			*p = '0';
		else {
			i = val-1;
			do {
				*p-- = i % 26 + np->n_form;
			} while(i /= 26);
			p++;
		}
		break;
	}
	/* wow !! got number in p - put into output queue */
	mp = (struct source *)mymalloc(sizeof(struct source), TRUE);
	mp->m_val = mymalloc(strlen(p) + 1, FALSE);
	strcpy(mp->m_val, p);
	mp->m_curp = mp->m_val;
	mp->m_last = curmacro;
	curmacro = mp;
	if(sign && (np->n_status & (NRO|NSYS)) == NSYS)
		rsetsvals(np);
}

/* convert val to a string in roman numerals */

char	*
setroman(tbuf, val, ucase)
char	*tbuf;
{
	char	*tlets;
	char	*p;

	p = tbuf + 15;
	*p-- = '\0';
	if(val > 3999)
		val = 3999;
	else if(val <= 0){
		*p = '0';
		return(p);
	}
	if(ucase)
		tlets = "IVXLCDM";
	else
		tlets = "ivxlcdm";

	for(; val ; val /= 10, tlets += 2){
		switch(val % 10){
		case 0:
			continue;
		case 3:
			*p-- = *tlets;
		case 2:
			*p-- = *tlets;
		case 1:
			*p-- = *tlets;
			continue;
		case 4:
			*p-- = tlets[1];
			*p-- = *tlets;
			continue;
		case 8:
			*p-- = *tlets;
		case 7:
			*p-- = *tlets;
		case 6:
			*p-- = *tlets;
		case 5:
			*p-- = tlets[1];
			continue;
		case 9:
			*p-- = tlets[2];
			*p-- = *tlets;
			continue;
		}
	}
	return(++p);
}

/* regread, called from ngets to set redirection */

regread()
{
	struct	source	*findreg();
	char	name[2];
	struct	source	*mp;
	int	c;

	if(getrname(name, (int *)0) == 0)
		return;
	mp = findreg(name, FALSE);
	if(mp == 0)
		return;
	if( (mp->m_type & ASTR) == 0)
		return;
	if(mp->m_last != 0)	/* already in use */
		return;
	mp->m_last = curmacro;	/* invoke the macro */
	curmacro = mp;
	curmacro->m_curp = mp->m_val;
}

struct	source	*
findreg(str, tocreat)
char	*str;
{
	char	c = str[1];
	struct	source	*mp;

	if(c == ' ')
		c = '\0';
	for(mp = regstrs ; mp ; mp = mp->m_next)
		if(mp->m_name[0] == *str && mp->m_name[1] == c)
			return(mp);
	if(!tocreat)
		return(mp);
	mp = (struct source *)mymalloc(sizeof(struct source), TRUE);
	mp->m_next = regstrs;
	regstrs = mp;
	mp->m_name[0] = *str;
	mp->m_name[1] = c;
	return(mp);
}
	
/* define a string */

defstr(buf, cmd)
char	*buf;
{
	char	name[2];
	char	*ttl = name;
	struct	source	*mp;
	int	len;
	int	c;
	int	olen;
	
	if(*buf == '\0')
		return;
	for(;*buf && *buf != ' '; buf++)
		if(ttl < name + 2)
			*ttl++ = *buf;
	if(ttl == name + 1)
		*ttl = '\0';
	while(*buf == ' ')
		buf++;
	if(*buf == '"')
		c = *buf++;
	else
		c = ' ';
	ttl = buf;
	while(*buf && *buf != c)
		buf++;
	*buf = '\0';
	mp = findreg(name, TRUE);
	if(mp->m_type == 0)
		mp->m_type = AREG|ASTR;
	else
		if((mp->m_type & (ASTR|AREG|ARO)) != (ASTR|AREG)) 
			return;
	/*
	 * shame about the current string !! can't do
	 */
	if(mp->m_last || mp == curmacro || mp == curdiv)
		return;
	
	if(cmd != AS){			/* must junk original str */
		if(mp->m_val)
			myfree(mp->m_val);
		mp->m_val = 0;
	}
	len = buf - ttl + 1;
	if(mp->m_val == 0){
		mp->m_val = mymalloc(len, FALSE);
		strcpy(mp->m_val, ttl);
	}
	else {
		olen = strlen(mp->m_val);
		buf = mymalloc(olen + len, FALSE);
		strcpy(buf, mp->m_val);
		myfree(mp->m_val);
		mp->m_val = buf;
		strcpy(mp->m_val + olen, ttl);
	}
}
