/*
 *		optcfg.exe		dviprt.exe の用に、プリンタに合わせたコンフィギュレ
 *						ーション・ファイルを作る。
 *
 *		April 6, 1990,  by SHIMA
 *		Nov  23, 1990   T,I options
 *      Dec  21, 1990   D option
 *		Jan  28, 1991   line_feed, form_feed, after_bit_image, M option
 *		Feb   7, 1991   LEFT_IS_HIGH, LEFT_IS_LOW (Change format Ver 2.0)
 *		Feb  20, 1992   bit_row_header (for Kyocera L-880, HP LaserJet+) by Oh-Yeah?
 **		Mar  23, 1992	dpi by sempa
 **		Dec   6, 1992	HEX_MODE (for XEROX ART mode) by I. Matsuda (Ver 2.2)
 **		Jan  17, 1993	Slightly modified by I. Matsuda (Ver 2.3)
 *      Aug  16, 1993   Slightly modified by SHIMA(Ver 2.4)
 */

#include	<stdio.h>
#include	<stdlib.h>
#include	<ctype.h>
#include	<string.h>

#define	SRC_EXT	"src"
#define	BIN_EXT	"cfg"

#define	is_space(c)	(c==' '||c=='\t'||c=='\n'||c==13)
#define	is_hex(c)	((c>='0'&&c<='9')||(c>='a'&&c<='f')||(c>='A'&&c<='F'))
#define	is_oct(c)	(c>='0'&&c<='7')
#define	is_dig(c)	(c>='0'&&c<='9')
#define	is_key(c)	((c>='a'&&c<='z')||c=='_')
#define	is_comment(c)	(c==';')

#define	hex_to_dig(c)	((c>='0'&&c<='9')?(c-'0'):(toupper(c)-('A'-10)))
#define	to_dig(c)	(c-'0')

#define	LINE_BUF_SIZE	7000
#define	CFG_BUF_SIZE	7000
#define	MAX_DEF			1000
#define	FNAME_SIZE		150

#define	TYPE_BIT		0xc0

#define	NO_NUM			0
#define	BINARY_LTOH		1
#define	BINARY_HTOL		2
#define	DECIMAL_3		3
#define	DECIMAL_4		4
#define	DECIMAL_5		5
#define	DECIMAL_V		6

#define	TOTAL_BYTE		0x80
#define	ISO_NUMBER		0x40
#define	DIVIDEBY_2		0x10
#define	DIVIDE_ALL		0x30
#define	MULT_CONST		0x08

#define	OK 		 0
#define	TRUE	 1
#define	ERROR	-1

enum {
	NAME,
	UPPER_POSITION,
	PINS,
	MINIMAL_UNIT,
	MAXIMAL_UNIT,
	DPI,
	CONSTANT,
	BIT_IMAGE_MODE,
	NORML_MODE,
	SEND_BIT_IMAGE,
	SKIP_SPACES,
	LINE_FEED,
	FORM_FEED,
	AFTER_BIT_IMAGE,
	BIT_ROW_HEADER,
	NO_MATCH
};

static unsigned char *prtopt[] =
{
	"name",
	"upper_position",
	"pins",
	"minimal_unit",
	"maximal_unit",
	"dpi",
	"constant",
	"bit_image_mode",
	"normal_mode",
	"send_bit_image",
	"skip_spaces",
	"line_feed",
	"form_feed",
	"after_bit_image",
	"bit_row_header",
	""
};

enum {
	HIGH_BIT,
	LOW_BIT,
	LEFT_IS_HIGH,
	LEFT_IS_LOW,
	NON_MOVING,
	HEX_MODE
};

struct {
	unsigned char *symbol, bit_flag;
}

u_position[] = {
	{
		"HIGH_BIT", 0x00
	}
	,
	{
		"LOW_BIT", 0x80
	}
	,
	{
		"LEFT_IS_HIGH", 0x40
	}
	,
	{
		"LEFT_IS_LOW", 0xc0
	}
	,
	{
		"NON_MOVING", 0x20
	}
	,
	{
		"HEX_MODE", 0x10
	}
};

void usage();
void print_key(int, unsigned char *);
void trans_cfg();
int trans_src(FILE *);
int to_num(unsigned char *);
unsigned char *skip_space(unsigned char *);
unsigned char *strchk(unsigned char *, unsigned char *);
int trns_str(unsigned char *, unsigned char *);
char *readline(unsigned char *, int, FILE *);
FILE *fropen(int, char *);

