astring.cpp

Go to the documentation of this file.
00001 /*
00002 *  Name   : astring
00003 *  Author : Chris Koeritz
00004 **
00005 * Copyright (c) 1992-$now By Author.  This program is free software; you can  *
00006 * redistribute it and/or modify it under the terms of the GNU General Public  *
00007 * License as published by the Free Software Foundation; either version 2 of   *
00008 * the License or (at your option) any later version.  This is online at:      *
00009 *     http://www.fsf.org/copyleft/gpl.html                                    *
00010 * Please send any updates to: fred@gruntose.com                               *
00011 */
00012 
00013 #include "astring.h"
00014 #include "definitions.h"
00015 #include "functions.h"
00016 #include "guards.h"
00017 
00018 #include <stdio.h>
00019 #include <stdlib.h>
00020 #include <string.h>
00021 
00022 #ifdef __WIN32__
00023   #undef strcasecmp 
00024   #undef strncasecmp 
00025   #define strcasecmp strcmpi
00026   #define strncasecmp strnicmp
00027 #endif
00028 
00029 //#define DEBUG_STRING
00030   // uncomment for debugging version.
00031 
00032 #define no_increment
00033   // macro just documents a blank parameter in the code.
00034 
00035 namespace basis {
00036 
00037 const int LONGEST_SPRINTF = 600;  // the longest a simple sprintf can be here.
00038 
00039 const char CASE_DIFFERENCE = char('A' - 'a');
00040   // the measurement of the difference between upper and lower case.
00041 
00042 // this factor is used to bias dynamic sprintfs for cases where the length
00043 // is specified, but the actual string is shorter than that length.
00044 const int MAX_FIELD_FUDGE_FACTOR = 64;
00045 
00046 const abyte empty_char_star[] = { 0 };
00047   // used to initialize empty strings.
00048 
00050 
00051 bool astring_comparator(const astring &a, const astring &b) { return a.equal_to(b); }
00052 
00053 int calculate_proper_length(int repeat) { return negative(repeat)? 1 : repeat + 1; }
00054 
00056 
00057 astring::astring()
00058 : c_character_manager(1, empty_char_star),
00059   c_held_string((char * const *)c_character_manager.internal_offset_mem())
00060 {}
00061 
00062 astring::astring(const base_string &initial)
00063 : c_character_manager(strlen(initial.observe()) + 1, (abyte *)initial.observe()),
00064   c_held_string((char * const *)c_character_manager.internal_offset_mem())
00065 {}
00066 
00067 astring::astring(char initial, int repeat)
00068 : c_character_manager(calculate_proper_length(repeat))
00069 {
00070   if (!initial) initial = ' ';  // for nulls, we use spaces.
00071   int new_size = c_character_manager.length() - 1;
00072   memset(c_character_manager.access(), initial, new_size);
00073   c_character_manager.put(new_size, '\0');
00074   c_held_string = (char * const *)c_character_manager.internal_offset_mem();
00075 }
00076 
00077 astring::astring(const astring &s1)
00078 : base_string(),
00079   c_character_manager(s1.c_character_manager),
00080   c_held_string((char * const *)c_character_manager.internal_offset_mem())
00081 {
00082 }
00083 
00084 astring::astring(const char *initial)
00085 : c_character_manager(calculate_proper_length(initial? int(strlen(initial)) : 0))
00086 {
00087   c_character_manager.put(0, '\0');
00088   if (!initial) return;  // bail because there's no string to copy.
00089   strcpy(access(), initial);
00090   c_held_string = (char * const *)c_character_manager.internal_offset_mem();
00091 }
00092 
00093 astring::astring(special_flag flag, const char *initial, ...)
00094 : c_character_manager(1, empty_char_star),
00095   c_held_string((char * const *)c_character_manager.internal_offset_mem())
00096 {
00097   if (!initial) return;
00098   if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) {
00099     operator = (astring(astring::SPRINTF, "unknown flag %d", flag));
00100     return;
00101   }
00102 
00103   va_list args;
00104   va_start(args, initial);
00105 
00106   if (flag == UNTERMINATED) {
00107     // special process for grabbing a string that has no terminating nil.  
00108     int length = va_arg(args, int);  // get the length of the string out.
00109     c_character_manager.reset(length, (abyte *)initial);
00110     c_character_manager += abyte(0);
00111     va_end(args);
00112     return;
00113   }
00114 
00115   // only other flag currently supported is sprintf, so we do that...
00116   base_sprintf(initial, args);
00117   va_end(args);
00118 }
00119 
00120 astring::~astring() { c_held_string = NIL; }
00121 
00122 const astring &astring::empty_string() { return bogonic<astring>(); }
00123 
00124 void astring::text_form(base_string &state_fill) const { state_fill.assign(*this); }
00125 
00126 int astring::length() const { return c_character_manager.length() - 1; }
00127 
00128 byte_array &astring::get_implementation() { return c_character_manager; }
00129 
00130 char *astring::access() { return (char *)c_character_manager.access(); }
00131 
00132 char astring::get(int index) const { return (char)c_character_manager.get(index); }
00133 
00134 const char *astring::observe() const
00135 { return (const char *)c_character_manager.observe(); }
00136 
00137 bool astring::equal_to(const equalizable &s2) const
00138 {
00139   const astring *s2_cast = cast_or_throw(s2, *this);
00140   return comparator(*s2_cast) == 0;
00141 }
00142 
00143 bool astring::less_than(const orderable &s2) const
00144 {
00145   const astring *s2_cast = dynamic_cast<const astring *>(&s2);
00146   if (!s2_cast) throw "error: astring::<: unknown type";
00147   return comparator(*s2_cast) < 0;
00148 }
00149 
00150 int astring::comparator(const astring &s2) const
00151 { return strcmp(observe(), s2.observe()); }
00152 
00153 bool astring::equal_to(const char *that) const
00154 { return strcmp(observe(), that) == 0; }
00155 
00156 bool astring::contains(const astring &to_find) const
00157 { return (find(to_find, 0) < 0) ? false : true; }
00158 
00159 astring &astring::operator += (const astring &s1)
00160 { insert(length(), s1); return *this; }
00161 
00162 void astring::shrink()
00163 {
00164   astring copy_of_this(observe());
00165   c_character_manager.swap_contents(copy_of_this.c_character_manager);
00166 }
00167 
00168 astring &astring::sprintf(const char *initial, ...)
00169 {
00170   va_list args;
00171   va_start(args, initial);
00172   astring &to_return = base_sprintf(initial, args);
00173   va_end(args);
00174   return to_return;
00175 }
00176 
00177 astring &astring::base_sprintf(const char *initial, va_list &args)
00178 {
00179   reset();
00180   if (!initial) return *this;  // skip null strings.
00181   if (!initial[0]) return *this;  // skip empty strings.
00182 
00183   // these accumulate parts of the sprintf format within the loop.
00184   char flag_chars[23], width_chars[23], precision_chars[23], modifier_chars[23];
00185 
00186   // thanks for the inspiration to k&r page 156.
00187   for (const char *traverser = initial; *traverser; traverser++) {
00188 #ifdef DEBUG_STRING
00189     printf("index=%d, char=%c\n", traverser - initial, *traverser);
00190 #endif
00191 
00192     if (*traverser != '%') {
00193       // not a special character, so just drop it in.
00194       *this += *traverser;
00195       continue;
00196     }
00197     traverser++; // go to the next character.
00198 #ifdef DEBUG_STRING
00199     printf("index=%d, char=%c\n", traverser - initial, *traverser);
00200 #endif
00201     if (*traverser == '%') {
00202       // capture the "%%" style format specifier.
00203       *this += *traverser;
00204       continue;
00205     }
00206     bool failure = false;
00207       // becomes set to true if something didn't match in a necessary area.
00208 
00209     seek_flag(traverser, flag_chars, failure);
00210     if (failure) {
00211       *this += '%';
00212       *this += flag_chars;
00213       continue;
00214     }
00215     seek_width(traverser, width_chars);
00216     seek_precision(traverser, precision_chars);
00217     seek_modifier(traverser, modifier_chars);
00218     get_type_character(traverser, args, *this, flag_chars,
00219         width_chars, precision_chars, modifier_chars);
00220   }
00221   return *this;
00222 }
00223 
00224 void astring::seek_flag(const char *&traverser, char *flag_chars, bool &failure)
00225 {
00226   flag_chars[0] = '\0';
00227   failure = false;
00228   bool keep_going = true;
00229   while (!failure && keep_going) {
00230     switch (*traverser) {
00231       case '-': case '+': case ' ': case '\011': case '#':
00232         flag_chars[strlen(flag_chars) + 1] = '\0';
00233         flag_chars[strlen(flag_chars)] = *traverser++;
00234         break;
00235       default:
00236         // we found a character that doesn't belong in the flags.
00237         keep_going = false;
00238         break;
00239     }
00240   }
00241 #ifdef DEBUG_STRING
00242   if (strlen(flag_chars)) printf("[flag=%s]\n", flag_chars);
00243   else printf("no flags\n");
00244 #endif
00245 }
00246 
00247 void astring::seek_width(const char *&traverser, char *width_chars)
00248 {
00249   width_chars[0] = '\0';
00250   bool no_more_nums = false;
00251   bool first_num = true;
00252   while (!no_more_nums) {
00253     char wideness[2] = { *traverser, '\0' };
00254     if (first_num && (wideness[0] == '0')) {
00255       strcpy(width_chars, wideness);
00256       traverser++;
00257     } else if (first_num && (wideness[0] == '*') ) {
00258       strcpy(width_chars, wideness);
00259       traverser++;
00260       no_more_nums = true;
00261     } else if ( (wideness[0] <= '9') && (wideness[0] >= '0') ) {
00262       // a failure?
00263       strcat(width_chars, wideness);
00264       traverser++;
00265     } else no_more_nums = true;
00266     first_num = false;
00267   }
00268 #ifdef DEBUG_STRING
00269   if (strlen(width_chars)) printf("[width=%s]\n", width_chars);
00270   else printf("no widths\n");
00271 #endif
00272 }
00273 
00274 void astring::seek_precision(const char *&traverser, char *precision_chars)
00275 {
00276   precision_chars[0] = '\0';
00277   if (*traverser != '.') return;
00278   strcpy(precision_chars, ".");
00279   traverser++;
00280   bool no_more_nums = false;
00281   bool first_num = true;
00282   while (!no_more_nums) {
00283     char preciseness[2] = { *traverser, '\0' };
00284     if (first_num && (preciseness[0] == '0')) {
00285       strcat(precision_chars, preciseness);
00286       traverser++;
00287       no_more_nums = true;
00288     } else if (first_num && (preciseness[0] == '*') ) {
00289       strcat(precision_chars, preciseness);
00290       traverser++;
00291       no_more_nums = true;
00292     } else if ( (preciseness[0] <= '9') && (preciseness[0] >= '0') ) {
00293       strcat(precision_chars, preciseness);
00294       traverser++;
00295     } else no_more_nums = true;
00296     first_num = false;
00297   }
00298 #ifdef DEBUG_STRING
00299   if (strlen(precision_chars)) printf("[precis=%s]\n", precision_chars);
00300   else printf("no precision\n");
00301 #endif
00302 }
00303 
00304 void astring::seek_modifier(const char *&traverser, char *modifier_chars)
00305 {
00306   modifier_chars[0] = '\0';
00307   switch (*traverser) {
00308     case 'F': case 'N': case 'h': case 'l': case 'L': {
00309         modifier_chars[strlen(modifier_chars) + 1] = '\0';
00310         modifier_chars[strlen(modifier_chars)] = *traverser++;
00311       break;
00312     }
00313   }
00314 #ifdef DEBUG_STRING
00315   if (strlen(modifier_chars)) printf("[mod=%s]\n", modifier_chars);
00316   else printf("no modifiers\n");
00317 #endif
00318 }
00319 
00320 void astring::get_type_character(const char * &traverser, va_list &args,
00321     astring &output_string, const char *flag_chars, const char *width_chars,
00322     const char *precision_chars, const char *modifier_chars)
00323 {
00324   char formatting[120];
00325   strcpy(formatting, "%");
00326   strcat(formatting, flag_chars);
00327   strcat(formatting, width_chars);
00328   strcat(formatting, precision_chars);
00329   strcat(formatting, modifier_chars);
00330   char tmposh[2] = { *traverser, '\0' };
00331   strcat(formatting, tmposh);
00332 #ifdef DEBUG_STRING
00333   printf("format: %s\n", formatting);
00334 #endif
00335 
00336   enum argument_size { bits_8, bits_16, bits_32, bits_64, bits_80 };
00337   bool ints_are_32_bits;
00338 #ifdef __WIN32__
00339   ints_are_32_bits = true;
00340 #elif defined(__OS2__)
00341   ints_are_32_bits = true;
00342 #elif defined(__MSDOS__)
00343   ints_are_32_bits = false;
00344 #elif defined(__WIN32__)
00345   ints_are_32_bits = false;
00346 #else
00347   ints_are_32_bits = true;
00348 #endif
00349   argument_size next_argument;
00350   bool use_dynamic_sprintf = false;  // for dynamic printing of strings only.
00351   // get the type character first and ensure it's valid.
00352   switch (*traverser) {
00353     case 'd': case 'i': case 'o': case 'u': case 'x': case 'X':
00354       next_argument = bits_16;
00355       if (ints_are_32_bits) next_argument = bits_32;
00356       break;
00357     case 'f': case 'e': case 'g': case 'E': case 'G':
00358       next_argument = bits_64;
00359       break;
00360     case 'c':
00361       next_argument = bits_8;
00362       break;
00363     case 's':
00364       next_argument = bits_32;
00365       use_dynamic_sprintf = true;
00366       break;
00367     case 'n':
00368       next_argument = bits_32; //?????
00369       break;
00370     case 'p':
00371       next_argument = bits_32; //????
00372       break;
00373     default:
00374       // this is an error; the character is not recognized, so spew out
00375       // any characters accumulated so far as just themselves.
00376 #ifdef DEBUG_STRING
00377       printf("failure in type char: %c\n", *traverser);
00378 #endif
00379       output_string += formatting;
00380       return;
00381   }
00382 /* hmmm: not supported yet.
00383   if (width_chars && (width_chars[0] == '*')) {
00384   }
00385   if (precision_chars && (precision_chars[0] == '*')) {
00386   }
00387 */
00388   if (strlen(modifier_chars)) {
00389     switch (modifier_chars[0]) {
00390       case 'N':  // near pointer.
00391         next_argument = bits_16;
00392         if (ints_are_32_bits) next_argument = bits_32;
00393         break;
00394       case 'F':  // far pointer.
00395         next_argument = bits_32;
00396         break;
00397       case 'h':  // short int.
00398         next_argument = bits_16;
00399         break;
00400       case 'l':  // long.
00401         next_argument = bits_32;
00402         break;
00403       case 'L':  // long double;
00404         next_argument = bits_80;
00405         break;
00406       default:
00407         // a failure has occurred because the modifier character is not
00408         // one of the recognized values.  everything is just spewed out.
00409 #ifdef DEBUG_STRING
00410         printf("failure in modifier: %s\n", modifier_chars);
00411 #endif
00412         output_string += formatting;
00413         return;
00414     }
00415   }
00416   // action time: the output string is given a tasty value.
00417   char temp[LONGEST_SPRINTF];
00418   char *temp2 = NIL;  // for dynamic only.
00419   switch (next_argument) {
00420 //hmmm: this switch is where support would need to be added for having two
00421 //      arguments (for the '*' case).
00422     case bits_8: case bits_16:
00423       if (ints_are_32_bits) ::sprintf(temp, formatting, va_arg(args, long));
00424       else ::sprintf(temp, formatting, va_arg(args, int));
00425       break;
00426     case bits_32:
00427       if (use_dynamic_sprintf) {
00428         // currently we only do dynamic sprintf for strings.
00429         char *to_print = va_arg(args, char *);
00430         // check if it's valid and if we really need to do it dynamically.
00431         if (!to_print) {
00432           // bogus string; put in a complaint.
00433           use_dynamic_sprintf = false;
00434           ::sprintf(temp, "{error:parm=NIL}");
00435         } else if (strlen(to_print) < LONGEST_SPRINTF - 2) {
00436           // we're within our bounds, plus some safety room, so just do a
00437           // regular sprintf.
00438           use_dynamic_sprintf = false;
00439           ::sprintf(temp, formatting, to_print);
00440         } else {
00441           // it's too long, so we definitely need to do it dynamically.
00442           temp2 = new char[strlen(to_print) + MAX_FIELD_FUDGE_FACTOR];
00443           ::sprintf(temp2, formatting, to_print);
00444         }
00445       } else ::sprintf(temp, formatting, va_arg(args, void *));
00446       break;
00447     case bits_64:
00448       ::sprintf(temp, formatting, va_arg(args, double));
00449       break;
00450     case bits_80:
00451       ::sprintf(temp, formatting, va_arg(args, long double));
00452       break;
00453   }
00454   if (use_dynamic_sprintf) {
00455     output_string += temp2;
00456     delete [] temp2;
00457   } else output_string += temp;
00458 }
00459 
00460 //hmmm: de-redundify this function, which is identical to the constructor.
00461 void astring::reset(special_flag flag, const char *initial, ...)
00462 {
00463   reset();  // clear the string out.
00464   if (!initial) return;
00465   if ( (flag != UNTERMINATED) && (flag != SPRINTF) ) {
00466     operator = (astring(astring::SPRINTF, "unknown flag %d", flag));
00467     return;
00468   }
00469 
00470   va_list args;
00471   va_start(args, initial);
00472 
00473   if (flag == UNTERMINATED) {
00474     // special process for grabbing a string that has no terminating nil.  
00475     int length = va_arg(args, int);  // get the length of the string out.
00476     c_character_manager.reset(length, (abyte *)initial);
00477     c_character_manager += abyte(0);
00478     va_end(args);
00479     return;
00480   }
00481 
00482   // only other flag currently supported is sprintf, so we do that...
00483   base_sprintf(initial, args);
00484   va_end(args);
00485 }
00486 
00487 void astring::pad(int len, char padding)
00488 {
00489   if (length() >= len) return;
00490   byte_array pad(len - length());
00491   memset(pad.access(), padding, pad.length());
00492   operator += (astring(UNTERMINATED, (char *)pad.observe(), pad.length()));
00493 }
00494 
00495 void astring::trim(int len)
00496 {
00497   if (length() <= len) return;
00498   zap(len, end());
00499 }
00500 
00501 astring &astring::operator = (const astring &s1)
00502 {
00503   if (this != &s1)
00504     c_character_manager = s1.c_character_manager;
00505   return *this;
00506 }
00507 
00508 astring &astring::operator = (const char *s1)
00509 {
00510   reset();
00511   *this += s1;
00512   return *this;
00513 }
00514 
00515 void astring::zap(int position1, int position2)
00516 {
00517   bounds_return(position1, 0, end(), );
00518   bounds_return(position2, 0, end(), );
00519   c_character_manager.zap(position1, position2);
00520 }
00521 
00522 void astring::to_lower()
00523 {
00524   for (int i = 0; i < length(); i++)
00525     if ( (get(i) >= 'A') && (get(i) <= 'Z') )
00526       c_character_manager.put(i, char(get(i) - CASE_DIFFERENCE));
00527 }
00528 
00529 void astring::to_upper()
00530 {
00531   for (int i = 0; i < length(); i++)
00532     if ( (get(i) >= 'a') && (get(i) <= 'z') )
00533       c_character_manager.put(i, char(get(i) + CASE_DIFFERENCE));
00534 }
00535 
00536 astring astring::lower() const
00537 {
00538   astring to_return(*this);
00539   to_return.to_lower();
00540   return to_return;
00541 }
00542 
00543 astring astring::upper() const
00544 {
00545   astring to_return(*this);
00546   to_return.to_upper();
00547   return to_return;
00548 }
00549 
00550 void astring::copy(char *array_to_stuff, int how_many) const
00551 {
00552   if (!array_to_stuff) return;
00553   array_to_stuff[0] = '\0';
00554   if ( (how_many <= 0) || (length() <= 0) ) return;
00555   strncpy(array_to_stuff, observe(), minimum(how_many, int(length())));
00556   array_to_stuff[minimum(how_many, int(length()))] = '\0';
00557 }
00558 
00559 bool astring::iequals(const astring &that) const
00560 { return strcasecmp(observe(), that.observe()) == 0; }
00561 
00562 bool astring::iequals(const char *that) const
00563 { return strcasecmp(observe(), that) == 0; }
00564 
00565 int astring::ifind(char to_find, int position, bool reverse) const
00566 { return char_find(to_find, position, reverse, false); }
00567 
00568 int astring::find(char to_find, int position, bool reverse) const
00569 { return char_find(to_find, position, reverse, true); }
00570 
00571 int astring::find_any(const char *to_find, int position, bool reverse) const
00572 { return char_find_any(to_find, position, reverse, true); }
00573 
00574 int astring::ifind_any(const char *to_find, int position, bool reverse) const
00575 { return char_find_any(to_find, position, reverse, false); }
00576 
00577 int astring::find_non_match(const char *to_find, int position,
00578     bool reverse) const
00579 { return char_find_any(to_find, position, reverse, false, true); }
00580 
00581 char simple_lower(char input)
00582 {
00583   if ( (input <= 'Z') && (input >= 'A') ) return input - CASE_DIFFERENCE;
00584   return input;
00585 }
00586 
00587 int astring::char_find(char to_find, int position, bool reverse,
00588     bool case_sense) const
00589 {
00590   if (position < 0) return common::OUT_OF_RANGE;
00591   if (position > end()) return common::OUT_OF_RANGE;
00592   if (reverse) {
00593     for (int i = position; i >= 0; i--) {
00594       if (case_sense && (get(i) == to_find)) return i;
00595       else if (simple_lower(get(i)) == simple_lower(to_find)) return i;
00596     }
00597   } else {
00598     if (case_sense) {
00599       const char *const pos = strchr(observe() + position, to_find);
00600       if (pos) return int(pos - observe());
00601     } else {
00602       for (int i = position; i < length(); i++)
00603         if (simple_lower(get(i)) == simple_lower(to_find)) return i;
00604     }
00605   }
00606   return common::NOT_FOUND;
00607 }
00608 
00609 bool imatches_any(char to_check, const astring &list)
00610 {
00611   for (int i = 0; i < list.length(); i++)
00612     if (simple_lower(to_check) == simple_lower(list[i])) return true;
00613   return false;
00614 }
00615 
00616 bool matches_any(char to_check, const astring &list)
00617 {
00618   for (int i = 0; i < list.length(); i++)
00619     if (to_check == list[i]) return true;
00620   return false;
00621 }
00622 
00623 bool matches_none(char to_check, const astring &list)
00624 {
00625   bool saw_match = false;
00626   for (int i = 0; i < list.length(); i++)
00627     if (to_check == list[i]) {
00628       saw_match = true;
00629       break;
00630     }
00631   return !saw_match;
00632 }
00633 
00634 int astring::char_find_any(const astring &to_find, int position, bool reverse,
00635     bool case_sense, bool invert_find) const
00636 {
00637   if (position < 0) return common::OUT_OF_RANGE;
00638   if (position > end()) return common::OUT_OF_RANGE;
00639   if (reverse) {
00640     for (int i = position; i >= 0; i--) {
00641       if (!invert_find) {
00642         if (case_sense && matches_any(get(i), to_find)) return i;
00643         else if (imatches_any(get(i), to_find)) return i;
00644       } else {
00645 //printf("rev posn=%d char=%c", i, get(i));
00646         // case-sensitivity is not used for inverted finds.
00647         if (matches_none(get(i), to_find)) return i;
00648       }
00649     }
00650   } else {
00651     for (int i = position; i < length(); i++) {
00652       if (!invert_find) {
00653         if (case_sense && matches_any(get(i), to_find)) return i;
00654         else if (imatches_any(get(i), to_find)) return i;
00655       } else {
00656         // case-sensitivity is not used for inverted finds.
00657 //printf("fwd posn=%d char=%c", i, get(i));
00658         if (matches_none(get(i), to_find)) return i;
00659       }
00660     }
00661   }
00662   return common::NOT_FOUND;
00663 }
00664 
00665 int astring::find(const astring &to_find, int posn, bool reverse) const
00666 { return str_find(to_find, posn, reverse, true); }
00667 
00668 int astring::ifind(const astring &to_find, int posn, bool reverse) const
00669 { return str_find(to_find, posn, reverse, false); }
00670 
00671 int astring::str_find(const astring &to_find, int posn, bool reverse,
00672     bool case_sense) const
00673 {
00674   bounds_return(posn, 0, end(), common::OUT_OF_RANGE);
00675   if (!to_find.length()) return common::BAD_INPUT;
00676 
00677   // skip some steps by finding the first place that the first character of
00678   // the string resides in our string.
00679   if (case_sense)
00680     posn = find(to_find[0], posn, reverse);
00681   else posn = ifind(to_find[0], posn, reverse);
00682   if (posn < 0) return common::NOT_FOUND;
00683   
00684 //hmmm: there is a better way to do this loop in terms of the number of
00685 //      comparisons performed.  knuth morris pratt algorithm?
00686   if (case_sense) {
00687 //hmmm: this could use strncmp too?
00688     if (reverse) {
00689       if (posn > length() - to_find.length())
00690         posn = length() - to_find.length();
00691       for (int i = posn; i >= 0; i--)
00692         if (!memcmp((void *)&observe()[i], (void *)to_find.observe(),
00693               to_find.length()))
00694           return i;
00695     } else {
00696       const int find_len = to_find.length();
00697       const int str_len = length();
00698       const char first_char = to_find[0];
00699       bounds_return(posn, 0, str_len - find_len, common::OUT_OF_RANGE);
00700       for (int i = posn - 1;
00701           ( ( (i = find(first_char, i + 1)) >= 0)
00702             && (str_len - i >= find_len) ); no_increment) {
00703         if (!memcmp((void *)&observe()[i], (void *)to_find.observe(),
00704               to_find.length()))
00705           return i;
00706       }
00707     }
00708   } else {
00709     // not case-sensitive.
00710     if (reverse) {
00711       if (posn > length() - to_find.length())
00712         posn = length() - to_find.length();
00713       for (int i = posn; i >= 0; i--)
00714         if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length()))
00715           return i;
00716     } else {
00717       bounds_return(posn, 0, length() - to_find.length(), common::OUT_OF_RANGE);
00718       for (int i = posn; i < length() - to_find.length() + 1; i++)
00719         if (!strncasecmp(&observe()[i], to_find.observe(), to_find.length()))
00720           return i;
00721     }
00722   }
00723   return common::NOT_FOUND;
00724 }
00725 
00726 astring astring::operator + (const astring &s1) const
00727 {
00728   astring to_return(*this);
00729   to_return += s1;
00730   return to_return;
00731 }
00732 
00733 char &astring::operator [] (int position)
00734 {
00735   if (position < 0) position = 0;
00736   if (position > end()) position = 0;
00737   abyte &found = c_character_manager.use(position);
00738   char &to_return = *((char *)(&found));
00739   return to_return;
00740 }
00741 
00742 const char &astring::operator [] (int position) const
00743 {
00744   if (position < 0) position = 0;
00745   if (position > end()) position = 0;
00746   const abyte &found = c_character_manager.get(position);
00747   const char &to_return = *((const char *)(&found));
00748   return to_return;
00749 }
00750 
00751 int astring::convert(int default_value) const
00752 {
00753   if (!length()) return default_value;
00754   int to_return;
00755   int fields = sscanf(observe(), "%d", &to_return);
00756   if (fields < 1) return default_value;
00757   return to_return;
00758 }
00759 
00760 long astring::convert(long default_value) const
00761 {
00762   if (!length()) return default_value;
00763   long to_return;
00764   int fields = sscanf(observe(), "%ld", &to_return);
00765   if (fields < 1) return default_value;
00766   return to_return;
00767 }
00768 
00769 float astring::convert(float default_value) const
00770 {
00771   if (!length()) return default_value;
00772   float to_return;
00773   int fields = sscanf(observe(), "%f", &to_return);
00774   if (fields < 1) return default_value;
00775   return to_return;
00776 }
00777 
00778 double astring::convert(double default_value) const
00779 {
00780   if (!length()) return default_value;
00781   double to_return;
00782   int fields = sscanf(observe(), "%lf", &to_return);
00783   if (fields < 1) return default_value;
00784   return to_return;
00785 }
00786 
00787 astring &astring::operator += (const char *s1)
00788 {
00789   if (!s1) return *this;
00790   int len = length();
00791   c_character_manager.insert(len, int(strlen(s1)));
00792   memmove((char *)&c_character_manager[len], s1, int(strlen(s1)));
00793   return *this;
00794 }
00795 
00796 astring &astring::operator += (char s1)
00797 {
00798   int len = length();
00799   c_character_manager.insert(len, 1);
00800   c_character_manager.put(len, s1);
00801   return *this;
00802 }
00803 
00804 bool astring::compare(const astring &to_compare, int start_first,
00805   int start_second, int count, bool case_sensitive) const
00806 {
00807   bounds_return(start_first, 0, end(), false);
00808   bounds_return(start_second, 0, to_compare.end(), false);
00809   bounds_return(start_first + count, start_first, length(), false);
00810   bounds_return(start_second + count, start_second, to_compare.length(), false);
00811 
00812   if (!case_sensitive) {
00813     return !strncasecmp(&observe()[start_first],
00814         &to_compare.observe()[start_second], count);
00815   } else {
00816     return !memcmp((void *)&observe()[start_first],
00817         (void *)&to_compare.observe()[start_second], count);
00818   }
00819 }
00820 
00821 /*
00822 int astring::icompare(const char *to_compare, int length_in) const
00823 {
00824   if (!length_in) return 0;  // nothing is equal to nothing.
00825   int real_len = length_in;
00826   // if they're passing a negative length, we use the full length.
00827   if (negative(length_in))
00828     real_len = length();
00829   // if we have no length, make the obvious returns now.
00830   int to_compare_len = int(strlen(to_compare));
00831   if (!real_len) return to_compare_len? -1 : 0;
00832   // if the second string is empty, it's always less than the non-empty.
00833   if (!to_compare_len) return 1;
00834   int to_return = strncasecmp(observe(), to_compare, real_len);
00835   if (negative(length_in) && !to_return && (to_compare_len > length()) ) {
00836     // catch special case for default length when the two are equal except
00837     // that the second string is longer--this means the first is less than
00838     // second, not equal.
00839     return -1;
00840   } else
00841     return to_return;
00842 }
00843 */
00844 
00845 /*
00846 bool astring::oy_icompare(const astring &to_compare, int start_first,
00847     int start_second, int count) const
00848 {
00849   bounds_return(start_first, 0, end(), false);
00850   bounds_return(start_second, 0, to_compare.end(), false);
00851   bounds_return(start_first + count, start_first, length(), false);
00852   bounds_return(start_second + count, start_second, to_compare.length(), false);
00853   const char *actual_first = this->observe() + start_first;
00854   const char *actual_second = to_compare.observe() + start_second;
00855   return !strncasecmp(actual_first, actual_second, count);
00856 }
00857 */
00858 
00859 bool astring::substring(astring &target, int start, int bender) const
00860 {
00861   target.reset();
00862   if (bender < start) return false;
00863   const int last = end();  // final position that's valid in the string.
00864   bounds_return(start, 0, last, false);
00865   bounds_return(bender, 0, last, false);
00866   target.reset(UNTERMINATED, observe() + start, bender - start + 1);
00867   return true;
00868 }
00869 
00870 astring astring::substring(int start, int end) const
00871 {
00872   astring to_return;
00873   substring(to_return, start, end);
00874   return to_return;
00875 }
00876 
00877 astring astring::middle(int start, int count)
00878 { return substring(start, start + count - 1); }
00879 
00880 astring astring::left(int count)
00881 { return substring(0, count - 1); }
00882 
00883 astring astring::right(int count)
00884 { return substring(end() - count + 1, end()); }
00885 
00886 void astring::insert(int position, const astring &to_insert)
00887 {
00888   bounds_return(position, 0, length(), );
00889   if (this == &to_insert) {
00890     astring copy_of_me(to_insert);
00891     insert(position, copy_of_me);  // not recursive because no longer == me.
00892   } else {
00893     c_character_manager.insert(position, to_insert.length());
00894     c_character_manager.overwrite(position, to_insert.c_character_manager,
00895         to_insert.length());
00896   }
00897 }
00898 
00899 bool astring::replace(const astring &tag, const astring &replacement)
00900 {
00901   int where = find(tag);
00902   if (negative(where)) return false;
00903   zap(where, where + tag.end());
00904   insert(where, replacement);
00905   return true;
00906 }
00907 
00908 bool astring::replace_all(const astring &to_replace, const astring &new_string)
00909 {
00910   bool did_any = false;
00911   for (int i = 0; i < length(); i++) {
00912     int indy = find(to_replace, i);
00913     if (negative(indy)) break;  // get out since there are no more matches.
00914     i = indy;  // update our position to where we found the string.
00915     zap(i, i + to_replace.length() - 1);  // remove old string.
00916     insert(i, new_string);  // plug the new string into the old position.
00917     i += new_string.length() - 1;  // jump past what we replaced.
00918     did_any = true;
00919   }
00920   return did_any;
00921 }
00922 
00923 bool astring::replace_all(char to_replace, char new_char)
00924 {
00925   bool did_any = false;
00926   for (int i = 0; i < length(); i++) {
00927     if (get(i) == to_replace) {
00928       put(i, new_char);
00929       did_any = true;
00930     }
00931   }
00932   return did_any;
00933 }
00934 
00935 bool astring::matches(const astring &match_list, char to_match)
00936 {
00937   for (int i = 0; i < match_list.length(); i++)
00938     if (to_match == match_list.get(i)) return true;
00939   return false;
00940 }
00941 
00942 void astring::strip(const astring &strip_list, how_to_strip way)
00943 {
00944   if (way & FROM_FRONT)
00945     while (length() && matches(strip_list, get(0)))
00946       zap(0, 0);
00947 
00948   if (way & FROM_END)
00949     while (length() && matches(strip_list, get(end())))
00950       zap(end(), end());
00951 }
00952 
00953 int astring::packed_size() const { return length() + 1; }
00954 
00955 void astring::pack(byte_array &target) const
00956 { attach(target, (char *)c_character_manager.observe()); }
00957 
00958 bool astring::unpack(byte_array &source)
00959 { return detach(source, *this); }
00960 
00963 
00964 /*
00965 int astring::slow_strncasecmp(const char *first, const char *second, int length)
00966 {
00967   int len1 = int(strlen(first));
00968   int len2 = int(strlen(second));
00969   if (!length) return 0;  // no characters are equal to none.
00970   if (!len1 && !len2) return 0;  // equal as empty.
00971   if (!len1 && len2) return -1;  // first < second.
00972   if (len1 && !len2) return 1;  // first > second.
00973   if (positive(length)) {
00974     len1 = minimum(length, len1);
00975     len2 = minimum(length, len2);
00976   }
00977   for (int i = 0; i < len1; i++) {
00978     if (i > len2 - 1) return 1;  // first > second, had more length.
00979     if (simple_lower(first[i]) < simple_lower(second[i]))
00980       return -1;  // first < second.
00981     if (simple_lower(first[i]) > simple_lower(second[i]))
00982       return 1;  // first > second.
00983   }
00984   // at this point we know second is equal to first up to the length of
00985   // first.
00986   if (len2 > len1) return -1;  // second was longer and therefore greater.
00987   return 0;  // equal.
00988 }
00989 */
00990 
00992 
00993 a_sprintf::a_sprintf() : astring() {}
00994 
00995 a_sprintf::a_sprintf(const astring &s) : astring(s) {}
00996 
00997 a_sprintf::a_sprintf(const char *initial, ...)
00998 : astring()
00999 {
01000   if (!initial) return;
01001   va_list args;
01002   va_start(args, initial);
01003   base_sprintf(initial, args);
01004   va_end(args);
01005 }
01006 
01008 
01009 void attach(byte_array &packed_form, const char *to_attach)
01010 {
01011   const int len = int(strlen(to_attach));
01012   const int old_pos = packed_form.last();
01013   packed_form.insert(old_pos + 1, len + 1);
01014   memmove((char *)packed_form.observe() + old_pos + 1, to_attach, len + 1);
01015 }
01016 
01017 bool detach(byte_array &packed_form, astring &to_detach)
01018 {
01019   if (!packed_form.length()) return false;
01020   // locate the zero termination if possible.
01021   const void *zero_posn = memchr(packed_form.observe(), '\0',
01022       packed_form.length()); 
01023   // make sure we could find the zero termination.
01024   if (!zero_posn) {
01025     // nope, never saw a zero.  good thing we checked.
01026     to_detach.reset();
01027     return false;
01028   }
01029   // set the string up using a standard constructor since we found the zero
01030   // position; we know the string constructor will be happy.
01031   to_detach = (char *)packed_form.observe();
01032   // compute the length of the string we found based on the position of the
01033   // zero character.
01034   int find_len = int((abyte *)zero_posn - packed_form.observe());
01035   // whack the portion of the array that we consumed.
01036   packed_form.zap(0, find_len);
01037   return true;
01038 }
01039 
01041 
01042 // contract fulfillment area.
01043 
01044 base_string &astring::concatenate_string(const base_string &s)
01045 {
01046   const astring *cast = dynamic_cast<const astring *>(&s);
01047   if (cast) *this += *cast;
01048   else *this += astring(s.observe());
01049   return *this;
01050 }
01051 
01052 base_string &astring::concatenate_char(char c)
01053 {
01054   *this += c;
01055   return *this;
01056 }
01057 
01058 base_string &astring::assign(const base_string &s)
01059 {
01060   const astring *cast = dynamic_cast<const astring *>(&s);
01061   if (cast) *this = *cast;
01062   else *this = astring(s.observe());
01063   return *this;
01064 }
01065 
01066 base_string &astring::upgrade(const char *s)
01067 {
01068   *this = s;
01069   return *this;
01070 }
01071 
01072 bool astring::sub_string(base_string &target, int start, int end) const
01073 {
01074   astring *cast = dynamic_cast<astring *>(&target);
01075   if (!cast) throw "error: astring::sub_string: unknown type";
01076   return substring(*cast, start, end);
01077 }
01078 
01079 bool astring::sub_compare(const base_string &to_compare, int start_first,
01080     int start_second, int count, bool case_sensitive) const
01081 {
01082   const astring *cast = dynamic_cast<const astring *>(&to_compare);
01083   if (cast) return compare(*cast, start_first, start_second, count, case_sensitive);
01084   else return compare(astring(to_compare.observe()), start_first, start_second,
01085       count, case_sensitive);
01086 }
01087 
01088 void astring::insert(int position, const base_string &to_insert)
01089 {
01090   const astring *cast = dynamic_cast<const astring *>(&to_insert);
01091   if (cast) this->insert(position, *cast);
01092   else this->insert(position, astring(to_insert.observe()));
01093 }
01094 
01095 } //namespace.
01096 
Generated on Sat Jan 28 04:22:10 2012 for hoople2 project by  doxygen 1.6.3