text: /**************************************************************************
  *  File: dg_olc.c                                     Part of LuminariMUD *
  *  Usage: This source file is used in extending Oasis OLC for trigedit.   *
  *                                                                         *
  *  $Author: Chris Jacobsen/Mark A. Heilpern/egreen/Welcor $               *
  *  $Date: 2004/10/11 12:07:00$                                            *
  *  $Revision: 1.0.14 $                                                    *
  **************************************************************************/
  
  #include "conf.h"
  #include "sysdep.h"
  #include "structs.h"
  #include "utils.h"
  #include "comm.h"
  #include "db.h"
  #include "genolc.h"
  #include "interpreter.h"
  #include "oasis.h"
  #include "dg_olc.h"
  #include "dg_event.h"
  #include "genzon.h"    /* for real_zone_by_thing */
  #include "constants.h" /* for the *trig_types */
  #include "modify.h"    /* for smash_tilde */
  
  /* local functions */
  static void trigedit_disp_menu(struct descriptor_data *d);
  static void trigedit_disp_types(struct descriptor_data *d);
  static void trigedit_create_index(int znum, char *type);
  static void trigedit_setup_new(struct descriptor_data *d);
  
  /* Trigedit */
  ACMD(do_oasis_trigedit)
  {
    int number, real_num;
    struct descriptor_data *d;
  
    /* No building as a mob or while being forced. */
    if (IS_NPC(ch) || !ch->desc || STATE(ch->desc) != CON_PLAYING)
      return;
  
    /* Parse any arguments. */
    skip_spaces(&argument);
    if (!*argument || !isdigit(*argument))
    {
      send_to_char(ch, "Specify a trigger VNUM to edit.\r\n");
      return;
    }
  
    number = atoi(argument);
  
    /* Check that it isn't already being edited. */
    for (d = descriptor_list; d; d = d->next)
    {
      if (STATE(d) == CON_TRIGEDIT)
      {
        if (d->olc && OLC_NUM(d) == number)
        {
          send_to_char(ch, "That trigger is currently being edited by %s.\r\n",
                       GET_NAME(d->character));
          return;
        }
      }
    }
    d = ch->desc;
    /* Give descriptor an OLC structure. */
    if (d->olc)
    {
      mudlog(BRF, LVL_IMMORT, TRUE,
             "SYSERR: do_oasis_trigedit: Player already had olc structure.");
      free(d->olc);
    }
    CREATE(d->olc, struct oasis_olc_data, 1);
  
    /* Find the zone. */
    if ((OLC_ZNUM(d) = real_zone_by_thing(number)) == NOWHERE)
    {
      send_to_char(ch, "Sorry, there is no zone for that number!\r\n");
      free(d->olc);
      d->olc = NULL;
      return;
    }
  
    /* Everyone but IMPLs can only edit zones they have been assigned.*/
    if (!can_edit_zone(ch, OLC_ZNUM(d)))
    {
      send_cannot_edit(ch, zone_table[OLC_ZNUM(d)].number);
      /* Free the OLC structure. */
      free(d->olc);
      d->olc = NULL;
      return;
    }
    OLC_NUM(d) = number;
  
    /* If this is a new trigger, setup a new one, otherwise, setup the a copy of 
     * the existing trigger. */
    if ((real_num = real_trigger(number)) == NOTHING)
      trigedit_setup_new(d);
    else
      trigedit_setup_existing(d, real_num);
  
    trigedit_disp_menu(d);
    STATE(d) = CON_TRIGEDIT;
  
    act("$n starts using OLC.", TRUE, d->character, 0, 0, TO_ROOM);
    SET_BIT_AR(PLR_FLAGS(ch), PLR_WRITING);
  
    mudlog(CMP, LVL_IMMORT, TRUE, "OLC: %s starts editing zone %d [trigger](allowed zone %d)",
           GET_NAME(ch), zone_table[OLC_ZNUM(d)].number, GET_OLC_ZONE(ch));
  }
  
  /* Called when a mob or object is being saved to disk, so its script can be 
   * saved. */
  void script_save_to_disk(FILE *fp, void *item, int type)
  {
    struct trig_proto_list *t;
  
    if (type == MOB_TRIGGER)
      t = ((struct char_data *)item)->proto_script;
    else if (type == OBJ_TRIGGER)
      t = ((struct obj_data *)item)->proto_script;
    else if (type == WLD_TRIGGER)
      t = ((struct room_data *)item)->proto_script;
    else
    {
      log("SYSERR: Invalid type passed to script_save_to_disk()");
      return;
    }
  
    while (t)
    {
      fprintf(fp, "T %d\n", t->vnum);
      t = t->next;
    }
  }
  
  static void trigedit_setup_new(struct descriptor_data *d)
  {
    struct trig_data *trig;
  
    /* Allocate a scratch trigger structure. */
    CREATE(trig, struct trig_data, 1);
  
    trig->nr = NOWHERE;
  
    /* Set up some defaults. */
    trig->name = strdup("new trigger");
    trig->trigger_type = MTRIG_GREET;
  
    /* cmdlist will be a large char string until the trigger is saved */
    CREATE(OLC_STORAGE(d), char, MAX_CMD_LENGTH);
    strncpy(OLC_STORAGE(d),
            "%echo% This trigger commandlist is not complete!\r\n", MAX_CMD_LENGTH - 1);
    trig->narg = 100;
  
    OLC_TRIG(d) = trig;
    OLC_VAL(d) = 0; /* Has changed flag. (It hasn't so far, we just made it.) */
  }
  
  void trigedit_setup_existing(struct descriptor_data *d, int rtrg_num)
  {
    struct trig_data *trig;
    struct cmdlist_element *c;
    /* Allocate a scratch trigger structure. */
    CREATE(trig, struct trig_data, 1);
  
    trig_data_copy(trig, trig_index[rtrg_num]->proto);
  
    /* convert cmdlist to a char string */
    c = trig->cmdlist;
    CREATE(OLC_STORAGE(d), char, MAX_CMD_LENGTH);
    strcpy(OLC_STORAGE(d), "");
  
    while (c)
    {
      strcat(OLC_STORAGE(d), c->cmd);
      strcat(OLC_STORAGE(d), "\r\n");
      c = c->next;
    }
    /* Now trig->cmdlist is something to pass to the text editor it will be 
     * converted back to a real cmdlist_element list later. */
  
    OLC_TRIG(d) = trig;
    OLC_VAL(d) = 0; /* Has changed flag. (It hasn't so far, we just made it.) */
  }
  
  static void trigedit_disp_menu(struct descriptor_data *d)
  {
    struct trig_data *trig = OLC_TRIG(d);
    char *attach_type;
    char trgtypes[MEDIUM_STRING];
  
    get_char_colors(d->character);
  
    if (trig->attach_type == OBJ_TRIGGER)
    {
      attach_type = "Objects";
      sprintbit(GET_TRIG_TYPE(trig), otrig_types, trgtypes, sizeof(trgtypes));
    }
    else if (trig->attach_type == WLD_TRIGGER)
    {
      attach_type = "Rooms";
      sprintbit(GET_TRIG_TYPE(trig), wtrig_types, trgtypes, sizeof(trgtypes));
    }
    else
    {
      attach_type = "Mobiles";
      sprintbit(GET_TRIG_TYPE(trig), trig_types, trgtypes, sizeof(trgtypes));
    }
  
    clear_screen(d);
  
    write_to_output(d,
                    "Trigger Editor [%s%d%s]\r\n\r\n"
                    "%s1)%s Name         : %s%s\r\n"
                    "%s2)%s Intended for : %s%s\r\n"
                    "%s3)%s Trigger types: %s%s\r\n"
                    "%s4)%s Numeric Arg  : %s%d\r\n"
                    "%s5)%s Arguments    : %s%s\r\n"
                    "%s6)%s Commands:\r\n%s%s\r\n"
                    "%sW%s) Copy Trigger\r\n"
                    "%sQ)%s Quit\r\n"
                    "Enter Choice :",
  
                    grn, OLC_NUM(d), nrm,                              /* vnum on the title line */
                    grn, nrm, yel, GET_TRIG_NAME(trig),                /* name                   */
                    grn, nrm, yel, attach_type,                        /* attach type            */
                    grn, nrm, yel, trgtypes,                           /* greet/drop/etc         */
                    grn, nrm, yel, trig->narg,                         /* numeric arg            */
                    grn, nrm, yel, trig->arglist ? trig->arglist : "", /* strict arg             */
                    grn, nrm, cyn, OLC_STORAGE(d),                     /* the command list       */
                    grn, nrm, grn, nrm);                               /* quit colors            */
  
    OLC_MODE(d) = TRIGEDIT_MAIN_MENU;
  }
  
  static void trigedit_disp_types(struct descriptor_data *d)
  {
    int i, columns = 0;
    const char **types;
    char bitbuf[MAX_STRING_LENGTH];
  
    switch (OLC_TRIG(d)->attach_type)
    {
    case WLD_TRIGGER:
      types = wtrig_types;
      break;
    case OBJ_TRIGGER:
      types = otrig_types;
      break;
    case MOB_TRIGGER:
    default:
      types = trig_types;
      break;
    }
  
    get_char_colors(d->character);
    clear_screen(d);
  
    for (i = 0; i < NUM_TRIG_TYPE_FLAGS; i++)
    {
      write_to_output(d, "%s%2d%s) %-20.20s  %s", grn, i + 1, nrm, types[i],
                      !(++columns % 2) ? "\r\n" : "");
    }
    sprintbit(GET_TRIG_TYPE(OLC_TRIG(d)), types, bitbuf, sizeof(bitbuf));
    write_to_output(d, "\r\nCurrent types : %s%s%s\r\nEnter type (0 to quit) : ",
                    cyn, bitbuf, nrm);
  }
  
  void trigedit_parse(struct descriptor_data *d, char *arg)
  {
    int i = 0;
  
    switch (OLC_MODE(d))
    {
    case TRIGEDIT_MAIN_MENU:
      switch (tolower(*arg))
      {
      case 'q':
        if (OLC_VAL(d))
        { /* Anything been changed? */
          if (!GET_TRIG_TYPE(OLC_TRIG(d)))
          {
            write_to_output(d, "Invalid Trigger Type! Answer a to abort quit!\r\n");
          }
          write_to_output(d, "Do you wish to save your changes? : ");
          OLC_MODE(d) = TRIGEDIT_CONFIRM_SAVESTRING;
        }
        else
          cleanup_olc(d, CLEANUP_ALL);
        return;
      case '1':
        OLC_MODE(d) = TRIGEDIT_NAME;
        write_to_output(d, "Name: ");
        break;
      case '2':
        OLC_MODE(d) = TRIGEDIT_INTENDED;
        write_to_output(d, "0: Mobiles, 1: Objects, 2: Rooms: ");
        break;
      case '3':
        OLC_MODE(d) = TRIGEDIT_TYPES;
        trigedit_disp_types(d);
        break;
      case '4':
        OLC_MODE(d) = TRIGEDIT_NARG;
        write_to_output(d, "Numeric argument: ");
        break;
      case '5':
        OLC_MODE(d) = TRIGEDIT_ARGUMENT;
        write_to_output(d, "Argument: ");
        break;
      case '6':
        OLC_MODE(d) = TRIGEDIT_COMMANDS;
        write_to_output(d, "Enter trigger commands: (/s saves /h for help)\r\n\r\n");
        d->backstr = NULL;
        if (OLC_STORAGE(d))
        {
          write_to_output(d, "%s", OLC_STORAGE(d));
          d->backstr = strdup(OLC_STORAGE(d));
        }
        d->str = &OLC_STORAGE(d);
        d->max_str = MAX_CMD_LENGTH;
        d->mail_to = 0;
        OLC_VAL(d) = 1;
  
        break;
      case 'w':
      case 'W':
        write_to_output(d, "Copy what trigger? ");
        OLC_MODE(d) = TRIGEDIT_COPY;
        break;
      default:
        trigedit_disp_menu(d);
        return;
      }
      return;
  
    case TRIGEDIT_CONFIRM_SAVESTRING:
      switch (tolower(*arg))
      {
      case 'y':
        trigedit_save(d);
        mudlog(CMP, MAX(LVL_BUILDER, GET_INVIS_LEV(d->character)), TRUE,
               "OLC: %s edits trigger %d", GET_NAME(d->character),
               OLC_NUM(d));
        /* fall through */
      case 'n':
        cleanup_olc(d, CLEANUP_ALL);
        return;
      case 'a': /* abort quitting */
        break;
      default:
        write_to_output(d, "Invalid choice!\r\n");
        write_to_output(d, "Do you wish to save your changes? : ");
        return;
      }
      break;
  
    case TRIGEDIT_NAME:
      smash_tilde(arg);
      if (OLC_TRIG(d)->name)
        free(OLC_TRIG(d)->name);
      OLC_TRIG(d)->name = strdup((arg && *arg) ? arg : "undefined");
      OLC_VAL(d)
      ++;
      break;
  
    case TRIGEDIT_INTENDED:
      if ((atoi(arg) >= MOB_TRIGGER) || (atoi(arg) <= WLD_TRIGGER))
        OLC_TRIG(d)->attach_type = atoi(arg);
      OLC_VAL(d)
      ++;
      break;
  
    case TRIGEDIT_NARG:
      OLC_TRIG(d)->narg = LIMIT(atoi(arg), 0, 100);
      OLC_VAL(d)
      ++;
      break;
  
    case TRIGEDIT_ARGUMENT:
      smash_tilde(arg);
      OLC_TRIG(d)->arglist = (*arg ? strdup(arg) : NULL);
      OLC_VAL(d)
      ++;
      break;
  
    case TRIGEDIT_TYPES:
      if ((i = atoi(arg)) == 0)
        break;
      else if (!((i < 0) || (i > NUM_TRIG_TYPE_FLAGS)))
        TOGGLE_BIT((GET_TRIG_TYPE(OLC_TRIG(d))), 1 << (i - 1));
      OLC_VAL(d)
      ++;
      trigedit_disp_types(d);
      return;
  
    case TRIGEDIT_COPY:
      if ((i = real_trigger(atoi(arg))) != NOWHERE)
      {
        trigedit_setup_existing(d, i);
      }
      else
        write_to_output(d, "That trigger does not exist.\r\n");
      break;
  
    case TRIGEDIT_COMMANDS:
      break;
    }
  
    OLC_MODE(d) = TRIGEDIT_MAIN_MENU;
    trigedit_disp_menu(d);
  }
  
  /* save the zone's triggers to internal memory and to disk */
  void trigedit_save(struct descriptor_data *d)
  {
    int i;
    trig_rnum rnum;
    int found = 0;
    char *s;
    trig_data *proto;
    trig_data *trig = OLC_TRIG(d);
    trig_data *live_trig;
    struct cmdlist_element *cmd, *next_cmd;
    struct index_data **new_index;
    struct descriptor_data *dsc;
    FILE *trig_file;
    int zone, top;
    char buf[MAX_CMD_LENGTH];
    char bitBuf[MAX_INPUT_LENGTH];
    char fname[MAX_INPUT_LENGTH];
  
    if ((rnum = real_trigger(OLC_NUM(d))) != NOTHING)
    {
      proto = trig_index[rnum]->proto;
      for (cmd = proto->cmdlist; cmd; cmd = next_cmd)
      {
        next_cmd = cmd->next;
        if (cmd->cmd)
          free(cmd->cmd);
        free(cmd);
      }
  
      free(proto->arglist);
      free(proto->name);
  
      /* Recompile the command list from the new script */
      s = OLC_STORAGE(d);
  
      CREATE(trig->cmdlist, struct cmdlist_element, 1);
      if (s)
      {
        char *t = strtok(s, "\n\r"); /* strtok returns NULL if s is "\r\n" */
        if (t)
          trig->cmdlist->cmd = strdup(t);
        else
          trig->cmdlist->cmd = strdup("* No script");
  
        cmd = trig->cmdlist;
        while ((s = strtok(NULL, "\n\r")))
        {
          CREATE(cmd->next, struct cmdlist_element, 1);
          cmd = cmd->next;
          cmd->cmd = strdup(s);
        }
      }
      else
        trig->cmdlist->cmd = strdup("* No Script");
  
      /* make the prorotype look like what we have */
      trig_data_copy(proto, trig);
  
      /* go through the mud and replace existing triggers         */
      live_trig = trigger_list;
      while (live_trig)
      {
        if (GET_TRIG_RNUM(live_trig) == rnum)
        {
          if (live_trig->arglist)
          {
            free(live_trig->arglist);
            live_trig->arglist = NULL;
          }
          if (live_trig->name)
          {
            free(live_trig->name);
            live_trig->name = NULL;
          }
  
          if (proto->arglist)
            live_trig->arglist = strdup(proto->arglist);
          if (proto->name)
            live_trig->name = strdup(proto->name);
  
          /* anything could have happened so we don't want to keep these */
          if (GET_TRIG_WAIT(live_trig))
          {
            event_cancel(GET_TRIG_WAIT(live_trig));
            GET_TRIG_WAIT(live_trig) = NULL;
          }
          if (live_trig->var_list)
          {
            free_varlist(live_trig->var_list);
            live_trig->var_list = NULL;
          }
  
          live_trig->cmdlist = proto->cmdlist;
          live_trig->curr_state = live_trig->cmdlist;
          live_trig->trigger_type = proto->trigger_type;
          live_trig->attach_type = proto->attach_type;
          live_trig->narg = proto->narg;
          live_trig->data_type = proto->data_type;
          live_trig->depth = 0;
        }
  
        live_trig = live_trig->next_in_world;
      }
    }
    else
    {
      /* this is a new trigger */
      CREATE(new_index, struct index_data *, top_of_trigt + 2);
  
      /* Recompile the command list from the new script */
  
      s = OLC_STORAGE(d);
  
      CREATE(trig->cmdlist, struct cmdlist_element, 1);
      if (s)
      {
        /* strtok returns NULL if s is "\r\n" */
        char *t = strtok(s, "\n\r");
        trig->cmdlist->cmd = strdup(t ? t : "* No script");
        cmd = trig->cmdlist;
  
        while ((s = strtok(NULL, "\n\r")))
        {
          CREATE(cmd->next, struct cmdlist_element, 1);
          cmd = cmd->next;
          cmd->cmd = strdup(s);
        }
      }
      else
        trig->cmdlist->cmd = strdup("* No Script");
  
      for (i = 0; i < top_of_trigt; i++)
      {
        if (!found)
        {
          if (trig_index[i]->vnum > OLC_NUM(d))
          {
            found = TRUE;
            rnum = i;
  
            CREATE(new_index[rnum], struct index_data, 1);
            GET_TRIG_RNUM(OLC_TRIG(d)) = rnum;
            new_index[rnum]->vnum = OLC_NUM(d);
            new_index[rnum]->number = 0;
            new_index[rnum]->func = NULL;
            CREATE(proto, struct trig_data, 1);
            new_index[rnum]->proto = proto;
            trig_data_copy(proto, trig);
  
            new_index[rnum + 1] = trig_index[rnum];
  
            proto = trig_index[rnum]->proto;
            proto->nr = rnum + 1;
          }
          else
          {
            new_index[i] = trig_index[i];
          }
        }
        else
        {
          new_index[i + 1] = trig_index[i];
          proto = trig_index[i]->proto;
          proto->nr = i + 1;
        }
      }
  
      if (!found)
      {
        rnum = i;
        CREATE(new_index[rnum], struct index_data, 1);
        GET_TRIG_RNUM(OLC_TRIG(d)) = rnum;
        new_index[rnum]->vnum = OLC_NUM(d);
        new_index[rnum]->number = 0;
        new_index[rnum]->func = NULL;
  
        CREATE(proto, struct trig_data, 1);
        new_index[rnum]->proto = proto;
        trig_data_copy(proto, trig);
      }
  
      free(trig_index);
  
      trig_index = new_index;
      top_of_trigt++;
  
      /* HERE IT HAS TO GO THROUGH AND FIX ALL SCRIPTS/TRIGS OF HIGHER RNUM */
      for (live_trig = trigger_list; live_trig; live_trig = live_trig->next_in_world)
        GET_TRIG_RNUM(live_trig) += (GET_TRIG_RNUM(live_trig) != NOTHING && GET_TRIG_RNUM(live_trig) > rnum);
  
      /* Update other trigs being edited. */
      for (dsc = descriptor_list; dsc; dsc = dsc->next)
        if (STATE(dsc) == CON_TRIGEDIT)
          if (GET_TRIG_RNUM(OLC_TRIG(dsc)) >= rnum)
            GET_TRIG_RNUM(OLC_TRIG(dsc))
            ++;
    }
  
    /* now write the trigger out to disk, along with the rest of the triggers for
     * this zone. We write this to disk NOW instead of letting the builder have 
     * control because if we lose this after having assigned a new trigger to an 
     * item, we will get SYSERR's upton reboot that could make things hard to 
     * debug. */
    zone = zone_table[OLC_ZNUM(d)].number;
    top = zone_table[OLC_ZNUM(d)].top;
  
  #ifdef CIRCLE_MAC
    snprintf(fname, sizeof(fname), "%s:%i.new", TRG_PREFIX, zone);
  #else
    snprintf(fname, sizeof(fname), "%s/%i.new", TRG_PREFIX, zone);
  #endif
  
    if (!(trig_file = fopen(fname, "w")))
    {
      mudlog(BRF, MAX(LVL_STAFF, GET_INVIS_LEV(d->character)), TRUE,
             "SYSERR: OLC: Can't open trig file \"%s\"", fname);
      return;
    }
  
    for (i = zone_table[OLC_ZNUM(d)].bot; i <= top; i++)
    {
      if ((rnum = real_trigger(i)) != NOTHING)
      {
        trig = trig_index[rnum]->proto;
  
        if (fprintf(trig_file, "#%d\n", i) < 0)
        {
          mudlog(BRF, MAX(LVL_STAFF, GET_INVIS_LEV(d->character)), TRUE,
                 "SYSERR: OLC: Can't write trig file!");
          fclose(trig_file);
          return;
        }
        sprintascii(bitBuf, GET_TRIG_TYPE(trig));
        fprintf(trig_file, "%s%c\n"
                           "%d %s %d\n"
                           "%s%c\n",
                (GET_TRIG_NAME(trig)) ? (GET_TRIG_NAME(trig)) : "unknown trigger", STRING_TERMINATOR,
                trig->attach_type,
                *bitBuf ? bitBuf : "0", GET_TRIG_NARG(trig),
                GET_TRIG_ARG(trig) ? GET_TRIG_ARG(trig) : "", STRING_TERMINATOR);
  
        /* Build the text for the script */
        strcpy(buf, ""); /* strcpy OK for MAX_CMD_LENGTH > 0*/
        for (cmd = trig->cmdlist; cmd; cmd = cmd->next)
        {
          strcat(buf, cmd->cmd);
          strcat(buf, "\n");
        }
  
        if (!buf[0])
          strcpy(buf, "* Empty script");
  
        fprintf(trig_file, "%s%c\n", buf, STRING_TERMINATOR);
        *buf = '\0';
      }
    }
  
    fprintf(trig_file, "$%c\n", STRING_TERMINATOR);
    fclose(trig_file);
  
  #ifdef CIRCLE_MAC
    snprintf(buf, sizeof(buf), "%s:%d.trg", TRG_PREFIX, zone);
  #else
    snprintf(buf, sizeof(buf), "%s/%d.trg", TRG_PREFIX, zone);
  #endif
  
    remove(buf);
    rename(fname, buf);
  
    write_to_output(d, "Trigger saved to disk.\r\n");
    trigedit_create_index(zone, "trg");
  }
  
  static void trigedit_create_index(int znum, char *type)
  {
    FILE *newfile, *oldfile;
    char new_name[128], old_name[128], *prefix;
    char buf[MAX_STRING_LENGTH], buf1[MAX_STRING_LENGTH];
    int num, found = FALSE;
  
    prefix = TRG_PREFIX;
  
    snprintf(old_name, sizeof(old_name), "%s/index", prefix);
    snprintf(new_name, sizeof(new_name), "%s/newindex", prefix);
  
    if (!(oldfile = fopen(old_name, "r")))
    {
      mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: DG_OLC: Failed to open %s", old_name);
      return;
    }
    else if (!(newfile = fopen(new_name, "w")))
    {
      mudlog(BRF, LVL_IMPL, TRUE, "SYSERR: DG_OLC: Failed to open %s", new_name);
      return;
    }
  
    /* Index contents must be in order: search through the old file for the right
     * place, insert the new file, then copy the rest over. */
    snprintf(buf1, sizeof(buf1), "%d.%s", znum, type);
    while (get_line(oldfile, buf))
    {
      if (*buf == '$')
      {
        fprintf(newfile, "%s\n$\n", (!found ? buf1 : ""));
        break;
      }
      else if (!found)
      {
        sscanf(buf, "%d", &num);
        if (num == znum)
          found = TRUE;
        else if (num > znum)
        {
          found = TRUE;
          fprintf(newfile, "%s\n", buf1);
        }
      }
      fprintf(newfile, "%s\n", buf);
    }
  
    fclose(newfile);
    fclose(oldfile);
  
    /* Out with the old, in with the new. */
    remove(old_name);
    rename(new_name, old_name);
  }
  
  void dg_olc_script_copy(struct descriptor_data *d)
  {
    struct trig_proto_list *origscript, *editscript;
  
    if (OLC_ITEM_TYPE(d) == MOB_TRIGGER)
      origscript = OLC_MOB(d)->proto_script;
    else if (OLC_ITEM_TYPE(d) == OBJ_TRIGGER)
      origscript = OLC_OBJ(d)->proto_script;
    else
      origscript = OLC_ROOM(d)->proto_script;
  
    if (origscript)
    {
      CREATE(editscript, struct trig_proto_list, 1);
      OLC_SCRIPT(d) = editscript;
  
      while (origscript)
      {
        editscript->vnum = origscript->vnum;
        origscript = origscript->next;
        if (origscript)
          CREATE(editscript->next, struct trig_proto_list, 1);
        editscript = editscript->next;
      }
    }
    else
      OLC_SCRIPT(d) = NULL;
  }
  
  void dg_script_menu(struct descriptor_data *d)
  {
    struct trig_proto_list *editscript;
    int i = 0;
  
    /* make sure our input parser gets used */
    OLC_MODE(d) = OLC_SCRIPT_EDIT;
    OLC_SCRIPT_EDIT_MODE(d) = SCRIPT_MAIN_MENU;
  
    clear_screen(d);
    write_to_output(d, "     Triggers Attached:\r\n");
  
    editscript = OLC_SCRIPT(d);
  
    while (editscript)
    {
      write_to_output(d, "     %2d) [%s%d%s] %s%s%s", ++i, cyn,
                      editscript->vnum, nrm, cyn,
                      trig_index[real_trigger(editscript->vnum)]->proto->name, nrm);
      if (trig_index[real_trigger(editscript->vnum)]->proto->attach_type != OLC_ITEM_TYPE(d))
        write_to_output(d, "   %s** Mis-matched Trigger Type **%s\r\n", grn, nrm);
      else
        write_to_output(d, "\r\n");
  
      editscript = editscript->next;
    }
    if (i == 0)
      write_to_output(d, "     <none>\r\n");
  
    write_to_output(d, "\r\n"
                       " %sN%s)  Attach trigger\r\n"
                       " %sX%s)  Detach trigger\r\n"
                       " %sQ%s)  Quit\r\n\r\n"
                       "     Enter choice :",
                    grn, nrm, grn, nrm, grn, nrm);
  }
  
  int dg_script_edit_parse(struct descriptor_data *d, char *arg)
  {
    struct trig_proto_list *trig, *currtrig;
    int count, pos, vnum;
  
    switch (OLC_SCRIPT_EDIT_MODE(d))
    {
    case SCRIPT_MAIN_MENU:
      switch (tolower(*arg))
      {
      case 'q':
        /* This was buggy. First we created a copy of a thing, but maintained
  	   * pointers to scripts, then if we altered the scripts, we freed the 
  	   * pointers and added new ones to the OLC_THING. If we then choose NOT
  	   * to save the changes, the pointers in the original pointed to 
  	   * garbage. If we saved changes the pointers were updated correctly.
  	   * Solution: Here we just point the working copies to the new 
  	   * proto_scripts. We only update the original when choosing to save 
  	   * internally, then free the unused memory there. -Welcor
  	   * Thanks to Jeremy Stanley and Torgny Bjers for the bug report.
  	   * After updating to OasisOLC 2.0.3 I discovered some malfunctions
  	   * in this code, so I restructured it a bit. Now things work like 
  	   * this: OLC_SCRIPT(d) is assigned a copy of the edited things' 
  	   * proto_script. OLC_OBJ(d), etc.. are initalized with proto_script =
  	   * NULL; On save, the saved copy is updated with OLC_SCRIPT(d) as new
  	   * proto_script (freeing the old one). On quit/nosave, OLC_SCRIPT is 
  	   * free()'d, and the prototype not touched. */
        return 0;
      case 'n':
        write_to_output(d, "\r\nPlease enter position, vnum   (ex: 1, 200):");
        OLC_SCRIPT_EDIT_MODE(d) = SCRIPT_NEW_TRIGGER;
        break;
      case 'x':
        write_to_output(d, "     Which entry should be deleted?  0 to abort :");
        OLC_SCRIPT_EDIT_MODE(d) = SCRIPT_DEL_TRIGGER;
        break;
      default:
        dg_script_menu(d);
        break;
      }
      return 1;
  
    case SCRIPT_NEW_TRIGGER:
      vnum = -1;
      count = sscanf(arg, "%d, %d", &pos, &vnum);
      if (count == 1)
      {
        vnum = pos;
        pos = 999;
      }
  
      if (pos <= 0)
        break; /* this aborts a new trigger entry */
  
      if (vnum == 0)
        break; /* this aborts a new trigger entry */
  
      if (real_trigger(vnum) == NOTHING)
      {
        write_to_output(d, "Invalid Trigger VNUM!\r\n"
                           "Please enter position, vnum   (ex: 1, 200):");
        return 1;
      }
  
      /* add the new info in position */
      currtrig = OLC_SCRIPT(d);
      CREATE(trig, struct trig_proto_list, 1);
      trig->vnum = vnum;
  
      if (pos == 1 || !currtrig)
      {
        trig->next = OLC_SCRIPT(d);
        OLC_SCRIPT(d) = trig;
      }
      else
      {
        while (currtrig->next && --pos)
        {
          currtrig = currtrig->next;
        }
        trig->next = currtrig->next;
        currtrig->next = trig;
      }
      OLC_VAL(d)
      ++;
      break;
  
    case SCRIPT_DEL_TRIGGER:
      pos = atoi(arg);
      if (pos <= 0)
        break;
  
      if (pos == 1 && OLC_SCRIPT(d))
      {
        OLC_VAL(d)
        ++;
        currtrig = OLC_SCRIPT(d);
        OLC_SCRIPT(d) = currtrig->next;
        free(currtrig);
        break;
      }
  
      pos--;
      currtrig = OLC_SCRIPT(d);
      while (--pos && currtrig)
        currtrig = currtrig->next;
      /* now curtrig points one before the target */
      if (currtrig && currtrig->next)
      {
        OLC_VAL(d)
        ++;
        trig = currtrig->next;
        currtrig->next = trig->next;
        free(trig);
      }
      break;
    }
  
    dg_script_menu(d);
    return 1;
  }
  
  void trigedit_string_cleanup(struct descriptor_data *d, int terminator)
  {
    switch (OLC_MODE(d))
    {
    case TRIGEDIT_COMMANDS:
      trigedit_disp_menu(d);
      break;
    }
  }
  
  int format_script(struct descriptor_data *d)
  {
    char nsc[MAX_CMD_LENGTH], *t, line[READ_SIZE];
    char *sc;
    size_t len = 0, nlen = 0, llen = 0;
    int indent = 0, indent_next = FALSE, found_case = FALSE, i, line_num = 0;
  
    if (!d->str || !*d->str)
      return FALSE;
  
    sc = strdup(*d->str); /* we work on a copy, because of strtok() */
    t = strtok(sc, "\n\r");
    *nsc = '\0';
  
    while (t)
    {
      line_num++;
      skip_spaces(&t);
      if (!strn_cmp(t, "if ", 3) ||
          !strn_cmp(t, "switch ", 7))
      {
        indent_next = TRUE;
      }
      else if (!strn_cmp(t, "while ", 6))
      {
        found_case = TRUE; /* so you can 'break' a loop without complains */
        indent_next = TRUE;
      }
      else if (!strn_cmp(t, "end", 3) ||
               !strn_cmp(t, "done", 4))
      {
        if (!indent)
        {
          write_to_output(d, "Unmatched 'end' or 'done' (line %d)!\r\n", line_num);
          free(sc);
          return FALSE;
        }
        indent--;
        indent_next = FALSE;
      }
      else if (!strn_cmp(t, "else", 4))
      {
        if (!indent)
        {
          write_to_output(d, "Unmatched 'else' (line %d)!\r\n", line_num);
          free(sc);
          return FALSE;
        }
        indent--;
        indent_next = TRUE;
      }
      else if (!strn_cmp(t, "case", 4) ||
               !strn_cmp(t, "default", 7))
      {
        if (!indent)
        {
          write_to_output(d, "Case/default outside switch (line %d)!\r\n", line_num);
          free(sc);
          return FALSE;
        }
        if (!found_case) /* so we don't indent multiple case statements without a break */
          indent_next = TRUE;
        found_case = TRUE;
      }
      else if (!strn_cmp(t, "break", 5))
      {
        if (!found_case || !indent)
        {
          write_to_output(d, "Break not in case (line %d)!\r\n", line_num);
          free(sc);
          return FALSE;
        }
        found_case = FALSE;
        indent--;
      }
  
      *line = '\0';
      for (nlen = 0, i = 0; i < indent; i++)
      {
        strncat(line, "  ", sizeof(line) - 1);
        nlen += 2;
      }
      llen = snprintf(line + nlen, sizeof(line) - nlen, "%s\r\n", t);
      if (llen < 0 || llen + nlen + len > d->max_str - 1)
      {
        write_to_output(d, "String too long, formatting aborted\r\n");
        free(sc);
        return FALSE;
      }
      len = len + nlen + llen;
      strcat(nsc, line); /* strcat OK, size checked above */
  
      if (indent_next)
      {
        indent++;
        indent_next = FALSE;
      }
      t = strtok(NULL, "\n\r");
    }
  
    if (indent)
      write_to_output(d, "Unmatched if, while or switch ignored.\r\n");
  
    free(*d->str);
    *d->str = strdup(nsc);
    free(sc);
  
    return TRUE;
  }
  