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;
73 /*hmmm: eclipse was badgering me into adding types on this, but it's not really an error in my code seemingly.
74 * eclipse seems to want a ? type in the last parameter, not a size_t or int. why? doesn't it know size_t?
76 memset((void *)c_character_manager.access(), (int)initial, (size_t)new_size);
77 c_character_manager.put(new_size, '\0');
78 c_held_string = (char * const *)c_character_manager.internal_offset_mem();
81 astring::astring(const astring &s1)
83 c_character_manager(s1.c_character_manager),
84 c_held_string((char * const *)c_character_manager.internal_offset_mem())
88 astring::astring(const char *initial)
89 : c_character_manager(calculate_proper_length(initial? int(strlen(initial)) : 0))
91 c_character_manager.put(0, '\0');
92 if (!initial) return; // bail because there's no string to copy.
93 strcpy(access(), initial);
94 c_held_string = (char * const *)c_character_manager.internal_offset_mem();
97 astring::astring(special_flag flag, const char *initial, ...)
98 : c_character_manager(1, empty_char_star),
99 c_held_string((char * const *)c_character_manager.internal_offset_mem())
101 if (!initial) return;
102 if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) {
103 operator = (astring(astring::SPRINTF, "unknown flag %d", flag));
108 va_start(args, initial);
110 if (flag == UNTERMINATED) {
111 // special process for grabbing a string that has no terminating nil.
112 int length = va_arg(args, int); // get the length of the string out.
113 c_character_manager.reset(length, (abyte *)initial);
114 c_character_manager += abyte(0);
119 // only other flag currently supported is sprintf, so we do that...
120 base_sprintf(initial, args);
124 astring::~astring() { c_held_string = NULL_POINTER; }
126 const astring &astring::empty_string() { return bogonic<astring>(); }
128 void astring::text_form(base_string &state_fill) const { state_fill.assign(*this); }
130 int astring::length() const { return c_character_manager.length() - 1; }
132 byte_array &astring::get_implementation() { return c_character_manager; }
134 char *astring::access() { return (char *)c_character_manager.access(); }
136 char astring::get(int index) const { return (char)c_character_manager.get(index); }
138 const char *astring::observe() const
139 { return (const char *)c_character_manager.observe(); }
141 bool astring::equal_to(const equalizable &s2) const
143 const astring *s2_cast = cast_or_throw(s2, *this);
144 return comparator(*s2_cast) == 0;
147 bool astring::less_than(const orderable &s2) const
149 const astring *s2_cast = dynamic_cast<const astring *>(&s2);
150 if (!s2_cast) throw "error: astring::<: unknown type";
151 return comparator(*s2_cast) < 0;
154 int astring::comparator(const astring &s2) const
155 { return strcmp(observe(), s2.observe()); }
157 bool astring::equal_to(const char *that) const
158 { return strcmp(observe(), that) == 0; }
160 bool astring::contains(const astring &to_find) const
161 { return (find(to_find, 0) < 0) ? false : true; }
163 astring &astring::operator += (const astring &s1)
164 { insert(length(), s1); return *this; }
166 void astring::shrink()
168 astring copy_of_this(observe());
169 c_character_manager.swap_contents(copy_of_this.c_character_manager);
172 astring &astring::sprintf(const char *initial, ...)
175 va_start(args, initial);
176 astring &to_return = base_sprintf(initial, args);
181 astring &astring::base_sprintf(const char *initial, va_list &args)
184 if (!initial) return *this; // skip null strings.
185 if (!initial[0]) return *this; // skip empty strings.
187 // these accumulate parts of the sprintf format within the loop.
188 char flag_chars[23], width_chars[23], precision_chars[23], modifier_chars[23];
190 // thanks for the inspiration to k&r page 156.
191 for (const char *traverser = initial; *traverser; traverser++) {
193 printf("index=%d, char=%c\n", traverser - initial, *traverser);
196 if (*traverser != '%') {
197 // not a special character, so just drop it in.
201 traverser++; // go to the next character.
203 printf("index=%d, char=%c\n", traverser - initial, *traverser);
205 if (*traverser == '%') {
206 // capture the "%%" style format specifier.
210 bool failure = false;
211 // becomes set to true if something didn't match in a necessary area.
213 seek_flag(traverser, flag_chars, failure);
219 seek_width(traverser, width_chars);
220 seek_precision(traverser, precision_chars);
221 seek_modifier(traverser, modifier_chars);
222 get_type_character(traverser, args, *this, flag_chars,
223 width_chars, precision_chars, modifier_chars);
228 void astring::seek_flag(const char *&traverser, char *flag_chars, bool &failure)
230 flag_chars[0] = '\0';
232 bool keep_going = true;
233 while (!failure && keep_going) {
234 switch (*traverser) {
235 case '-': case '+': case ' ': case '\011': case '#':
236 flag_chars[strlen(flag_chars) + 1] = '\0';
237 flag_chars[strlen(flag_chars)] = *traverser++;
240 // we found a character that doesn't belong in the flags.
246 if (strlen(flag_chars)) printf("[flag=%s]\n", flag_chars);
247 else printf("no flags\n");
251 void astring::seek_width(const char *&traverser, char *width_chars)
253 width_chars[0] = '\0';
254 bool no_more_nums = false;
255 bool first_num = true;
256 while (!no_more_nums) {
257 char wideness[2] = { *traverser, '\0' };
258 if (first_num && (wideness[0] == '0')) {
259 strcpy(width_chars, wideness);
261 } else if (first_num && (wideness[0] == '*') ) {
262 strcpy(width_chars, wideness);
265 } else if ( (wideness[0] <= '9') && (wideness[0] >= '0') ) {
267 strcat(width_chars, wideness);
269 } else no_more_nums = true;
273 if (strlen(width_chars)) printf("[width=%s]\n", width_chars);
274 else printf("no widths\n");
278 void astring::seek_precision(const char *&traverser, char *precision_chars)
280 precision_chars[0] = '\0';
281 if (*traverser != '.') return;
282 strcpy(precision_chars, ".");
284 bool no_more_nums = false;
285 bool first_num = true;
286 while (!no_more_nums) {
287 char preciseness[2] = { *traverser, '\0' };
288 if (first_num && (preciseness[0] == '0')) {
289 strcat(precision_chars, preciseness);
292 } else if (first_num && (preciseness[0] == '*') ) {
293 strcat(precision_chars, preciseness);
296 } else if ( (preciseness[0] <= '9') && (preciseness[0] >= '0') ) {
297 strcat(precision_chars, preciseness);
299 } else no_more_nums = true;
303 if (strlen(precision_chars)) printf("[precis=%s]\n", precision_chars);
304 else printf("no precision\n");
308 void astring::seek_modifier(const char *&traverser, char *modifier_chars)
310 modifier_chars[0] = '\0';
311 switch (*traverser) {
312 case 'F': case 'N': case 'h': case 'l': case 'L': {
313 modifier_chars[strlen(modifier_chars) + 1] = '\0';
314 modifier_chars[strlen(modifier_chars)] = *traverser++;
319 if (strlen(modifier_chars)) printf("[mod=%s]\n", modifier_chars);
320 else printf("no modifiers\n");
324 void astring::get_type_character(const char * &traverser, va_list &args,
325 astring &output_string, const char *flag_chars, const char *width_chars,
326 const char *precision_chars, const char *modifier_chars)
328 char formatting[120];
329 strcpy(formatting, "%");
330 strcat(formatting, flag_chars);
331 strcat(formatting, width_chars);
332 strcat(formatting, precision_chars);
333 strcat(formatting, modifier_chars);
334 char tmposh[2] = { *traverser, '\0' };
335 strcat(formatting, tmposh);
337 printf("format: %s\n", formatting);
340 enum argument_size { bits_8, bits_16, bits_32, bits_64, bits_80 };
341 bool ints_are_32_bits;
343 ints_are_32_bits = true;
344 #elif defined(__OS2__)
345 ints_are_32_bits = true;
346 #elif defined(__MSDOS__)
347 ints_are_32_bits = false;
348 #elif defined(__WIN32__)
349 ints_are_32_bits = false;
351 ints_are_32_bits = true;
353 argument_size next_argument;
354 bool use_dynamic_sprintf = false; // for dynamic printing of strings only.
355 // get the type character first and ensure it's valid.
356 switch (*traverser) {
357 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
358 next_argument = bits_16;
359 if (ints_are_32_bits) next_argument = bits_32;
361 case 'f': case 'e': case 'g': case 'E': case 'G':
362 next_argument = bits_64;
365 next_argument = bits_8;
368 next_argument = bits_32;
369 use_dynamic_sprintf = true;
372 next_argument = bits_32; //?????
375 next_argument = bits_32; //????
378 // this is an error; the character is not recognized, so spew out
379 // any characters accumulated so far as just themselves.
381 printf("failure in type char: %c\n", *traverser);
383 output_string += formatting;
386 /* hmmm: not supported yet.
387 if (width_chars && (width_chars[0] == '*')) {
389 if (precision_chars && (precision_chars[0] == '*')) {
392 if (strlen(modifier_chars)) {
393 switch (modifier_chars[0]) {
394 case 'N': // near pointer.
395 next_argument = bits_16;
396 if (ints_are_32_bits) next_argument = bits_32;
398 case 'F': // far pointer.
399 next_argument = bits_32;
401 case 'h': // short int.
402 next_argument = bits_16;
405 next_argument = bits_32;
407 case 'L': // long double;
408 next_argument = bits_80;
411 // a failure has occurred because the modifier character is not
412 // one of the recognized values. everything is just spewed out.
414 printf("failure in modifier: %s\n", modifier_chars);
416 output_string += formatting;
420 // action time: the output string is given a tasty value.
421 char temp[LONGEST_SPRINTF];
422 char *temp2 = NULL_POINTER; // for dynamic only.
423 switch (next_argument) {
424 //hmmm: this switch is where support would need to be added for having two
425 // arguments (for the '*' case).
426 case bits_8: case bits_16:
427 if (ints_are_32_bits) ::sprintf(temp, formatting, va_arg(args, long));
428 else ::sprintf(temp, formatting, va_arg(args, int));
431 if (use_dynamic_sprintf) {
432 // currently we only do dynamic sprintf for strings.
433 char *to_print = va_arg(args, char *);
434 // check if it's valid and if we really need to do it dynamically.
436 // bogus string; put in a complaint.
437 use_dynamic_sprintf = false;
438 ::sprintf(temp, "{error:parm=NULL_POINTER}");
439 } else if (strlen(to_print) < LONGEST_SPRINTF - 2) {
440 // we're within our bounds, plus some safety room, so just do a
442 use_dynamic_sprintf = false;
443 ::sprintf(temp, formatting, to_print);
445 // it's too long, so we definitely need to do it dynamically.
446 temp2 = new char[strlen(to_print) + MAX_FIELD_FUDGE_FACTOR];
447 ::sprintf(temp2, formatting, to_print);
449 } else ::sprintf(temp, formatting, va_arg(args, void *));
452 ::sprintf(temp, formatting, va_arg(args, double));
455 ::sprintf(temp, formatting, va_arg(args, long double));
458 if (use_dynamic_sprintf) {
459 output_string += temp2;
461 } else output_string += temp;
464 //hmmm: de-redundify this function, which is identical to the constructor.
465 void astring::reset(special_flag flag, const char *initial, ...)
467 reset(); // clear the string out.
468 if (!initial) return;
469 if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) {
470 operator = (astring(astring::SPRINTF, "unknown flag %d", flag));
475 va_start(args, initial);
477 if (flag == UNTERMINATED) {
478 // special process for grabbing a string that has no terminating nil.
479 int length = va_arg(args, int); // get the length of the string out.
480 c_character_manager.reset(length, (abyte *)initial);
481 c_character_manager += abyte(0);
486 // only other flag currently supported is sprintf, so we do that...
487 base_sprintf(initial, args);
491 void astring::pad(int len, char padding)
493 if (length() >= len) return;
494 byte_array pad(len - length());
495 memset(pad.access(), padding, pad.length());
496 operator += (astring(UNTERMINATED, (char *)pad.observe(), pad.length()));
499 void astring::trim(int len)
501 if (length() <= len) return;
505 astring &astring::operator = (const astring &s1)
508 c_character_manager = s1.c_character_manager;
512 astring &astring::operator = (const char *s1)
519 void astring::zap(int position1, int position2)
521 bounds_return(position1, 0, end(), );
522 bounds_return(position2, 0, end(), );
523 c_character_manager.zap(position1, position2);
526 void astring::to_lower()
528 for (int i = 0; i < length(); i++)
529 if ( (get(i) >= 'A') && (get(i) <= 'Z') )
530 c_character_manager.put(i, char(get(i) - CASE_DIFFERENCE));
533 void astring::to_upper()
535 for (int i = 0; i < length(); i++)
536 if ( (get(i) >= 'a') && (get(i) <= 'z') )
537 c_character_manager.put(i, char(get(i) + CASE_DIFFERENCE));
540 astring astring::lower() const
542 astring to_return(*this);
543 to_return.to_lower();
547 astring astring::upper() const
549 astring to_return(*this);
550 to_return.to_upper();
554 void astring::copy(char *array_to_stuff, int how_many) const
556 if (!array_to_stuff) return;
557 array_to_stuff[0] = '\0';
558 if ( (how_many <= 0) || (length() <= 0) ) return;
559 strncpy(array_to_stuff, observe(), (size_t)minimum(how_many, int(length())));
560 array_to_stuff[minimum(how_many, int(length()))] = '\0';
563 bool astring::iequals(const astring &that) const
564 { return strcasecmp(observe(), that.observe()) == 0; }
566 bool astring::iequals(const char *that) const
567 { return strcasecmp(observe(), that) == 0; }
569 int astring::ifind(char to_find, int position, bool reverse) const
570 { return char_find(to_find, position, reverse, false); }
572 int astring::find(char to_find, int position, bool reverse) const
573 { return char_find(to_find, position, reverse, true); }
575 int astring::find_any(const char *to_find, int position, bool reverse) const
576 { return char_find_any(to_find, position, reverse, true); }
578 int astring::ifind_any(const char *to_find, int position, bool reverse) const
579 { return char_find_any(to_find, position, reverse, false); }
581 int astring::find_non_match(const char *to_find, int position,
583 { return char_find_any(to_find, position, reverse, false, true); }
585 char simple_lower(char input)
587 if ( (input <= 'Z') && (input >= 'A') ) return input - CASE_DIFFERENCE;
591 int astring::char_find(char to_find, int position, bool reverse,
592 bool case_sense) const
594 if (position < 0) return common::OUT_OF_RANGE;
595 if (position > end()) return common::OUT_OF_RANGE;
597 for (int i = position; i >= 0; i--) {
598 if (case_sense && (get(i) == to_find)) return i;
599 else if (simple_lower(get(i)) == simple_lower(to_find)) return i;
603 const char *const pos = strchr(observe() + position, to_find);
604 if (pos) return int(pos - observe());
606 for (int i = position; i < length(); i++)
607 if (simple_lower(get(i)) == simple_lower(to_find)) return i;
610 return common::NOT_FOUND;
613 bool imatches_any(char to_check, const astring &list)
615 for (int i = 0; i < list.length(); i++)
616 if (simple_lower(to_check) == simple_lower(list[i])) return true;
620 bool matches_any(char to_check, const astring &list)
622 for (int i = 0; i < list.length(); i++)
623 if (to_check == list[i]) return true;
627 bool matches_none(char to_check, const astring &list)
629 bool saw_match = false;
630 for (int i = 0; i < list.length(); i++)
631 if (to_check == list[i]) {
638 int astring::char_find_any(const astring &to_find, int position, bool reverse,
639 bool case_sense, bool invert_find) const
641 if (position < 0) return common::OUT_OF_RANGE;
642 if (position > end()) return common::OUT_OF_RANGE;
644 for (int i = position; i >= 0; i--) {
646 if (case_sense && matches_any(get(i), to_find)) return i;
647 else if (imatches_any(get(i), to_find)) return i;
649 //printf("rev posn=%d char=%c", i, get(i));
650 // case-sensitivity is not used for inverted finds.
651 if (matches_none(get(i), to_find)) return i;
655 for (int i = position; i < length(); i++) {
657 if (case_sense && matches_any(get(i), to_find)) return i;
658 else if (imatches_any(get(i), to_find)) return i;
660 // case-sensitivity is not used for inverted finds.
661 //printf("fwd posn=%d char=%c", i, get(i));
662 if (matches_none(get(i), to_find)) return i;
666 return common::NOT_FOUND;
669 int astring::find(const astring &to_find, int posn, bool reverse) const
670 { return str_find(to_find, posn, reverse, true); }
672 int astring::ifind(const astring &to_find, int posn, bool reverse) const
673 { return str_find(to_find, posn, reverse, false); }
675 int astring::str_find(const astring &to_find, int posn, bool reverse,
676 bool case_sense) const
678 bounds_return(posn, 0, end(), common::OUT_OF_RANGE);
679 if (!to_find.length()) return common::BAD_INPUT;
681 // skip some steps by finding the first place that the first character of
682 // the string resides in our string.
684 posn = find(to_find[0], posn, reverse);
685 else posn = ifind(to_find[0], posn, reverse);
686 if (posn < 0) return common::NOT_FOUND;
688 //hmmm: there is a better way to do this loop in terms of the number of
689 // comparisons performed. knuth morris pratt algorithm?
691 //hmmm: this could use strncmp too?
693 if (posn > length() - to_find.length())
694 posn = length() - to_find.length();
695 for (int i = posn; i >= 0; i--)
696 if (!memcmp((void *)&observe()[i], (void *)to_find.observe(),
700 const int find_len = to_find.length();
701 const int str_len = length();
702 const char first_char = to_find[0];
703 bounds_return(posn, 0, str_len - find_len, common::OUT_OF_RANGE);
704 for (int i = posn - 1;
705 ( ( (i = find(first_char, i + 1)) >= 0)
706 && (str_len - i >= find_len) ); no_increment) {
707 if (!memcmp((void *)&observe()[i], (void *)to_find.observe(),
713 // not case-sensitive.
715 if (posn > length() - to_find.length())
716 posn = length() - to_find.length();
717 for (int i = posn; i >= 0; i--)
718 if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length()))
721 bounds_return(posn, 0, length() - to_find.length(), common::OUT_OF_RANGE);
722 for (int i = posn; i < length() - to_find.length() + 1; i++)
723 if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length()))
727 return common::NOT_FOUND;
730 astring astring::operator + (const astring &s1) const
732 astring to_return(*this);
737 char &astring::operator [] (int position)
739 if (position < 0) position = 0;
740 if (position > end()) position = 0;
741 abyte &found = c_character_manager.use(position);
742 char &to_return = *((char *)(&found));
746 const char &astring::operator [] (int position) const
748 if (position < 0) position = 0;
749 if (position > end()) position = 0;
750 const abyte &found = c_character_manager.get(position);
751 const char &to_return = *((const char *)(&found));
755 int astring::convert(int default_value) const
757 if (!length()) return default_value;
759 int fields = sscanf(observe(), "%d", &to_return);
760 if (fields < 1) return default_value;
764 long astring::convert(long default_value) const
766 if (!length()) return default_value;
768 int fields = sscanf(observe(), "%ld", &to_return);
769 if (fields < 1) return default_value;
773 float astring::convert(float default_value) const
775 if (!length()) return default_value;
777 int fields = sscanf(observe(), "%f", &to_return);
778 if (fields < 1) return default_value;
782 double astring::convert(double default_value) const
784 if (!length()) return default_value;
786 int fields = sscanf(observe(), "%lf", &to_return);
787 if (fields < 1) return default_value;
791 astring &astring::operator += (const char *s1)
793 if (!s1) return *this;
795 c_character_manager.insert(len, int(strlen(s1)));
796 memmove((char *)&c_character_manager[len], s1, int(strlen(s1)));
800 astring &astring::operator += (char s1)
803 c_character_manager.insert(len, 1);
804 c_character_manager.put(len, s1);
808 bool astring::compare(const astring &to_compare, int start_first,
809 int start_second, int count, bool case_sensitive) const
811 bounds_return(start_first, 0, end(), false);
812 bounds_return(start_second, 0, to_compare.end(), false);
813 bounds_return(start_first + count, start_first, length(), false);
814 bounds_return(start_second + count, start_second, to_compare.length(), false);
816 if (!case_sensitive) {
817 return !strncasecmp(&observe()[start_first],
818 &to_compare.observe()[start_second], count);
820 return !memcmp((void *)&observe()[start_first],
821 (void *)&to_compare.observe()[start_second], count);
826 int astring::icompare(const char *to_compare, int length_in) const
828 if (!length_in) return 0; // nothing is equal to nothing.
829 int real_len = length_in;
830 // if they're passing a negative length, we use the full length.
831 if (negative(length_in))
833 // if we have no length, make the obvious returns now.
834 int to_compare_len = int(strlen(to_compare));
835 if (!real_len) return to_compare_len? -1 : 0;
836 // if the second string is empty, it's always less than the non-empty.
837 if (!to_compare_len) return 1;
838 int to_return = strncasecmp(observe(), to_compare, real_len);
839 if (negative(length_in) && !to_return && (to_compare_len > length()) ) {
840 // catch special case for default length when the two are equal except
841 // that the second string is longer--this means the first is less than
842 // second, not equal.
850 bool astring::oy_icompare(const astring &to_compare, int start_first,
851 int start_second, int count) const
853 bounds_return(start_first, 0, end(), false);
854 bounds_return(start_second, 0, to_compare.end(), false);
855 bounds_return(start_first + count, start_first, length(), false);
856 bounds_return(start_second + count, start_second, to_compare.length(), false);
857 const char *actual_first = this->observe() + start_first;
858 const char *actual_second = to_compare.observe() + start_second;
859 return !strncasecmp(actual_first, actual_second, count);
863 bool astring::substring(astring &target, int start, int bender) const
866 if (bender < start) return false;
867 const int last = end(); // final position that's valid in the string.
868 bounds_return(start, 0, last, false);
869 bounds_return(bender, 0, last, false);
870 target.reset(UNTERMINATED, observe() + start, bender - start + 1);
874 astring astring::substring(int start, int end) const
877 substring(to_return, start, end);
881 astring astring::middle(int start, int count)
882 { return substring(start, start + count - 1); }
884 astring astring::left(int count)
885 { return substring(0, count - 1); }
887 astring astring::right(int count)
888 { return substring(end() - count + 1, end()); }
890 void astring::insert(int position, const astring &to_insert)
892 bounds_return(position, 0, length(), );
893 if (this == &to_insert) {
894 astring copy_of_me(to_insert);
895 insert(position, copy_of_me); // not recursive because no longer == me.
897 c_character_manager.insert(position, to_insert.length());
898 c_character_manager.overwrite(position, to_insert.c_character_manager,
903 bool astring::replace(const astring &tag, const astring &replacement)
905 int where = find(tag);
906 if (negative(where)) return false;
907 zap(where, where + tag.end());
908 insert(where, replacement);
912 bool astring::replace_all(const astring &to_replace, const astring &new_string)
914 bool did_any = false;
915 for (int i = 0; i < length(); i++) {
916 int indy = find(to_replace, i);
917 if (negative(indy)) break; // get out since there are no more matches.
918 i = indy; // update our position to where we found the string.
919 zap(i, i + to_replace.length() - 1); // remove old string.
920 insert(i, new_string); // plug the new string into the old position.
921 i += new_string.length() - 1; // jump past what we replaced.
927 bool astring::replace_all(char to_replace, char new_char)
929 bool did_any = false;
930 for (int i = 0; i < length(); i++) {
931 if (get(i) == to_replace) {
939 bool astring::matches(const astring &match_list, char to_match)
941 for (int i = 0; i < match_list.length(); i++)
942 if (to_match == match_list.get(i)) return true;
946 void astring::strip(const astring &strip_list, how_to_strip way)
948 if (way & FROM_FRONT)
949 while (length() && matches(strip_list, get(0)))
953 while (length() && matches(strip_list, get(end())))
957 int astring::packed_size() const { return length() + 1; }
959 void astring::pack(byte_array &target) const
960 { attach(target, (char *)c_character_manager.observe()); }
962 bool astring::unpack(byte_array &source)
963 { return detach(source, *this); }
965 ///int astring::icompare(const astring &to_compare, int length_in) const
966 ///{ return icompare(to_compare.observe(), length_in); }
969 int astring::slow_strncasecmp(const char *first, const char *second, int length)
971 int len1 = int(strlen(first));
972 int len2 = int(strlen(second));
973 if (!length) return 0; // no characters are equal to none.
974 if (!len1 && !len2) return 0; // equal as empty.
975 if (!len1 && len2) return -1; // first < second.
976 if (len1 && !len2) return 1; // first > second.
977 if (positive(length)) {
978 len1 = minimum(length, len1);
979 len2 = minimum(length, len2);
981 for (int i = 0; i < len1; i++) {
982 if (i > len2 - 1) return 1; // first > second, had more length.
983 if (simple_lower(first[i]) < simple_lower(second[i]))
984 return -1; // first < second.
985 if (simple_lower(first[i]) > simple_lower(second[i]))
986 return 1; // first > second.
988 // at this point we know second is equal to first up to the length of
990 if (len2 > len1) return -1; // second was longer and therefore greater.
997 a_sprintf::a_sprintf() : astring() {}
999 a_sprintf::a_sprintf(const astring &s) : astring(s) {}
1001 a_sprintf::a_sprintf(const char *initial, ...)
1004 if (!initial) return;
1006 va_start(args, initial);
1007 base_sprintf(initial, args);
1013 void attach(byte_array &packed_form, const char *to_attach)
1015 const int len = int(strlen(to_attach));
1016 const int old_pos = packed_form.last();
1017 packed_form.insert(old_pos + 1, len + 1);
1018 memmove((char *)packed_form.observe() + old_pos + 1, to_attach, len + 1);
1021 bool detach(byte_array &packed_form, astring &to_detach)
1023 if (!packed_form.length()) return false;
1024 // locate the zero termination if possible.
1025 const void *zero_posn = memchr(packed_form.observe(), '\0',
1026 packed_form.length());
1027 // make sure we could find the zero termination.
1029 // nope, never saw a zero. good thing we checked.
1033 // set the string up using a standard constructor since we found the zero
1034 // position; we know the string constructor will be happy.
1035 to_detach = (char *)packed_form.observe();
1036 // compute the length of the string we found based on the position of the
1038 int find_len = int((abyte *)zero_posn - packed_form.observe());
1039 // whack the portion of the array that we consumed.
1040 packed_form.zap(0, find_len);
1046 // contract fulfillment area.
1048 base_string &astring::concatenate_string(const base_string &s)
1050 const astring *cast = dynamic_cast<const astring *>(&s);
1051 if (cast) *this += *cast;
1052 else *this += astring(s.observe());
1056 base_string &astring::concatenate_char(char c)
1062 base_string &astring::assign(const base_string &s)
1064 const astring *cast = dynamic_cast<const astring *>(&s);
1065 if (cast) *this = *cast;
1066 else *this = astring(s.observe());
1070 base_string &astring::upgrade(const char *s)
1076 bool astring::sub_string(base_string &target, int start, int end) const
1078 astring *cast = dynamic_cast<astring *>(&target);
1079 if (!cast) throw "error: astring::sub_string: unknown type";
1080 return substring(*cast, start, end);
1083 bool astring::sub_compare(const base_string &to_compare, int start_first,
1084 int start_second, int count, bool case_sensitive) const
1086 const astring *cast = dynamic_cast<const astring *>(&to_compare);
1087 if (cast) return compare(*cast, start_first, start_second, count, case_sensitive);
1088 else return compare(astring(to_compare.observe()), start_first, start_second,
1089 count, case_sensitive);
1092 void astring::insert(int position, const base_string &to_insert)
1094 const astring *cast = dynamic_cast<const astring *>(&to_insert);
1095 if (cast) this->insert(position, *cast);
1096 else this->insert(position, astring(to_insert.observe()));