/*  Part of XPCE --- The SWI-Prolog GUI toolkit

    Author:        Jan Wielemaker and Anjo Anjewierden
    E-mail:        jan@swi.psy.uva.nl
    WWW:           http://www.swi.psy.uva.nl/projects/xpce/
    Copyright (c)  1985-2002, University of Amsterdam
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

    1. Redistributions of source code must retain the above copyright
       notice, this list of conditions and the following disclaimer.

    2. Redistributions in binary form must reproduce the above copyright
       notice, this list of conditions and the following disclaimer in
       the documentation and/or other materials provided with the
       distribution.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
    BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
    ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    POSSIBILITY OF SUCH DAMAGE.
*/

#include <h/kernel.h>
#include <h/unix.h>			/* storeStringFile() prototype */

static CharArray	stringToCharArray(PceString s);

		/********************************
		*         CREATE/CONVERT	*
		********************************/


status
initialiseCharArray(CharArray n, CharArray value)
{ str_cphdr(&n->data, &value->data);
  str_alloc(&n->data);
  if ( value->data.s_readonly )
    n->data.s_textA = value->data.s_textA;
  else
    memcpy(n->data.s_textA, value->data.s_textA, str_datasize(&n->data));

  succeed;
}


static status
unlinkCharArray(CharArray n)
{ str_unalloc(&n->data);

  succeed;
}


static status
cloneCharArray(CharArray str, CharArray clone)
{ clonePceSlots(str, clone);
  clone->data = str->data;
  str_alloc(&clone->data);
  memcpy(clone->data.s_textA, str->data.s_textA, str_datasize(&str->data));

  succeed;
}


static status
storeCharArray(CharArray s, FileObj file)
{ TRY(storeSlotsObject(s, file));

  return storeStringFile(file, &s->data);
}


static status
loadCharArray(CharArray s, IOSTREAM *fd, ClassDef def)
{ TRY(loadSlotsObject(s, fd, def));

  return loadStringFile(fd, &s->data);
}


static CharArray
getConvertCharArray(Any ctx, Any val)
{ string s;

  TRY(toString(val, &s));
  answer(stringToCharArray(&s));
}


Name
getValueCharArray(CharArray n)
{ answer(StringToName(&n->data));
}


		/********************************
		*             TESTS		*
		********************************/

status
equalCharArray(CharArray n1, CharArray n2, BoolObj ign_case)
{ if ( ign_case == ON )
    return str_icase_eq(&n1->data, &n2->data);
  else
    return str_eq(&n1->data, &n2->data);
}


/* n2 is prefix of n1 */

status
prefixCharArray(CharArray n1, CharArray n2, BoolObj ign_case)
{ if ( ign_case == ON )
    return str_icase_prefix(&n1->data, &n2->data);
  else
    return str_prefix(&n1->data, &n2->data);
}


status
suffixCharArray(CharArray n, CharArray s, BoolObj ign_case)
{ if ( ign_case == ON )
    return str_icase_suffix(&n->data, &s->data);
  else
    return str_suffix(&n->data, &s->data);
}


static status
largerCharArray(CharArray n1, CharArray n2)
{ if ( str_cmp(&n1->data, &n2->data) > 0 )
    succeed;
  fail;
}


static status
smallerCharArray(CharArray n1, CharArray n2)
{ if ( str_cmp(&n1->data, &n2->data) < 0 )
    succeed;
  fail;
}


static status
subCharArray(CharArray n1, CharArray n2, BoolObj ign_case)
{ if ( ign_case != ON )
  { if ( str_sub(&n1->data, &n2->data) )
      succeed;
  } else
  { if ( str_icasesub(&n1->data, &n2->data) )
      succeed;
  }

  fail;
}


status
isWideCharArray(Any s)
{ CharArray ca = s;

  return str_iswide(&ca->data);
}


		/********************************
		*         MODIFICATIONS		*
		********************************/

static CharArray
getModifyCharArray(CharArray n, CharArray n2)
{ answer(answerObject(classOfObject(n), n2, EAV));
}


