/*
 * Written by Carl Otto III
 * distortions@gmail.com
 * If you use this code, credits must be at shown at login
 */

#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include "merc.h"
#include "recycle.h"
#include "tables.h"
#include "db.h"
#include "olc.h"

const struct color_type colorizer_table[] = {
  /* Do not move this */
  {CTYPE_UNKNOWN, "Unknown", "Unknown", "No type set."},

  /* Order here vs doa_headers.h is irrelevant. Use lookup_ctype */
  /* The lookup is done because changing the values of the CTYPES would break the colorizer data */
  {CTYPE_LEGACY, "Legacy", "{RL{De{Dg{ra{Rcy{x", "Old colors converted from DoC. (Reserved)"},
  {CTYPE_CHANCE, "Chance", "{RC{Dha{rn{Rc{re{x", "The color is decided by chance, from a user-specified list."},
  {CTYPE_GRADIENT, "Gradient", "{rG{Dr{wa{Wdi{we{Dn{rt{x", "A list of colors spaced across the line."},
  {CTYPE_RWPATTERN, "RWPattern", "{RRW{RP{ra{Dt{Rt{re{Dr{Rn{x", "A repeating pattern, with a random start point, per-word."},
  {CTYPE_RPATTERN, "RPattern", "{RR{RP{ra{Dt{Rt{re{Dr{Rn{x", "A repeating pattern, with a random start point."},
  {CTYPE_WPATTERN, "WPattern", "{RW{RP{ra{Dt{Rt{re{Dr{Rn{x", "A repeating pattern, per-word."},
  {CTYPE_PATTERN, "Pattern", "{RP{ra{Dt{rt{Re{rr{Dn{x", "A repeating pattern."},

  /*
   * Do not remove/edit/move this, end-of-list marker
   */
  {0, NULL, NULL, NULL}
};

bool has_quote ( char *argument )
{
  int i;

  for ( i = 0; argument[i] != '\0' && i < MSL; i++ )
    if ( argument[i] == '\'' || argument[i] == '\"' )
      return TRUE;

  return FALSE;
}

CH_CMD ( do_gcolordb )
{
  char arg1[MIL];

  argument = one_argument ( argument, arg1 );

  if ( !str_cmp ( "load", arg1 ) && IS_IMMORTAL ( ch ) )
  {
    load_gcolorizers (  );
    send_to_char ( "gColorDB loaded...\n\r", ch );
    return;
  }
  else if ( !str_cmp ( "save", arg1 ) && IS_IMMORTAL ( ch ) )
  {
    save_gcolorizers (  );
    send_to_char ( "gColorDB saved...\n\r", ch );
    return;
  }
  else if ( !str_cmp ( "info", arg1 ) )
  {
    COLORIZER_DATA *pcolor;
    char buf[MSL];

    pcolor = get_gcolorizer ( atol ( argument ) );

    if ( pcolor != NULL )
    {
      sprintf ( buf,
        "\n\r{DgUUID   {R: {w%ld\n\r{DName    {R: {w%s\n\r{DType    {R: {w%s\n\r{DCreator {R: {w%s\n\r{DOwner   {R: {w%s\n\r{DCreated {R:{w%s ago{R.\n\r{DModifed {R:{w%s ago{R.\n\r{DString  {R: {w%s\n\r{DSample  {R: {w%s{x\n\r",
        pcolor->gUUID, pcolor->name, colorizer_table[lookup_ctype ( pcolor->type )].name, pcolor->creator, pcolor->owner,
        sec_to_hms ( current_time - pcolor->cre_time ), sec_to_hms ( current_time - pcolor->mod_time ), pcolor->color_string,
        do_colorize ( teststr_long, pcolor, FALSE ) );
      send_to_char ( buf, ch );
    }
    else
    {
      send_to_char ( "Could not find a Global Colorizer with that gUUID.\n\r", ch );
      return;
    }

  }
  else if ( !str_cmp ( "copy", arg1 ) )
  {
    char arg2[MIL];
    char buf[MSL];
    COLORIZER_DATA *scolor;
    COLORIZER_DATA *dcolor;

    argument = one_argument ( argument, arg2 );

    scolor = get_gcolorizer ( atol ( arg2 ) );

    if ( scolor != NULL )
    {
      dcolor = new_colorizer (  );
      dcolor->name = str_dup ( scolor->name );
      dcolor->type = scolor->type;
      dcolor->creator = str_dup ( scolor->creator );
      dcolor->owner = str_dup ( ch->name );
      dcolor->cre_time = scolor->cre_time;
      dcolor->mod_time = scolor->mod_time;
      dcolor->color_string = str_dup ( scolor->color_string );
      dcolor->clan = scolor->clan;

      dcolor->next = ch->pcdata->colorizer;
      ch->pcdata->colorizer = dcolor;

      sprintf ( buf, "Colorizer '%s' (gUUID %ld) copied to your colorizer list.\n\r", dcolor->name, scolor->gUUID );
      send_to_char ( buf, ch );
      save_char_obj ( ch );
      return;
    }
    else
    {
      send_to_char ( "Unable to find a gcolor with that gUUID.\n\r", ch );
      return;
    }
  }
  else if ( !str_cmp ( "delete", arg1 ) && IS_IMMORTAL ( ch ) )
  {
    char arg2[MIL];
    char buf[MSL];
    bool found = FALSE;
    COLORIZER_DATA *pcolor;
    COLORIZER_DATA *prev;

    argument = one_argument ( argument, arg2 );


    send_to_char ( "Disabled.\n\r", ch );
    return;

    if ( arg2[0] == '\0' )
    {
      send_to_char ( "{DSyntax{R: gcolor delete {r<{wnum{r>{x\n\r", ch );
      return;
    }

    pcolor = get_gcolorizer ( atol ( arg2 ) );

    if ( pcolor == NULL )
    {
      send_to_char ( "No such gUUID.\n\r", ch );
      return;
    }

    /*
       Find, delete, re-link 
     */
    if ( pcolor == global_colorizers )
    {
      global_colorizers = pcolor->next;
      found = TRUE;
    }
    else
    {
      for ( prev = global_colorizers; prev != NULL; prev = prev->next )
      {
        if ( prev->next == pcolor )
        {
          prev->next = pcolor->next;
          found = TRUE;
          break;
        }
      }
    }

    if ( found )
    {
      sprintf ( buf, "gUUID %ld retired and colorizer '%s' deleted.\n\r", pcolor->gUUID, pcolor->name );
      send_to_char ( buf, ch );
      save_gcolorizers (  );
    }
    else
    {
      /* This should never happen, but its here just in case */
      sprintf ( buf, "Attempt to delete the colorizer was unsuccessful.\n\rPlease report this to an IMP.\n\r" );
      send_to_char ( buf, ch );

      sprintf ( buf, "gColor delete error. Argument: '%s', not found in linked list.\n\r", arg2 );
      bug ( buf, 0 );
    }

    return;
  }
  else if ( !str_cmp ( "rename", arg1 ) && IS_IMMORTAL ( ch ) )
  {
    char arg2[MIL];
    char buf[MSL];
    COLORIZER_DATA *pcolor;

    argument = one_argument ( argument, arg2 );

    if ( argument[0] == '\0' || has_quote ( argument ) )
    {
      send_to_char
        ( "{DSyntax{R: gcolor rename {r<{wnum{r> <{wnew name{r>{x\n\rThe name can not contain a single or double quote.",
        ch );
      return;
    }

    pcolor = get_gcolorizer ( atol ( arg2 ) );

    if ( pcolor != NULL )
    {
      sprintf ( buf, "{DColorizer {R'{w%s{R' {Drenamed to {R'{w%s{R'{D.{x\n\r", pcolor->name, argument );
      pcolor->name = str_dup ( argument );

      save_gcolorizers (  );
      send_to_char ( buf, ch );
      return;
    }
    else
    {
      send_to_char ( "{DCould not find a Global Colorizer with that gUUID{R.{x\n\r", ch );
      return;
    }
  }
  else
  {
    COLORIZER_DATA *pcolor;
    char buf[MSL];
    char output[MSL];
    int num;

    output[0] = '\0';
    buf[0] = '\0';

    send_to_char ( "\n\r{D[ {rGlobal Colorizers {D]{x\n\r", ch );

    if ( global_colorizers != NULL )
      for ( num = 0; num <= gUUID_TOP; num++ )
        for ( pcolor = global_colorizers; pcolor != NULL; pcolor = pcolor->next )
          if ( pcolor->gUUID == num )
          {
            sprintf ( buf, "{r#{w%3ld  {DName{R: {w%-17s  {DType{R: {w%-10s  {DSample{R: {w%s{x\n\r", pcolor->gUUID,
              pcolor->name, colorizer_table[lookup_ctype ( pcolor->type )].name, do_colorize ( teststr, pcolor, FALSE ) );
            strcat ( output, buf );
          }
    if ( IS_IMMORTAL ( ch ) )
    {
      sprintf ( buf, "{DgUUID_TOP{r: {w%ld\n\r", gUUID_TOP );
      strcat ( output, buf );
    }

    strcat ( output, "\n\r{DSyntax{R: {Dgcolor info {r<{wnum{r>              {R: {wMore info about that gColor{r.{x\n\r" );
    strcat ( output,
      "        {Dgcolor copy {r<{wnum{r>              {R: {wCopy gColor <num> to your color list{r.{x\n\r                                        {D({wFor use on channels{D){x\n\r" );
    if ( IS_IMMORTAL ( ch ) )
    {
      strcat ( output, "\n\r{DImmortal Options{R:\n\r" );
      strcat ( output,
        "        {Dgcolor {r<{wload{r/{wsave{r>             {R: {wForce a load or save of the gColorDB{r.{x\n\r" );
      strcat ( output,
        "        {Dgcolor rename {r<{wnum{r> <{wnew name{r> {R: {wRename gColor <num> to <new name>{r.{x\n\r" );
      strcat ( output, "        {Dgcolor delete {r<{wnum{r>            {R: {wDelete gColor <num>{r.{x\n\r" );
    }

    send_to_char ( output, ch );
    return;
  }

  return;
}

