tf5-5.0beta8/.git/
tf5-5.0beta8/.git/info/
tf5-5.0beta8/.git/logs/
tf5-5.0beta8/.git/logs/refs/heads/
tf5-5.0beta8/.git/objects/00/
tf5-5.0beta8/.git/objects/01/
tf5-5.0beta8/.git/objects/04/
tf5-5.0beta8/.git/objects/05/
tf5-5.0beta8/.git/objects/07/
tf5-5.0beta8/.git/objects/09/
tf5-5.0beta8/.git/objects/0a/
tf5-5.0beta8/.git/objects/0c/
tf5-5.0beta8/.git/objects/0e/
tf5-5.0beta8/.git/objects/12/
tf5-5.0beta8/.git/objects/13/
tf5-5.0beta8/.git/objects/14/
tf5-5.0beta8/.git/objects/16/
tf5-5.0beta8/.git/objects/17/
tf5-5.0beta8/.git/objects/19/
tf5-5.0beta8/.git/objects/1c/
tf5-5.0beta8/.git/objects/1d/
tf5-5.0beta8/.git/objects/1e/
tf5-5.0beta8/.git/objects/1f/
tf5-5.0beta8/.git/objects/20/
tf5-5.0beta8/.git/objects/21/
tf5-5.0beta8/.git/objects/23/
tf5-5.0beta8/.git/objects/27/
tf5-5.0beta8/.git/objects/29/
tf5-5.0beta8/.git/objects/2a/
tf5-5.0beta8/.git/objects/2b/
tf5-5.0beta8/.git/objects/2f/
tf5-5.0beta8/.git/objects/30/
tf5-5.0beta8/.git/objects/33/
tf5-5.0beta8/.git/objects/34/
tf5-5.0beta8/.git/objects/35/
tf5-5.0beta8/.git/objects/39/
tf5-5.0beta8/.git/objects/3c/
tf5-5.0beta8/.git/objects/3d/
tf5-5.0beta8/.git/objects/3f/
tf5-5.0beta8/.git/objects/40/
tf5-5.0beta8/.git/objects/41/
tf5-5.0beta8/.git/objects/42/
tf5-5.0beta8/.git/objects/44/
tf5-5.0beta8/.git/objects/46/
tf5-5.0beta8/.git/objects/47/
tf5-5.0beta8/.git/objects/48/
tf5-5.0beta8/.git/objects/4a/
tf5-5.0beta8/.git/objects/4d/
tf5-5.0beta8/.git/objects/4f/
tf5-5.0beta8/.git/objects/53/
tf5-5.0beta8/.git/objects/54/
tf5-5.0beta8/.git/objects/58/
tf5-5.0beta8/.git/objects/5b/
tf5-5.0beta8/.git/objects/5c/
tf5-5.0beta8/.git/objects/5e/
tf5-5.0beta8/.git/objects/5f/
tf5-5.0beta8/.git/objects/60/
tf5-5.0beta8/.git/objects/61/
tf5-5.0beta8/.git/objects/62/
tf5-5.0beta8/.git/objects/63/
tf5-5.0beta8/.git/objects/66/
tf5-5.0beta8/.git/objects/67/
tf5-5.0beta8/.git/objects/6c/
tf5-5.0beta8/.git/objects/6e/
tf5-5.0beta8/.git/objects/72/
tf5-5.0beta8/.git/objects/73/
tf5-5.0beta8/.git/objects/75/
tf5-5.0beta8/.git/objects/77/
tf5-5.0beta8/.git/objects/7a/
tf5-5.0beta8/.git/objects/7b/
tf5-5.0beta8/.git/objects/7c/
tf5-5.0beta8/.git/objects/7e/
tf5-5.0beta8/.git/objects/7f/
tf5-5.0beta8/.git/objects/81/
tf5-5.0beta8/.git/objects/84/
tf5-5.0beta8/.git/objects/86/
tf5-5.0beta8/.git/objects/87/
tf5-5.0beta8/.git/objects/88/
tf5-5.0beta8/.git/objects/8b/
tf5-5.0beta8/.git/objects/8c/
tf5-5.0beta8/.git/objects/8f/
tf5-5.0beta8/.git/objects/91/
tf5-5.0beta8/.git/objects/93/
tf5-5.0beta8/.git/objects/96/
tf5-5.0beta8/.git/objects/97/
tf5-5.0beta8/.git/objects/99/
tf5-5.0beta8/.git/objects/9a/
tf5-5.0beta8/.git/objects/9b/
tf5-5.0beta8/.git/objects/9c/
tf5-5.0beta8/.git/objects/9d/
tf5-5.0beta8/.git/objects/9e/
tf5-5.0beta8/.git/objects/a1/
tf5-5.0beta8/.git/objects/a3/
tf5-5.0beta8/.git/objects/a4/
tf5-5.0beta8/.git/objects/a6/
tf5-5.0beta8/.git/objects/a7/
tf5-5.0beta8/.git/objects/a8/
tf5-5.0beta8/.git/objects/a9/
tf5-5.0beta8/.git/objects/ab/
tf5-5.0beta8/.git/objects/ac/
tf5-5.0beta8/.git/objects/ae/
tf5-5.0beta8/.git/objects/b1/
tf5-5.0beta8/.git/objects/b2/
tf5-5.0beta8/.git/objects/b3/
tf5-5.0beta8/.git/objects/b7/
tf5-5.0beta8/.git/objects/b9/
tf5-5.0beta8/.git/objects/bb/
tf5-5.0beta8/.git/objects/bc/
tf5-5.0beta8/.git/objects/bd/
tf5-5.0beta8/.git/objects/bf/
tf5-5.0beta8/.git/objects/c0/
tf5-5.0beta8/.git/objects/c1/
tf5-5.0beta8/.git/objects/c2/
tf5-5.0beta8/.git/objects/c3/
tf5-5.0beta8/.git/objects/c5/
tf5-5.0beta8/.git/objects/c7/
tf5-5.0beta8/.git/objects/ca/
tf5-5.0beta8/.git/objects/ce/
tf5-5.0beta8/.git/objects/d1/
tf5-5.0beta8/.git/objects/d3/
tf5-5.0beta8/.git/objects/d4/
tf5-5.0beta8/.git/objects/d5/
tf5-5.0beta8/.git/objects/d8/
tf5-5.0beta8/.git/objects/d9/
tf5-5.0beta8/.git/objects/dc/
tf5-5.0beta8/.git/objects/dd/
tf5-5.0beta8/.git/objects/e1/
tf5-5.0beta8/.git/objects/e4/
tf5-5.0beta8/.git/objects/e5/
tf5-5.0beta8/.git/objects/e6/
tf5-5.0beta8/.git/objects/e7/
tf5-5.0beta8/.git/objects/e8/
tf5-5.0beta8/.git/objects/ea/
tf5-5.0beta8/.git/objects/eb/
tf5-5.0beta8/.git/objects/ed/
tf5-5.0beta8/.git/objects/ee/
tf5-5.0beta8/.git/objects/ef/
tf5-5.0beta8/.git/objects/f0/
tf5-5.0beta8/.git/objects/f4/
tf5-5.0beta8/.git/objects/f5/
tf5-5.0beta8/.git/objects/f6/
tf5-5.0beta8/.git/objects/f8/
tf5-5.0beta8/.git/objects/f9/
tf5-5.0beta8/.git/objects/fa/
tf5-5.0beta8/.git/objects/fb/
tf5-5.0beta8/.git/objects/fc/
tf5-5.0beta8/.git/objects/fd/
tf5-5.0beta8/.git/refs/heads/
tf5-5.0beta8/.git/refs/tags/
tf5-5.0beta8/autom4te.cache/
tf5-5.0beta8/macos/
tf5-5.0beta8/unix/
tf5-5.0beta8/win32/
/*************************************************************************
 *  TinyFugue - programmable mud client
 *  Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2002, 2003, 2004, 2005, 2006-2007 Ken Keys
 *
 *  TinyFugue (aka "tf") is protected under the terms of the GNU
 *  General Public License.  See the file "COPYING" for details.
 ************************************************************************/