static CharArray
ModifiedCharArray(CharArray n, PceString buf)
{
  Class class = classOfObject(n);

  if ( class == ClassName )
    return (CharArray) StringToName(buf);
  else if ( class == ClassString)
    return (CharArray) StringToString(buf);	/* ??? */
  else
  { CharArray scratch = StringToScratchCharArray(buf);
    CharArray rval = get(n, NAME_modify, scratch, EAV);

    doneScratchCharArray(scratch);
    answer(rval);
  }
}


CharArray
getCopyCharArray(CharArray n)
{ answer(ModifiedCharArray(n, &n->data));
}


/* This assumes the capitalised version of an ISO Latin-1 string always
   fits in an ISO Latin-1 string.  Is this true for all accented characters?
 */

CharArray
getCapitaliseCharArray(CharArray n)
{ if ( n->data.s_size == 0 )
    answer(n);
  else
  { PceString d = &n->data;
    int size = d->s_size;
    LocalString(buf, d->s_iswide, size);
    int i=1, o=1;

    str_store(buf, 0, towupper(str_fetch(d, 0)));

    for(; i < size; i++, o++)
    { wint_t c = str_fetch(d, i);

      if ( iswordsep(c) )
      { if ( ++i < size )
	  str_store(buf, o, towupper(str_fetch(d, i)));
	else
	  break;
      } else
	str_store(buf, o, towlower(c));
    }

    buf->s_size = o;
    answer(ModifiedCharArray(n, buf));
  }
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Upcase first letter, downcase the rest  and replace word separators (-_)
by spaces.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

CharArray
getLabelNameCharArray(CharArray n)
{ PceString s = &n->data;
  int size = s->s_size;
  int i;

  if ( size == 0 )
    return n;

  { LocalString(buf, s->s_iswide, size);
    int o = 0;
    wint_t c = str_fetch(s, 0);

    i = 0;
    str_store(buf, o, towupper(c));
    i++, o++;

    for( ; i < size; i++, o++ )
    { c = str_fetch(s, i);

      if ( iswordsep(c) )
	str_store(buf, o, ' ');
      else
	str_store(buf, o, c);
    }
    buf->s_size = o;

    answer(ModifiedCharArray(n, buf));
  }
}


CharArray
getDowncaseCharArray(CharArray n)
{ PceString s = &n->data;
  int size = s->s_size;
  LocalString(buf, s->s_iswide, size);
  int i;

  for(i=0; i<size; i++)
    str_store(buf, i, tolower(str_fetch(s, i)));
  buf->s_size = size;

  answer(ModifiedCharArray(n, buf));
}


static CharArray
getUpcaseCharArray(CharArray n)
{ PceString s = &n->data;
  int size = s->s_size;
  LocalString(buf, s->s_iswide, size);
  int i;

  for(i=0; i<size; i++)
    str_store(buf, i, towupper(str_fetch(s, i)));
  buf->s_size = size;

  answer(ModifiedCharArray(n, buf));
}


static CharArray
getStripCharArray(CharArray n, Name how)
{ PceString s = &n->data;
  int size = s->s_size;
  LocalString(buf, s->s_iswide, size);
  int i=0, o=0, lnb=0;

  if ( isDefault(how) )
    how = NAME_canonicalise;

  if ( how == NAME_canonicalise || how == NAME_leading || how == NAME_both )
  { for(; i<size && iswspace(str_fetch(s, i)); i++)
      ;
  }
  for( ; i<size; i++)
  { wint_t c = str_fetch(s, i);

    str_store(buf, o++, c);
    if ( !iswspace(c) )
      lnb = o;
    else if ( how == NAME_canonicalise )
    { for( ; i+1<size && iswspace(str_fetch(s, i+1)); i++)
	;
    }
  }
  if ( how == NAME_canonicalise || how == NAME_trailing || how == NAME_both )
    buf->s_size = lnb;
  else
    buf->s_size = o;

  answer(ModifiedCharArray(n, buf));
}


static Chain
getSplitCharArray(CharArray in, CharArray br)
{ PceString s1 = &in->data;
  int size = s1->s_size;
  int i=0, last=0;
  Chain ch = answerObject(ClassChain, EAV);
  string buf;

  str_cphdr(&buf, s1);

  if ( notDefault(br) )			/* given pattern */
  { PceString b = &br->data;

    while( i<=size-b->s_size )
    { if ( str_prefix_offset(s1, i, b) )
      { if ( isstrA(s1) )
	  buf.s_textA = s1->s_textA+last;
	else
	  buf.s_textW = s1->s_textW+last;

	buf.s_size = i-last;
	appendChain(ch, ModifiedCharArray(in, &buf));

	i = last = i+b->s_size;
      } else
	i++;
    }
  } else
  { for(; i<size && iswspace(str_fetch(s1, i)); i++) /* strip leading */
      ;
    last = i;

    while( i<size )
    { if ( iswspace(str_fetch(s1, i)) )
      { if ( isstrA(s1) )
	  buf.s_textA = s1->s_textA+last;
	else
	  buf.s_textW = s1->s_textW+last;

	buf.s_size = i-last;
	appendChain(ch, ModifiedCharArray(in, &buf));

	while(i < size && iswspace(str_fetch(s1, i)))
	  i++;
	last = i;
	if ( i == size )		/* trailing blanks */
	  answer(ch);
      } else
	i++;
    }
  }

  if ( isstrA(s1) )
    buf.s_textA = s1->s_textA+last;
  else
    buf.s_textW = s1->s_textW+last;

  buf.s_size = size-last;
  appendChain(ch, ModifiedCharArray(in, &buf));

  answer(ch);
}


CharArray
getAppendCharArray(CharArray n1, CharArray n2)
{ PceString s1 = &n1->data;
  PceString s2 = &n2->data;
  int iswide = (s1->s_iswide || s2->s_iswide);
  LocalString(buf, iswide, s1->s_size + s2->s_size);

  buf->s_size = s1->s_size + s2->s_size;
  str_ncpy(buf, 0, s1, 0, s1->s_size);
  str_ncpy(buf, s1->s_size, s2, 0, s2->s_size);

  answer(ModifiedCharArray(n1, buf));
}


static CharArray
getAppendCharArrayv(CharArray ca, int argc, CharArray *argv)
{ int l = ca->data.s_size;
  int iswide = ca->data.s_iswide;
  int i;

  for( i=0; i<argc; i++ )
  { l += argv[i]->data.s_size;
    if ( argv[i]->data.s_iswide )
      iswide = TRUE;
  }

  { LocalString(buf, iswide, l);
    int d;

    str_ncpy(buf, 0, &ca->data, 0, ca->data.s_size);
    d = ca->data.s_size;

    for( i=0; i<argc; i++ )
    { str_ncpy(buf, d, &argv[i]->data, 0, argv[i]->data.s_size);
      d += argv[i]->data.s_size;
    }

    buf->s_size = l;
    answer(ModifiedCharArray(ca, buf));
  }
}


CharArray
getDeleteSuffixCharArray(CharArray n, CharArray s)
{ if ( suffixCharArray(n, s, OFF) )
  { string buf;

    str_cphdr(&buf, &n->data);
    buf.s_text = n->data.s_text;
    buf.s_size = n->data.s_size - s->data.s_size;

    answer(ModifiedCharArray(n, &buf));
  }

  fail;
}


CharArray
getEnsureSuffixCharArray(CharArray n, CharArray s)
{ if ( suffixCharArray(n, s, OFF) )
    answer(n);

  answer(getAppendCharArray(n, s));
}


static CharArray
getDeletePrefixCharArray(CharArray n, CharArray s)
{ if ( prefixCharArray(n, s, OFF) )
  { string buf;

    str_cphdr(&buf, &n->data);
    buf.s_size = n->data.s_size - s->data.s_size;
    if ( isstrA(&buf) )
      buf.s_textA = &n->data.s_textA[s->data.s_size];
    else
      buf.s_textW = &n->data.s_textW[s->data.s_size];

    answer(ModifiedCharArray(n, &buf));
  }

  fail;
}


CharArray
getSubCharArray(CharArray n, Int start, Int end)
{ string s;
  int x, y;
  int len = n->data.s_size;

  x = valInt(start);
  y = (isDefault(end) ? len : valInt(end));
  if ( x < 0 || y > len || x > y )
    fail;

  str_cphdr(&s, &n->data);
  s.s_size = y-x;
  if ( isstrA(&n->data) )
    s.s_textA = &n->data.s_textA[x];
  else
    s.s_textW = &n->data.s_textW[x];

  answer(ModifiedCharArray(n, &s));
}


		 /*******************************
		 *	 BASE-64 ENCODING	*
		 *******************************/

static int
base64_char(unsigned int in)
{ if ( in < 26 ) return 'A'+in;
  if ( in < 52 ) return 'a'+in-26;
  if ( in < 62 ) return '0'+in-52;
  if ( in == 62 ) return '+';
  assert(in == 63);
  return '/';
}


static unsigned long
base64_code(unsigned int in)
{ if ( in == '+' ) return 62;
  if ( in == '/' ) return 63;
  if ( in <  '0' ) return ~0L;
  if ( in <= '9' ) return in - '0' + 52;
  if ( in <  'A' ) return ~0L;
  if ( in <= 'Z' ) return in - 'A';
  if ( in <  'a' ) return ~0L;
  if ( in <= 'z' ) return in - 'a' + 26;
  return ~0L;
}


static CharArray
getBase64EncodeCharArray(CharArray in)
{ PceString s = &in->data;
  int size = s->s_size;
  int triples = (size+2)/3;
  LocalString(buf, FALSE, triples*4);
  int i, o=0;
  unsigned long v;

  for(i=0; i+2<size;)
  { v = (str_fetch(s, i)<<16) + (str_fetch(s, i+1)<<8) + str_fetch(s, i+2);
    i += 3;
    str_store(buf, o++, base64_char((v>>18)&0x3f));
    str_store(buf, o++, base64_char((v>>12)&0x3f));
    str_store(buf, o++, base64_char((v>> 6)&0x3f));
    str_store(buf, o++, base64_char((v>> 0)&0x3f));
  }

  if ( size - i == 2 )
  { v = (str_fetch(s, i)<<16) + (str_fetch(s, i+1)<<8);
    str_store(buf, o++, base64_char((v>>18)&0x3f));
    str_store(buf, o++, base64_char((v>>12)&0x3f));
    str_store(buf, o++, base64_char((v>> 6)&0x3f));
    str_store(buf, o++, '=');
  } else if ( size - i == 1)
  { v = (str_fetch(s, i)<<16);
    str_store(buf, o++, base64_char((v>>18)&0x3f));
    str_store(buf, o++, base64_char((v>>12)&0x3f));
    str_store(buf, o++, '=');
    str_store(buf, o++, '=');
  }

  buf->s_size = o;
  answer(ModifiedCharArray(in, buf));
}


static CharArray
getBase64DecodeCharArray(CharArray in)
{ PceString s = &in->data;
  int size = s->s_size;
  LocalString(buf, FALSE, (size/4)*3);
  int i, o = 0;
  unsigned long v = 0L;

  for(i=0; i+3<size; )
  { int c;

    v = (base64_code(str_fetch(s, i)) << 18) |
	(base64_code(str_fetch(s, i+1)) << 12);
    i += 2;
    c = str_fetch(s, i++);
    if ( c == '=' )
    { i++;				/* skip last (must be =) */
      str_store(buf, o++, (v>>16) & 0xff);
      break;
    }
    v |= base64_code(c) << 6;
    c = str_fetch(s, i++);
    if ( c == '=' )
    { str_store(buf, o++, (v>>16) & 0xff);
      str_store(buf, o++, (v>>8) & 0xff);
      break;
    }
    v |= base64_code(c);
    if ( v == ~(unsigned long)0 )
      fail;
    str_store(buf, o++, (v>>16) & 0xff);
    str_store(buf, o++, (v>>8) & 0xff);
    str_store(buf, o++, (v>>0) & 0xff);
  }

  if ( i != size || v == ~(unsigned long)0 )
    fail;

  buf->s_size = o;
  answer(ModifiedCharArray(in, buf));
}


		 /*******************************
		 *	      AS-FILE		*
		 *******************************/

static CharArray
getReadAsFileCharArray(CharArray n, Int from, Int size)
{ int f = valInt(from);
  int s = valInt(size);

  if ( f < 0 || s < 0 || f > n->data.s_size )
    fail;

  if ( f == 0 && s >= n->data.s_size )
    answer(n);
  else
  { string str;

    if ( f+s > n->data.s_size )
      s = n->data.s_size - f;

    str_cphdr(&str, &n->data);
    str.s_size = s;
    if ( isstrA(&n->data) )
      str.s_textA = &n->data.s_textA[f];
    else
      str.s_textW = &n->data.s_textW[f];

    answer((CharArray)StringToString(&str));
  }
}


		/********************************
		*          READING GETS		*
		********************************/

Int
getSizeCharArray(Any n)
{ CharArray c = n;

  answer(toInt(c->data.s_size));
}


static Int
getCharacterCharArray(CharArray n, Int idx)
{ int i = valInt(idx);

  if ( i < 0 || i >= n->data.s_size )
    fail;

  answer(toInt(str_fetch(&n->data, i)));
}


static Int
getIndexCharArray(CharArray n, Int chr, Int here)
{ wint_t c = valInt(chr);
  int h;

  h = (isDefault(here) ? 0 : valInt(here));
  if ( (h = str_next_index(&n->data, h, c)) >= 0 )
    answer(toInt(h));

  fail;
}


static Int
getRindexCharArray(CharArray n, Int chr, Int here)
{ wint_t c = valInt(chr);
  int h, len = n->data.s_size;

  h = (isDefault(here) ? (len - 1) : valInt(here));
  if ( (h = str_next_rindex(&n->data, h, c)) >= 0 )
    answer(toInt(h));

  fail;
}


static Int
getLineNoCharArray(CharArray name, Int caret)
{ int here = (isDefault(caret) ? name->data.s_size : valInt(caret));

  answer(toInt(str_lineno(&name->data, here)));
}


static Vector
getScanCharArray(CharArray n, CharArray fmt)
{ if ( isstrA(&n->data) && isstrA(&fmt->data) )
  { Any argv[SCAN_MAX_ARGS];
    Int argc;

    TRY(argc = scanstr((char *)n->data.s_textA,
		       (char *)fmt->data.s_textA,
		       argv));

    answer(answerObjectv(ClassVector, valInt(argc), argv));
  } else
  { errorPce(n, NAME_notSupportedForChar16);
    fail;
  }
}


static Name
getCompareCharArray(CharArray n1, CharArray n2, BoolObj ignore_case)
{ int rval;

  if ( ignore_case == ON )
    rval = str_icase_cmp(&n1->data, &n2->data);
  else
    rval = str_cmp(&n1->data, &n2->data);

  if ( rval < 0 )
    answer(NAME_smaller);
  else if ( rval == 0 )
    answer(NAME_equal);
  else
    answer(NAME_larger);

  assert(0);				/* should not get here! */
  fail;
}


		/********************************
		*          C-CONVERSIONS	*
		********************************/

#define SCRATCH_CHAR_ARRAYS	(10)

static CharArray scratch_char_arrays;

void
initCharArrays(void)
{ CharArray ca;
  int n;
  int size = sizeof(struct char_array) * SCRATCH_CHAR_ARRAYS;

  scratch_char_arrays = alloc(size);
  memset(scratch_char_arrays, 0, size);

  for(ca=scratch_char_arrays, n = 0; n < SCRATCH_CHAR_ARRAYS; ca++, n++)
  { initHeaderObj(ca, ClassCharArray);
    setProtectedObj(ca);
    createdObject(ca, NAME_new);
  }
}


CharArray
CtoScratchCharArray(const char *s)
{ CharArray name = scratch_char_arrays;
  size_t len = strlen(s);
  int n;

  for(n = 0; n < SCRATCH_CHAR_ARRAYS; n++, name++)
  { if ( name->data.s_textA == NULL )
    { str_set_n_ascii(&name->data, len, (char *)s);

      return name;
    }
  }

  initCharArrays();			/* handle the crash better */
  NOTREACHED;
  fail;
}


CharArray
StringToScratchCharArray(const PceString s)
{ CharArray name = scratch_char_arrays;
  int n;

  for(n = 0; n < SCRATCH_CHAR_ARRAYS; n++, name++)
  { if ( name->data.s_textA == NULL )
    { str_cphdr(&name->data, s);
      name->data.s_text = s->s_text;
      return name;
    }
  }

  initCharArrays();			/* handle the crash better */
  NOTREACHED;
  fail;
}


void
doneScratchCharArray(CharArray n)
{ n->data.s_text = NULL;
}


CharArray
CtoCharArray(char *s)
{ CharArray name = CtoScratchCharArray(s);
  CharArray rval = answerObject(ClassCharArray, name, EAV);

  doneScratchCharArray(name);
  return rval;
}


static CharArray
stringToCharArray(PceString s)
{ CharArray name = StringToScratchCharArray(s);
  CharArray rval = answerObject(ClassCharArray, name, EAV);

  doneScratchCharArray(name);
  return rval;
}


		 /*******************************
		 *	 CLASS DECLARATION	*
		 *******************************/

/* Type declarations */

static char *T_ofAchar_fromADintD[] =
        { "of=char", "from=[int]" };
static char *T_gsub[] =
        { "start=int", "end=[int]" };
static char *T_match[] =
        { "text=char_array", "ignore_case=[bool]" };
static char *T_readAsFile[] =
        { "from=int", "size=int" };

/* Instance Variables */

static vardecl var_charArray[] =
{ IV(NAME_header, "alien:str_h", IV_NONE,
     NAME_internal, "Header info (packed)"),
  IV(NAME_text, "alien:wint_t *", IV_NONE,
     NAME_internal, "Text represented (8- or 16-bits chars)")
};

/* Send Methods */

static senddecl send_charArray[] =
{ SM(NAME_initialise, 1, "text=char_array", initialiseCharArray,
     DEFAULT, "Create from other char_array"),
  SM(NAME_unlink, 0, NULL, unlinkCharArray,
     DEFAULT, "Free the char *"),
  SM(NAME_equal, 2, T_match, equalCharArray,
     NAME_compare, "Test if names represent same text"),
  SM(NAME_larger, 1, "than=char_array", largerCharArray,
     NAME_compare, "Test if I'm alphabetically after arg"),
  SM(NAME_smaller, 1, "than=char_array", smallerCharArray,
     NAME_compare, "Test if I'm alphabetically before arg"),
  SM(NAME_prefix, 2, T_match, prefixCharArray,
     NAME_test, "Test if receiver has prefix argument"),
  SM(NAME_sub, 2, T_match, subCharArray,
     NAME_test, "Test if argument is a substring"),
  SM(NAME_suffix, 2, T_match, suffixCharArray,
     NAME_test, "Test if receiver has suffix argument"),
  SM(NAME_isWide, 0, NULL, isWideCharArray,
     NAME_encoding, "Test if text contains non ISO-Latin-1 characters")
};

/* Get Methods */

static getdecl get_charArray[] =
{ GM(NAME_printName, 0, "char_array", NULL, getSelfObject,
     DEFAULT, "Equivalent to <-self"),
  GM(NAME_size, 0, "int", NULL, getSizeCharArray,
     NAME_cardinality, "Number of characters in the text"),
  GM(NAME_capitalise, 0, "char_array", NULL, getCapitaliseCharArray,
     NAME_case, "Capitalised version of name"),
  GM(NAME_downcase, 0, "char_array", NULL, getDowncaseCharArray,
     NAME_case, "Map all uppercase letters to lowercase"),
  GM(NAME_labelName, 0, "char_array", NULL, getLabelNameCharArray,
     NAME_case, "Default name used for labels"),
  GM(NAME_upcase, 0, "char_array", NULL, getUpcaseCharArray,
     NAME_case, "Map all lowercase letters to uppercase"),
  GM(NAME_strip, 1, "char_array", "[{canonicalise,leading,trailing,both}]", getStripCharArray,
     NAME_content, "Strip leading/trailing blanks"),
  GM(NAME_compare, 2, "{smaller,equal,larger}", T_match, getCompareCharArray,
     NAME_compare, "Alphabetical comparison"),
  GM(NAME_append, 1, "char_array", "char_array ...", getAppendCharArrayv,
     NAME_content, "Concatenation of me and the argument(s)"),
  GM(NAME_character, 1, "char", "int", getCharacterCharArray,
     NAME_content, "Character code of 0-based nth character"),
  GM(NAME_deletePrefix, 1, "char_array", "prefix=char_array", getDeletePrefixCharArray,
     NAME_content, "Delete specified prefix"),
  GM(NAME_deleteSuffix, 1, "char_array", "suffix=char_array", getDeleteSuffixCharArray,
     NAME_content, "Delete specified suffix"),
  GM(NAME_ensureSuffix, 1, "char_array", "suffix=char_array", getEnsureSuffixCharArray,
     NAME_content, "Append suffix if not already there"),
  GM(NAME_sub, 2, "char_array", T_gsub, getSubCharArray,
     NAME_content, "Get substring from 0-based start and end"),
  GM(NAME_convert, 1, "char_array", "any", getConvertCharArray,
     NAME_conversion, "Convert `text-convertible'"),
  GM(NAME_value, 0, "name", NULL, getValueCharArray,
     NAME_conversion, "Value as a name"),
  GM(NAME_copy, 0, "char_array", NULL, getCopyCharArray,
     NAME_copy, "Copy representing the same text"),
  GM(NAME_split, 1, "chain", "separator=[char_array]", getSplitCharArray,
     NAME_content, "Split text using separator"),
  GM(NAME_modify, 1, "char_array", "char_array", getModifyCharArray,
     NAME_internal, "New instance of my class"),
  GM(NAME_lineNo, 1, "line=int", "index=[int]", getLineNoCharArray,
     NAME_line, "Get 1-based line number at which index is"),
  GM(NAME_index, 2, "int", T_ofAchar_fromADintD, getIndexCharArray,
     NAME_parse, "Get 0-based index starting at pos (forwards)"),
  GM(NAME_rindex, 2, "int", T_ofAchar_fromADintD, getRindexCharArray,
     NAME_parse, "Get 0-based index starting at pos (backwards)"),
  GM(NAME_scan, 1, "vector", "format=char_array", getScanCharArray,
     NAME_parse, "C-scanf like parsing of string"),
  GM(NAME_base64Encode, 0, "char_array", NULL, getBase64EncodeCharArray,
     NAME_mime, "Perform base-64 encoding on the argument"),
  GM(NAME_base64Decode, 0, "char_array", NULL, getBase64DecodeCharArray,
     NAME_mime, "Perform base-64 decoding on the argument"),
  GM(NAME_readAsFile, 2, "char_array", T_readAsFile, getReadAsFileCharArray,
     NAME_stream, "Read data from object using pce_open/3"),
  GM(NAME_sizeAsFile, 0, "characters=int", NULL, getSizeCharArray,
     NAME_stream, "Support pce_open/3")
};

/* Resources */

#define rc_charArray NULL
/*
static classvardecl rc_charArray[] =
{
};
*/

/* Class Declaration */

static Name charArray_termnames[] = { NAME_value };

ClassDecl(charArray_decls,
          var_charArray, send_charArray, get_charArray, rc_charArray,
          1, charArray_termnames);

status
makeClassCharArray(Class class)
{ declareClass(class, &charArray_decls);

  setCloneFunctionClass(class, cloneCharArray);
  setLoadStoreFunctionClass(class, loadCharArray, storeCharArray);

  initCharArrays();

  succeed;
}