void save_gcolorizers (  )
{
  FILE *fp;
  COLORIZER_DATA *pcolor;

  if ( ( fp = file_open ( GCOLOR_FILE, "w" ) ) == NULL )
  {
    bug ( "save_gcolorizers: fopen of GCOLOR_FILE failed", 0 );
    file_close ( fp );
    return;
  }

  fprintf ( fp, "gUUID_TOP %ld\n", gUUID_TOP );

  if ( global_colorizers != NULL )
  {
    for ( pcolor = global_colorizers; pcolor != NULL; pcolor = pcolor->next )
    {
      if ( pcolor->name[0] != '\0' && !has_quote ( pcolor->name ) )
      {
        /* Don't save global colorizers with no name or invalid chars */
        if ( pcolor->name[0] == '\0' || has_quote ( pcolor->name ) || has_quote ( pcolor->color_string ) )
          continue;

        /* Don't save global colorizers with no gUUID set */
        if ( pcolor->gUUID <= 0 )
          continue;

        fprintf ( fp, "gColor_v1 %ld '%s' %d '%s' '%s' %d %ld %ld '%s' %s\n", pcolor->gUUID, pcolor->name, pcolor->type,
          pcolor->creator, pcolor->owner, pcolor->price, pcolor->cre_time, pcolor->mod_time, pcolor->color_string,
          pcolor->clan ? "CLAN" : "NOTCLAN" );
      }
    }
  }

  fprintf ( fp, "End\n" );
  file_close ( fp );
  return;
}

void load_gcolorizers (  )
{
  FILE *fp;
  char *word;
  char *temp;
  COLORIZER_DATA *pcolor, *pcolor_next;

  if ( ( fp = file_open ( GCOLOR_FILE, "r" ) ) == NULL )
  {
    bug ( "load_gcolorizers: fopen of GCOLOR_FILE failed", 0 );
    file_close ( fp );
    return;
  }

  gUUID_TOP = 0;

  for ( pcolor = global_colorizers; pcolor != NULL; pcolor = pcolor_next )
  {
    pcolor_next = pcolor->next;
    free_colorizer ( pcolor );
  }

  global_colorizers = NULL;

  for ( ;; )
  {
    word = feof ( fp ) ? "End" : fread_word ( fp );

    if ( !str_cmp ( word, "gUUID_TOP" ) )
    {
      gUUID_TOP = fread_long ( fp );
    }
    else if ( !str_cmp ( word, "gColor_v1" ) )
    {
      pcolor = new_colorizer (  );

      pcolor->gUUID = fread_long ( fp );

      /* Bump the gUUID up */
      if ( pcolor->gUUID > gUUID_TOP )
        gUUID_TOP = pcolor->gUUID;

      temp = fread_word ( fp );
      pcolor->name = str_dup ( temp );

      pcolor->type = fread_number ( fp );

      temp = fread_word ( fp );
      pcolor->creator = str_dup ( temp );

      temp = fread_word ( fp );
      pcolor->owner = str_dup ( temp );

      pcolor->price = fread_number ( fp );
      pcolor->cre_time = fread_long ( fp );
      pcolor->mod_time = fread_long ( fp );

      temp = fread_word ( fp );
      pcolor->color_string = str_dup ( temp );

      if ( !str_cmp ( fread_word ( fp ), "NOTCLAN" ) )
      {
        pcolor->clan = FALSE;
      }
      else if ( !str_cmp ( fread_word ( fp ), "CLAN" ) )
      {
        pcolor->clan = TRUE;
      }

      pcolor->pending = FALSE;

      pcolor->next = global_colorizers;
      global_colorizers = pcolor;
    }
    else
    {
      break;
    }
  }

  file_close ( fp );
  return;
}

