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