/* ************************************************************************
*  file: modify.c                                         Part of DIKUMUD *
*  Usage: Run-time modification (by users) of game variables              *
*  Copyright (C) 1990, 1991 - see 'license.doc' for complete information. *
************************************************************************ */

#include "os.h"

#include "structs.h"
#include "utils.h"
#include "interpreter.h"
#include "handler.h"
#include "db.h"
#include "comm.h"
#include "prototypes.h"

#define REBOOT_AT    10         /* 0-23, time of optional reboot if -e lib/reboot */


#define TP_MOB    0
#define TP_OBJ     1
#define TP_ERROR  2


void show_string (struct descriptor_data *d, char *input);



char *string_fields[] = {
  "name",
  "short",
  "long",
  "description",
  "title",
  "delete-description",
  "\n"
};




/* maximum length for text field x+1 */
int length[] = {
  15,
  60,
  256,
  240,
  60
};




char *skill_fields[] = {
  "learned",
  "recognize",
  "\n"
};



/* ************************************************************************
*  modification of malloc'ed strings                                      *
************************************************************************ */

/* Add user input to the 'current' string (as defined by d->str) */
void string_add (struct descriptor_data *d, char *str)
{
  char *scan;
  int terminator = 0;

  /* determine if this is the terminal string, and truncate if so */
  for (scan = str; *scan; scan++)
    if (terminator = (*scan == '@')) {
      *scan = '\0';
      break;
    }

  if (!(*d->str)) {
    if ((int)strlen (str) > d->max_str) {
      send_to_char ("String too long - Truncated.\n\r", d->character);
      *(str + d->max_str) = '\0';
      terminator = 1;
    }
    CREATE (*d->str, char, strlen (str) + 3);
    strcpy (*d->str, str);
  } else {
    if ((int)strlen (str) + (int)strlen (*d->str) > d->max_str) {
      send_to_char ("String too long. Last line skipped.\n\r", d->character);
      terminator = 1;
    } else {
      if (!(*d->str = (char *) realloc (*d->str, strlen (*d->str) +
            strlen (str) + 3))) {
        perror ("string_add");
        WIN32CLEANUP
        exit (1);
      }
      strcat (*d->str, str);
    }
  }

  if (terminator) {
    d->str = 0;
    if (d->connected == CON_EXDSCR) {
      SEND_TO_Q (MENU, d);
      d->connected = CON_SLCT;
    }
  } else
    strcat (*d->str, "\n\r");
}


#undef MAX_STR

/* interpret an argument for do_string */
void quad_arg (char *arg, int *type, char *name, int *field, char *string)
{
  char buf[MAX_STRING_LENGTH];
  int i;

  /* determine type */
  arg = one_argument (arg, buf);
  if (is_abbrev (buf, "char"))
    *type = TP_MOB;
  else if (is_abbrev (buf, "obj"))
    *type = TP_OBJ;
  else {
    *type = TP_ERROR;
    return;
  }

  /* find name */
  arg = one_argument (arg, name);

  /* field name and number */
  arg = one_argument (arg, buf);
  if (!(*field = old_search_block (buf, 0, strlen (buf), string_fields, 0)))
    return;

  /* string */
  for (; isspace ((int)*arg); arg++);
  for (; *string = *arg; arg++, string++);

  return;
}




