1 char * cklibv = "C-Kermit library, 8.0.033, 16 Mar 2003";
5 /* C K C L I B . C -- C-Kermit Library routines. */
8 Author: Frank da Cruz <fdc@columbia.edu>,
9 Columbia University Academic Information Systems, New York City.
11 Copyright (C) 1999, 2004,
12 Trustees of Columbia University in the City of New York.
13 All rights reserved. See the C-Kermit COPYING.TXT file or the
14 copyright text in the ckcmai.c module for disclaimer and permissions.
18 General-purpose, system/platform/compiler-independent routines for use
19 by all modules. Many are replacements for commonly used C library
20 functions that are not found on every platform, and/or that lack needed
21 functionality (e.g. caseless string search/compare) or safety features.
23 ckstrncpy() - Similar to strncpy() but different (see comments).
24 ckstrncat() - Similar to strncat() but different (see comments).
25 chartostr() - Converts a char to a string (self or ctrl char name).
26 ckstrchr() - Portable strchr().
27 ckstrpbrk() - Portable strpbrk().
28 cklower() - Lowercase a string (in place).
29 ckupper() - Uppercase a string (in place).
30 ckindex() - Left or right index.
31 ckstrstr() - Portable strstr().
32 ckitoa() - Converts int to string.
33 ckuitoa() - Converts unsigned int to string.
34 ckltoa() - Converts long to string.
35 ckultoa() - Converts unsigned long to string.
36 ckctoa() - Converts char to string.
37 ckmakmsg() - Constructs a message from 4 source strings.
38 ckmakxmsg() - Constructs a message from 12 source strings.
39 ckmatch() - Pattern matching.
40 ckmemcpy() - Portable memcpy().
41 ckrchar() - Rightmost character of a string.
42 ckstrcmp() - Possibly caseless string comparison.
43 ckstrpre() - Caseless string prefix comparison.
44 sh_sort() - Sorts an array of strings, many options.
45 brstrip() - Strips enclosing braces (and doublequotes).
46 makelist() - Splits "{{item}{item}...}" into an array.
47 makestr() - Careful malloc() front end.
48 xmakestr() - ditto (see comments).
49 ckradix() - Convert number radix (2-36).
50 b8tob64() - Convert data to base 64.
51 b64tob8() - Convert base 64 to data.
52 chknum() - Checks if string is a (possibly signed) integer.
53 rdigits() - Checks if string is composed only of decimal digits.
54 isfloat() - Checks if string is a valid floating-point number.
55 parnam() - Returns parity name string.
56 hhmmss() - Converts seconds to hh:mm:ss string.
57 lset() - Write fixed-length field left-adjusted into a record.
58 rset() - Write fixed-length field right-adjusted into a record.
59 ulongtohex() - Converts an unsigned long to a hex string.
60 hextoulong() - Converts a hex string to an unsigned long.
61 cksplit() - Splits a string into an array of words.
63 Prototypes are in ckclib.h.
65 Note: This module should not contain any extern declarations.
71 /* Public variables */
73 int dblquo = 1; /* Nonzero if doublequotes can be used for grouping */
76 ccntab[] = { /* Names of ASCII (C0) control characters 0-31 */
77 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
78 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
79 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
80 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
84 c1tab[] = { /* Names of ISO 6429 (C1) control characters 0-32 */
85 "XXX", "XXX", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
86 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
87 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
88 "SOS", "XXX", "SCI", "CSI", "ST", "OSC", "PM", "APC", "NBS"
92 static char rxdigits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
93 static char rxresult[RXRESULT+1];
95 /* C K S T R N C P Y */
98 Copies a NUL-terminated string into a buffer whose total length is given,
99 ensuring that the result is NUL-terminated even if it has to be truncated.
102 dest = pointer to destination buffer
103 src = pointer to source string
104 len = length of destination buffer (the actual length, not one less).
107 int, The number of bytes copied, 0 or more.
109 NOTE: This is NOT a replacement for strncpy():
110 . strncpy() does not require its source string to be NUL-terminated.
111 . strncpy() does not necessarily NUL-terminate its result.
112 . strncpy() right-pads dest with NULs if it is longer than src.
113 . strncpy() treats the length argument as the number of bytes to copy.
114 . ckstrncpy() treats the length argument as the size of the dest buffer.
115 . ckstrncpy() doesn't dump core if given NULL string pointers.
116 . ckstrncpy() returns a number.
118 Use ckstrncpy() when you want to:
119 . Copy an entire string into a buffer without overrun.
120 . Get the length of the string back.
122 Use strncpy() when you want to:
123 . Copy a piece of a string.
127 ckstrncpy(char * dest, const char * src, int len)
129 ckstrncpy(dest,src,len) char * dest, * src; int len;
130 #endif /* CK_ANSIC */
133 if (len < 1 || !src || !dest) { /* Nothing or nowhere to copy */
134 if (dest) *dest = NUL;
138 for (i = 0; src[i] && (i < len-1); i++) /* Args OK, copy */
143 if (i > len) i = len;
146 #endif /* NOCKSTRNCPY */
150 /* C K S T R N C A T */
153 Appends a NUL-terminated string to a buffer whose total length is given,
154 ensuring that the result is NUL-terminated even if it had to be truncated.
157 dest = pointer to destination buffer containing a null-terminated string
158 src = pointer to null-terminated source string
159 len = length of destination buffer (the actual length, not one less).
162 int, The number of bytes copied, 0 or more.
166 ckstrncat(char * dest, const char * src, int len)
168 ckstrncat(dest,src,len) char * dest, * src; int len;
169 #endif /* CK_ANSIC */
173 register char * s1, * s2;
174 #endif /* NOCKSTRNCPY */
175 if (len < 1 || !src || !dest) { /* Nothing or nowhere to copy */
176 if (dest) *dest = NUL;
181 for (i = 0, j = strlen(dest); src[i] && (i < len-j-1); i++)
187 while (*s1++) j++; /* j = strlen(dest); */
188 s1--; /* (back up over NUL) */
192 while (*s2++) i++; /* i = strlen(src); */
200 strncpy(&dest[j],src,i);
202 j = i; /* This should be a bit faster... */
203 s2 = src; /* depends on strcpy implementation; */
204 while ((*s1++ = *s2++) && j--) /* at least it shouldn't be slower. */
206 dest[len-1] = NUL; /* In case of early exit. */
209 #endif /* NOCKSTRNCPY */
213 /* C K M A K M S G */
216 Constructs a message from up to 4 pieces with length checking.
217 Result is always NUL terminated. Call with:
218 buf: Pointer to buffer for constructing message.
219 len: Length of buffer.
220 s1-s4: String pointers (can be NULL).
222 0: Nothing was copied.
223 n: (positive number) n bytes copied, all args copied successfully.
224 -n: n bytes were copied, destination buffer not big enough for all.
226 ckmakxmsg() -- accepts 12 string args.
227 ckitoa(), ckltoa(), ckctoa(), ckitox(), etc.
228 Use ckmak[x]msg() plus ck?to?() as a safe replacement for sprintf().
232 ckmakmsg(char * buf, int len, char *s1, char *s2, char *s3, char *s4)
234 ckmakmsg(buf,len,s1,s2,s3,s4) char *buf, *s1, *s2, *s3, *s4; int len;
235 #endif /* CK_ANSIC */
241 if (!buf) return(n); /* No destination */
242 if (len < 1) return(n); /* No size */
244 s = buf; /* Point to destination */
245 a[0] = s1; a[1] = s2; a[2] = s3; a[3] = s4; /* Array of source strings */
246 for (i = 0; i < 4; i++) { /* Loop thru array */
247 p = a[i]; /* Point to this element */
248 if (p) { /* If pointer not null */
249 n = ckstrncpy(s,p,len); /* Copy safely */
250 m += n; /* Accumulate total */
251 if (p[n]) /* Didn't get whole thing? */
252 return(-m); /* return indicating buffer full */
253 len -= n; /* Deduct from space left */
254 s += n; /* Otherwise advance dest pointer */
257 return(m); /* Return total bytes copied */
261 /* C K M A K X M S G */
263 /* Exactly like ckmakmsg(), but accepts 12 string arguments. */
267 ckmakxmsg(char * buf, int len,
268 char *s1, char *s2, char *s3, char *s4, char *s5, char *s6,
269 char *s7, char *s8, char *s9, char *s10, char *s11, char *s12)
271 ckmakxmsg(buf,len,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12)
272 char *buf, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12;
274 #endif /* CK_ANSIC */
280 if (!buf) return(n); /* No destination */
281 if (len < 1) return(n); /* No size */
283 s = buf; /* Point to destination */
284 a[0] = s1; a[1] = s2; a[2] = s3; a[3] = s4; /* Source-string array */
285 a[4] = s5; a[5] = s6; a[6] = s7; a[7] = s8;
286 a[8] = s9; a[9] = s10; a[10] = s11; a[11] = s12;
287 for (i = 0; i < 12; i++) { /* Loop thru array */
288 p = a[i]; /* Point to this element */
289 if (p) { /* If pointer not null */
290 n = ckstrncpy(s,p,len); /* Copy safely */
291 m += n; /* Accumulate total */
292 if (p[n]) /* Didn't get whole thing? */
293 return(-m); /* return indicating buffer full */
294 len -= n; /* Deduct from space left */
295 s += n; /* Otherwise advance dest pointer */
298 return(m); /* Return total bytes copied */
301 /* C H A R T O S T R */
303 /* Converts a character to a string, interpreting controls. */
306 chartostr(x) int x; { /* Call with char x */
307 static char buf[2]; /* Returns string pointer. */
312 if (x > 127 && x < 161)
313 return(c1tab[x - 128]);
317 buf[0] = (unsigned)(x & 0xff);
323 /* Returns the rightmost character of the given null-terminated string */
326 ckrchar(s) char * s; {
327 register CHAR c = '\0', *p;
329 if (!p) p = (CHAR *)""; /* Null pointer == empty string */
331 while (*p) /* Crawl to end of string */
333 return((unsigned)(c & 0xff)); /* Return final character */
336 /* C K S T R C H R */
338 /* Replacement for strchr(), which is not universal. */
340 s = pointer to string to look in.
341 c = character to look for.
343 NULL if c not found in s or upon any kind of error, or:
344 pointer to first occurrence of c in s, searching from left to right.
348 ckstrchr(char * s, char c)
350 ckstrchr(s,c) char *s, c;
351 #endif /* CK_ANSIC */
355 while (*s && *s != c)
357 return((*s == c) ? s : NULL);
360 /* C K S T R R C H R */
362 /* Replacement for strrchr(), which is not universal. */
364 s = pointer to string to look in.
365 c = character to look for.
367 NULL if c not found in s or upon any kind of error, or:
368 pointer to first occurrence of c in s, searching from right to left.
372 ckstrrchr(char * s, char c)
374 ckstrrchr(s,c) char *s, c;
375 #endif /* CK_ANSIC */
389 /* C K S T R P B R K -- Portable replacement for strpbrk() */
391 /* Returns pointer to first char in s1 that is also in s2, or NULL */
394 ckstrpbrk(s1, s2) char * s1, * s2; {
396 if (!s1 || !s2) return(NULL);
397 if (!*s1 || !*s2) return(NULL);
398 while ((c1 = *s1++)) {
400 while ((c2 = *s3++)) {
408 /* C K L O W E R -- Lowercase a string IN PLACE */
410 /* Returns the length of the string */
413 cklower(s) char *s; {
417 if (isupper(*s)) *s = (char) tolower(*s);
423 /* C K U P P E R -- Uppercase a string IN PLACE */
425 /* Returns the length of the string */
428 ckupper(s) char *s; {
432 if (islower(*s)) *s = (char) toupper(*s);
438 /* C K L T O A -- Long to string -- FOR DISCIPLINED USE ONLY */
441 static char numbuf[NUMBUF+32] = { NUL, NUL };
442 static int numbp = 0;
444 ckltoa() and ckitoa() are like atol() and atoi() in the reverse direction,
445 returning a pointer to the string representation of the given number without
446 the caller having to worry about allocating or defining a buffer first.
447 They manage their own internal buffer, so successive calls return different
448 pointers. However, to keep memory consumption from growing without bound,
449 the buffer recycles itself. So after several hundred calls (depending on
450 the size of the numbers), some of the earlier pointers might well find
451 themselves referencing something different. Moral: You can't win in C.
452 Therefore, these routines are intended mainly for generating numeric strings
453 for short-term use, e.g. for passing numbers in string form as parameters to
454 functions. For long-term use, the result must be copied to a safe place.
461 #endif /* CK_ANSIC */
463 char buf[32]; /* Internal working buffer */
465 int k, x, len = 0, sign = 0;
466 if (n < 0L) { /* Sign */
471 for (k = 30; k > 0; k--) { /* Convert number to string */
478 if (sign) buf[--k] = '-'; /* Add sign if necessary */
480 if (len + numbp > NUMBUF)
485 while ((*p++ = *s++)) ; /* Copy */
488 return(q); /* Return pointer */
491 /* C K U L T O A -- Unsigned long to string */
495 ckultoa(unsigned long n)
497 ckultoa(n) unsigned long n;
498 #endif /* CK_ANSIC */
500 char buf[32]; /* Internal working buffer */
504 for (k = 30; k > 0; k--) { /* Convert number to string */
512 if (len + numbp > NUMBUF)
517 while ((*p++ = *s++)) ; /* Copy */
519 return(q); /* Return pointer */
524 ckltox(long n) /* Long int to "0x.." hex string */
527 #endif /* CK_ANSIC */
529 char buf[32]; /* Internal working buffer */
530 char *p, *q, *s, *bp = buf + 2;
534 sprintf(bp, "%lx", n);
537 sprintf(bp, "0%lx", n);
541 if (numbp + k >= NUMBUF)
546 while ((*p++ = *s++)) ; /* Copy */
549 return(q); /* Return pointer */
553 /* C K I T O A -- Int to string -- FOR DISCIPLINED USE ONLY */
556 ckitoa(n) int n; { /* See comments with ckltoa(). */
563 char * /* Unsigned int to string */
564 ckuitoa(n) unsigned int n; {
571 ckitox(n) int n; { /* Int to hex */
579 ckctoa(char c) /* Char to string */
585 static int current = 0;
589 buf[current++] = '\0';
590 return((char *)(buf + current - 2));
595 ckctox(CHAR c, int flag) /* Unsigned char to hex */
597 ckctox(c, flag) CHAR c; int flag;
601 static int current = 0;
608 if (!flag && isupper(rxdigits[x]))
609 h = tolower(rxdigits[x]);
613 if (!flag && isupper(rxdigits[x]))
614 h = tolower(rxdigits[x]);
616 buf[current++] = '\0';
617 return((char *)(buf + current - 3));
620 /* C K I N D E X -- C-Kermit's index function */
622 We can't depend on C libraries to have one, so here is our own.
624 s1 - String to look for.
625 s2 - String to look in.
626 t - Offset from right or left of s2, 0 based; -1 for rightmost char in s2.
627 r - 0 for left-to-right search, non-0 for right-to-left.
628 icase 0 for case independence, non-0 if alphabetic case matters.
629 Returns 0 if string not found, otherwise a 1-based result.
630 Also returns 0 on any kind of error, e.g. junk parameters.
633 ckindex(s1,s2,t,r,icase) char *s1, *s2; int t, r, icase; {
634 int len1 = 0, len2 = 0, i, j, x, ot = t; /* ot = original t */
637 if (!s1 || !s2) return(0);
639 while (*s++) len1++; /* length of string to look for */
641 while (*s++) len2++; /* length of string to look in */
643 if (t < 0) t = len2 - 1;
645 j = len2 - len1; /* length difference */
647 if (j < 0 || (r == 0 && t > j)) /* search string is longer */
649 if (r == 0) { /* Index */
650 s = s2 + t; /* Point to beginning of target */
651 for (i = 0; i <= (j - t); i++) { /* Now compare */
652 x = ckstrcmp(s1,s++,len1,icase);
656 } else { /* Reverse Index */
657 i = len2 - len1; /* Where to start looking */
658 if (ot > 0) /* Figure in offset if any */
660 for (j = i; j > -1; j--) {
661 if (!ckstrcmp(s1,&s2[j],len1,icase))
668 /* C K S T R S T R -- Portable replacement for strstr() */
670 /* Returns pointer to first occurrence of s1 in s2, or NULL */
673 ckstrstr(s1, s2) char * s1, * s2; {
675 k = ckindex(s2,s1,0,0,1);
676 return((k < 1) ? NULL : &s1[k-1]);
680 /* B R S T R I P -- Strip enclosing braces from arg string, in place. */
683 Pointer to string that can be poked.
685 Pointer to string without enclosing braces.
686 If original string was not braced, this is the arg pointer;
687 otherwise it is 1 + the arg pointer, with the matching closing
688 brace zero'd out. If the string starts with a brace but does
689 not end with a matching brace, the original pointer to the original
690 string is returned. If the arg pointer is NULL, a pointer to an
691 empty string is returned.
695 /* This is the original version, handling only braces */
698 brstrip(p) char *p; {
702 x = (int)strlen(p) - 1;
712 /* New version handles braces and doublequotes */
715 brstrip(p) char *p; {
717 if (*p == '{' || (*p == '"' && dblquo)) {
719 x = (int)strlen(p) - 1;
721 if ((*p == '{' && p[x] == '}') ||
722 (*p == '"' && p[x] == '"')) {
723 if (x > 0 && p[x-1] != CMDQ) {
736 /* Even newer experimental version -- breaks many things */
739 fnstrip(p) char *p; {
741 extern int cmd_quoting; /* Bad - no externs allowed! */
750 for (j = 0; j < len; j++ ) {
752 (!cmd_quoting || j == 0 || p[j-1] != CMDQ)) {
753 for (n = 1, i = j+1; i < len; i++ ) {
754 if (p[i] == '{' && (!cmd_quoting || p[i-1] != CMDQ))
756 else if (p[i] == '}' && (!cmd_quoting || p[i-1] != CMDQ)) {
758 for (k = j; k < i - 1; k++)
760 for (; i < len; i++ )
769 if (n == 1) { /* Implied right brace at end of field */
770 for (k = j; k < len; k++)
774 } else if (*p == '"') {
778 for (j = 0; j < len; j++) {
780 (!cmd_quoting || j == 0 || p[j-1] != CMDQ)) {
783 for (i = j + 1; i < len; i++) {
784 if (p[i] == '"' && (!cmd_quoting || p[i-1] != CMDQ)) {
787 for (k = j; k < i - 1; k++)
797 if (n == 1) { /* Implied double quote at end of field */
798 for (k = j; k < len; k++ )
809 Not used -- Note: these not only write into their arg, but write past
813 brace(fn) char *fn; {
816 for (p = fn; *p; p++) {
844 /* d q u o t e -- Puts doublequotes around arg in place. */
847 Pointer to buffer and its total length and flag = 0 to use
848 doublequotes, 1 to use braces.
850 Number: length of result.
853 dquote(fn, len, flag) char *fn; int len; int flag; {
854 int spaces = 0, k = 0;
860 for (p = fn; *p; p++) {
871 *p = flag ? '{' : '"';
882 *p = flag ? '}' : '"';
890 /* U N T A B I F Y --- Untabify s1 into s2, assuming tabs every 8 space */
893 untabify(s1,s2,max) char * s1, * s2; int max; {
896 for (i = 0, k = 0; k < x; k++) {
907 for (j = 0; j < z && i < max; j++)
915 /* M A K E L I S T --- Breaks {{s1}{s2}..{sn}} into an array of strings */
918 s = pointer to string to break up.
919 list = array of string pointers.
920 len = number of elements in array.
921 NOTE: The array must be preinitialized to all NULL pointers.
922 If any array element is not NULL, it is assumed to have been malloc'd
923 and is therefore freed. Do NOT call this function with an uninitialized
924 array, or with an array that has had any static elements assigned to it.
927 makelist(s,list,len) char * s; char *list[]; int len; {
929 char *p = NULL, *s2 = NULL;
930 debug(F110,"makelist s",s,0);
931 if (!s) { /* Check for null or empty string */
940 if ((s2 = (char *)malloc(n+1))) { /* Safe copy for poking */
941 strcpy(s2,s); /* (no need for ckstrncpy here) */
944 s = brstrip(s); /* Strip braces */
945 n = strlen(s); /* Get length */
946 if (*s != '{') { /* Outer braces only */
947 if ((p = (char *)malloc(n+1))) { /* So just one pattern */
948 strcpy(p,s); /* (no need for ckstrncpy here) */
956 q = 0; /* Inner ones too */
957 i = 0; /* so a list of patterns. */
959 while (*s && i < len) {
960 if (*s == CMDQ) { /* Quote... */
966 if (*s == '{' && !q) { /* Opening brace */
967 if (bc++ == 0) { /* Beginning of a group */
970 } else { /* It's a brace inside the group */
975 } else if (*s == '}' && !q) { /* Closing brace */
976 if (--bc == 0) { /* End of a group */
978 debug(F111,"makelist element",p,i);
981 if ((list[i] = (char *)malloc(n+1))) {
982 ckstrncpy(list[i],p,n+1); /* Note: n+1 */
985 while (*s == SP) s++;
989 } else { /* Within a group */
993 } else { /* Regular character */
999 if (*p && i < len) { /* Last one */
1002 if ((list[i] = (char *)malloc(n+1))) {
1003 ckstrncpy(list[i],p,n+1);
1004 debug(F111,"makelist last element",p,i);
1007 i++; /* Clear out the rest of the list */
1008 for ( ; i < len; i++) {
1017 M A K E S T R -- Creates a dynamically allocated string.
1019 Makes a new copy of string s and sets pointer p to its address.
1020 Handles degenerate cases, like when buffers overlap or are the same,
1021 one or both arguments are NULL, etc.
1023 The source string is assumed to be NUL-terminated. Therefore it can not
1024 be a UCS-2 string or arbitrary binary data.
1026 The target pointer must be either NULL or else a pointer to a previously
1027 malloc'ed buffer. If not, expect a core dump or segmentation fault.
1029 Note: The caller can tell whether this routine failed as follows:
1032 if (q & !p) { makestr() failed };
1034 Really this routine should have returned a length, but since it doesn't
1035 we set the global variable makestrlen to the length of the result string.
1041 makestr(char **p, const char *s)
1043 makestr(p,s) char **p, *s;
1049 register const char * s2;
1052 #endif /* CK_ANSIC */
1055 if (*p == s) /* The two pointers are the same. */
1056 return; /* Don't do anything. */
1058 if (!s) { /* New definition is null? */
1059 if (*p) /* Free old storage. */
1061 *p = NULL; /* Return null pointer. */
1065 s2 = s; /* Maybe new string will fit */
1069 This is a fairly big win, allowing us to skip the malloc() and free if the
1070 destination string already exists and is not shorter than the source string.
1071 But it doesn't allow for possible overlap of source and destination.
1073 if (*p) { /* into old storage... */
1077 if (!(*p2++ = *s2++))
1084 #endif /* COMMENT */
1089 while (*s2++) x++; /* Get (rest of) length of s. */
1091 if (x >= 0) { /* Get length, even of empty string. */
1092 q = malloc(x+1); /* Get and point to temp storage. */
1094 makestrlen = x; /* Remember length for stats */
1095 s2 = s; /* Point back to beginning of source */
1096 q2 = q; /* Copy dest pointer to increment... */
1097 while ((*q2++ = *s2++)) ; /* Instead of calling strcpy(). */
1099 Note: HP flexelint says that the above loop can result in creation (++) and
1100 access (*) of out-of-bounds pointers. I really don't see it.
1104 else { /* This would be a really bad error */
1105 char tmp[24]; /* So get a good record of it. */
1107 ckstrncpy(tmp,s,20);
1108 strcpy(tmp+20,"...");
1111 strcpy(tmp,s); /* We already checked the length */
1113 debug(F110,"MAKESTR MALLOC FAILURE ",tmp,0);
1117 q = NULL; /* Length of string is zero */
1119 if (*p) /* Now free the original storage. */
1124 /* X M A K E S T R -- Non-destructive makestr() if s is NULL. */
1128 xmakestr(char **p, const char *s)
1130 xmakestr(p,s) char **p, *s;
1133 if (s) makestr(p,s);
1137 /* C K M E M C P Y -- Portable (but slow) memcpy() */
1139 /* Copies n bytes from s to p, allowing for overlap. */
1140 /* For use when real memcpy() not available. */
1143 ckmemcpy(p,s,n) char *p, *s; int n; {
1148 if (!s || !p || n <= 0 || p == s) /* Verify args */
1150 x = p - s; /* Check for overlap */
1153 if (x < n) { /* They overlap */
1155 if (!(p = (char *)malloc(n))) /* So use a temporary buffer */
1158 for (i = 0; i < n; i++) /* Copy n bytes */
1160 if (q) { /* If we used a temporary buffer */
1161 for (i = 0; i < n; i++) /* copy from it to destination */
1163 if (p) free(p); /* and free the temporary buffer */
1166 #endif /* USE_MEMCPY */
1169 /* C K S T R C M P -- String comparison with case-matters selection */
1171 Call with pointers to the two strings, s1 and s2, a length, n,
1172 and c == 0 for caseless comparison, nonzero for case matters.
1173 Call with n == -1 to compare without a length limit.
1174 Compares up to n characters of the two strings and returns:
1178 Note: case handling is only as good as isupper() and tolower().
1181 ckstrcmp(s1,s2,n,c) char *s1, *s2; register int n, c; {
1182 register CHAR t1, t2;
1183 if (n == 0) return(0);
1184 if (!s1) s1 = ""; /* Watch out for null pointers. */
1186 if (!*s1) return(*s2 ? -1 : 0);
1187 if (!*s2) return(1);
1189 t1 = (CHAR) *s1++; /* Get next character from each. */
1191 if (!t1) return(t2 ? -1 : 0);
1193 if (!c) { /* If case doesn't matter */
1194 if (isupper(t1)) t1 = tolower(t1); /* Convert case. */
1195 if (isupper(t2)) t2 = tolower(t2);
1197 if (t1 < t2) return(-1); /* s1 < s2 */
1198 if (t1 > t2) return(1); /* s1 > s2 */
1200 return(0); /* They're equal */
1203 /* C K S T R P R E -- Caseless string prefix comparison */
1205 /* Returns position of the first char in the 2 strings that doesn't match */
1208 ckstrpre(s1,s2) char *s1, *s2; {
1216 if (!t1 || !t2) return(n);
1217 if (isupper(t1)) t1 = tolower(t1);
1218 if (isupper(t2)) t2 = tolower(t2);
1227 /* C K M A T C H -- Match a string against a pattern */
1230 pattern to be matched.
1231 string to look for the pattern in.
1232 icase is 1 if case-sensitive, 0 otherwise.
1235 1 = Match strings starting with '.'
1236 0 = Don't match them (used with UNIX filenames).
1238 1 = File globbing (dirseps are fences);
1239 0 = Dirseps are not fences.
1241 1 = Allow ^ and $ anchors at beginning and end of pattern.
1242 0 = Don't allow them (normal case for filename matching).
1243 Bit 3 (and beyond): Undefined.
1244 Works only with NUL-terminated strings.
1245 Pattern may contain any number of ? and/or *.
1246 If CKREGEX is defined, also [abc], [a-z], and/or {string,string,...}.
1247 (Note: REGEX is a misnomer, see below.)
1250 0 if string does not match pattern,
1251 >= 1, the 1-based position in the string where the match was found.
1254 Find a way to identify the piece of the string that matched the pattern,
1255 as in Snobol "LINE (PAT . RESULT)". This is now partially done by
1256 setting matchpos and matchend (except matchend needs some tuning). But
1257 these are useless unless a copy of the string is kept, or a copy of the
1258 matching part is made. But that would be too costly in performance --
1259 this routine has to be fast because it's used for wildcard expansion.
1262 Patterns are not the same as regular expressions, in which '*' means
1263 0 or more repetitions of the preceding item. For example "a*b" as a
1264 pattern matches any string that starts with 'a' and ends with 'b'; as a
1265 regular expression it matches any string of zero or more a's followed by
1266 one b. Regular expressions are especially useful in matching strings of
1267 (say) digits, or letters, e.g. "[0-9]*" matches any string of digits.
1269 static char * mypat = NULL; /* For rewriting pattern */
1270 static int matchpos = 0;
1272 static int matchdepth = 0;
1273 static int stringpos = 0;
1274 static char * ostring = NULL;
1276 #define MATCHRETURN(x,y) { rc=y; where=x; goto xckmatch; }
1277 static char * lastpat = NULL;
1280 ckmatch(pattern, string, icase, opts) char *pattern,*string; int icase, opts; {
1281 int q = 0, i = 0, k = -1, x, flag = 0;
1282 int rc = 0; /* Return code */
1284 CHAR cp; /* Current character from pattern */
1285 CHAR cs; /* Current character from string */
1286 int plen, dot, globbing, xstar = 0;
1287 int bronly = 0; /* Whole pattern is {a,b,c,...} */
1289 debug(F111,"CKMATCH ENTRY pat opt",pattern,opts);
1290 debug(F111,"CKMATCH ENTRY str dep",string,matchdepth);
1291 /* debug(F101,"CKMATCH ENTRY icase","",icase); */
1293 globbing = opts & 2;
1295 if (!string) string = "";
1296 if (!pattern) pattern = "";
1297 if (!*pattern) { /* Empty pattern matches anything */
1298 matchdepth++; /* (it wasn't incremented yet) */
1301 if (matchdepth == 0) { /* Top-level call? */
1302 stringpos = 0; /* Reset indices etc. */
1307 if (*pattern == '{') /* Entire pattern is {a,b.c} */
1308 bronly = 1; /* Maybe */
1309 dot = (opts & 1) || /* Match leading dot (if file) */
1310 (opts & 2 == 0) || /* always if not file */
1311 (pattern[0] == '.'); /* or if pattern starts with '.' */
1313 plen = strlen(pattern); /* Length of pattern */
1314 /* This would be used in calculating length of matching segment */
1315 if (plen > 0) /* User's pattern ends with '*' */
1316 if (pattern[plen - 1] == '*')
1318 if (pattern[0] == '*') { /* User's pattern starts with '*' */
1320 debug(F111,"CKMATCH 1",string, matchpos);
1322 if (opts & 4) { /* ^..$ allowed (top level only) */
1323 /* Rewrite pattern to account for ^..$ anchoring... */
1325 if (mypat) free(mypat); /* Get space for "*pattern*" */
1326 mypat = (char *)malloc(plen + 4);
1327 if (mypat) { /* Got space? */
1328 char * s = pattern, * p = mypat; /* Set working pointers */
1329 if (*s == '^') { /* First source char is ^ */
1330 s++; /* so skip past it */
1331 } else if (*s != '*') { /* otherwise */
1332 *p++ = '*'; /* prepend '*' to pattern */
1334 while (*s) { /* Copy rest of pattern */
1335 if (!*(s+1)) { /* Final pattern character? */
1336 if (*s != '$') { /* If it's not '$' */
1337 *p++ = *s; /* Copy it into the pattern */
1338 if (*s++ != '*') /* And if it's also not '*' */
1339 *p++ = '*'; /* append '*'. */
1342 } else /* Not final character */
1343 *p++ = *s++; /* Just copy it */
1345 *p = NUL; /* Terminate the new pattern */
1346 pattern = mypat; /* Make the switch */
1348 debug(F110,"CKMATCH INIT pat",pattern,0);
1351 matchdepth++; /* Now increment call depth */
1354 if (!dot) { /* For UNIX file globbing */
1355 if (*string == '.' && *pattern != '.' && !matchdot) {
1358 *pattern != '{' && *pattern != '['
1361 #endif /* CKREGEX */
1363 debug(F110,"ckmatch skip",string,0);
1371 cp = *pattern; /* Character from pattern */
1372 cs = *string; /* Character from string */
1375 debug(F000,"CKMATCH pat cp",pattern,cp);
1376 debug(F000,"CKMATCH str cs",string,cs);
1377 #endif /* COMMENT */
1379 if (!cs) { /* End of string - done. */
1380 x = (!cp || (cp == '*' && !*(pattern+1))) ? 1 : 0;
1383 matchpos = stringpos;
1384 debug(F111,"CKMATCH A",string, matchpos);
1386 matchend = stringpos;
1387 MATCHRETURN(2,matchpos);
1389 debug(F111,"CKMATCH ZERO d",string, matchpos);
1391 MATCHRETURN(16,matchpos);
1393 if (!icase) { /* If ignoring case */
1394 if (isupper(cp)) /* convert both to lowercase. */
1399 if (q) { /* This character was quoted */
1400 debug(F000,"CKMATCH QUOTED",pattern,cp);
1401 q = 0; /* Turn off quote flag */
1403 if (cs == cp) { /* Compare directly */
1404 if (!matchpos) { /* Matches */
1405 matchpos = stringpos;
1406 debug(F111,"CKMATCH \\ new match",string, matchpos);
1409 } else { /* Doesn't match */
1410 pattern = lastpat; /* Back up the pattern */
1412 debug(F111,"CKMATCH \\ no match",pattern, matchpos);
1418 if (cp == CMDQ && !q) { /* Quote in pattern */
1419 debug(F000,"CKMATCH QUOTE",pattern,cp);
1420 q = 1; /* Set flag */
1421 pattern++; /* Advance to next pattern character */
1422 continue; /* and continue. */
1424 if (cs && cp == '?') { /* '?' matches any char */
1426 matchpos = stringpos;
1427 debug(F111,"CKMATCH D",string, matchpos);
1429 debug(F110,"CKMATCH ? pat",pattern,0);
1430 debug(F110,"CKMATCH ? str",string,0);
1431 pattern++, string++;
1435 } else if (cp == '[') { /* Have bracket */
1436 int q = 0; /* My own private q */
1437 char * psave = NULL; /* and backup pointer */
1438 CHAR clist[256]; /* Character list from brackets */
1440 for (i = 0; i < 256; i++) /* memset() etc not portable */
1442 psave = ++pattern; /* Where pattern starts */
1443 debug(F111,"CKMATCH [] ",pattern-1, matchpos);
1444 for (flag = 0; !flag; pattern++) { /* Loop thru pattern */
1445 c = (CHAR)*pattern; /* Current char */
1446 if (q) { /* Quote within brackets */
1451 if (!icase) /* Case conversion */
1454 switch (c) { /* Handle unquoted character */
1455 case NUL: /* End of string */
1456 MATCHRETURN(4,0); /* No matching ']' so fail */
1457 case CMDQ: /* Next char is quoted */
1458 q = 1; /* Set flag */
1459 continue; /* and continue. */
1460 case '-': /* A range is specified */
1461 c1 = (pattern > psave) ? (CHAR)*(pattern-1) : NUL;
1462 c2 = (CHAR)*(pattern+1); /* IGNORE OUT-OF-BOUNDS WARNING */
1463 if (c2 == ']') c2 = NUL; /* (it can't happen) */
1464 if (c1 == NUL) c1 = c2;
1465 for (c = c1; c <= c2; c++) {
1469 clist[toupper(c)] = 1;
1470 } else if (isupper(c)) {
1471 clist[tolower(c)] = 1;
1476 case ']': /* End of bracketed sequence */
1477 flag = 1; /* Done with FOR loop */
1478 break; /* Compare what we have */
1479 default: /* Just a char */
1480 clist[c] = 1; /* Record it */
1483 clist[toupper(c)] = 1;
1484 } else if (isupper(c)) {
1485 clist[tolower(c)] = 1;
1491 if (!clist[(unsigned)cs]) { /* Match? */
1492 MATCHRETURN(5,0); /* Nope, done. */
1495 matchpos = stringpos;
1496 debug(F111,"CKMATCH [] match",string, matchpos);
1498 string++; /* Yes, advance string pointer */
1500 continue; /* and go on. */
1501 } else if (cp == '{') { /* Braces enclosing list of strings */
1502 char * p, * s, * s2, * buf = NULL;
1505 debug(F111,"CKMATCH {} ",string, matchpos);
1506 for (p = pattern++; *p; p++) {
1507 if (*p == '{') bc++;
1508 if (*p == '}') bc--;
1511 if (bc != 0) { /* Braces don't match */
1512 MATCHRETURN(6,0); /* Fail */
1513 } else { /* Braces do match */
1514 int q = 0, done = 0;
1515 len = *p ? strlen(p+1) : 0; /* Length of rest of pattern */
1518 if (bronly && (matchdepth != 1))
1520 n = p - pattern; /* Size of list in braces */
1521 if ((buf = (char *)malloc(n+1))) { /* Copy so we can poke it */
1524 ckstrncpy(buf,pattern,n+1);
1525 sofar = string - ostring - matchpos + 1;
1526 if (sofar < 0) sofar = 0;
1527 debug(F111,"CKMATCH .. string",string,sofar);
1528 debug(F111,"CKMATCH .. ostring",ostring,sofar);
1530 for (s = s2 = buf; 1; s++) { /* Loop through segments */
1532 if (q) { /* This char is quoted */
1538 if (*s == CMDQ && !q) { /* Quote next char */
1542 if (!*s || *s == ',') { /* End of this segment */
1544 if (!*s) /* If end of buffer */
1545 done = 1; /* then end of last segment */
1546 *s = NUL; /* Overwrite comma with NUL */
1547 debug(F111,"CKMATCH {} segment",s2,done);
1548 tplen = n + len + sofar + 2;
1549 if (!*s2) { /* Empty segment, no advancement */
1551 } else if ((tp = (char *)malloc(tplen))) {
1555 &ostring[matchpos-1] :
1559 ckstrncpy(tp,pp,sofar+1);
1561 ckstrncpy(tp,pp,sofar);
1566 ckstrncpy(&tp[1],pp,sofar+1);
1568 ckstrncpy(&tp[1],pp,sofar);
1570 ckstrncat(tp,s2,tplen); /* Current segment */
1571 ckstrncat(tp,p+1,tplen); /* rest of pattern */
1573 debug(F101,"CKMATCH {} matchpos","",matchpos);
1578 debug(F111,"CKMATCH {} tp",tp,matchpos);
1579 debug(F111,"CKMATCH {} string",
1581 debug(F111,"CKMATCH {} ostring",
1585 /* If segment starts with dot */
1586 /* then set matchdot option.. */
1588 if (*s2 == '.') opts2 |= 1;
1589 debug(F111,"CKMATCH {} recursing",s2,opts2);
1591 (string > ostring) ?
1592 &ostring[savpos-1] : string,
1596 debug(F101,"CKMATCH {} k","",k);
1597 debug(F101,"CKMATCH {} savpos","",savpos);
1605 if (k > 0) { /* If it matched we're done */
1608 } else { /* Malloc failure */
1611 if (k) { /* Successful comparison */
1613 matchpos = stringpos;
1614 debug(F111,"CKMATCH {} match",
1617 string += n-1; /* Advance pointers */
1621 if (done) /* If no more segments */
1622 break; /* break out of segment loop. */
1623 s2 = s+1; /* Otherwise, on to next segment */
1630 #endif /* CKREGEX */
1631 } else if (cp == '*') { /* Pattern char is asterisk */
1633 char * p, * s = NULL; /* meaning match anything */
1635 while (*pattern == '*') /* Collapse successive asterisks */
1637 psave = pattern; /* First non-asterisk after asterisk */
1638 lastpat = pattern - 1; /* Ditto, global */
1639 debug(F111,"CKMATCH * ",string,matchpos);
1640 for (n = 0, p = psave; *p; p++,n++) { /* Find next meta char */
1642 if (*p == '?' || *p == '*' || *p == CMDQ
1644 || *p == '[' || *p == '{'
1645 #endif /* CKREGEX */
1654 && (*p == '.' || *p == ']' ||
1655 *p == '<' || *p == '>' ||
1656 *p == ':' || *p == ';')
1663 #endif /* STRATUS */
1664 #endif /* datageneral */
1666 #endif /* UNIXOROSK */
1669 #endif /* GLOBBING */
1672 debug(F111,"CKMATCH * n string",string,n);
1673 debug(F111,"CKMATCH * n pattrn",pattern,n);
1674 debug(F111,"CKMATCH * n p",p,n);
1675 if (n > 0) { /* Literal string to match */
1676 s = (char *)malloc(n+1);
1678 ckstrncpy(s,psave,n+1); /* Copy cuz no poking original */
1680 k = ckindex(s,string,0,0,icase); /* 1-based index() */
1681 debug(F110,"CKMATCH * Index() string",string,0);
1682 debug(F110,"CKMATCH * Index() pattrn",s,0);
1683 debug(F101,"CKMATCH * Index() result","",k);
1684 } else { /* String is right-anchored */
1685 k = ckindex(s,string,-1,1,icase); /* rindex() */
1686 debug(F111,"CKMATCH * Rindex()",string,k);
1687 debug(F110,"CKMATCH * Rindex() pattrn",s,0);
1688 debug(F101,"CKMATCH * Rindex() result","",k);
1694 debug(F111,"CKMATCH * stringpos matchpos",
1695 ckitoa(stringpos), matchpos);
1697 matchpos = string - ostring + k;
1698 debug(F111,"CKMATCH * new match ", string, matchpos);
1700 string += k + n - 1;
1701 stringpos += k + n - 1;
1703 debug(F111,"CKMATCH * new string", string, stringpos);
1704 debug(F110,"CKMATCH * new pattrn", pattern, 0);
1707 } else if (!*p) { /* Asterisk at end matches the rest */
1708 if (!globbing) { /* (if not filename globbing) */
1710 matchpos = stringpos;
1711 debug(F111,"CKMATCH *$ ",string, matchpos);
1713 matchend = stringpos;
1714 MATCHRETURN(9,matchpos);
1718 if (globbing /* Filespec so don't cross fields */
1720 && *string == '/' || *string == '\\' ||
1727 && (*string == '.' || *string == ']' ||
1728 *string == '<' || *string == '>' ||
1729 *string == ':' || *string == ';')
1737 && *string == '/' /* (catch-all) */
1738 #endif /* STRATUS */
1739 #endif /* datageneral */
1741 #endif /* UNIXOROSK */
1744 matchend = stringpos;
1748 matchpos = stringpos;
1749 debug(F111,"CKMATCH *$ match",string, matchpos);
1754 #endif /* GLOBBING */
1756 matchpos = stringpos;
1757 debug(F111,"CKMATCH ** match",string, matchpos);
1759 matchend = stringpos;
1760 MATCHRETURN(11,matchpos);
1762 } else { /* A meta char follows asterisk */
1764 MATCHRETURN(17, matchpos = 0);
1765 while (*string && (k = ckmatch(p,string,icase,opts) < 1)) {
1769 debug(F111,"CKMATCH *<meta> k",string, k);
1770 if (!matchpos && k > 0) {
1771 matchpos = stringpos;
1772 debug(F111,"CKMATCH *<meta>",string, matchpos);
1774 MATCHRETURN(12, (*string) ? matchpos : 0);
1776 } else if (cs == cp) {
1777 pattern++, string++;
1780 matchpos = stringpos;
1781 debug(F111,"CKMATCH cs=cp",string, matchpos);
1797 ckmakxmsg(msgbuf,96,
1804 " string=",NULL,NULL,NULL,NULL,NULL
1806 debug(F110,msgbuf,string,0);
1814 /* I S F L O A T -- Verify that arg represents a floating-point number */
1817 Portable replacement for atof(), strtod(), scanf(), etc.
1820 s = pointer to string
1821 flag == 0 means entire string must be a (floating-pointing) number.
1822 flag != 0 means to terminate scan on first character that is not legal.
1825 1 if result is a legal number;
1826 2 if result has a fractional part;
1827 0 if not or if string empty.
1830 Sets global floatval to floating-point value if successful.
1832 Number need not contain a decimal point -- integer is subcase of float.
1833 Scientific notation not supported.
1835 CKFLOAT floatval = 0.0; /* For returning value */
1838 isfloat(s,flag) char *s; int flag; {
1842 CKFLOAT d = 0.0, f = 0.0;
1847 while (isspace(*s)) s++;
1849 if (*s == '-') { /* Handle optional sign */
1852 } else if (*s == '+')
1854 while ((c = *s++)) { /* Handle numeric part */
1856 case 0: /* Mantissa... */
1858 f = f * 10.0 + (CKFLOAT)(c - '0');
1860 } else if (c == '.') {
1865 if (flag) /* Not digit or period */
1866 goto done; /* break if flag != 0 */
1867 return(0); /* otherwise fail. */
1868 case 1: /* Fraction... */
1871 f += (CKFLOAT)(c - '0') / d;
1875 if (flag) /* Illegal character */
1876 goto done; /* Break */
1877 return(0); /* or fail, depending on flag */
1881 if (sign) f = 0.0 - f; /* Apply sign to result */
1882 floatval = f; /* Set result */
1883 return(d ? 2 : 1); /* Succeed */
1885 #endif /* CKFLOAT */
1887 /* Sorting routines... */
1889 /* S H _ S O R T -- Shell sort -- sorts string array s in place. */
1892 Highly defensive and relatively quick.
1893 Uses shell sort algorithm.
1896 s = pointer to array of strings.
1897 p = pointer to a second array to sort in parallel s, or NULL for none.
1898 n = number of elements in s.
1899 k = position of key.
1900 r = ascending lexical order if zero, reverse lexical order if nonzero.
1901 c = 0 for case independence, 1 for case matters, 2 for numeric.
1903 If k is past the right end of a string, the string is considered empty
1904 for comparison purposes.
1907 To sort a piece of an array, call with s pointing to the first element
1908 and n the number of elements to sort.
1911 None. Always succeeds, unless any of s[0]..s[n-1] are bad pointers,
1912 in which case memory violations are possible, but C offers no defense
1913 against this, so no way to gracefully return an error code.
1916 sh_sort(s,p,n,k,r,c) char **s, **p; int n, k, r, c; {
1918 char *t, *t1, *t2, *u = NULL;
1923 #endif /* CKFLOAT */
1925 if (!s) return; /* Nothing to sort? */
1926 if (n < 2) return; /* Not enough elements to sort? */
1927 if (k < 0) k = 0; /* Key */
1929 m = n; /* Initial group size is whole array */
1931 m = m / 2; /* Divide group size in half */
1932 if (m < 1) /* Small as can be, so done */
1934 for (j = 0; j < n-m; j++) { /* Sort each group */
1935 t = t2 = s[j+m]; /* Compare this one... */
1936 if (!t) /* But if it's NULL */
1937 t2 = ""; /* make it the empty string */
1938 if (p) /* Handle parallel array, if any */
1941 if ((int)strlen(t2) < k) /* If key too big */
1942 t2 = ""; /* make key the empty string */
1943 else /* Key is in string */
1944 t2 = t + k; /* so point to key position */
1946 for (i = j; i >= 0; i -= m) { /* Loop thru comparands s[i..]*/
1948 if (!t1) /* Same deal */
1951 if ((int)strlen(t1) < k)
1956 if (c == 2) { /* Numeric comparison */
1961 if (isfloat(t1,1)) {
1986 #endif /* CKFLOAT */
1988 x = ckstrcmp(t1,t2,-1,c); /* Compare */
1990 if (r == 0 && x < 0)
1992 if (r != 0 && x > 0)
1995 if (p) p[i+m] = p[i];
2004 /* C K R A D I X -- Radix converter */
2007 s: a number in string format.
2008 in: int, specifying the radix of s, 2-36.
2009 out: int, specifying the radix to convert to, 2-36.
2011 NULL on error (illegal radix, illegal number, etc.).
2012 "-1" on overflow (number too big for unsigned long).
2013 Otherwise: Pointer to result.
2016 ckradix(s,in,out) char * s; int in, out; {
2017 char c, *r = rxresult;
2019 unsigned long zz = 0L;
2021 if (in < 2 || in > 36) /* Verify legal input radix */
2023 if (out < 2 || out > 36) /* and output radix. */
2025 if (*s == '+') { /* Get sign if any */
2027 } else if (*s == '-') {
2031 while (*s == SP || *s == '0') /* Trim leading blanks or 0's */
2034 For detecting overflow, we use a signed copy of the unsigned long
2035 accumulator. If it goes negative, we know we'll overflow NEXT time
2038 for (; *s; s++) { /* Convert from input radix to */
2039 c = *s; /* unsigned long */
2040 if (islower(c)) c = toupper(c);
2041 if (c >= '0' && c <= '9')
2043 else if (c >= 'A' && c <= 'Z')
2047 if (d >= in) /* Check for illegal digit */
2050 if (z < 0L) /* Clever(?) overflow detector */
2054 if (!zz) return("0");
2055 r = &rxresult[RXRESULT]; /* Convert from unsigned long */
2056 *r-- = NUL; /* to output radix. */
2057 while (zz > 0 && r > rxresult) {
2058 d = zz % (unsigned)out;
2060 zz = zz / (unsigned)out;
2062 if (minus) *r-- = '-'; /* Replace original sign */
2063 return((char *)(r+1));
2067 /* Base-64 conversion routines */
2069 static char b64[] = { /* Encoding vector */
2071 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
2073 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S',
2074 'T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l',
2075 'm','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4',
2076 '5','6','7','8','9','+','/','=','\0'
2079 static int b64tbl[] = { /* Decoding vector */
2080 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
2081 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
2082 -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
2083 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
2084 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
2085 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
2086 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2087 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
2088 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2089 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2090 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2091 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2092 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2093 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2094 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2095 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
2099 B 8 T O B 6 4 -- Converts 8-bit data to Base64 encoding.
2102 s = Pointer to 8-bit data;
2103 n = Number of source bytes to encode (SEE NOTE).
2104 If it's a null-terminated string, you can use -1 here.
2105 out = Address of output buffer.
2106 len = Length of output buffer (should > 4/3 longer than input).
2109 >= 0 if OK, number of bytes placed in output buffer,
2110 with the subsequent byte set to NUL if space permits.
2111 -1 on error (output buffer space exhausted).
2114 If this function is to be called repeatedly, e.g. to encode a data
2115 stream a chunk at a time, the source length must be a multiple of 3
2116 in all calls but the final one to avoid the generation of extraneous
2117 pad characters that would throw the decoder out of sync. When encoding
2118 only a single string, this is not a consideration. No internal state
2119 is kept, so there is no reset function.
2122 b8tob64(s,n,out,len) char * s,* out; int n, len; {
2123 int b3, b4, i, x = 0;
2126 if (n < 0) n = strlen(s);
2128 for (i = 0; i < n; i += 3,x += 4) { /* Loop through source bytes */
2130 t = (unsigned)((unsigned)((unsigned int)s[i] & 0xff) << 8);
2131 if (n - 1 > i) { /* Do we have another after this? */
2132 t |= (unsigned)(s[i+1] & 0xff); /* Yes, OR it in */
2133 b3 = 1; /* And remember */
2135 t <<= 8; /* Move over */
2136 if (n - 2 > i) { /* Another one after that? */
2137 t |= (unsigned)(s[i+2] & 0xff); /* Yes, OR it in */
2138 b4 = 1; /* and remember */
2140 if (x + 4 > len) /* Check output space */
2142 out[x+3] = b64[b4 ? (t & 0x3f) : 64]; /* 64 = code for '=' */
2144 out[x+2] = b64[b3 ? (t & 0x3f) : 64];
2146 out[x+1] = b64[t & 0x3f];
2148 out[x] = b64[t & 0x3f];
2150 if (x < len) out[x] = NUL; /* Null-terminate the string */
2156 B 6 4 T O B 8 -- Converts Base64 string to 8-bit data.
2159 s = pointer to Base64 string (whitespace ignored).
2160 n = length of string, or -1 if null terminated, or 0 to reset.
2161 out = address of output buffer.
2162 len = length of output buffer.
2165 >= 0 if OK, number of bytes placed in output buffer,
2166 with the subsequent byte set to NUL if space permits.
2168 -1 = output buffer too small for input.
2169 -2 = input contains illegal characters.
2170 -3 = internal coding error.
2173 Can be called repeatedly to decode a Base64 stream, one chunk at a
2174 time. However, if it is to be called for multiple streams in
2175 succession, its internal state must be reset at the beginning of
2179 b64tob8(s,n,out,len) char * s,* out; int n, len; { /* Decode */
2180 static int bits = 0;
2181 static unsigned int r = 0;
2185 if (n == 0) { /* Reset state */
2190 x = (n < 0) ? strlen(s) : n; /* Source length */
2192 n = ((x + 3) / 4) * 3; /* Compute destination length */
2193 if (x > 0 && s[x-1] == '=') n--; /* Account for padding */
2194 if (x > 1 && s[x-2] == '=') n--;
2195 if (n > len) /* Destination not big enough */
2196 return(-1); /* Fail */
2198 for (i = 0; i < x; i++) { /* Loop thru source */
2199 c = (CHAR)s[i]; /* Next char */
2200 t = b64tbl[c]; /* Code for this char */
2201 if (t == -2) { /* Whitespace or Ctrl */
2204 } else if (t == -1) { /* Illegal code */
2205 return(-2); /* Fail. */
2206 } else if (t > 63 || t < 0) /* Illegal value */
2207 return(-3); /* fail. */
2208 bits += 6; /* Count bits */
2209 r <<= 6; /* Make space */
2210 r |= (unsigned) t; /* OR in new code */
2211 if (bits >= 8) { /* Have a byte yet? */
2212 bits -= 8; /* Output it */
2213 c = (unsigned) ((r >> bits) & 0xff);
2217 if (k < len) out[k] = NUL; /* Null-terminate in case it's */
2218 return(k); /* a text string */
2222 /* C H K N U M -- See if argument string is an integer */
2224 /* Returns 1 if OK, zero if not OK */
2225 /* If OK, string should be acceptable to atoi() */
2226 /* Allows leading space, sign */
2229 chknum(s) char *s; { /* Check Numeric String */
2230 int x = 0; /* Flag for past leading space */
2231 int y = 0; /* Flag for digit seen */
2233 debug(F110,"chknum",s,0);
2236 while ((c = *s++)) { /* For each character in the string */
2238 case SP: /* Allow leading spaces */
2240 if (x == 0) continue;
2242 case '+': /* Allow leading sign */
2247 default: /* After that, only decimal digits */
2248 if (c >= '0' && c <= '9') {
2258 /* R D I G I T S -- Verify that all characters in arg ARE DIGITS */
2260 /* Returns 1 if so, 0 if not or if string is empty */
2263 rdigits(s) char *s; {
2266 if (!isdigit(*s)) return(0);
2272 /* P A R N A M -- Return parity name */
2279 #endif /* CK_ANSIC */
2282 case 'e': return("even");
2283 case 'o': return("odd");
2284 case 'm': return("mark");
2285 case 's': return("space");
2286 case 0: return("none");
2287 default: return("invalid");
2291 char * /* Convert seconds to hh:mm:ss */
2296 #endif /* CK_ANSIC */
2298 static char buf[10];
2300 h = x / 3600L; /* Hours */
2302 m = x / 60L; /* Minutes */
2303 s = x % 60L; /* Seconds */
2305 sprintf(buf,"%02ld:%02ld:%02ld",h,m,s);
2308 return((char *)buf);
2311 /* L S E T -- Set s into p, right padding to length n with char c; */
2313 s is a NUL-terminated string.
2314 If length(s) > n, only n bytes are moved.
2315 The result is NOT NUL terminated unless c == NUL and length(s) < n.
2316 The intended of this routine is for filling in fixed-length record fields.
2319 lset(p,s,n,c) char *s; char *p; int n; int c; {
2323 #endif /* USE_MEMCPY */
2332 for (i = 0; i < x; i++)
2336 #endif /* USE_MEMCPY */
2339 /* R S E T -- Right-adjust s in p, left padding to length n with char c */
2342 rset(p,s,n,c) char *s; char *p; int n; int c; {
2346 #endif /* USE_MEMCPY */
2354 for (i = 0; i < (n - x); i++)
2358 #endif /* USE_MEMCPY */
2361 /* U L O N G T O H E X -- Unsigned long to hex */
2364 Converts unsigned long arg to hex and returns string pointer to
2365 rightmost n hex digits left padded with 0's. Allows for longs
2366 up to 64 bits. Returns pointer to result.
2370 ulongtohex( unsigned long z, int n )
2372 ulongtohex(z,n) unsigned long z; int n;
2373 #endif /* CK_ANSIC */
2375 static char hexbuf[17];
2376 int i = 16, x, k = 0;
2379 k = 2 * (sizeof(long));
2380 for (i = 0; i < n; i++) {
2381 if (i > k || z == 0) {
2386 hexbuf[15-i] = x + ((x < 10) ? '0' : 0x37);
2389 return((char *)(&hexbuf[16-i]));
2392 /* H E X T O U L O N G -- Hex string to unsigned long */
2395 Converts n chars from s from hex to unsigned long.
2397 0L or positive, good result (0L is returned if arg is NULL or empty).
2398 -1L on error: non-hex arg or overflow.
2401 hextoulong(s,n) char *s; int n; {
2403 unsigned long result = 0L;
2418 if ((d == '0' || d == ' ')) {
2426 if (d >= '0' && d <= '9') {
2428 } else if (d >= 'A' && d <= 'F') {
2433 if (++count > (sizeof(long) * 2))
2435 result = (result << 4) | (d & 0x0f);
2441 c k s p l i t -- Splits a string into words, or:
2442 extracts a given word from a string.
2444 Allows for grouping.
2445 Operates on a copy of the string; does not alter the original string.
2446 All strings NUL-terminated.
2450 1 = split, 0 = word.
2451 n1 = desired word number if fc == 0.
2453 s2 = break string (NULL to accept default = all non-alphanum).
2454 s3 = include string (NULL to accept default = all alphanum).
2455 n2 = grouping mask (OR desired ones together):
2456 1 = doublequotes, 2 = braces, 4 = apostrophes,
2457 8 = parens, 16 = brackets, 32 = angle brackets,
2458 -1 = 63 = all of these.
2459 n3 = group quote character, ASCII value, used and tested only for
2460 LISP quote, e.g. (a 'b c '(d e f)).
2461 n4 = 0 to collapse adjacent separators;
2462 nonzero not to collapse them.
2465 Pointer to struct stringarray, with size:
2466 -1 = memory allocation error.
2467 -2 = too many words in string.
2468 n = number of words (0 or more).
2471 wordarray = array of pointers to n words (n == 1 if fc == 0), 1-based.
2472 Each pointer is to a malloc'd string. This array is recycled upon each
2473 call; if you need to keep the strings, make copies of them. This routine
2474 must have control of allocation and deallocation.
2476 If a given character is included in the include list, it is not treated
2477 as a separator or as a grouping character.
2479 Groups may be nested only if they are formed with braces, parens, or
2480 brackets, but not with quotes or apostrophes since ASCII quotes have no
2481 intrinsic handedness. Group-start and end characters are treated as
2482 separators even in the absence of other separators, so a string such as
2483 "a{b}c" results in three words, not one.
2485 Sample call to split a string into an array:
2486 struct stringarray * q;
2487 q = cksplit(1,0,s1,s2,s3,-1,0);
2488 q->a_size = size (>=0) or failure code (<0)
2489 q->a_head = pointer to array (elements 0 thru q->asize - 1).
2491 Sample call to extract word n from a string:
2492 struct stringarray * q;
2493 q = cksplit(0,n,s1,s2,s3,-1,0);
2494 q->a_size = size (1) or failure code (<0)
2495 q->a_head = pointer to array (element 1 is the desired word).
2500 #define ST_BW 0 /* Between Words */
2501 #define ST_IW 1 /* In Word */
2502 #define ST_IG 2 /* Start Group */
2504 /* Character Classes (bitmap) */
2506 #define CL_SEP 1 /* Separator */
2507 #define CL_OPN 2 /* Group Open */
2508 #define CL_CLS 4 /* Group Close */
2509 #define CL_DAT 8 /* Data */
2510 #define CL_QUO 16 /* Group quote */
2514 #define MAXWORDS 4096 /* Max number of words */
2515 #endif /* MAXWORDS */
2517 #define NESTMAX 64 /* Maximum nesting level */
2518 #endif /* NESTMAX */
2521 #define MAXWORDS 128 /* Max number of words */
2522 #endif /* MAXWORDS */
2524 #define NESTMAX 16 /* Maximum nesting level */
2525 #endif /* NESTMAX */
2526 #endif /* BIGBUFOK */
2528 /* static */ char ** wordarray = NULL; /* Result array of word pointers */
2530 static struct stringarray ck_sval = { /* Return value structure */
2531 NULL, /* Pointer to array */
2534 static int * wordsize = NULL;
2537 setword(n,s,len) int n, len; char * s; {
2543 if (!wordarray) { /* Allocate result array (only once) */
2544 if (!(wordarray = (char **)malloc((MAXWORDS+1) * sizeof(char *))))
2546 if (!(wordsize = (int *)malloc((MAXWORDS+1) * sizeof(int))))
2548 for (i = 0; i <= MAXWORDS; i++) { /* Initialize result array */
2549 wordarray[i] = NULL;
2553 if (wordsize[n] < len /* || !wordarray[n] */ ) {
2554 k = (len < 16) ? 16 : len + (len / 4);
2555 wordarray[n] = (char *) malloc(k+1);
2556 wordsize[n] = (wordarray[n]) ? k : 0;
2559 while ((*p++ = *s++) && k-- > 0) ;
2561 } else if (len > 0) {
2562 k = wordsize[n]; /* (In case len arg is a lie) */
2564 while ((*p++ = *s++) && k-- > 0) {
2566 if ((*(s-1) == CMDQ) && *s != CMDQ) {
2570 #endif /* COMMENT */
2572 } else if (wordarray[n]) {
2573 wordarray[n][0] = NUL;
2577 static char * splitbuf = NULL;
2578 static int nsplitbuf = 0;
2580 /* n4 = 1 to NOT collapse adjacent separators */
2583 struct stringarray *
2584 cksplit(fc,n1,s1,s2,s3,n2,n3,n4) int fc,n1,n2,n3,n4; char *s1, *s2, *s3; {
2585 int splitting = 0; /* What I was asked to do */
2586 int i, k, ko = 0, n, x, max = MAXWORDS; /* Workers */
2587 char * s = NULL, * ss, * p; /* Workers */
2588 char * sep = ""; /* Default break set */
2589 char * notsep = ""; /* Default include set */
2590 int grouping = 0; /* Grouping option */
2591 char * gr_opn = "\"{'([<"; /* Group open brackets */
2592 char * gr_cls = "\"}')]>"; /* Group close brackets */
2593 int gr_stk[NESTMAX]; /* Nesting bracket stack */
2594 int gr_lvl = 0; /* Nesting level */
2595 int wordnum = 0; /* Current word number */
2596 char c = 'A'; /* Current char (dummy start value) */
2597 int class = 0; /* Current character class */
2598 int state = ST_BW; /* Current FSA state */
2599 int len = 0; /* Length of current word */
2600 int slen = 0; /* Length of s1 */
2601 int gquote = 0; /* Quoted group */
2602 int cquote = 0; /* Quoted character */
2603 int collapse = 1; /* Collapse adjacent separators */
2605 if (n4) collapse = 0; /* Don't collapse */
2607 for (i = 0; i < NESTMAX; i++) /* Initialize nesting stack */
2611 ck_sval.a_head = wordarray; /* Initialize return value struct */
2614 if (!s1) s1 = ""; /* s1 = source string */
2615 if (!*s1) { /* If none, nothing to do */
2618 splitting = fc; /* Our job */
2619 if (splitting) { /* If splitting n = word count */
2620 n = 0; /* Initialize it */
2621 } else { /* Otherwise */
2622 if (n1 < 1) { /* If 0 (or less) */
2623 ck_sval.a_size = 0; /* nothing to do. */
2626 n = n1; /* n = desired word number. */
2628 slen = 0; /* Get length of s1 */
2629 debug(F111,"cksplit",s1,n);
2633 while (*p++) slen++; /* Make pokeable copy of s1 */
2634 if (!splitbuf || slen > nsplitbuf) { /* Allocate buffer if needed */
2638 xx = (slen < 255) ? 255 : xx + (xx / 4);
2639 debug(F101,"cksplit splitbuf","",xx);
2640 splitbuf = (char *)malloc(xx+1);
2641 if (!splitbuf) { /* Memory allocation failure... */
2642 ck_sval.a_size = -1;
2645 nsplitbuf = xx; /* Remember size of buffer */
2649 The idea is to just copy the string into splitbuf (if it exists). This
2650 gives us both the copy and the length so we only need to grovel through the
2651 string once in most cases. Only when splitbuf doesn't exist or is too short
2652 do we re-malloc(), which should be very infrequent so who cares if we have
2653 to go through the string again in that case.
2657 if (splitbuf) { /* Make pokeable copy of s1 */
2658 while ((*s++ = *p++) && (slen++ < nsplitbuf)) /* Try to copy */
2661 if (!splitbuf || slen >= nsplitbuf) { /* Need to do more... */
2663 if (splitbuf) /* Free previous buf if any */
2665 while (*p++) slen++; /* Get (rest of) length */
2666 xx = (slen < 255) ? 255 : slen + (slen / 4); /* Size of new buffer */
2667 splitbuf = (char *)malloc(xx+1); /* Allocate it */
2668 if (!splitbuf) { /* Memory allocation failure... */
2669 ck_sval.a_size = -1;
2672 nsplitbuf = xx; /* Remember (new) buffer size */
2675 while ((*s++ = *p++)) ;
2678 #endif /* COMMENT */
2680 sep = s2; /* s2 = break set */
2682 notsep = s3; /* s3 = include set */
2683 if (!notsep) notsep = "";
2684 if (n2 < 0) n2 = 63; /* n2 = grouping mask */
2686 p = ""; /* Pointer to current word */
2687 while (c) { /* Loop through string */
2690 if (!cquote && c == CMDQ) { /* If CMDQ */
2691 cquote++; /* next one is quoted */
2692 goto nextc; /* go get it */
2694 if (cquote && c == CMDQ) { /* Quoted CMDQ is special */
2695 if (state != ST_BW) { /* because it can still separate */
2697 while (s2 > p) { *s2 = *(s2-1); s2--; }
2702 if (cquote) { /* Other quoted character */
2703 if (state != ST_BW) { /* can't separate or group */
2705 while (s2 > p) { *s2 = *(s2-1); s2--; }
2708 class = CL_DAT; /* so treat it as data */
2711 } else { /* Character is not quoted */
2712 if (c < SP) { /* Get its class */
2713 x = 0; /* x == 0 means "is separator" */
2714 } else if (*sep) { /* Break set given */
2716 while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */
2718 } else { /* Default break set is */
2719 x = ((c >= 'a' && c <= 'z') || /* all but alphanumerics */
2720 (c >= '0' && c <= '9') ||
2721 (c >= 'A' && c <= 'Z')
2724 if (x == 0 && *notsep && c) { /* Include set if given */
2726 while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */
2729 if (c == n3 && grouping && state == ST_BW) { /* Group quote? */
2732 class = x ? CL_DAT : CL_SEP; /* Class = data or separator */
2734 if (grouping) { /* Grouping? */
2735 int j; /* Look for group start */
2736 for (k = 0; k < 6; k++) { /* Group-start char? */
2737 j = 1 << k; /* Get its mask bit value */
2739 if (c == gr_opn[k]) { /* Selected group opener? */
2742 if (c == '"' || c == '\'') { /* These can also */
2743 class |= CL_CLS; /* be closers */
2746 } else if (c == gr_cls[k]) { /* Group closer? */
2754 switch (state) { /* State switcher... */
2755 case ST_BW: /* BETWEENWORDS */
2756 if (class & CL_OPN) { /* Group opener */
2757 if (gr_lvl == 0 && !gquote) { /* If not in group */
2758 p = s; /* point to beginning of word */
2760 gr_lvl++; /* Push closer on nesting stack */
2761 if (gr_lvl >= NESTMAX)
2763 gr_stk[gr_lvl] = gr_cls[ko];
2764 state = ST_IG; /* Switch to INGROUP state */
2765 } else if (class & CL_DAT) { /* Data character */
2766 gr_lvl = 0; /* Clear group nesting stack */
2769 p = s; /* Point to beginning of word */
2770 if (gquote) { /* Adjust for quote */
2775 state = ST_IW; /* Switch to INWORD state */
2776 } else if (class & CL_QUO) { /* Group quote */
2777 gquote = gr_lvl+1; /* Remember quoted level */
2784 case ST_IW: /* INWORD (but not in a group) */
2785 if (class & CL_SEP) { /* Ends on any kind of separator */
2786 *s = NUL; /* Terminate this word */
2787 wordnum++; /* Count it */
2788 if (splitting) { /* Dispose of it appropriately */
2789 if (wordnum > max) { /* Add to array if splitting */
2790 ck_sval.a_size = -2;
2793 setword(wordnum,p,len);
2794 } else if (wordnum == n) { /* Searching for word n */
2799 state = ST_BW; /* Switch to BETWEENWORDS state */
2804 case ST_IG: /* INGROUP */
2805 if (class & CL_CLS) { /* Have group closer? */
2806 if (c == gr_stk[gr_lvl]) { /* Does it match current opener? */
2807 gr_lvl--; /* Yes, pop stack */
2808 if (gr_lvl < 0) /* Don't pop it too much */
2810 if (gr_lvl == 0) { /* If at top of stack */
2814 *s = NUL; /* we have word. */
2816 wordnum++; /* Count and dispose of it. */
2819 if (wordnum > max) {
2820 ck_sval.a_size = -2;
2823 setword(wordnum,p+1,len);
2824 } else if (wordnum == n) {
2829 state = ST_BW; /* Switch to BETWEENWORDS state */
2832 if (gr_lvl < gquote)
2835 } else if (class & CL_OPN) { /* Have group opener */
2836 gr_lvl++; /* Push on nesting stack */
2837 if (gr_lvl >= NESTMAX) goto xxsplit;
2838 gr_stk[gr_lvl] = gr_cls[ko];
2843 s++; /* Next char */
2848 if (gr_lvl > 0) { /* In case of an unclosed group */
2849 if (splitting) { /* make it the last word. */
2850 if (++wordnum > max) {
2851 ck_sval.a_size = -2;
2854 setword(wordnum,p+1,len);
2855 } else if (wordnum == n) {
2861 if (!splitting) { /* Wanted word n but there was none? */
2865 } else { /* Succeed otherwise */
2866 ck_sval.a_size = wordnum;
2867 if (wordnum < MAXWORDS)
2868 setword(wordnum+1,NULL,0);
2872 for (i = 1; i <= wordnum; i++)
2873 debug(F111,"cksplit result",wordarray[i],i);
2878 xxsplit: /* Error return */
2879 ck_sval.a_size = -2;
2883 /* End of ckclib.c */