first check-in of feisty meow codebase. many things broken still due to recent
[feisty_meow.git] / core / 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 __WIN32__
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   memset(c_character_manager.access(), initial, new_size);
73   c_character_manager.put(new_size, '\0');
74   c_held_string = (char * const *)c_character_manager.internal_offset_mem();
75 }
76
77 astring::astring(const astring &s1)
78 : base_string(),
79   c_character_manager(s1.c_character_manager),
80   c_held_string((char * const *)c_character_manager.internal_offset_mem())
81 {
82 }
83
84 astring::astring(const char *initial)
85 : c_character_manager(calculate_proper_length(initial? int(strlen(initial)) : 0))
86 {
87   c_character_manager.put(0, '\0');
88   if (!initial) return;  // bail because there's no string to copy.
89   strcpy(access(), initial);
90   c_held_string = (char * const *)c_character_manager.internal_offset_mem();
91 }
92
93 astring::astring(special_flag flag, const char *initial, ...)
94 : c_character_manager(1, empty_char_star),
95   c_held_string((char * const *)c_character_manager.internal_offset_mem())
96 {
97   if (!initial) return;
98   if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) {
99     operator = (astring(astring::SPRINTF, "unknown flag %d", flag));
100     return;
101   }
102
103   va_list args;
104   va_start(args, initial);
105
106   if (flag == UNTERMINATED) {
107     // special process for grabbing a string that has no terminating nil.  
108     int length = va_arg(args, int);  // get the length of the string out.
109     c_character_manager.reset(length, (abyte *)initial);
110     c_character_manager += abyte(0);
111     va_end(args);
112     return;
113   }
114
115   // only other flag currently supported is sprintf, so we do that...
116   base_sprintf(initial, args);
117   va_end(args);
118 }
119
120 astring::~astring() { c_held_string = NIL; }
121
122 const astring &astring::empty_string() { return bogonic<astring>(); }
123
124 void astring::text_form(base_string &state_fill) const { state_fill.assign(*this); }
125
126 int astring::length() const { return c_character_manager.length() - 1; }
127
128 byte_array &astring::get_implementation() { return c_character_manager; }
129
130 char *astring::access() { return (char *)c_character_manager.access(); }
131
132 char astring::get(int index) const { return (char)c_character_manager.get(index); }
133
134 const char *astring::observe() const
135 { return (const char *)c_character_manager.observe(); }
136
137 bool astring::equal_to(const equalizable &s2) const
138 {
139   const astring *s2_cast = cast_or_throw(s2, *this);
140   return comparator(*s2_cast) == 0;
141 }
142
143 bool astring::less_than(const orderable &s2) const
144 {
145   const astring *s2_cast = dynamic_cast<const astring *>(&s2);
146   if (!s2_cast) throw "error: astring::<: unknown type";
147   return comparator(*s2_cast) < 0;
148 }
149
150 int astring::comparator(const astring &s2) const
151 { return strcmp(observe(), s2.observe()); }
152
153 bool astring::equal_to(const char *that) const
154 { return strcmp(observe(), that) == 0; }
155
156 bool astring::contains(const astring &to_find) const
157 { return (find(to_find, 0) < 0) ? false : true; }
158
159 astring &astring::operator += (const astring &s1)
160 { insert(length(), s1); return *this; }
161
162 void astring::shrink()
163 {
164   astring copy_of_this(observe());
165   c_character_manager.swap_contents(copy_of_this.c_character_manager);
166 }
167
168 astring &astring::sprintf(const char *initial, ...)
169 {
170   va_list args;
171   va_start(args, initial);
172   astring &to_return = base_sprintf(initial, args);
173   va_end(args);
174   return to_return;
175 }
176
177 astring &astring::base_sprintf(const char *initial, va_list &args)
178 {
179   reset();
180   if (!initial) return *this;  // skip null strings.
181   if (!initial[0]) return *this;  // skip empty strings.
182
183   // these accumulate parts of the sprintf format within the loop.
184   char flag_chars[23], width_chars[23], precision_chars[23], modifier_chars[23];
185
186   // thanks for the inspiration to k&r page 156.
187   for (const char *traverser = initial; *traverser; traverser++) {
188 #ifdef DEBUG_STRING
189     printf("index=%d, char=%c\n", traverser - initial, *traverser);
190 #endif
191
192     if (*traverser != '%') {
193       // not a special character, so just drop it in.
194       *this += *traverser;
195       continue;
196     }
197     traverser++; // go to the next character.
198 #ifdef DEBUG_STRING
199     printf("index=%d, char=%c\n", traverser - initial, *traverser);
200 #endif
201     if (*traverser == '%') {
202       // capture the "%%" style format specifier.
203       *this += *traverser;
204       continue;
205     }
206     bool failure = false;
207       // becomes set to true if something didn't match in a necessary area.
208
209     seek_flag(traverser, flag_chars, failure);
210     if (failure) {
211       *this += '%';
212       *this += flag_chars;
213       continue;
214     }
215     seek_width(traverser, width_chars);
216     seek_precision(traverser, precision_chars);
217     seek_modifier(traverser, modifier_chars);
218     get_type_character(traverser, args, *this, flag_chars,
219         width_chars, precision_chars, modifier_chars);
220   }
221   return *this;
222 }
223
224 void astring::seek_flag(const char *&traverser, char *flag_chars, bool &failure)
225 {
226   flag_chars[0] = '\0';
227   failure = false;
228   bool keep_going = true;
229   while (!failure && keep_going) {
230     switch (*traverser) {
231       case '-': case '+': case ' ': case '\011': case '#':
232         flag_chars[strlen(flag_chars) + 1] = '\0';
233         flag_chars[strlen(flag_chars)] = *traverser++;
234         break;
235       default:
236         // we found a character that doesn't belong in the flags.
237         keep_going = false;
238         break;
239     }
240   }
241 #ifdef DEBUG_STRING
242   if (strlen(flag_chars)) printf("[flag=%s]\n", flag_chars);
243   else printf("no flags\n");
244 #endif
245 }
246
247 void astring::seek_width(const char *&traverser, char *width_chars)
248 {
249   width_chars[0] = '\0';
250   bool no_more_nums = false;
251   bool first_num = true;
252   while (!no_more_nums) {
253     char wideness[2] = { *traverser, '\0' };
254     if (first_num && (wideness[0] == '0')) {
255       strcpy(width_chars, wideness);
256       traverser++;
257     } else if (first_num && (wideness[0] == '*') ) {
258       strcpy(width_chars, wideness);
259       traverser++;
260       no_more_nums = true;
261     } else if ( (wideness[0] <= '9') && (wideness[0] >= '0') ) {
262       // a failure?
263       strcat(width_chars, wideness);
264       traverser++;
265     } else no_more_nums = true;
266     first_num = false;
267   }
268 #ifdef DEBUG_STRING
269   if (strlen(width_chars)) printf("[width=%s]\n", width_chars);
270   else printf("no widths\n");
271 #endif
272 }
273
274 void astring::seek_precision(const char *&traverser, char *precision_chars)
275 {
276   precision_chars[0] = '\0';
277   if (*traverser != '.') return;
278   strcpy(precision_chars, ".");
279   traverser++;
280   bool no_more_nums = false;
281   bool first_num = true;
282   while (!no_more_nums) {
283     char preciseness[2] = { *traverser, '\0' };
284     if (first_num && (preciseness[0] == '0')) {
285       strcat(precision_chars, preciseness);
286       traverser++;
287       no_more_nums = true;
288     } else if (first_num && (preciseness[0] == '*') ) {
289       strcat(precision_chars, preciseness);
290       traverser++;
291       no_more_nums = true;
292     } else if ( (preciseness[0] <= '9') && (preciseness[0] >= '0') ) {
293       strcat(precision_chars, preciseness);
294       traverser++;
295     } else no_more_nums = true;
296     first_num = false;
297   }
298 #ifdef DEBUG_STRING
299   if (strlen(precision_chars)) printf("[precis=%s]\n", precision_chars);
300   else printf("no precision\n");
301 #endif
302 }
303
304 void astring::seek_modifier(const char *&traverser, char *modifier_chars)
305 {
306   modifier_chars[0] = '\0';
307   switch (*traverser) {
308     case 'F': case 'N': case 'h': case 'l': case 'L': {
309         modifier_chars[strlen(modifier_chars) + 1] = '\0';
310         modifier_chars[strlen(modifier_chars)] = *traverser++;
311       break;
312     }
313   }
314 #ifdef DEBUG_STRING
315   if (strlen(modifier_chars)) printf("[mod=%s]\n", modifier_chars);
316   else printf("no modifiers\n");
317 #endif
318 }
319
320 void astring::get_type_character(const char * &traverser, va_list &args,
321     astring &output_string, const char *flag_chars, const char *width_chars,
322     const char *precision_chars, const char *modifier_chars)
323 {
324   char formatting[120];
325   strcpy(formatting, "%");
326   strcat(formatting, flag_chars);
327   strcat(formatting, width_chars);
328   strcat(formatting, precision_chars);
329   strcat(formatting, modifier_chars);
330   char tmposh[2] = { *traverser, '\0' };
331   strcat(formatting, tmposh);
332 #ifdef DEBUG_STRING
333   printf("format: %s\n", formatting);
334 #endif
335
336   enum argument_size { bits_8, bits_16, bits_32, bits_64, bits_80 };
337   bool ints_are_32_bits;
338 #ifdef __WIN32__
339   ints_are_32_bits = true;
340 #elif defined(__OS2__)
341   ints_are_32_bits = true;
342 #elif defined(__MSDOS__)
343   ints_are_32_bits = false;
344 #elif defined(__WIN32__)
345   ints_are_32_bits = false;
346 #else
347   ints_are_32_bits = true;
348 #endif
349   argument_size next_argument;
350   bool use_dynamic_sprintf = false;  // for dynamic printing of strings only.
351   // get the type character first and ensure it's valid.
352   switch (*traverser) {
353     case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
354       next_argument = bits_16;
355       if (ints_are_32_bits) next_argument = bits_32;
356       break;
357     case 'f': case 'e': case 'g': case 'E': case 'G':
358       next_argument = bits_64;
359       break;
360     case 'c':
361       next_argument = bits_8;
362       break;
363     case 's':
364       next_argument = bits_32;
365       use_dynamic_sprintf = true;
366       break;
367     case 'n':
368       next_argument = bits_32; //?????
369       break;
370     case 'p':
371       next_argument = bits_32; //????
372       break;
373     default:
374       // this is an error; the character is not recognized, so spew out
375       // any characters accumulated so far as just themselves.
376 #ifdef DEBUG_STRING
377       printf("failure in type char: %c\n", *traverser);
378 #endif
379       output_string += formatting;
380       return;
381   }
382 /* hmmm: not supported yet.
383   if (width_chars && (width_chars[0] == '*')) {
384   }
385   if (precision_chars && (precision_chars[0] == '*')) {
386   }
387 */
388   if (strlen(modifier_chars)) {
389     switch (modifier_chars[0]) {
390       case 'N':  // near pointer.
391         next_argument = bits_16;
392         if (ints_are_32_bits) next_argument = bits_32;
393         break;
394       case 'F':  // far pointer.
395         next_argument = bits_32;
396         break;
397       case 'h':  // short int.
398         next_argument = bits_16;
399         break;
400       case 'l':  // long.
401         next_argument = bits_32;
402         break;
403       case 'L':  // long double;
404         next_argument = bits_80;
405         break;
406       default:
407         // a failure has occurred because the modifier character is not
408         // one of the recognized values.  everything is just spewed out.
409 #ifdef DEBUG_STRING
410         printf("failure in modifier: %s\n", modifier_chars);
411 #endif
412         output_string += formatting;
413         return;
414     }
415   }
416   // action time: the output string is given a tasty value.
417   char temp[LONGEST_SPRINTF];
418   char *temp2 = NIL;  // for dynamic only.
419   switch (next_argument) {
420 //hmmm: this switch is where support would need to be added for having two
421 //      arguments (for the '*' case).
422     case bits_8: case bits_16:
423       if (ints_are_32_bits) ::sprintf(temp, formatting, va_arg(args, long));
424       else ::sprintf(temp, formatting, va_arg(args, int));
425       break;
426     case bits_32:
427       if (use_dynamic_sprintf) {
428         // currently we only do dynamic sprintf for strings.
429         char *to_print = va_arg(args, char *);
430         // check if it's valid and if we really need to do it dynamically.
431         if (!to_print) {
432           // bogus string; put in a complaint.
433           use_dynamic_sprintf = false;
434           ::sprintf(temp, "{error:parm=NIL}");
435         } else if (strlen(to_print) < LONGEST_SPRINTF - 2) {
436           // we're within our bounds, plus some safety room, so just do a
437           // regular sprintf.
438           use_dynamic_sprintf = false;
439           ::sprintf(temp, formatting, to_print);
440         } else {
441           // it's too long, so we definitely need to do it dynamically.
442           temp2 = new char[strlen(to_print) + MAX_FIELD_FUDGE_FACTOR];
443           ::sprintf(temp2, formatting, to_print);
444         }
445       } else ::sprintf(temp, formatting, va_arg(args, void *));
446       break;
447     case bits_64:
448       ::sprintf(temp, formatting, va_arg(args, double));
449       break;
450     case bits_80:
451       ::sprintf(temp, formatting, va_arg(args, long double));
452       break;
453   }
454   if (use_dynamic_sprintf) {
455     output_string += temp2;
456     delete [] temp2;
457   } else output_string += temp;
458 }
459
460 //hmmm: de-redundify this function, which is identical to the constructor.
461 void astring::reset(special_flag flag, const char *initial, ...)
462 {
463   reset();  // clear the string out.
464   if (!initial) return;
465   if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) {
466     operator = (astring(astring::SPRINTF, "unknown flag %d", flag));
467     return;
468   }
469
470   va_list args;
471   va_start(args, initial);
472
473   if (flag == UNTERMINATED) {
474     // special process for grabbing a string that has no terminating nil.  
475     int length = va_arg(args, int);  // get the length of the string out.
476     c_character_manager.reset(length, (abyte *)initial);
477     c_character_manager += abyte(0);
478     va_end(args);
479     return;
480   }
481
482   // only other flag currently supported is sprintf, so we do that...
483   base_sprintf(initial, args);
484   va_end(args);
485 }
486
487 void astring::pad(int len, char padding)
488 {
489   if (length() >= len) return;
490   byte_array pad(len - length());
491   memset(pad.access(), padding, pad.length());
492   operator += (astring(UNTERMINATED, (char *)pad.observe(), pad.length()));
493 }
494
495 void astring::trim(int len)
496 {
497   if (length() <= len) return;
498   zap(len, end());
499 }
500
501 astring &astring::operator = (const astring &s1)
502 {
503   if (this != &s1)
504     c_character_manager = s1.c_character_manager;
505   return *this;
506 }
507
508 astring &astring::operator = (const char *s1)
509 {
510   reset();
511   *this += s1;
512   return *this;
513 }
514
515 void astring::zap(int position1, int position2)
516 {
517   bounds_return(position1, 0, end(), );
518   bounds_return(position2, 0, end(), );
519   c_character_manager.zap(position1, position2);
520 }
521
522 void astring::to_lower()
523 {
524   for (int i = 0; i < length(); i++)
525     if ( (get(i) >= 'A') && (get(i) <= 'Z') )
526       c_character_manager.put(i, char(get(i) - CASE_DIFFERENCE));
527 }
528
529 void astring::to_upper()
530 {
531   for (int i = 0; i < length(); i++)
532     if ( (get(i) >= 'a') && (get(i) <= 'z') )
533       c_character_manager.put(i, char(get(i) + CASE_DIFFERENCE));
534 }
535
536 astring astring::lower() const
537 {
538   astring to_return(*this);
539   to_return.to_lower();
540   return to_return;
541 }
542
543 astring astring::upper() const
544 {
545   astring to_return(*this);
546   to_return.to_upper();
547   return to_return;
548 }
549
550 void astring::copy(char *array_to_stuff, int how_many) const
551 {
552   if (!array_to_stuff) return;
553   array_to_stuff[0] = '\0';
554   if ( (how_many <= 0) || (length() <= 0) ) return;
555   strncpy(array_to_stuff, observe(), minimum(how_many, int(length())));
556   array_to_stuff[minimum(how_many, int(length()))] = '\0';
557 }
558
559 bool astring::iequals(const astring &that) const
560 { return strcasecmp(observe(), that.observe()) == 0; }
561
562 bool astring::iequals(const char *that) const
563 { return strcasecmp(observe(), that) == 0; }
564
565 int astring::ifind(char to_find, int position, bool reverse) const
566 { return char_find(to_find, position, reverse, false); }
567
568 int astring::find(char to_find, int position, bool reverse) const
569 { return char_find(to_find, position, reverse, true); }
570
571 int astring::find_any(const char *to_find, int position, bool reverse) const
572 { return char_find_any(to_find, position, reverse, true); }
573
574 int astring::ifind_any(const char *to_find, int position, bool reverse) const
575 { return char_find_any(to_find, position, reverse, false); }
576
577 int astring::find_non_match(const char *to_find, int position,
578     bool reverse) const
579 { return char_find_any(to_find, position, reverse, false, true); }
580
581 char simple_lower(char input)
582 {
583   if ( (input <= 'Z') && (input >= 'A') ) return input - CASE_DIFFERENCE;
584   return input;
585 }
586
587 int astring::char_find(char to_find, int position, bool reverse,
588     bool case_sense) const
589 {
590   if (position < 0) return common::OUT_OF_RANGE;
591   if (position > end()) return common::OUT_OF_RANGE;
592   if (reverse) {
593     for (int i = position; i >= 0; i--) {
594       if (case_sense && (get(i) == to_find)) return i;
595       else if (simple_lower(get(i)) == simple_lower(to_find)) return i;
596     }
597   } else {
598     if (case_sense) {
599       const char *const pos = strchr(observe() + position, to_find);
600       if (pos) return int(pos - observe());
601     } else {
602       for (int i = position; i < length(); i++)
603         if (simple_lower(get(i)) == simple_lower(to_find)) return i;
604     }
605   }
606   return common::NOT_FOUND;
607 }
608
609 bool imatches_any(char to_check, const astring &list)
610 {
611   for (int i = 0; i < list.length(); i++)
612     if (simple_lower(to_check) == simple_lower(list[i])) return true;
613   return false;
614 }
615
616 bool matches_any(char to_check, const astring &list)
617 {
618   for (int i = 0; i < list.length(); i++)
619     if (to_check == list[i]) return true;
620   return false;
621 }
622
623 bool matches_none(char to_check, const astring &list)
624 {
625   bool saw_match = false;
626   for (int i = 0; i < list.length(); i++)
627     if (to_check == list[i]) {
628       saw_match = true;
629       break;
630     }
631   return !saw_match;
632 }
633
634 int astring::char_find_any(const astring &to_find, int position, bool reverse,
635     bool case_sense, bool invert_find) const
636 {
637   if (position < 0) return common::OUT_OF_RANGE;
638   if (position > end()) return common::OUT_OF_RANGE;
639   if (reverse) {
640     for (int i = position; i >= 0; i--) {
641       if (!invert_find) {
642         if (case_sense && matches_any(get(i), to_find)) return i;
643         else if (imatches_any(get(i), to_find)) return i;
644       } else {
645 //printf("rev posn=%d char=%c", i, get(i));
646         // case-sensitivity is not used for inverted finds.
647         if (matches_none(get(i), to_find)) return i;
648       }
649     }
650   } else {
651     for (int i = position; i < length(); i++) {
652       if (!invert_find) {
653         if (case_sense && matches_any(get(i), to_find)) return i;
654         else if (imatches_any(get(i), to_find)) return i;
655       } else {
656         // case-sensitivity is not used for inverted finds.
657 //printf("fwd posn=%d char=%c", i, get(i));
658         if (matches_none(get(i), to_find)) return i;
659       }
660     }
661   }
662   return common::NOT_FOUND;
663 }
664
665 int astring::find(const astring &to_find, int posn, bool reverse) const
666 { return str_find(to_find, posn, reverse, true); }
667
668 int astring::ifind(const astring &to_find, int posn, bool reverse) const
669 { return str_find(to_find, posn, reverse, false); }
670
671 int astring::str_find(const astring &to_find, int posn, bool reverse,
672     bool case_sense) const
673 {
674   bounds_return(posn, 0, end(), common::OUT_OF_RANGE);
675   if (!to_find.length()) return common::BAD_INPUT;
676
677   // skip some steps by finding the first place that the first character of
678   // the string resides in our string.
679   if (case_sense)
680     posn = find(to_find[0], posn, reverse);
681   else posn = ifind(to_find[0], posn, reverse);
682   if (posn < 0) return common::NOT_FOUND;
683   
684 //hmmm: there is a better way to do this loop in terms of the number of
685 //      comparisons performed.  knuth morris pratt algorithm?
686   if (case_sense) {
687 //hmmm: this could use strncmp too?
688     if (reverse) {
689       if (posn > length() - to_find.length())
690         posn = length() - to_find.length();
691       for (int i = posn; i >= 0; i--)
692         if (!memcmp((void *)&observe()[i], (void *)to_find.observe(),
693               to_find.length()))
694           return i;
695     } else {
696       const int find_len = to_find.length();
697       const int str_len = length();
698       const char first_char = to_find[0];
699       bounds_return(posn, 0, str_len - find_len, common::OUT_OF_RANGE);
700       for (int i = posn - 1;
701           ( ( (i = find(first_char, i + 1)) >= 0)
702             && (str_len - i >= find_len) ); no_increment) {
703         if (!memcmp((void *)&observe()[i], (void *)to_find.observe(),
704               to_find.length()))
705           return i;
706       }
707     }
708   } else {
709     // not case-sensitive.
710     if (reverse) {
711       if (posn > length() - to_find.length())
712         posn = length() - to_find.length();
713       for (int i = posn; i >= 0; i--)
714         if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length()))
715           return i;
716     } else {
717       bounds_return(posn, 0, length() - to_find.length(), common::OUT_OF_RANGE);
718       for (int i = posn; i < length() - to_find.length() + 1; i++)
719         if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length()))
720           return i;
721     }
722   }
723   return common::NOT_FOUND;
724 }
725
726 astring astring::operator + (const astring &s1) const
727 {
728   astring to_return(*this);
729   to_return += s1;
730   return to_return;
731 }
732
733 char &astring::operator [] (int position)
734 {
735   if (position < 0) position = 0;
736   if (position > end()) position = 0;
737   abyte &found = c_character_manager.use(position);
738   char &to_return = *((char *)(&found));
739   return to_return;
740 }
741
742 const char &astring::operator [] (int position) const
743 {
744   if (position < 0) position = 0;
745   if (position > end()) position = 0;
746   const abyte &found = c_character_manager.get(position);
747   const char &to_return = *((const char *)(&found));
748   return to_return;
749 }
750
751 int astring::convert(int default_value) const
752 {
753   if (!length()) return default_value;
754   int to_return;
755   int fields = sscanf(observe(), "%d", &to_return);
756   if (fields < 1) return default_value;
757   return to_return;
758 }
759
760 long astring::convert(long default_value) const
761 {
762   if (!length()) return default_value;
763   long to_return;
764   int fields = sscanf(observe(), "%ld", &to_return);
765   if (fields < 1) return default_value;
766   return to_return;
767 }
768
769 float astring::convert(float default_value) const
770 {
771   if (!length()) return default_value;
772   float to_return;
773   int fields = sscanf(observe(), "%f", &to_return);
774   if (fields < 1) return default_value;
775   return to_return;
776 }
777
778 double astring::convert(double default_value) const
779 {
780   if (!length()) return default_value;
781   double to_return;
782   int fields = sscanf(observe(), "%lf", &to_return);
783   if (fields < 1) return default_value;
784   return to_return;
785 }
786
787 astring &astring::operator += (const char *s1)
788 {
789   if (!s1) return *this;
790   int len = length();
791   c_character_manager.insert(len, int(strlen(s1)));
792   memmove((char *)&c_character_manager[len], s1, int(strlen(s1)));
793   return *this;
794 }
795
796 astring &astring::operator += (char s1)
797 {
798   int len = length();
799   c_character_manager.insert(len, 1);
800   c_character_manager.put(len, s1);
801   return *this;
802 }
803
804 bool astring::compare(const astring &to_compare, int start_first,
805   int start_second, int count, bool case_sensitive) const
806 {
807   bounds_return(start_first, 0, end(), false);
808   bounds_return(start_second, 0, to_compare.end(), false);
809   bounds_return(start_first + count, start_first, length(), false);
810   bounds_return(start_second + count, start_second, to_compare.length(), false);
811
812   if (!case_sensitive) {
813     return !strncasecmp(&observe()[start_first],
814         &to_compare.observe()[start_second], count);
815   } else {
816     return !memcmp((void *)&observe()[start_first],
817         (void *)&to_compare.observe()[start_second], count);
818   }
819 }
820
821 /*
822 int astring::icompare(const char *to_compare, int length_in) const
823 {
824   if (!length_in) return 0;  // nothing is equal to nothing.
825   int real_len = length_in;
826   // if they're passing a negative length, we use the full length.
827   if (negative(length_in))
828     real_len = length();
829   // if we have no length, make the obvious returns now.
830   int to_compare_len = int(strlen(to_compare));
831   if (!real_len) return to_compare_len? -1 : 0;
832   // if the second string is empty, it's always less than the non-empty.
833   if (!to_compare_len) return 1;
834   int to_return = strncasecmp(observe(), to_compare, real_len);
835   if (negative(length_in) && !to_return && (to_compare_len > length()) ) {
836     // catch special case for default length when the two are equal except
837     // that the second string is longer--this means the first is less than
838     // second, not equal.
839     return -1;
840   } else
841     return to_return;
842 }
843 */
844
845 /*
846 bool astring::oy_icompare(const astring &to_compare, int start_first,
847     int start_second, int count) const
848 {
849   bounds_return(start_first, 0, end(), false);
850   bounds_return(start_second, 0, to_compare.end(), false);
851   bounds_return(start_first + count, start_first, length(), false);
852   bounds_return(start_second + count, start_second, to_compare.length(), false);
853   const char *actual_first = this->observe() + start_first;
854   const char *actual_second = to_compare.observe() + start_second;
855   return !strncasecmp(actual_first, actual_second, count);
856 }
857 */
858
859 bool astring::substring(astring &target, int start, int bender) const
860 {
861   target.reset();
862   if (bender < start) return false;
863   const int last = end();  // final position that's valid in the string.
864   bounds_return(start, 0, last, false);
865   bounds_return(bender, 0, last, false);
866   target.reset(UNTERMINATED, observe() + start, bender - start + 1);
867   return true;
868 }
869
870 astring astring::substring(int start, int end) const
871 {
872   astring to_return;
873   substring(to_return, start, end);
874   return to_return;
875 }
876
877 astring astring::middle(int start, int count)
878 { return substring(start, start + count - 1); }
879
880 astring astring::left(int count)
881 { return substring(0, count - 1); }
882
883 astring astring::right(int count)
884 { return substring(end() - count + 1, end()); }
885
886 void astring::insert(int position, const astring &to_insert)
887 {
888   bounds_return(position, 0, length(), );
889   if (this == &to_insert) {
890     astring copy_of_me(to_insert);
891     insert(position, copy_of_me);  // not recursive because no longer == me.
892   } else {
893     c_character_manager.insert(position, to_insert.length());
894     c_character_manager.overwrite(position, to_insert.c_character_manager,
895         to_insert.length());
896   }
897 }
898
899 bool astring::replace(const astring &tag, const astring &replacement)
900 {
901   int where = find(tag);
902   if (negative(where)) return false;
903   zap(where, where + tag.end());
904   insert(where, replacement);
905   return true;
906 }
907
908 bool astring::replace_all(const astring &to_replace, const astring &new_string)
909 {
910   bool did_any = false;
911   for (int i = 0; i < length(); i++) {
912     int indy = find(to_replace, i);
913     if (negative(indy)) break;  // get out since there are no more matches.
914     i = indy;  // update our position to where we found the string.
915     zap(i, i + to_replace.length() - 1);  // remove old string.
916     insert(i, new_string);  // plug the new string into the old position.
917     i += new_string.length() - 1;  // jump past what we replaced.
918     did_any = true;
919   }
920   return did_any;
921 }
922
923 bool astring::replace_all(char to_replace, char new_char)
924 {
925   bool did_any = false;
926   for (int i = 0; i < length(); i++) {
927     if (get(i) == to_replace) {
928       put(i, new_char);
929       did_any = true;
930     }
931   }
932   return did_any;
933 }
934
935 bool astring::matches(const astring &match_list, char to_match)
936 {
937   for (int i = 0; i < match_list.length(); i++)
938     if (to_match == match_list.get(i)) return true;
939   return false;
940 }
941
942 void astring::strip(const astring &strip_list, how_to_strip way)
943 {
944   if (way & FROM_FRONT)
945     while (length() && matches(strip_list, get(0)))
946       zap(0, 0);
947
948   if (way & FROM_END)
949     while (length() && matches(strip_list, get(end())))
950       zap(end(), end());
951 }
952
953 int astring::packed_size() const { return length() + 1; }
954
955 void astring::pack(byte_array &target) const
956 { attach(target, (char *)c_character_manager.observe()); }
957
958 bool astring::unpack(byte_array &source)
959 { return detach(source, *this); }
960
961 ///int astring::icompare(const astring &to_compare, int length_in) const
962 ///{ return icompare(to_compare.observe(), length_in); }
963
964 /*
965 int astring::slow_strncasecmp(const char *first, const char *second, int length)
966 {
967   int len1 = int(strlen(first));
968   int len2 = int(strlen(second));
969   if (!length) return 0;  // no characters are equal to none.
970   if (!len1 && !len2) return 0;  // equal as empty.
971   if (!len1 && len2) return -1;  // first < second.
972   if (len1 && !len2) return 1;  // first > second.
973   if (positive(length)) {
974     len1 = minimum(length, len1);
975     len2 = minimum(length, len2);
976   }
977   for (int i = 0; i < len1; i++) {
978     if (i > len2 - 1) return 1;  // first > second, had more length.
979     if (simple_lower(first[i]) < simple_lower(second[i]))
980       return -1;  // first < second.
981     if (simple_lower(first[i]) > simple_lower(second[i]))
982       return 1;  // first > second.
983   }
984   // at this point we know second is equal to first up to the length of
985   // first.
986   if (len2 > len1) return -1;  // second was longer and therefore greater.
987   return 0;  // equal.
988 }
989 */
990
991 //////////////
992
993 a_sprintf::a_sprintf() : astring() {}
994
995 a_sprintf::a_sprintf(const astring &s) : astring(s) {}
996
997 a_sprintf::a_sprintf(const char *initial, ...)
998 : astring()
999 {
1000   if (!initial) return;
1001   va_list args;
1002   va_start(args, initial);
1003   base_sprintf(initial, args);
1004   va_end(args);
1005 }
1006
1007 //////////////
1008
1009 void attach(byte_array &packed_form, const char *to_attach)
1010 {
1011   const int len = int(strlen(to_attach));
1012   const int old_pos = packed_form.last();
1013   packed_form.insert(old_pos + 1, len + 1);
1014   memmove((char *)packed_form.observe() + old_pos + 1, to_attach, len + 1);
1015 }
1016
1017 bool detach(byte_array &packed_form, astring &to_detach)
1018 {
1019   if (!packed_form.length()) return false;
1020   // locate the zero termination if possible.
1021   const void *zero_posn = memchr(packed_form.observe(), '\0',
1022       packed_form.length()); 
1023   // make sure we could find the zero termination.
1024   if (!zero_posn) {
1025     // nope, never saw a zero.  good thing we checked.
1026     to_detach.reset();
1027     return false;
1028   }
1029   // set the string up using a standard constructor since we found the zero
1030   // position; we know the string constructor will be happy.
1031   to_detach = (char *)packed_form.observe();
1032   // compute the length of the string we found based on the position of the
1033   // zero character.
1034   int find_len = int((abyte *)zero_posn - packed_form.observe());
1035   // whack the portion of the array that we consumed.
1036   packed_form.zap(0, find_len);
1037   return true;
1038 }
1039
1040 //////////////
1041
1042 // contract fulfillment area.
1043
1044 base_string &astring::concatenate_string(const base_string &s)
1045 {
1046   const astring *cast = dynamic_cast<const astring *>(&s);
1047   if (cast) *this += *cast;
1048   else *this += astring(s.observe());
1049   return *this;
1050 }
1051
1052 base_string &astring::concatenate_char(char c)
1053 {
1054   *this += c;
1055   return *this;
1056 }
1057
1058 base_string &astring::assign(const base_string &s)
1059 {
1060   const astring *cast = dynamic_cast<const astring *>(&s);
1061   if (cast) *this = *cast;
1062   else *this = astring(s.observe());
1063   return *this;
1064 }
1065
1066 base_string &astring::upgrade(const char *s)
1067 {
1068   *this = s;
1069   return *this;
1070 }
1071
1072 bool astring::sub_string(base_string &target, int start, int end) const
1073 {
1074   astring *cast = dynamic_cast<astring *>(&target);
1075   if (!cast) throw "error: astring::sub_string: unknown type";
1076   return substring(*cast, start, end);
1077 }
1078
1079 bool astring::sub_compare(const base_string &to_compare, int start_first,
1080     int start_second, int count, bool case_sensitive) const
1081 {
1082   const astring *cast = dynamic_cast<const astring *>(&to_compare);
1083   if (cast) return compare(*cast, start_first, start_second, count, case_sensitive);
1084   else return compare(astring(to_compare.observe()), start_first, start_second,
1085       count, case_sensitive);
1086 }
1087
1088 void astring::insert(int position, const base_string &to_insert)
1089 {
1090   const astring *cast = dynamic_cast<const astring *>(&to_insert);
1091   if (cast) this->insert(position, *cast);
1092   else this->insert(position, astring(to_insert.observe()));
1093 }
1094
1095 } //namespace.
1096