char fname[FNAME_SIZE];
char *ext_ptr;
unsigned char line_buf[LINE_BUF_SIZE];
unsigned char cfg_buf[CFG_BUF_SIZE];
unsigned char string_buf[NO_MATCH - BIT_IMAGE_MODE][MAX_DEF + 4];
int flag[NO_MATCH];
int f_M;
int f_r_format;

FILE *
 fropen(int flag, char *name)
{
	FILE *frpt;
	char *dest;
	int i;

	dest = fname;

	for (i = FNAME_SIZE - 4; ((*dest++ = *name++) != 0) && (i > 0); i--) {
		if (*name == '.')
			ext_ptr = dest + 1;
	}

	if (ext_ptr == NULL) {
		ext_ptr = dest;
		if (flag == 0) {
			strcat(fname, "." BIN_EXT);
		}
		else {
			strcat(fname, "." SRC_EXT);
		}
	}
	if ((frpt = fopen(fname, "rb")) == NULL) {
		fprintf(stderr, "%s doesn't exist!\n", fname);
		exit(1);
	}
	return (frpt);
}

void main(int argc, char **argv)
{
	int i, j;
	FILE *frout, *frpt;

	if (argc == 3 && strcmp(argv[1], "-r") == 0) {
		frpt = fropen(0, argv[2]);
		fread(cfg_buf, CFG_BUF_SIZE, 1, frpt);
		trans_cfg();
	}
	else if (argc == 2) {
		ext_ptr = argv[1];
		while (*ext_ptr != '.' && *ext_ptr > '\0')
			ext_ptr++;
		if (strcmp(ext_ptr, "." BIN_EXT) == 0) {
			frpt = fropen(0, argv[1]);
			fread(cfg_buf, CFG_BUF_SIZE, 1, frpt);
			trans_cfg();
		}
		else {
			frpt = fropen(1, argv[1]);
			*ext_ptr = 0;
			strcat(fname, BIN_EXT);
			if ((frout = fopen(fname, "wb")) == NULL) {
				fprintf(stderr, "Cannot open %s!", fname);
				exit(1);
			}
			for (i = 0; i < CFG_BUF_SIZE; i++)	/*	clear buffer */
				cfg_buf[i] = 0;
			i = trans_src(frpt);
			if (fwrite(cfg_buf, 1, i, frout) != i) {
				fprintf(stderr, "Cannot write data to %s!", fname);
				exit(1);
			}
			else {
				fprintf(stderr, "%s is made!", fname);
			}
		}
	}
	else
		usage();
	exit(0);
}

void usage()
{
	int i;

	fprintf(stderr,
			"optcfg  ver 2.4, file.%s <-> file.%s for dviprt.exe "
			"(TeX Device Driver)\n"
			"\t1990-93 written by SHIMA,\n"
			"\t1992-93 changed by Oh-Yeah?, sempa, Matsuda\n\n"
			"Usage: optcfg file\n"
			"\t\tTranslate file.%s to file.%s for dviprt.exe\n\n"
			"       optcfg -r file\n"
			"\t\tRead file.%s\n\n"
			"\t\t\t<<< key words >>>\n",
			SRC_EXT, BIN_EXT, SRC_EXT, BIN_EXT, BIN_EXT );
	i = 0;
	while (*prtopt[i]) {
		fprintf(stderr, "%-16s", prtopt[i++]);
	}
	fprintf(stderr, "\n\nupper_position  %s / %s / %s / %s\n\t\t(%s, %s)\n",
			u_position[HIGH_BIT].symbol,
			u_position[LOW_BIT].symbol,
			u_position[LEFT_IS_HIGH].symbol,
			u_position[LEFT_IS_LOW].symbol,
			u_position[NON_MOVING].symbol,
			u_position[HEX_MODE].symbol);
	fprintf(stderr,
		 "code     \\123 (decimal), \\x12 (hexadecimal), \\012 (octal)\n"
			"special  \\ESC \\SP\n"
			"binary   \\b2 (low,high) \\B2 (high,low)\n"
			"decimal  \\d3 \\d4 \\d5 \\d?\n"
			"M T D DD DDD I followed by \\b2 ... \\d5, \\d?\n"
		);
	exit(1);
}

