2 * str - string list routines
4 * Copyright (C) 1999-2007 David I. Bell and Ernest Bowen
6 * Primary author: David I. Bell
8 * Calc is open software; you can redistribute it and/or modify it under
9 * the terms of the version 2.1 of the GNU Lesser General Public License
10 * as published by the Free Software Foundation.
12 * Calc is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
15 * Public License for more details.
17 * A copy of version 2.1 of the GNU Lesser General Public License is
18 * distributed with calc under the filename COPYING-LGPL. You should have
19 * received a copy with calc; if not, write to Free Software Foundation, Inc.
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * @(#) $Revision: 30.2 $
23 * @(#) $Id: str.c,v 30.2 2013/08/11 08:41:38 chongo Exp $
24 * @(#) $Source: /usr/local/src/bin/calc/RCS/str.c,v $
26 * Under source code control: 1990/02/15 01:48:10
27 * File existed as early as: before 1990
29 * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/
37 #define STR_TABLECHUNK 100 /* how often to reallocate string table */
38 #define STR_CHUNK 2000 /* size of string storage allocation */
39 #define STR_UNIQUE 100 /* size of string to allocate separately */
41 STRING _nullstring_
= {"", 0, 1, NULL
};
43 STATIC
char *chartable
; /* single character string table */
46 long l_count
; /* count of strings in table */
47 long l_maxcount
; /* maximum strings storable in table */
48 size_t l_avail
; /* characters available in current string */
49 char *l_alloc
; /* next available string storage */
50 char **l_table
; /* current string table */
55 * Initialize or reinitialize a string header for use.
58 * hp structure to be inited
61 initstr(STRINGHEAD
*hp
)
63 if (hp
->h_list
== NULL
) {
64 hp
->h_list
= (char *)malloc(2000);
68 hp
->h_avail
+= hp
->h_used
;
77 * Copy a string to the end of a list of strings, and return the address
78 * of the copied string. Returns NULL if the string could not be copied.
79 * No checks are made to see if the string is already in the list.
80 * The string cannot be null or have imbedded nulls.
83 * hp header of string storage
84 * str string to be added
87 addstr(STRINGHEAD
*hp
, char *str
)
89 char *retstr
; /* returned string pointer */
90 char *list
; /* string list */
91 long newsize
; /* new size of string list */
92 size_t len
; /* length of current string */
94 if ((str
== NULL
) || (*str
== '\0'))
96 len
= strlen(str
) + 1;
97 if (hp
->h_avail
<= len
) {
98 newsize
= len
+ 2000 + hp
->h_used
+ hp
->h_avail
;
99 list
= (char *)realloc(hp
->h_list
, newsize
);
103 hp
->h_avail
= newsize
- hp
->h_used
;
105 retstr
= hp
->h_list
+ hp
->h_used
;
116 * Return a null-terminated string which consists of a single character.
117 * The table is initialized on the first call.
125 if (chartable
== NULL
) {
126 cp
= (char *)malloc(512);
128 math_error("Cannot allocate character table");
131 for (i
= 0; i
< 256; i
++) {
135 chartable
= cp
- 512;
137 return &chartable
[(ch
& 0xff) * 2];
142 * Find a string with the specified name and return its number in the
143 * string list. The first string is numbered zero. Minus one is returned
144 * if the string is not found.
147 * hp header of string storage
148 * str string to be added
151 findstr(STRINGHEAD
*hp
, char *str
)
153 register char *test
; /* string being tested */
154 size_t len
; /* length of string being found */
155 size_t testlen
; /* length of test string */
156 int index
; /* index of string */
158 if ((hp
->h_count
<= 0) || (str
== NULL
))
164 testlen
= strlen(test
);
165 if ((testlen
== len
) && (*test
== *str
) &&
166 (strcmp(test
, str
) == 0))
168 test
+= (testlen
+ 1);
176 * Return the name of a string with the given index.
177 * If the index is illegal, a pointer to an empty string is returned.
180 * hp header of string storage
184 namestr(STRINGHEAD
*hp
, long n
)
186 register char *str
; /* current string */
188 if (n
>= hp
->h_count
)
194 str
+= (strlen(str
) + 1);
201 * Useful routine to return the index of one string within another one
202 * which has the format: "str1\000str2\000str3\000...strn\0\0". Index starts
203 * at one for the first string. Returns zero if the string being checked
204 * is not contained in the formatted string.
206 * Be sure to use \000 instead of \0. ANSI-C compilers interpret "foo\0foo..."
210 * format string formatted into substrings
211 * test string to be found in formatted string
214 stringindex(char *format
, char *test
)
216 long index
; /* found index */
217 size_t len
; /* length of current piece of string */
218 size_t testlen
; /* length of test string */
220 testlen
= strlen(test
);
223 len
= strlen(format
);
224 if ((len
== testlen
) && (*format
== *test
) &&
225 (strcmp(format
, test
) == 0))
235 * Add a possibly new literal string to the literal string pool.
236 * Returns the new string address which is guaranteed to be always valid.
237 * Duplicate strings will repeatedly return the same address.
240 addliteral(char *str
)
242 register char **table
; /* table of strings */
243 char *newstr
; /* newly allocated string */
244 long count
; /* number of strings */
245 size_t len
; /* length of string to allocate */
249 return charstr(*str
);
251 * See if the string is already in the table.
253 table
= literals
.l_table
;
254 count
= literals
.l_count
;
255 while (count
-- > 0) {
256 if ((str
[0] == table
[0][0]) && (str
[1] == table
[0][1]) &&
257 (strcmp(str
, table
[0]) == 0))
262 * Make the table of string pointers larger if necessary.
264 if (literals
.l_count
>= literals
.l_maxcount
) {
265 count
= literals
.l_maxcount
+ STR_TABLECHUNK
;
266 if (literals
.l_maxcount
)
267 table
= (char **) realloc(literals
.l_table
, count
*
270 table
= (char **) malloc(count
* sizeof(char *));
272 math_error("Cannot allocate string literal table");
275 literals
.l_table
= table
;
276 literals
.l_maxcount
= count
;
278 table
= literals
.l_table
;
280 * If the new string is very long, allocate it manually.
282 len
= (len
+ 2) & ~1; /* add room for null and round up to word */
283 if (len
>= STR_UNIQUE
) {
284 newstr
= (char *)malloc(len
);
285 if (newstr
== NULL
) {
286 math_error("Cannot allocate large literal string");
290 table
[literals
.l_count
++] = newstr
;
294 * If the remaining space in the allocate string is too small,
295 * then allocate a new one.
297 if (literals
.l_avail
< len
) {
298 newstr
= (char *)malloc(STR_CHUNK
);
299 if (newstr
== NULL
) {
300 math_error("Cannot allocate new literal string");
303 literals
.l_alloc
= newstr
;
304 literals
.l_avail
= STR_CHUNK
;
307 * Allocate the new string from the allocate string.
309 newstr
= literals
.l_alloc
;
310 literals
.l_avail
-= len
;
311 literals
.l_alloc
+= len
;
312 table
[literals
.l_count
++] = newstr
;
319 stringadd(STRING
*s1
, STRING
*s2
)
325 len
= s1
->s_len
+ s2
->s_len
;
328 s
->s_str
= (char *) malloc(len
+ 1);
329 if (s
->s_str
== NULL
)
345 * stringneg reverses the characters in a string, returns null if malloc fails
348 stringneg(STRING
*str
)
357 c
= (char *) malloc(len
+ 1);
363 cfrom
= str
->s_str
+ len
;
371 stringsub(STRING
*s1
, STRING
*s2
)
378 s
= stringadd(s1
, tmp
);
385 * stringmul: repeated concatenation, reverse if negative multiplier
386 * returns null if malloc fails
389 stringmul(NUMBER
*q
, STRING
*str
)
401 q
= neg
? qneg(q
): qlink(q
);
402 tmp1
= itoq(str
->s_len
);
403 tmp2
= qmul(q
, tmp1
);
407 if (zge31b(tmp1
->num
)) {
416 return slink(&_nullstring_
);
417 c
= (char *) malloc(len
+ 1);
420 str
= neg
? stringneg(str
) : slink(str
);
428 if (++j
== str
->s_len
) {
439 stringand(STRING
*s1
, STRING
*s2
)
445 if (s1
->s_len
== 0 || s2
->s_len
== 0)
446 return slink(&_nullstring_
);
459 *c
++ = *c1
++ & *c2
++;
465 stringor(STRING
*s1
, STRING
*s2
)
471 if (s1
->s_len
< s2
->s_len
) {
475 } /* Now len(s1) >= len(s2) */
480 return slink(&_nullstring_
);
493 *c
++ = *c1
++ | *c2
++;
501 stringxor(STRING
*s1
, STRING
*s2
)
507 if (s1
->s_len
< s2
->s_len
) {
511 } /* Now len(s1) >= len(s2) */
516 return slink(&_nullstring_
);
529 *c
++ = *c1
++ ^ *c2
++;
537 stringdiff(STRING
*s1
, STRING
*s2
)
557 stringcomp(STRING
*s1
)
565 return slink(&_nullstring_
);
580 stringsegment(STRING
*s1
, long n1
, long n2
)
586 if ((n1
< 0 && n2
< 0) ||
587 ((size_t)n1
>= s1
->s_len
&& (size_t)n2
>= s1
->s_len
))
588 return slink(&_nullstring_
);
593 if ((size_t)n1
>= s1
->s_len
)
595 if ((size_t)n2
>= s1
->s_len
)
597 len
= (n1
>= n2
) ? n1
- n2
+ 1 : n2
- n1
+ 1;
607 * We prevent the c1 pointer from walking behind s1_s_str
608 * by stopping one short of the end and running the loop one
611 * We could stop the loop with just len-- > 0, but stopping
612 * short and running the loop one last time manually helps make
613 * code checkers such as insure happy.
618 /* run the loop manually one last time */
629 * stringshift shifts s1 n bits to left if n > 0, -n to the right if n < 0;
630 * octets in string considered to be in decreasing order of index, as in
631 * ... a_3 a_2 a_1 a_0. Returned string has same length as s1.
632 * Vacated bits are filled with '\0'; bits shifted off end are lost
635 stringshift(STRING
*s1
, long n
)
644 if (len
== 0 || n
== 0)
670 *--c
= ((unsigned char) *--c1
>> j
) | ch
;
671 ch
= (unsigned char) *c1
<< k
;
678 *c
++ = ((unsigned char) *c1
<< j
) | ch
;
679 ch
= (unsigned char) *c1
++ >> k
;
686 * stringcpy copies as many characters as possible
687 * from s2 to s1 and returns s1
690 stringcpy(STRING
*s1
, STRING
*s2
)
709 * stringncpy copies up to num characters from s2 to s1 and returns s1
710 * If num > size(s2) null characters are copied until s1 is full or
711 * a total of num characters have been copied
714 stringncpy(STRING
*s1
, STRING
*s2
, size_t num
)
728 if (num
> s2
->s_len
) {
729 memset(c1
, 0, num
- s2
->s_len
);
736 * stringcontent counts the number of set bits in s
739 stringcontent(STRING
*s
)
760 stringhighbit(STRING
*s
)
768 while (--i
>= 0 && *--c
== '\0');
772 for (ch
= *c
; ch
>>= 1; i
++);
777 stringlowbit(STRING
*s
)
783 for (i
= s
->s_len
, c
= s
->s_str
; i
> 0 && *c
== '\0'; i
--, c
++);
786 i
= (s
->s_len
- i
) << 3;
787 for (ch
= *c
; !(ch
& 1); ch
>>= 1, i
++);
793 * stringcompare compares first len characters of strings starting at c1, c2
794 * Returns TRUE if and only if a difference is encountered.
795 * Essentially a local version of memcmp.
798 stringcompare(char *c1
, char *c2
, long len
)
808 * stringcmp returns TRUE if strings differ, FALSE if strings equal
811 stringcmp(STRING
*s1
, STRING
*s2
)
813 if (s1
->s_len
!= s2
->s_len
)
815 return stringcompare(s1
->s_str
, s2
->s_str
, s1
->s_len
);
820 * stringrel returns 0 if strings are equal; otherwise 1 or -1 according
821 * as the greater of the first unequal characters are in the first or
822 * second string, or in the case of unequal-length strings when the compared
823 * characters are all equal, 1 or -1 according as the first or second string
827 stringrel(STRING
*s1
, STRING
*s2
)
843 while (i1
> 1 && i2
> 1 && *c1
== *c2
) {
849 if ((unsigned char) *c1
> (unsigned char) *c2
) return 1;
850 if ((unsigned char) *c1
< (unsigned char) *c2
) return -1;
851 if (i1
< i2
) return -1;
857 * str with characters c0, c1, ... is considered as a bitstream, 8 bits
858 * per character; within a character the bits ordered from low order to
859 * high order. For 0 <= i < 8 * length of str, stringbit returns 1 or 0
860 * according as the bit with index i is set or not set; other values of i
864 stringbit(STRING
*s
, long index
)
873 if ((size_t)index
>= s
->s_len
)
875 ch
= s
->s_str
[index
];
876 return (ch
>> res
) & 1;
881 stringtest(STRING
*s
)
896 * If index is in acceptable range, stringsetbit sets or resets specified
897 * bit in string s according as val is TRUE or FALSE, and returns 0.
898 * Returns 1 if index < 0; 2 if index too large.
901 stringsetbit(STRING
*s
, long index
, BOOL val
)
908 bit
= 1 << (index
& 7);
910 if ((size_t)index
>= s
->s_len
)
912 c
= &s
->s_str
[index
];
920 * stringsearch returns 0 and sets index = i if the first occurrence
921 * of s2 in s1 for start <= i < end is at index i. If no such occurrence
922 * is found, -1 is returned.
925 stringsearch(STRING
*s1
, STRING
*s2
, long start
, long end
, ZVALUE
*index
)
934 if (end
< start
+ len2
)
940 i
= end
- start
- len2
;
941 c1
= s1
->s_str
+ start
;
948 while (--j
> 0 && *c
++ == *++c2
);
950 itoz(end
- len2
- i
- 1, index
);
959 stringrsearch(STRING
*s1
, STRING
*s2
, long start
, long end
, ZVALUE
*index
)
961 long len1
, len2
, i
, j
;
962 char *c1
, *c2
, *c
, *c2top
;
971 if (end
< start
+ len2
)
977 i
= end
- start
- len2
+ 1;
978 c1
= s1
->s_str
+ end
- 1;
979 c2top
= s2
->s_str
+ len2
- 1;
987 while (--j
> 0 && *c
-- == *--c2
);
989 itoz(start
+ i
, index
);
999 * String allocation routines
1002 #define STRALLOC 100
1005 STATIC STRING
*freeStr
= NULL
;
1006 STATIC STRING
**firstStrs
= NULL
;
1007 STATIC
long blockcount
= 0;
1016 if (freeStr
== NULL
) {
1017 freeStr
= (STRING
*) malloc(sizeof (STRING
) * STRALLOC
);
1018 if (freeStr
== NULL
) {
1019 math_error("Unable to allocate memory for stralloc");
1022 freeStr
[STRALLOC
- 1].s_next
= NULL
;
1023 freeStr
[STRALLOC
- 1].s_links
= 0;
1026 * We prevent the temp pointer from walking behind freeStr
1027 * by stopping one short of the end and running the loop one
1030 * We would stop the loop with just temp >= freeStr, but
1031 * doing this helps make code checkers such as insure happy.
1033 for (temp
= freeStr
+ STRALLOC
- 2; temp
> freeStr
; --temp
) {
1034 temp
->s_next
= temp
+ 1;
1037 /* run the loop manually one last time */
1038 temp
->s_next
= temp
+ 1;
1042 if (firstStrs
== NULL
) {
1043 newfn
= (STRING
**) malloc( blockcount
* sizeof(STRING
*));
1046 realloc(firstStrs
, blockcount
* sizeof(STRING
*));
1048 if (newfn
== NULL
) {
1049 math_error("Cannot allocate new string block");
1053 firstStrs
[blockcount
- 1] = freeStr
;
1056 freeStr
= temp
->s_next
;
1064 * makestring to be called only when str is the result of a malloc
1067 makestring(char *str
)
1085 c
= (char *) malloc(2);
1087 math_error("Allocation failure for charstring");
1100 * makenewstring creates a new string by copying null-terminated str;
1104 makenewstring(char *str
)
1112 return slink(&_nullstring_
);
1113 c
= (char *) malloc(len
+ 1);
1115 math_error("malloc for makenewstring failed");
1121 memcpy(c
, str
, len
);
1128 stringcopy (STRING
*s1
)
1137 c
= malloc(len
+ 1);
1139 math_error("Malloc failed for stringcopy");
1145 memcpy(c
, s1
->s_str
, len
);
1154 if (s
->s_links
<= 0) {
1155 math_error("Argument for slink has nonpositive links!!!");
1166 if (s
->s_links
<= 0) {
1167 math_error("Argument for sfree has nonpositive links!!!");
1170 if (--s
->s_links
> 0 || s
->s_len
== 0)
1173 s
->s_next
= freeStr
;
1177 STATIC
long stringconstcount
= 0;
1178 STATIC
long stringconstavail
= 0;
1179 STATIC STRING
**stringconsttable
;
1180 #define STRCONSTALLOC 100
1185 stringconsttable
= (STRING
**) malloc(sizeof(STRING
*) * STRCONSTALLOC
);
1186 if (stringconsttable
== NULL
) {
1187 math_error("Unable to allocate constant table");
1190 stringconsttable
[0] = &_nullstring_
;
1191 stringconstcount
= 1;
1192 stringconstavail
= STRCONSTALLOC
- 1;
1196 * addstring is called only from token.c
1197 * When called, len is length of string including '\0'
1200 addstring(char *str
, size_t len
)
1205 long index
; /* index into constant table */
1206 long first
; /* first non-null position found */
1210 math_error("addstring length including trailing NUL < 1");
1212 if (stringconstavail
<= 0) {
1213 if (stringconsttable
== NULL
) {
1216 sp
= (STRING
**) realloc((char *) stringconsttable
,
1217 sizeof(STRING
*) * (stringconstcount
+ STRCONSTALLOC
));
1219 math_error("Unable to reallocate string "
1223 stringconsttable
= sp
;
1224 stringconstavail
= STRCONSTALLOC
;
1230 sp
= stringconsttable
;
1231 for (index
= 0; index
< stringconstcount
; index
++, sp
++) {
1233 if (s
->s_links
== 0) {
1240 if (s
->s_len
== len
&& stringcompare(s
->s_str
, str
, len
) == 0) {
1246 c
= (char *) malloc(len
+ 1);
1248 math_error("Unable to allocate string constant memory");
1253 memcpy(s
->s_str
, str
, len
+1);
1255 stringconsttable
[first
] = s
;
1259 stringconsttable
[stringconstcount
++] = s
;
1265 findstring(long index
)
1267 if (index
< 0 || index
>= stringconstcount
) {
1268 math_error("Bad index for findstring");
1271 return stringconsttable
[index
];
1276 freestringconstant(long index
)
1282 s
= findstring(index
);
1284 if (index
== stringconstcount
- 1) {
1285 sp
= &stringconsttable
[index
];
1286 while (stringconstcount
> 0 && (*sp
)->s_links
== 0) {
1298 unsigned char ch
, cc
;
1299 unsigned char ech
; /* for escape sequence */
1302 if (ch
>= ' ' && ch
< 127 && ch
!= '\\' && ch
!= '\"' && ch
!= '\'') {
1309 case '\n': ech
= 'n'; break;
1310 case '\r': ech
= 'r'; break;
1311 case '\t': ech
= 't'; break;
1312 case '\b': ech
= 'b'; break;
1313 case '\f': ech
= 'f'; break;
1314 case '\v': ech
= 'v'; break;
1315 case '\\': ech
= '\\'; break;
1316 case '\"': ech
= '\"'; break;
1317 case '\'': ech
= '\''; break;
1318 case 0: ech
= '0'; break;
1319 case 7: ech
= 'a'; break;
1320 case 27: ech
= 'e'; break;
1324 if (cc
>= '0' && cc
< '8') {
1335 math_chr((cc
< 10) ? '0' + cc
: 87 + cc
);
1337 math_chr((cc
< 10) ? '0' + cc
: 87 + cc
);
1343 fitstring(char *str
, long len
, long width
)
1347 unsigned char ch
, nch
;
1349 max
= (width
- 3)/2;
1353 for (i
= 0, n
= 0; i
< len
&& n
< max
; i
++) {
1354 n
+= printechar(c
++);
1360 for (n
= 0, j
= len
; j
> i
&& n
< max
; --j
, nch
= ch
) {
1363 if (ch
>= ' ' && ch
<= 127 && ch
!= '\\' && ch
!= '\"')
1367 case '\n': case '\r': case '\t': case '\b': case '\f':
1368 case '\v': case '\\': case '\"': case 7: case 27:
1371 if (ch
>= 64 || (nch
>= '0' && nch
<= '7')) {
1381 (void) printechar(c
++);
1385 strprint(STRING
*str
) {
1393 (void) printechar(c
++);
1404 printf("Index Links Length String\n");
1405 printf("----- ----- ------ ------\n");
1407 printf(" 0 %5ld 0 \"\"\n", sp
->s_links
);
1408 for (i
= 0, k
= 1, count
= 1; i
< blockcount
; i
++) {
1410 for (j
= 0; j
< STRALLOC
; j
++, k
++, sp
++) {
1411 if (sp
->s_links
> 0) {
1413 printf("%5ld %5ld %6ld \"",
1414 k
, sp
->s_links
, (long int)sp
->s_len
);
1415 fitstring(sp
->s_str
, sp
->s_len
, 50);
1420 printf("\nNumber: %ld\n", count
);
1432 printf("Index Links Length String\n");
1433 printf("----- ----- ------ ------\n");
1434 for (i
= 0; i
< stringconstcount
; i
++) {
1435 sp
= stringconsttable
[i
];
1436 if (sp
->s_links
> 0) {
1438 printf("%5ld %5ld %6ld \"",
1439 i
, sp
->s_links
, (long int)sp
->s_len
);
1440 fitstring(sp
->s_str
, sp
->s_len
, 50);
1445 printf("\nNumber: %ld\n", count
);