istring.cpp

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