void print_key(int key, unsigned char *string)
{
	int i;
	static int max_len;

	if (key == 0) {
		max_len = 0;
		for (i = 0; i < NO_MATCH; i++) {
			max_len = (strlen(prtopt[i]) > max_len) ?
				strlen(prtopt[i]) : max_len;
		}
	}
	printf("%s ", prtopt[key]);
	for (i = max_len - strlen(prtopt[key]); i > 0; i--) {
		printf(" ");
	}
	printf(": %s\n", string);
}

void trans_cfg()
{
	unsigned char *ptr, *ptrb, *ptrs;
	int ch, type, f_cont, f_type, pos, i, j, k, lens, items;

	if (cfg_buf[16] == 'P') {
		fprintf(stderr, "This is made by old version.\n");
		exit(1);
	}
	else if (cfg_buf[16] == 'Q')
		f_r_format = 0;
	else if (cfg_buf[16] == 'R')
		f_r_format = 1;
	else
		f_r_format = -1;
	if (f_r_format == -1 || cfg_buf[18] != 0xff) {
		fprintf(stderr, "This is not the %s file for dviprt.\n", BIN_EXT);
		exit(1);
	}
	cfg_buf[16] = '\0';
	print_key(NAME, cfg_buf);

	for (i = HIGH_BIT; i <= LEFT_IS_LOW; i++) {
		if ((cfg_buf[17] & TYPE_BIT) == u_position[i].bit_flag) {
			strcpy(line_buf, u_position[i].symbol);
			break;
		}
	}
	for (i = NON_MOVING; i <= HEX_MODE; i++) {
		if (cfg_buf[17] & u_position[i].bit_flag) {
			strcat(line_buf, " ");
			strcat(line_buf, u_position[i].symbol);
		}
	}
	print_key(UPPER_POSITION, line_buf);

	sprintf(line_buf, "%3d", ((int) (cfg_buf[17]) & 0x0f) * 8);
	print_key(PINS, line_buf);

	items = f_r_format ? 3 : 2;
	for (i = 0; i < items; i++) {
		ptr = cfg_buf + 23 + i + i;
		sprintf(line_buf, "%3d", (int) (ptr[0]) + (int) (ptr[1]) * 256);
		print_key(MINIMAL_UNIT + i, line_buf);
	}
	if (cfg_buf[20] != 0) {
		sprintf(line_buf, "%3d", cfg_buf[20]);
		print_key(CONSTANT, line_buf);
	}
	ptr = cfg_buf + (int) (cfg_buf[19]);
	for (i = 0; i < NO_MATCH - BIT_IMAGE_MODE; i++) {
		ptrb = line_buf;
		do {
			lens = *ptr++;
			f_cont = *ptr++;
			pos = *ptr++;
			f_type = *ptr++;
			type = f_type & 0x7;

			for (j = 0; j < lens; j++) {
				ch = *ptr++;
				if (pos == j && type != NO_NUM) {
					if (*(ptrb - 1) != ' ' && ptrb > line_buf)
						*ptrb++ = ' ';
					*ptrb++ = '\\';
					j++;
					ptr++;
					switch (type) {
					case (BINARY_LTOH):
						strcpy(ptrb, "b2");
						break;

					case (BINARY_HTOL):
						strcpy(ptrb, "B2");
						break;

					case (DECIMAL_3):
						strcpy(ptrb, "d3");
						j++;
						ptr++;
						break;

					case (DECIMAL_4):
						strcpy(ptrb, "d4");
						j += 2;
						ptr += 2;
						break;

					case (DECIMAL_5):
						strcpy(ptrb, "d5");
						j += 3;
						ptr += 3;
						break;

					case (DECIMAL_V):
						strcpy(ptrb, "d?");
						j++;
						ptr++;
						break;

					default:
						strcpy(ptrb, "ER");
						break;
					}
					ptrb += 2;
					if (f_type & TOTAL_BYTE) {
						*ptrb++ = 'T';
					}
					if ((k = (f_type & DIVIDE_ALL)) != 0) {
						for (; k > 0; k -= DIVIDEBY_2) {
							*ptrb++ = 'D';
						}
					}
					if (f_type & ISO_NUMBER) {
						*ptrb++ = 'I';
					}
					if (f_type & MULT_CONST) {
						*ptrb++ = 'M';
					}
					*ptrb++ = ' ';
				}
				else if (ch > ' ' && ch < 128) {
					*ptrb++ = ch;
				}
				else {
					if (*(ptrb - 1) != ' ' && ptrb > line_buf)
						*ptrb++ = ' ';
					*ptrb++ = '\\';
					switch (ch) {

					case (0x1b):
						strcpy(ptrb, "ESC");
						ptrb += 3;
						break;

					case (' '):
						strcpy(ptrb, "SP");
						ptrb += 2;
						break;

					default:
						sprintf(ptrb, "x%02x", ch);
						ptrb += 3;
						break;
					}
					*ptrb++ = ' ';
				}
			}
		} while (f_cont & 0x80);
		*ptrb = '\0';
		print_key(BIT_IMAGE_MODE + i, line_buf);
	}
}

