/
umud/DOC/
umud/DOC/examples/
umud/DOC/internals/
umud/DOC/wizard/
umud/MISC/
umud/MISC/dbchk/
umud/RWHO/rwhod/
#include "copyright.h"

#include <stdio.h>
#include <ctype.h>
#include <sys/param.h>

#include "db.h"
#include "config.h"

#ifndef MUDNAME
#define MUDNAME "Islandia"
#endif

static int include_all = 0; /* include everything unless specified */
static int keep_players = 0;    /* keep all players */
static int safe_below = 1;  /* Keep everything <= safe_below */
static int safe_above = 2e9;    /* Keep everything >= safe_above */
static int reachable = 0;   /* Only keep rooms reachable from #0 */
static int norecycle = 0;   /* Exclude things in recycling center */
static int inbuild = 0;     /* True when in main nuild_trans loop */
static int recycling_center = 0;/* Room number home("Recycler") */

# define REACH_FLAG 0x40000000
# define REACHABLE(X) (db[X].flags & REACH_FLAG)
# define SET_REACHABLE(X) (db[X].flags |= REACH_FLAG)

static dbref included[NCARGS+1];
static dbref excluded[NCARGS+1];

static dbref *trans;        /* translation vector */

#define DEFAULT_LOCATION (0)
#define DEFAULT_OWNER (1)

static int isok(dbref);
char *OIF_ID(int);
void OIFboolexp(FILE *,struct boolexp *);
char *ofix(const char *);

/* returns 1 if object is specifically excluded */
static int is_excluded(dbref x)
{
    int i;

    if(x == NOTHING) return 0; /* Don't exclude nothing */

    /* check that it isn't excluded */
    for(i = 0; excluded[i] != NOTHING; i++) {
    if(excluded[i] == x) return 1; /* always exclude specifics */
        if(excluded[i] == db[x].owner) return 1;
    }

    return (0);
}

/* returns 1 if it is not excluded */
static int not_excluded(dbref x)
{
    int i;

    if(x == NOTHING) return 1; /* Don't exclude nothing */

    /* check that it isn't excluded */
    for(i = 0; excluded[i] != NOTHING; i++) {
    if(excluded[i] == x) return 0; /* always exclude specifics */
        if(excluded[i] == db[x].owner) return 0;
    }

    /* if it's an exit, check that its destination is ok */
    if(Typeof(x) == TYPE_EXIT && db[x].location >= 0) {
    return isok(db[x].location);
    } else {
    return 1;
    }
}

/* returns 1 if it should be included in translation vector */
static int isok(dbref x)
{
    int i;

    if(x == DEFAULT_OWNER || x == DEFAULT_LOCATION) return 1;
    if(x == NOTHING) return 1;
    
    for(i = 0; included[i] != NOTHING; i++) {
    if(included[i] == x) return 1; /* always get specific ones */
    
    if(reachable && Typeof(x) == TYPE_ROOM && !REACHABLE(x))
    { 
# ifdef DEBUG
      if (inbuild)
        fprintf (stderr, "Excluding %s(%dR), not reachable\n", 
             db[x].name, x);
# endif
          return 0;
    }

#ifdef	RECYCLER
    if(norecycle && db[x].location == recycling_center) return 0;
#endif

    if(included[i] == db[x].owner 
       || (x <= safe_below || x >= safe_above)
       || keep_players && Typeof(x) == TYPE_PLAYER) {
        return not_excluded(x);
    }
    }

#if	0
    /* This is an UnterMUD converter. UnterMUDs don't have carried
       exits, so DROP such. Notez Bien: relies on the new source field. */
    if (Typeof(i) == TYPE_EXIT && db[i].source == NOTHING)
	return 0;
#endif

    /* not in the list, can only get it if include_all is on */
    /* or it's owned by DEFAULT_OWNER */
    return (include_all && not_excluded(x));
}

static void build_trans(void)
{
    dbref i;
    dbref val;

    if((trans = (dbref *) malloc(sizeof(dbref) * db_top)) == 0) {
    abort();
    }
    
    inbuild++;

    val = 0;
    for(i = 0; i < db_top; i++) {
    if(isok(i)) {
        trans[i] = val++;
    } else {
        trans[i] = NOTHING;
    }
    }
    
    inbuild--;
}