static const char RCSid[] = "$Id: world.c,v 35004.77 2007/01/13 23:12:39 kkeys Exp $";


/********************************************************
 * Fugue world routines.                                *
 ********************************************************/

#include "tfconfig.h"
#include "port.h"
#include "tf.h"
#include "util.h"
#include "pattern.h"
#include "search.h"	/* for tfio.h */
#include "tfio.h"
#include "history.h"
#include "world.h"
#include "process.h"
#include "macro.h"	/* remove_world_macros() */
#include "cmdlist.h"
#include "socket.h"
#include "output.h"	/* columns */

#define LW_TABLE	001
#define LW_UNNAMED	002
#define LW_SHORT	004

static int  list_worlds(const Pattern *name, const Pattern *type,
    struct TFILE *file, int flags, int (*cmp)(const void *, const void *));
static void free_world(World *w);
static World *alloc_world(void);

static World *hworld = NULL;

World *defaultworld = NULL;


static void free_world(World *w)
{
    if (w->name)      FREE(w->name);
    if (w->character) FREE(w->character);
    if (w->pass)      FREE(w->pass);
    if (w->host)      FREE(w->host);
    if (w->port)      FREE(w->port);
    if (w->mfile)     FREE(w->mfile);
    if (w->type)      FREE(w->type);
    if (w->myhost)    FREE(w->myhost);
    if (w->screen)    free_screen(w->screen);
#if !NO_HISTORY
    if (w->history) {
        free_history(w->history);
        FREE(w->history);
    }
#endif
    FREE(w);
}