int trans_src(FILE * fptr)
{
	int i, size, len, ch, num;
	unsigned char *ptr, *ptrn, *tmp;

	string_buf[LINE_FEED - BIT_IMAGE_MODE][0] = 2;
	string_buf[LINE_FEED - BIT_IMAGE_MODE][4] = 0x0d;
	string_buf[LINE_FEED - BIT_IMAGE_MODE][5] = 0x0a;
	string_buf[FORM_FEED - BIT_IMAGE_MODE][0] = 2;
	string_buf[FORM_FEED - BIT_IMAGE_MODE][4] = 0x0d;
	string_buf[FORM_FEED - BIT_IMAGE_MODE][5] = 0x0c;

	while (readline(line_buf, LINE_BUF_SIZE, fptr) != NULL) {
		if ((num = trans_line(line_buf)) != OK) {
			ch = line_buf[num];
			line_buf[num] = '\0';
			fprintf(stderr, "%s\n", line_buf);
			ptr = tmp = line_buf;
			for (i = 0; i < num; i++, tmp++) {
				if (*tmp == 0x0A || *tmp == 0x0D)
					ptr = tmp + 1;
				if (*tmp != '\t')
					*tmp = ' ';
			}
			line_buf[i] = ch;
			fprintf(stderr, "%s\n", ptr);
			exit(1);
		}
	}
	size = 0;
	for (i = 0; i < NO_MATCH; i++) {
		if (flag[i] == 0) {
			if (i < LINE_FEED && i != CONSTANT) {
				fprintf(stderr, "%s is not found!\n", prtopt[i]);
				size++;
			}
		}
		else if (flag[i] != 1) {
			fprintf(stderr, "Too many %s!\n", prtopt[i]);
			size++;
		}
	}
	if (f_M != 0 && cfg_buf[20] == 0) {
		fprintf(stderr, "constant: is not properly defined!\n");
		size++;
	}
	if (size != 0)
		exit(1);
	ptr = cfg_buf + 29;			/* changed 27 -> 29 for R-format */
	cfg_buf[19] = 29;			/* same above */
	for (i = 0; i < NO_MATCH - BIT_IMAGE_MODE; i++) {
		tmp = string_buf[i];
cont:	ch = tmp[1];
		len = tmp[0] + 4;
		size += len;
		while (len--)
			*ptr++ = *tmp++;
		if (ch & 0x80)
			goto cont;
	}
	cfg_buf[16] = 'R';			/* changed 'Q' -> 'R' for R-format */
	cfg_buf[18] = 0xff;
	return ((int) (ptr - cfg_buf));
}

char *readline(unsigned char *line, int buf_size, FILE * fp)
{
	unsigned char *ptr, *tmp;
	int ch;
	ptr = line;
	if (fgets(ptr, buf_size, fp) == NULL)
		return (NULL);
	if (!is_key(*ptr))
		return (line);
	if ((ch = getc(fp)) != EOF)
		ungetc(ch, fp);
	while (is_comment(ch) || is_space(ch)) {
		while (*ptr)
			ptr++;
		if (fgets(ptr, buf_size - (ptr - line), fp) == NULL)
			return (NULL);
		tmp = ptr;
		while (is_space(*tmp))
			tmp++;
		if (is_comment(*tmp))
			*ptr = '\0';		/* コメント? */
		if ((ch = getc(fp)) != EOF)
			ungetc(ch, fp);
	}
	return (line);
}

int to_num(unsigned char *ptr)
{
	int num = 0;

	while (is_dig(*ptr))
		num = num * 10 + to_dig(*ptr++);
	return (num);
}

