/*	
	A second pass of a MUSH 2.0 flat file filter.

	It consumes a *sorted* file of 'used' attribute numbers,
and builds a renumbering map. It then eats a flat file on stdin,
and writes another on stdout with the attributes renumbered.

	The renumbering rewrites +N lines to the next actual available
attribute after renumbering, +A lines and > lines as appropriate. The
mapping is done crudely, by keeping an ordered array of old attribute
numbers, and looking them up via a binary search, and using the index
they're found at (+ A_USER_START + 1) as the new attribute number.
+F lines are discarded.

*/

/* Because I am stupid */
/*
#define DEBUG_STUPID_BIN_SRCH
*/

typedef struct mapent {
	int	old;
	int	new;
} mapent;

#define MAP_FAIL "Could not map attr num %d, not in numbers file.\n"

#define MAX(a,b) ((a) > (b) ? (a) : (b))

#include <stdio.h>
#include "filter.h"


main(ac,av)
int	ac;
char	*av[];
{
	FILE	*numfile;
	int	lines = 0;
	mapent	*map;
	int	size = 0;
	int	i,num,new;
	int	attr,newattr;
	int	attrcount;
	char	buff[32],ch;

	if(ac != 2)
		exit(usage(av[0]));

	/* Open the file, and gobble up the attribute numbers */

	if((numfile = fopen(av[1],"r")) == (FILE *)0){
		fprintf(stderr,"Could not open numbers file %s\n",av[1]);
		exit(usage(av[0]));
	}

	/* Get an initial map */

	map = (mapent *)malloc(1024 * sizeof(mapent));
	size = 1024;
	if(!map){
		fprintf(stderr,"Initial malloc failed.\n");
		exit(1);
	}

	i = -1;
	new = A_USER_START;

	while(fgets(buff,32,numfile)){
		if(buff[strlen(buff) - 1] != '\n'){
			fprintf(stderr,"Badly formatted numbers file.\n");
			exit(usage(av[0]));
		}
		buff[strlen(buff) - 1] = '\0';
		num = atoi(buff);
		if(num >= size){
			map = (mapent *)realloc(map,sizeof(mapent) * (num + 1024));
			size = num + 1024;
			if(!map){
				fprintf(stderr,"realloc failed.\n");
				exit(1);
			}
		}
		if(i >= 0 && num <= map[i].old){
			fprintf(stderr,"Unsorted or malformed numbers file.\n");
			exit(usage(av[0]));
		}
		map[++i].old = num;
		if((++new & 0x7f) == 0)
			++new;

		map[i].new = new;
	}

	fprintf(stderr,"Read %d attributes numbers.\n",i+1);

	/* Now eat stdin, translating attr numbers on stdout.  */
	/* This means rewrite '+N', '+A' lines and '>' lines.  */
	/* Throw '+F' lines away, we'll have no free attr nums */
                                                              
	while((ch = getchar()) != EOF){
		if(++lines % 1000 == 0){
			fputc('.',stderr);
			fflush(stderr);
		}

		if(ch == '+'){
			switch(ch = getchar()){
			case 'A':
				scanf("%d",&attr);
				if(attr <= A_USER_START)
					newattr = attr;
				else
					newattr = mapattr(attr,i,map);

				if(newattr != -1){
					printf("+A%d\n",newattr);
    					eatline();
				} else {
					eatline();
					eatline();
				}
				break;
			case 'N':
				scanf("%d",&attrcount);
				if(attrcount < i){
					fprintf(stderr,
					"New attr count, %d > old, %d\n",
					i,attrcount);
					exit(1);
				}
				printf("+N%d\n",new+1);
				eatline();
				break;
			case 'F':
				eatline();
				break;
			default:
				putchar('+');
				putchar(ch);
				copyline();
			}
		} else if(ch == '>'){
			scanf("%d",&attr);
			if(attr <= A_USER_START)
				newattr = attr;
			else
				newattr = mapattr(attr,i,map);

			if(newattr == -1){
				fprintf(stderr,MAP_FAIL,attr);
				exit(1);
			}
			printf(">%d\n",newattr);
			eatline();

			/* Next bit is the attribute */

			copyattr();

		} else if(ch == '!') {
			/* Next line is the name -- copy it raw! */

			ungetc(ch,stdin);
			copyline();
			copyline();

		} else {
			/* Let copyline() deal with the whole thing */
			/* Bad juju otherwise, if ch == \n, see..   */

			ungetc(ch,stdin);
			copyline();
		}
	}

	fflush(stdout);
	fprintf(stderr,"Done.\n");
}

/*
	Map an old attribute number to a new one with a binary search
of our table.

*/

mapattr(a,siz,map)
int	a;
int	siz;
mapent	map[];
{
	register int	blk,i;

#ifdef DEBUG_STUPID_BIN_SRCH
	fprintf(stderr,"----------->Mapping %d..\n",a);
#endif

	blk = i = siz >> 1;
	while(a != map[i].old && blk > 2){
#ifdef DEBUG_STUPID_BIN_SRCH
		fprintf(stderr,
		"size = %d, a = %d, i = %d, map[i].old = %d, blk = %d\n",
		siz,a,i,map[i].old,blk);
#endif
		blk = blk >> 1;
		i = (a > map[i].old ? i + blk : i - blk);
	}
#ifdef DEBUG_STUPID_BIN_SRCH
	fprintf(stderr,
		"size = %d, a = %d, i = %d, map[i].old = %d, blk = %d\n",
		siz,a,i,map[i].old,blk);
#endif
	/* I am Too Dumb. Finish w/a short linear search */

	if(a < map[i].old){
		while(a < map[i].old && i > 0)
			i--;
	} else if (a > map[i].old){
		while(a > map[i].old && i < siz)
			i++;
	}

	if(a != map[i].old)
		return(-1);

	return(map[i].new);
}

/*
	Copy chars stdin->stdout until end of line
*/

copyline()
{
	int	ch;

	do {
		ch = getchar();
		putchar(ch);
	} while(ch != '\n' && ch != EOF);
}

/*
	Copy an attribute. This is complex -- \n's internal to the attr
are escaped with a \r before them. Unlike some other routines, we wanna
be positioned so next char is 1st char of attribute.

*/

copyattr()
{
	char	last;
	int	ch = '\0'; /* anything other than a \r */

	do {
		last = ch;
		ch = getchar();
		putchar(ch);
	} while((ch != '\n' || last == '\r') && ch != EOF);
}

/*
	Eat chars on stdin until end of line.
*/

eatline()
{
	int	ch;

	do {
		ch = getchar();
	} while(ch != '\n' && ch != EOF);
}

usage(cmd)
char	*cmd;
{
	fprintf(stderr,"usage: %s <attr_num_file>\n",cmd);
	return(1);
}