#if USE_DMALLOC
void free_worlds(void)
{
    World *next;

    if (defaultworld)
        free_world(defaultworld);
    for ( ; hworld; hworld = next) {
        next = hworld->next;
        free_world(hworld);
    }
}
#endif

static World *alloc_world(void)
{
    World *result;
    result = (World *) XMALLOC(sizeof(World));
    memset(result, 0, sizeof(World));
    return result;
}

/* A NULL name means unnamed; world will be given a temp name. */
World *new_world(const char *name, const char *type,
    const char *host, const char *port,
    const char *character, const char *pass,
    const char *mfile, int flags,
    const char *myhost)
{
    World *result;
    static int unnamed = 1;
    smallstr buffer;
    int is_redef = FALSE;

    /* unnamed worlds can't be defined but can have other fields changed. */
    if (name && (!*name || strchr(name, ' ') ||
	(*name == '(' && (*host || *port))))
    {
        eprintf("illegal world name: %s", name);
        return NULL;
    }

    if (name && cstrcmp(name, "default") == 0) {
        if (defaultworld) {
	    /* redefine existing default world */
            result = defaultworld;
            FREE(defaultworld->name);
            is_redef = TRUE;
        } else {
	    /* define default world */
            result = defaultworld = alloc_world();
        }

    } else if (name && (result = find_world(name))) {
	/* redefine existing world */
        FREE(result->name);
        is_redef = TRUE;

    } else {
	/* define new world */
        World **pp;
	if ((!*host && *port) || (*host && !*port)) {
	    eprintf("world %s: host and port must be both blank or both "
		"non-blank.", name);
	    return NULL;
	}
        for (pp = &hworld; *pp; pp = &(*pp)->next);
        *pp = result = alloc_world();
	init_list(result->triglist);
	init_list(result->hooklist);
#if !NO_HISTORY
        /* Don't allocate the history's queue until we actually need it. */
        result->history = init_history(NULL, 0);
#endif
    }

    if (name) {
        result->name = STRDUP(name);
    } else {
        sprintf(buffer, "(unnamed%d)", unnamed++);
        result->name = STRDUP(buffer);
    }

#define setfield(field) \
    do { \
        if (field && *field) { \
            if (result->field) FREE(result->field); \
            result->field = STRDUP(field); \
        } \
    } while (0);

    setfield(character);
    setfield(pass);
    setfield(host);
    setfield(port);
    setfield(mfile);
    setfield(type);
    setfield(myhost);
    result->flags |= flags;

#ifdef PLATFORM_UNIX
# ifndef __CYGWIN32__
    if (pass && *pass && loadfile && (loadfile->mode & (S_IROTH | S_IRGRP)) &&
        !loadfile->warned)
    {
        wprintf("file contains passwords and is readable by others.");
        loadfile->warned++;
    }
# endif /* __CYGWIN32__ */
#endif /* PLATFORM_UNIX */

    if (is_redef)
        do_hook(H_REDEF, "!Redefined %s %s", "%s %s", "world", result->name);
    return result;
}

