/* digraph.c */
/* Copyright 1995 by Steve Kirkendall */

char id_digraph[] = "$Id: digraph.c,v 2.8 1997/11/12 03:02:43 steve Exp $";

#include "elvis.h"
#ifndef NO_DIGRAPH

static void adjustctype P_((_CHAR_ ch));

/* This structure is used to store digraphs.  Note that "in1" is always less
 * than or equal to "in2".
 */
typedef struct dig_s
{
	struct dig_s	*next;		/* another digraph */
	CHAR		in1, in2;	/* the input characters of this digraph */
	CHAR		out;		/* the character they form */
	BOOLEAN		save;		/* user-defined? */
} DIGRAPH;


/* This is a list of all defined digraphs */
DIGRAPH *digs;


#ifdef NEED_CTYPE
CHAR elvct_upper[256] =
{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
  80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
  96, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
  80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90,123,124,125,126,127,
 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
};
CHAR elvct_lower[256] =
{  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
  16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
  32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
  64, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
 112,113,114,115,116,117,118,119,120,121,122, 91, 92, 93, 94, 95,
  96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111,
 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
 160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
 176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
 192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255
};
#define U  ELVCT_UPPER
#define L  ELVCT_LOWER
#define XU ELVCT_UPPER|ELVCT_XDIGIT
#define XL ELVCT_LOWER|ELVCT_XDIGIT
#define XD ELVCT_DIGIT|ELVCT_XDIGIT
#define P  ELVCT_PUNCT
#define S  ELVCT_SPACE
#define C  ELVCT_CNTRL
#define SC ELVCT_SPACE|ELVCT_CNTRL
CHAR elvct_class[256] =
{
   C,  C,  C,  C,  C,  C,  C,  C,  C, SC, SC,  C, SC, SC,  C,  C,
   C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,  C,
   S,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
  XD, XD, XD, XD, XD, XD, XD, XD, XD, XD,  P,  P,  P,  P,  P,  P,
   P, XU, XU, XU, XU, XU, XU,  U,  U,  U,  U,  U,  U,  U,  U,  U,
   U,  U,  U,  U,  U,  U,  U,  U,  U,  U,  U,  P,  P,  P,  P,  P,
   P, XL, XL, XL, XL, XL, XL,  L,  L,  L,  L,  L,  L,  L,  L,  L,
   L,  L,  L,  L,  L,  L,  L,  L,  L,  L,  L,  P,  P,  P,  P,  C,
   P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
   P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
   P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
   P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
   P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
   P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
   P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,
   P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P,  P
};
#undef U
#undef L
#undef XU
#undef XL
#undef XD
#undef P
#undef S
#undef C
#undef SC

static void adjustctype(ch)
	_CHAR_	ch;	/* a digraph character that changed */
{
	DIGRAPH	*dp;	/* the changed digraph, or NULL if deleted */
	CHAR	tmp;

	/* if ASCII, leave it alone */
	if (ch < 0x80)
		return;

	/* try to find a digraph that results in this character */
	for (dp = digs; dp && dp->out != ch; dp = dp->next)
	{
	}

	/* what kind of change? */
	if (dp && ((isupper(dp->in1) && !islower(dp->in2)) || isupper(dp->in2)))
	{
		/* making it uppercase */
		setupper(ch);
		clrlower(ch);
		clrpunct(ch);
		tmp = digraph(tolower(dp->in1), tolower(dp->in2));
		if (tmp >= 0x80)
		{
			/* we can make an uppercase/lowercase pair */
			toupper(tmp) = ch;
			tolower(ch) = tmp;
		}
	}
	else if (dp && ((!isupper(dp->in1) && islower(dp->in2)) || islower(dp->in1)))
	{
		/* making it lowercase */
		setlower(ch);
		clrupper(ch);
		clrpunct(ch);
		tmp = digraph(toupper(dp->in1), toupper(dp->in2));
		if (tmp >= 0x80)
		{
			/* we can make an uppercase/lowercase pair */
			tolower(tmp) = ch;
			toupper(ch) = tmp;
		}
	}
	else
	{
		/* deleting it, or making it punctuation */
		if (isupper(ch))
		{
			clrupper(ch);
			tmp = tolower(ch);
			tolower(ch) = ch;
			toupper(tmp) = tmp;
			clrupper(ch);
		}
		else if (tolower(ch))
		{
			clrlower(ch);
			tmp = toupper(ch);
			toupper(ch) = ch;
			tolower(tmp) = tmp;
			clrlower(ch);
		}
		setpunct(ch);
	}
}
#endif