static dbref translate(dbref x)
{
    if(x == NOTHING || x == HOME) {
    return(x);
    } else {
    return(trans[x]);
    }
}

/* TRUE_BOOLEXP means throw this argument out */
/* even on OR; it's effectively a null boolexp */
/* NOTE: this doesn't free anything, it just munges it up */
static struct boolexp *translate_boolexp(struct boolexp *exp)
{
    struct boolexp *s1;
    struct boolexp *s2;

    if(exp == TRUE_BOOLEXP) {
    return TRUE_BOOLEXP;
    } else {
    switch(exp->type) {
      case BOOLEXP_NOT:
        s1 = translate_boolexp(exp->sub1);
        if(s1 == TRUE_BOOLEXP) {
        return TRUE_BOOLEXP;
        } else {
        exp->sub1 = s1;
        return exp;
        }
        /* break; */
      case BOOLEXP_AND:
      case BOOLEXP_OR:
        s1 = translate_boolexp(exp->sub1);
        s2 = translate_boolexp(exp->sub2);
        if(s1 == TRUE_BOOLEXP && s2 == TRUE_BOOLEXP) {
        /* nothing left */
        return TRUE_BOOLEXP;
        } else if(s1 == TRUE_BOOLEXP && s2 != TRUE_BOOLEXP) {
        /* s2 is all that is left */
        return s2;
        } else if(s1 != TRUE_BOOLEXP && s2 == TRUE_BOOLEXP) {
        /* s1 is all that is left */
        return s1;
        } else {
        exp->sub1 = s1;
        exp->sub2 = s2;
        return exp;
        }
        /* break; */
      case BOOLEXP_CONST:
        exp->thing = translate(exp->thing);
        if(exp->thing == NOTHING) {
        return TRUE_BOOLEXP;
        } else {
        return exp;
        }
        /* break; */
      default:
        abort();        /* bad boolexp type, we lose */
        return TRUE_BOOLEXP;
    }
    }
}

static int ok(dbref x)
{
    if(x == NOTHING || x == HOME) {
    	return 1;
    } 
    /* This is an UnterMUD converter. UnterMUDs don't have carried
       exits, so DROP such. Notez Bien: relies on the new source field. */
    if (Typeof(x) == TYPE_EXIT && db[x].source == NOTHING)
	return 0; /* Put here to avoid things getting renumbered. */
#ifdef	TYPE_GARBAGE
    if (Typeof(x) == TYPE_GARBAGE)
	    return 0;
#endif
    return trans[x] != NOTHING;
}

static void check_bad_exits(dbref x)
{
    dbref e;

    if(Typeof(x) == TYPE_ROOM && !isok(x)) {
    /* mark all exits as excluded */
    DOLIST(e, db[x].exits) {
        trans[e] = NOTHING;
    }
    }
}

static void check_owner(dbref x)
{
    if(ok(x) && !ok(db[x].owner)) {
    db[x].owner = DEFAULT_OWNER;
    }
}

static void check_location(dbref x)
{
    dbref loc;
    dbref newloc;

    if(ok(x) && (Typeof(x) == TYPE_THING || Typeof(x) == TYPE_PLAYER)
       && !ok(loc = db[x].location)) {
    /* move it to home or DEFAULT_LOCATION */
    if(ok(db[x].exits)) {
        newloc = db[x].exits; /* home */
    } else {
        newloc = DEFAULT_LOCATION;
    }
    db[loc].contents = remove_first(db[loc].contents, x);
    PUSH(x, db[newloc].contents);
    db[x].location = newloc;
    }
}

static void check_next(dbref x)
{
    dbref next;

    if(ok(x)) {
    while(!ok(next = db[x].next)) db[x].next = db[next].next;
    }
}

static void check_contents(dbref x)
{
    dbref c;

    if(ok(x)) {
    while(!ok(c = db[x].contents)) db[x].contents = db[c].next;
    }
}