int trans_line(unsigned char *line)
{								/* 複数行にわたる key_word の読み込み */
	unsigned char *ptr, *n_ptr;
	int i, j, num;

	if (!is_key(*(ptr = line)))
		return (OK);			/* key_word 以外はコメント扱い */
	for (i = 0; i < NO_MATCH; i++) {
		if ((ptr = strchk(line, prtopt[i])) == NULL)
			continue;
		num = LINE_BUF_SIZE;
		while (*ptr++ != ':') {
			if (*ptr == 0 || num-- <= 0) {
				fprintf(stderr, "key_word missing : !\n");
				return ((int) (strchk(line, prtopt[i]) - line));
			}
		}
		num = to_num(ptr = skip_space(ptr));
		flag[i]++;
		break;
	}
	if (i >= NO_MATCH) {
		j = 0;
		while (is_key(line[j]))
			j++;
		fprintf(stderr, "Unknown key_word!\n");
		return (j);
	}
	switch (i) {
	case (NAME):
		j = 0;
		while (*ptr >= ' ' && j < 16) {
			cfg_buf[j++] = *ptr++;
		}
		if (*ptr >= ' ') {
			fprintf(stderr, "Too long name!\n");
			goto l_err;
		}
		cfg_buf[j] = 0;
		break;

	case (UPPER_POSITION):
		while (*ptr) {
			for (j = 0; j <= HEX_MODE; j++) {
				if (strchk(ptr, u_position[j].symbol)) {
					cfg_buf[17] |= u_position[j].bit_flag;
					break;
				}
			}
			if (j > HEX_MODE) {
				fprintf(stderr, "Unknown parameter in %s!\n",
						prtopt[i]);
				goto l_err;
			}
			while (!is_space(*ptr) && *ptr)
				ptr++;
			ptr = skip_space(ptr);
		}
		break;

	case (PINS):
		if (num == 0) {
			goto n_err;
		}
		if (num >= 128) {
			fprintf(stderr, "%s must be less than 128!\n",
					prtopt[i]);
			goto l_err;
		}
		if ((num & 0x07) != 0) {
			fprintf(stderr, "%s must be a multiple of 8!\n",
					prtopt[i]);
			goto l_err;
		}
		cfg_buf[17] |= num / 8;
		break;

	case (MINIMAL_UNIT):
		if (num == 0) {
			goto n_err;
		}
		n_ptr = cfg_buf + 23;
set_num:
		n_ptr[0] = num & 0xff;
		n_ptr[1] = num >> 8;
		break;

	case (MAXIMAL_UNIT):
		if (num == 0) {
			goto n_err;
		}
		n_ptr = cfg_buf + 25;
		goto set_num;

	case (DPI):				/* add for R-format */
		if (num == 0) {
			goto n_err;
		}
		n_ptr = cfg_buf + 27;
		goto set_num;

	case (CONSTANT):
		if (num >= 256) {
			fprintf(stderr, "%s must be less than 256!\n",
					prtopt[i]);
			goto l_err;
		}
		if (num == 0) {
			goto n_err;
		}
		cfg_buf[20] = num;
		break;

	case (BIT_IMAGE_MODE):
	case (NORML_MODE):
	case (SEND_BIT_IMAGE):
	case (SKIP_SPACES):
	case (LINE_FEED):
	case (FORM_FEED):
	case (AFTER_BIT_IMAGE):
	case (BIT_ROW_HEADER):
		num = trns_str(ptr, string_buf[i - BIT_IMAGE_MODE]);
		if (num != OK) {
			fprintf(stderr, "Invalid parameter in %s!\n",
					prtopt[i]);
			return ((int) (ptr - line) + num);
		}
		break;
	}
	return (OK);				/* done */
l_err:
	return ((int) (ptr - line));
n_err:
	fprintf(stderr, "Invalid value for %s!\n",
			prtopt[i]);
	return ((int) (ptr - line));
}

unsigned char *
 skip_space(unsigned char *string)
{
	while (is_space(*string))
		string++;
	return (string);
}

unsigned char *
 strchk(unsigned char *line, unsigned char *token)
 /* If the head token of 'line' matchs 'token', return 'line'.
     * Otherwise, returning NULL
     */
{
	while (*token)
		if (*token++ != *line++)
			return (NULL);
	return (line);
}

