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