/* also updates home */
/* MUST BE CALLED AFTER check_owner! */
static void check_exits(dbref x)
{
    dbref e;

    if(ok(x) && !ok(e = db[x].exits)) {
    switch(Typeof(x)) {
      case TYPE_ROOM:
        while(!ok(e = db[x].exits)) db[x].exits = db[e].next;
        break;
      case TYPE_PLAYER:
      case TYPE_THING:
        if(ok(db[db[x].owner].exits)) {
        /* set it to owner's home */
        db[x].exits = db[db[x].owner].exits; /* home */
        } else {
        /* set it to DEFAULT_LOCATION */
        db[x].exits = DEFAULT_LOCATION; /* home */
        }
        break;
    }
    }
}

void
exitsources()
{
	dbref	i;
	dbref	e;

	/* Make sure all sources have something recognizable. */
	for (i = 0; i < db_top; i++)
		db[i].source = NOTHING;
	for(i=0; i < db_top; i++) {
		if (Typeof(i) == TYPE_ROOM) {
			DOLIST(e, db[i].exits) {
				db[e].source = i;
			}
		}
	}
}


char buf[BUFSIZ];

static void do_OIF_write(void)
{
    dbref i;
    dbref kludge;

    for(i = 0, kludge = 0; i < db_top; i++) {
        if(trans[i] != NOTHING) trans[i] = kludge++;
    }

    printf("object sysobj\nint _objcnt=%d\nint newsarticle=0\nstr nam=The System Object (all mighty data point)\nobj _syslimbo=0\nlst own=wizard\nlst _wizards=wizard\nendobj\n",db_top-1);
    strcpy(buf,"wizard");
    rot_init(buf);
    rot_encode("potrzebie",buf);
    printf("object wizard\nflg _wz=\nflg _pl=\nstr nam=UnterWiz\nobj home=0@%s\nstr pass=%s\nobj loc=0\nlst own=wizard\nendobj\n",MUDNAME,buf);

    for(i=0;i<db_top;i++)
        if(ok(i)) {
            printf("object %s\n",OIF_ID(i));
            printf("str nam=%s\n",db[i].name);
            if (db[i].description)
                printf("str desc=%s\n",db[i].description);
            if (db[i].contents) {
                printf("lst con=");
                for (kludge=db[i].contents;kludge!=NOTHING;
                                                    kludge=db[kludge].next)
                    if (Typeof(kludge) != TYPE_PLAYER)
                        printf("%s;",OIF_ID(kludge));
                printf("\n");
            }
            if (db[i].key && !(Typeof(i) == TYPE_EXIT && db[i].location == NOTHING)) {
                printf("boo lok=");
                OIFboolexp(stdout,translate_boolexp(db[i].key));
	    } else if (Typeof(i) == TYPE_EXIT && db[i].location == NOTHING)
		printf("boo lok=F\n");	/* People can't walk through unlinked exits. */
	    else
                printf("boo lok=T\n");
            if (db[i].fail_message)
                printf("str fail=%s\n",db[i].fail_message);
            if (db[i].ofail)
                printf("str ofail=%s\n",ofix(db[i].ofail));
            if (db[i].succ_message)
                printf("str succ=%s\n",db[i].succ_message);
            if (db[i].osuccess)
                printf("str osucc=%s\n",ofix(db[i].osuccess));
            printf("lst own=%s\n",OIF_ID(db[i].owner));
            if (db[i].flags&LINK_OK)
                printf("boo lnk=T\n");
            if (db[i].flags&DARK)
                printf("flg dark=\n");
            switch(Typeof(i)) {
                case TYPE_PLAYER:
                    printf("flg _pl=\n");
                    if (Wizard(i))
                        printf("flg _wz=\n");
                    printf("obj loc=%s\n",OIF_ID(db[i].location));
                    printf("obj home=%d@%s\n",translate(db[i].exits),MUDNAME);
					if (Genderof(i) == GENDER_MALE) {
						printf("str subv=he\n");
						printf("str objv=him\n");
						printf("str posv=his\n");
					} else if (Genderof(i) == GENDER_FEMALE) {
						printf("str subv=she\n");
						printf("str objv=her\n");
						printf("str posv=her\n");
					} else if (Genderof(i) == GENDER_NEUTER) {
						printf("str subv=it\n");
						printf("str objv=it\n");
						printf("str posv=its\n");
					} else {
						printf("str subv=%s\n",db[i].name);
						printf("str objv=%s\n",db[i].name);
						printf("str posv=%s's\n",db[i].name);
					}
                    OIF_ID(i);
		    if (db[i].password) {
                    	rot_init(buf);
                    	rot_encode(db[i].password,buf);
		    } else {
			buf[0] = 0;
		    }
                    printf("str pass=%s\n",buf);
                    break;
                case TYPE_THING:
                    printf("obj loc=%s\n",OIF_ID(db[i].location));
                    printf("obj home=%d@%s\n",translate(db[i].exits),MUDNAME);
		    if (db[i].flags&STICKY)
			printf("cmd drop=tel $me home;_echo Dropped.\n");
                    break;
                case TYPE_ROOM:
                    printf("flg _rm=\n");
		    if (translate(db[i].location) != NOTHING)
                        printf("obj drpto=%s\n",OIF_ID(db[i].location));
                    printf("lst xit=");
                    for (kludge=db[i].exits;kludge!=NOTHING;
                                                kludge=db[kludge].next)
                        printf("%s;",OIF_ID(kludge));
                    printf("\n");
                    printf("lst ply=");
                    for (kludge=db[i].contents;kludge!=NOTHING;
                                                kludge=db[kludge].next)
                        if (Typeof(kludge) == TYPE_PLAYER)
                            printf("%s;",OIF_ID(kludge));
                    printf("\n");
                    break;
                case TYPE_EXIT:
		    /* Unlinked exit? Make it actually linked to home. */
		    if (db[i].location == NOTHING)
			    printf("obj dst=home\n");
		    else
			    printf("obj dst=%s\n",OIF_ID(db[i].location));
		    /* Yes, this is NEW. */
		    printf("obj loc=%s\n", OIF_ID(db[i].source));
                    break;
		default:	/* Do something sensible for stuff we don't
				   understand. */
		    fprintf(stderr, "Can't handle type %d\n", Typeof(i));
		    abort();
		    /*NOTREACHED*/
        }
	printf("endobj\n");
    }
}

