1 |
/* |
2 |
* HT Editor |
3 |
* str.cc |
4 |
* |
5 |
* Copyright (C) 2002 Stefan Weyergraf |
6 |
* Copyright (C) 2002, 2003 Sebastian Biallas (sb@biallas.net) |
7 |
* |
8 |
* This program is free software; you can redistribute it and/or modify |
9 |
* it under the terms of the GNU General Public License version 2 as |
10 |
* published by the Free Software Foundation. |
11 |
* |
12 |
* This program is distributed in the hope that it will be useful, |
13 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 |
* GNU General Public License for more details. |
16 |
* |
17 |
* You should have received a copy of the GNU General Public License |
18 |
* along with this program; if not, write to the Free Software |
19 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
20 |
*/ |
21 |
|
22 |
#include <cctype> |
23 |
#include <cstdlib> |
24 |
#include <cstring> |
25 |
|
26 |
#include "except.h" |
27 |
#include "debug.h" |
28 |
#include "snprintf.h" |
29 |
#include "str.h" |
30 |
#include "stream.h" |
31 |
#include "strtools.h" |
32 |
|
33 |
#ifdef HAVE_HT_OBJECTS |
34 |
#include "atom.h" |
35 |
#endif |
36 |
|
37 |
//extern "C" { |
38 |
//#include "regex.h" |
39 |
//} |
40 |
|
41 |
/* |
42 |
* CLASS String |
43 |
*/ |
44 |
|
45 |
/** |
46 |
* creates empty string |
47 |
*/ |
48 |
String::String() |
49 |
{ |
50 |
mContent = NULL; |
51 |
realloc(0); |
52 |
} |
53 |
|
54 |
/** |
55 |
* create string from char * |
56 |
*/ |
57 |
String::String(const char *s) |
58 |
{ |
59 |
mContent = NULL; |
60 |
assign(s); |
61 |
} |
62 |
|
63 |
/** |
64 |
* copy constructor |
65 |
*/ |
66 |
String::String(const String *s) |
67 |
{ |
68 |
mContent = NULL; |
69 |
assign(s); |
70 |
} |
71 |
|
72 |
/** |
73 |
* copy constructor |
74 |
*/ |
75 |
String::String(const String &s) |
76 |
{ |
77 |
ASSERT(&s != this); |
78 |
mContent = NULL; |
79 |
assign(s); |
80 |
} |
81 |
|
82 |
/** |
83 |
* creates string from array |s| size |aLength| |
84 |
*/ |
85 |
String::String(const byte *s, int aLength) |
86 |
{ |
87 |
mContent = NULL; |
88 |
assign(s, aLength); |
89 |
} |
90 |
|
91 |
/** |
92 |
* creates string with |count| times |c| |
93 |
*/ |
94 |
String::String(char c, int count) |
95 |
{ |
96 |
mContent = NULL; |
97 |
assign(c, count); |
98 |
} |
99 |
|
100 |
String::~String() |
101 |
{ |
102 |
if (mContent) free(mContent); |
103 |
} |
104 |
|
105 |
/** |
106 |
* (re-)assigns string to |s| |
107 |
*/ |
108 |
void String::assign(const String *s) |
109 |
{ |
110 |
realloc(s->mLength); |
111 |
memcpy(mContent, s->mContent, mLength); |
112 |
} |
113 |
|
114 |
/** |
115 |
* (re-)assigns string to |s| |
116 |
*/ |
117 |
void String::assign(const String &s) |
118 |
{ |
119 |
realloc(s.mLength); |
120 |
memcpy(mContent, s.mContent, mLength); |
121 |
} |
122 |
|
123 |
/** |
124 |
* (re-)assigns string to char * |s| |
125 |
*/ |
126 |
void String::assign(const char *s) |
127 |
{ |
128 |
int slen = s ? strlen(s) : 0; |
129 |
realloc(slen); |
130 |
memcpy(mContent, s, mLength); |
131 |
} |
132 |
|
133 |
/** |
134 |
* (re-)assigns string to array |s| length |aLength| |
135 |
*/ |
136 |
void String::assign(const byte *s, int aLength) |
137 |
{ |
138 |
realloc(aLength); |
139 |
memcpy(mContent, s, mLength); |
140 |
} |
141 |
|
142 |
/** |
143 |
* (re-)assigns string to |count| times |c| |
144 |
*/ |
145 |
void String::assign(char c, int count) |
146 |
{ |
147 |
realloc(count); |
148 |
memset(mContent, c, count); |
149 |
} |
150 |
|
151 |
/** |
152 |
* (re-)assigns string via ht_snprintf |
153 |
*/ |
154 |
void String::assignFormat(const char *s, ...) |
155 |
{ |
156 |
char buf[1024]; |
157 |
va_list vargs; |
158 |
va_start(vargs, s); |
159 |
ht_vsnprintf(buf, sizeof buf, s, vargs); |
160 |
va_end(vargs); |
161 |
assign(buf); |
162 |
} |
163 |
|
164 |
/** |
165 |
* appends |s| to the end |
166 |
*/ |
167 |
void String::append(const String &s) |
168 |
{ |
169 |
if (s.mLength) { |
170 |
int oldLength = mLength; |
171 |
realloc(mLength + s.mLength); |
172 |
memcpy(&mContent[oldLength], s.mContent, s.mLength); |
173 |
} |
174 |
} |
175 |
|
176 |
void String::append(const char *s) |
177 |
{ |
178 |
if (s && *s) { |
179 |
int oldLength = mLength; |
180 |
int slen = strlen(s); |
181 |
realloc(mLength + slen); |
182 |
memcpy(&mContent[oldLength], s, slen); |
183 |
} |
184 |
} |
185 |
|
186 |
void String::appendChar(char c) |
187 |
{ |
188 |
realloc(mLength+1); |
189 |
mContent[mLength-1] = c; |
190 |
} |
191 |
|
192 |
/** |
193 |
* prepends |s| to the front |
194 |
*/ |
195 |
void String::prepend(const String &s) |
196 |
{ |
197 |
if (s.mLength) { |
198 |
int oldLength = mLength; |
199 |
realloc(mLength + s.mLength); |
200 |
memmove(&mContent[s.mLength], &mContent[0], oldLength); |
201 |
memcpy(&mContent[0], s.mContent, s.mLength); |
202 |
} |
203 |
} |
204 |
|
205 |
/** |
206 |
* Empties string. |
207 |
*/ |
208 |
void String::clear() |
209 |
{ |
210 |
realloc(0); |
211 |
} |
212 |
|
213 |
String *String::clone() const |
214 |
{ |
215 |
return new String(mContent, mLength); |
216 |
} |
217 |
|
218 |
/** |
219 |
* compares to characters. |
220 |
* used in compareTo() and findXXX() (and therefore replace()) |
221 |
* @returns 0 for equality, negative number if |c1<c2| and positive number if |c1>c2| |
222 |
*/ |
223 |
int String::compareChar(char c1, char c2) const |
224 |
{ |
225 |
if (c1<c2) return -1; |
226 |
if (c1>c2) return 1; |
227 |
return 0; |
228 |
} |
229 |
|
230 |
int String::compare(const char *s) const |
231 |
{ |
232 |
if (!mLength) { |
233 |
return (s) ? -1: 0; |
234 |
} |
235 |
if (!s) { |
236 |
return 1; |
237 |
} |
238 |
int l = mLength; |
239 |
for (int i=0; i < l; i++) { |
240 |
if (!*s) return 1; |
241 |
int r = compareChar(mContent[i], s[i]); |
242 |
if (r) return r; |
243 |
} |
244 |
if (s[l]) return -1; |
245 |
return 0; |
246 |
} |
247 |
|
248 |
int String::compare(const String &s) const |
249 |
{ |
250 |
if (!mContent) { |
251 |
return (s.mContent) ? -1: 0; |
252 |
} |
253 |
if (!s.mContent) { |
254 |
return 1; |
255 |
} |
256 |
int l = MIN(mLength, s.mLength); |
257 |
for (int i=0; i<l; i++) { |
258 |
int r = compareChar(mContent[i], s.mContent[i]); |
259 |
if (r) return r; |
260 |
} |
261 |
if (mLength < s.mLength) return -1; |
262 |
if (mLength == s.mLength) return 0; |
263 |
return 1; |
264 |
} |
265 |
|
266 |
/* |
267 |
* like compare(s) but considers a maximum of |aMax| characters |
268 |
*/ |
269 |
int String::compare(const String &s, int aMax) const |
270 |
{ |
271 |
if (aMax <= 0) return 0; |
272 |
if (!mContent) { |
273 |
return (s.mContent) ? -1: 0; |
274 |
} |
275 |
if (!s.mContent) { |
276 |
return 1; |
277 |
} |
278 |
int l = MIN(mLength, s.mLength); |
279 |
l = MIN(l, aMax); |
280 |
int i; |
281 |
for (i=0; i<l; i++) { |
282 |
int r = compareChar(mContent[i], s.mContent[i]); |
283 |
if (r) return r; |
284 |
} |
285 |
if (i == aMax) return 0; |
286 |
if (mLength < s.mLength) return -1; |
287 |
if (mLength == s.mLength) return 0; |
288 |
return 1; |
289 |
} |
290 |
|
291 |
int String::compareTo(const Object *o) const |
292 |
{ |
293 |
ASSERT(getObjectID() == o->getObjectID()); |
294 |
return compare(*((String *)o)); |
295 |
} |
296 |
|
297 |
/** |
298 |
* Crops the string to contain a maximum of |aNewLength| characters. |
299 |
*/ |
300 |
void String::crop(int aNewLength) |
301 |
{ |
302 |
if ((aNewLength >= 0) && (aNewLength < mLength)) realloc(aNewLength); |
303 |
} |
304 |
|
305 |
/** |
306 |
* Deletes |aLength| characters at |pos| |
307 |
*/ |
308 |
void String::del(int pos, int aLength) |
309 |
{ |
310 |
if (pos < 0) { |
311 |
aLength += pos; |
312 |
pos = 0; |
313 |
} |
314 |
if (aLength <= 0 || pos >= mLength) return; |
315 |
if (pos+aLength >= mLength) aLength = mLength-pos; |
316 |
if (!aLength) return; |
317 |
if (pos + aLength < mLength) { |
318 |
memmove(&mContent[pos], &mContent[pos+aLength], mLength-aLength-pos); |
319 |
} |
320 |
realloc(mLength-aLength); |
321 |
} |
322 |
|
323 |
/** |
324 |
* Escapes certains characters in a c-style manner (all characters < 0x20). |
325 |
* @param aSpecialChars characters that need a \ |
326 |
* @param bit7 hex encode (\x..) characters >127 |
327 |
*/ |
328 |
void String::escape(const char *aSpecialChars, bool bit7) |
329 |
{ |
330 |
if (!mLength) return; |
331 |
String copy(this); |
332 |
realloc(mLength*4); |
333 |
realloc(escape_special((char*)mContent, mLength, copy.mContent, |
334 |
copy.mLength, aSpecialChars, bit7)); |
335 |
} |
336 |
|
337 |
/** |
338 |
* Search forwards for |c| in string |
339 |
* @param c character to search for |
340 |
* @param start first character position to look for |
341 |
* @returns position of character or number < 0 if not found |
342 |
*/ |
343 |
int String::findFirstChar(char c, int start) const |
344 |
{ |
345 |
if (!mLength) return -1; |
346 |
if (start >= mLength) return -1; |
347 |
if (start < 0) start = 0; |
348 |
for (int i=start; i<mLength; i++) { |
349 |
if (compareChar(mContent[i], c) == 0) return i; |
350 |
} |
351 |
return -1; |
352 |
} |
353 |
|
354 |
/** |
355 |
* Search forwards for |s| in string |
356 |
* @param s string to search for |
357 |
* @param start first character position to look for |
358 |
* @returns position of character or number < 0 if not found |
359 |
*/ |
360 |
int String::findFirstString(const String &s, int start) const |
361 |
{ |
362 |
if (!s.mLength) return 0; |
363 |
if (start < 0) start = 0; |
364 |
if (!mLength || (start+s.mLength > mLength)) return -1; |
365 |
for (int i=start; (i+s.mLength <= mLength); i++) { |
366 |
for (int j=i; (j>=0) && (j+s.mLength <= mLength) && (j-i < s.mLength); j++) { |
367 |
if (compareChar(mContent[j], s.mContent[j-i])) goto notfound; |
368 |
} |
369 |
return i; |
370 |
notfound:; |
371 |
} |
372 |
return -1; |
373 |
} |
374 |
|
375 |
/** |
376 |
* Search backwards for |c| in string |
377 |
* @param c character to search for |
378 |
* @param start first character position to look for |
379 |
* @returns position of character or number < 0 if not found |
380 |
*/ |
381 |
int String::findLastChar(char c, int start) const |
382 |
{ |
383 |
if (!mLength) return -1; |
384 |
if (start >= mLength) return -1; |
385 |
if (start < 0) start = mLength-1; |
386 |
for (int i=start; i>=0; i--) { |
387 |
if (compareChar(mContent[i], c) == 0) return i; |
388 |
} |
389 |
return -1; |
390 |
} |
391 |
|
392 |
/** |
393 |
* Search backwards for |s| in string |
394 |
* @param s string to search for |
395 |
* @param start first character position to look for |
396 |
* @returns position of character or number < 0 if not found |
397 |
*/ |
398 |
int String::findLastString(const String &s, int start) const |
399 |
{ |
400 |
if (!mLength) return -1; |
401 |
return -1; |
402 |
} |
403 |
|
404 |
/** |
405 |
* inserts |s| at postion |pos| in string. |
406 |
*/ |
407 |
void String::insert(const String &s, int pos) |
408 |
{ |
409 |
if (pos > mLength || pos < 0) throw MsgException("index out of bounds"); |
410 |
if (!s.mLength) return; |
411 |
realloc(mLength+s.mLength); |
412 |
if (mLength-s.mLength-pos > 0) |
413 |
memmove(&mContent[pos+s.mLength], &mContent[pos], mLength-s.mLength-pos); |
414 |
memmove(&mContent[pos], s.mContent, s.mLength); |
415 |
} |
416 |
|
417 |
bool String::instanceOf(ObjectID id) const |
418 |
{ |
419 |
if (id == getObjectID()) return true; |
420 |
return Object::instanceOf(id); |
421 |
} |
422 |
|
423 |
bool String::leftSplit(char chr, String &initial, String &rem) const |
424 |
{ |
425 |
int pivot = findFirstChar(chr); |
426 |
if (pivot < 0) { |
427 |
initial = *this; |
428 |
rem.clear(); |
429 |
return false; |
430 |
} |
431 |
subString(0, pivot, initial); |
432 |
subString(pivot+1, length(), rem); |
433 |
return true; |
434 |
} |
435 |
|
436 |
ObjectID String::getObjectID() const |
437 |
{ |
438 |
return OBJID_STRING; |
439 |
} |
440 |
|
441 |
void String::realloc(int aNewSize) |
442 |
{ |
443 |
mLength = aNewSize; |
444 |
mContent = (byte*)::realloc(mContent, mLength+1); |
445 |
mContent[mLength] = 0; |
446 |
/* if (mContent) { |
447 |
if (aNewSize) { |
448 |
mContent = (byte*)::realloc(mContent, aNewSize); |
449 |
} else { |
450 |
free(mContent); |
451 |
mContent = NULL; |
452 |
} |
453 |
} else { |
454 |
if (aNewSize) { |
455 |
mContent = (byte*)malloc(aNewSize); |
456 |
} |
457 |
} |
458 |
mLength = aNewSize;*/ |
459 |
} |
460 |
|
461 |
/*bool String::regexMatch(const String &aRegEx, Container *resultStrings, int maxRegExMatches) const |
462 |
{ |
463 |
const char *re = aRegEx.toString(); |
464 |
bool result = false; |
465 |
regex_t rx; |
466 |
|
467 |
int r = regcomp(&rx, re, REG_EXTENDED | ((compareChar('A','a')==0) ? REG_ICASE : 0)); |
468 |
if (r) throw MsgException("EINVAL"); |
469 |
|
470 |
regmatch_t pmatch[maxRegExMatches]; |
471 |
if (regexec(&rx, (char*)mContent, maxRegExMatches, pmatch, 0) != 0) return false; |
472 |
|
473 |
if (resultStrings) { |
474 |
for (int i=1; i < maxRegExMatches; i++) { |
475 |
if (pmatch[i].rm_so == -1) break; |
476 |
String *s = new String(); |
477 |
subString(pmatch[i].rm_so, pmatch[i].rm_eo-pmatch[i].rm_so, *s); |
478 |
resultStrings->insert(s); |
479 |
} |
480 |
} |
481 |
|
482 |
delete re; |
483 |
return result; |
484 |
}*/ |
485 |
|
486 |
/** |
487 |
* replaces all occurences of |what| in string with |with| |
488 |
* @param what searchstring |
489 |
* @param with replacement |
490 |
* @param start |
491 |
* @param maxReplacements |
492 |
* @returns number of replacements |
493 |
*/ |
494 |
int String::replace(const String &what, const String &with, int start, int maxReplacements) |
495 |
{ |
496 |
if (!maxReplacements) return 0; |
497 |
int whatlen = what.length(); |
498 |
int withlen = with.length(); |
499 |
int numRepl = 0; |
500 |
int p = findFirstString(what, start); |
501 |
while (p >= 0) { |
502 |
if (whatlen == withlen) { |
503 |
// replace in situ |
504 |
memmove(&mContent[p], with.mContent, withlen); |
505 |
} else { |
506 |
del(p, whatlen); |
507 |
insert(with, p); |
508 |
} |
509 |
numRepl++; |
510 |
if (maxReplacements > 0) { |
511 |
maxReplacements--; |
512 |
if (!maxReplacements) break; |
513 |
} |
514 |
p = findFirstString(what, p+withlen); |
515 |
} |
516 |
return numRepl; |
517 |
} |
518 |
|
519 |
bool String::rightSplit(char chr, String &initial, String &rem) const |
520 |
{ |
521 |
int pivot = findLastChar(chr); |
522 |
if (pivot < 0) { |
523 |
initial = *this; |
524 |
rem.clear(); |
525 |
return false; |
526 |
} |
527 |
subString(0, pivot, initial); |
528 |
subString(pivot+1, length(), rem); |
529 |
return true; |
530 |
} |
531 |
|
532 |
/** |
533 |
* assigns result to the substring with |
534 |
* |aLength| characters starting at |aStart|. |
535 |
* @param result will hold the result |
536 |
* @param aStart position of first character in new string |
537 |
* @param aLength number of characters to copy |
538 |
* @returns number of characters copied |
539 |
*/ |
540 |
int String::subString(int aStart, int aLength, String &result) const |
541 |
{ |
542 |
if (aLength <= 0 || aStart >= mLength) { |
543 |
result.clear(); |
544 |
return 0; |
545 |
} |
546 |
if (aStart+aLength >= mLength) aLength = mLength-aStart; |
547 |
result.assign(&mContent[aStart], aLength); |
548 |
return aLength; |
549 |
} |
550 |
|
551 |
/** |
552 |
* |
553 |
*/ |
554 |
void String::transformCase(StringCase c) |
555 |
{ |
556 |
if (c==stringCaseCaps) { |
557 |
} else { |
558 |
for (int i=0; i<mLength; i++) { |
559 |
if (c==stringCaseLower) { |
560 |
mContent[i]=tolower(mContent[i]); |
561 |
} else { |
562 |
mContent[i]=toupper(mContent[i]); |
563 |
} |
564 |
} |
565 |
} |
566 |
} |
567 |
|
568 |
/** |
569 |
* replace all characters of |inAlpha| in string with the corrensponding |
570 |
* characters of |outAlpha|. The lengths of |inAlpha| and |outAlpha| must |
571 |
* be identical. |inAlpha| should not contain the same characters multiple |
572 |
* times. |
573 |
*/ |
574 |
void String::translate(const String &inAlpha, const String &outAlpha) |
575 |
{ |
576 |
ASSERT(inAlpha.mLength == outAlpha.mLength); |
577 |
if (inAlpha.isEmpty() || isEmpty()) return; |
578 |
byte tr[256]; |
579 |
for (int i=0; i<256; i++) tr[i] = i; |
580 |
for (int i=0; i<inAlpha.mLength; i++) { |
581 |
tr[inAlpha.mContent[i]] = outAlpha.mContent[i]; |
582 |
} |
583 |
for (int i=0; i<mLength; i++) { |
584 |
mContent[i] = tr[mContent[i]]; |
585 |
} |
586 |
} |
587 |
|
588 |
/** |
589 |
* |
590 |
*/ |
591 |
int String::toArray(byte *buf, int buflen) const |
592 |
{ |
593 |
if (buflen <= 0) return 0; |
594 |
int r = MIN(mLength, buflen-1); |
595 |
memcpy(buf, mContent, r); |
596 |
return r; |
597 |
} |
598 |
|
599 |
/** |
600 |
* |
601 |
*/ |
602 |
bool String::toInt(int &i, int defaultbase) const |
603 |
{ |
604 |
const char *b = (const char*)mContent; |
605 |
uint64 u64; |
606 |
if (!parseIntStr(b, u64, defaultbase)) return false; |
607 |
if (b-(const char*)mContent != mLength) return false; |
608 |
i = (sint64)u64; |
609 |
return true; |
610 |
} |
611 |
|
612 |
/** |
613 |
* |
614 |
*/ |
615 |
bool String::toInt32(uint32 &u32, int defaultbase) const |
616 |
{ |
617 |
const char *b = (const char*)mContent; |
618 |
uint64 u64; |
619 |
if (!parseIntStr(b, u64, defaultbase)) return false; |
620 |
if (b-(const char*)mContent != mLength) return false; |
621 |
u32 = u64; |
622 |
return true; |
623 |
} |
624 |
|
625 |
/** |
626 |
* |
627 |
*/ |
628 |
bool String::toInt64(uint64 &u64, int defaultbase) const |
629 |
{ |
630 |
const char *b = (const char*)mContent; |
631 |
if (!parseIntStr(b, u64, defaultbase)) return false; |
632 |
return (b-(const char*)mContent == mLength); |
633 |
} |
634 |
|
635 |
int String::toString(char *buf, int buflen) const |
636 |
{ |
637 |
if (buflen <= 0) return 0; |
638 |
int r = MIN(mLength, buflen-1); |
639 |
for (int i=0; i<r; i++) { |
640 |
if (mContent[i]) { |
641 |
buf[i] = mContent[i]; |
642 |
} else { |
643 |
buf[i] = ' '; |
644 |
} |
645 |
} |
646 |
buf[r] = 0; |
647 |
return r; |
648 |
} |
649 |
|
650 |
/** |
651 |
* all '\0' will be converted to ' ' (space) |
652 |
* @returns new[] allocated char * |
653 |
*/ |
654 |
char *String::toString() const |
655 |
{ |
656 |
char *s = new char[mLength+1]; |
657 |
for (int i=0; i<mLength; i++) { |
658 |
if (mContent[i]) { |
659 |
s[i] = mContent[i]; |
660 |
} else { |
661 |
s[i] = ' '; |
662 |
} |
663 |
} |
664 |
s[mLength] = 0; |
665 |
return s; |
666 |
} |
667 |
|
668 |
/** |
669 |
* reverses action of <code>escape</code>. |
670 |
* Note that output isnt defined if string wasnt escaped before. |
671 |
*/ |
672 |
void String::unescape() |
673 |
{ |
674 |
String copy(this); |
675 |
realloc(unescape_special(mContent, mLength, (char*)copy.mContent)); |
676 |
} |
677 |
|
678 |
String &String::operator +=(char c) |
679 |
{ |
680 |
realloc(mLength+1); |
681 |
mContent[mLength-1] = c; |
682 |
return *this; |
683 |
} |
684 |
|
685 |
/* |
686 |
* global |
687 |
*/ |
688 |
|
689 |
String operator +(const String &s1, const String &s2) |
690 |
{ |
691 |
String temp = String(s1); |
692 |
temp.append(s2); |
693 |
return temp; |
694 |
} |
695 |
|
696 |
String operator +(const char *s1, const String &s2) |
697 |
{ |
698 |
String temp = String(s1); |
699 |
temp.append(s2); |
700 |
return temp; |
701 |
} |
702 |
|
703 |
/* |
704 |
* CLASS IString |
705 |
*/ |
706 |
|
707 |
IString::IString() |
708 |
{ |
709 |
} |
710 |
|
711 |
IString *IString::clone() const |
712 |
{ |
713 |
IString *r = new IString(); |
714 |
*r = *this; |
715 |
return r; |
716 |
} |
717 |
|
718 |
int IString::compareChar(char c1, char c2) const |
719 |
{ |
720 |
c1 = tolower(c1); |
721 |
c2 = tolower(c2); |
722 |
return String::compareChar(c1, c2); |
723 |
} |
724 |
|
725 |
bool IString::instanceOf(ObjectID id) const |
726 |
{ |
727 |
if (id == getObjectID()) return true; |
728 |
return String::instanceOf(id); |
729 |
} |
730 |
|
731 |
ObjectID IString::getObjectID() const |
732 |
{ |
733 |
return OBJID_ISTRING; |
734 |
} |
735 |
|