tmuck2.4/
tmuck2.4/admin/scripts/
tmuck2.4/docs/
tmuck2.4/minimal-db/
tmuck2.4/minimal-db/data/
tmuck2.4/minimal-db/logs/
tmuck2.4/minimal-db/muf/
tmuck2.4/old/
tmuck2.4/src/
tmuck2.4/src/compile/
tmuck2.4/src/editor/
tmuck2.4/src/game/
tmuck2.4/src/interface/
tmuck2.4/src/scripts/
tmuck2.4/src/utilprogs/
/* Copyright (c) 1992 by David Moore.  All rights reserved. */
/* macro.c,v 2.12 1997/08/18 19:11:35 dmoore Exp */
#include "config.h"

#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "db.h"
#include "externs.h"
#include "params.h"

/* Entry stored in hashtable (and linked list) storing all of the pertinent
   information about macro entries.
   NOTE: name _must_ be the first field of the struct.  This is because it
   uses the generic compare and key routines which expect it there. */
struct macro_entry {
    const char *name;		/* Name of macro. */
    const char *definition;	/* Expansion of the macro. */
    dbref player;		/* Player who created this entry. */
    struct macro_entry *next;	/* In order linked list. */
};

static struct macro_entry *macro_head = NULL; /* Linked list of macros. */
static hash_tab *macro_table = NULL; /* Fast access hash table. */


/* Free up the memory used by all the macros. */
void clear_macros(void)
{
    clear_hash_table(macro_table);
    macro_table = NULL;
    macro_head = NULL;
}


/* Routine called when a macro entry is removed. */
void delete_macro_entry(void *entry)
{
    struct macro_entry *macro, *prev;

    macro = (struct macro_entry *) entry;

    /* Snip it out of the linked list. */
    if (macro == macro_head) {
	macro_head = macro_head->next;
    } else {
	/* Find it in list. */
	for (prev = macro_head; prev; prev = prev->next)
	    if (macro == prev->next) {
		prev->next = macro->next;
		break;
	    }
    }

    FREE_STRING(macro->name);
    FREE_STRING(macro->definition);
    FREE(macro);
}


/* Add a new macro definition.  Return 0 if already present.  Otherwise
   add the definition and return 1. */
int new_macro(const char *name, const char *definition, const dbref player)
{
    struct macro_entry *macro, *prev;
    struct macro_entry dummy;
    Buffer buf;
    char *p;

    /* Make the name all lowercase. */
    Bufcpy(&buf, name);
    for (p = Buftext(&buf); *p; p++) *p = tolower(*p);
    name = Buftext(&buf);

    dummy.name = name;
    if (find_hash_entry(macro_table, &dummy))
	return 0;

    /* Find where it goes in the list. */
    dummy.next = macro_head;
    for (prev = &dummy; prev->next; prev = prev->next) {
	if (strcmp(name, prev->next->name) < 0)
	    break;
    }

    /* Make the new entry. */
    MALLOC(macro, struct macro_entry, 1);
    macro->name = ALLOC_STRING(name);
    if (definition && *definition) {
	macro->definition = ALLOC_STRING(definition);
    } else {
	macro->definition = ALLOC_STRING(" ");
    }
    macro->player = player;

    /* Chain it in. */
    if (prev == &dummy) {
	macro->next = prev->next;
	macro_head = macro;
    } else {
	macro->next = prev->next;
	prev->next = macro;
    }

    /* Add it to the hash table. */
    add_hash_entry(macro_table, macro);

    return 1;
}


/* Removes the named macro, returns 0 if fails, otherwise 1. */
int kill_macro(const dbref player, const char *name)
{
    struct macro_entry match;
    const struct macro_entry *result;

    match.name = name;
    result = find_hash_entry(macro_table, &match);

    if (!result) return 0;

    if ((player != result->player) && !Wizard(player))
	return 0;

    clear_hash_entry(macro_table, &match);

    return 1;
}


/* Chown macros from own player to another (for toading). */
void chown_macros(const dbref from, const dbref to)
{
    struct macro_entry *temp;

    for (temp = macro_head; temp; temp = temp->next) {
	if (temp->player == from) {
	    temp->player = to;
	}
    }
}


/* Return the macro definition for the given macro, or NULL if not found. */
const char *macro_expansion(const char *name)
{
    struct macro_entry match;
    const struct macro_entry *result;

    match.name = name;
    result = find_hash_entry(macro_table, &match);
    
    if (!result) return NULL;
    else return result->definition;
}


/* Read macros out of the already open file. */
void macroload(FILE *f)
{
    const char *line;
    Buffer buf;
    
    while ((line = getstring(f))) {
	Bufcpy(&buf, line);	/* Since getstring uses a static buffer. */
	line = getstring(f);
	new_macro(Buftext(&buf), line, getref(f));
    }
}


/* Dump the macros into the already opened file. */
int macrodump(const char *name)
{
    FILE *f;
    struct macro_entry *temp;

    f = fopen(name, "w");
    if (!f) return 0;

    for (temp = macro_head; temp; temp = temp->next) {
	putstring(f, temp->name);
	putstring(f, temp->definition);
	putref(f, temp->player);
	fflush(f);
	if (feof(f) || ferror(f)) return 0;
    }

    fclose(f);
    
    return 1;
}


/* Dump a list of macros in the specified range of dictionary. */
void show_macros(const char *first, const char *last, const int full, const dbref player)
{
    /* This should really be made to use a Buffer.  But it should be ok,
       until bufsprint gets field widths.  20 is to provide enough
       space for owner's name in displayed version.  FIX FIX FIX. */
    static char  buf[BUFFER_LEN+20];
    struct macro_entry *macro;
    int last_len, first_len;
    const char *player_name;

    last_len = strlen(last);
    first_len = strlen(first);

    buf[0] = '\0';		/* Used for abbreviated listing. */

    for (macro = macro_head; macro; macro = macro->next) {
	if (strncmp(macro->name, first, first_len) < 0)
	    continue;
	if (strncmp(macro->name, last, last_len) > 0)
	    break;
	    
	/* In the correct range. */
	if (full) {
	    if (Garbage(macro->player) 
		|| (Typeof(macro->player) != TYPE_PLAYER)) {
		log_status("BAD MACRO: macro owner is not a player: %s",
			   macro->name);
		player_name = "*ILLEGAL*";
	    } else {
		player_name = GetName(macro->player);
	    }

	    sprintf(buf, "%-16s %-16s  %s", macro->name,
		    player_name,
		    macro->definition);
	    notify(player, "%s", buf);
	} else {
	    /* Was originally 16. */
	    sprintf(buf + strlen(buf), "%-15s", macro->name);
	    if (strlen(buf) > 70) {
		notify(player, "%s", buf);
		buf[0] = '\0';
	    }
	}
    }

    /* flush last line if doing short mode. */
    if (!full && *buf)
	notify(player, "%s", buf);
}


void init_macros(void)
{
    /* Initialize hash table if needed. */
    if (macro_table) return;

    macro_table = init_hash_table("Macro table", compare_generic_hash,
				  make_key_generic_hash, delete_macro_entry,
				  MACRO_HASH_SIZE);
}