/* modification of malloc'ed strings in chars/objects */
void do_string (struct char_data *ch, char *arg, int cmd)
{
  char name[MAX_STRING_LENGTH], string[MAX_STRING_LENGTH];
  int field, type;
  struct char_data *mob;
  struct obj_data *obj;
  struct extra_descr_data *ed, *tmp;

  if (IS_NPC (ch))
    return;

  quad_arg (arg, &type, name, &field, string);

  if (type == TP_ERROR) {
    send_to_char
      ("Syntax:\n\rstring ('obj'|'char') <name> <field> [<string>].", ch);
    return;
  }

  if (!field) {
    send_to_char ("No field by that name. Try 'help string'.\n\r", ch);
    return;
  }

  if (type == TP_MOB) {
    /* locate the beast */
    if (!(mob = get_char_vis (ch, name))) {
      send_to_char ("I don't know anyone by that name...\n\r", ch);
      return;
    }

    switch (field) {
    case 1:
      if (!IS_NPC (mob) && GET_LEVEL (ch) < 24) {
        send_to_char ("You can't change that field for players.", ch);
        return;
      }
      ch->desc->str = &GET_NAME (mob);
      if (!IS_NPC (mob))
        send_to_char ("WARNING: You have changed the name of a player.\n\r",
          ch);
      break;
    case 2:
      if (!IS_NPC (mob)) {
        send_to_char ("That field is for monsters only.\n\r", ch);
        return;
      }
      ch->desc->str = &mob->player.short_descr;
      break;
    case 3:
      if (!IS_NPC (mob)) {
        send_to_char ("That field is for monsters only.\n\r", ch);
        return;
      }
      ch->desc->str = &mob->player.long_descr;
      break;
    case 4:
      ch->desc->str = &mob->player.description;
      break;
    case 5:
      if (IS_NPC (mob)) {
        send_to_char ("Monsters have no titles.\n\r", ch);
        return;
      }
      ch->desc->str = &mob->player.title;
      break;
    default:
      send_to_char ("That field is undefined for monsters.\n\r", ch);
      return;
      break;
    }
  } else {                      /* type == TP_OBJ */

    /* locate the object */
    if (!(obj = get_obj_vis (ch, name))) {
      send_to_char ("Can't find such a thing here..\n\r", ch);
      return;
    }

    switch (field) {
    case 1:
      ch->desc->str = &obj->name;
      break;
    case 2:
      ch->desc->str = &obj->short_description;
      break;
    case 3:
      ch->desc->str = &obj->description;
      break;
    case 4:
      if (!*string) {
        send_to_char ("You have to supply a keyword.\n\r", ch);
        return;
      }
      /* try to locate extra description */
      for (ed = obj->ex_description;; ed = ed->next)
        if (!ed) {              /* the field was not found. create a new one. */
          CREATE (ed, struct extra_descr_data, 1);
          ed->next = obj->ex_description;
          obj->ex_description = ed;
          CREATE (ed->keyword, char, strlen (string) + 1);
          strcpy (ed->keyword, string);
          ed->description = 0;
          ch->desc->str = &ed->description;
          send_to_char ("New field.\n\r", ch);
          break;
        } else if (!str_cmp (ed->keyword, string)) {    /* the field exists */
          free (ed->description);
          ed->description = 0;
          ch->desc->str = &ed->description;
          send_to_char ("Modifying description.\n\r", ch);
          break;
        }
      ch->desc->max_str = MAX_STRING_LENGTH;
      return;                   /* the stndrd (see below) procedure does not apply here */
      break;
    case 6:
      if (!*string) {
        send_to_char ("You must supply a field name.\n\r", ch);
        return;
      }
      /* try to locate field */
      for (ed = obj->ex_description;; ed = ed->next)
        if (!ed) {
          send_to_char ("No field with that keyword.\n\r", ch);
          return;
        } else if (!str_cmp (ed->keyword, string)) {
          free (ed->keyword);
          if (ed->description)
            free (ed->description);

          /* delete the entry in the desr list */
          if (ed == obj->ex_description)
            obj->ex_description = ed->next;
          else {
            for (tmp = obj->ex_description; tmp->next != ed; tmp = tmp->next);
            tmp->next = ed->next;
          }
          free (ed);

          send_to_char ("Field deleted.\n\r", ch);
          return;
        }
      break;
    default:
      send_to_char ("That field is undefined for objects.\n\r", ch);
      return;
      break;
    }
  }

  if (*ch->desc->str) {
    free (*ch->desc->str);
  }

  if (*string) {                /* there was a string in the argument array */
    if ((int)strlen (string) > length[field - 1]) {
      send_to_char ("String too long - truncated.\n\r", ch);
      *(string + length[field - 1]) = '\0';
    }
    CREATE (*ch->desc->str, char, strlen (string) + 1);
    strcpy (*ch->desc->str, string);
    ch->desc->str = 0;
    send_to_char ("Ok.\n\r", ch);
  } else {                      /* there was no string. enter string mode */

    send_to_char ("Enter string. terminate with '@'.\n\r", ch);
    *ch->desc->str = 0;
    ch->desc->max_str = length[field - 1];
  }
}















/* db stuff *********************************************** */


/* One_Word is like one_argument, execpt that words in quotes "" are */
/* regarded as ONE word                                              */

char *one_word (char *argument, char *first_arg)
{
  int found, begin, look_at;

  found = begin = 0;

  do {
    for (; isspace ((int)*(argument + begin)); begin++);

    if (*(argument + begin) == '\"') {  /* is it a quote */

      begin++;

      for (look_at = 0; (*(argument + begin + look_at) >= ' ') &&
        (*(argument + begin + look_at) != '\"'); look_at++)
        *(first_arg + look_at) = LOWER (*(argument + begin + look_at));

      if (*(argument + begin + look_at) == '\"')
        begin++;

    } else {

      for (look_at = 0; *(argument + begin + look_at) > ' '; look_at++)
        *(first_arg + look_at) = LOWER (*(argument + begin + look_at));

    }

    *(first_arg + look_at) = '\0';
    begin += look_at;
  }
  while (fill_word (first_arg));

  return (argument + begin);
}