char *
OIF_ID(dbref i)
{
    if (i == HOME)
	sprintf(buf,"home");
    else if (Typeof(i) == TYPE_PLAYER || Typeof(i) == TYPE_THING)
        sprintf(buf,"%d@%s",translate(i),MUDNAME);
    else
        sprintf(buf,"%d",translate(i));
    return(buf);
}


int reach_lvl = 0;

void
make_reachable (dbref x)
{   dbref e, r;
#ifdef DEBUG
    int i;
#endif
    
    if (Typeof(x) != TYPE_ROOM || is_excluded(x)) return;

    reach_lvl++;

    SET_REACHABLE(x);

#ifdef DEBUG
    for (i=0; i<reach_lvl; i++ ) fputc (' ', stderr);
    fprintf (stderr, "Set %s(%dR) reachable.\n", db[x].name, x);
#endif

    DOLIST(e, db[x].exits) {
    r = db[e].location;
 
    if (r < 0) continue;
    if (is_excluded(r)) continue;
        if (is_excluded(e)) continue;
    if (!REACHABLE(r)) make_reachable(r);
    }

    reach_lvl--;
}

void main(int argc, char **argv)
{
    dbref i;
    int top_in;
    int top_ex;
    char *arg0;

    top_in = 0;
    top_ex = 0;

    /* Load database */
    if(db_read(stdin) < 0) {
    fputs("Database load failed!\n", stderr);
    exit(1);
    } 

    fputs("Done loading database...\n", stderr);


    /* now parse args */
    arg0 = *argv;
    for (argv++, argc--; argc > 0; argv++, argc--) {
    if (isdigit (**argv) || **argv == '-' && isdigit ((*argv)[1])) {
        i = atol(*argv);
    } else if (**argv == '+' && isdigit ((*argv)[1])) {
        i = atol(*argv+1);
    } else if (**argv == 'b' && isdigit ((*argv)[1])) {
        safe_below = atol(*argv+1);
        fprintf (stderr, "Including all objects %d and below\n",
                 safe_below);
    } else if (**argv == 'a' && isdigit ((*argv)[1])) {
        safe_above = atol(*argv+1);
        fprintf (stderr, "Including all objects %d and above\n",
                 safe_above);
    } else if (!strcmp(*argv, "all")) {
        include_all = 1;
    } else if (!strcmp(*argv, "reachable")) {
        reachable = 1;
    } else if (!strcmp(*argv, "players")) {
        keep_players = 1;
    } else if (!strcmp(*argv, "norecycle")) {
        norecycle = 1;
    } else if (**argv == '-' &&
               (i = lookup_player (*argv+1)) != 0) {
        fprintf (stderr, "Excluding player %s(%d)\n",
             db[i].name, i);
        i = -i;
    } else if (**argv != '-' &&
                   (i = lookup_player (*argv)) != NOTHING) {
        fprintf (stderr, "Including player %s(%d)\n",
             db[i].name, i);
    } else {
        fprintf(stderr, "%s: bogus argument %s\n", arg0, *argv);
        continue;
    }
    
    if(i < 0) {
        excluded[top_ex++] = -i;
    } else {
        included[top_in++] = i;
    }
    }

    /* Terminate */
    included[top_in++] = NOTHING;
    excluded[top_ex++] = NOTHING;

    /* Check for reachability from DEFAULT_LOCATION */
    if (reachable)
    { make_reachable (DEFAULT_LOCATION); 
      fputs ("Done marking reachability...\n", stderr); 
    }

    /* Gives all exits sources -- this becomes important later on. */
    exitsources();

#ifdef	RECYCLER
    /* Find recycler */
    if (norecycle && ((i = lookup_player (RECYCLER)) != NOTHING))
    { recycling_center = db[i].exits;
/*      if (recycling_center == DEFAULT_LOCATION) norecycle = 0;
      else */
      { fprintf (stderr, "Excluding all players in %s(%d)\n",
              db[recycling_center].name, recycling_center);
      }
    }
#endif

    /* Build translation table */
    build_trans();
    fputs("Done building translation table...\n", stderr);

    /* Scan everything */
    for(i = 0; i < db_top; i++) check_bad_exits(i);
    fputs("Done checking bad exits...\n", stderr);

    for(i = 0; i < db_top; i++) check_owner(i);
    fputs("Done checking owners...\n", stderr);

    for(i = 0; i < db_top; i++) check_location(i);
    fputs("Done checking locations...\n", stderr);

    for(i = 0; i < db_top; i++) check_next(i);
    fputs("Done checking next pointers...\n", stderr);

    for(i = 0; i < db_top; i++) check_contents(i);
    fputs("Done checking contents...\n", stderr);

    for(i = 0; i < db_top; i++) check_exits(i);
    fputs("Done checking homes and exits...\n", stderr);

/*    do_write();   */
    do_OIF_write();
    fputs("Done.\n", stderr);

    exit(0);
}

