1 char * cklibv = "C-Kermit library, 9.0.052, 29 Jun 2011";
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, 2011,
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 ckfstoa() - Converts off_t-type integer (long or long long) to string.
37 ckatofs() - Converts a numeric string to an off_t-type integer.
38 ckctoa() - Converts char to string.
39 ckmakmsg() - Constructs a message from 4 source strings.
40 ckmakxmsg() - Constructs a message from 12 source strings.
41 ckmatch() - Pattern matching.
42 ckmemcpy() - Portable memcpy().
43 ckrchar() - Rightmost character of a string.
44 ckstrcmp() - Possibly caseless string comparison.
45 ckstrpre() - Caseless string prefix comparison.
46 sh_sort() - Sorts an array of strings, many options.
47 brstrip() - Strips enclosing braces (and doublequotes).
48 makelist() - Splits "{{item}{item}...}" into an array.
49 makestr() - Careful malloc() front end.
50 xmakestr() - ditto (see comments).
51 ckradix() - Convert number radix (2-36).
52 b8tob64() - Convert data to base 64.
53 b64tob8() - Convert base 64 to data.
54 chknum() - Checks if string is a (possibly signed) integer.
55 rdigits() - Checks if string is composed only of decimal digits.
56 isfloat() - Checks if string is a valid floating-point number.
57 ckround() - Rounds a floating-point number to desired precision.
58 parnam() - Returns parity name string.
59 hhmmss() - Converts seconds to hh:mm:ss string.
60 lset() - Write fixed-length field left-adjusted into a record.
61 rset() - Write fixed-length field right-adjusted into a record.
62 ulongtohex() - Converts an unsigned long to a hex string.
63 hextoulong() - Converts a hex string to an unsigned long.
64 cksplit() - Splits a string into an array of words.
65 ispattern() - Tells if argument string is a pattern.
67 Prototypes are in ckclib.h.
69 Note: This module should not contain any extern declarations.
75 /* Public variables */
77 int dblquo = 1; /* Nonzero if doublequotes can be used for grouping */
80 ccntab[] = { /* Names of ASCII (C0) control characters 0-31 */
81 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
82 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
83 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
84 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
88 c1tab[] = { /* Names of ISO 6429 (C1) control characters 0-32 */
89 "XXX", "XXX", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
90 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
91 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
92 "SOS", "XXX", "SCI", "CSI", "ST", "OSC", "PM", "APC", "NBS"
96 static char rxdigits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
97 static char rxresult[RXRESULT+1];
99 /* C K S T R N C P Y */
102 Copies a NUL-terminated string into a buffer whose total length is given,
103 ensuring that the result is NUL-terminated even if it has to be truncated.
106 dest = pointer to destination buffer
107 src = pointer to source string
108 len = length of destination buffer (the actual length, not one less).
111 int, The number of bytes copied, 0 or more.
113 NOTE: This is NOT a replacement for strncpy():
114 . strncpy() does not require its source string to be NUL-terminated.
115 . strncpy() does not necessarily NUL-terminate its result.
116 . strncpy() right-pads dest with NULs if it is longer than src.
117 . strncpy() treats the length argument as the number of bytes to copy.
118 . ckstrncpy() treats the length argument as the size of the dest buffer.
119 . ckstrncpy() doesn't dump core if given NULL string pointers.
120 . ckstrncpy() returns a number.
122 Use ckstrncpy() when you want to:
123 . Copy an entire string into a buffer without overrun.
124 . Get the length of the string back.
126 Use strncpy() when you want to:
127 . Copy a piece of a string.
131 ckstrncpy(char * dest, const char * src, int len)
133 ckstrncpy(dest,src,len) char * dest, * src; int len;
134 #endif /* CK_ANSIC */
137 if (len < 1 || !src || !dest) { /* Nothing or nowhere to copy */
138 if (dest) *dest = NUL;
142 for (i = 0; src[i] && (i < len-1); i++) /* Args OK, copy */
147 if (i > len) i = len;
150 #endif /* NOCKSTRNCPY */
154 /* C K S T R N C A T */
157 Appends a NUL-terminated string to a buffer whose total length is given,
158 ensuring that the result is NUL-terminated even if it had to be truncated.
161 dest = pointer to destination buffer containing a null-terminated string
162 src = pointer to null-terminated source string
163 len = length of destination buffer (the actual length, not one less).
166 int, The number of bytes copied, 0 or more.
170 ckstrncat(char * dest, const char * src, int len)
172 ckstrncat(dest,src,len) char * dest, * src; int len;
173 #endif /* CK_ANSIC */
177 register char * s1, * s2;
178 #endif /* NOCKSTRNCPY */
179 if (len < 1 || !src || !dest) { /* Nothing or nowhere to copy */
180 if (dest) *dest = NUL;
185 for (i = 0, j = strlen(dest); src[i] && (i < len-j-1); i++)
191 while (*s1++) j++; /* j = strlen(dest); */
192 s1--; /* (back up over NUL) */
196 while (*s2++) i++; /* i = strlen(src); */
204 strncpy(&dest[j],src,i);
206 j = i; /* This should be a bit faster... */
207 s2 = (char *)src; /* depends on strcpy implementation; */
208 while ((*s1++ = *s2++) && j--) /* at least it shouldn't be slower. */
210 dest[len-1] = NUL; /* In case of early exit. */
213 #endif /* NOCKSTRNCPY */
217 /* C K M A K M S G */
220 Constructs a message from up to 4 pieces with length checking.
221 Result is always NUL terminated. Call with:
222 buf: Pointer to buffer for constructing message.
223 len: Length of buffer.
224 s1-s4: String pointers (can be NULL).
226 0: Nothing was copied.
227 n: (positive number) n bytes copied, all args copied successfully.
228 -n: n bytes were copied, destination buffer not big enough for all.
230 ckmakxmsg() -- accepts 12 string args.
231 ckitoa(), ckltoa(), ckctoa(), ckitox(), etc.
232 Use ckmak[x]msg() plus ck?to?() as a safe replacement for sprintf().
236 ckmakmsg(char * buf, int len, char *s1, char *s2, char *s3, char *s4)
238 ckmakmsg(buf,len,s1,s2,s3,s4) char *buf, *s1, *s2, *s3, *s4; int len;
239 #endif /* CK_ANSIC */
245 if (!buf) return(n); /* No destination */
246 if (len < 1) return(n); /* No size */
248 s = buf; /* Point to destination */
249 a[0] = s1; a[1] = s2; a[2] = s3; a[3] = s4; /* Array of source strings */
250 for (i = 0; i < 4; i++) { /* Loop thru array */
251 p = a[i]; /* Point to this element */
252 if (p) { /* If pointer not null */
253 n = ckstrncpy(s,p,len); /* Copy safely */
254 m += n; /* Accumulate total */
255 if (p[n]) /* Didn't get whole thing? */
256 return(-m); /* return indicating buffer full */
257 len -= n; /* Deduct from space left */
258 s += n; /* Otherwise advance dest pointer */
261 return(m); /* Return total bytes copied */
265 /* C K M A K X M S G */
267 /* Exactly like ckmakmsg(), but accepts 12 string arguments. */
271 ckmakxmsg(char * buf, int len,
272 char *s1, char *s2, char *s3, char *s4, char *s5, char *s6,
273 char *s7, char *s8, char *s9, char *s10, char *s11, char *s12)
275 ckmakxmsg(buf,len,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12)
276 char *buf, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12;
278 #endif /* CK_ANSIC */
285 if (!buf) return(n); /* No destination */
286 if (len < 1) return(n); /* No size */
288 s = buf; /* Point to destination */
289 a[0] = s1; a[1] = s2; a[2] = s3; a[3] = s4; /* Source-string array */
290 a[4] = s5; a[5] = s6; a[6] = s7; a[7] = s8;
291 a[8] = s9; a[9] = s10; a[10] = s11; a[11] = s12;
292 for (i = 0; i < 12; i++) { /* Loop thru array */
293 p = a[i]; /* Point to this element */
294 if (p) { /* If pointer not null */
295 n = ckstrncpy(s,p,len); /* Copy safely */
296 m += n; /* Accumulate total */
297 if (p[n]) /* Didn't get whole thing? */
298 return(-m); /* return indicating buffer full */
299 len -= n; /* Deduct from space left */
300 s += n; /* Otherwise advance dest pointer */
303 return(m); /* Return total bytes copied */
306 /* C H A R T O S T R */
308 /* Converts a character to a string, interpreting controls. */
311 chartostr(x) int x; { /* Call with char x */
312 static char buf[2]; /* Returns string pointer. */
317 if (x > 127 && x < 161)
318 return(c1tab[x - 128]);
322 buf[0] = (unsigned)(x & 0xff);
328 /* Returns the rightmost character of the given null-terminated string */
331 ckrchar(s) char * s; {
332 register CHAR c = '\0', *p;
334 if (!p) p = (CHAR *)""; /* Null pointer == empty string */
336 while (*p) /* Crawl to end of string */
338 return((unsigned)(c & 0xff)); /* Return final character */
341 /* C K S T R C H R */
343 /* Replacement for strchr(), which is not universal. */
345 s = pointer to string to look in.
346 c = character to look for.
348 NULL if c not found in s or upon any kind of error, or:
349 pointer to first occurrence of c in s, searching from left to right.
353 ckstrchr(char * s, char c)
355 ckstrchr(s,c) char *s, c;
356 #endif /* CK_ANSIC */
360 while (*s && *s != c)
362 return((*s == c) ? s : NULL);
365 /* C K S T R R C H R */
367 /* Replacement for strrchr(), which is not universal. */
369 s = pointer to string to look in.
370 c = character to look for.
372 NULL if c not found in s or upon any kind of error, or:
373 pointer to first occurrence of c in s, searching from right to left.
377 ckstrrchr(char * s, char c)
379 ckstrrchr(s,c) char *s, c;
380 #endif /* CK_ANSIC */
394 /* C K S T R P B R K -- Portable replacement for strpbrk() */
396 /* Returns pointer to first char in s1 that is also in s2, or NULL */
399 ckstrpbrk(s1, s2) char * s1, * s2; {
401 if (!s1 || !s2) return(NULL);
402 if (!*s1 || !*s2) return(NULL);
403 while ((c1 = *s1++)) {
405 while ((c2 = *s3++)) {
413 /* C K L O W E R -- Lowercase a string IN PLACE */
415 /* Returns the length of the string */
418 cklower(s) char *s; {
422 if (isupper(*s)) *s = (char) tolower(*s);
428 /* C K U P P E R -- Uppercase a string IN PLACE */
430 /* Returns the length of the string */
433 ckupper(s) char *s; {
437 if (islower(*s)) *s = (char) toupper(*s);
443 /* C K L T O A -- Long to string -- FOR DISCIPLINED USE ONLY */
446 static char numbuf[NUMBUF+32] = { NUL, NUL };
447 static int numbp = 0;
449 ckltoa() and ckitoa() are like atol() and atoi() in the reverse direction,
450 returning a pointer to the string representation of the given number without
451 the caller having to worry about allocating or defining a buffer first.
452 They manage their own internal buffer, so successive calls return different
453 pointers. However, to keep memory consumption from growing without bound,
454 the buffer recycles itself. So after several hundred calls (depending on
455 the size of the numbers), some of the earlier pointers might well find
456 themselves referencing something different. Moral: You can't win in C.
457 Therefore, these routines are intended mainly for generating numeric strings
458 for short-term use, e.g. for passing numbers in string form as parameters to
459 functions. For long-term use, the result must be copied to a safe place.
466 #endif /* CK_ANSIC */
468 char buf[32]; /* Internal working buffer */
470 int k, x, len = 0, sign = 0;
471 if (n < 0L) { /* Sign */
476 for (k = 30; k > 0; k--) { /* Convert number to string */
483 if (sign) buf[--k] = '-'; /* Add sign if necessary */
485 if (len + numbp > NUMBUF)
490 while ((*p++ = *s++)) ; /* Copy */
493 return(q); /* Return pointer */
496 /* C K U L T O A -- Unsigned long to string */
500 ckultoa(unsigned long n)
502 ckultoa(n) unsigned long n;
503 #endif /* CK_ANSIC */
505 char buf[32]; /* Internal working buffer */
509 for (k = 30; k > 0; k--) { /* Convert number to string */
517 if (len + numbp > NUMBUF)
522 while ((*p++ = *s++)) ; /* Copy */
524 return(q); /* Return pointer */
529 ckltox(long n) /* Long int to "0x.." hex string */
532 #endif /* CK_ANSIC */
534 char buf[32]; /* Internal working buffer */
535 char *p, *q, *s, *bp = buf + 2;
539 sprintf(bp, "%lx", n);
542 sprintf(bp, "0%lx", n);
546 if (numbp + k >= NUMBUF)
551 while ((*p++ = *s++)) ; /* Copy */
554 return(q); /* Return pointer */
558 /* C K F S T O A -- File Size (or offset) to string */
560 /* This is just like ckltoa() except for the data type of the argument. */
561 /* It's mainly for printing file sizes without having to know their data */
562 /* type, so we don't have to hardware "%ld" or "%lld" into printf()s. */
563 /* Works for 32 or 64 bits, according to CK_OFF_T definition. */
569 ckfstoa(n) CK_OFF_T n;
570 #endif /* CK_ANSIC */
572 char buf[32]; /* Internal working buffer */
574 int k, x, len = 0, sign = 0;
576 if (n < (CK_OFF_T)0) { /* Sign */
580 buf[31] = NUL; /* 2^63-1 is about 20 decimal digits */
581 for (k = 30; k > 0; k--) { /* Convert number to string */
582 x = n % (CK_OFF_T)10;
585 ckstrncpy(&buf[23],"OVERFLOW",32);
591 n = n / (CK_OFF_T)10;
595 if (sign) buf[--k] = '-'; /* Add sign if necessary */
597 if (len + numbp > NUMBUF)
602 while ((*p++ = *s++)) ; /* Copy */
605 return(q); /* Return pointer */
608 /* C K A T O F S -- String to File Size (or offset) */
610 /* This is the inverse of ckfstoa(), a replacement for atol() that works */
611 /* for either 32-bit or 64-bit arguments, according to CK_OFF_T definition. */
612 /* Like atol(), there is no error indication. */
619 #endif /* CK_ANSIC */
621 CK_OFF_T result = (CK_OFF_T)0;
623 while (*s && (*s == SP || *s == HT)) s++;
629 while (isdigit(*s)) {
630 result = (result * (CK_OFF_T)10) + (CK_OFF_T)(*s - '0');
633 return(minus ? -result : result);
636 /* C K I T O A -- Int to string -- FOR DISCIPLINED USE ONLY */
639 ckitoa(n) int n; { /* See comments with ckltoa(). */
646 char * /* Unsigned int to string */
647 ckuitoa(n) unsigned int n; {
654 ckitox(n) int n; { /* Int to hex */
662 ckctoa(char c) /* Char to string */
668 static int current = 0;
672 buf[current++] = '\0';
673 return((char *)(buf + current - 2));
678 ckctox(CHAR c, int flag) /* Unsigned char to hex */
680 ckctox(c, flag) CHAR c; int flag;
684 static int current = 0;
691 if (!flag && isupper(rxdigits[x]))
692 h = tolower(rxdigits[x]);
696 if (!flag && isupper(rxdigits[x]))
697 h = tolower(rxdigits[x]);
699 buf[current++] = '\0';
700 return((char *)(buf + current - 3));
703 /* C K I N D E X -- C-Kermit's index function */
705 We can't depend on C libraries to have one, so here is our own.
707 s1 - String to look for.
708 s2 - String to look in.
709 t - Offset from right or left of s2, 0 based; -1 for rightmost char in s2.
710 r - 0 for left-to-right search, non-0 for right-to-left.
711 icase 0 for case independence, non-0 if alphabetic case matters.
712 Returns 0 if string not found, otherwise a 1-based result.
713 Also returns 0 on any kind of error, e.g. junk parameters.
716 ckindex(s1,s2,t,r,icase) char *s1, *s2; int t, r, icase; {
717 int len1 = 0, len2 = 0, i, j, x, ot = t; /* ot = original t */
720 if (!s1 || !s2) return(0);
722 while (*s++) len1++; /* length of string to look for */
724 while (*s++) len2++; /* length of string to look in */
726 if (t < 0) t = len2 - 1;
728 j = len2 - len1; /* length difference */
730 if (j < 0 || (r == 0 && t > j)) /* search string is longer */
732 if (r == 0) { /* Index */
733 s = s2 + t; /* Point to beginning of target */
734 for (i = 0; i <= (j - t); i++) { /* Now compare */
735 x = ckstrcmp(s1,s++,len1,icase);
739 } else { /* Reverse Index */
740 i = len2 - len1; /* Where to start looking */
741 if (ot > 0) /* Figure in offset if any */
743 for (j = i; j > -1; j--) {
744 if (!ckstrcmp(s1,&s2[j],len1,icase))
751 /* C K S T R S T R -- Portable replacement for strstr() */
753 /* Returns pointer to first occurrence of s1 in s2, or NULL */
756 ckstrstr(s1, s2) char * s1, * s2; {
758 k = ckindex(s2,s1,0,0,1);
759 return((k < 1) ? NULL : &s1[k-1]);
763 /* B R S T R I P -- Strip enclosing braces from arg string, in place. */
766 Pointer to string that can be poked.
768 Pointer to string without enclosing braces.
769 If original string was not braced, this is the arg pointer;
770 otherwise it is 1 + the arg pointer, with the matching closing
771 brace zero'd out. If the string starts with a brace but does
772 not end with a matching brace, the original pointer to the original
773 string is returned. If the arg pointer is NULL, a pointer to an
774 empty string is returned.
778 /* This is the original version, handling only braces */
781 brstrip(p) char *p; {
785 x = (int)strlen(p) - 1;
795 /* New version handles braces and doublequotes */
796 /* WARNING: this function writes into its argument, it always has. */
799 brstrip(p) char *p; {
801 if (*p == '{' || (*p == '"' && dblquo)) {
803 x = (int)strlen(p) - 1;
805 if ((*p == '{' && p[x] == '}') ||
806 (*p == '"' && p[x] == '"')) {
807 if (x > 0 && p[x-1] != CMDQ) {
820 /* Even newer experimental version -- breaks many things */
823 fnstrip(p) char *p; {
825 extern int cmd_quoting; /* Bad - no externs allowed! */
834 for (j = 0; j < len; j++ ) {
836 (!cmd_quoting || j == 0 || p[j-1] != CMDQ)) {
837 for (n = 1, i = j+1; i < len; i++ ) {
838 if (p[i] == '{' && (!cmd_quoting || p[i-1] != CMDQ))
840 else if (p[i] == '}' && (!cmd_quoting || p[i-1] != CMDQ)) {
842 for (k = j; k < i - 1; k++)
844 for (; i < len; i++ )
853 if (n == 1) { /* Implied right brace at end of field */
854 for (k = j; k < len; k++)
858 } else if (*p == '"') {
862 for (j = 0; j < len; j++) {
864 (!cmd_quoting || j == 0 || p[j-1] != CMDQ)) {
867 for (i = j + 1; i < len; i++) {
868 if (p[i] == '"' && (!cmd_quoting || p[i-1] != CMDQ)) {
871 for (k = j; k < i - 1; k++)
881 if (n == 1) { /* Implied double quote at end of field */
882 for (k = j; k < len; k++ )
893 Not used -- Note: these not only write into their arg, but write past
897 brace(fn) char *fn; {
900 for (p = fn; *p; p++) {
928 /* d q u o t e -- Puts doublequotes around arg in place. */
931 Pointer to buffer and its total length and flag = 0 to use
932 doublequotes, 1 to use braces.
934 Number: length of result.
937 dquote(fn, len, flag) char *fn; int len; int flag; {
938 int spaces = 0, k = 0;
944 for (p = fn; *p; p++) {
955 *p = flag ? '{' : '"';
966 *p = flag ? '}' : '"';
974 /* U N T A B I F Y --- Untabify s1 into s2, assuming tabs every 8 space */
977 untabify(s1,s2,max) char * s1, * s2; int max; {
980 for (i = 0, k = 0; k < x; k++) {
991 for (j = 0; j < z && i < max; j++)
999 /* M A K E L I S T --- Breaks {{s1}{s2}..{sn}} into an array of strings */
1002 s = pointer to string to break up.
1003 list = array of string pointers.
1004 len = number of elements in array.
1005 NOTE: The array must be preinitialized to all NULL pointers.
1006 If any array element is not NULL, it is assumed to have been malloc'd
1007 and is therefore freed. Do NOT call this function with an uninitialized
1008 array, or with an array that has had any static elements assigned to it.
1011 makelist(s,list,len) char * s; char *list[]; int len; {
1012 int i, n, q, bc = 0;
1013 char *p = NULL, *s2 = NULL;
1014 debug(F110,"makelist s",s,0);
1015 if (!s) { /* Check for null or empty string */
1024 if ((s2 = (char *)malloc(n+1))) { /* Safe copy for poking */
1025 strcpy(s2,s); /* (no need for ckstrncpy here) */
1028 s = brstrip(s); /* Strip braces */
1029 n = strlen(s); /* Get length */
1030 if (*s != '{') { /* Outer braces only */
1031 if ((p = (char *)malloc(n+1))) { /* So just one pattern */
1032 strcpy(p,s); /* (no need for ckstrncpy here) */
1040 q = 0; /* Inner ones too */
1041 i = 0; /* so a list of patterns. */
1043 while (*s && i < len) {
1044 if (*s == CMDQ) { /* Quote... */
1050 if (*s == '{' && !q) { /* Opening brace */
1051 if (bc++ == 0) { /* Beginning of a group */
1054 } else { /* It's a brace inside the group */
1059 } else if (*s == '}' && !q) { /* Closing brace */
1060 if (--bc == 0) { /* End of a group */
1062 debug(F111,"makelist element",p,i);
1065 if ((list[i] = (char *)malloc(n+1))) {
1066 ckstrncpy(list[i],p,n+1); /* Note: n+1 */
1069 while (*s == SP) s++;
1073 } else { /* Within a group */
1077 } else { /* Regular character */
1083 if (*p && i < len) { /* Last one */
1086 if ((list[i] = (char *)malloc(n+1))) {
1087 ckstrncpy(list[i],p,n+1);
1088 debug(F111,"makelist last element",p,i);
1091 i++; /* Clear out the rest of the list */
1092 for ( ; i < len; i++) {
1101 M A K E S T R -- Creates a dynamically allocated string.
1103 Makes a new copy of string s and sets pointer p to its address.
1104 Handles degenerate cases, like when buffers overlap or are the same,
1105 one or both arguments are NULL, etc.
1107 The source string is assumed to be NUL-terminated. Therefore it can not
1108 be a UCS-2 string or arbitrary binary data.
1110 The target pointer must be either NULL or else a pointer to a previously
1111 malloc'ed buffer. If not, expect a core dump or segmentation fault.
1113 Note: The caller can tell whether this routine failed as follows:
1116 if (q & !p) { makestr() failed };
1118 Really this routine should have returned a length, but since it doesn't
1119 we set the global variable makestrlen to the length of the result string.
1125 makestr(char **p, const char *s)
1127 makestr(p,s) char **p, *s;
1133 register const char * s2;
1136 #endif /* CK_ANSIC */
1139 if (*p == s) /* The two pointers are the same. */
1140 return; /* Don't do anything. */
1142 if (!s) { /* New definition is null? */
1143 if (*p) /* Free old storage. */
1145 *p = NULL; /* Return null pointer. */
1149 s2 = s; /* Maybe new string will fit */
1153 This is a fairly big win, allowing us to skip the malloc() and free if the
1154 destination string already exists and is not shorter than the source string.
1155 But it doesn't allow for possible overlap of source and destination.
1157 if (*p) { /* into old storage... */
1161 if (!(*p2++ = *s2++))
1168 #endif /* COMMENT */
1173 while (*s2++) x++; /* Get (rest of) length of s. */
1175 if (x >= 0) { /* Get length, even of empty string. */
1176 q = malloc(x+1); /* Get and point to temp storage. */
1178 makestrlen = x; /* Remember length for stats */
1179 s2 = s; /* Point back to beginning of source */
1180 q2 = q; /* Copy dest pointer to increment... */
1181 while ((*q2++ = *s2++)) ; /* Instead of calling strcpy(). */
1183 Note: HP flexelint says that the above loop can result in creation (++) and
1184 access (*) of out-of-bounds pointers. I really don't see it.
1188 else { /* This would be a really bad error */
1189 char tmp[24]; /* So get a good record of it. */
1191 ckstrncpy(tmp,s,20);
1192 strcpy(tmp+20,"...");
1195 strcpy(tmp,s); /* We already checked the length */
1197 debug(F110,"MAKESTR MALLOC FAILURE ",tmp,0);
1201 q = NULL; /* Length of string is zero */
1203 if (*p) /* Now free the original storage. */
1208 /* X M A K E S T R -- Non-destructive makestr() if s is NULL. */
1212 xmakestr(char **p, const char *s)
1214 xmakestr(p,s) char **p, *s;
1217 if (s) makestr(p,s);
1221 /* C K M E M C P Y -- Portable (but slow) memcpy() */
1223 /* Copies n bytes from s to p, allowing for overlap. */
1224 /* For use when real memcpy() not available. */
1227 ckmemcpy(p,s,n) char *p, *s; int n; {
1232 if (!s || !p || n <= 0 || p == s) /* Verify args */
1234 x = p - s; /* Check for overlap */
1237 if (x < n) { /* They overlap */
1239 if (!(p = (char *)malloc(n))) /* So use a temporary buffer */
1242 for (i = 0; i < n; i++) /* Copy n bytes */
1244 if (q) { /* If we used a temporary buffer */
1245 for (i = 0; i < n; i++) /* copy from it to destination */
1247 if (p) free(p); /* and free the temporary buffer */
1250 #endif /* USE_MEMCPY */
1253 /* C K S T R C M P -- String comparison with case-matters selection */
1255 Call with pointers to the two strings, s1 and s2, a length, n,
1256 and c == 0 for caseless comparison, nonzero for case matters.
1257 Call with n == -1 to compare without a length limit.
1258 Compares up to n characters of the two strings and returns:
1262 Note: case handling is only as good as isupper() and tolower().
1265 ckstrcmp(s1,s2,n,c) char *s1, *s2; register int n, c; {
1266 register CHAR t1, t2;
1267 if (n == 0) return(0);
1268 if (!s1) s1 = ""; /* Watch out for null pointers. */
1270 if (!*s1) return(*s2 ? -1 : 0);
1271 if (!*s2) return(1);
1273 t1 = (CHAR) *s1++; /* Get next character from each. */
1275 if (!t1) return(t2 ? -1 : 0);
1277 if (!c) { /* If case doesn't matter */
1278 if (isupper(t1)) t1 = tolower(t1); /* Convert case. */
1279 if (isupper(t2)) t2 = tolower(t2);
1281 if (t1 < t2) return(-1); /* s1 < s2 */
1282 if (t1 > t2) return(1); /* s1 > s2 */
1284 return(0); /* They're equal */
1287 /* C K S T R P R E -- Caseless string prefix comparison */
1289 /* Returns position of the first char in the 2 strings that doesn't match */
1292 ckstrpre(s1,s2) char *s1, *s2; {
1300 if (!t1 || !t2) return(n);
1301 if (isupper(t1)) t1 = tolower(t1);
1302 if (isupper(t2)) t2 = tolower(t2);
1311 /* C K M A T C H -- Match a string against a pattern */
1314 pattern to be matched.
1315 string to look for the pattern in.
1316 icase is 1 if case-sensitive, 0 otherwise.
1319 1 = Match strings starting with '.'
1320 0 = Don't match them (used with UNIX filenames).
1322 1 = File globbing (dirseps are fences);
1323 0 = Dirseps are not fences.
1325 1 = Allow ^ and $ anchors at beginning and end of pattern.
1326 0 = Don't allow them (normal case for filename matching).
1327 Bit 3 (and beyond): Undefined.
1328 Works only with NUL-terminated strings.
1329 Pattern may contain any number of ? and/or *.
1330 If CKREGEX is defined, also [abc], [a-z], and/or {string,string,...}.
1331 (Note: REGEX is a misnomer, see below.)
1334 0 if string does not match pattern,
1335 >= 1, the 1-based position in the string where the match was found.
1338 Find a way to identify the piece of the string that matched the pattern,
1339 as in Snobol "LINE (PAT . RESULT)". This is now partially done by
1340 setting matchpos and matchend (except matchend needs some tuning). But
1341 these are useless unless a copy of the string is kept, or a copy of the
1342 matching part is made. But that would be too costly in performance --
1343 this routine has to be fast because it's used for wildcard expansion.
1346 Patterns are not the same as regular expressions, in which '*' means
1347 0 or more repetitions of the preceding item. For example "a*b" as a
1348 pattern matches any string that starts with 'a' and ends with 'b'; as a
1349 regular expression it matches any string of zero or more a's followed by
1350 one b. Regular expressions are especially useful in matching strings of
1351 (say) digits, or letters, e.g. "[0-9]*" matches any string of digits.
1352 So far, Kermit doesn't do this.
1354 static char * mypat = NULL; /* For rewriting pattern */
1355 static int matchpos = 0;
1357 static int matchdepth = 0;
1358 static int stringpos = 0;
1359 static char * ostring = NULL;
1361 #define MATCHRETURN(x,y) { rc=y; where=x; goto xckmatch; }
1362 static char * lastpat = NULL;
1364 static int xxflag = 0; /* Global bailout flag for ckmatch() */
1367 ispattern(s) char * s; {
1368 int quote = 0, sbflag = 0, sb = 0, cbflag = 0, cb = 0;
1371 if (*s == '^') return(1);
1372 while ((c = *s++)) {
1381 if (c == '*') return(1);
1382 if (c == '?') return(1);
1383 /* Unquoted brackets or braces must match */
1384 if (c == '[') { sbflag++; sb++; continue; }
1385 if (c == ']') { sb--; continue; }
1386 if (c == '{') { cbflag++; cb++; continue; }
1387 if (c == '}') { cb--; continue; }
1388 if (!*s && c == '$') return(1);
1390 return(sbflag || cbflag);
1394 ckmatch(pattern, string, icase, opts) char *pattern,*string; int icase, opts; {
1395 int q = 0, i = 0, k = -1, x, flag = 0;
1396 int rc = 0; /* Return code */
1399 CHAR cp; /* Current character from pattern */
1400 CHAR cs; /* Current character from string */
1401 char * patstart; /* Start of pattern */
1402 int plen, dot, globbing, xstar = 0;
1403 int bronly = 0; /* Whole pattern is {a,b,c,...} */
1405 debug(F111,"CKMATCH ENTRY pat opt",pattern,opts);
1406 debug(F111,"CKMATCH ENTRY str dep",string,matchdepth);
1407 /* debug(F101,"CKMATCH ENTRY icase","",icase); */
1409 globbing = opts & 2;
1411 if (!string) string = "";
1412 if (!pattern) pattern = "";
1414 if (!*pattern) { /* Empty pattern matches anything */
1415 matchdepth++; /* (it wasn't incremented yet) */
1417 } else if (!*string) {
1420 patstart = pattern; /* Remember beginning of pattern */
1422 if (matchdepth == 0) { /* Top-level call? */
1424 stringpos = 0; /* Reset indices etc. */
1429 if (*pattern == '{') /* Entire pattern is {a,b.c} */
1430 bronly = 1; /* Maybe */
1431 dot = (opts & 1) || /* Match leading dot (if file) */
1432 ((opts & 2) == 0) || /* always if not file */
1433 (pattern[0] == '.'); /* or if pattern starts with '.' */
1435 plen = strlen(pattern); /* Length of pattern */
1436 /* This would be used in calculating length of matching segment */
1437 if (plen > 0) /* User's pattern ends with '*' */
1438 if (pattern[plen - 1] == '*')
1440 if (pattern[0] == '*') { /* User's pattern starts with '*' */
1442 debug(F111,"CKMATCH 1",string, matchpos);
1444 if (opts & 4) { /* ^..$ allowed (top level only) */
1445 /* Rewrite pattern to account for ^..$ anchoring... */
1447 if (mypat) free(mypat); /* Get space for "*pattern*" */
1448 mypat = (char *)malloc(plen + 4);
1449 if (mypat) { /* Got space? */
1450 char * s = pattern, * p = mypat; /* Set working pointers */
1451 if (*s == '^') { /* First source char is ^ */
1452 s++; /* so skip past it */
1453 } else if (*s != '*') { /* otherwise */
1454 *p++ = '*'; /* prepend '*' to pattern */
1456 while (*s) { /* Copy rest of pattern */
1457 if (!*(s+1)) { /* Final pattern character? */
1458 if (*s != '$') { /* If it's not '$' */
1459 *p++ = *s; /* Copy it into the pattern */
1460 if (*s++ != '*') /* And if it's also not '*' */
1461 *p++ = '*'; /* append '*'. */
1464 } else /* Not final character */
1465 *p++ = *s++; /* Just copy it */
1467 *p = NUL; /* Terminate the new pattern */
1468 pattern = mypat; /* Make the switch */
1470 debug(F110,"CKMATCH INIT pat",pattern,0);
1473 matchdepth++; /* Now increment call depth */
1476 if (!dot) { /* For UNIX file globbing */
1477 if (*string == '.' && *pattern != '.' && !matchdot) {
1480 *pattern != '{' && *pattern != '['
1483 #endif /* CKREGEX */
1485 debug(F110,"ckmatch skip",string,0);
1493 cp = *pattern; /* Character from pattern */
1494 cs = *string; /* Character from string */
1497 debug(F000,"CKMATCH pat cp",pattern,cp);
1498 debug(F000,"CKMATCH str cs",string,cs);
1499 #endif /* COMMENT */
1501 if (!cs) { /* End of string - done. */
1502 x = (!cp || (cp == '*' && !*(pattern+1))) ? 1 : 0;
1505 matchpos = stringpos;
1506 debug(F111,"CKMATCH A",string, matchpos);
1508 matchend = stringpos;
1509 MATCHRETURN(2,matchpos);
1511 debug(F111,"CKMATCH ZERO d",string, matchpos);
1513 MATCHRETURN(16,matchpos);
1515 if (!icase) { /* If ignoring case */
1516 if (isupper(cp)) /* convert both to lowercase. */
1521 if (q) { /* This character was quoted */
1522 debug(F000,"CKMATCH QUOTED",pattern,cp);
1523 q = 0; /* Turn off quote flag */
1525 if (cs == cp) { /* Compare directly */
1526 if (!matchpos) { /* Matches */
1527 matchpos = stringpos;
1528 debug(F111,"CKMATCH \\ new match",string, matchpos);
1531 } else { /* Doesn't match */
1532 pattern = lastpat; /* Back up the pattern */
1534 debug(F111,"CKMATCH \\ no match",pattern, matchpos);
1540 if (cp == CMDQ && !q) { /* Quote in pattern */
1541 debug(F000,"CKMATCH QUOTE",pattern,cp);
1542 q = 1; /* Set flag */
1543 pattern++; /* Advance to next pattern character */
1544 continue; /* and continue. */
1546 if (cs && cp == '?') { /* '?' matches any char */
1548 matchpos = stringpos;
1549 debug(F111,"CKMATCH D",string, matchpos);
1551 debug(F110,"CKMATCH ? pat",pattern,0);
1552 debug(F110,"CKMATCH ? str",string,0);
1558 } else if (cp == '[') { /* Have bracket */
1559 int q = 0; /* My own private q */
1560 char * psave = NULL; /* and backup pointer */
1561 CHAR clist[256]; /* Character list from brackets */
1564 for (i = 0; i < 256; i++) /* memset() etc not portable */
1566 psave = ++pattern; /* Where pattern starts */
1567 debug(F111,"CKMATCH [] ",pattern-1, matchpos);
1568 for (flag = 0; !flag; pattern++) { /* Loop thru pattern */
1569 c = (CHAR)*pattern; /* Current char */
1570 debug(F000,">>> pattern char","",c);
1571 if (q) { /* Quote within brackets */
1576 if (!icase) /* Case conversion */
1579 switch (c) { /* Handle unquoted character */
1580 case NUL: /* End of string */
1581 MATCHRETURN(4,0); /* No matching ']' so fail */
1582 case CMDQ: /* Next char is quoted */
1583 q = 1; /* Set flag */
1584 continue; /* and continue. */
1585 case '-': /* A range is specified */
1586 c1 = (pattern > psave) ? (CHAR)*(pattern-1) : NUL;
1587 c2 = (CHAR)*(pattern+1); /* IGNORE OUT-OF-BOUNDS WARNING */
1588 if (c2 == ']') c2 = NUL; /* (it can't happen) */
1589 if (c1 == NUL) c1 = c2;
1590 for (c = c1; c <= c2; c++) {
1594 clist[toupper(c)] = 1;
1595 } else if (isupper(c)) {
1596 clist[tolower(c)] = 1;
1601 case ']': /* End of bracketed sequence */
1602 flag = 1; /* Done with FOR loop */
1603 break; /* Compare what we have */
1604 default: /* Just a char */
1605 clist[c] = 1; /* Record it */
1608 clist[toupper(c)] = 1;
1609 } else if (isupper(c)) {
1610 clist[tolower(c)] = 1;
1616 debug(F000,">>> cs","",cs);
1617 debug(F101,">>> clist[cs]","",clist[cs]);
1618 debug(F000,">>> string",string,*string);
1620 if (!clist[(unsigned)cs]) { /* No match? */
1621 if (!*string) { /* This clause 16 Jun 2005 */
1622 MATCHRETURN(5,0); /* Nope, done. */
1625 We need to fail here if the [clist] is not allowed to float.
1626 The [clist] is not allowed to float if it is not preceded
1627 by an asterisk, right? 30 Dec 2005.
1632 string++; /* From here to end added 2005/6/15 */
1634 pattern = lastpat; /* Back up pattern */
1635 k = ckmatch(pattern,string,icase,opts);
1636 if (xxflag) MATCHRETURN(0,0);
1637 if (!matchpos && k > 0)
1638 matchpos = stringpos;
1639 MATCHRETURN(5, (*string) ? matchpos : 0);
1642 matchpos = stringpos;
1643 debug(F111,"CKMATCH [] match",string, matchpos);
1645 string++; /* Yes, advance string pointer */
1647 continue; /* and go on. */
1648 } else if (cp == '{') { /* Braces enclosing list of strings */
1649 char * p, * s, * s2, * buf = NULL;
1652 debug(F111,"CKMATCH {} ",string, matchpos);
1653 for (p = pattern++; *p; p++) {
1654 if (*p == '{') bc++;
1655 if (*p == '}') bc--;
1658 if (bc != 0) { /* Braces don't match */
1659 MATCHRETURN(6,0); /* Fail */
1660 } else { /* Braces do match */
1661 int q = 0, done = 0;
1662 len = *p ? strlen(p+1) : 0; /* Length of rest of pattern */
1665 if (bronly && (matchdepth != 1))
1667 n = p - pattern; /* Size of list in braces */
1668 if ((buf = (char *)malloc(n+1))) { /* Copy so we can poke it */
1671 ckstrncpy(buf,pattern,n+1);
1672 sofar = string - ostring - matchpos + 1;
1673 if (sofar < 0) sofar = 0;
1674 debug(F111,"CKMATCH .. string",string,sofar);
1675 debug(F111,"CKMATCH .. ostring",ostring,sofar);
1677 for (s = s2 = buf; 1; s++) { /* Loop through segments */
1679 if (q) { /* This char is quoted */
1685 if (*s == CMDQ && !q) { /* Quote next char */
1689 if (!*s || *s == ',') { /* End of this segment */
1691 if (!*s) /* If end of buffer */
1692 done = 1; /* then end of last segment */
1693 *s = NUL; /* Overwrite comma with NUL */
1694 debug(F111,"CKMATCH {} segment",s2,done);
1695 tplen = n + len + sofar + 2;
1696 if (!*s2) { /* Empty segment, no advancement */
1698 } else if ((tp = (char *)malloc(tplen))) {
1702 &ostring[matchpos-1] :
1706 ckstrncpy(tp,pp,sofar+1);
1708 ckstrncpy(tp,pp,sofar);
1713 ckstrncpy(&tp[1],pp,sofar+1);
1715 ckstrncpy(&tp[1],pp,sofar);
1717 ckstrncat(tp,s2,tplen); /* Current segment */
1718 ckstrncat(tp,p+1,tplen); /* rest of pattern */
1720 debug(F101,"CKMATCH {} matchpos","",matchpos);
1725 debug(F111,"CKMATCH {} tp",tp,matchpos);
1726 debug(F111,"CKMATCH {} string",
1728 debug(F111,"CKMATCH {} ostring",
1732 /* If segment starts with dot */
1733 /* then set matchdot option.. */
1735 if (*s2 == '.') opts2 |= 1;
1736 debug(F111,"CKMATCH {} recursing",s2,opts2);
1738 (string > ostring) ?
1739 &ostring[savpos-1] : string,
1743 debug(F101,"CKMATCH {} k","",k);
1744 debug(F101,"CKMATCH {} savpos","",savpos);
1749 if (xxflag) MATCHRETURN(0,0);
1753 if (k > 0) { /* If it matched we're done */
1756 } else { /* Malloc failure */
1759 if (k) { /* Successful comparison */
1761 matchpos = stringpos;
1762 debug(F111,"CKMATCH {} match",
1765 string += n-1; /* Advance pointers */
1769 if (done) /* If no more segments */
1770 break; /* break out of segment loop. */
1771 s2 = s+1; /* Otherwise, on to next segment */
1778 #endif /* CKREGEX */
1779 } else if (cp == '*') { /* Pattern char is asterisk */
1781 char * p, * s = NULL; /* meaning match anything */
1783 havestar++; /* The rest can float */
1784 while (*pattern == '*') /* Collapse successive asterisks */
1786 psave = pattern; /* First non-asterisk after asterisk */
1787 lastpat = pattern - 1; /* Ditto, global */
1788 debug(F111,"CKMATCH * ",string,matchpos);
1789 for (n = 0, p = psave; *p; p++,n++) { /* Find next meta char */
1791 if (*p == '?' || *p == '*' || *p == CMDQ
1793 || *p == '[' || *p == '{'
1794 #endif /* CKREGEX */
1803 && (*p == '.' || *p == ']' ||
1804 *p == '<' || *p == '>' ||
1805 *p == ':' || *p == ';')
1812 #endif /* STRATUS */
1813 #endif /* datageneral */
1815 #endif /* UNIXOROSK */
1818 #endif /* GLOBBING */
1821 debug(F111,"CKMATCH * n string",string,n);
1822 debug(F111,"CKMATCH * n pattrn",pattern,n);
1823 debug(F111,"CKMATCH * n p",p,n);
1824 if (n > 0) { /* Literal string to match */
1825 s = (char *)malloc(n+1);
1827 ckstrncpy(s,psave,n+1); /* Copy cuz no poking original */
1829 k = ckindex(s,string,0,0,icase); /* 1-based index() */
1830 debug(F110,"CKMATCH * Index() string",string,0);
1831 debug(F110,"CKMATCH * Index() pattrn",s,0);
1832 debug(F101,"CKMATCH * Index() result","",k);
1833 } else { /* String is right-anchored */
1834 k = ckindex(s,string,-1,1,icase); /* rindex() */
1835 debug(F111,"CKMATCH * Rindex()",string,k);
1836 debug(F110,"CKMATCH * Rindex() pattrn",s,0);
1837 debug(F101,"CKMATCH * Rindex() result","",k);
1843 debug(F111,"CKMATCH * stringpos matchpos",
1844 ckitoa(stringpos), matchpos);
1846 matchpos = string - ostring + k;
1847 debug(F111,"CKMATCH * new match ", string, matchpos);
1849 string += k + n - 1;
1850 stringpos += k + n - 1;
1852 debug(F111,"CKMATCH * new string", string, stringpos);
1853 debug(F110,"CKMATCH * new pattrn", pattern, 0);
1856 } else if (!*p) { /* Asterisk at end matches the rest */
1857 if (!globbing) { /* (if not filename globbing) */
1859 matchpos = stringpos;
1860 debug(F111,"CKMATCH *$ ",string, matchpos);
1862 matchend = stringpos;
1863 MATCHRETURN(9,matchpos);
1867 if (globbing /* Filespec so don't cross fields */
1869 && *string == '/' || *string == '\\' ||
1876 && (*string == '.' || *string == ']' ||
1877 *string == '<' || *string == '>' ||
1878 *string == ':' || *string == ';')
1886 && *string == '/' /* (catch-all) */
1887 #endif /* STRATUS */
1888 #endif /* datageneral */
1890 #endif /* UNIXOROSK */
1893 matchend = stringpos;
1897 matchpos = stringpos;
1898 debug(F111,"CKMATCH *$ match",string, matchpos);
1903 #endif /* GLOBBING */
1905 matchpos = stringpos;
1906 debug(F111,"CKMATCH ** match",string, matchpos);
1908 matchend = stringpos;
1909 MATCHRETURN(11,matchpos);
1911 } else { /* A meta char follows asterisk */
1913 MATCHRETURN(17, matchpos = 0);
1915 /* This is more elegant but it doesn't work. */
1919 k = ckmatch(p,string,icase,opts);
1921 while (*string && ((k = ckmatch(p,string,icase,opts)) < 1)) {
1922 if (xxflag) MATCHRETURN(0,0);
1926 if (!*string && k < 1) {
1928 Definitely no match so we set a global flag to inibit further backing up
1929 and retrying by previous incarnations, since they don't see that the string
1930 and/or pattern, which are on the stack, have been exhausted at this level.
1933 debug(F111,"CKMATCH DEFINITELY NO MATCH",p,k);
1936 #endif /* COMMENT */
1937 debug(F111,"CKMATCH *<meta> k",string, k);
1938 if (!matchpos && k > 0) {
1939 matchpos = stringpos;
1940 debug(F111,"CKMATCH *<meta> matchpos",string, matchpos);
1942 MATCHRETURN(12, (*string) ? matchpos : 0);
1944 } else if (cs == cp) {
1949 matchpos = stringpos;
1950 debug(F111,"CKMATCH cs=cp",string, matchpos);
1966 ckmakxmsg(msgbuf,256,
1974 " string=",string,NULL,NULL
1976 debug(F110,msgbuf,string,0);
1984 /* I S F L O A T -- Verify that arg represents a floating-point number */
1987 Portable replacement for atof(), strtod(), scanf(), etc.
1990 s = pointer to string
1991 flag == 0 means entire string must be a (floating-pointing) number.
1992 flag != 0 means to terminate scan on first character that is not legal.
1995 1 if result is a legal number;
1996 2 if result has a fractional part;
1997 0 if not or if string empty.
2000 Sets global floatval to floating-point value if successful.
2002 Number need not contain a decimal point -- integer is subcase of float.
2003 Scientific notation not supported.
2005 CKFLOAT floatval = 0.0; /* For returning value */
2008 isfloat(s,flag) char *s; int flag; {
2012 CKFLOAT d = 0.0, f = 0.0;
2017 while (isspace(*s)) s++;
2019 if (*s == '-') { /* Handle optional sign */
2022 } else if (*s == '+')
2024 while ((c = *s++)) { /* Handle numeric part */
2026 case 0: /* Mantissa... */
2028 f = f * 10.0 + (CKFLOAT)(c - '0');
2030 } else if (c == '.') {
2035 if (flag) /* Not digit or period */
2036 goto done; /* break if flag != 0 */
2037 return(0); /* otherwise fail. */
2038 case 1: /* Fraction... */
2041 f += (CKFLOAT)(c - '0') / d;
2045 if (flag) /* Illegal character */
2046 goto done; /* Break */
2047 return(0); /* or fail, depending on flag */
2051 if (sign) f = 0.0 - f; /* Apply sign to result */
2052 floatval = f; /* Set result */
2053 return(d ? 2 : 1); /* Succeed */
2057 c k r o u n d -- Rounds a floating point number or an integer.
2060 Floating-point number to round.
2062 Positive...To how many decimal places.
2063 Zero.......Round to integer.
2064 Negative...-1 = nearest ten, -2 = nearest 100, -3 = nearest thousand, etc.
2066 Output buffer for string result if desired.
2068 Length of output buffer.
2070 Result as CKFLOAT (which is not going to be as exact as the string result)
2071 And the exact result in the string output buffer, if one was specified.
2075 ckround(CKFLOAT fpnum, int places, char *obuf, int obuflen)
2077 ckround(fpnum,places,obuf,obuflen)
2078 CKFLOAT fpnum; int places, obuflen; char *obuf;
2079 #endif /* CK_ANSIC */
2082 int i, p, len, x, n, digits;
2088 extern int fp_digits;
2090 sprintf(buf,"%200.100f",fpnum); /* Make string version to work with */
2091 number = (char *) buf; /* Make pointer to it */
2093 p = places; /* Precision */
2094 d = (char *)0; /* Pointer to decimal or string end */
2096 s = number; /* Fix number... */
2097 while (*s == ' ' || *s == '\011') s++; /* Strip leading whitespace */
2098 if (*s == '+') s++; /* Skip leading plus sign*/
2099 number = s; /* Start of number */
2100 if (*s == '-') { minus++; number++; s++; } /* Remember if negative */
2102 s = number; /* Don't allow false precision */
2104 while (*s && *s != '.') s++, n++; /* Find decimal */
2106 if (p + n > fp_digits) /* Too many digits */
2107 p = fp_digits - n; /* Don't ask for bogus precision */
2108 if (p < 0) p = 0; /* But don't ask for less than zero */
2109 if (n > fp_digits) /* Integer part has too many digits */
2110 *s = 0; /* but we can't truncate it */
2111 else /* Magnitude is OK */
2112 number[fp_digits+1] = 0; /* Truncate fractional part. */
2114 len = (int)strlen(number); /* Length of non-bogus number */
2115 d = s; /* Pointer to decimal point */
2116 if (p > 0) { /* Rounding the fractional part */
2117 if (n + p < len) { /* If it's not already shorter */
2118 if (*s == '.') s++; /* Skip past decimal */
2119 s += p; /* Go to desired spot */
2120 if (*s > '4' && *s <= '9') /* Check value of digit */
2122 *s = 0; /* And end the string */
2123 s--; /* Point to last digit */
2125 } else if (p == 0) { /* Rounding to integer */
2127 *s = 0; /* erase the decimal point */
2128 if (*(s+1)) { /* and there is a factional part */
2129 if (*(s+1) > '4' && *(s+1) <= '9') /* Check for carry */
2132 s--; /* Point to last digit */
2134 } else { /* Rounding the integer part */
2135 if (s + p > number) { /* as in "the nearest hundred" */
2136 s += p; /* Go left to desired digit */
2137 *d = 0; /* Discard fraction */
2139 if (*s > '4') /* Check first digit of fraction */
2140 carry = 1; /* and set carry flag */
2142 while (s2 < d) /* Fill in the rest with zeros */
2144 s--; /* Point to last digit */
2147 if (carry) { /* Handle carry, if any */
2148 while (s >= number) {
2149 if (*s == '.') { /* Skip backwards over decimal */
2153 *s += 1; /* Add 1 to current digit */
2155 if (*s <= '9') /* If result is 9 or less */
2156 break; /* we're done */
2157 *s = '0'; /* Otherwise put 0 */
2158 carry = 1; /* carry the 1 */
2159 s--; /* and back up to next digit */
2163 sscanf(number,"%f",&value); /* Convert back to floating point */
2165 sscanf(number,"%lf",&value); /* Convert back to floating point */
2167 if (obuf) strncpy(obuf,number,obuflen); /* Set string result */
2168 return(value); /* Return floating-point result */
2171 #endif /* CKFLOAT */
2173 /* Sorting routines... */
2175 /* S H _ S O R T -- Shell sort -- sorts string array s in place. */
2178 Highly defensive and relatively quick.
2179 Uses shell sort algorithm.
2182 s = pointer to array of strings.
2183 p = pointer to a second array to sort in parallel s, or NULL for none.
2184 n = number of elements in s.
2185 k = position of key.
2186 r = ascending lexical order if zero, reverse lexical order if nonzero.
2187 c = 0 for case independence, 1 for case matters, 2 for numeric.
2189 If k is past the right end of a string, the string is considered empty
2190 for comparison purposes.
2193 To sort a piece of an array, call with s pointing to the first element
2194 and n the number of elements to sort.
2197 None. Always succeeds, unless any of s[0]..s[n-1] are bad pointers,
2198 in which case memory violations are possible, but C offers no defense
2199 against this, so no way to gracefully return an error code.
2202 sh_sort(s,p,n,k,r,c) char **s, **p; int n, k, r, c; {
2204 char *t, *t1, *t2, *u = NULL;
2209 #endif /* CKFLOAT */
2211 if (!s) return; /* Nothing to sort? */
2212 if (n < 2) return; /* Not enough elements to sort? */
2213 if (k < 0) k = 0; /* Key */
2215 m = n; /* Initial group size is whole array */
2217 m = m / 2; /* Divide group size in half */
2218 if (m < 1) /* Small as can be, so done */
2220 for (j = 0; j < n-m; j++) { /* Sort each group */
2221 t = t2 = s[j+m]; /* Compare this one... */
2222 if (!t) /* But if it's NULL */
2223 t2 = ""; /* make it the empty string */
2224 if (p) /* Handle parallel array, if any */
2227 if ((int)strlen(t2) < k) /* If key too big */
2228 t2 = ""; /* make key the empty string */
2229 else /* Key is in string */
2230 t2 = t + k; /* so point to key position */
2232 for (i = j; i >= 0; i -= m) { /* Loop thru comparands s[i..]*/
2234 if (!t1) /* Same deal */
2237 if ((int)strlen(t1) < k)
2242 if (c == 2) { /* Numeric comparison */
2247 if (isfloat(t1,1)) {
2272 #endif /* CKFLOAT */
2274 x = ckstrcmp(t1,t2,-1,c); /* Compare */
2276 if (r == 0 && x < 0)
2278 if (r != 0 && x > 0)
2281 if (p) p[i+m] = p[i];
2290 /* C K R A D I X -- Radix converter */
2293 s: a number in string format.
2294 in: int, specifying the radix of s, 2-36.
2295 out: int, specifying the radix to convert to, 2-36.
2297 NULL on error (illegal radix, illegal number, etc.).
2298 "-1" on overflow (number too big for unsigned long).
2299 Otherwise: Pointer to result.
2302 ckradix(s,in,out) char * s; int in, out; {
2303 char c, *r = rxresult;
2306 unsigned long zz = 0L;
2310 To get 64 bits on 32-bit hardware we use off_t, but there
2311 is no unsigned version of off_t, so we lose the ability to
2314 CK_OFF_T zz = (CK_OFF_T)0;
2315 CK_OFF_T z = (CK_OFF_T)0;
2316 #endif /* COMMENT */
2318 if (in < 2 || in > 36) /* Verify legal input radix */
2320 if (out < 2 || out > 36) /* and output radix. */
2322 if (*s == '+') { /* Get sign if any */
2324 } else if (*s == '-') {
2328 while (*s == SP || *s == '0') /* Trim leading blanks or 0's */
2331 For detecting overflow, we use a signed copy of the unsigned long
2332 accumulator. If it goes negative, we know we'll overflow NEXT time
2335 for (; *s; s++) { /* Convert from input radix to */
2336 c = *s; /* unsigned long */
2337 if (islower(c)) c = toupper(c);
2338 if (c >= '0' && c <= '9')
2340 else if (c >= 'A' && c <= 'Z')
2344 if (d >= in) /* Check for illegal digit */
2347 if (z < 0L) /* Clever(?) overflow detector */
2351 if (!zz) return("0");
2352 r = &rxresult[RXRESULT]; /* Convert from unsigned long */
2353 *r-- = NUL; /* to output radix. */
2354 while (zz > 0 && r > rxresult) {
2355 d = zz % (unsigned)out;
2357 zz = zz / (unsigned)out;
2359 if (minus) *r-- = '-'; /* Replace original sign */
2360 return((char *)(r+1));
2364 /* Base-64 conversion routines */
2366 static char b64[] = { /* Encoding vector */
2368 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
2370 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S',
2371 'T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l',
2372 'm','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4',
2373 '5','6','7','8','9','+','/','=','\0'
2376 static int b64tbl[] = { /* Decoding vector */
2377 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
2378 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
2379 -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
2380 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
2381 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
2382 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
2383 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2384 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
2385 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2386 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2387 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2388 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2389 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2390 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2391 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2392 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
2396 B 8 T O B 6 4 -- Converts 8-bit data to Base64 encoding.
2399 s = Pointer to 8-bit data;
2400 n = Number of source bytes to encode (SEE NOTE).
2401 If it's a null-terminated string, you can use -1 here.
2402 out = Address of output buffer.
2403 len = Length of output buffer (should > 4/3 longer than input).
2406 >= 0 if OK, number of bytes placed in output buffer,
2407 with the subsequent byte set to NUL if space permits.
2408 -1 on error (output buffer space exhausted).
2411 If this function is to be called repeatedly, e.g. to encode a data
2412 stream a chunk at a time, the source length must be a multiple of 3
2413 in all calls but the final one to avoid the generation of extraneous
2414 pad characters that would throw the decoder out of sync. When encoding
2415 only a single string, this is not a consideration. No internal state
2416 is kept, so there is no reset function.
2419 b8tob64(s,n,out,len) char * s,* out; int n, len; {
2420 int b3, b4, i, x = 0;
2423 if (n < 0) n = strlen(s);
2425 for (i = 0; i < n; i += 3,x += 4) { /* Loop through source bytes */
2427 t = (unsigned)((unsigned)((unsigned int)s[i] & 0xff) << 8);
2428 if (n - 1 > i) { /* Do we have another after this? */
2429 t |= (unsigned)(s[i+1] & 0xff); /* Yes, OR it in */
2430 b3 = 1; /* And remember */
2432 t <<= 8; /* Move over */
2433 if (n - 2 > i) { /* Another one after that? */
2434 t |= (unsigned)(s[i+2] & 0xff); /* Yes, OR it in */
2435 b4 = 1; /* and remember */
2437 if (x + 4 > len) /* Check output space */
2439 out[x+3] = b64[b4 ? (t & 0x3f) : 64]; /* 64 = code for '=' */
2441 out[x+2] = b64[b3 ? (t & 0x3f) : 64];
2443 out[x+1] = b64[t & 0x3f];
2445 out[x] = b64[t & 0x3f];
2447 if (x < len) out[x] = NUL; /* Null-terminate the string */
2453 B 6 4 T O B 8 -- Converts Base64 string to 8-bit data.
2456 s = pointer to Base64 string (whitespace ignored).
2457 n = length of string, or -1 if null terminated, or 0 to reset.
2458 out = address of output buffer.
2459 len = length of output buffer.
2462 >= 0 if OK, number of bytes placed in output buffer,
2463 with the subsequent byte set to NUL if space permits.
2465 -1 = output buffer too small for input.
2466 -2 = input contains illegal characters.
2467 -3 = internal coding error.
2470 Can be called repeatedly to decode a Base64 stream, one chunk at a
2471 time. However, if it is to be called for multiple streams in
2472 succession, its internal state must be reset at the beginning of
2476 b64tob8(s,n,out,len) char * s,* out; int n, len; { /* Decode */
2477 static int bits = 0;
2478 static unsigned int r = 0;
2482 if (n == 0) { /* Reset state */
2487 x = (n < 0) ? strlen(s) : n; /* Source length */
2489 n = ((x + 3) / 4) * 3; /* Compute destination length */
2490 if (x > 0 && s[x-1] == '=') n--; /* Account for padding */
2491 if (x > 1 && s[x-2] == '=') n--;
2492 if (n > len) /* Destination not big enough */
2493 return(-1); /* Fail */
2495 for (i = 0; i < x; i++) { /* Loop thru source */
2496 c = (CHAR)s[i]; /* Next char */
2497 t = b64tbl[c]; /* Code for this char */
2498 if (t == -2) { /* Whitespace or Ctrl */
2501 } else if (t == -1) { /* Illegal code */
2502 return(-2); /* Fail. */
2503 } else if (t > 63 || t < 0) /* Illegal value */
2504 return(-3); /* fail. */
2505 bits += 6; /* Count bits */
2506 r <<= 6; /* Make space */
2507 r |= (unsigned) t; /* OR in new code */
2508 if (bits >= 8) { /* Have a byte yet? */
2509 bits -= 8; /* Output it */
2510 c = (unsigned) ((r >> bits) & 0xff);
2514 if (k < len) out[k] = NUL; /* Null-terminate in case it's */
2515 return(k); /* a text string */
2519 /* C H K N U M -- See if argument string is an integer */
2521 /* Returns 1 if OK, zero if not OK */
2522 /* If OK, string should be acceptable to atoi() or atol() or ckatofs() */
2523 /* Allows leading space, sign */
2526 chknum(s) char *s; { /* Check Numeric String */
2527 int x = 0; /* Flag for past leading space */
2528 int y = 0; /* Flag for digit seen */
2530 debug(F110,"chknum",s,0);
2533 while ((c = *s++)) { /* For each character in the string */
2535 case SP: /* Allow leading spaces */
2537 if (x == 0) continue;
2539 case '+': /* Allow leading sign */
2544 default: /* After that, only decimal digits */
2545 if (c >= '0' && c <= '9') {
2555 /* R D I G I T S -- Verify that all characters in arg ARE DIGITS */
2557 /* Returns 1 if so, 0 if not or if string is empty */
2560 rdigits(s) char *s; {
2563 if (!isdigit(*s)) return(0);
2569 /* P A R N A M -- Return parity name */
2576 #endif /* CK_ANSIC */
2579 case 'e': return("even");
2580 case 'o': return("odd");
2581 case 'm': return("mark");
2582 case 's': return("space");
2583 case 0: return("none");
2584 default: return("invalid");
2588 char * /* Convert seconds to hh:mm:ss */
2593 #endif /* CK_ANSIC */
2595 static char buf[10];
2597 h = x / 3600L; /* Hours */
2599 m = x / 60L; /* Minutes */
2600 s = x % 60L; /* Seconds */
2602 sprintf(buf,"%02ld:%02ld:%02ld",h,m,s);
2605 return((char *)buf);
2608 /* L S E T -- Set s into p, right padding to length n with char c; */
2610 s is a NUL-terminated string.
2611 If length(s) > n, only n bytes are moved.
2612 The result is NOT NUL terminated unless c == NUL and length(s) < n.
2613 The intended of this routine is for filling in fixed-length record fields.
2616 lset(p,s,n,c) char *s; char *p; int n; int c; {
2620 #endif /* USE_MEMCPY */
2629 for (i = 0; i < x; i++)
2633 #endif /* USE_MEMCPY */
2636 /* R S E T -- Right-adjust s in p, left padding to length n with char c */
2639 rset(p,s,n,c) char *s; char *p; int n; int c; {
2643 #endif /* USE_MEMCPY */
2651 for (i = 0; i < (n - x); i++)
2655 #endif /* USE_MEMCPY */
2658 /* U L O N G T O H E X -- Unsigned long to hex */
2661 Converts unsigned long arg to hex and returns string pointer to
2662 rightmost n hex digits left padded with 0's. Allows for longs
2663 up to 64 bits. Returns pointer to result.
2667 ulongtohex( unsigned long z, int n )
2669 ulongtohex(z,n) unsigned long z; int n;
2670 #endif /* CK_ANSIC */
2672 static char hexbuf[17];
2673 int i = 16, x, k = 0;
2676 k = 2 * (sizeof(long));
2677 for (i = 0; i < n; i++) {
2678 if (i > k || z == 0) {
2683 hexbuf[15-i] = x + ((x < 10) ? '0' : 0x37);
2686 return((char *)(&hexbuf[16-i]));
2689 /* H E X T O U L O N G -- Hex string to unsigned long */
2692 Converts n chars from s from hex to unsigned long.
2694 0L or positive, good result (0L is returned if arg is NULL or empty).
2695 -1L on error: non-hex arg or overflow.
2698 hextoulong(s,n) char *s; int n; {
2700 unsigned long result = 0L;
2715 if ((d == '0' || d == ' ')) {
2723 if (d >= '0' && d <= '9') {
2725 } else if (d >= 'A' && d <= 'F') {
2730 if (++count > (sizeof(long) * 2))
2732 result = (result << 4) | (d & 0x0f);
2738 c k s p l i t -- Splits a string into words, or:
2739 extracts a given word from a string.
2741 Allows for grouping.
2742 Operates on a copy of the string; does not alter the original string.
2743 All strings NUL-terminated.
2747 1 = split, 0 = word.
2748 n1 = desired word number if fc == 0.
2750 s2 = break string (NULL to accept default = all non-alphanum).
2751 s3 = include string (NULL to accept default = all alphanum).
2752 n2 = grouping mask (OR desired ones together):
2753 1 = doublequotes, 2 = braces, 4 = apostrophes,
2754 8 = parens, 16 = brackets, 32 = angle brackets,
2755 -1 = 63 = all of these.
2756 n3 = group quote character, ASCII value, used and tested only for
2757 LISP quote, e.g. (a 'b c '(d e f)).
2758 n4 = 0 to collapse adjacent separators;
2759 nonzero not to collapse them.
2762 Pointer to struct stringarray, with size:
2763 -1 = memory allocation error.
2764 -2 = too many words in string.
2765 n = number of words (0 or more).
2768 wordarray = array of pointers to n words (n == 1 if fc == 0), 1-based.
2769 Each pointer is to a malloc'd string. This array is recycled upon each
2770 call; if you need to keep the strings, make copies of them. This routine
2771 must have control of allocation and deallocation.
2773 If a given character is included in the include list, it is not treated
2774 as a separator or as a grouping character.
2776 Groups may be nested only if they are formed with braces, parens, or
2777 brackets, but not with quotes or apostrophes since ASCII quotes have no
2778 intrinsic handedness. Group-start and end characters are treated as
2779 separators even in the absence of other separators, so a string such as
2780 "a{b}c" results in three words, not one.
2782 Sample call to split a string into an array:
2783 struct stringarray * q;
2784 q = cksplit(1,0,s1,s2,s3,-1,0);
2785 q->a_size = size (>=0) or failure code (<0)
2786 q->a_head = pointer to array (elements 0 thru q->asize - 1).
2788 Sample call to extract word n from a string:
2789 struct stringarray * q;
2790 q = cksplit(0,n,s1,s2,s3,-1,0);
2791 q->a_size = size (1) or failure code (<0)
2792 q->a_head = pointer to array (element 1 is the desired word).
2797 #define ST_BW 0 /* Between Words */
2798 #define ST_IW 1 /* In Word */
2799 #define ST_IG 2 /* Start Group */
2801 /* Character Classes (bitmap) */
2803 #define CL_SEP 1 /* Separator */
2804 #define CL_OPN 2 /* Group Open */
2805 #define CL_CLS 4 /* Group Close */
2806 #define CL_DAT 8 /* Data */
2807 #define CL_QUO 16 /* Group quote */
2811 #define MAXWORDS 4096 /* Max number of words */
2812 #endif /* MAXWORDS */
2814 #define NESTMAX 64 /* Maximum nesting level */
2815 #endif /* NESTMAX */
2818 #define MAXWORDS 128 /* Max number of words */
2819 #endif /* MAXWORDS */
2821 #define NESTMAX 16 /* Maximum nesting level */
2822 #endif /* NESTMAX */
2823 #endif /* BIGBUFOK */
2825 /* static */ char ** wordarray = NULL; /* Result array of word pointers */
2827 static struct stringarray ck_sval = { /* Return value structure */
2828 NULL, /* Pointer to array */
2831 static int * wordsize = NULL;
2834 setword(n,s,len) int n, len; char * s; {
2840 if (!wordarray) { /* Allocate result array (only once) */
2841 if (!(wordarray = (char **)malloc((MAXWORDS+1) * sizeof(char *))))
2843 if (!(wordsize = (int *)malloc((MAXWORDS+1) * sizeof(int))))
2845 for (i = 0; i <= MAXWORDS; i++) { /* Initialize result array */
2846 wordarray[i] = NULL;
2850 if (wordsize[n] < len /* || !wordarray[n] */ ) {
2851 k = (len < 16) ? 16 : len + (len / 4);
2852 wordarray[n] = (char *) malloc(k+1);
2853 wordsize[n] = (wordarray[n]) ? k : 0;
2856 while ((*p++ = *s++) && k-- > 0) ;
2858 } else if (len > 0) {
2859 k = wordsize[n]; /* (In case len arg is a lie) */
2861 while ((*p++ = *s++) && k-- > 0) {
2863 if ((*(s-1) == CMDQ) && *s != CMDQ) {
2867 #endif /* COMMENT */
2869 } else if (wordarray[n]) {
2870 wordarray[n][0] = NUL;
2874 static char * splitbuf = NULL;
2875 static int nsplitbuf = 0;
2877 /* n4 = 1 to NOT collapse adjacent separators */
2880 struct stringarray *
2881 cksplit(fc,n1,s1,s2,s3,n2,n3,n4) int fc,n1,n2,n3,n4; char *s1, *s2, *s3; {
2882 int splitting = 0; /* What I was asked to do */
2883 int i, k, ko = 0, n, x, max = MAXWORDS; /* Workers */
2884 char * s = NULL, * ss, * p; /* Workers */
2885 char * sep = ""; /* Default break set */
2886 char * notsep = ""; /* Default include set */
2887 int grouping = 0; /* Grouping option */
2888 char * gr_opn = "\"{'([<"; /* Group open brackets */
2889 char * gr_cls = "\"}')]>"; /* Group close brackets */
2890 int gr_stk[NESTMAX]; /* Nesting bracket stack */
2891 int gr_lvl = 0; /* Nesting level */
2892 int wordnum = 0; /* Current word number */
2893 CHAR c = 'A'; /* Current char (dummy start value) */
2894 int class = 0; /* Current character class */
2895 int state = ST_BW; /* Current FSA state */
2896 int len = 0; /* Length of current word */
2897 int slen = 0; /* Length of s1 */
2898 int gquote = 0; /* Quoted group */
2899 int cquote = 0; /* Quoted character */
2900 int collapse = 1; /* Collapse adjacent separators */
2901 int all = 0; /* s3 == ALL */
2902 int csv = 0; /* s3 == CSV */
2903 int tsv = 0; /* s3 == TSV */
2906 unsigned int hex80 = 128;
2907 unsigned int hexff = 255;
2908 CHAR notsepbuf[256];
2910 notsepbuf[0] = NUL; /* Keep set for "ALL" */
2911 if (n4) collapse = 0; /* Don't collapse */
2913 for (i = 0; i < NESTMAX; i++) /* Initialize nesting stack */
2917 ck_sval.a_head = wordarray; /* Initialize return value struct */
2920 if (!s1) s1 = ""; /* s1 = source string */
2921 if (!*s1) { /* If none, nothing to do */
2924 splitting = fc; /* Our job */
2925 if (splitting) { /* If splitting n = word count */
2926 n = 0; /* Initialize it */
2927 } else { /* Otherwise */
2929 if (n1 < 1) { /* If 0 (or less) */
2930 ck_sval.a_size = 0; /* nothing to do. */
2934 if (n1 == 0) { /* If 0 */
2935 ck_sval.a_size = 0; /* nothing to do. */
2938 #endif /* COMMENT */
2939 n = n1; /* n = desired word number. */
2941 slen = 0; /* Get length of s1 */
2942 debug(F111,"cksplit",s1,n);
2947 while (*p++) slen++; /* Make pokeable copy of s1 */
2948 if (!splitbuf || slen > nsplitbuf) { /* Allocate buffer if needed */
2952 xx = (slen < 255) ? 255 : xx + (xx / 4);
2953 debug(F101,"cksplit splitbuf","",xx);
2954 splitbuf = (char *)malloc(xx+1);
2955 if (!splitbuf) { /* Memory allocation failure... */
2956 ck_sval.a_size = -1;
2959 nsplitbuf = xx; /* Remember size of buffer */
2963 The idea is to just copy the string into splitbuf (if it exists). This
2964 gives us both the copy and the length so we only need to grovel through the
2965 string once in most cases. Only when splitbuf doesn't exist or is too short
2966 do we re-malloc(), which should be very infrequent so who cares if we have
2967 to go through the string again in that case.
2971 if (splitbuf) { /* Make pokeable copy of s1 */
2972 while ((*s++ = *p++) && (slen++ < nsplitbuf)) /* Try to copy */
2975 if (!splitbuf || slen >= nsplitbuf) { /* Need to do more... */
2977 if (splitbuf) /* Free previous buf if any */
2979 while (*p++) slen++; /* Get (rest of) length */
2980 xx = (slen < 255) ? 255 : slen + (slen / 4); /* Size of new buffer */
2981 splitbuf = (char *)malloc(xx+1); /* Allocate it */
2982 if (!splitbuf) { /* Memory allocation failure... */
2983 ck_sval.a_size = -1;
2986 nsplitbuf = xx; /* Remember (new) buffer size */
2989 while ((*s++ = *p++)) ;
2992 #endif /* COMMENT */
2994 sep = s2; /* s2 = break set */
2996 notsep = s3; /* s3 = include set */
2999 } else if ((all = !ckstrcmp(notsep,"ALL",3,1)) ||
3000 (csv = !ckstrcmp(notsep,"CSV",3,1)) ||
3001 (tsv = !ckstrcmp(notsep,"TSV",3,1))) {
3002 int i, flag; CHAR c;
3005 if (!all && (csv || tsv)) {
3013 for (i = 1; i < 256; i++) {
3016 while (c = *ss++ && !flag) {
3019 if (!flag) notsepbuf[n++] = c;
3022 notsep = (char *)notsepbuf;
3023 debug(F110,"CKMATCH NOTSEPBUF ALL",notsep,0);
3025 if (*s && csv) { /* For CSV skip leading whitespace */
3026 while (*s == SP || *s == HT)
3030 if (n2 == 0 && csv) n2 = 1; /* CSV implies doublequote grouping */
3031 if (n2 < 0) n2 = 63; /* n2 = grouping mask */
3033 p = ""; /* Pointer to current word */
3034 while (c) { /* Loop through string */
3037 if (!csv && !tsv) { /* fdc 2010-12-30 */
3038 /* In CSV and TSV splitting, backslash is not special */
3039 if (!cquote && c == CMDQ) { /* If CMDQ */
3040 cquote++; /* next one is quoted */
3041 goto nextc; /* go get it */
3045 if (cquote && c == CMDQ) { /* Quoted CMDQ is special */
3046 if (state != ST_BW) { /* because it can still separate */
3048 while (s2 > p) { *s2 = *(s2-1); s2--; }
3053 if (cquote) { /* Other quoted character */
3054 if (state != ST_BW) { /* can't separate or group */
3056 while (s2 > p) { *s2 = *(s2-1); s2--; }
3059 class = CL_DAT; /* so treat it as data */
3062 } else { /* Character is not quoted */
3063 if (!all && c < SP) { /* Get its class */
3064 x = 0; /* x == 0 means "is separator" */
3065 } else if (*sep) { /* Break set given */
3067 while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */
3069 } else { /* Default break set is */
3070 x = ((c >= 'a' && c <= 'z') || /* all but alphanumerics */
3071 (c >= '0' && c <= '9') ||
3072 (c >= 'A' && c <= 'Z') ||
3073 ((unsigned int)c >= hex80 && (unsigned int)c <= hexff)
3076 if (x == 0 && *notsep && c) { /* Include set if given */
3078 while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */
3081 if (c == n3 && grouping && state == ST_BW) { /* Group quote? */
3084 class = x ? CL_DAT : CL_SEP; /* Class = data or separator */
3086 if (grouping) { /* Grouping? */
3087 int j; /* Look for group start */
3088 for (k = 0; k < 6; k++) { /* Group-start char? */
3089 j = 1 << k; /* Get its mask bit value */
3091 if (c == gr_opn[k]) { /* Selected group opener? */
3094 if (c == '"' || c == '\'') { /* These can also */
3095 class |= CL_CLS; /* be closers */
3098 } else if (c == gr_cls[k]) { /* Group closer? */
3106 debug(F000,"cksplit char",s,c);
3107 debug(F101,"cksplit state","",state);
3108 debug(F101,"cksplit class","",class);
3110 switch (state) { /* State switcher... */
3111 case ST_BW: /* BETWEENWORDS */
3112 if (class & CL_OPN) { /* Group opener */
3113 if (gr_lvl == 0 && !gquote) { /* If not in group */
3114 p = s; /* point to beginning of word */
3116 gr_lvl++; /* Push closer on nesting stack */
3117 if (gr_lvl >= NESTMAX)
3119 gr_stk[gr_lvl] = gr_cls[ko];
3121 state = ST_IG; /* Switch to INGROUP state */
3122 } else if (class & CL_DAT) { /* Data character */
3123 gr_lvl = 0; /* Clear group nesting stack */
3126 p = s; /* Point to beginning of word */
3127 if (gquote) { /* Adjust for quote */
3133 state = ST_IW; /* Switch to INWORD state */
3134 } else if (class & CL_QUO) { /* Group quote */
3135 gquote = gr_lvl+1; /* Remember quoted level */
3142 case ST_IW: /* INWORD (but not in a group) */
3143 if (class & CL_SEP) { /* Ends on any kind of separator */
3144 *s = NUL; /* Terminate this word */
3145 if (csv) { /* If comma-separated list */
3146 char * s2 = s; /* discard surrounding spaces */
3147 while (s2 > splitbuf) { /* first backwards... */
3149 if (*s2 == SP || *s2 == HT)
3154 s2 = s+1; /* Then forwards... */
3155 while (*s2 && (*s2 == SP || *s2 == HT))
3159 if (!csv || prevstate != ST_IG) {
3160 wordnum++; /* Count it */
3161 if (splitting || n < 0) { /* Dispose of it appropriately */
3162 if (wordnum > max) { /* Add to array if splitting */
3163 ck_sval.a_size = -2;
3166 /* This inelegant bit corrects an edge condition */
3167 if (csv && !*p && (!c || !*(s+1))) {
3170 setword(wordnum,p,len);
3172 } else if (wordnum == n) { /* Searching for word n */
3179 state = ST_BW; /* Switch to BETWEENWORDS state */
3184 case ST_IG: /* INGROUP */
3185 if (class & CL_CLS) { /* Have group closer? */
3189 while ((*s2 = *(s2+1))) s2++;
3194 if (c == gr_stk[gr_lvl]) { /* Does it match current opener? */
3195 gr_lvl--; /* Yes, pop stack */
3196 if (gr_lvl < 0) /* Don't pop it too much */
3198 if (gr_lvl == 0) { /* If at top of stack */
3202 *s = NUL; /* we have word. */
3203 wordnum++; /* Count and dispose of it. */
3205 if (splitting || n < 0) {
3206 if (wordnum > max) {
3207 ck_sval.a_size = -2;
3210 setword(wordnum,p+1,len);
3211 } else if (wordnum == n) {
3217 state = ST_BW; /* Switch to BETWEENWORDS state */
3220 if (gr_lvl < gquote)
3223 } else if (class & CL_OPN) { /* Have group opener */
3224 gr_lvl++; /* Push on nesting stack */
3225 if (gr_lvl >= NESTMAX) goto xxsplit;
3226 gr_stk[gr_lvl] = gr_cls[ko];
3231 s++; /* Next char */
3236 if (gr_lvl > 0) { /* In case of an unclosed group */
3237 if (splitting || n < 0) { /* make it the last word. */
3238 if (++wordnum > max) {
3239 ck_sval.a_size = -2;
3242 setword(wordnum,p+1,len);
3243 } else if (wordnum == n) { /* Counting from left */
3247 } else if (n < 0 && (wordnum + n > -1)) { /* Counting from right */
3248 char * s = wordarray[wordnum + n + 1];
3250 setword(1,s,strlen(s));
3255 if (!splitting) { /* Fword... */
3256 if (n < 0 && (wordnum + n > -1)) { /* Counting from right */
3257 char * s = wordarray[wordnum + n + 1];
3259 setword(1,s,strlen(s));
3263 setword(1,NULL,0); /* From left... */
3264 ck_sval.a_size = 0; /* but there weren't n words */
3266 } else { /* Succeed otherwise */
3267 ck_sval.a_size = wordnum;
3269 Always put a null element at the end of the array. It does no harm in
3270 the normal case, and it's required if we're making an argv[] array to
3271 pass to execvp(). This element is not included in the count.
3273 if (wordnum < MAXWORDS)
3274 setword(wordnum+1,NULL,0);
3278 for (i = 1; i <= wordnum; i++)
3279 debug(F111,"cksplit result",wordarray[i],i);
3284 xxsplit: /* Error return */
3285 ck_sval.a_size = -2;
3290 ckhexbytetoint() expects a string of two hex characters,
3291 returns the int equivalent or -1 on error.
3295 ckhexbytetoint( char * s )
3297 ckhexbytetoint(s) char * s;
3298 #endif /* CK_ANSIC */
3302 if ((int)strlen(s) != 2) return(-1);
3303 for (i = 0; i < 2; i++) {
3305 if (!c[i]) return(-1);
3306 if (islower(c[i])) c[i] = toupper(c[i]);
3307 if (c[i] >= '0' && c[i] <= '9') {
3309 } else if (c[i] >= 'A' && c[i] <= 'F') {
3315 return(c[0] * 16 + c[1]);
3319 /* End of ckclib.c */