/
2.0.5/doc/
2.0.5/gnu/
2.0.5/sha/
/* misc.c */

#include "config.h"

/*
 *		       This file is part of TeenyMUD II.
 *		 Copyright(C) 1993, 1994, 1995 by Jason Downs.
 *                           All rights reserved.
 * 
 * TeenyMUD II is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * TeenyMUD II is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file 'COPYING'); if not, write to
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA.
 *
 */

/* AIX requires this to be the first thing in the file. */
#ifdef __GNUC__
#define alloca	__builtin_alloca
#else	/* not __GNUC__ */
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#else	/* not HAVE_ALLOCA_H */
#ifdef _AIX
 #pragma alloca
#endif	/* not _AIX */
#endif	/* not HAVE_ALLOCA_H */
#endif 	/* not __GNUC__ */

#include <stdio.h>
#include <sys/types.h>
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif			/* HAVE_STRING_H */
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif				/* HAVE_MALLOC_H */
#include <ctype.h>
#include <time.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif			/* HAVE_STDLIB_H */

#include "conf.h"
#include "flaglist.h"
#include "teeny.h"
#include "externs.h"

/* Some generic utility functions for TeenyMUD */

/* Write a timestamp on an object. */

void stamp(obj, code)
    int obj, code;
{
  int uses;

  switch (code) {
  case STAMP_USED:
    /* case STAMP_MODIFIED: */
    if (!isPLAYER(obj)) {
      if (set_int_elt(obj, TIMESTAMP, (int) mudstat.now) == -1)
	logfile(LOG_ERROR, "stamp: failed to update object #%d\n", obj);
      if (get_int_elt(obj, USES, &uses) != -1) {
	uses++;
	if (set_int_elt(obj, USES, uses) == -1)
	  logfile(LOG_ERROR, "stamp: couldn't update use count of object #%d\n",
	      	  obj);
      }
    }
    break;
  case STAMP_LOGIN:
    if (isPLAYER(obj)) {
      if (set_int_elt(obj, TIMESTAMP, (int) mudstat.now) == -1)
	logfile(LOG_ERROR, "stamp: failed to update object #%d\n", obj);
      if (get_int_elt(obj, USES, &uses) != -1) {
	uses++;
	if (set_int_elt(obj, USES, uses) == -1)
	  logfile(LOG_ERROR, "stamp: couldn't update use count of object #%d\n",
	          obj);
      }
    }
    break;
  case STAMP_CREATED:
    if (set_int_elt(obj, CREATESTAMP, (int) mudstat.now) == -1)
      logfile(LOG_ERROR, "stamp: failed to update object #%d\n", obj);
    break;
  }
}

/* assuming argument is a pathname, return last component */
char *ty_basename(pname)
    char *pname;
{
  register char *ptr;

  if(pname != (char *)NULL) {
    ptr = &pname[strlen(pname)];

    while((*ptr != '/') && (ptr > pname))
      ptr--;
    if(*ptr == '/')
      ptr++;

    return(ptr);
  } else
    return(pname);
}

struct alphatable {
  char upcase;
  char lowcase;
};

static struct alphatable alphatab[26] = {
  {'A', 'a'}, {'B', 'b'}, {'C', 'c'}, {'D', 'd'}, {'E', 'e'},
  {'F', 'f'}, {'G', 'g'}, {'H', 'h'}, {'I', 'i'}, {'J', 'j'},
  {'K', 'k'}, {'L', 'l'}, {'M', 'm'}, {'N', 'n'}, {'O', 'o'},
  {'P', 'p'}, {'Q', 'q'}, {'R', 'r'}, {'S', 's'}, {'T', 't'},
  {'U', 'u'}, {'V', 'v'}, {'W', 'w'}, {'X', 'x'}, {'Y', 'y'},
  {'Z', 'z'}
};

/* fast, safe, ANSI compliant tolower() */
#if defined(__STDC__)
INLINE char to_lower(register char c)
#else
INLINE char to_lower(c)
    register char c;
#endif
{
  register int i;

  for(i = 0; i < 26; i++) {
    if(alphatab[i].upcase == c)
      return(alphatab[i].lowcase);
  }
  return (c);
}

/* fast, safe, ANSI compliant toupper() */
#if defined(__STDC__)
INLINE char to_upper(register char c)
#else
INLINE char to_upper(c)
    register char c;
#endif
{
  register int i;

  for(i = 0; i < 26; i++) {
    if(alphatab[i].lowcase == c)
      return(alphatab[i].upcase);
  }
  return (c);
}

