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