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"
26 #define strcasecmp strcmpi
27 #define strncasecmp strnicmp
31 //#define DEBUG_STRING
32 // uncomment for debugging version.
35 // macro just documents a blank parameter in the code.
39 const int LONGEST_SPRINTF = 600; // the longest a simple sprintf can be here.
41 const char CASE_DIFFERENCE = char('A' - 'a');
42 // the measurement of the difference between upper and lower case.
44 // this factor is used to bias dynamic sprintfs for cases where the length
45 // is specified, but the actual string is shorter than that length.
46 const int MAX_FIELD_FUDGE_FACTOR = 64;
48 const abyte empty_char_star[] = { 0 };
49 // used to initialize empty strings.
53 bool astring_comparator(const astring &a, const astring &b) { return a.equal_to(b); }
55 int calculate_proper_length(int repeat) { return negative(repeat)? 1 : repeat + 1; }
60 : c_character_manager(1, empty_char_star),
61 c_held_string((char * const *)c_character_manager.internal_offset_mem())
64 astring::astring(const base_string &initial)
65 : c_character_manager(strlen(initial.observe()) + 1, (abyte *)initial.observe()),
66 c_held_string((char * const *)c_character_manager.internal_offset_mem())
69 astring::astring(char initial, int repeat)
70 : c_character_manager(calculate_proper_length(repeat))
72 if (!initial) initial = ' '; // for nulls, we use spaces.
73 int new_size = c_character_manager.length() - 1;
75 /*hmmm: eclipse was badgering me into adding types on this, but it's not really an error in my code seemingly.
76 * eclipse seems to want a ? type in the last parameter, not a size_t or int. why? doesn't it know size_t?
78 memset((void *)c_character_manager.access(), (int)initial, (size_t)new_size);
79 c_character_manager.put(new_size, '\0');
80 c_held_string = (char * const *)c_character_manager.internal_offset_mem();
83 astring::astring(const astring &s1)
85 c_character_manager(s1.c_character_manager),
86 c_held_string((char * const *)c_character_manager.internal_offset_mem())
90 astring::astring(const char *initial)
91 : c_character_manager(calculate_proper_length(initial? int(strlen(initial)) : 0))
93 c_character_manager.put(0, '\0');
94 if (!initial) return; // bail because there's no string to copy.
95 strcpy(access(), initial);
96 c_held_string = (char * const *)c_character_manager.internal_offset_mem();
99 astring::astring(special_flag flag, const char *initial, ...)
100 : c_character_manager(1, empty_char_star),
101 c_held_string((char * const *)c_character_manager.internal_offset_mem())
103 if (!initial) return;
104 if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) {
105 operator = (astring(astring::SPRINTF, "unknown flag %d", flag));
110 va_start(args, initial);
112 if (flag == UNTERMINATED) {
113 // special process for grabbing a string that has no terminating nil.
114 int length = va_arg(args, int); // get the length of the string out.
115 c_character_manager.reset(length, (abyte *)initial);
116 c_character_manager += abyte(0);
121 // only other flag currently supported is sprintf, so we do that...
122 base_sprintf(initial, args);
126 astring::~astring() { c_held_string = NULL_POINTER; }
128 const astring &astring::empty_string() { return bogonic<astring>(); }
130 void astring::text_form(base_string &state_fill) const { state_fill.assign(*this); }
132 int astring::length() const { return c_character_manager.length() - 1; }
134 byte_array &astring::get_implementation() { return c_character_manager; }
136 char *astring::access() { return (char *)c_character_manager.access(); }
138 char astring::get(int index) const { return (char)c_character_manager.get(index); }
140 const char *astring::observe() const
141 { return (const char *)c_character_manager.observe(); }
143 bool astring::equal_to(const equalizable &s2) const
145 const astring *s2_cast = cast_or_throw(s2, *this);
146 return comparator(*s2_cast) == 0;
149 bool astring::less_than(const orderable &s2) const
151 const astring *s2_cast = dynamic_cast<const astring *>(&s2);
152 if (!s2_cast) throw "error: astring::<: unknown type";
153 return comparator(*s2_cast) < 0;
156 int astring::comparator(const astring &s2) const
157 { return strcmp(observe(), s2.observe()); }
159 bool astring::equal_to(const char *that) const
160 { return strcmp(observe(), that) == 0; }
162 bool astring::contains(const astring &to_find) const
163 { return (find(to_find, 0) < 0) ? false : true; }
165 astring &astring::operator += (const astring &s1)
166 { insert(length(), s1); return *this; }
168 void astring::shrink()
170 astring copy_of_this(observe());
171 c_character_manager.swap_contents(copy_of_this.c_character_manager);
174 astring &astring::sprintf(const char *initial, ...)
177 va_start(args, initial);
178 astring &to_return = base_sprintf(initial, args);
183 astring &astring::base_sprintf(const char *initial, va_list &args)
186 if (!initial) return *this; // skip null strings.
187 if (!initial[0]) return *this; // skip empty strings.
189 // these accumulate parts of the sprintf format within the loop.
190 char flag_chars[23], width_chars[23], precision_chars[23], modifier_chars[23];
192 // thanks for the inspiration to k&r page 156.
193 for (const char *traverser = initial; *traverser; traverser++) {
195 printf("index=%d, char=%c\n", traverser - initial, *traverser);
198 if (*traverser != '%') {
199 // not a special character, so just drop it in.
203 traverser++; // go to the next character.
205 printf("index=%d, char=%c\n", traverser - initial, *traverser);
207 if (*traverser == '%') {
208 // capture the "%%" style format specifier.
212 bool failure = false;
213 // becomes set to true if something didn't match in a necessary area.
215 seek_flag(traverser, flag_chars, failure);
221 seek_width(traverser, width_chars);
222 seek_precision(traverser, precision_chars);
223 seek_modifier(traverser, modifier_chars);
224 get_type_character(traverser, args, *this, flag_chars,
225 width_chars, precision_chars, modifier_chars);
230 void astring::seek_flag(const char *&traverser, char *flag_chars, bool &failure)
232 flag_chars[0] = '\0';
234 bool keep_going = true;
235 while (!failure && keep_going) {
236 switch (*traverser) {
237 case '-': case '+': case ' ': case '\011': case '#':
238 flag_chars[strlen(flag_chars) + 1] = '\0';
239 flag_chars[strlen(flag_chars)] = *traverser++;
242 // we found a character that doesn't belong in the flags.
248 if (strlen(flag_chars)) printf("[flag=%s]\n", flag_chars);
249 else printf("no flags\n");
253 void astring::seek_width(const char *&traverser, char *width_chars)
255 width_chars[0] = '\0';
256 bool no_more_nums = false;
257 bool first_num = true;
258 while (!no_more_nums) {
259 char wideness[2] = { *traverser, '\0' };
260 if (first_num && (wideness[0] == '0')) {
261 strcpy(width_chars, wideness);
263 } else if (first_num && (wideness[0] == '*') ) {
264 strcpy(width_chars, wideness);
267 } else if ( (wideness[0] <= '9') && (wideness[0] >= '0') ) {
269 strcat(width_chars, wideness);
271 } else no_more_nums = true;
275 if (strlen(width_chars)) printf("[width=%s]\n", width_chars);
276 else printf("no widths\n");
280 void astring::seek_precision(const char *&traverser, char *precision_chars)
282 precision_chars[0] = '\0';
283 if (*traverser != '.') return;
284 strcpy(precision_chars, ".");
286 bool no_more_nums = false;
287 bool first_num = true;
288 while (!no_more_nums) {
289 char preciseness[2] = { *traverser, '\0' };
290 if (first_num && (preciseness[0] == '0')) {
291 strcat(precision_chars, preciseness);
294 } else if (first_num && (preciseness[0] == '*') ) {
295 strcat(precision_chars, preciseness);
298 } else if ( (preciseness[0] <= '9') && (preciseness[0] >= '0') ) {
299 strcat(precision_chars, preciseness);
301 } else no_more_nums = true;
305 if (strlen(precision_chars)) printf("[precis=%s]\n", precision_chars);
306 else printf("no precision\n");
310 void astring::seek_modifier(const char *&traverser, char *modifier_chars)
312 modifier_chars[0] = '\0';
313 switch (*traverser) {
314 case 'F': case 'N': case 'h': case 'l': case 'L': {
315 modifier_chars[strlen(modifier_chars) + 1] = '\0';
316 modifier_chars[strlen(modifier_chars)] = *traverser++;
321 if (strlen(modifier_chars)) printf("[mod=%s]\n", modifier_chars);
322 else printf("no modifiers\n");
326 void astring::get_type_character(const char * &traverser, va_list &args,
327 astring &output_string, const char *flag_chars, const char *width_chars,
328 const char *precision_chars, const char *modifier_chars)
330 char formatting[120];
331 strcpy(formatting, "%");
332 strcat(formatting, flag_chars);
333 strcat(formatting, width_chars);
334 strcat(formatting, precision_chars);
335 strcat(formatting, modifier_chars);
336 char tmposh[2] = { *traverser, '\0' };
337 strcat(formatting, tmposh);
339 printf("format: %s\n", formatting);
342 enum argument_size { bits_8, bits_16, bits_32, bits_64, bits_80 };
343 bool ints_are_32_bits;
345 ints_are_32_bits = true;
346 #elif defined(__OS2__)
347 ints_are_32_bits = true;
348 #elif defined(__MSDOS__)
349 ints_are_32_bits = false;
350 #elif defined(__WIN32__)
351 ints_are_32_bits = false;
353 ints_are_32_bits = true;
355 argument_size next_argument;
356 bool use_dynamic_sprintf = false; // for dynamic printing of strings only.
357 // get the type character first and ensure it's valid.
358 switch (*traverser) {
359 case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
360 next_argument = bits_16;
361 if (ints_are_32_bits) next_argument = bits_32;
363 case 'f': case 'e': case 'g': case 'E': case 'G':
364 next_argument = bits_64;
367 next_argument = bits_8;
370 next_argument = bits_32;
371 use_dynamic_sprintf = true;
374 next_argument = bits_32; //?????
377 next_argument = bits_32; //????
380 // this is an error; the character is not recognized, so spew out
381 // any characters accumulated so far as just themselves.
383 printf("failure in type char: %c\n", *traverser);
385 output_string += formatting;
388 /* hmmm: not supported yet.
389 if (width_chars && (width_chars[0] == '*')) {
391 if (precision_chars && (precision_chars[0] == '*')) {
394 if (strlen(modifier_chars)) {
395 switch (modifier_chars[0]) {
396 case 'N': // near pointer.
397 next_argument = bits_16;
398 if (ints_are_32_bits) next_argument = bits_32;
400 case 'F': // far pointer.
401 next_argument = bits_32;
403 case 'h': // short int.
404 next_argument = bits_16;
407 next_argument = bits_32;
409 case 'L': // long double;
410 next_argument = bits_80;
413 // a failure has occurred because the modifier character is not
414 // one of the recognized values. everything is just spewed out.
416 printf("failure in modifier: %s\n", modifier_chars);
418 output_string += formatting;
422 // action time: the output string is given a tasty value.
423 char temp[LONGEST_SPRINTF];
424 char *temp2 = NULL_POINTER; // for dynamic only.
425 switch (next_argument) {
426 //hmmm: this switch is where support would need to be added for having two
427 // arguments (for the '*' case).
428 case bits_8: case bits_16:
429 if (ints_are_32_bits) ::sprintf(temp, formatting, va_arg(args, long));
430 else ::sprintf(temp, formatting, va_arg(args, int));
433 if (use_dynamic_sprintf) {
434 // currently we only do dynamic sprintf for strings.
435 char *to_print = va_arg(args, char *);
436 // check if it's valid and if we really need to do it dynamically.
438 // bogus string; put in a complaint.
439 use_dynamic_sprintf = false;
440 ::sprintf(temp, "{error:parm=NULL_POINTER}");
441 } else if (strlen(to_print) < LONGEST_SPRINTF - 2) {
442 // we're within our bounds, plus some safety room, so just do a
444 use_dynamic_sprintf = false;
445 ::sprintf(temp, formatting, to_print);
447 // it's too long, so we definitely need to do it dynamically.
448 temp2 = new char[strlen(to_print) + MAX_FIELD_FUDGE_FACTOR];
449 ::sprintf(temp2, formatting, to_print);
451 } else ::sprintf(temp, formatting, va_arg(args, void *));
454 ::sprintf(temp, formatting, va_arg(args, double));
457 ::sprintf(temp, formatting, va_arg(args, long double));
460 if (use_dynamic_sprintf) {
461 output_string += temp2;
463 } else output_string += temp;
466 //hmmm: de-redundify this function, which is identical to the constructor.
467 void astring::reset(special_flag flag, const char *initial, ...)
469 reset(); // clear the string out.
470 if (!initial) return;
471 if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) {
472 operator = (astring(astring::SPRINTF, "unknown flag %d", flag));
477 va_start(args, initial);
479 if (flag == UNTERMINATED) {
480 // special process for grabbing a string that has no terminating nil.
481 int length = va_arg(args, int); // get the length of the string out.
482 c_character_manager.reset(length, (abyte *)initial);
483 c_character_manager += abyte(0);
488 // only other flag currently supported is sprintf, so we do that...
489 base_sprintf(initial, args);
493 void astring::pad(int len, char padding)
495 if (length() >= len) return;
496 byte_array pad(len - length());
497 memset(pad.access(), padding, pad.length());
498 operator += (astring(UNTERMINATED, (char *)pad.observe(), pad.length()));
501 void astring::trim(int len)
503 if (length() <= len) return;
507 astring &astring::operator = (const astring &s1)
510 c_character_manager = s1.c_character_manager;
514 astring &astring::operator = (const char *s1)
521 void astring::zap(int position1, int position2)
523 bounds_return(position1, 0, end(), );
524 bounds_return(position2, 0, end(), );
525 c_character_manager.zap(position1, position2);
528 void astring::to_lower()
530 for (int i = 0; i < length(); i++)
531 if ( (get(i) >= 'A') && (get(i) <= 'Z') )
532 c_character_manager.put(i, char(get(i) - CASE_DIFFERENCE));
535 void astring::to_upper()
537 for (int i = 0; i < length(); i++)
538 if ( (get(i) >= 'a') && (get(i) <= 'z') )
539 c_character_manager.put(i, char(get(i) + CASE_DIFFERENCE));
542 astring astring::lower() const
544 astring to_return(*this);
545 to_return.to_lower();
549 astring astring::upper() const
551 astring to_return(*this);
552 to_return.to_upper();
556 void astring::copy(char *array_to_stuff, int how_many) const
558 if (!array_to_stuff) return;
559 array_to_stuff[0] = '\0';
560 if ( (how_many <= 0) || (length() <= 0) ) return;
561 strncpy(array_to_stuff, observe(), (size_t)minimum(how_many, int(length())));
562 array_to_stuff[minimum(how_many, int(length()))] = '\0';
565 bool astring::iequals(const astring &that) const
566 { return strcasecmp(observe(), that.observe()) == 0; }
568 bool astring::iequals(const char *that) const
569 { return strcasecmp(observe(), that) == 0; }
571 int astring::ifind(char to_find, int position, bool reverse) const
572 { return char_find(to_find, position, reverse, false); }
574 int astring::find(char to_find, int position, bool reverse) const
575 { return char_find(to_find, position, reverse, true); }
577 int astring::find_any(const char *to_find, int position, bool reverse) const
578 { return char_find_any(to_find, position, reverse, true); }
580 int astring::ifind_any(const char *to_find, int position, bool reverse) const
581 { return char_find_any(to_find, position, reverse, false); }
583 int astring::find_non_match(const char *to_find, int position,
585 { return char_find_any(to_find, position, reverse, false, true); }
587 char simple_lower(char input)
589 if ( (input <= 'Z') && (input >= 'A') ) return input - CASE_DIFFERENCE;
593 int astring::char_find(char to_find, int position, bool reverse,
594 bool case_sense) const
596 if (position < 0) return common::OUT_OF_RANGE;
597 if (position > end()) return common::OUT_OF_RANGE;
599 for (int i = position; i >= 0; i--) {
600 if (case_sense && (get(i) == to_find)) return i;
601 else if (simple_lower(get(i)) == simple_lower(to_find)) return i;
605 const char *const pos = strchr(observe() + position, to_find);
606 if (pos) return int(pos - observe());
608 for (int i = position; i < length(); i++)
609 if (simple_lower(get(i)) == simple_lower(to_find)) return i;
612 return common::NOT_FOUND;
615 bool imatches_any(char to_check, const astring &list)
617 for (int i = 0; i < list.length(); i++)
618 if (simple_lower(to_check) == simple_lower(list[i])) return true;
622 bool matches_any(char to_check, const astring &list)
624 for (int i = 0; i < list.length(); i++)
625 if (to_check == list[i]) return true;
629 bool matches_none(char to_check, const astring &list)
631 bool saw_match = false;
632 for (int i = 0; i < list.length(); i++)
633 if (to_check == list[i]) {
640 int astring::char_find_any(const astring &to_find, int position, bool reverse,
641 bool case_sense, bool invert_find) const
643 if (position < 0) return common::OUT_OF_RANGE;
644 if (position > end()) return common::OUT_OF_RANGE;
646 for (int i = position; i >= 0; i--) {
648 if (case_sense && matches_any(get(i), to_find)) return i;
649 else if (imatches_any(get(i), to_find)) return i;
651 //printf("rev posn=%d char=%c", i, get(i));
652 // case-sensitivity is not used for inverted finds.
653 if (matches_none(get(i), to_find)) return i;
657 for (int i = position; i < length(); i++) {
659 if (case_sense && matches_any(get(i), to_find)) return i;
660 else if (imatches_any(get(i), to_find)) return i;
662 // case-sensitivity is not used for inverted finds.
663 //printf("fwd posn=%d char=%c", i, get(i));
664 if (matches_none(get(i), to_find)) return i;
668 return common::NOT_FOUND;
671 int astring::find(const astring &to_find, int posn, bool reverse) const
672 { return str_find(to_find, posn, reverse, true); }
674 int astring::ifind(const astring &to_find, int posn, bool reverse) const
675 { return str_find(to_find, posn, reverse, false); }
677 int astring::str_find(const astring &to_find, int posn, bool reverse,
678 bool case_sense) const
680 bounds_return(posn, 0, end(), common::OUT_OF_RANGE);
681 if (!to_find.length()) return common::BAD_INPUT;
683 // skip some steps by finding the first place that the first character of
684 // the string resides in our string.
686 posn = find(to_find[0], posn, reverse);
687 else posn = ifind(to_find[0], posn, reverse);
688 if (posn < 0) return common::NOT_FOUND;
690 //hmmm: there is a better way to do this loop in terms of the number of
691 // comparisons performed. knuth morris pratt algorithm?
693 //hmmm: this could use strncmp too?
695 if (posn > length() - to_find.length())
696 posn = length() - to_find.length();
697 for (int i = posn; i >= 0; i--)
698 if (!memcmp((void *)&observe()[i], (void *)to_find.observe(),
702 const int find_len = to_find.length();
703 const int str_len = length();
704 const char first_char = to_find[0];
705 bounds_return(posn, 0, str_len - find_len, common::OUT_OF_RANGE);
706 for (int i = posn - 1;
707 ( ( (i = find(first_char, i + 1)) >= 0)
708 && (str_len - i >= find_len) ); no_increment) {
709 if (!memcmp((void *)&observe()[i], (void *)to_find.observe(),
715 // not case-sensitive.
717 if (posn > length() - to_find.length())
718 posn = length() - to_find.length();
719 for (int i = posn; i >= 0; i--)
720 if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length()))
723 bounds_return(posn, 0, length() - to_find.length(), common::OUT_OF_RANGE);
724 for (int i = posn; i < length() - to_find.length() + 1; i++)
725 if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length()))
729 return common::NOT_FOUND;
732 astring astring::operator + (const astring &s1) const
734 astring to_return(*this);
739 char &astring::operator [] (int position)
741 if (position < 0) position = 0;
742 if (position > end()) position = 0;
743 abyte &found = c_character_manager.use(position);
744 char &to_return = *((char *)(&found));
748 const char &astring::operator [] (int position) const
750 if (position < 0) position = 0;
751 if (position > end()) position = 0;
752 const abyte &found = c_character_manager.get(position);
753 const char &to_return = *((const char *)(&found));
757 int astring::convert(int default_value) const
759 if (!length()) return default_value;
761 int fields = sscanf(observe(), "%d", &to_return);
762 if (fields < 1) return default_value;
766 long astring::convert(long default_value) const
768 if (!length()) return default_value;
770 int fields = sscanf(observe(), "%ld", &to_return);
771 if (fields < 1) return default_value;
775 float astring::convert(float default_value) const
777 if (!length()) return default_value;
779 int fields = sscanf(observe(), "%f", &to_return);
780 if (fields < 1) return default_value;
784 double astring::convert(double default_value) const
786 if (!length()) return default_value;
788 int fields = sscanf(observe(), "%lf", &to_return);
789 if (fields < 1) return default_value;
793 astring &astring::operator += (const char *s1)
795 if (!s1) return *this;
797 c_character_manager.insert(len, int(strlen(s1)));
798 memmove((char *)&c_character_manager[len], s1, int(strlen(s1)));
802 astring &astring::operator += (char s1)
805 c_character_manager.insert(len, 1);
806 c_character_manager.put(len, s1);
810 bool astring::compare(const astring &to_compare, int start_first,
811 int start_second, int count, bool case_sensitive) const
813 bounds_return(start_first, 0, end(), false);
814 bounds_return(start_second, 0, to_compare.end(), false);
815 bounds_return(start_first + count, start_first, length(), false);
816 bounds_return(start_second + count, start_second, to_compare.length(), false);
818 if (!case_sensitive) {
819 return !strncasecmp(&observe()[start_first],
820 &to_compare.observe()[start_second], count);
822 return !memcmp((void *)&observe()[start_first],
823 (void *)&to_compare.observe()[start_second], count);
828 int astring::icompare(const char *to_compare, int length_in) const
830 if (!length_in) return 0; // nothing is equal to nothing.
831 int real_len = length_in;
832 // if they're passing a negative length, we use the full length.
833 if (negative(length_in))
835 // if we have no length, make the obvious returns now.
836 int to_compare_len = int(strlen(to_compare));
837 if (!real_len) return to_compare_len? -1 : 0;
838 // if the second string is empty, it's always less than the non-empty.
839 if (!to_compare_len) return 1;
840 int to_return = strncasecmp(observe(), to_compare, real_len);
841 if (negative(length_in) && !to_return && (to_compare_len > length()) ) {
842 // catch special case for default length when the two are equal except
843 // that the second string is longer--this means the first is less than
844 // second, not equal.
852 bool astring::oy_icompare(const astring &to_compare, int start_first,
853 int start_second, int count) const
855 bounds_return(start_first, 0, end(), false);
856 bounds_return(start_second, 0, to_compare.end(), false);
857 bounds_return(start_first + count, start_first, length(), false);
858 bounds_return(start_second + count, start_second, to_compare.length(), false);
859 const char *actual_first = this->observe() + start_first;
860 const char *actual_second = to_compare.observe() + start_second;
861 return !strncasecmp(actual_first, actual_second, count);
865 bool astring::substring(astring &target, int start, int bender) const
868 if (bender < start) return false;
869 const int last = end(); // final position that's valid in the string.
870 bounds_return(start, 0, last, false);
871 bounds_return(bender, 0, last, false);
872 target.reset(UNTERMINATED, observe() + start, bender - start + 1);
876 astring astring::substring(int start, int end) const
879 substring(to_return, start, end);
883 astring astring::middle(int start, int count)
884 { return substring(start, start + count - 1); }
886 astring astring::left(int count)
887 { return substring(0, count - 1); }
889 astring astring::right(int count)
890 { return substring(end() - count + 1, end()); }
892 void astring::insert(int position, const astring &to_insert)
894 bounds_return(position, 0, length(), );
895 if (this == &to_insert) {
896 astring copy_of_me(to_insert);
897 insert(position, copy_of_me); // not recursive because no longer == me.
899 c_character_manager.insert(position, to_insert.length());
900 c_character_manager.overwrite(position, to_insert.c_character_manager,
905 bool astring::replace(const astring &tag, const astring &replacement)
907 int where = find(tag);
908 if (negative(where)) return false;
909 zap(where, where + tag.end());
910 insert(where, replacement);
914 bool astring::replace_all(const astring &to_replace, const astring &new_string)
916 bool did_any = false;
917 for (int i = 0; i < length(); i++) {
918 int indy = find(to_replace, i);
919 if (negative(indy)) break; // get out since there are no more matches.
920 i = indy; // update our position to where we found the string.
921 zap(i, i + to_replace.length() - 1); // remove old string.
922 insert(i, new_string); // plug the new string into the old position.
923 i += new_string.length() - 1; // jump past what we replaced.
929 bool astring::replace_all(char to_replace, char new_char)
931 bool did_any = false;
932 for (int i = 0; i < length(); i++) {
933 if (get(i) == to_replace) {
941 bool astring::matches(const astring &match_list, char to_match)
943 for (int i = 0; i < match_list.length(); i++)
944 if (to_match == match_list.get(i)) return true;
948 void astring::strip(const astring &strip_list, how_to_strip way)
950 if (way & FROM_FRONT)
951 while (length() && matches(strip_list, get(0)))
955 while (length() && matches(strip_list, get(end())))
959 int astring::packed_size() const { return length() + 1; }
961 void astring::pack(byte_array &target) const
962 { attach(target, (char *)c_character_manager.observe()); }
964 bool astring::unpack(byte_array &source)
965 { return detach(source, *this); }
967 ///int astring::icompare(const astring &to_compare, int length_in) const
968 ///{ return icompare(to_compare.observe(), length_in); }
971 int astring::slow_strncasecmp(const char *first, const char *second, int length)
973 int len1 = int(strlen(first));
974 int len2 = int(strlen(second));
975 if (!length) return 0; // no characters are equal to none.
976 if (!len1 && !len2) return 0; // equal as empty.
977 if (!len1 && len2) return -1; // first < second.
978 if (len1 && !len2) return 1; // first > second.
979 if (positive(length)) {
980 len1 = minimum(length, len1);
981 len2 = minimum(length, len2);
983 for (int i = 0; i < len1; i++) {
984 if (i > len2 - 1) return 1; // first > second, had more length.
985 if (simple_lower(first[i]) < simple_lower(second[i]))
986 return -1; // first < second.
987 if (simple_lower(first[i]) > simple_lower(second[i]))
988 return 1; // first > second.
990 // at this point we know second is equal to first up to the length of
992 if (len2 > len1) return -1; // second was longer and therefore greater.
999 a_sprintf::a_sprintf() : astring() {}
1001 a_sprintf::a_sprintf(const astring &s) : astring(s) {}
1003 a_sprintf::a_sprintf(const char *initial, ...)
1006 if (!initial) return;
1008 va_start(args, initial);
1009 base_sprintf(initial, args);
1015 void attach(byte_array &packed_form, const char *to_attach)
1017 const int len = int(strlen(to_attach));
1018 const int old_pos = packed_form.last();
1019 packed_form.insert(old_pos + 1, len + 1);
1020 memmove((char *)packed_form.observe() + old_pos + 1, to_attach, len + 1);
1023 bool detach(byte_array &packed_form, astring &to_detach)
1025 if (!packed_form.length()) return false;
1026 // locate the zero termination if possible.
1027 const void *zero_posn = memchr(packed_form.observe(), '\0',
1028 packed_form.length());
1029 // make sure we could find the zero termination.
1031 // nope, never saw a zero. good thing we checked.
1035 // set the string up using a standard constructor since we found the zero
1036 // position; we know the string constructor will be happy.
1037 to_detach = (char *)packed_form.observe();
1038 // compute the length of the string we found based on the position of the
1040 int find_len = int((abyte *)zero_posn - packed_form.observe());
1041 // whack the portion of the array that we consumed.
1042 packed_form.zap(0, find_len);
1048 // contract fulfillment area.
1050 base_string &astring::concatenate_string(const base_string &s)
1052 const astring *cast = dynamic_cast<const astring *>(&s);
1053 if (cast) *this += *cast;
1054 else *this += astring(s.observe());
1058 base_string &astring::concatenate_char(char c)
1064 base_string &astring::assign(const base_string &s)
1066 const astring *cast = dynamic_cast<const astring *>(&s);
1067 if (cast) *this = *cast;
1068 else *this = astring(s.observe());
1072 base_string &astring::upgrade(const char *s)
1078 bool astring::sub_string(base_string &target, int start, int end) const
1080 astring *cast = dynamic_cast<astring *>(&target);
1081 if (!cast) throw "error: astring::sub_string: unknown type";
1082 return substring(*cast, start, end);
1085 bool astring::sub_compare(const base_string &to_compare, int start_first,
1086 int start_second, int count, bool case_sensitive) const
1088 const astring *cast = dynamic_cast<const astring *>(&to_compare);
1089 if (cast) return compare(*cast, start_first, start_second, count, case_sensitive);
1090 else return compare(astring(to_compare.observe()), start_first, start_second,
1091 count, case_sensitive);
1094 void astring::insert(int position, const base_string &to_insert)
1096 const astring *cast = dynamic_cast<const astring *>(&to_insert);
1097 if (cast) this->insert(position, *cast);
1098 else this->insert(position, astring(to_insert.observe()));