/* checks to see if object is near player. */
int nearby(player, object)
    int player, object;
{
  int ploc, oloc;

  if(!exists_object(player) || !exists_object(object))
    return(0);
  if((get_int_elt(object, LOC, &oloc) == -1) || !exists_object(oloc))
    return(0);
  if(oloc == player)
    return(1);
  if((get_int_elt(player, LOC, &ploc) == -1) || !exists_object(ploc))
    return(0);
  return((ploc == oloc) || (object == ploc));
}

/* checks to see if object is in the list */
int member(object, list)
    int object, list;
{
  while (list != -1) {
    if (list == object)
      return (1);
    if (get_int_elt(list, NEXT, &list) == -1) {
      logfile(LOG_ERROR, "member: bad next reference on object #%d\n", list);
      return (0);
    }
  }
  return (0);
}

/* drop something from a list, my style. */
void list_drop(what, where, code)
    int what, where, code;
{
  int list, prev, next;

  if(!is_list(code)){
    logfile(LOG_ERROR, "list_drop: passed a bad list code (%d)\n", code);
    return;
  }

  if(get_int_elt(where, code, &list) == -1) {
    logfile(LOG_ERROR, "list_drop: bad list on object #%d\n", where);
    return;
  }

  for(prev = -1; list != -1; prev = list, list = next) {
    if(get_int_elt(list, NEXT, &next) == -1) {
      logfile(LOG_ERROR, "list_drop: bad next reference on object #%d\n", list);
      return;
    }
    if(list == what) {		/* got it */
      if(prev == -1) {		/* head of list */
	if(set_int_elt(where, code, next) == -1) {
	  logfile(LOG_ERROR, "list_drop: couldn't set list on object #%d\n",
	      	  where);
	  return;
	}
      } else {
	if(set_int_elt(prev, NEXT, next) == -1) {
	  logfile(LOG_ERROR, "list_drop: couldn't set next on object #%d\n",
		  prev);
	  return;
	}
      }
      break;			/* done */
    }
  }
}

/* add an object to a list, my style. */
void list_add(what, where, code)
    int what, where, code;
{
  int list;

  if(!is_list(code)){
    logfile(LOG_ERROR, "list_add: passed a bad list code (%d)\n", code);
    return;
  }

  if(get_int_elt(where, code, &list) == -1){
    logfile(LOG_ERROR, "list_add: bad list on object #%d\n", where);
    return;
  }
  if(member(what, list))
    return;

  if(set_int_elt(what, NEXT, list) == -1){
    logfile(LOG_ERROR, "list_add: couldn't set next on object #%d\n", what);
    return;
  }
  if(set_int_elt(where, code, what) == -1){
    logfile(LOG_ERROR, "list_add: couldn't set list on object #%d\n", where);
    return;
  }
}

void parse_flags(str, ret)
    char *str;
    int *ret;
{
  register FlagList *flags;
  register int matched;

  /* Special key(s) for no flags. */
  if((strcmp(str, "0") == 0) || (strcasecmp(str, "none") == 0)) {
    ret[0] = 0;
    ret[1] = 0;
    return;
  }

  while (*str) {
    matched = 0;

    for (flags = GenFlags; flags->name; flags++) {
      if (flags->chr == *str) {
	ret[flags->word] |= flags->code;
	matched = 1;
	break;
      }
    }
    for (flags = PlayerFlags; flags->name; flags++) {
      if(flags->chr == *str) {
        ret[flags->word] |= flags->code;
	matched = 1;
	break;
      }
    }
    for (flags = RoomFlags; flags->name; flags++) {
      if(flags->chr == *str) {
        ret[flags->word] |= flags->code;
	matched = 1;
	break;
      }
    }
    for (flags = ThingFlags; flags->name; flags++) {
      if(flags->chr == *str) {
        ret[flags->word] |= flags->code;
	matched = 1;
	break;
      }
    }
    for (flags = ExitFlags; flags->name; flags++) {
      if(flags->chr == *str) {
        ret[flags->word] |= flags->code;
	matched = 1;
	break;
      }
    }

    if (!matched) {
      ret[0] = -1;
      ret[1] = -1;
      return;
    }
    str++;
  }
}

void parse_time(str, ptr)
    register char *str;
    register time_t *ptr;
{
  char *sptr;

  *ptr = (time_t)strtol(str, &sptr, 10);
  switch(*sptr) {
  case 'm':
    *ptr = (*ptr) * 60;
    break;
  case 'h':
    *ptr = (*ptr) * 3600;
    break;
  case 'd':
    *ptr = (*ptr) * 86400;
    break;
  }
}