COLORIZER_DATA *get_gcolorizer ( long gUUID )
{
  COLORIZER_DATA *pcolor;

  for ( pcolor = global_colorizers; pcolor != NULL; pcolor = pcolor->next )
    if ( pcolor->gUUID == gUUID )
      return pcolor;

  /* Return NULL if there is no result */
  return NULL;
}

/* 
 * This finds all the global colorizer tags, splits up the text into arrays,
 * runs it through the colorizer code, then re-assembles it.
 *
 * To Do:
 * Make var names more descriptive
 */
char *do_gcolorize ( char *arg, CHAR_DATA * ch )
{
  /* Giant buffers */
  static char buf[MSL * 4];
  static char colorbuf[MSL * 4];
  char tempbuf[MSL * 4];

  char gidstr[10];

  char argument[MSL];

  sprintf ( argument, "%s{}", arg );

  /*
   * Giant memory-sucking multi-dimensional arrays
   * where we store, process and re-assmble the string
   */
  char beginstr[MSL][maxtags];  /* Text before the colorizer */
  char colorstr[MSL][maxtags];  /* Text being colored */
  char endstr[MSL][maxtags];    /* Text after the colorizer */

  /* Arrays to hold all the tag positions and the colorizer number */
  int open[maxtags], close[maxtags], reset[maxtags], gid[maxtags];

  /*
   * Tons of increment / position ints
   * Needs to be cleaned up...
   */
  int i = 0, o = 0, c = 0, r = 0, x = 0, gpos = 0, pos = 0, cpos = 0, spos = 0, epos = 0, bufpos = 0, tstart = 0, otemp = 0;

  memset ( gidstr, 0, 10 );

  /* Initalize our tag data arrays */
  for ( i = 0; i < maxtags; i++ )
  {
    open[i] = 0;
    close[i] = 0;
    reset[i] = 0;
    gid[i] = 0;
  }

  /*
   * Find all the tag positions (start, end and reset)
   * for each tag in the string and put them into our arrays
   * 
   * We have to know this so we can
   * 1: Get the number from the tag so we know what colorizer to use.
   * 2: Remove the tag characters from the text.
   * 3: Divide the string up, store it, colorize it and re-assemble it.
   */
  for ( i = 0; i < MSL && argument[i] != '\0'; i++ )
  {
    if ( argument[i] == '{' && ( argument[( i + 1 )] >= '0' && argument[( i + 1 )] <= '9' ) )
    {
      open[o] = ( i + 1 );
      o++;
    }
    else if ( argument[i] == '}' && ( argument[( i - 1 )] >= '0' && argument[( i - 1 )] <= '9' ) )
    {
      if ( o > 0 )
      {
        close[( o - 1 )] = ( i + 1 );
        c++;
      }
    }
    else if ( argument[i] == '{' && argument[( i + 1 )] == '}' )
    {
      if ( o > 0 )
      {
        reset[( o - 1 )] = i;
        r++;
      }
    }

  }

  /* Grab global colorizer numbers from tags */
  if ( o > 0 && c > 0 && c == o )
  {
    for ( i = 0; i < maxtags; i++ )
    {
      gpos = 0;
      /*
       * We look between the start of the tag and the end of the tag
       * so we can get the number from it
       *
       * Then we store the data into a string and convert it into a number.
       * Then that number is stored into an array so we can tell do_colorize()
       * what colorizer we want to use when we're processing the text
       */
      for ( x = open[i]; x <= close[i] && gpos < 9; x++ )
      {
        gidstr[gpos] = argument[x];
        gpos++;
      }

      gidstr[gpos] = '\0';
      if ( gidstr[0] != '\0' )
      {
        gid[i] = atoi ( gidstr );
      }
    }

    /*
     * Seperate the string data and store it in huge arrays.
     * This takes way too much effort...
     */
    bufpos = 0;
    for ( i = 0; i < maxtags; i++ )
    {
      /* Clear all this crap */
      bool truereset = FALSE;

      beginstr[0][i] = '\0';
      colorstr[0][i] = '\0';
      endstr[0][i] = '\0';

      /*
       * We make sure there is data in the tags before we start
       * We set truereset to TRUE, but if we do not detect a manual reset []
       * we set it to FALSE later on
       *
       * We set the next tag's open position even if there is no next tag,
       * because we need that data when we put all the data into the arrays.
       * 
       * If there is no manual reset, we set the reset position to the next tag's start position
       * If there is no next tag, we set the reset position to the end of the string
       */
      if ( open[i] < close[i] )
      {
        if ( reset[i] > 0 )
          truereset = TRUE;

        if ( open[( i + 1 )] == 0 )
        {
          open[( i + 1 )] = ( strlen ( argument ) );
        }
        if ( reset[i] == 0 )
        {
          if ( open[( i + 1 )] > 0 )
            reset[i] = ( open[( i + 1 )] - 1 );
          else
            reset[i] = ( strlen ( argument ) );

          truereset = FALSE;
        }

        /*
         * Put data into the arrays
         */

        /* Color array */
        cpos = 0;
        for ( pos = close[i]; pos < reset[i]; pos++ )
        {
          colorstr[cpos][i] = argument[pos];
          cpos++;
        }
        colorstr[cpos][i] = '\0';

        /* Begining array */
        /*
         * If we are on the first tag we start at the begining of the string.
         * Otherwise we start at the previous tag's reset position.
         * The previous tag's reset position is either a manual reset, or us.
         */
        spos = 0;
        if ( i > 0 )
          tstart = reset[( i - 1 )];
        else
          tstart = 0;
        /*
         * If this is a manual reset and we're not on the first tag
         * we end at the start of our tag.
         */
        if ( truereset && i > 0 )
          tstart = close[i];
        /* Copy the string into the array, skipping the start of the tag */
        for ( pos = tstart; pos < ( open[i] - 1 ); pos++ )
        {
          beginstr[spos][i] = argument[pos];
          spos++;
        }
        beginstr[spos][i] = '\0';

        /* Ending array */
        epos = 0;
        /* If it is a truereset ( manual reset: [] ) then we skip over the [] chars */
        if ( truereset )
          otemp = ( reset[i] + 2 );
        else
          otemp = reset[i];
        /*
         * Copy the text into the array and stop when we get to the next tag's start
         * Code before us sets this data for us if there is no next tag
         */
        for ( pos = otemp; argument[pos] != '\0' && pos < ( open[( i + 1 )] - 1 ); pos++ )
        {
          endstr[epos][i] = argument[pos];
          epos++;
        }
        endstr[epos][i] = '\0';

        /*
         * We copy the data out of the color array into a standard string
         * so we can pass it through do_colorize()
         */
        colorbuf[0] = '\0';
        if ( colorstr[0][i] != '\0' )
        {
          for ( x = 0; colorstr[x][i] != '\0' && x < MSL; x++ )
            tempbuf[x] = colorstr[x][i];

          tempbuf[x] = '\0';

          sprintf ( colorbuf, "%s{x", IS_SET ( ch->act2, PLR2_NOCOLORIZERS ) ? tempbuf : do_colorize ( tempbuf,
              get_gcolorizer ( gid[i] ), FALSE ) );
        }

        /* Re-assemble everything into the buffer, making sure not to overflow. */
        for ( pos = 0; beginstr[pos][i] != '\0' && bufpos < MSL; pos++ )
        {
          buf[bufpos] = beginstr[pos][i];
          bufpos++;
        }
        for ( pos = 0; colorbuf[pos] != '\0' && bufpos < MSL; pos++ )
        {
          buf[bufpos] = colorbuf[pos];
          bufpos++;
        }
        for ( pos = 0; endstr[pos][i] != '\0' && bufpos < MSL; pos++ )
        {
          buf[bufpos] = endstr[pos][i];
          bufpos++;
        }
      }
    }

    /* Add end marker to the buffer */
    buf[bufpos] = '\0';
    /* Send it off */
    return buf;
  }
  else
  {
    /* No colorizer tags, pass unedited argument */
    sprintf ( buf, "%s", argument );
  }
  return buf;
}