/* should not be called for defaultworld */
int nuke_world(World *w)
{
    World **pp;

    if (w->sock) {
        eprintf("%s is in use.", w->name);
        return 0;
    }

    for (pp = &hworld; *pp != w; pp = &(*pp)->next);
    *pp = w->next;
    remove_world_macros(w);
    kill_procs_by_world(w);
    free_world(w);
    return 1;
}

struct Value *handle_unworld_command(String *args, int offset)
{
    World *w;
    int result = 0;
    const char *name;
    char *ptr = args->data + offset;

    while (*(name = stringarg(&ptr, NULL))) {
        if (defaultworld && cstrcmp(name, "default") == 0) {
            free_world(defaultworld);
            defaultworld = NULL;
        } else if ((w = find_world(name))) {
            result += nuke_world(w);
        } else {
            eprintf("No world %s", name);
        }
    }
    return newint(result);
}

static int worldtypecmp(const void *a, const void *b) {
    return nullcstrcmp((*(World**)a)->type, (*(World**)b)->type);
}

static int worldhostcmp(const void *a, const void *b) {
    return nullcstrcmp((*(World**)a)->host, (*(World**)b)->host);
}

static int worldportcmp(const void *a, const void *b) {
    return nullcstrcmp((*(World**)a)->port, (*(World**)b)->port);
}

static int worldcharcmp(const void *a, const void *b) {
    return nullcstrcmp((*(World**)a)->character, (*(World**)b)->character);
}

struct Value *handle_listworlds_command(String *args, int offset)
{
    int flags = LW_TABLE, mflag = matching, error = 0, result, n;
    char c;
    const char *ptr;
    int (*cmp)(const void *, const void *) = cstrpppcmp;
    Pattern type, name, *namep = NULL;

    init_pattern_str(&type, NULL);
    init_pattern_str(&name, NULL);
    startopt(CS(args), "T:uscm:S:");
    while ((c = nextopt(&ptr, NULL, NULL, &offset))) {
        switch (c) {
            case 'T':	free_pattern(&type);
			error += !init_pattern_str(&type, ptr);
			break;
            case 'u':	flags |= LW_UNNAMED; break;
            case 's':	flags |= LW_SHORT;   /* fall through */
            case 'c':	flags &= ~LW_TABLE;  break;
            case 'm':	error = ((mflag = enum2int(ptr,0,enum_match,"-m")) < 0);
			break;
	    case 'S':	n = strlen(ptr);
			if (n == 0 || cstrncmp(ptr, "name", n) == 0)
			    cmp = cstrpppcmp;
			else if (cstrncmp(ptr, "type", n) == 0)
			    cmp = worldtypecmp;
			else if (cstrncmp(ptr, "host", n) == 0)
			    cmp = worldhostcmp;
			else if (cstrncmp(ptr, "port", n) == 0)
			    cmp = worldportcmp;
			else if (cstrncmp(ptr, "character", n) == 0)
			    cmp = worldcharcmp;
			else if (cstrncmp(ptr, "-", n) == 0)
			    cmp = NULL;
			else { eprintf("invalid sort criterion"); error++; }
			break;
            default:	return shareval(val_zero);
        }
    }
    if (error) return shareval(val_zero);
    init_pattern_mflag(&type, mflag, 'T');
    Stringstriptrail(args);
    if (args->len > offset) {
        namep = &name;
        error += !init_pattern(namep, args->data + offset, mflag);
    }
    if (error) return shareval(val_zero);
    result = list_worlds(namep, type.str?&type:NULL, tfout, flags, cmp);
    free_pattern(&name);
    free_pattern(&type);
    return newint(result);
}

