3 * Author : Chris Koeritz
5 * Copyright (c) 1992-$now By Author. This program is free software; you can *
6 * redistribute it and/or modify it under the terms of the GNU General Public *
7 * License as published by the Free Software Foundation; either version 2 of *
8 * the License or (at your option) any later version. This is online at: *
9 * http://www.fsf.org/copyleft/gpl.html *
10 * Please send any updates to: fred@gruntose.com *
14 #include "definitions.h"
15 #include "functions.h"
25 #define strcasecmp strcmpi
26 #define strncasecmp strnicmp
29 //#define DEBUG_STRING
30 // uncomment for debugging version.
33 // macro just documents a blank parameter in the code.
37 const int LONGEST_SPRINTF = 600; // the longest a simple sprintf can be here.
39 const char CASE_DIFFERENCE = char('A' - 'a');
40 // the measurement of the difference between upper and lower case.
42 // this factor is used to bias dynamic sprintfs for cases where the length
43 // is specified, but the actual string is shorter than that length.
44 const int MAX_FIELD_FUDGE_FACTOR = 64;
46 const abyte empty_char_star[] = { 0 };
47 // used to initialize empty strings.
51 bool astring_comparator(const astring &a, const astring &b) { return a.equal_to(b); }
53 int calculate_proper_length(int repeat) { return negative(repeat)? 1 : repeat + 1; }
58 : c_character_manager(1, empty_char_star),
59 c_held_string((char * const *)c_character_manager.internal_offset_mem())
62 astring::astring(const base_string &initial)
63 : c_character_manager(strlen(initial.observe()) + 1, (abyte *)initial.observe()),
64 c_held_string((char * const *)c_character_manager.internal_offset_mem())
67 astring::astring(char initial, int repeat)
68 : c_character_manager(calculate_proper_length(repeat))
70 if (!initial) initial = ' '; // for nulls, we use spaces.
71 int new_size = c_character_manager.length() - 1;
72 memset(c_character_manager.access(), initial, new_size);
73 c_character_manager.put(new_size, '\0');
74 c_held_string = (char * const *)c_character_manager.internal_offset_mem();
77 astring::astring(const astring &s1)
79 c_character_manager(s1.c_character_manager),
80 c_held_string((char * const *)c_character_manager.internal_offset_mem())
84 astring::astring(const char *initial)
85 : c_character_manager(calculate_proper_length(initial? int(strlen(initial)) : 0))
87 c_character_manager.put(0, '\0');
88 if (!initial) return; // bail because there's no string to copy.
89 strcpy(access(), initial);
90 c_held_string = (char * const *)c_character_manager.internal_offset_mem();
93 astring::astring(special_flag flag, const char *initial, ...)
94 : c_character_manager(1, empty_char_star),
95 c_held_string((char * const *)c_character_manager.internal_offset_mem())
98 if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) {
99 operator = (astring(astring::SPRINTF, "unknown flag %d", flag));
104 va_start(args, initial);
106 if (flag == UNTERMINATED) {
107 // special process for grabbing a string that has no terminating nil.
108 int length = va_arg(args, int); // get the length of the string out.
109 c_character_manager.reset(length, (abyte *)initial);
110 c_character_manager += abyte(0);
115 // only other flag currently supported is sprintf, so we do that...
116 base_sprintf(initial, args);
120 astring::~astring() { c_held_string = NIL; }
122 const astring &astring::empty_string() { return bogonic<astring>(); }
124 void astring::text_form(base_string &state_fill) const { state_fill.assign(*this); }
126 int astring::length() const { return c_character_manager.length() - 1; }
128 byte_array &astring::get_implementation() { return c_character_manager; }
130 char *astring::access() { return (char *)c_character_manager.access(); }
132 char astring::get(int index) const { return (char)c_character_manager.get(index); }
134 const char *astring::observe() const
135 { return (const char *)c_character_manager.observe(); }
137 bool astring::equal_to(const equalizable &s2) const
139 const astring *s2_cast = cast_or_throw(s2, *this);
140 return comparator(*s2_cast) == 0;
143 bool astring::less_than(const orderable &s2) const
145 const astring *s2_cast = dynamic_cast<const astring *>(&s2);
146 if (!s2_cast) throw "error: astring::<: unknown type";
147 return comparator(*s2_cast) < 0;
150 int astring::comparator(const astring &s2) const
151 { return strcmp(observe(), s2.observe()); }
153 bool astring::equal_to(const char *that) const
154 { return strcmp(observe(), that) == 0; }
156 bool astring::contains(const astring &to_find) const
157 { return (find(to_find, 0) < 0) ? false : true; }
159 astring &astring::operator += (const astring &s1)
160 { insert(length(), s1); return *this; }
162 void astring::shrink()
164 astring copy_of_this(observe());
165 c_character_manager.swap_contents(copy_of_this.c_character_manager);
168 astring &astring::sprintf(const char *initial, ...)
171 va_start(args, initial);
172 astring &to_return = base_sprintf(initial, args);
177 astring &astring::base_sprintf(const char *initial, va_list &args)
180 if (!initial) return *this; // skip null strings.
181 if (!initial[0]) return *this; // skip empty strings.
183 // these accumulate parts of the sprintf format within the loop.
184 char flag_chars[23], width_chars[23], precision_chars[23], modifier_chars[23];
186 // thanks for the inspiration to k&r page 156.
187 for (const char *traverser = initial; *traverser; traverser++) {
189 printf("index=%d, char=%c\n", traverser - initial, *traverser);
192 if (*traverser != '%') {
193 // not a special character, so just drop it in.
197 traverser++; // go to the next character.
199 printf("index=%d, char=%c\n", traverser - initial, *traverser);
201 if (*traverser == '%') {
202 // capture the "%%" style format specifier.
206 bool failure = false;
207 // becomes set to true if something didn't match in a necessary area.
209 seek_flag(traverser, flag_chars, failure);
215 seek_width(traverser, width_chars);
216 seek_precision(traverser, precision_chars);
217 seek_modifier(traverser, modifier_chars);
218 get_type_character(traverser, args, *this, flag_chars,
219 width_chars, precision_chars, modifier_chars);
224 void astring::seek_flag(const char *&traverser, char *flag_chars, bool &failure)
226 flag_chars[0] = '\0';
228 bool keep_going = true;
229 while (!failure && keep_going) {
230 switch (*traverser) {
231 case '-': case '+': case ' ': case '\011': case '#':
232 flag_chars[strlen(flag_chars) + 1] = '\0';
233 flag_chars[strlen(flag_chars)] = *traverser++;
236 // we found a character that doesn't belong in the flags.
242 if (strlen(flag_chars)) printf("[flag=%s]\n", flag_chars);
243 else printf("no flags\n");
247 void astring::seek_width(const char *&traverser, char *width_chars)
249 width_chars[0] = '\0';
250 bool no_more_nums = false;
251 bool first_num = true;
252 while (!no_more_nums) {
253 char wideness[2] = { *traverser, '\0' };
254 if (first_num && (wideness[0] == '0')) {
255 strcpy(width_chars, wideness);
257 } else if (first_num && (wideness[0] == '*') ) {
258 strcpy(width_chars, wideness);
261 } else if ( (wideness[0] <= '9') && (wideness[0] >= '0') ) {
263 strcat(width_chars, wideness);
265 } else no_more_nums = true;
269 if (strlen(width_chars)) printf("[width=%s]\n", width_chars);
270 else printf("no widths\n");
274 void astring::seek_precision(const char *&traverser, char *precision_chars)
276 precision_chars[0] = '\0';
277 if (*traverser != '.') return;
278 strcpy(precision_chars, ".");
280 bool no_more_nums = false;
281 bool first_num = true;
282 while (!no_more_nums) {
283 char preciseness[2] = { *traverser, '\0' };
284 if (first_num && (preciseness[0] == '0')) {
285 strcat(precision_chars, preciseness);
288 } else if (first_num && (preciseness[0] == '*') ) {
289 strcat(precision_chars, preciseness);
292 } else if ( (preciseness[0] <= '9') && (preciseness[0] >= '0') ) {
293 strcat(precision_chars, preciseness);
295 } else no_more_nums = true;
299 if (strlen(precision_chars)) printf("[precis=%s]\n", precision_chars);
300 else printf("no precision\n");
304 void astring::seek_modifier(const char *&traverser, char *modifier_chars)
306 modifier_chars[0] = '\0';
307 switch (*traverser) {
308 case 'F': case 'N': case 'h': case 'l': case 'L': {
309 modifier_chars[strlen(modifier_chars) + 1] = '\0';
310 modifier_chars[strlen(modifier_chars)] = *traverser++;
315 if (strlen(modifier_chars)) printf("[mod=%s]\n", modifier_chars);
316 else printf("no modifiers\n");
320 void astring::get_type_character(const char * &traverser, va_list &args,
321 astring &output_string, const char *flag_chars, const char *width_chars,
322 const char *precision_chars, const char *modifier_chars)
324 char formatting[120];
325 strcpy(formatting, "%");
326 strcat(formatting, flag_chars);
327 strcat(formatting, width_chars);
328 strcat(formatting, precision_chars);
329 strcat(formatting, modifier_chars);
330 char tmposh[2] = { *traverser, '\0' };
331 strcat(formatting, tmposh);
333 printf("format: %s\n", formatting);
336 enum argument_size { bits_8, bits_16, bits_32, bits_64, bits_80 };
337 bool ints_are_32_bits;
339 ints_are_32_bits = true;
340 #elif defined(__OS2__)
341 ints_are_32_bits = true;
342 #elif defined(__MSDOS__)
343 ints_are_32_bits = false;
344 #elif defined(__WIN32__)
345 ints_are_32_bits = false;
347 ints_are_32_bits = true;
349 argument_size next_argument;
350 bool use_dynamic_sprintf = false; // for dynamic printing of strings only.
351 // get the type character first and ensure it's valid.
352 switch (*traverser) {
353 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
354 next_argument = bits_16;
355 if (ints_are_32_bits) next_argument = bits_32;
357 case 'f': case 'e': case 'g': case 'E': case 'G':
358 next_argument = bits_64;
361 next_argument = bits_8;
364 next_argument = bits_32;
365 use_dynamic_sprintf = true;
368 next_argument = bits_32; //?????
371 next_argument = bits_32; //????
374 // this is an error; the character is not recognized, so spew out
375 // any characters accumulated so far as just themselves.
377 printf("failure in type char: %c\n", *traverser);
379 output_string += formatting;
382 /* hmmm: not supported yet.
383 if (width_chars && (width_chars[0] == '*')) {
385 if (precision_chars && (precision_chars[0] == '*')) {
388 if (strlen(modifier_chars)) {
389 switch (modifier_chars[0]) {
390 case 'N': // near pointer.
391 next_argument = bits_16;
392 if (ints_are_32_bits) next_argument = bits_32;
394 case 'F': // far pointer.
395 next_argument = bits_32;
397 case 'h': // short int.
398 next_argument = bits_16;
401 next_argument = bits_32;
403 case 'L': // long double;
404 next_argument = bits_80;
407 // a failure has occurred because the modifier character is not
408 // one of the recognized values. everything is just spewed out.
410 printf("failure in modifier: %s\n", modifier_chars);
412 output_string += formatting;
416 // action time: the output string is given a tasty value.
417 char temp[LONGEST_SPRINTF];
418 char *temp2 = NIL; // for dynamic only.
419 switch (next_argument) {
420 //hmmm: this switch is where support would need to be added for having two
421 // arguments (for the '*' case).
422 case bits_8: case bits_16:
423 if (ints_are_32_bits) ::sprintf(temp, formatting, va_arg(args, long));
424 else ::sprintf(temp, formatting, va_arg(args, int));
427 if (use_dynamic_sprintf) {
428 // currently we only do dynamic sprintf for strings.
429 char *to_print = va_arg(args, char *);
430 // check if it's valid and if we really need to do it dynamically.
432 // bogus string; put in a complaint.
433 use_dynamic_sprintf = false;
434 ::sprintf(temp, "{error:parm=NIL}");
435 } else if (strlen(to_print) < LONGEST_SPRINTF - 2) {
436 // we're within our bounds, plus some safety room, so just do a
438 use_dynamic_sprintf = false;
439 ::sprintf(temp, formatting, to_print);
441 // it's too long, so we definitely need to do it dynamically.
442 temp2 = new char[strlen(to_print) + MAX_FIELD_FUDGE_FACTOR];
443 ::sprintf(temp2, formatting, to_print);
445 } else ::sprintf(temp, formatting, va_arg(args, void *));
448 ::sprintf(temp, formatting, va_arg(args, double));
451 ::sprintf(temp, formatting, va_arg(args, long double));
454 if (use_dynamic_sprintf) {
455 output_string += temp2;
457 } else output_string += temp;
460 //hmmm: de-redundify this function, which is identical to the constructor.
461 void astring::reset(special_flag flag, const char *initial, ...)
463 reset(); // clear the string out.
464 if (!initial) return;
465 if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) {
466 operator = (astring(astring::SPRINTF, "unknown flag %d", flag));
471 va_start(args, initial);
473 if (flag == UNTERMINATED) {
474 // special process for grabbing a string that has no terminating nil.
475 int length = va_arg(args, int); // get the length of the string out.
476 c_character_manager.reset(length, (abyte *)initial);
477 c_character_manager += abyte(0);
482 // only other flag currently supported is sprintf, so we do that...
483 base_sprintf(initial, args);
487 void astring::pad(int len, char padding)
489 if (length() >= len) return;
490 byte_array pad(len - length());
491 memset(pad.access(), padding, pad.length());
492 operator += (astring(UNTERMINATED, (char *)pad.observe(), pad.length()));
495 void astring::trim(int len)
497 if (length() <= len) return;
501 astring &astring::operator = (const astring &s1)
504 c_character_manager = s1.c_character_manager;
508 astring &astring::operator = (const char *s1)
515 void astring::zap(int position1, int position2)
517 bounds_return(position1, 0, end(), );
518 bounds_return(position2, 0, end(), );
519 c_character_manager.zap(position1, position2);
522 void astring::to_lower()
524 for (int i = 0; i < length(); i++)
525 if ( (get(i) >= 'A') && (get(i) <= 'Z') )
526 c_character_manager.put(i, char(get(i) - CASE_DIFFERENCE));
529 void astring::to_upper()
531 for (int i = 0; i < length(); i++)
532 if ( (get(i) >= 'a') && (get(i) <= 'z') )
533 c_character_manager.put(i, char(get(i) + CASE_DIFFERENCE));
536 astring astring::lower() const
538 astring to_return(*this);
539 to_return.to_lower();
543 astring astring::upper() const
545 astring to_return(*this);
546 to_return.to_upper();
550 void astring::copy(char *array_to_stuff, int how_many) const
552 if (!array_to_stuff) return;
553 array_to_stuff[0] = '\0';
554 if ( (how_many <= 0) || (length() <= 0) ) return;
555 strncpy(array_to_stuff, observe(), minimum(how_many, int(length())));
556 array_to_stuff[minimum(how_many, int(length()))] = '\0';
559 bool astring::iequals(const astring &that) const
560 { return strcasecmp(observe(), that.observe()) == 0; }
562 bool astring::iequals(const char *that) const
563 { return strcasecmp(observe(), that) == 0; }
565 int astring::ifind(char to_find, int position, bool reverse) const
566 { return char_find(to_find, position, reverse, false); }
568 int astring::find(char to_find, int position, bool reverse) const
569 { return char_find(to_find, position, reverse, true); }
571 int astring::find_any(const char *to_find, int position, bool reverse) const
572 { return char_find_any(to_find, position, reverse, true); }
574 int astring::ifind_any(const char *to_find, int position, bool reverse) const
575 { return char_find_any(to_find, position, reverse, false); }
577 int astring::find_non_match(const char *to_find, int position,
579 { return char_find_any(to_find, position, reverse, false, true); }
581 char simple_lower(char input)
583 if ( (input <= 'Z') && (input >= 'A') ) return input - CASE_DIFFERENCE;
587 int astring::char_find(char to_find, int position, bool reverse,
588 bool case_sense) const
590 if (position < 0) return common::OUT_OF_RANGE;
591 if (position > end()) return common::OUT_OF_RANGE;
593 for (int i = position; i >= 0; i--) {
594 if (case_sense && (get(i) == to_find)) return i;
595 else if (simple_lower(get(i)) == simple_lower(to_find)) return i;
599 const char *const pos = strchr(observe() + position, to_find);
600 if (pos) return int(pos - observe());
602 for (int i = position; i < length(); i++)
603 if (simple_lower(get(i)) == simple_lower(to_find)) return i;
606 return common::NOT_FOUND;
609 bool imatches_any(char to_check, const astring &list)
611 for (int i = 0; i < list.length(); i++)
612 if (simple_lower(to_check) == simple_lower(list[i])) return true;
616 bool matches_any(char to_check, const astring &list)
618 for (int i = 0; i < list.length(); i++)
619 if (to_check == list[i]) return true;
623 bool matches_none(char to_check, const astring &list)
625 bool saw_match = false;
626 for (int i = 0; i < list.length(); i++)
627 if (to_check == list[i]) {
634 int astring::char_find_any(const astring &to_find, int position, bool reverse,
635 bool case_sense, bool invert_find) const
637 if (position < 0) return common::OUT_OF_RANGE;
638 if (position > end()) return common::OUT_OF_RANGE;
640 for (int i = position; i >= 0; i--) {
642 if (case_sense && matches_any(get(i), to_find)) return i;
643 else if (imatches_any(get(i), to_find)) return i;
645 //printf("rev posn=%d char=%c", i, get(i));
646 // case-sensitivity is not used for inverted finds.
647 if (matches_none(get(i), to_find)) return i;
651 for (int i = position; i < length(); i++) {
653 if (case_sense && matches_any(get(i), to_find)) return i;
654 else if (imatches_any(get(i), to_find)) return i;
656 // case-sensitivity is not used for inverted finds.
657 //printf("fwd posn=%d char=%c", i, get(i));
658 if (matches_none(get(i), to_find)) return i;
662 return common::NOT_FOUND;
665 int astring::find(const astring &to_find, int posn, bool reverse) const
666 { return str_find(to_find, posn, reverse, true); }
668 int astring::ifind(const astring &to_find, int posn, bool reverse) const
669 { return str_find(to_find, posn, reverse, false); }
671 int astring::str_find(const astring &to_find, int posn, bool reverse,
672 bool case_sense) const
674 bounds_return(posn, 0, end(), common::OUT_OF_RANGE);
675 if (!to_find.length()) return common::BAD_INPUT;
677 // skip some steps by finding the first place that the first character of
678 // the string resides in our string.
680 posn = find(to_find[0], posn, reverse);
681 else posn = ifind(to_find[0], posn, reverse);
682 if (posn < 0) return common::NOT_FOUND;
684 //hmmm: there is a better way to do this loop in terms of the number of
685 // comparisons performed. knuth morris pratt algorithm?
687 //hmmm: this could use strncmp too?
689 if (posn > length() - to_find.length())
690 posn = length() - to_find.length();
691 for (int i = posn; i >= 0; i--)
692 if (!memcmp((void *)&observe()[i], (void *)to_find.observe(),
696 const int find_len = to_find.length();
697 const int str_len = length();
698 const char first_char = to_find[0];
699 bounds_return(posn, 0, str_len - find_len, common::OUT_OF_RANGE);
700 for (int i = posn - 1;
701 ( ( (i = find(first_char, i + 1)) >= 0)
702 && (str_len - i >= find_len) ); no_increment) {
703 if (!memcmp((void *)&observe()[i], (void *)to_find.observe(),
709 // not case-sensitive.
711 if (posn > length() - to_find.length())
712 posn = length() - to_find.length();
713 for (int i = posn; i >= 0; i--)
714 if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length()))
717 bounds_return(posn, 0, length() - to_find.length(), common::OUT_OF_RANGE);
718 for (int i = posn; i < length() - to_find.length() + 1; i++)
719 if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length()))
723 return common::NOT_FOUND;
726 astring astring::operator + (const astring &s1) const
728 astring to_return(*this);
733 char &astring::operator [] (int position)
735 if (position < 0) position = 0;
736 if (position > end()) position = 0;
737 abyte &found = c_character_manager.use(position);
738 char &to_return = *((char *)(&found));
742 const char &astring::operator [] (int position) const
744 if (position < 0) position = 0;
745 if (position > end()) position = 0;
746 const abyte &found = c_character_manager.get(position);
747 const char &to_return = *((const char *)(&found));
751 int astring::convert(int default_value) const
753 if (!length()) return default_value;
755 int fields = sscanf(observe(), "%d", &to_return);
756 if (fields < 1) return default_value;
760 long astring::convert(long default_value) const
762 if (!length()) return default_value;
764 int fields = sscanf(observe(), "%ld", &to_return);
765 if (fields < 1) return default_value;
769 float astring::convert(float default_value) const
771 if (!length()) return default_value;
773 int fields = sscanf(observe(), "%f", &to_return);
774 if (fields < 1) return default_value;
778 double astring::convert(double default_value) const
780 if (!length()) return default_value;
782 int fields = sscanf(observe(), "%lf", &to_return);
783 if (fields < 1) return default_value;
787 astring &astring::operator += (const char *s1)
789 if (!s1) return *this;
791 c_character_manager.insert(len, int(strlen(s1)));
792 memmove((char *)&c_character_manager[len], s1, int(strlen(s1)));
796 astring &astring::operator += (char s1)
799 c_character_manager.insert(len, 1);
800 c_character_manager.put(len, s1);
804 bool astring::compare(const astring &to_compare, int start_first,
805 int start_second, int count, bool case_sensitive) const
807 bounds_return(start_first, 0, end(), false);
808 bounds_return(start_second, 0, to_compare.end(), false);
809 bounds_return(start_first + count, start_first, length(), false);
810 bounds_return(start_second + count, start_second, to_compare.length(), false);
812 if (!case_sensitive) {
813 return !strncasecmp(&observe()[start_first],
814 &to_compare.observe()[start_second], count);
816 return !memcmp((void *)&observe()[start_first],
817 (void *)&to_compare.observe()[start_second], count);
822 int astring::icompare(const char *to_compare, int length_in) const
824 if (!length_in) return 0; // nothing is equal to nothing.
825 int real_len = length_in;
826 // if they're passing a negative length, we use the full length.
827 if (negative(length_in))
829 // if we have no length, make the obvious returns now.
830 int to_compare_len = int(strlen(to_compare));
831 if (!real_len) return to_compare_len? -1 : 0;
832 // if the second string is empty, it's always less than the non-empty.
833 if (!to_compare_len) return 1;
834 int to_return = strncasecmp(observe(), to_compare, real_len);
835 if (negative(length_in) && !to_return && (to_compare_len > length()) ) {
836 // catch special case for default length when the two are equal except
837 // that the second string is longer--this means the first is less than
838 // second, not equal.
846 bool astring::oy_icompare(const astring &to_compare, int start_first,
847 int start_second, int count) const
849 bounds_return(start_first, 0, end(), false);
850 bounds_return(start_second, 0, to_compare.end(), false);
851 bounds_return(start_first + count, start_first, length(), false);
852 bounds_return(start_second + count, start_second, to_compare.length(), false);
853 const char *actual_first = this->observe() + start_first;
854 const char *actual_second = to_compare.observe() + start_second;
855 return !strncasecmp(actual_first, actual_second, count);
859 bool astring::substring(astring &target, int start, int bender) const
862 if (bender < start) return false;
863 const int last = end(); // final position that's valid in the string.
864 bounds_return(start, 0, last, false);
865 bounds_return(bender, 0, last, false);
866 target.reset(UNTERMINATED, observe() + start, bender - start + 1);
870 astring astring::substring(int start, int end) const
873 substring(to_return, start, end);
877 astring astring::middle(int start, int count)
878 { return substring(start, start + count - 1); }
880 astring astring::left(int count)
881 { return substring(0, count - 1); }
883 astring astring::right(int count)
884 { return substring(end() - count + 1, end()); }
886 void astring::insert(int position, const astring &to_insert)
888 bounds_return(position, 0, length(), );
889 if (this == &to_insert) {
890 astring copy_of_me(to_insert);
891 insert(position, copy_of_me); // not recursive because no longer == me.
893 c_character_manager.insert(position, to_insert.length());
894 c_character_manager.overwrite(position, to_insert.c_character_manager,
899 bool astring::replace(const astring &tag, const astring &replacement)
901 int where = find(tag);
902 if (negative(where)) return false;
903 zap(where, where + tag.end());
904 insert(where, replacement);
908 bool astring::replace_all(const astring &to_replace, const astring &new_string)
910 bool did_any = false;
911 for (int i = 0; i < length(); i++) {
912 int indy = find(to_replace, i);
913 if (negative(indy)) break; // get out since there are no more matches.
914 i = indy; // update our position to where we found the string.
915 zap(i, i + to_replace.length() - 1); // remove old string.
916 insert(i, new_string); // plug the new string into the old position.
917 i += new_string.length() - 1; // jump past what we replaced.
923 bool astring::replace_all(char to_replace, char new_char)
925 bool did_any = false;
926 for (int i = 0; i < length(); i++) {
927 if (get(i) == to_replace) {
935 bool astring::matches(const astring &match_list, char to_match)
937 for (int i = 0; i < match_list.length(); i++)
938 if (to_match == match_list.get(i)) return true;
942 void astring::strip(const astring &strip_list, how_to_strip way)
944 if (way & FROM_FRONT)
945 while (length() && matches(strip_list, get(0)))
949 while (length() && matches(strip_list, get(end())))
953 int astring::packed_size() const { return length() + 1; }
955 void astring::pack(byte_array &target) const
956 { attach(target, (char *)c_character_manager.observe()); }
958 bool astring::unpack(byte_array &source)
959 { return detach(source, *this); }
961 ///int astring::icompare(const astring &to_compare, int length_in) const
962 ///{ return icompare(to_compare.observe(), length_in); }
965 int astring::slow_strncasecmp(const char *first, const char *second, int length)
967 int len1 = int(strlen(first));
968 int len2 = int(strlen(second));
969 if (!length) return 0; // no characters are equal to none.
970 if (!len1 && !len2) return 0; // equal as empty.
971 if (!len1 && len2) return -1; // first < second.
972 if (len1 && !len2) return 1; // first > second.
973 if (positive(length)) {
974 len1 = minimum(length, len1);
975 len2 = minimum(length, len2);
977 for (int i = 0; i < len1; i++) {
978 if (i > len2 - 1) return 1; // first > second, had more length.
979 if (simple_lower(first[i]) < simple_lower(second[i]))
980 return -1; // first < second.
981 if (simple_lower(first[i]) > simple_lower(second[i]))
982 return 1; // first > second.
984 // at this point we know second is equal to first up to the length of
986 if (len2 > len1) return -1; // second was longer and therefore greater.
993 a_sprintf::a_sprintf() : astring() {}
995 a_sprintf::a_sprintf(const astring &s) : astring(s) {}
997 a_sprintf::a_sprintf(const char *initial, ...)
1000 if (!initial) return;
1002 va_start(args, initial);
1003 base_sprintf(initial, args);
1009 void attach(byte_array &packed_form, const char *to_attach)
1011 const int len = int(strlen(to_attach));
1012 const int old_pos = packed_form.last();
1013 packed_form.insert(old_pos + 1, len + 1);
1014 memmove((char *)packed_form.observe() + old_pos + 1, to_attach, len + 1);
1017 bool detach(byte_array &packed_form, astring &to_detach)
1019 if (!packed_form.length()) return false;
1020 // locate the zero termination if possible.
1021 const void *zero_posn = memchr(packed_form.observe(), '\0',
1022 packed_form.length());
1023 // make sure we could find the zero termination.
1025 // nope, never saw a zero. good thing we checked.
1029 // set the string up using a standard constructor since we found the zero
1030 // position; we know the string constructor will be happy.
1031 to_detach = (char *)packed_form.observe();
1032 // compute the length of the string we found based on the position of the
1034 int find_len = int((abyte *)zero_posn - packed_form.observe());
1035 // whack the portion of the array that we consumed.
1036 packed_form.zap(0, find_len);
1042 // contract fulfillment area.
1044 base_string &astring::concatenate_string(const base_string &s)
1046 const astring *cast = dynamic_cast<const astring *>(&s);
1047 if (cast) *this += *cast;
1048 else *this += astring(s.observe());
1052 base_string &astring::concatenate_char(char c)
1058 base_string &astring::assign(const base_string &s)
1060 const astring *cast = dynamic_cast<const astring *>(&s);
1061 if (cast) *this = *cast;
1062 else *this = astring(s.observe());
1066 base_string &astring::upgrade(const char *s)
1072 bool astring::sub_string(base_string &target, int start, int end) const
1074 astring *cast = dynamic_cast<astring *>(&target);
1075 if (!cast) throw "error: astring::sub_string: unknown type";
1076 return substring(*cast, start, end);
1079 bool astring::sub_compare(const base_string &to_compare, int start_first,
1080 int start_second, int count, bool case_sensitive) const
1082 const astring *cast = dynamic_cast<const astring *>(&to_compare);
1083 if (cast) return compare(*cast, start_first, start_second, count, case_sensitive);
1084 else return compare(astring(to_compare.observe()), start_first, start_second,
1085 count, case_sensitive);
1088 void astring::insert(int position, const base_string &to_insert)
1090 const astring *cast = dynamic_cast<const astring *>(&to_insert);
1091 if (cast) this->insert(position, *cast);
1092 else this->insert(position, astring(to_insert.observe()));