/* Use this to fetch the colorizer data for a channel, for local colorizers. */
COLORIZER_DATA *find_channel_colorizers ( CHAR_DATA * ch, long channel )
{
  COLORIZER_DATA *pcolor;

  if ( !IS_NPC ( ch ) )
    if ( ch->pcdata->colorizer != NULL )
      for ( pcolor = ch->pcdata->colorizer; pcolor != NULL; pcolor = pcolor->next )
        if ( IS_SET ( pcolor->channel, channel ) && channel != 0 )
          return pcolor;

  return NULL;
}

/*
 * This does all the colorizing -- Dist
 * Note to self: parsing the colorizer data each time is inefficient. Parse and store data parsed as an optimization.
 *
 * As of Oct 22nd 2006 the channel/ch data has been decoupled and moved into find_channel_colorizers()
 * to make way for the global colorizer / colorizer tags.
 */

char *do_colorize ( char *argument, COLORIZER_DATA * pcolor, bool output )
{
  static char outbuf[MSL];

  outbuf[0] = '\0';

  /* If we don't get valid colorizer data, skip and just send the plain text */
  if ( pcolor != NULL )
  {

    /* Type chance */
    if ( pcolor->type == CTYPE_CHANCE || pcolor->type == CTYPE_LEGACY )
    {
      /* Local vars */
      char buf[MSL];
      int i = 0;
      int x = 0;
      int cpos = 0;
      int npos = 0;
      int count = 0;
      int c = 0;
      bool found = FALSE;
      bool skip = FALSE;
      bool force = TRUE;

      /* Little arrays to hold the data we parse */
      char ccodes[MAX_COLORCHANCE];
      int chance[MAX_COLORCHANCE];

      /* Clear buffers */
      memset ( ccodes, 0, MAX_COLORCHANCE );
      memset ( chance, 0, MAX_COLORCHANCE );

      /* Loop through the colorizer string, parse */
      for ( i = 0; pcolor->color_string[i] != '\0' && i < MAX_COLORCHANCE; i++ )
      {
        /* Don't overflow our max */
        if ( cpos >= MAX_COLORCHANCE && npos >= MAX_COLORCHANCE )
          return argument;

        /* If the arg is a number */
        if ( ( pcolor->color_string[i] >= '0' && pcolor->color_string[i] <= '9' ) && pcolor->color_string[i] != ' '
          && npos < MAX_COLORCHANCE )
        {
          sprintf ( buf, "%c", pcolor->color_string[i] );
          chance[npos] = atoi ( buf );
          npos++;
        }
        /* Otherwise, if it is a letter */
        else if ( pcolor->color_string[i] != '{' && pcolor->color_string[i] != ' ' && cpos < MAX_COLORCHANCE )
        {
          sprintf ( buf, "%c", pcolor->color_string[i] );
          strcat ( ccodes, buf );
          cpos++;
        }
      }

      /* Make sure there isn't an uneven count of letters and numbers */
      if ( cpos != npos )
        return argument;

      count = 0;

      /* Colorize the argument */
      for ( i = 0; argument[i] != '\0'; i++ )
      {
        /* Reset */
        found = FALSE;
        force = FALSE;

        /* Check for color codes */
        if ( argument[i] == '{' && argument[i + 1] != 'x' )
        {
          skip = TRUE;
        }
        else if ( argument[i] == '{' && argument[i + 1] == 'x' )
        {
          skip = FALSE;
          i += 2;
        }

        /* Print normally, until we see a reset or end */
        if ( ( skip || argument[i] == ' ' || argument[i] == '\n' || argument[i] == '\r' ) && !force )
        {
          sprintf ( buf, "%c", argument[i] );
          strcat ( outbuf, buf );
          continue;
        }

        if ( i == 0 )
          force = TRUE;

        /* Keep looping until we get a hit, or some sane limit */
        while ( !found && !skip && count < 5000 )
        {
          count++;
          /* Pick one of their colors at random */
          x = number_range ( 0, ( npos - 1 ) );

          /* Roll the dice */
          c = number_range ( 0, 9 );

          if ( ( c <= chance[x] || force ) )
          {
            /* We hit, print it */
            sprintf ( buf, "%s%c%c", output ? "{{" : "{", ccodes[x], argument[i] );
            strcat ( outbuf, buf );
            found = TRUE;
            break;
          }
          else if ( !force )
          {
            sprintf ( buf, "%c", argument[i] );
            strcat ( outbuf, buf );
            found = TRUE;
          }
        }
      }

      /* Send off finished product */
      return outbuf;
    }
    else if ( pcolor->type == CTYPE_GRADIENT )
    {
      /* Local vars */
      int i = 0;
      int clen = 0;
      int len = 0;
      int x = 0;
      int t = 0;
      double lastcolor = 0;
      double newcolor = 0;
      bool skip = FALSE;
      bool skipp = FALSE;

      /* Buffers */
      char ccodes[MAX_COLORCHANCE];
      char buf[MSL];
      char temp = '\0';

      /* Clear bufs */
      outbuf[0] = '\0';
      buf[0] = '\0';
      ccodes[0] = '\0';

      /* Parse colorizer string */
      for ( i = 0; pcolor->color_string[i] != '\0' && i < MAX_COLORCHANCE; i++ )
        if ( pcolor->color_string[i] != '{' && pcolor->color_string[i] != ' ' )
        {
          sprintf ( buf, "%c", pcolor->color_string[i] );
          strcat ( ccodes, buf );
          /* Count length */
          clen++;
        }

      /* Find arg length */
      len = ( cstrlen ( argument ) + 1 );

      /* Sanity checking */
      if ( len <= 0 || clen <= 0 )
        return argument;

      /* Print */
      for ( i = 0; argument[i] != '\0'; i++ )
      {
        /* Handle inline color */
        if ( argument[i] == '{' )
        {
          if ( argument[i + 1] == 'x' )
          {
            skip = FALSE;
            skipp = TRUE;
            sprintf ( buf, "%s%c", output ? "{{" : "{", temp );
            strcat ( outbuf, buf );
            i += 2;
          }
          else
          {
            skip = TRUE;
          }
        }
        if ( argument[i] != '{' )
          t++;

        /* Calculate */
        newcolor = ( double ) ( clen * t / len );

        /* Time for new color code? */
        if ( skipp )
          skipp = FALSE;
        else if ( ( lastcolor != newcolor || i == 0 ) )
        {
          temp = ccodes[x];
          if ( !skip )
          {
            sprintf ( buf, "%s%c", output ? "{{" : "{", ccodes[x] );
            strcat ( outbuf, buf );
          }

          /* Next color code */
          if ( x < ( clen - 1 ) )
            x++;;

        }

        sprintf ( buf, "%c", argument[i] );
        strcat ( outbuf, buf );
        lastcolor = newcolor;
      }

      /* Send it off */
      return outbuf;
    }
    else if ( pcolor->type == CTYPE_PATTERN || pcolor->type == CTYPE_WPATTERN || pcolor->type == CTYPE_RPATTERN
      || pcolor->type == CTYPE_RWPATTERN )
    {
      /* Buffers */
      char ccodes[MAX_COLORCHANCE];
      char buf[MSL];

      /* Local vars */
      int cpos = 0;
      int i = 0;
      int x = 0;
      int y = 0;
      bool skip = FALSE;

      /* Clear bufs */
      ccodes[0] = '\0';
      buf[0] = '\0';
      outbuf[0] = '\0';

      /* Parse colorizer */
      for ( y = 0; pcolor->color_string[y] != '\0' && y < MAX_COLORCHANCE; y++ )
        if ( pcolor->color_string[i] != '{' && pcolor->color_string[i] != ' ' )
        {
          sprintf ( buf, "%c", pcolor->color_string[y] );
          strcat ( ccodes, buf );
          cpos++;
        }

      /* Randomize if needed */
      if ( pcolor->type == CTYPE_RPATTERN || pcolor->type == CTYPE_RWPATTERN )
        x = number_range ( 0, ( cpos - 1 ) );

      /* Step through arg */
      for ( i = 0; argument[i] != '\0'; i++ )
      {
        /* Check for color codes */
        if ( argument[i] == '{' && argument[i + 1] != 'x' )
        {
          skip = TRUE;
        }
        else if ( argument[i] == '{' && argument[i + 1] == 'x' )
        {
          skip = FALSE;
          i += 2;
        }

        /* Skip spaces */
        if ( skip || argument[i] == ' ' || argument[i] == '\n' || argument[i] == '\r' )
        {
          sprintf ( buf, "%c", argument[i] );
        }
        else
        {
          sprintf ( buf, "%s%c%c", output ? "{{" : "{", ccodes[x], argument[i] );
        }

        /* Add to output */
        strcat ( outbuf, buf );

        /* Reset our position in the color string as needed */
        if ( x >= ( y - 1 ) || ( ( argument[i] == ' ' || argument[i] == '\n' || argument[i] == '\r' )
            && pcolor->type == CTYPE_WPATTERN ) )
          x = 0;
        else if ( argument[i] != ' ' && argument[( i + 1 )] != '\0' )
          x++;

        /* Randomize if needed */
        if ( ( argument[i] == ' ' || argument[i] == '\n' || argument[i] == '\r' ) && pcolor->type == CTYPE_RWPATTERN )
          x = number_range ( 0, ( cpos - 1 ) );

      }
      /* Return output */
      return outbuf;
    }
  }

  sprintf ( outbuf, "%s", argument );
  return outbuf;
}

