/* 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 #include /* 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; is_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; is_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(; is_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(; is_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; idata.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; idata, 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>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>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, "$Rev$"); status makeClassCharArray(Class class) { declareClass(class, &charArray_decls); setCloneFunctionClass(class, cloneCharArray); setLoadStoreFunctionClass(class, loadCharArray, storeCharArray); initCharArrays(); succeed; }