/* This function looks up a digraph.  If it finds the digraph, it returns
 * the non-ASCII character; otherwise it returns the second parameter character.
 */
CHAR digraph(in1, in2)
	_CHAR_	in1;	/* the underlying character */
	_CHAR_	in2;	/* the second character */
{
	CHAR	newkey;
	DIGRAPH	*dp;

	/* remember the new key, so we can return it if this isn't a digraph */
	newkey = in2;

	/* sort in1 and in2, so that their original order won't matter */
	if (in1 > in2)
	{
		in2 = in1;
		in1 = newkey;
	}

	/* scan through the digraph chart */
	for (dp = digs;
	     dp && (dp->in1 != in1 || dp->in2 != in2);
	     dp = dp->next)
	{
	}

	/* if this combination isn't in there, just use the new key */
	if (!dp)
	{
		return newkey;
	}

	/* else use the digraph key */
	return dp->out;
}


/* This function lists, defines, or deletes digraphs.  If passed a NULL
 * pointer it will list the user-defined digraphs or all digraphs, depending
 * on the value of "bang."  If passed a 2-character string, it will delete
 * a digraph.  If passed a 3-character string, it will define a digraph.
 */
void digaction(win, bang, extra)
	WINDOW	win;	/* window to write to, if listing */
	BOOLEAN	bang;	/* list all, or define non-ASCII */
	CHAR	*extra;	/* NULL to list, "xx" to delete, "xxy" to add */
{
	int		dig;
	DIGRAPH		*dp;
	DIGRAPH		*prev;
	CHAR		listbuf[8];

	/* if no args, then display the existing digraphs */
	if (!extra)
	{
		listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' ';
		listbuf[7] = '\0';
		for (dig = 0, dp = digs; dp; dp = dp->next)
		{
			if (dp->save || bang)
			{
				dig += 7;
				if (dig >= o_columns(win))
				{
					drawextext(win, toCHAR("\n"), 1);
					dig = 7;
				}
				listbuf[3] = dp->in1;
				listbuf[4] = dp->in2;
				listbuf[6] = dp->out;
				drawextext(win, listbuf, 7);
			}
		}
		drawextext(win, toCHAR("\n"), 1);
		return;
	}

	/* make sure we have at least two characters */
	if (!extra[1])
	{
		msg(MSG_ERROR, "digraphs must be composed of two characters");
		return;
	}

	/* sort in1 and in2, so that their original order won't matter */
	if (extra[0] > extra[1])
	{
		dig = extra[0];
		extra[0] = extra[1];
		extra[1] = dig;
	}

	/* locate the new digraph character */
	for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++)
	{
	}
	dig = extra[dig];
	if (!bang && dig)
	{
		dig |= 0x80;
	}

	/* search for the digraph */
	for (prev = (DIGRAPH *)0, dp = digs;
	     dp && (dp->in1 != extra[0] || dp->in2 != extra[1]);
	     prev = dp, dp = dp->next)
	{
	}

	/* deleting the digraph? */
	if (!dig)
	{
		if (!dp)
		{
			return;
		}
		if (prev)
			prev->next = dp->next;
		else
			digs = dp->next;
#ifdef NEED_CTYPE
		adjustctype(dp->out);
#endif
		safefree(dp);
		return;
	}

	/* if necessary, create a new digraph struct for the new digraph */
	if (dig && !dp)
	{
		dp = (DIGRAPH *)safekept(1, sizeof *dp);
		if (prev)
			prev->next = dp;
		else
			digs = dp;
		dp->next = (DIGRAPH *)0;
	}

	/* assign it the new digraph value */
	dp->in1 = extra[0];
	dp->in2 = extra[1];
	dp->out = dig;
	dp->save = (BOOLEAN)(win != (WINDOW)0);
# ifdef NEED_CTYPE
	adjustctype(dig);
# endif
}

# ifdef FEATURE_MKEXRC
void digsave(buf)
	BUFFER	buf;	/* the buffer to append commands onto */
{
	static CHAR	text[] = "digraph! XX Y\n";
	DIGRAPH		*dp;
	MARK		append;

	append = markalloc(buf, o_bufchars(buf));
	for (dp = digs; dp; dp = dp->next)
	{
		if (dp->save)
		{
			text[9] = dp->in1;
			text[10] = dp->in2;
			text[12] = dp->out;
			bufreplace(append, append, text, QTY(text) - 1);
			markaddoffset(append, QTY(text) - 1);
		}
	}
	markfree(append);
}
# endif /* FEATURE_MKEXRC */
#endif
