StringClassMisc.cpp
Go to the documentation of this file.
1 /*----------------------------------------------------------------------------*/
2 /* */
3 /* Copyright (c) 1995, 2004 IBM Corporation. All rights reserved. */
4 /* Copyright (c) 2005-2009 Rexx Language Association. All rights reserved. */
5 /* */
6 /* This program and the accompanying materials are made available under */
7 /* the terms of the Common Public License v1.0 which accompanies this */
8 /* distribution. A copy is also available at the following address: */
9 /* http://www.oorexx.org/license.html */
10 /* */
11 /* Redistribution and use in source and binary forms, with or */
12 /* without modification, are permitted provided that the following */
13 /* conditions are met: */
14 /* */
15 /* Redistributions of source code must retain the above copyright */
16 /* notice, this list of conditions and the following disclaimer. */
17 /* Redistributions in binary form must reproduce the above copyright */
18 /* notice, this list of conditions and the following disclaimer in */
19 /* the documentation and/or other materials provided with the distribution. */
20 /* */
21 /* Neither the name of Rexx Language Association nor the names */
22 /* of its contributors may be used to endorse or promote products */
23 /* derived from this software without specific prior written permission. */
24 /* */
25 /* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS */
26 /* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT */
27 /* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS */
28 /* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT */
29 /* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, */
30 /* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED */
31 /* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, */
32 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY */
33 /* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING */
34 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS */
35 /* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
36 /* */
37 /*----------------------------------------------------------------------------*/
38 /******************************************************************************/
39 /* REXX Kernel */
40 /* */
41 /* Miscellaneous REXX string methods */
42 /* */
43 /******************************************************************************/
44 
45 #include <ctype.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <math.h>
49 #include "RexxCore.h"
50 #include "StringClass.hpp"
51 #include "SourceFile.hpp"
52 #include "ActivityManager.hpp"
53 #include "StringUtil.hpp"
54 
56 /*********************************************************************/
57 /* */
58 /* Function: determines valid rexx symbols and returns */
59 /* a type indicator for valid symbols that can */
60 /* be passed on to the dictionary routines. */
61 /* */
62 /*********************************************************************/
63 {
64  const char *Scan; /* string scan pointer */
65  size_t Compound; /* count of periods */
66  size_t i; /* loop counter */
67  const char *Linend; /* end of line */
68  int Type; /* return type */
69 
70  /* name too long */
71  /* or too short */
72  if (this->getLength() > (size_t)MAX_SYMBOL_LENGTH || this->getLength() == 0)
73  {
74  return STRING_BAD_VARIABLE; /* set a bad type */
75  }
76 
77  /* step to end */
78  Linend = this->getStringData() + this->getLength();
79 
80  Compound = 0; /* set compound name is no */
81  Scan = this->getStringData(); /* save start position */
82  /* while still part of symbol */
83  while (Scan < Linend && RexxSource::isSymbolCharacter(*Scan))
84  {
85 
86  if (*Scan == '.') /* a dot found.. */
87  {
88  Compound++; /* indicate a compound var */
89  }
90 
91  Scan++; /* step the pointer */
92  } /* len of symbol */
93  /* now check for exponent */
94  if (((Scan + 1) < Linend) &&
95  (*Scan == '-' || *Scan == '+') &&
96  (isdigit(this->getChar(0)) || *Scan == '.') &&
97  (toupper(*(Scan - 1)) == 'E'))
98  {
99  Scan++; /* step to next */
100 
101  while (Scan < Linend)
102  { /* while more characters */
103  if (!isdigit(*Scan)) /* if not a digit */
104  {
105  return STRING_BAD_VARIABLE; /* this isn't valid */
106  }
107  Scan++; /* step to next char */
108  }
109  }
110  if (Scan < Linend) /* use the entire string? */
111  {
112  return STRING_BAD_VARIABLE; /* no, can't be good */
113  }
114  /* now determine symbol type */
115  /* possible number? */
116  if (this->getChar(0) == '.' || isdigit(this->getChar(0)))
117  {
118 
119  /* only a period? */
120  if (Compound == 1 && this->getLength() == 1)
121  {
122  Type = STRING_LITERAL_DOT; /* yes, set the token type */
123  }
124  else if (Compound > 1) /* too many periods? */
125  {
126  Type = STRING_LITERAL; /* yes, just a literal token */
127  }
128  else
129  { /* check for a real number */
130  Type = STRING_NUMERIC; /* assume numeric for now */
131  Scan = this->getStringData(); /* point to symbol */
132  /* scan symbol, validating */
133  for (i = this->getLength() ; i != 0; i-- )
134  {
135  if (!isdigit(*Scan) && /* if not a digit and */
136  *Scan != '.') /* and not a period... */
137  {
138  break; /* finished */
139  }
140  Scan++; /* step to next character */
141  }
142  if (i > 1 && /* if tripped over an 'E' */
143  toupper(*Scan) == 'E')
144  { /* could be exponential */
145  Scan++; /* step past E */
146  i--; /* count the character */
147  /* +/- case already validated */
148  if (*Scan != '+' && *Scan != '-')
149  {
150  for (; i != 0; i--)
151  { /* scan rest of symbol */
152  if (!isdigit(*Scan))
153  { /* if not a digit... */
154  Type = STRING_LITERAL; /* not a number */
155  break;
156  }
157  Scan++; /* step to next character */
158  }
159  }
160  }
161  else if (i != 0) /* literal if stuff left */
162  {
163  Type = STRING_LITERAL; /* yes, just a literal token */
164  }
165  }
166  }
167 
168  else if (!Compound)
169  { /* not a compound so... */
170  Type = STRING_NAME; /* set the token type */
171  }
172  /* is it a stem? */
173  else if (Compound == 1 && *(Scan - 1) == '.')
174  {
175  Type = STRING_STEM; /* yes, set the token type */
176  }
177  else
178  {
179  Type = STRING_COMPOUND_NAME; /* otherwise just plain */
180  }
181  /* compound */
182  return Type; /* return the type info */
183 }
184 
185 // in behaviour
187  RexxString *info, /* target compared value */
188  RexxInteger *_length) /* minimum length */
189 /******************************************************************************/
190 /* Function: ABBREV string method */
191 /******************************************************************************/
192 {
193  size_t Len1; /* length of string1 */
194  size_t Len2; /* length of string1 */
195  size_t ChkLen; /* required check length */
196  int rc; /* compare result */
197 
198  info = stringArgument(info, OREF_positional, ARG_ONE); /* process the information string */
199  Len2 = info->getLength(); /* get the length also */
200  /* get the optional check length */
201  /* get the optional check length */
202  ChkLen = optionalLengthArgument(_length, Len2, ARG_TWO);
203  Len1 = this->getLength(); /* get this length */
204 
205  if (ChkLen == 0 && Len2 == 0) /* if null string match */
206  {
207  rc = 1; /* then we have an abbrev */
208  }
209  else if (Len1 == 0L || /* len 1 is zero, */
210  (Len2 < ChkLen) || /* or second is too short */
211  (Len1 < Len2)) /* or second is too long */
212  {
213  rc = 0; /* not an abbreviation */
214  }
215 
216  else /* now we have to check it */
217  {
218  /* do the comparison */
219  rc = !(memcmp(this->getStringData(), info->getStringData(), Len2));
220  }
221  /* return proper string value */
222  return(rc) ? IntegerOne : IntegerZero;
223 }
224 
225 
226 // in behaviour
228 {
229  // the info must be a string value
230  info = stringArgument(info, OREF_positional, ARG_ONE);
231  stringsize_t len2 = info->getLength();
232  // the check length is optional, and defaults to the length of info.
233  stringsize_t chkLen = optionalLengthArgument(_length, len2, ARG_TWO);
234 
235  stringsize_t len1 = this->getLength();
236 
237  // if a null string match is allowed, this is true
238  if (chkLen == 0 && len2 == 0)
239  {
240  return TheTrueObject;
241  }
242 
243  // if the info is a null string, no match is possible
244  // if the target string is shorter than the check length, also no match
245  // if the info string is shorter than this string, not a match.
246  if (len1 == 0 || (len2 < chkLen) || (len1 < len2))
247  {
248  return TheFalseObject;
249  }
250  /* do the comparison */
252 }
253 
254 
255 // in behaviour
257  RexxString *string2, /* other string to compare against */
258  RexxString *pad) /* optional padding character */
259 /******************************************************************************/
260 /* Function: String class COMPARE method/function. */
261 /******************************************************************************/
262 {
263  codepoint_t PadChar; /* pad character */
264  size_t MisMatch; /* mismatch location */
265  RexxInteger *Retval; /* returned result */
266  const char *String1; /* string 1 pointer */
267  const char *String2; /* string 2 pointer */
268  size_t Lead; /* leading length */
269  size_t Remainder; /* trailing length */
270  size_t i; /* loop index */
271  size_t Length1; /* first string length */
272  size_t Length2; /* second string length */
273 
274  Length1 = this->getLength(); /* get this strings length */
275  /* validate the compare string */
276  string2 = stringArgument(string2, OREF_positional, ARG_ONE);
277  Length2 = string2->getLength(); /* get the length also */
278  PadChar = optionalPadArgument(pad, ' ', ARG_TWO);/* get the pad character */
279  if (Length1 > Length2)
280  { /* first longer? */
281  String1 = this->getStringData(); /* make arg 1 first string */
282  /* arg 2 is second string */
283  String2 = string2->getStringData();
284  Lead = Length2; /* get shorter length */
285  Remainder = Length1 - Lead; /* get trailing size */
286  }
287  else
288  { /* make arg 2 first string */
289  String1 = string2->getStringData();
290  String2 = this->getStringData(); /* arg 1 is second string */
291  Lead = Length1; /* get shorter length */
292  Remainder = Length2 - Lead; /* get trailing size */
293  }
294  MisMatch = 0; /* assume they are equal */
295  i = 0; /* set the start */
296  while (i < Lead)
297  { /* if have leading compare */
298  if (String1[i] != String2[i])
299  { /* not the same? */
300  MisMatch = i + 1; /* save position, origin one */
301  break; /* exit the loop */
302  }
303  i++; /* step the index */
304  }
305  if (MisMatch == 0 && Remainder != 0)
306  { /* need to handle padding? */
307  String1 += Lead; /* step to remainder */
308  for (i = 0; i < Remainder; i++)
309  { /* scan the remainder */
310  if (String1[i] != PadChar)
311  { /* pad mismatch? */
312  MisMatch = Lead + i + 1; /* get mismatch position */
313  break; /* finished */
314  }
315  }
316  }
317  if (MisMatch == 0)
318  {
319  Retval = IntegerZero; /* this is zero */
320  }
321  else
322  {
323  Retval = new_integer(MisMatch); /* make an integer return value */
324  }
325  return Retval; /* return result string */
326 }
327 
328 
329 /**
330  * Caseless version of the compare() method.
331  *
332  * @param string2 The string to compare to.
333  * @param pad The padding character used for length mismatches.
334  *
335  * @return 0 if the two strings are equal (with padding applied), otherwise
336  * it returns the mismatch position.
337  */
338 // in behaviour
340 {
341  stringsize_t length1 = this->getLength(); /* get this strings length */
342  /* validate the compare string */
343  other = stringArgument(other, OREF_positional, ARG_ONE);
344  stringsize_t length2 = other->getLength(); /* get the length also */
345  // we uppercase the pad character now since this is caseless
346  char padChar = toupper((int)optionalPadArgument(pad, ' ', ARG_TWO));
347 
348  const char *string1;
349  const char *string2;
350  stringsize_t lead;
351  stringsize_t _remainder;
352 
353  // is the first longer?
354  if (length1 > length2)
355  {
356  string1 = this->getStringData(); /* make arg 1 first string */
357  /* arg 2 is second string */
358  string2 = other->getStringData();
359  lead = length2; /* get shorter length */
360  _remainder = length1 - lead; /* get trailing size */
361  }
362  else
363  {
364  string1 = other->getStringData(); /* make arg 2 first string */
365  string2 = this->getStringData(); /* arg 1 is second string */
366  lead = length1; /* get shorter length */
367  _remainder = length2 - lead; /* get trailing size */
368  }
369  stringsize_t i = 0; /* set the start */
370  // compare the leading parts
371  for (i = 0; i < lead; i++)
372  {
373  // have a mismatch?
374  if (toupper(string1[i]) != toupper(string2[i]))
375  {
376  return new_integer(i+1); // return the mismatch position
377  }
378  }
379  string1 += lead; // step to the remainder and scan
380  for (i = 0; i < _remainder; i++)
381  {
382  // mismatch on the pad?
383  if (toupper(string1[i]) != padChar)
384  {
385  // this is the mismatch position, return it
386  return new_integer(lead + i + 1);
387  }
388  }
389  return IntegerZero; // no mismatch, return the failure indicator
390 }
391 
392 
393 // in behaviour
395 /******************************************************************************/
396 /* Function: String class COPIES method/function */
397 /******************************************************************************/
398 {
399  size_t Count; /* copies count */
400  RexxString *Retval; /* return value */
401  size_t Len; /* copy string length */
402  char *Temp; /* copy location */
403 
404  requiredArgument(_copies, OREF_positional, ARG_ONE); /* the count is required */
405  /* get the copies count */
406  Count = _copies->requiredNonNegative(OREF_positional, ARG_ONE);
407  Len = this->getLength(); /* get argument length */
408 
409  if (Count == 0 || /* no copies requested? */
410  Len == 0 ) /* or copying a null string */
411  {
412  Retval = OREF_NULLSTRING; /* just a null string */
413  }
414  else
415  { /* get storage size */
416  /* allocate storage needed */
417  /* allocate storage needed */
418  Retval = (RexxString *)raw_string(Len * Count);
419 
420  if (Len == 1)
421  { /* if only 1 char long */
422  /* just do this with memset */
423  memset(Retval->getWritableData(), this->getChar(0), Count);
424  }
425  /* if any copies */
426  else
427  {
428  /* point to the string */
429  Temp = Retval->getWritableData();
430  while (Count--)
431  { /* copy 2 thru n copies */
432  /* copy the string */
433  memcpy(Temp, this->getStringData(), Len);
434  Temp += Len;
435  }
436  }
437  }
438  return Retval; /* return copied string */
439 }
440 
441 // in behaviour
443 /******************************************************************************/
444 /* Function: String class DATATYPE method/function */
445 /******************************************************************************/
446 {
447  if (pType != OREF_NULL)
448  { /* see if type was specified? */
449  /* yes, specified, get 1st char */
450  int type = optionalOptionArgument(pType, 0, ARG_ONE);
451  /* and call datatype routine to */
452  return StringUtil::dataType(this, type); /* determine if its type specified. */
453  }
454  /* type not specified, see if its a */
455  /* valid number */
456  return(StringUtil::dataType(this, 'N') == TheTrueObject
457  ? new_string("NUM",3) /* if so we return NUM */
458  : new_string("CHAR",4)); /* otherwise we return CHAR */
459 }
460 
461 
462 /**
463  * Do a lastpos() search for a string.
464  *
465  * @param needle The search needle.
466  * @param _start The starting position.
467  *
468  * @return the offset of the match position (origin 1). Returns 0
469  * if no match was found.
470  */
471 // in behaviour
473 {
474  return StringUtil::lastPosRexx(getStringData(), getLength(), needle, _start, _range);
475 }
476 
477 
478 /**
479  * Rexx exported version of the caselessLastPos() method.
480  *
481  * @param needle The search needle.
482  * @param _start The starting position.
483  *
484  * @return The match position. 0 means not found.
485  */
486 // in behaviour
488 {
489  // validate that this is a good string argument
490  needle = stringArgument(needle, OREF_positional, ARG_ONE);
491  // find out where to start the search. The default is at the very end.
492  size_t startPos = optionalPositionArgument(_start, getLength(), ARG_TWO);
493  size_t range = optionalLengthArgument(_range, getLength(), ARG_THREE);
494  // now perform the actual search.
495  size_t result = StringUtil::caselessLastPos(getStringData(), getLength(), needle, startPos, range);
496  return new_integer(result);
497 }
498 
499 
500 /**
501  * Primitive implementation of a lastpos search.
502  *
503  * @param needle The search needle.
504  * @param start The starting position (origin 1).
505  *
506  * @return Returns the last match position, searching back from the start
507  * position. The starting position is the right-most character
508  * of the past possible match (as if the string was truncated
509  * at start).
510  */
511 size_t RexxString::lastPos(RexxString *needle, size_t _start)
512 {
513  size_t result = StringUtil::lastPos(getStringData(), getLength(), needle, _start, getLength());
514  return result;
515 }
516 
517 
518 /**
519  * Primitive implementation of a caseless lastpos search.
520  *
521  * @param needle The search needle.
522  * @param start The starting position (origin 1).
523  *
524  * @return Returns the last match position, searching back from the start
525  * position. The starting position is the right-most character
526  * of the past possible match (as if the string was truncated
527  * at start).
528  */
529 size_t RexxString::caselessLastPos(RexxString *needle, size_t _start)
530 {
531  size_t result = StringUtil::caselessLastPos(getStringData(), getLength(), needle, _start, getLength());
532  return result;
533 }
534 
535 // in behaviour
537 /******************************************************************************/
538 /* Function: Count occurrences of one string in another. */
539 /******************************************************************************/
540 {
541  /* force needle to a string */
542  needle = stringArgument(needle, OREF_positional, ARG_ONE);
543  // delegate the counting to the string util
545 }
546 
547 
548 // in behaviour
550 /******************************************************************************/
551 /* Function: Count occurrences of one string in another. */
552 /******************************************************************************/
553 {
554  /* force needle to a string */
555  needle = stringArgument(needle, OREF_positional, ARG_ONE);
556  // delegate the counting to the string util
558 }
559 
560 // in behaviour
562 /******************************************************************************/
563 /* Function: Change strings into another string. */
564 /******************************************************************************/
565 {
566  size_t _start; /* converted start position */
567  size_t matchPos; /* last match position */
568  size_t needleLength; /* length of the needle */
569  size_t newLength; /* length of the replacement string */
570  size_t matches; /* number of replacements */
571  size_t copyLength; /* length to copy */
572  const char *source; /* point to the string source */
573  char *copyPtr; /* current copy position */
574  const char *newPtr; /* pointer to replacement data */
575  RexxString *result; /* returned result string */
576  size_t i;
577 
578  /* force needle to a string */
579  needle = stringArgument(needle, OREF_positional, ARG_ONE);
580  ProtectedObject p1(needle);
581  /* newneedle must be a string two */
582  newNeedle = stringArgument(newNeedle, OREF_positional, ARG_TWO);
583  ProtectedObject p2(newNeedle);
584 
585  // we'll only change up to a specified count. If not there, we do everything.
586  size_t count = optionalPositive(countArg, Numerics::MAX_WHOLENUMBER, OREF_positional, ARG_THREE);
587  matches = StringUtil::countStr(getStringData(), getLength(), needle); /* find the number of replacements */
588  if (matches > count) // the matches are bounded by the count
589  {
590  matches = count;
591  }
592  needleLength = needle->getLength(); /* get the length of the needle */
593  newLength = newNeedle->getLength(); /* and the replacement length */
594  /* get a proper sized string */
595  result = (RexxString *)raw_string(this->getLength() - (matches * needleLength) + (matches * newLength));
596  copyPtr = result->getWritableData(); /* point to the copy location */
597  source = this->getStringData(); /* and out own data */
598  /* and the string to replace */
599  newPtr = newNeedle->getStringData();
600  _start = 0; /* set a zero starting point */
601  for (i = 0; i < matches; i++)
602  { /* until we hit count or run out */
603  matchPos = pos(needle, _start); /* look for the next occurrence */
604  if (matchPos == 0) /* not found? */
605  {
606  break; /* get out of here */
607  }
608  copyLength = (matchPos - 1) - _start; /* get the next length to copy */
609  if (copyLength != 0)
610  { /* something to copy? */
611  /* add on the next string section */
612  memcpy(copyPtr, source + _start, copyLength);
613  copyPtr += copyLength; /* step over the copied part */
614  }
615  if (newLength != 0)
616  { /* something to replace with? */
617  memcpy(copyPtr, newPtr, newLength); /* copy over the new segment */
618  copyPtr += newLength; /* and step it over also */
619  }
620  _start = matchPos + needleLength - 1; /* step to the next position */
621  }
622  if (_start < this->getLength()) /* some remainder left? */
623  {
624  /* add it on */
625  memcpy(copyPtr, source + _start, this->getLength() - _start);
626  }
627  return result; /* finished */
628 }
629 
630 // in behaviour
632 /******************************************************************************/
633 /* Function: Change strings into another string. */
634 /******************************************************************************/
635 {
636  size_t _start; /* converted start position */
637  size_t matchPos; /* last match position */
638  size_t needleLength; /* length of the needle */
639  size_t newLength; /* length of the replacement string */
640  size_t matches; /* number of replacements */
641  size_t copyLength; /* length to copy */
642  const char *source; /* point to the string source */
643  char * copyPtr; /* current copy position */
644  const char *newPtr; /* pointer to replacement data */
645  RexxString *result; /* returned result string */
646  size_t i;
647 
648  /* force needle to a string */
649  needle = stringArgument(needle, OREF_positional, ARG_ONE);
650  ProtectedObject p1(needle);
651  /* newneedle must be a string two */
652  newNeedle = stringArgument(newNeedle, OREF_positional, ARG_TWO);
653  ProtectedObject p2(newNeedle);
654  // we'll only change up to a specified count. If not there, we do everything.
655  size_t count = optionalPositive(countArg, Numerics::MAX_WHOLENUMBER, OREF_positional, ARG_THREE);
656 
657  matches = StringUtil::caselessCountStr(getStringData(), getLength(), needle); /* find the number of replacements */
658  if (matches > count) // the matches are bounded by the count
659  {
660  matches = count;
661  }
662  needleLength = needle->getLength(); /* get the length of the needle */
663  newLength = newNeedle->getLength(); /* and the replacement length */
664  /* get a proper sized string */
665  result = (RexxString *)raw_string(this->getLength() - (matches * needleLength) + (matches * newLength));
666  copyPtr = result->getWritableData(); /* point to the copy location */
667  source = this->getStringData(); /* and out own data */
668  /* and the string to replace */
669  newPtr = newNeedle->getStringData();
670  _start = 0; /* set a zero starting point */
671  for (i = 0; i < matches; i++)
672  { /* until we hit count or run out */
673  matchPos = this->caselessPos(needle, _start); /* look for the next occurrence */
674  if (matchPos == 0) /* not found? */
675  {
676  break; /* get out of here */
677  }
678  copyLength = (matchPos - 1) - _start; /* get the next length to copy */
679  if (copyLength != 0)
680  { /* something to copy? */
681  /* add on the next string section */
682  memcpy(copyPtr, source + _start, copyLength);
683  copyPtr += copyLength; /* step over the copied part */
684  }
685  if (newLength != 0)
686  { /* something to replace with? */
687  memcpy(copyPtr, newPtr, newLength); /* copy over the new segment */
688  copyPtr += newLength; /* and step it over also */
689  }
690  _start = matchPos + needleLength - 1; /* step to the next position */
691  }
692  if (_start < this->getLength()) /* some remainder left? */
693  {
694  /* add it on */
695  memcpy(copyPtr, source + _start, this->getLength() - _start);
696  }
697  return result; /* finished */
698 }
699 
700 
701 // in behaviour
703 /******************************************************************************/
704 /* Function: String class POS method/function */
705 /******************************************************************************/
706 {
707  return StringUtil::posRexx(getStringData(), getLength(), needle, pstart, range);
708 }
709 
710 
711 /**
712  * Do a caseless search for one string in another.
713  *
714  * @param needle The search string.
715  * @param pstart The starting position for the search.
716  * @param range A maximum range for the search.
717  *
718  * @return The index of any match position, or 0 if not found.
719  */
720 // in behaviour
722 {
723  /* force needle to a string */
724  needle = stringArgument(needle, OREF_positional, ARG_ONE);
725  /* get the starting position */
726  size_t _start = optionalPositionArgument(pstart, 1, ARG_TWO);
727  size_t _range = optionalLengthArgument(range, getLength() - _start + 1, ARG_THREE);
728  /* pass on to the primitive function */
729  /* and return as an integer object */
730  size_t result = StringUtil::caselessPos(getStringData(), getLength(), needle , _start - 1, _range);
731  return new_integer(result);
732 }
733 
734 
735 /**
736  * Do a primitive level pos() search on a string.
737  *
738  * @param needle The search needle.
739  * @param _start The starting position (origin 0)
740  *
741  * @return The match position (origin 1). Returns 0 for no match.
742  */
743 size_t RexxString::pos(RexxString *needle, size_t _start)
744 {
745  size_t result = StringUtil::pos(getStringData(), getLength(), needle, _start, getLength());
746  return result;
747 }
748 
749 
750 /**
751  * Do a primitive level pos() search on a string.
752  *
753  * @param needle The search needle.
754  * @param _start The starting position (origin 0)
755  *
756  * @return The match position (origin 1). Returns 0 for no match.
757  */
758 size_t RexxString::caselessPos(RexxString *needle, size_t _start)
759 {
760  size_t result = StringUtil::caselessPos(getStringData(), getLength(), needle, _start, getLength());
761  return result;
762 }
763 
764 
765 // in behaviour
767  RexxString *tableo, /* output table */
768  RexxString *tablei, /* input table */
769  RexxString *pad, /* pad character */
770  RexxInteger *_start, // start position to translate
771  RexxInteger *_range) // length to translate
772 /******************************************************************************/
773 /* Function: String class TRANSLATE method/function */
774 /******************************************************************************/
775 {
776  RexxString *Retval; /* return value */
777  const char *OutTable; /* output table */
778  size_t OutTableLength; /* length of output table */
779  const char *InTable; /* input table */
780  char *ScanPtr; /* scanning pointer */
781  size_t ScanLength; /* scanning length */
782  size_t InTableLength; /* length of input table */
783  codepoint_t PadChar; /* pad character */
784  char ch; /* current character */
785  size_t Position; /* table position */
786 
787  /* just a simple uppercase? */
788  if (tableo == OREF_NULL && tablei == OREF_NULL && pad == OREF_NULL)
789  {
790  return this->upperRexx(_start, _range); /* return the uppercase version */
791  }
792  /* validate the tables */
793  /* validate the tables */
794  tableo = optionalStringArgument(tableo, OREF_NULLSTRING, OREF_positional, ARG_ONE);
795  ProtectedObject p1(tableo);
796  OutTableLength = tableo->getLength(); /* get the table length */
797  /* input table too */
798  tablei = optionalStringArgument(tablei, OREF_NULLSTRING, OREF_positional, ARG_TWO);
799  ProtectedObject p2(tablei);
800  InTableLength = tablei->getLength(); /* get the table length */
801  InTable = tablei->getStringData(); /* point at the input table */
802  OutTable = tableo->getStringData(); /* and the output table */
803  /* get the pad character */
804  PadChar = optionalPadArgument(pad, ' ', ARG_THREE);
805  size_t startPos = optionalPositionArgument(_start, 1, ARG_FOUR);
806  size_t range = optionalLengthArgument(_range, getLength() - startPos + 1, ARG_FOUR);
807 
808  // if nothing to translate, we can return now
809  if (startPos > getLength() || range == 0)
810  {
811  return this;
812  }
813  // cap the real range
814  range = Numerics::minVal(range, getLength() - startPos + 1);
815 
816  /* allocate space for answer */
817  /* and copy the string */
818  Retval = new_string(this->getStringData(), this->getLength());
819  ScanPtr = Retval->getWritableData() + startPos - 1; /* point to data */
820  ScanLength = range; /* get the length too */
821 
822  while (ScanLength-- != 0)
823  { /* spin thru input */
824  ch = *ScanPtr; /* get a character */
825 
826  if (tablei != OREF_NULLSTRING) /* input table specified? */
827  {
828  /* search for the character */
829  Position = StringUtil::memPos(InTable, InTableLength, ch);
830  }
831  else
832  {
833  Position = ((size_t)ch) & 0xFF; /* position is the character value */
834  }
835  if (Position != (size_t)(-1))
836  { /* found in the table? */
837  if (Position < OutTableLength) /* in the output table? */
838  {
839  /* convert the character */
840  *ScanPtr = *(OutTable + Position);
841  }
842  else
843  {
844  *ScanPtr = (char)PadChar; /* else use the pad character */
845  }
846  }
847  ScanPtr++; /* step the pointer */
848  }
849  return Retval; /* return translated string */
850 }
851 
852 
853 // in behaviour
855  RexxString *ref, /* compare reference string */
856  RexxString *option, /* Match/NoMatch option */
857  RexxInteger *_start, /* optional starg position */
858  RexxInteger *range) // length to search
859 /******************************************************************************/
860 /* Function: String class VERIFY function */
861 /******************************************************************************/
862 {
863  return StringUtil::verify(getStringData(), getLength(), ref, option, _start, range);
864 }
865 
866 
867 /**
868  * Test if regions within two strings match.
869  *
870  * @param start_ The starting compare position within the target string. This
871  * must be within the bounds of the string.
872  * @param other The other compare string.
873  * @param offset_ The starting offset of the compare string. This must be
874  * within the string bounds. The default start postion is 1.
875  * @param len_ The length of the compare substring. The length and the
876  * offset must specify a valid substring of other. If not
877  * specified, this defaults to the substring from the
878  * offset to the end of the string.
879  *
880  * @return True if the two regions match, false for any mismatch.
881  */
882 // in behaviour
884 {
885  stringsize_t _start = positionArgument(start_, ARG_ONE);
886  // the start position must be within the string bounds
887  if (_start > getLength())
888  {
890  }
891  other = stringArgument(other, OREF_positional, ARG_TWO);
892 
893  stringsize_t offset = optionalPositionArgument(offset_, 1, ARG_THREE);
894 
895  if (offset > other->getLength())
896  {
898  }
899 
900  stringsize_t len = optionalLengthArgument(len_, other->getLength() - offset + 1, ARG_FOUR);
901 
902  if ((offset + len - 1) > other->getLength())
903  {
905  }
906 
907  return primitiveMatch(_start, other, offset, len) ? TheTrueObject : TheFalseObject;
908 }
909 
910 
911 /**
912  * Test if regions within two strings match.
913  *
914  * @param start_ The starting compare position within the target string. This
915  * must be within the bounds of the string.
916  * @param other The other compare string.
917  * @param offset_ The starting offset of the compare string. This must be
918  * within the string bounds. The default start postion is 1.
919  * @param len_ The length of the compare substring. The length and the
920  * offset must specify a valid substring of other. If not
921  * specified, this defaults to the substring from the
922  * offset to the end of the string.
923  *
924  * @return True if the two regions match, false for any mismatch.
925  */
926 // in behaviour
928 {
929  stringsize_t _start = positionArgument(start_, ARG_ONE);
930  // the start position must be within the string bounds
931  if (_start > getLength())
932  {
934  }
935  other = stringArgument(other, OREF_positional, ARG_TWO);
936 
937  stringsize_t offset = optionalPositionArgument(offset_, 1, ARG_THREE);
938 
939  if (offset > other->getLength())
940  {
942  }
943 
944  stringsize_t len = optionalLengthArgument(len_, other->getLength() - offset + 1, ARG_FOUR);
945 
946  if ((offset + len - 1) > other->getLength())
947  {
949  }
950 
951  return primitiveCaselessMatch(_start, other, offset, len) ? TheTrueObject : TheFalseObject;
952 }
953 
954 
955 /**
956  * Perform a compare of regions of two string objects. Returns
957  * true if the two regions match, returns false for mismatches.
958  *
959  * @param start The starting offset within the target string.
960  * @param other The source string for the compare.
961  * @param offset The offset of the substring of the other string to use.
962  * @param len The length of the substring to compare.
963  *
964  * @return True if the regions match, false otherwise.
965  */
967 {
968  _start--; // make the starting point origin zero
969  offset--;
970 
971  // if the match is not possible in the target string, just return false now.
972  if ((_start + len) > getLength())
973  {
974  return false;
975  }
976 
977  return memcmp(getStringData() + _start, other->getStringData() + offset, len) == 0;
978 }
979 
980 
981 /**
982  * Perform a caselesee compare of regions of two string objects.
983  * Returns true if the two regions match, returns false for
984  * mismatches.
985  *
986  * @param start The starting offset within the target string.
987  * @param other The source string for the compare.
988  * @param offset The offset of the substring of the other string to use.
989  * @param len The length of the substring to compare.
990  *
991  * @return True if the regions match, false otherwise.
992  */
994 {
995  _start--; // make the starting point origin zero
996  offset--;
997 
998  // if the match is not possible in the target string, just return false now.
999  if ((_start + len) > getLength())
1000  {
1001  return false;
1002  }
1003 
1004  return StringUtil::caselessCompare(getStringData() + _start, other->getStringData() + offset, len) == 0;
1005 }
1006 
1007 
1008 /**
1009  * Compare a single character at a give position against
1010  * a set of characters to see if any of the characters is
1011  * a match.
1012  *
1013  * @param position_ The character position
1014  * @param matchSet The set to compare against.
1015  *
1016  * @return true if the character at the give position is any of the characters,
1017  * false if none of them match.
1018  */
1019 // in behaviour
1021 {
1022  stringsize_t position = positionArgument(position_, ARG_ONE);
1023  // the start position must be within the string bounds
1024  if (position > getLength())
1025  {
1027  }
1028  matchSet = stringArgument(matchSet, OREF_positional, ARG_TWO);
1029 
1030  stringsize_t _setLength = matchSet->getLength();
1031  codepoint_t _matchChar = getChar(position - 1);
1032 
1033  // iterate through the match set looking for a match
1034  for (stringsize_t i = 0; i < _setLength; i++)
1035  {
1036  if (_matchChar == matchSet->getChar(i))
1037  {
1038  return TheTrueObject;
1039  }
1040  }
1041  return TheFalseObject;
1042 }
1043 
1044 
1045 /**
1046  * Compare a single character at a give position against
1047  * a set of characters to see if any of the characters is
1048  * a match.
1049  *
1050  * @param position_ The character position
1051  * @param matchSet The set to compare against.
1052  *
1053  * @return true if the character at the give position is any of the characters,
1054  * false if none of them match.
1055  */
1056 // in behaviour
1058 {
1059  stringsize_t position = positionArgument(position_, ARG_ONE);
1060  // the start position must be within the string bounds
1061  if (position > getLength())
1062  {
1064  }
1065  matchSet = stringArgument(matchSet, OREF_positional, ARG_TWO);
1066 
1067  stringsize_t _setLength = matchSet->getLength();
1068  codepoint_t _matchChar = getChar(position - 1);
1069  _matchChar = toupper((int)_matchChar);
1070 
1071  // iterate through the match set looking for a match, using a
1072  // caseless compare
1073  for (stringsize_t i = 0; i < _setLength; i++)
1074  {
1075  if (_matchChar == toupper((int)matchSet->getChar(i)))
1076  {
1077  return TheTrueObject;
1078  }
1079  }
1080  return TheFalseObject;
1081 }
1082 
1083 
1084 /**
1085  * Do a sorting comparison of two strings.
1086  *
1087  * @param other The other compare string.
1088  * @param start_ The starting compare position within the target string.
1089  * @param len_ The length of the compare substring.
1090  *
1091  * @return True if the two regions match, false for any mismatch.
1092  */
1093 // in behaviour
1095 {
1096  other = stringArgument(other, OREF_positional, ARG_ONE);
1097 
1098  stringsize_t _start = optionalPositionArgument(start_, 1, ARG_TWO);
1099  stringsize_t len = optionalLengthArgument(len_, Numerics::maxVal(getLength(), other->getLength()) - _start + 1, ARG_THREE);
1100 
1101  return primitiveCompareTo(other, _start, len);
1102 }
1103 
1104 
1105 /**
1106  * Perform a compare of regions of two string objects. Returns
1107  * -1, 0, 1 based on the relative ordering of the two strings.
1108  *
1109  * @param other The source string for the compare.
1110  * @param start The starting offset within the target string.
1111  * @param len The length of the substring to compare.
1112  *
1113  * @return -1 if the target string is less than, 0 if the two strings are
1114  * equal, 1 if the target string is the greater.
1115  */
1117 {
1118  stringsize_t myLength = getLength();
1119  stringsize_t otherLength = other->getLength();
1120 
1121  // if doing the compare outside of the string length, we're less than the other string
1122  // unless the start is
1123  if (_start > myLength)
1124  {
1125  return _start > otherLength ? IntegerZero : IntegerMinusOne;
1126  }
1127  // if beyond the other length, they we're the larger
1128  if (_start > otherLength)
1129  {
1130  return IntegerOne;
1131  }
1132 
1133  _start--; // make the starting point origin zero
1134 
1135  myLength = Numerics::minVal(len, myLength - _start);
1136  otherLength = Numerics::minVal(len, otherLength - _start);
1137 
1138  len = Numerics::minVal(myLength, otherLength);
1139 
1140  wholenumber_t result = memcmp(getStringData() + _start, other->getStringData() + _start, len);
1141 
1142  // if they compare equal, then they are only
1143  if (result == 0)
1144  {
1145  if (myLength == otherLength)
1146  {
1147  return IntegerZero;
1148  }
1149  else if (myLength > otherLength)
1150  {
1151  return IntegerOne;
1152  }
1153  else
1154  {
1155  return IntegerMinusOne;
1156  }
1157  }
1158  else if (result > 0)
1159  {
1160  return IntegerOne;
1161  }
1162  else
1163  {
1164  return IntegerMinusOne;
1165  }
1166 }
1167 
1168 
1169 
1170 
1171 /**
1172  * Do a sorting comparison of two strings.
1173  *
1174  * @param other The other compare string.
1175  * @param start_ The starting compare position within the target string.
1176  * @param len_ The length of the compare substring.
1177  *
1178  * @return True if the two regions match, false for any mismatch.
1179  */
1180 // in behaviour
1182 {
1183  other = stringArgument(other, OREF_positional, ARG_ONE);
1184 
1185  stringsize_t _start = optionalPositionArgument(start_, 1, ARG_TWO);
1186  stringsize_t len = optionalLengthArgument(len_, Numerics::maxVal(getLength(), other->getLength()) - _start + 1, ARG_THREE);
1187 
1188  return primitiveCaselessCompareTo(other, _start, len);
1189 }
1190 
1191 
1192 
1193 
1194 /**
1195  * Perform a compare of regions of two string objects. Returns
1196  * -1, 0, 1 based on the relative ordering of the two strings.
1197  *
1198  * @param other The source string for the compare.
1199  * @param start The starting offset within the target string.
1200  * @param len The length of the substring to compare.
1201  *
1202  * @return -1 if the target string is less than, 0 if the two strings are
1203  * equal, 1 if the target string is the greater.
1204  */
1206 {
1207  stringsize_t myLength = getLength();
1208  stringsize_t otherLength = other->getLength();
1209 
1210  // if doing the compare outside of the string length, we're less than the other string
1211  // unless the start is
1212  if (_start > myLength)
1213  {
1214  return _start > otherLength ? IntegerZero : IntegerMinusOne;
1215  }
1216  // if beyond the other length, they we're the larger
1217  if (_start > otherLength)
1218  {
1219  return IntegerOne;
1220  }
1221 
1222  _start--; // make the starting point origin zero
1223 
1224  myLength = Numerics::minVal(len, myLength - _start);
1225  otherLength = Numerics::minVal(len, otherLength - _start);
1226 
1227  len = Numerics::minVal(myLength, otherLength);
1228 
1229  wholenumber_t result = StringUtil::caselessCompare(getStringData() + _start, other->getStringData() + _start, len);
1230 
1231  // if they compare equal, then they are only
1232  if (result == 0)
1233  {
1234  if (myLength == otherLength)
1235  {
1236  return IntegerZero;
1237  }
1238  else if (myLength > otherLength)
1239  {
1240  return IntegerOne;
1241  }
1242  else
1243  {
1244  return IntegerMinusOne;
1245  }
1246  }
1247  else if (result > 0)
1248  {
1249  return IntegerOne;
1250  }
1251  else
1252  {
1253  return IntegerMinusOne;
1254  }
1255 }
void reportException(wholenumber_t error)
RexxInteger * new_integer(wholenumber_t v)
codepoint_t optionalPadArgument(RexxObject *o, codepoint_t d, size_t p)
Definition: RexxCore.h:370
#define OREF_NULL
Definition: RexxCore.h:61
RexxString * stringArgument(RexxObject *object, RexxString *kind, size_t position)
Definition: RexxCore.h:315
const int ARG_FOUR
Definition: RexxCore.h:86
const int MAX_SYMBOL_LENGTH
Definition: RexxCore.h:77
#define IntegerOne
Definition: RexxCore.h:200
const int ARG_THREE
Definition: RexxCore.h:85
size_t optionalPositive(RexxObject *o, size_t d, RexxString *kind, size_t p)
Definition: RexxCore.h:387
#define TheTrueObject
Definition: RexxCore.h:196
const int ARG_TWO
Definition: RexxCore.h:84
size_t optionalLengthArgument(RexxObject *o, size_t d, size_t p)
Definition: RexxCore.h:355
char optionalOptionArgument(RexxObject *o, char d, size_t p)
Definition: RexxCore.h:377
size_t optionalPositionArgument(RexxObject *o, size_t d, size_t p)
Definition: RexxCore.h:363
#define TheFalseObject
Definition: RexxCore.h:195
const int ARG_ONE
Definition: RexxCore.h:83
RexxString * optionalStringArgument(RexxObject *o, RexxString *d, RexxString *kind, size_t p)
Definition: RexxCore.h:340
#define IntegerZero
Definition: RexxCore.h:199
void requiredArgument(RexxObject *object, RexxString *kind, size_t position)
Definition: RexxCore.h:303
#define IntegerMinusOne
Definition: RexxCore.h:209
#define Error_Incorrect_method_position
#define Error_Incorrect_method_length
#define STRING_LITERAL
Definition: StringClass.hpp:57
#define STRING_NUMERIC
Definition: StringClass.hpp:59
RexxString * raw_string(stringsize_t l)
#define STRING_NAME
Definition: StringClass.hpp:60
RexxString * new_string(const char *s, stringsize_t l)
#define STRING_COMPOUND_NAME
Definition: StringClass.hpp:56
#define STRING_STEM
Definition: StringClass.hpp:55
#define STRING_LITERAL_DOT
Definition: StringClass.hpp:58
#define STRING_BAD_VARIABLE
Definition: StringClass.hpp:54
stringsize_t positionArgument(RexxObject *argument, size_t position)
static const wholenumber_t MAX_WHOLENUMBER
Definition: Numerics.hpp:62
static wholenumber_t maxVal(wholenumber_t n1, wholenumber_t n2)
Definition: Numerics.hpp:118
static wholenumber_t minVal(wholenumber_t n1, wholenumber_t n2)
Definition: Numerics.hpp:116
stringsize_t requiredNonNegative(RexxString *kind, size_t position, size_t precision=Numerics::ARGUMENT_DIGITS)
static bool isSymbolCharacter(codepoint_t ch)
Definition: SourceFile.hpp:387
size_t lastPos(RexxString *needle, size_t start)
RexxString * changeStr(RexxString *, RexxString *, RexxInteger *)
RexxInteger * caselessLastPosRexx(RexxString *, RexxInteger *, RexxInteger *)
RexxString * translate(RexxString *, RexxString *, RexxString *, RexxInteger *, RexxInteger *)
size_t getLength()
bool primitiveMatch(stringsize_t start, RexxString *other, stringsize_t offset, stringsize_t len)
RexxString * upperRexx(RexxInteger *, RexxInteger *)
size_t caselessPos(RexxString *, size_t)
size_t pos(RexxString *, size_t)
RexxInteger * abbrev(RexxString *, RexxInteger *)
RexxInteger * caselessMatchChar(RexxInteger *position_, RexxString *matchSet)
const char * getStringData()
RexxInteger * verify(RexxString *, RexxString *, RexxInteger *, RexxInteger *)
RexxInteger * caselessMatch(RexxInteger *start_, RexxString *other, RexxInteger *offset_, RexxInteger *len_)
RexxString * caselessChangeStr(RexxString *, RexxString *, RexxInteger *)
RexxInteger * caselessPosRexx(RexxString *, RexxInteger *, RexxInteger *)
RexxInteger * match(RexxInteger *start_, RexxString *other, RexxInteger *offset_, RexxInteger *len_)
RexxInteger * caselessCountStrRexx(RexxString *)
RexxInteger * lastPosRexx(RexxString *, RexxInteger *, RexxInteger *)
RexxInteger * compareToRexx(RexxString *other, RexxInteger *start_, RexxInteger *len_)
RexxInteger * compare(RexxString *, RexxString *)
RexxInteger * caselessCompare(RexxString *, RexxString *)
char * getWritableData()
RexxInteger * matchChar(RexxInteger *position_, RexxString *matchSet)
RexxObject * dataType(RexxString *)
size_t caselessLastPos(RexxString *needle, size_t start)
RexxInteger * caselessCompareToRexx(RexxString *other, RexxInteger *start_, RexxInteger *len_)
RexxInteger * posRexx(RexxString *, RexxInteger *, RexxInteger *)
RexxString * copies(RexxInteger *)
RexxInteger * primitiveCompareTo(RexxString *other, stringsize_t start, stringsize_t len)
RexxInteger * countStrRexx(RexxString *)
RexxInteger * caselessAbbrev(RexxString *, RexxInteger *)
RexxInteger * primitiveCaselessCompareTo(RexxString *other, stringsize_t start, stringsize_t len)
char getChar(size_t p)
bool primitiveCaselessMatch(stringsize_t start, RexxString *other, stringsize_t offset, stringsize_t len)
static size_t pos(const char *stringData, size_t haystack_length, RexxString *needle, size_t _start, size_t _range)
Definition: StringUtil.cpp:155
static RexxObject * dataType(RexxString *String, char Option)
static size_t memPos(const char *string, size_t length, char target)
static size_t caselessLastPos(const char *stringData, size_t haystackLen, RexxString *needle, size_t _start, size_t range)
Definition: StringUtil.cpp:360
static int caselessCompare(const char *, const char *, size_t)
Definition: StringUtil.cpp:580
static RexxInteger * lastPosRexx(const char *stringData, size_t haystackLen, RexxString *needle, RexxInteger *_start, RexxInteger *_range)
Definition: StringUtil.cpp:255
static size_t caselessPos(const char *stringData, size_t haystack_length, RexxString *needle, size_t _start, size_t _range)
Definition: StringUtil.cpp:205
static size_t countStr(const char *hayStack, size_t hayStackLength, RexxString *needle)
static size_t caselessCountStr(const char *hayStack, size_t hayStackLength, RexxString *needle)
static RexxInteger * verify(const char *data, size_t stringLen, RexxString *ref, RexxString *option, RexxInteger *_start, RexxInteger *range)
static size_t lastPos(const char *stringData, size_t hastackLen, RexxString *needle, size_t _start, size_t _range)
Definition: StringUtil.cpp:278
static RexxInteger * posRexx(const char *stringData, size_t length, RexxString *needle, RexxInteger *pstart, RexxInteger *range)
Definition: StringUtil.cpp:130
int type
Definition: cmdparse.cpp:1888
ssize_t codepoint_t
Definition: rexx.h:232
ssize_t wholenumber_t
Definition: rexx.h:230
size_t stringsize_t
Definition: rexx.h:228