struct help_index_element *build_help_index (FILE * fl, int *num)
{
  int nr = -1, issorted, i;
  struct help_index_element *list = 0, mem;
  char buf[81], tmp[81], *scan;
  long pos;

  for (;;) {
    pos = ftell (fl);
    FGETS (buf, 81, fl);
    *(buf + strlen (buf) - 1) = '\0';
    scan = buf;
    for (;;) {
      /* extract the keywords */
      scan = one_word (scan, tmp);

      if (!*tmp)
        break;

      if (!list) {
        CREATE (list, struct help_index_element, 1);
        nr = 0;
      } else
        RECREATE (list, struct help_index_element, ++nr + 1);

      list[nr].pos = pos;
      CREATE (list[nr].keyword, char, strlen (tmp) + 1);
      strcpy (list[nr].keyword, tmp);
    }
    /* skip the text */
    do
      FGETS (buf, 81, fl);
    while (*buf != '#');
    if (*(buf + 1) == '~')
      break;
  }
  /* we might as well sort the stuff */
  do {
    issorted = 1;
    for (i = 0; i < nr; i++)
      if (str_cmp (list[i].keyword, list[i + 1].keyword) > 0) {
        mem = list[i];
        list[i] = list[i + 1];
        list[i + 1] = mem;
        issorted = 0;
      }
  }
  while (!issorted);

  *num = nr;
  return (list);
}



void page_string (struct descriptor_data *d, char *str, int keep_internal)
{
  if (!d)
    return;

  if (keep_internal) {
    CREATE (d->showstr_head, char, strlen (str) + 1);
    strcpy (d->showstr_head, str);
    d->showstr_point = d->showstr_head;
  } else
    d->showstr_point = str;

  show_string (d, "");
}



void show_string (struct descriptor_data *d, char *input)
{
  char buffer[MAX_STRING_LENGTH], buf[MAX_INPUT_LENGTH];
  register char *scan, *chk;
  int lines = 0, toggle = 1;

  one_argument (input, buf);

  if (*buf) {
    if (d->showstr_head) {
      free (d->showstr_head);
      d->showstr_head = 0;
    }
    d->showstr_point = 0;
    return;
  }

  /* show a chunk */
  for (scan = buffer;; scan++, d->showstr_point++)
    if ((((*scan = *d->showstr_point) == '\n') || (*scan == '\r')) &&
      ((toggle = -toggle) < 0))
      lines++;
    else if (!*scan || (lines >= 22)) {
      *scan = '\0';
      SEND_TO_Q (buffer, d);

      /* see if this is the end (or near the end) of the string */
      for (chk = d->showstr_point; isspace ((int)*chk); chk++);
      if (!*chk) {
        if (d->showstr_head) {
          free (d->showstr_head);
          d->showstr_head = 0;
        }
        d->showstr_point = 0;
      }
      return;
    }
}






void night_watchman (void)
{
  time_t tc;
  struct tm *t_info;

  extern int shutdown_server;

  void send_to_all (char *messg);

  tc = time (0);
  t_info = localtime (&tc);

  if ((t_info->tm_hour == 8) && (t_info->tm_wday > 0) &&
    (t_info->tm_wday < 6))
    if (t_info->tm_min > 50) {
      log ("Leaving the scene for the serious folks.");
      send_to_all ("Closing down. Thank you for flying DikuMUD.\n\r");
      shutdown_server = 1;
    } else if (t_info->tm_min > 40)
      send_to_all ("ATTENTION: DikuMUD will shut down in 10 minutes.\n\r");
    else if (t_info->tm_min > 30)
      send_to_all ("Warning: The game will close in 20 minutes.\n\r");
}


void check_reboot (void)
{
  time_t tc;
  struct tm *t_info;
  char dummy;
  FILE *boot;

#ifdef __FreeBSD__
  extern int shutdown_server, greboot;
#else
  extern int shutdown_server, reboot;
#endif

  tc = time (0);
  t_info = localtime (&tc);

  if ((t_info->tm_hour + 1) == REBOOT_AT && t_info->tm_min > 30)
    if (boot = fopen ("./reboot", "rb")) {
      if (t_info->tm_min > 50) {
        log ("Reboot exists.");
        fread (&dummy, sizeof (dummy), 1, boot);
        if (!feof (boot)) {     /* the file is nonepty */
          log ("Reboot is nonempty.");

          /* the script can't handle the signals */
          block_signals();

          if (system ("./reboot")) {
            log ("Reboot script terminated abnormally");
            send_to_all ("The reboot was cancelled.\n\r");
            system ("mv ./reboot reboot.FAILED");
            fclose (boot);
            restore_signals();
            return;
          } else
            system ("mv ./reboot reboot.SUCCEEDED");
          restore_signals();
        }

        send_to_all ("Automatic reboot. Come back in a little while.\n\r");
#ifdef __FreeBSD__
        shutdown_server = greboot = 1;
#else
        shutdown_server = reboot = 1;
#endif
      } else if (t_info->tm_min > 40)
        send_to_all ("ATTENTION: DikuMUD will reboot in 10 minutes.\n\r");
      else if (t_info->tm_min > 30)
        send_to_all
          ("Warning: The game will close and reboot in 20 minutes.\n\r");

      fclose (boot);
    }
}