/* parse a string until you hit a slash, stripping whitespace. */
/* string gets destroyed. */
char *parse_slash(first, second)
    char *first, **second;
{
  register char *ptr, *optr;

  if((first == (char *)NULL) || (first[0] == '\0'))
    return((char *)NULL);

  for(ptr = first; *ptr && (*ptr != '/'); ptr++);
  if(*ptr == '/') {
    optr = ptr;
    *ptr++ = '\0';

    /* skip whitespace. */
    while(*ptr && isspace(*ptr))
      ptr++;
    *second = ptr;

    /* eat trailing whitespace. */
    while((optr > first) && isspace(optr[-1]))
      optr--;
    if(isspace(*optr))
      *optr = '\0';
  } else
    *second = (char *)NULL;
  return(first);
}

/* parse a string until you hit delim, following standard quoting rules. */
/* destroys string, and returns a pointer to the character after delim. */
#if defined(__STDC__)
char *parse_to(register char *string, char sdelim, char delim)
#else
char *parse_to(string, sdelim, delim)
    register char *string;
    char sdelim, delim;
#endif
{
  register int depth = 1;

  while(*string && depth) {
    if (*string == '\\' && string[1]) {
      string++;
    } else if(*string == sdelim) {
      depth++;
    } else if(*string == delim) {
      depth--;
    }
    if (depth)
      string++;
  }
  if (*string) {
    *string++ = '\0';
    return(string);
  } else
    return((char *)NULL);
}

/* Returns non-NULL if one string is contained within the other. */
char *strcasestr(str, key)
    register char *str, *key;
{
  register char *curr = key;
  register char *holder = str;

  while (str && *str && *curr) {
    if (to_lower(*str) == to_lower(*curr))
      curr++;
    else {
      curr = key;
      holder = str + 1;
    }
    str++;
  }
  if (*curr == '\0')
    return (holder);
  else
    return ((char *) NULL);
}

int stringprefix(str, prefix)
    register char *str, *prefix;
{
  register int count = 0;

  while(*str && *prefix && (to_lower(*prefix) == to_lower(*str))) {
    count++;
    str++;
    prefix++;
  }
  if(*prefix == '\0')
    return(count);
  return(0);
}

/* malloc stuff */
static bool malloc_inited = 0;

#ifdef MEMTRACKING
static void ty_malloc_report()
{
  register struct mmem *curr;

  for(curr = mudstat.memlist; curr != (struct mmem *)NULL; curr = curr->next) {
    logfile(LOG_STATUS, "ty_malloc_report: %d bytes still malloc'd from %s.\n",
	    curr->msize, curr->mfunc);
  }
  logfile(LOG_STATUS, "ty_malloc_report: %ld bytes total still malloc'd.\n",
	  mudstat.memtotal);
}
#endif			/* MEMTRACKING */

void ty_malloc_init()
{
  if(!malloc_inited) {
#if defined(sun) && defined(M_MXFAST) && defined(HAVE_MALLOPT)
    mallopt(M_MXFAST, 128);
    mallopt(M_NLBLKS, 50);
    mallopt(M_GRAIN, 16);
#endif
#ifdef MEMTRACKING
    mudstat.memlist = (struct mmem *)NULL;
    mudstat.memtotal = 0;

    /* some C libraries (i.e., GNU) have both on_exit() and atexit(). */
#if defined(MALLOC_DEBUG) && defined(HAVE_ON_EXIT) && !defined(HAVE_ATEXIT)
    on_exit(ty_malloc_report, NULL);
#endif
#if defined(MALLOC_DEBUG) && defined(HAVE_ATEXIT)
    atexit(ty_malloc_report);
#endif
#endif			/* MEMTRACKING */
    malloc_inited = TRUE;
  }
}

VOID *ty_malloc(size, where)
    int size;
    char *where;
{
  register VOID *ret;
#ifdef MEMTRACKING
  register mmem *newmem;
#endif			/* MEMTRACKING */

  if (!malloc_inited)
    ty_malloc_init();

  if ((where == (char *)NULL) || (where[0] == '\0'))
    where = "unknown";

  if (size <= 0) {
    logfile(LOG_ERROR, "ty_malloc: got a %d size request from %s.\n", size,
	    where);
    return ((VOID *) NULL);
  }
  ret = (VOID *)malloc((size_t)size);
  if (ret == (VOID *)NULL) {
    panic("%s: malloc() failed (%d bytes)\n", where, size);
  }

#ifdef MEMTRACKING
  newmem = (struct mmem *)malloc(sizeof(struct mmem));
  if (newmem == (struct mmem *)NULL) {
    panic("%s: malloc() failed (%d bytes)\n", where, size);
  }
  newmem->mfunc = (char *)malloc(srtlen(where)+1);
  if (newmem->mfunc == (char *)NULL) {
    panic("%s: malloc() failed (%d bytes)\n", where, size);
  }

  newmem->msize = size;
  newmem->mptr = ret;
  strcpy(newmem->mfunc, where);
  newmem->next = mudstat->memlist;
  mudstat->memlist = newmem;
  mudstat->memtotal += size;
#if MEMTRACKING > 1
  logfile(LOG_STATUS, "ty_malloc: allocation of %d bytes for %s, %x\n", size,
          where, ret);
#endif
#endif			/* MEMTRACKING */

  return (ret);
}