static int list_worlds(const Pattern *name, const Pattern *type, TFILE *file,
    int flags, int (*cmp)(const void *, const void *))
{
    World *p;
    Vector worlds = vector_init(128);
    int first = 1, i;
    int need, width, width_name, width_type, width_host, width_port;
    STATIC_BUFFER(buf);

    if (flags & LW_TABLE) {
        width = (columns < 80 ? 80 : columns) - 1;
        width_name = width / 5;
        width_type = width / 7;
        width_host = width / 3;
        width_port = 6;
        tfprintf(file, "%-*s %-*s %*s %-*s %s",
            width_name, "NAME", width_type, "TYPE", width_host, "HOST",
            width_port, "PORT", "CHARACTER");
    }

    /* collect matching worlds */
    for (p = defaultworld; p || first; p = first ? hworld : p->next, first=0)
    {
        if (!p || (!(flags & LW_UNNAMED) && p->flags & WORLD_TEMP)) continue;
        if (name && !patmatch(name, NULL, p->name)) continue;
        if (type && !patmatch(type, NULL, p->type ? p->type : "")) continue;
	vector_add(&worlds, p);
    }

    if (cmp)
	vector_sort(&worlds, cmp);

    Stringtrunc(buf, 0);
    for (i = 0; i < worlds.size; i++) {
	p = worlds.ptrs[i];
        if (flags & LW_SHORT) {
            tfputs(p->name, file);
        } else if (flags & LW_TABLE) {
            tfprintf(file, "%-*.*s %-*.*s %*.*s %-*.*s %s",
                width_name, width_name, p->name,
                width_type, width_type, p->type,
                width_host, width_host, p->host,
                width_port, width_port, p->port,
                p->character);
        } else {
            if (p->myhost) need = 9;
            else if (p->flags & ~WORLD_TEMP) need = 8;
            else if (p->mfile) need = 7;
            else if (p->character || p->pass) need = 6;
            else if (p->host || p->port) need = 4;
            else need = 2;

            Stringcpy(buf, "/test addworld(");
            Sappendf(buf, "\"%q\"", '"', p->name);
            Sappendf(buf, ", \"%q\"", '"', p->type);

            if (need < 3) goto listworld_tail;
            Sappendf(buf, ", \"%q\"", '"', p->host);

            if (need < 4) goto listworld_tail;
            Sappendf(buf, ", \"%q\"", '"', p->port);

            if (need < 5) goto listworld_tail;
            Sappendf(buf, ", \"%q\"", '"', p->character);

            if (need < 6) goto listworld_tail;
            Sappendf(buf, ", \"%q\"", '"', p->pass);

            if (need < 7) goto listworld_tail;
            Sappendf(buf, ", \"%q\"", '"', p->mfile);

            if (need < 8) goto listworld_tail;
            Sappendf(buf, ", \"%s%s%s\"",
                (p->flags & WORLD_NOPROXY) ? "p" : "",
                (p->flags & WORLD_SSL) ? "x" : "",
                (p->flags & WORLD_ECHO) ? "e" : "");

            if (need < 9) goto listworld_tail;
            Sappendf(buf, ", \"%q\"", '"', p->myhost);

listworld_tail:
	    Stringadd(buf, ')');
            tfputs(buf->data, file);
        }
    }
    vector_free(&worlds);
    return worlds.size;
}

struct Value *handle_saveworld_command(String *args, int offset)
{
    TFILE *file;
    char opt;
    const char *mode = "w";
    char *name;
    int result;

    if (restriction >= RESTRICT_FILE) {
        eprintf("restricted");
        return shareval(val_zero);
    }

    startopt(CS(args), "a");
    while ((opt = nextopt(NULL, NULL, NULL, &offset))) {
        if (opt != 'a') return shareval(val_zero);
        mode = "a";
    }
    if ((name = tfname(args->data + offset, "WORLDFILE")) == NULL)
        return shareval(val_zero);
    if ((file = tfopen(name, mode)) == NULL) {
        operror(args->data + offset);
        return shareval(val_zero);
    }
    oprintf("%% %sing world definitions to %s", *mode == 'a' ? "Append" :
        "Writ", file->name);
    result = list_worlds(NULL, NULL, file, 0, NULL);
    tfclose(file);
    return newint(result);
}

World *find_world(const char *name)
{
    World *p;

    if (!name || !*name) return hworld;
    for (p=hworld; p && (!p->name || cstrcmp(name, p->name) != 0); p = p->next);
    return p;
}

/* Perform (*func)(world) on every world */
void mapworld(void (*func)(World *world))
{
    World *w;

    for (w = hworld; w; w = w->next)
        (*func)(w);
}