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
37namespace basis {
38
39const int LONGEST_SPRINTF = 600; // the longest a simple sprintf can be here.
40
41const 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.
47
48const abyte empty_char_star[] = { 0 };
49 // used to initialize empty strings.
50
52
53bool astring_comparator(const astring &a, const astring &b) { return a.equal_to(b); }
54
55int 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
69astring::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
90astring::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
99astring::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
126astring::~astring() { c_held_string = NULL_POINTER; }
127
128const astring &astring::empty_string() { return bogonic<astring>(); }
129
130void astring::text_form(base_string &state_fill) const { state_fill.assign(*this); }
131
132int astring::length() const { return c_character_manager.length() - 1; }
133
134byte_array &astring::get_implementation() { return c_character_manager; }
135
136char *astring::access() { return (char *)c_character_manager.access(); }
137
138char astring::get(int index) const { return (char)c_character_manager.get(index); }
139
140const char *astring::observe() const
141{ return (const char *)c_character_manager.observe(); }
142
143bool 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
149bool 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
156int astring::comparator(const astring &s2) const
157{ return strcmp(observe(), s2.observe()); }
158
159bool astring::equal_to(const char *that) const
160{ return strcmp(observe(), that) == 0; }
161
162bool 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
174astring &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
183astring &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
233void 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
256void 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
283void 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
313void 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
329void 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.
470void 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
496void 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
504void 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
524void 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
559void 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
568bool astring::iequals(const astring &that) const
569{ return strcasecmp(observe(), that.observe()) == 0; }
570
571bool astring::iequals(const char *that) const
572{ return strcasecmp(observe(), that) == 0; }
573
574int astring::ifind(char to_find, int position, bool reverse) const
575{ return char_find(to_find, position, reverse, false); }
576
577int astring::find(char to_find, int position, bool reverse) const
578{ return char_find(to_find, position, reverse, true); }
579
580int astring::find_any(const char *to_find, int position, bool reverse) const
581{ return char_find_any(to_find, position, reverse, true); }
582
583int astring::ifind_any(const char *to_find, int position, bool reverse) const
584{ return char_find_any(to_find, position, reverse, false); }
585
586int 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
590char simple_lower(char input)
591{
592 if ( (input <= 'Z') && (input >= 'A') ) return input - CASE_DIFFERENCE;
593 return input;
594}
595
596int 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
618bool 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
625bool 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
632bool 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
643int 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
674int astring::find(const astring &to_find, int posn, bool reverse) const
675{ return str_find(to_find, posn, reverse, true); }
676
677int astring::ifind(const astring &to_find, int posn, bool reverse) const
678{ return str_find(to_find, posn, reverse, false); }
679
680int 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
742char &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
751const 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
760int 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
769long 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
778float 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
787double 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
813bool 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/*
831int 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/*
855bool 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
868bool 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
879astring astring::substring(int start, int end) const
880{
881 astring to_return;
882 substring(to_return, start, end);
883 return to_return;
884}
885
886astring 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
895void 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
908bool 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
917bool 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
932bool 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
944bool 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
951void 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
962int astring::packed_size() const { return length() + 1; }
963
964void astring::pack(byte_array &target) const
965{ attach(target, (char *)c_character_manager.observe()); }
966
968{ return detach(source, *this); }
969
972
973/*
974int 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
1006a_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
1018void 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
1026bool 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
1081bool 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
1088bool 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
1097void 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
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
const contents * observe() const
Returns a pointer to the underlying C array of data.
Definition array.h:172
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
contents *const * internal_offset_mem() const
Gritty Internal: the start of the actual stored data.
Definition array.h:253
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
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
const char * s() const
synonym for observe. the 's' stands for "string", if that helps.
Definition astring.h:113
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 base_string & assign(const base_string &s)=0
Sets the contents of this string to "s".
virtual const char * observe() const =0
observes the underlying pointer to the zero-terminated string.
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
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
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
int calculate_proper_length(int repeat)
Definition astring.cpp:55