static void OIFbool_subexp(FILE *f, struct boolexp *b)
{
    switch(b->type) {
      case BOOLEXP_AND:
    putc('(', f);
    OIFbool_subexp(f, b->sub1);
    putc(AND_TOKEN, f);
    OIFbool_subexp(f, b->sub2);
    putc(')', f);
    break;
      case BOOLEXP_OR:
    putc('(', f);
    OIFbool_subexp(f, b->sub1);
    putc(OR_TOKEN, f);
    OIFbool_subexp(f, b->sub2);
    putc(')', f);
    break;
      case BOOLEXP_NOT:
    putc('(', f);
    putc(NOT_TOKEN, f);
    OIFbool_subexp(f, b->sub1);
    putc(')', f);
    break;
      case BOOLEXP_CONST:
    fprintf(f, "%s", OIF_ID(b->thing));
    break;
      default:
    break;
    }
}

void OIFboolexp(FILE *f, struct boolexp *b)
{
    if(b != TRUE_BOOLEXP) {
    OIFbool_subexp(f, b);
    }
    putc('\n', f);
}
    
char *
ofix(const char *ins)
{
	char foo[BUFSIZ];
	char *bar,*baz;

	strcpy(foo,ins);
	strcpy(buf,"");
	bar = foo;
	while ((baz=(char *)index(bar,'%')) != NULL) {
		*baz++ = '\0';
		strcat(buf,bar);
		switch(*baz) {
			case 's':
				strcat(buf,"$subv");
				break;
			case 'o':
				strcat(buf,"$objv");
				break;
			case 'p':
				strcat(buf,"$posv");
				break;
			case 'n':
				strcat(buf,"$nam");
		}
		bar = baz+1;
	}
	strcat(buf,bar);
	return(buf);
}