int trns_str(unsigned char *str, unsigned char *buf)
{
	int ch, len, total, num, f_num, f_divide, f_dec;
	unsigned char *ptr;

	total = 0;
	ptr = str;
	f_num = f_divide = f_dec = 0;
	for (len = 4; len < 250;) {
		ptr = skip_space(ptr);
		if ((ch = *ptr++) < ' ') {
			buf[0] = len - 4;
			return (OK);
		}
		if (ch != '\\') {
usual:		buf[len++] = ch;
			/* usual character */
			continue;
		}
		else if (is_space(*ptr) || *ptr == '\0')
			goto usual;

		/* escape sequence by / */
		num = 0;
		if (((ch = *ptr++) == 'x' || ch == 'X')) {
			while (ch = *ptr++, is_hex(ch)) {	/* hex */
				num = num * 16 + hex_to_dig(ch);
				if (num >= 256)
					goto err;
			}
end_number:	if (!is_space(ch) && ch != '\0')
				goto err;
			buf[len++] = num;
			continue;
		}
		if (ch == '0') {
			ptr--;
			while (ch = *ptr++, is_oct(ch)) {	/* oct */
				num = num * 8 + to_dig(ch);
				if (num >= 256)
					goto err;
			}
			goto end_number;
		}
		if (is_dig(ch)) {		/* dec */
			ptr--;
			while (ch = *ptr++, is_dig(ch)) {
				num = num * 10 + to_dig(ch);
				if (num >= 256)
					goto err;
			}
			goto end_number;
		}
		if (f_num && (ch == 'b' || ch == 'B' || ch == 'd')) {
cont:		buf[0] = len - 4;
			buf[1] |= 0x80;
			buf += len;
			if ((total += len) >= MAX_DEF - 250)
				goto err;
			if (ch != 'C')
				ptr -= 2;
			len = 4;
			f_num = 0;
			continue;
		}
		switch (ch) {
		case ('E'):			/* ESC */
			if (*ptr++ == 'S' && *ptr++ == 'C' &&
				(is_space(*ptr) || *ptr == '\0')) {
				buf[len++] = 27;
				continue;
			}
			break;

		case ('S'):			/* SP */
			if (*ptr++ == 'P' && (is_space(*ptr) || *ptr == '\0')) {
				buf[len++] = ' ';
				continue;
			}
			break;

		case ('b'):			/* bin L -> H */
			buf[3] = BINARY_LTOH;
binary:		ch = 2;
			if (*ptr++ == '2'
				&& (is_space(*ptr) || *ptr == '\0'
			 || *ptr == 'T' || *ptr == 'I' || *ptr == 'D' || *ptr == 'M')
				&& f_num == 0) {
send_num:		buf[2] = len - 4;
				while (ch-- != 0)
					buf[len++] = 0;
				f_num = 1;

opt:			switch ((int) *ptr) {

				case ('M'):
					buf[3] |= MULT_CONST;
					f_M++;

					goto next_opt;

				case ('T'):
					buf[3] |= TOTAL_BYTE;
					goto next_opt;

				case ('D'):
					f_divide += DIVIDEBY_2;
					goto next_opt;

				case ('I'):
					buf[3] |= ISO_NUMBER;
next_opt:			ptr++;
					goto opt;

				default:
					break;
				}
				buf[3] |= (f_divide & DIVIDE_ALL);
				f_divide = 0;
				if (f_dec) {
					f_dec = 0;
					goto cut;
				}
				continue;
			}
			break;

		case ('B'):			/* bin H -> L */
			buf[3] = BINARY_HTOL;
			goto binary;

		case ('d'):			/* decimal */
			ch = *ptr++;
			if ((ch >= '3' && ch <= '5' || ch == '?')
				&& (is_space(*ptr) || *ptr == '\0'
			 || *ptr == 'T' || *ptr == 'I' || *ptr == 'D' || *ptr == 'M')
				&& f_num == 0) {
				if (ch == '?') {
					if (len != 4) {
						ptr -= 3;
						goto cut;
					}
					buf[3] = DECIMAL_V;
					buf[4] = '%';
					buf[5] = 'u';
					buf[6] = 0;
					ch = 0;
					f_num = f_dec = 1;
					len = 7;
					goto opt;
				}
				else {
					ch = to_dig(ch);
					buf[3] = DECIMAL_3 - 3 + ch;
					goto send_num;
				}
			}
			break;

		default:
			break;
		}
err:	return ((int) (ptr - str));
	}
cut:
	ch = 'C';
	goto cont;
}