CH_CMD ( do_colori )
{
  if ( !do_colorizer ( ch, argument ) )
    send_to_char ( "Invalid option\n\r", ch );

  return;
}

/* Return position in pcolor data */
/* Should to be changed to a sort() */
int find_colornum ( CHAR_DATA * ch, char *arg )
{
  COLORIZER_DATA *pcolor = NULL;
  int z = 0;
  int num = 0;

  if ( is_number ( arg ) )
    num = atoi ( arg );
  else
    return -1;

  for ( pcolor = ch->pcdata->colorizer; pcolor != NULL; pcolor = pcolor->next )
  {
    z++;
    if ( z == num )
    {
      return z;
      break;
    }
  }
  return -1;
}


bool do_colorizer ( CHAR_DATA * ch, char *argument )
{
  bool found = FALSE;
  COLORIZER_DATA *pcolor = NULL;
  COLORIZER_DATA *prev = NULL;
  COLORIZER_DATA *ptemp = NULL;
  char buf[MSL];
  char arg1[MSL];
  int i = 0;
  int colornum = 0;

  argument = one_argument ( argument, arg1 );

  send_to_char ( "{x", ch );

  if ( !str_prefix ( arg1, "colorizer" ) && arg1[0] != '\0' && ch->pcdata->colorizer_edit != 0 )
  {
    send_to_char ( "{WNotice{R: {wYou do not need to type the {r'{wcolorizer{r' {wcommand while in the editor{R.\n\r", ch );
    return TRUE;
  }

  if ( !str_cmp ( arg1, "accept" ) )
  {
    CHAR_DATA *vown = NULL;
    CHAR_DATA *vcre = NULL;

    for ( pcolor = ch->pcdata->colorizer; pcolor != NULL; pcolor = pcolor->next )
      if ( pcolor->pending == TRUE )
      {
        if ( ( vown = get_char_world ( ch, pcolor->owner ) ) != NULL )
          act ( "$N has accepted your colorizer called $t.", vown, pcolor->name, ch, TO_CHAR );

        if ( ( vcre = get_char_world ( ch, pcolor->creator ) ) != NULL && vcre != vown )
        {
          act ( "$N is now using a colorizer created by you, called $t.", vcre, pcolor->name, ch, TO_CHAR );
        }
        pcolor->pending = FALSE;
        if ( pcolor->owner != NULL )
          free_string ( pcolor->owner );
        pcolor->owner = str_dup ( ch->name );
      }
    send_to_char ( "All colorizers accepted.\n\r", ch );
    return TRUE;
  }

  /*
     Edit a colorizer 
   */
  if ( !str_cmp ( arg1, "edit" ) )
  {
    if ( is_number ( argument ) )
    {
      ch->pcdata->colorizer_edit = find_colornum ( ch, argument );
      if ( ch->pcdata->colorizer_edit > 0 )
      {
        send_to_char ( "\n\r{DNow editing{R.{x\n\r", ch );
        do_colorizer ( ch, "" );
        return TRUE;
      }
      else
      {
        send_to_char ( "\n\r{DUnable to find colorizer of that number.{x\n\r", ch );
        return TRUE;
      }
    }
    else
    {
      int x = 0;

      for ( pcolor = ch->pcdata->colorizer; pcolor != NULL; pcolor = pcolor->next )
      {
        x++;
        if ( !str_prefix ( argument, pcolor->name ) && argument[0] != '\0' )
        {
          ch->pcdata->colorizer_edit = x;
          send_to_char ( "\n\r{DNow editing{R.{x\n\r", ch );
          do_colorizer ( ch, "" );
          return TRUE;
        }
      }

      send_to_char ( "{DSyntax{R: {Dcolorizer edit {r<{wnum{r/{wname{r>{x\n\r", ch );
      return TRUE;
    }
  }

  /*
     Delete a colorizer 
   */
  if ( !str_cmp ( arg1, "delete" ) )
  {
    bool number = FALSE;

    found = FALSE;

    if ( is_number ( argument ) )
    {
      ch->pcdata->colorizer_edit = find_colornum ( ch, argument );
      number = TRUE;
    }
    /*
       Find the colorizer 
     */
    for ( pcolor = ch->pcdata->colorizer; pcolor != NULL; pcolor = pcolor->next )
    {
      colornum++;
      if ( number )
      {
        if ( colornum == ch->pcdata->colorizer_edit )
        {
          found = TRUE;
          break;
        }
      }
      else
      {
        if ( !str_prefix ( argument, pcolor->name ) )
        {
          ch->pcdata->colorizer_edit = colornum;
          found = TRUE;
          break;
        }
      }
    }
    if ( !found )
    {
      send_to_char ( "\n\r{DCould not find a valid colorizer with that number to delete{R.{x\n\r", ch );
      ch->pcdata->colorizer_edit = 0;
      return TRUE;
    }
    /*
       Find, delete, re-link 
     */
    if ( pcolor == ch->pcdata->colorizer )
    {
      ch->pcdata->colorizer = pcolor->next;
      found = TRUE;
    }
    else
    {
      for ( prev = ch->pcdata->colorizer; prev != NULL; prev = prev->next )
      {
        if ( prev->next == pcolor )
        {
          prev->next = pcolor->next;
          found = TRUE;
          break;
        }
      }
    }
    /*
       Deleted, reset editor 
     */
    if ( found )
    {
      /*
         Free the colorizer 
       */
      free_colorizer ( pcolor );
      send_to_char ( "\n\r{DColorizer deleted{R.{x\n\r", ch );
      ch->pcdata->colorizer_edit = 0;
      do_colorizer ( ch, "" );
      return TRUE;
    }
    else                        /* Invalid, reset editor */
    {
      send_to_char ( "\n\r{wFATAL{R: {DColorizer not found or invaild data{R.{x\n\r", ch );
      ch->pcdata->colorizer_edit = 0;
      return TRUE;
    }

    /*
       Failure, display syntax 
     */
    send_to_char ( "{DSyntaxP{R: {Dcolorizer delete {r<{wnum{r/{wname{r>{x\n\r", ch );
    return TRUE;
  }

  /*
     Create a new blank colorizer 
   */
  if ( !str_cmp ( arg1, "new" ) )
  {
    pcolor = new_colorizer (  );
    pcolor->name = str_dup ( "New colorizer" );
    pcolor->cre_time = current_time;
    pcolor->mod_time = current_time;
    pcolor->type = CTYPE_UNKNOWN;
    pcolor->price = 0;
    pcolor->color_string = str_dup ( "None" );
    pcolor->owner = ch->name;
    pcolor->creator = ch->name;
    pcolor->clan = FALSE;
    pcolor->channel = 0;
    pcolor->pending = FALSE;

    pcolor->next = ch->pcdata->colorizer;
    ch->pcdata->colorizer = pcolor;

    send_to_char ( "\n\r{DNew colorizer created{R.{x\n\r", ch );

    do_colorizer ( ch, "" );
    return TRUE;
  }

  /*
     Display our colorizers list if we're not editing 
   */
  if ( ch->pcdata->colorizer_edit == 0 )
  {
    int count = 0;
    int z = 0;

    for ( pcolor = ch->pcdata->colorizer; pcolor != NULL; pcolor = pcolor->next )
      count++;

    send_to_char ( "\n\r{D[ {rColorizers {D]{x\n\r", ch );
    for ( i = 0; i <= count; i++ )
    {
      z = 1;
      for ( pcolor = ch->pcdata->colorizer; pcolor != NULL; pcolor = pcolor->next )
      {
        sprintf ( buf, "%d", i );
        if ( find_colornum ( ch, buf ) == z )
        {
          found = TRUE;
          sprintf ( buf, "%s#{w%3d  {DName{R: {w%-17s  {DType{R: {w%-10s  {DSample{R: {w%s{x\n\r",
            pcolor->channel > 0 ? "{R" : "{r", i, pcolor->name,
            pcolor->pending == TRUE ? "Pending" : colorizer_table[lookup_ctype ( pcolor->type )].name, do_colorize ( teststr,
              pcolor, 0 ) );

          send_to_char ( buf, ch );
        }
        z++;
      }
    }

    if ( !found )
      send_to_char ( "{wYou have no colorizers{R.{x\n\r", ch );

    send_to_char
      ( "\n\r{DSyntax{R: {Dcolorizer edit {r<{wnum{r/{wname{r>   {R: {wEdits the specified colorizer{r.\n\r        {Dcolorizer new               {R: {wCreate a new colorizer{r.\n\r        {Dcolorizer delete {r<{wnum{r/{wname{r> {R: {wDelete a colorizer{r.{x\n\r",
      ch );
    return TRUE;
  }

  /*
   * Editor area 
   */

  /*
     Get the data for the colorizer 
   */
  if ( ch->pcdata->colorizer_edit != 0 )
    for ( pcolor = ch->pcdata->colorizer; pcolor != NULL; pcolor = pcolor->next )
    {
      colornum++;
      if ( colornum == ch->pcdata->colorizer_edit )
        break;
    }

  if ( pcolor == NULL )
  {
    send_to_char ( "Invalid colorizer, unable to edit.\n\r", ch );
    ch->pcdata->colorizer_edit = 0;
    return TRUE;
  }

  if ( !str_cmp ( arg1, "send" ) && ch->pcdata->colorizer_edit != 0 )
  {
    CHAR_DATA *victim = NULL;
    char arg2[MSL];

    argument = one_argument ( argument, arg2 );
    COLORIZER_DATA *npcolor = NULL;

    if ( !str_cmp ( "gcolordb", arg2 ) && IS_IMMORTAL ( ch ) )
    {
      npcolor = new_colorizer (  );
      npcolor->name = str_dup ( pcolor->name );
      npcolor->cre_time = pcolor->cre_time;
      npcolor->mod_time = pcolor->mod_time;
      npcolor->type = pcolor->type;
      npcolor->color_string = str_dup ( pcolor->color_string );
      npcolor->owner = str_dup ( pcolor->owner );
      npcolor->creator = str_dup ( pcolor->creator );
      npcolor->pending = FALSE;

      gUUID_TOP++;
      npcolor->gUUID = gUUID_TOP;

      npcolor->next = global_colorizers;
      global_colorizers = npcolor;
      save_gcolorizers (  );

      sprintf ( buf,
        "{xColorizer copied to the global database. gUUID is %ld.\n\rFor further information, see help Colorizers.\n\r",
        gUUID_TOP );
      send_to_char ( buf, ch );
      return TRUE;
    }
    else if ( ( victim = get_char_world ( ch, arg2 ) ) != NULL )
    {
      if ( IS_NPC ( victim ) )
      {
        send_to_char ( "You can't send colorizers to NPCs.\n\r", ch );
        return TRUE;
      }

      npcolor = new_colorizer (  );
      npcolor->name = str_dup ( pcolor->name );
      npcolor->cre_time = pcolor->cre_time;
      npcolor->mod_time = pcolor->mod_time;
      npcolor->type = pcolor->type;
      npcolor->color_string = str_dup ( pcolor->color_string );
      npcolor->owner = str_dup ( pcolor->owner );
      npcolor->creator = str_dup ( pcolor->creator );
      npcolor->pending = TRUE;

      npcolor->next = victim->pcdata->colorizer;
      victim->pcdata->colorizer = npcolor;

      sprintf ( buf, "{xColorizer %s{x to $N{x.\n\r", pcolor->name );
      act ( buf, ch, NULL, victim, TO_CHAR );

      sprintf ( buf,
        "{x$n has sent you a colorizer called %s.\n\rTo accept type colorizer accept.\n\rIf you do not wish to accept, simply delete the colorizer, or do nothing.\n\rColorizers that are not accepted do not save.\n\rSample: %s{x",
        npcolor->name, do_colorize ( teststr, npcolor, 0 ) );
      act ( buf, ch, NULL, victim, TO_VICT );
      return TRUE;
    }
    else
    {
      send_to_char ( "{DSyntax{R: {Dsend {r<{wplayer{r>{x\n\r", ch );
      return TRUE;
    }
  }

  /*
     Name 
   */
  if ( !str_cmp ( arg1, "name" ) && ch->pcdata->colorizer_edit != 0 )
  {
    if ( argument[0] != '\0' )
    {
      if ( cstrlen ( argument ) > 32 || cstrlen ( argument ) < 3 || has_quote ( argument ) )
      {
        send_to_char
          ( "{DName must be at least {w3{R, {Dbut not more than {w32 {Dchars long{R, and can not contain a single or double quote.{x\n\r",
          ch );
        return TRUE;
      }
      if ( pcolor->name != NULL )
        free_string ( pcolor->name );
      pcolor->name = str_dup ( stripcolor ( argument ) );
      send_to_char ( "\n\r{DColorizer name set{R.{x\n\r", ch );
      do_colorizer ( ch, "" );
      pcolor->mod_time = current_time;
      return TRUE;
    }
    else
    {
      send_to_char ( "{DSyntax{R: {Dname {r<{wname{r>{x\n\r", ch );
      return TRUE;
    }
  }

  /*
     String 
   */
  if ( !str_cmp ( arg1, "string" ) && ch->pcdata->colorizer_edit != 0 )
  {
    if ( argument[0] != '\0' )
    {
      if ( cstrlen ( argument ) > 80 || cstrlen ( argument ) < 2 || has_quote ( argument ) )
      {
        send_to_char
          ( "{DName must be at least {w2{R, {wbut not more than {w80 {Dchars long{R, and can not contain a single or double quote.{x\n\r",
          ch );
        return TRUE;
      }
      if ( pcolor->color_string != NULL )
        free_string ( pcolor->color_string );
      pcolor->color_string = str_dup ( argument );
      send_to_char ( "\n\r{DColorizer string set{R.{x\n\r", ch );
      do_colorizer ( ch, "" );
      pcolor->mod_time = current_time;
      return TRUE;
    }
    else
    {
      send_to_char ( "{DSyntax{R: {Dstring {r<{wstring{r>{x\n\r        {wSee {r'{whelp colorizer{r'{x", ch );
      return TRUE;
    }
  }

  /* Channels */
  if ( !str_cmp ( arg1, "comm" ) && arg1[0] != '\0' && ch->pcdata->colorizer_edit != 0 )
  {
    int channel;

    channel = find_channel_name ( argument );

    if ( !str_cmp ( argument, "all" ) && pcolor != NULL )
    {
      for ( i = 0; chan_table[i].command[0] != '\0'; i++ )
      {
        SET_BIT ( pcolor->channel, chan_table[i].cflag );
        for ( ptemp = ch->pcdata->colorizer; ptemp != NULL; ptemp = ptemp->next )
          if ( pcolor != ptemp )
            REMOVE_BIT ( ptemp->channel, chan_table[i].cflag );

      }

      send_to_char ( "Set to all channels.\n\r", ch );
      return TRUE;
    }
    else if ( !str_cmp ( argument, "none" ) && pcolor != NULL )
    {
      for ( i = 0; chan_table[i].command[0] != '\0'; i++ )
        REMOVE_BIT ( pcolor->channel, chan_table[i].cflag );

      send_to_char ( "All channels removed.\n\r", ch );
      return TRUE;
    }

    if ( channel <= 0 )
    {
      do_channels ( ch, "" );
      send_to_char ( "No such channel, here is a list.\n\r", ch );
      return TRUE;
    }

    if ( channel > 0 )
    {
      if ( IS_SET ( pcolor->channel, chan_table[channel].cflag ) )
      {
        REMOVE_BIT ( pcolor->channel, chan_table[channel].cflag );
        do_colorizer ( ch, "" );

        sprintf ( buf, "Channel: %s removed from colorizer: %s.\n\r", chan_table[channel].lname, pcolor->name );
        send_to_char ( buf, ch );

        pcolor->mod_time = current_time;
        return TRUE;
      }
      else
      {
        SET_BIT ( pcolor->channel, chan_table[channel].cflag );
        do_colorizer ( ch, "" );

        sprintf ( buf, "Channel: %s added to colorizer: %s.\n\r", chan_table[channel].lname, pcolor->name );
        send_to_char ( buf, ch );
        pcolor->mod_time = current_time;

        /* Remove from other pcolors */
        for ( ptemp = ch->pcdata->colorizer; ptemp != NULL; ptemp = ptemp->next )
        {
          if ( IS_SET ( ptemp->channel, chan_table[channel].cflag ) && pcolor != ptemp )
          {
            REMOVE_BIT ( ptemp->channel, chan_table[channel].cflag );
            sprintf ( buf, "Channel: %s removed from colorizer: %s.\n\r", chan_table[channel].lname, ptemp->name );
            send_to_char ( buf, ch );
            ptemp->mod_time = current_time;
          }
        }
        return TRUE;
      }
    }
    return TRUE;
  }

  /*
     Type 
   */
  if ( !str_cmp ( arg1, "type" ) && ch->pcdata->colorizer_edit != 0 )
  {
    if ( argument[0] != '\0' )
    {
      /*
         Search for type 
       */
      for ( i = 0; colorizer_table[i].name != NULL; i++ )
      {
        /*
           Match found, set type 
         */
        if ( !str_prefix ( argument, colorizer_table[i].name ) && colorizer_table[i].type != CTYPE_LEGACY )
        {
          pcolor->type = colorizer_table[i].type;
          send_to_char ( "\n\r{DType set{R.{x\n\r", ch );
          do_colorizer ( ch, "" );
          pcolor->mod_time = current_time;
          return TRUE;
        }
      }
      send_to_char ( "\n\r{DNo such type{R.{x\n\r", ch );
      do_colorizer ( ch, "type" );
      return TRUE;
    }
    else
    {
      /*
         No arg, show syntax 
       */
      send_to_char ( "\n\r{DTypes{R:{x\n\r", ch );
      for ( i = 0; colorizer_table[i].name != NULL; i++ )
      {
        sprintf ( buf, "  {D%s {R: {w%s\n\r", calign_text ( colorizer_table[i].cname, just_right, 12 ),
          colorizer_table[i].desc );
        send_to_char ( buf, ch );
      }
      send_to_char ( "\n\r{DSyntax{R: {wtype {r<{wtype{r>{x\n\r", ch );
      return TRUE;
    }
  }

  /*
     Exit editor 
   */
  if ( !str_cmp ( arg1, "exit" ) || !str_cmp ( arg1, "done" ) )
  {
    ch->pcdata->colorizer_edit = 0;
    send_to_char ( "\n\r{DExiting editor.{w.{W.{x\n\r", ch );
    do_colorizer ( ch, "" );
    do_save ( ch, "" );
    return TRUE;
  }

  /*
     Status 
   */
  if ( arg1[0] == '\0' )
  {
    if ( pcolor != NULL )
    {
      char buf1[MSL];
      char buf2[MSL];

      sprintf ( buf1, "%s", ( char * ) ctime ( &pcolor->cre_time ) );
      sprintf ( buf2, "%s", sec_to_hms ( current_time - pcolor->mod_time ) );

      sprintf ( buf,
        "\n\r\n\r{D[ {rEditor {D]{x\n\r\n\r        {DName{R: {w%s{x\n\r        {DType{R: {w%s{x\n\r      {DString{R: {w%s{x\n\r        {DComm{R: {w%s{x\n\r\n\r    {DCreation{R: {w%s{DModification{R:%s ago{R.{x\n\r      {DSample{R: {w%s{x\n\r",
        pcolor->name, colorizer_table[lookup_ctype ( pcolor->type )].name, pcolor->color_string,
        colorizer_channels ( pcolor ), buf1, buf2, do_colorize ( teststr, pcolor, 0 ) );
      send_to_char ( buf, ch );
      send_to_char
        ( "\n\r{DSyntax{R: {r<{woption{r>                {R: {wBrings up an explanation of that option{R.\n\r        {r<{woption{r> <{warg{r>          {R: {wSets the option to {r<{Darg{r>{R.\n\r        {wcomm {r<{wchannel{r/{wall{r/{wnone{r> {R: {wToggles {r<{wchannels{r>{R.{x\n\r        {wsend {r<{wplayer{r/{wgColorDB{r>  {R: {wSends a copy to {r<{Dplayer{r/{DgColorDB{r>{R.\n\r        {r<{wreturn{r>                {R: {wDisplays this screen{R.{x\n\r        {rexit                    {R: {wExits the editor{R.{x\n\r",
        ch );
      return TRUE;
    }
    else
    {
      send_to_char ( "\n\r{DNo valid colorizer with that number.{w.{W. {DExiting editor{r.{x\n\r\n\r", ch );
      ch->pcdata->colorizer_edit = 0;
      do_colorizer ( ch, "" );
      return TRUE;
    }
  }

  return FALSE;
}

/* Lists channels a colorizer has set */
char *colorizer_channels ( COLORIZER_DATA * pcolor )
{
  char buf[MSL];
  static char outbuf[MSL];
  int i;
  bool found = FALSE;

  /* Clear buffers */
  buf[0] = '\0';
  outbuf[0] = '\0';

  /* Go through the channel table and look for a hit */
  for ( i = 0; chan_table[i].command[0] != '\0'; i++ )
    if ( IS_SET ( pcolor->channel, chan_table[i].cflag ) )
    {
      sprintf ( buf, "{r'{w%s{r'  ", chan_table[i].command );
      strcat ( outbuf, buf );
      found = TRUE;
    }

  if ( found )
    return ( outbuf );
  else
    return "None";

}

int lookup_ctype ( int type )
{
  int i = 0;

  for ( i = 0; colorizer_table[i].name[0] != '\0'; i++ )
  {
    if ( colorizer_table[i].type == type )
      return i;
  }

  return 0;
}