#define GR
#define NEW
#ifdef GR




int workhours (void)
{
  time_t tc;
  struct tm *t_info;

  tc = time (0);
  t_info = localtime (&tc);

  return ((t_info->tm_wday > 0) && (t_info->tm_wday < 6)
    && (t_info->tm_hour >= 9)
    && (t_info->tm_hour < 17));
}






/*
* This procedure is *heavily* system dependent. If your system is not set up
* properly for this particular way of reading the system load (It's weird all
* right - but I couldn't think of anything better), change it, or don't use -l.
* It shouldn't be necessary to use -l anyhow. It's oppressive and unchristian
* to harness man's desire to play. Who needs a friggin' degree, anyhow?
*/

int load (void)
{
  struct syslinfo {
    char sl_date[12];           /* "Tue Sep 16\0" */
    char sl_time[8];            /* "11:10\0" */
    char sl_load1[6];           /* "12.0\0" */
    char sl_load2[10];          /* "+2.3 14u\0" */
  } info;
  FILE *fl;
  int ld, i, sum;
  static int previous[5];
  static int p_point = -1;
  extern int slow_death;

  if (!(fl = fopen ("/tmp/.sysline", "rb"))) {
    perror ("sysline. (dying)");
    slow_death = 1;
    return (-1);
  }
  if (!fread (&info, sizeof (info), 1, fl)) {
    perror ("fread sysline (dying)");
    slow_death = 1;
    return (-1);
  }
  fclose (fl);

  if (p_point < 0) {
    previous[0] = atoi (info.sl_load1);
    for (i = 1; i < 5; i++)
      previous[i] = previous[0];
    p_point = 1;
    return (previous[0]);
  } else {
    /* put new figure in table */
    previous[p_point] = atoi (info.sl_load1);
    if (++p_point > 4)
      p_point = 0;

    for (i = 0, sum = 0; i < 5; i++)
      sum += previous[i];
    return ((int) sum / 5);
  }
}




char *nogames (void)
{
  static char text[200];
  FILE *fl;

  if (fl = fopen ("lib/nogames", "rb")) {
    log ("/usr/games/nogames exists");
    FGETS (text, 200, fl);
    return (text);
    fclose (fl);
  }
  return (0);
}


#ifdef OLD_COMA

void coma (void)
{
  extern struct descriptor_data *descriptor_list;
  extern int tics;

  void close_socket (struct descriptor_data *d);

  log ("Entering comatose state");

  while (descriptor_list)
    close_socket (descriptor_list);

  do {
#if defined WIN32
    Sleep (300000);
#else
    sleep (300);
#endif
    tics = 1;
    if (workhours ()) {
      log ("Working hours collision during coma. Exit.");
      WIN32CLEANUP
      exit (0);
    }
  }
  while (load () >= 6);

  log ("Leaving coma");
}

#endif



/* emulate the game regulator */
void gr (SOCKET s)
{
  char *txt = 0, buf[1024];
  int ld = 0;
  static char *warnings[3] = {
    "If things don't look better within 3 minutes, the game will pause.\n\r",
    "The game will close temporarily 2 minutes from now.\n\r",
    "WARNING: The game will close in 1 minute.\n\r"
  };
  static int wnr = 0;

  extern int slow_death, shutdown_server;

  void send_to_all (char *messg);

  void coma (int s);

  if (((ld = load ()) >= 6) || (txt = nogames ()) || slow_death) {
    if (ld >= 6) {
      sprintf (buf, "The system load is greater than 6.0 (%d)\n\r", ld);
      send_to_all (buf);
    } else if (slow_death)
      send_to_all ("The game is dying.\n\r");
    else {
      strcpy (buf,
        "Game playing is no longer permitted on this machine:\n\r");
      strcat (buf, txt);
      strcat (buf, "\n\r");
      send_to_all (buf);
    }

    if (wnr < 3)
      send_to_all (warnings[wnr++]);
    else if (ld >= 6) {
      coma (s);
      wnr = 0;
    } else
      shutdown_server = 1;
  } else if (workhours ())
    shutdown_server = 1;        /* this shouldn't happen */
  else if (wnr) {
    send_to_all ("Things look brighter now - you can continue playing.\n\r");
    wnr = 0;
  }
}



#endif