new fortune
[feisty_meow.git] / nucleus / library / basis / astring.cpp
1 /*
2 *  Name   : astring
3 *  Author : Chris Koeritz
4 **
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                               *
11 */
12
13 #include "astring.h"
14 #include "definitions.h"
15 #include "functions.h"
16 #include "guards.h"
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #ifdef _MSC_VER
23   #undef strcasecmp 
24   #undef strncasecmp 
25   #define strcasecmp strcmpi
26   #define strncasecmp strnicmp
27 #endif
28
29 //#define DEBUG_STRING
30   // uncomment for debugging version.
31
32 #define no_increment
33   // macro just documents a blank parameter in the code.
34
35 namespace basis {
36
37 const int LONGEST_SPRINTF = 600;  // the longest a simple sprintf can be here.
38
39 const char CASE_DIFFERENCE = char('A' - 'a');
40   // the measurement of the difference between upper and lower case.
41
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;
45
46 const abyte empty_char_star[] = { 0 };
47   // used to initialize empty strings.
48
49 //////////////
50
51 bool astring_comparator(const astring &a, const astring &b) { return a.equal_to(b); }
52
53 int calculate_proper_length(int repeat) { return negative(repeat)? 1 : repeat + 1; }
54
55 //////////////
56
57 astring::astring()
58 : c_character_manager(1, empty_char_star),
59   c_held_string((char * const *)c_character_manager.internal_offset_mem())
60 {}
61
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())
65 {}
66
67 astring::astring(char initial, int repeat)
68 : c_character_manager(calculate_proper_length(repeat))
69 {
70   if (!initial) initial = ' ';  // for nulls, we use spaces.
71   int new_size = c_character_manager.length() - 1;
72
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?
75    */
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();
79 }
80
81 astring::astring(const astring &s1)
82 : base_string(),
83   c_character_manager(s1.c_character_manager),
84   c_held_string((char * const *)c_character_manager.internal_offset_mem())
85 {
86 }
87
88 astring::astring(const char *initial)
89 : c_character_manager(calculate_proper_length(initial? int(strlen(initial)) : 0))
90 {
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();
95 }
96
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())
100 {
101   if (!initial) return;
102   if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) {
103     operator = (astring(astring::SPRINTF, "unknown flag %d", flag));
104     return;
105   }
106
107   va_list args;
108   va_start(args, initial);
109
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);
115     va_end(args);
116     return;
117   }
118
119   // only other flag currently supported is sprintf, so we do that...
120   base_sprintf(initial, args);
121   va_end(args);
122 }
123
124 astring::~astring() { c_held_string = NULL_POINTER; }
125
126 const astring &astring::empty_string() { return bogonic<astring>(); }
127
128 void astring::text_form(base_string &state_fill) const { state_fill.assign(*this); }
129
130 int astring::length() const { return c_character_manager.length() - 1; }
131
132 byte_array &astring::get_implementation() { return c_character_manager; }
133
134 char *astring::access() { return (char *)c_character_manager.access(); }
135
136 char astring::get(int index) const { return (char)c_character_manager.get(index); }
137
138 const char *astring::observe() const
139 { return (const char *)c_character_manager.observe(); }
140
141 bool astring::equal_to(const equalizable &s2) const
142 {
143   const astring *s2_cast = cast_or_throw(s2, *this);
144   return comparator(*s2_cast) == 0;
145 }
146
147 bool astring::less_than(const orderable &s2) const
148 {
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;
152 }
153
154 int astring::comparator(const astring &s2) const
155 { return strcmp(observe(), s2.observe()); }
156
157 bool astring::equal_to(const char *that) const
158 { return strcmp(observe(), that) == 0; }
159
160 bool astring::contains(const astring &to_find) const
161 { return (find(to_find, 0) < 0) ? false : true; }
162
163 astring &astring::operator += (const astring &s1)
164 { insert(length(), s1); return *this; }
165
166 void astring::shrink()
167 {
168   astring copy_of_this(observe());
169   c_character_manager.swap_contents(copy_of_this.c_character_manager);
170 }
171
172 astring &astring::sprintf(const char *initial, ...)
173 {
174   va_list args;
175   va_start(args, initial);
176   astring &to_return = base_sprintf(initial, args);
177   va_end(args);
178   return to_return;
179 }
180
181 astring &astring::base_sprintf(const char *initial, va_list &args)
182 {
183   reset();
184   if (!initial) return *this;  // skip null strings.
185   if (!initial[0]) return *this;  // skip empty strings.
186
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];
189
190   // thanks for the inspiration to k&r page 156.
191   for (const char *traverser = initial; *traverser; traverser++) {
192 #ifdef DEBUG_STRING
193     printf("index=%d, char=%c\n", traverser - initial, *traverser);
194 #endif
195
196     if (*traverser != '%') {
197       // not a special character, so just drop it in.
198       *this += *traverser;
199       continue;
200     }
201     traverser++; // go to the next character.
202 #ifdef DEBUG_STRING
203     printf("index=%d, char=%c\n", traverser - initial, *traverser);
204 #endif
205     if (*traverser == '%') {
206       // capture the "%%" style format specifier.
207       *this += *traverser;
208       continue;
209     }
210     bool failure = false;
211       // becomes set to true if something didn't match in a necessary area.
212
213     seek_flag(traverser, flag_chars, failure);
214     if (failure) {
215       *this += '%';
216       *this += flag_chars;
217       continue;
218     }
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);
224   }
225   return *this;
226 }
227
228 void astring::seek_flag(const char *&traverser, char *flag_chars, bool &failure)
229 {
230   flag_chars[0] = '\0';
231   failure = false;
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++;
238         break;
239       default:
240         // we found a character that doesn't belong in the flags.
241         keep_going = false;
242         break;
243     }
244   }
245 #ifdef DEBUG_STRING
246   if (strlen(flag_chars)) printf("[flag=%s]\n", flag_chars);
247   else printf("no flags\n");
248 #endif
249 }
250
251 void astring::seek_width(const char *&traverser, char *width_chars)
252 {
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);
260       traverser++;
261     } else if (first_num && (wideness[0] == '*') ) {
262       strcpy(width_chars, wideness);
263       traverser++;
264       no_more_nums = true;
265     } else if ( (wideness[0] <= '9') && (wideness[0] >= '0') ) {
266       // a failure?
267       strcat(width_chars, wideness);
268       traverser++;
269     } else no_more_nums = true;
270     first_num = false;
271   }
272 #ifdef DEBUG_STRING
273   if (strlen(width_chars)) printf("[width=%s]\n", width_chars);
274   else printf("no widths\n");
275 #endif
276 }
277
278 void astring::seek_precision(const char *&traverser, char *precision_chars)
279 {
280   precision_chars[0] = '\0';
281   if (*traverser != '.') return;
282   strcpy(precision_chars, ".");
283   traverser++;
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);
290       traverser++;
291       no_more_nums = true;
292     } else if (first_num && (preciseness[0] == '*') ) {
293       strcat(precision_chars, preciseness);
294       traverser++;
295       no_more_nums = true;
296     } else if ( (preciseness[0] <= '9') && (preciseness[0] >= '0') ) {
297       strcat(precision_chars, preciseness);
298       traverser++;
299     } else no_more_nums = true;
300     first_num = false;
301   }
302 #ifdef DEBUG_STRING
303   if (strlen(precision_chars)) printf("[precis=%s]\n", precision_chars);
304   else printf("no precision\n");
305 #endif
306 }
307
308 void astring::seek_modifier(const char *&traverser, char *modifier_chars)
309 {
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++;
315       break;
316     }
317   }
318 #ifdef DEBUG_STRING
319   if (strlen(modifier_chars)) printf("[mod=%s]\n", modifier_chars);
320   else printf("no modifiers\n");
321 #endif
322 }
323
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)
327 {
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);
336 #ifdef DEBUG_STRING
337   printf("format: %s\n", formatting);
338 #endif
339
340   enum argument_size { bits_8, bits_16, bits_32, bits_64, bits_80 };
341   bool ints_are_32_bits;
342 #ifdef __WIN32__
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;
350 #else
351   ints_are_32_bits = true;
352 #endif
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;
360       break;
361     case 'f': case 'e': case 'g': case 'E': case 'G':
362       next_argument = bits_64;
363       break;
364     case 'c':
365       next_argument = bits_8;
366       break;
367     case 's':
368       next_argument = bits_32;
369       use_dynamic_sprintf = true;
370       break;
371     case 'n':
372       next_argument = bits_32; //?????
373       break;
374     case 'p':
375       next_argument = bits_32; //????
376       break;
377     default:
378       // this is an error; the character is not recognized, so spew out
379       // any characters accumulated so far as just themselves.
380 #ifdef DEBUG_STRING
381       printf("failure in type char: %c\n", *traverser);
382 #endif
383       output_string += formatting;
384       return;
385   }
386 /* hmmm: not supported yet.
387   if (width_chars && (width_chars[0] == '*')) {
388   }
389   if (precision_chars && (precision_chars[0] == '*')) {
390   }
391 */
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;
397         break;
398       case 'F':  // far pointer.
399         next_argument = bits_32;
400         break;
401       case 'h':  // short int.
402         next_argument = bits_16;
403         break;
404       case 'l':  // long.
405         next_argument = bits_32;
406         break;
407       case 'L':  // long double;
408         next_argument = bits_80;
409         break;
410       default:
411         // a failure has occurred because the modifier character is not
412         // one of the recognized values.  everything is just spewed out.
413 #ifdef DEBUG_STRING
414         printf("failure in modifier: %s\n", modifier_chars);
415 #endif
416         output_string += formatting;
417         return;
418     }
419   }
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));
429       break;
430     case bits_32:
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.
435         if (!to_print) {
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
441           // regular sprintf.
442           use_dynamic_sprintf = false;
443           ::sprintf(temp, formatting, to_print);
444         } else {
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);
448         }
449       } else ::sprintf(temp, formatting, va_arg(args, void *));
450       break;
451     case bits_64:
452       ::sprintf(temp, formatting, va_arg(args, double));
453       break;
454     case bits_80:
455       ::sprintf(temp, formatting, va_arg(args, long double));
456       break;
457   }
458   if (use_dynamic_sprintf) {
459     output_string += temp2;
460     delete [] temp2;
461   } else output_string += temp;
462 }
463
464 //hmmm: de-redundify this function, which is identical to the constructor.
465 void astring::reset(special_flag flag, const char *initial, ...)
466 {
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));
471     return;
472   }
473
474   va_list args;
475   va_start(args, initial);
476
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);
482     va_end(args);
483     return;
484   }
485
486   // only other flag currently supported is sprintf, so we do that...
487   base_sprintf(initial, args);
488   va_end(args);
489 }
490
491 void astring::pad(int len, char padding)
492 {
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()));
497 }
498
499 void astring::trim(int len)
500 {
501   if (length() <= len) return;
502   zap(len, end());
503 }
504
505 astring &astring::operator = (const astring &s1)
506 {
507   if (this != &s1)
508     c_character_manager = s1.c_character_manager;
509   return *this;
510 }
511
512 astring &astring::operator = (const char *s1)
513 {
514   reset();
515   *this += s1;
516   return *this;
517 }
518
519 void astring::zap(int position1, int position2)
520 {
521   bounds_return(position1, 0, end(), );
522   bounds_return(position2, 0, end(), );
523   c_character_manager.zap(position1, position2);
524 }
525
526 void astring::to_lower()
527 {
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));
531 }
532
533 void astring::to_upper()
534 {
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));
538 }
539
540 astring astring::lower() const
541 {
542   astring to_return(*this);
543   to_return.to_lower();
544   return to_return;
545 }
546
547 astring astring::upper() const
548 {
549   astring to_return(*this);
550   to_return.to_upper();
551   return to_return;
552 }
553
554 void astring::copy(char *array_to_stuff, int how_many) const
555 {
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';
561 }
562
563 bool astring::iequals(const astring &that) const
564 { return strcasecmp(observe(), that.observe()) == 0; }
565
566 bool astring::iequals(const char *that) const
567 { return strcasecmp(observe(), that) == 0; }
568
569 int astring::ifind(char to_find, int position, bool reverse) const
570 { return char_find(to_find, position, reverse, false); }
571
572 int astring::find(char to_find, int position, bool reverse) const
573 { return char_find(to_find, position, reverse, true); }
574
575 int astring::find_any(const char *to_find, int position, bool reverse) const
576 { return char_find_any(to_find, position, reverse, true); }
577
578 int astring::ifind_any(const char *to_find, int position, bool reverse) const
579 { return char_find_any(to_find, position, reverse, false); }
580
581 int astring::find_non_match(const char *to_find, int position,
582     bool reverse) const
583 { return char_find_any(to_find, position, reverse, false, true); }
584
585 char simple_lower(char input)
586 {
587   if ( (input <= 'Z') && (input >= 'A') ) return input - CASE_DIFFERENCE;
588   return input;
589 }
590
591 int astring::char_find(char to_find, int position, bool reverse,
592     bool case_sense) const
593 {
594   if (position < 0) return common::OUT_OF_RANGE;
595   if (position > end()) return common::OUT_OF_RANGE;
596   if (reverse) {
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;
600     }
601   } else {
602     if (case_sense) {
603       const char *const pos = strchr(observe() + position, to_find);
604       if (pos) return int(pos - observe());
605     } else {
606       for (int i = position; i < length(); i++)
607         if (simple_lower(get(i)) == simple_lower(to_find)) return i;
608     }
609   }
610   return common::NOT_FOUND;
611 }
612
613 bool imatches_any(char to_check, const astring &list)
614 {
615   for (int i = 0; i < list.length(); i++)
616     if (simple_lower(to_check) == simple_lower(list[i])) return true;
617   return false;
618 }
619
620 bool matches_any(char to_check, const astring &list)
621 {
622   for (int i = 0; i < list.length(); i++)
623     if (to_check == list[i]) return true;
624   return false;
625 }
626
627 bool matches_none(char to_check, const astring &list)
628 {
629   bool saw_match = false;
630   for (int i = 0; i < list.length(); i++)
631     if (to_check == list[i]) {
632       saw_match = true;
633       break;
634     }
635   return !saw_match;
636 }
637
638 int astring::char_find_any(const astring &to_find, int position, bool reverse,
639     bool case_sense, bool invert_find) const
640 {
641   if (position < 0) return common::OUT_OF_RANGE;
642   if (position > end()) return common::OUT_OF_RANGE;
643   if (reverse) {
644     for (int i = position; i >= 0; i--) {
645       if (!invert_find) {
646         if (case_sense && matches_any(get(i), to_find)) return i;
647         else if (imatches_any(get(i), to_find)) return i;
648       } else {
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;
652       }
653     }
654   } else {
655     for (int i = position; i < length(); i++) {
656       if (!invert_find) {
657         if (case_sense && matches_any(get(i), to_find)) return i;
658         else if (imatches_any(get(i), to_find)) return i;
659       } else {
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;
663       }
664     }
665   }
666   return common::NOT_FOUND;
667 }
668
669 int astring::find(const astring &to_find, int posn, bool reverse) const
670 { return str_find(to_find, posn, reverse, true); }
671
672 int astring::ifind(const astring &to_find, int posn, bool reverse) const
673 { return str_find(to_find, posn, reverse, false); }
674
675 int astring::str_find(const astring &to_find, int posn, bool reverse,
676     bool case_sense) const
677 {
678   bounds_return(posn, 0, end(), common::OUT_OF_RANGE);
679   if (!to_find.length()) return common::BAD_INPUT;
680
681   // skip some steps by finding the first place that the first character of
682   // the string resides in our string.
683   if (case_sense)
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;
687   
688 //hmmm: there is a better way to do this loop in terms of the number of
689 //      comparisons performed.  knuth morris pratt algorithm?
690   if (case_sense) {
691 //hmmm: this could use strncmp too?
692     if (reverse) {
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(),
697               to_find.length()))
698           return i;
699     } else {
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(),
708               to_find.length()))
709           return i;
710       }
711     }
712   } else {
713     // not case-sensitive.
714     if (reverse) {
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()))
719           return i;
720     } else {
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()))
724           return i;
725     }
726   }
727   return common::NOT_FOUND;
728 }
729
730 astring astring::operator + (const astring &s1) const
731 {
732   astring to_return(*this);
733   to_return += s1;
734   return to_return;
735 }
736
737 char &astring::operator [] (int position)
738 {
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));
743   return to_return;
744 }
745
746 const char &astring::operator [] (int position) const
747 {
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));
752   return to_return;
753 }
754
755 int astring::convert(int default_value) const
756 {
757   if (!length()) return default_value;
758   int to_return;
759   int fields = sscanf(observe(), "%d", &to_return);
760   if (fields < 1) return default_value;
761   return to_return;
762 }
763
764 long astring::convert(long default_value) const
765 {
766   if (!length()) return default_value;
767   long to_return;
768   int fields = sscanf(observe(), "%ld", &to_return);
769   if (fields < 1) return default_value;
770   return to_return;
771 }
772
773 float astring::convert(float default_value) const
774 {
775   if (!length()) return default_value;
776   float to_return;
777   int fields = sscanf(observe(), "%f", &to_return);
778   if (fields < 1) return default_value;
779   return to_return;
780 }
781
782 double astring::convert(double default_value) const
783 {
784   if (!length()) return default_value;
785   double to_return;
786   int fields = sscanf(observe(), "%lf", &to_return);
787   if (fields < 1) return default_value;
788   return to_return;
789 }
790
791 astring &astring::operator += (const char *s1)
792 {
793   if (!s1) return *this;
794   int len = length();
795   c_character_manager.insert(len, int(strlen(s1)));
796   memmove((char *)&c_character_manager[len], s1, int(strlen(s1)));
797   return *this;
798 }
799
800 astring &astring::operator += (char s1)
801 {
802   int len = length();
803   c_character_manager.insert(len, 1);
804   c_character_manager.put(len, s1);
805   return *this;
806 }
807
808 bool astring::compare(const astring &to_compare, int start_first,
809   int start_second, int count, bool case_sensitive) const
810 {
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);
815
816   if (!case_sensitive) {
817     return !strncasecmp(&observe()[start_first],
818         &to_compare.observe()[start_second], count);
819   } else {
820     return !memcmp((void *)&observe()[start_first],
821         (void *)&to_compare.observe()[start_second], count);
822   }
823 }
824
825 /*
826 int astring::icompare(const char *to_compare, int length_in) const
827 {
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))
832     real_len = length();
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.
843     return -1;
844   } else
845     return to_return;
846 }
847 */
848
849 /*
850 bool astring::oy_icompare(const astring &to_compare, int start_first,
851     int start_second, int count) const
852 {
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);
860 }
861 */
862
863 bool astring::substring(astring &target, int start, int bender) const
864 {
865   target.reset();
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);
871   return true;
872 }
873
874 astring astring::substring(int start, int end) const
875 {
876   astring to_return;
877   substring(to_return, start, end);
878   return to_return;
879 }
880
881 astring astring::middle(int start, int count)
882 { return substring(start, start + count - 1); }
883
884 astring astring::left(int count)
885 { return substring(0, count - 1); }
886
887 astring astring::right(int count)
888 { return substring(end() - count + 1, end()); }
889
890 void astring::insert(int position, const astring &to_insert)
891 {
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.
896   } else {
897     c_character_manager.insert(position, to_insert.length());
898     c_character_manager.overwrite(position, to_insert.c_character_manager,
899         to_insert.length());
900   }
901 }
902
903 bool astring::replace(const astring &tag, const astring &replacement)
904 {
905   int where = find(tag);
906   if (negative(where)) return false;
907   zap(where, where + tag.end());
908   insert(where, replacement);
909   return true;
910 }
911
912 bool astring::replace_all(const astring &to_replace, const astring &new_string)
913 {
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.
922     did_any = true;
923   }
924   return did_any;
925 }
926
927 bool astring::replace_all(char to_replace, char new_char)
928 {
929   bool did_any = false;
930   for (int i = 0; i < length(); i++) {
931     if (get(i) == to_replace) {
932       put(i, new_char);
933       did_any = true;
934     }
935   }
936   return did_any;
937 }
938
939 bool astring::matches(const astring &match_list, char to_match)
940 {
941   for (int i = 0; i < match_list.length(); i++)
942     if (to_match == match_list.get(i)) return true;
943   return false;
944 }
945
946 void astring::strip(const astring &strip_list, how_to_strip way)
947 {
948   if (way & FROM_FRONT)
949     while (length() && matches(strip_list, get(0)))
950       zap(0, 0);
951
952   if (way & FROM_END)
953     while (length() && matches(strip_list, get(end())))
954       zap(end(), end());
955 }
956
957 int astring::packed_size() const { return length() + 1; }
958
959 void astring::pack(byte_array &target) const
960 { attach(target, (char *)c_character_manager.observe()); }
961
962 bool astring::unpack(byte_array &source)
963 { return detach(source, *this); }
964
965 ///int astring::icompare(const astring &to_compare, int length_in) const
966 ///{ return icompare(to_compare.observe(), length_in); }
967
968 /*
969 int astring::slow_strncasecmp(const char *first, const char *second, int length)
970 {
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);
980   }
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.
987   }
988   // at this point we know second is equal to first up to the length of
989   // first.
990   if (len2 > len1) return -1;  // second was longer and therefore greater.
991   return 0;  // equal.
992 }
993 */
994
995 //////////////
996
997 a_sprintf::a_sprintf() : astring() {}
998
999 a_sprintf::a_sprintf(const astring &s) : astring(s) {}
1000
1001 a_sprintf::a_sprintf(const char *initial, ...)
1002 : astring()
1003 {
1004   if (!initial) return;
1005   va_list args;
1006   va_start(args, initial);
1007   base_sprintf(initial, args);
1008   va_end(args);
1009 }
1010
1011 //////////////
1012
1013 void attach(byte_array &packed_form, const char *to_attach)
1014 {
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);
1019 }
1020
1021 bool detach(byte_array &packed_form, astring &to_detach)
1022 {
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.
1028   if (!zero_posn) {
1029     // nope, never saw a zero.  good thing we checked.
1030     to_detach.reset();
1031     return false;
1032   }
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
1037   // zero character.
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);
1041   return true;
1042 }
1043
1044 //////////////
1045
1046 // contract fulfillment area.
1047
1048 base_string &astring::concatenate_string(const base_string &s)
1049 {
1050   const astring *cast = dynamic_cast<const astring *>(&s);
1051   if (cast) *this += *cast;
1052   else *this += astring(s.observe());
1053   return *this;
1054 }
1055
1056 base_string &astring::concatenate_char(char c)
1057 {
1058   *this += c;
1059   return *this;
1060 }
1061
1062 base_string &astring::assign(const base_string &s)
1063 {
1064   const astring *cast = dynamic_cast<const astring *>(&s);
1065   if (cast) *this = *cast;
1066   else *this = astring(s.observe());
1067   return *this;
1068 }
1069
1070 base_string &astring::upgrade(const char *s)
1071 {
1072   *this = s;
1073   return *this;
1074 }
1075
1076 bool astring::sub_string(base_string &target, int start, int end) const
1077 {
1078   astring *cast = dynamic_cast<astring *>(&target);
1079   if (!cast) throw "error: astring::sub_string: unknown type";
1080   return substring(*cast, start, end);
1081 }
1082
1083 bool astring::sub_compare(const base_string &to_compare, int start_first,
1084     int start_second, int count, bool case_sensitive) const
1085 {
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);
1090 }
1091
1092 void astring::insert(int position, const base_string &to_insert)
1093 {
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()));
1097 }
1098
1099 } //namespace.
1100