VOID *ty_realloc(ptr, size, where)
    VOID *ptr;
    int size;
    char *where;
{
  register VOID *ret;
#ifndef __STDC__
  VOID *realloc();
#endif
#ifdef MEMTRACKING
  register struct mmem *curr;
#endif			/* MEMTRACKING */

  if(!malloc_inited)
    ty_malloc_init();

  if((where == (char *)NULL) || (where[0] == '\0'))
    where = "unknown";

#ifdef MEMTRACKING
  /* This is *slow* */
  for(curr = mudstat.memlist; curr != (struct mmem *)NULL; curr = curr->next) {
    if(curr->mptr == ptr)
      break;
  }
  if(curr != (struct mmem *)NULL) {
#if MEMTRACKING > 1
    logfile(LOG_STATUS,
	    "ty_realloc: realloc of %d bytes, was %d bytes from %s\n",
	    size, curr->size, curr->mfunc);
#endif
    mudstat.memtotal -= curr->msize;
    mudstat.memtotal += size;
    curr->msize = size;
    if(curr->mfunc != (char *)NULL)
      free((VOID *)curr->mfunc);
    curr->mfunc = (char *)malloc(strlen(where) + 1);
    if(curr->mfunc == (char *)NULL) {
      panic("%s: malloc() failed (%d bytes)\n", where, size);
    }
    strcpy(curr->mfunc, where);
#endif			/* MEMTRACKING */

  ret = realloc(ptr, size);
  if(ret == (VOID *)NULL) {
    panic("%s: realloc() failed (%d bytes)\n", where, size);
  }
  return(ret);
}

void ty_free(ptr)
    VOID *ptr;
{
#ifdef MEMTRACKING
  register struct mmem *curr, *prev;
#endif			/* MEMTRACKING */

  if (ptr == (VOID *)NULL)
    return;			/* Ok */

  if(!malloc_inited)
    ty_malloc_init();

#ifdef MEMTRACKING
  /* This is *slow*. */
  for(prev = mudstat.memlist, curr = mudstat.memlist;
      curr != (struct mmem *)NULL; prev = curr, curr = curr->next) {
    if(curr->mptr == ptr)
      break;
  }
  if(curr != (struct mmem *)NULL) {
    /* Unlink and free. */
    mudstat.memtotal -= curr->msize;
    prev->next = curr->next;

    if(curr->mfunc != (chr *)NULL)
      free((VOID *)curr->mfunc);
    free((VOID *)curr);
  }
#endif			/* MEMTRACKING */
  free(ptr);
}

char *ty_strdup(str, where)
    char *str, *where;
{
  register char *ret = (char *)NULL;
  register int idx, len;

  if (str != (char *)NULL) {
    len = strlen(str) + 1;
    ret = (char *) ty_malloc(len, where);
    for (idx = 0; idx < len; idx++)
      ret[idx] = str[idx];
  }
  return (ret);
}

/* strncpy(), my way. */
void ty_strncpy(dest, source, len)
    char dest[], *source;
    int len;
{
  register int i;

  for(i = 0; (i < len) && (source[i] != '\0'); i++) {
    dest[i] = source[i];
  }
  dest[i] = '\0';
}

/* copy a file, using the stdio library. */
/* returns -1 on failure, or 0 upon success. */
void copy_file(source, dest)
    char *source, *dest;
{
  FILE *sfp, *dfp;
  char buf[512];
  register int ret;

  if((sfp = fopen(source, "r")) == (FILE *)NULL) {
    logfile(LOG_ERROR, "copy_file: couldn't open %s for read.\n", source);
    return;
  }
  if((dfp = fopen(dest, "w")) == (FILE *)NULL) {
    logfile(LOG_ERROR, "copy_file: couldn't open %s for write.\n", dest);
    return;
  }

  while(!feof(sfp)) {
    if(((ret = fread(buf, sizeof(char), 512, sfp)) < 512) && !feof(sfp)) {
      logfile(LOG_ERROR, "copy_file: failed read from %s.\n", source);
      goto copy_done;
    }
    if(fwrite(buf, sizeof(char), ret, dfp) < ret) {
      logfile(LOG_ERROR, "copy_file: failed write to %s.\n", dest);
      goto copy_done;
    }
  }

copy_done:

  fclose(sfp);
  fclose(dfp);
}