1 char * cklibv = "C-Kermit library, 9.0.049, 30 Dec 2010";
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, 2010,
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 parnam() - Returns parity name string.
58 hhmmss() - Converts seconds to hh:mm:ss string.
59 lset() - Write fixed-length field left-adjusted into a record.
60 rset() - Write fixed-length field right-adjusted into a record.
61 ulongtohex() - Converts an unsigned long to a hex string.
62 hextoulong() - Converts a hex string to an unsigned long.
63 cksplit() - Splits a string into an array of words.
64 ispattern() - Tells if argument string is a pattern.
66 Prototypes are in ckclib.h.
68 Note: This module should not contain any extern declarations.
74 /* Public variables */
76 int dblquo = 1; /* Nonzero if doublequotes can be used for grouping */
79 ccntab[] = { /* Names of ASCII (C0) control characters 0-31 */
80 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL",
81 "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
82 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
83 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
87 c1tab[] = { /* Names of ISO 6429 (C1) control characters 0-32 */
88 "XXX", "XXX", "BPH", "NBH", "IND", "NEL", "SSA", "ESA",
89 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
90 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
91 "SOS", "XXX", "SCI", "CSI", "ST", "OSC", "PM", "APC", "NBS"
95 static char rxdigits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
96 static char rxresult[RXRESULT+1];
98 /* C K S T R N C P Y */
101 Copies a NUL-terminated string into a buffer whose total length is given,
102 ensuring that the result is NUL-terminated even if it has to be truncated.
105 dest = pointer to destination buffer
106 src = pointer to source string
107 len = length of destination buffer (the actual length, not one less).
110 int, The number of bytes copied, 0 or more.
112 NOTE: This is NOT a replacement for strncpy():
113 . strncpy() does not require its source string to be NUL-terminated.
114 . strncpy() does not necessarily NUL-terminate its result.
115 . strncpy() right-pads dest with NULs if it is longer than src.
116 . strncpy() treats the length argument as the number of bytes to copy.
117 . ckstrncpy() treats the length argument as the size of the dest buffer.
118 . ckstrncpy() doesn't dump core if given NULL string pointers.
119 . ckstrncpy() returns a number.
121 Use ckstrncpy() when you want to:
122 . Copy an entire string into a buffer without overrun.
123 . Get the length of the string back.
125 Use strncpy() when you want to:
126 . Copy a piece of a string.
130 ckstrncpy(char * dest, const char * src, int len)
132 ckstrncpy(dest,src,len) char * dest, * src; int len;
133 #endif /* CK_ANSIC */
136 if (len < 1 || !src || !dest) { /* Nothing or nowhere to copy */
137 if (dest) *dest = NUL;
141 for (i = 0; src[i] && (i < len-1); i++) /* Args OK, copy */
146 if (i > len) i = len;
149 #endif /* NOCKSTRNCPY */
153 /* C K S T R N C A T */
156 Appends a NUL-terminated string to a buffer whose total length is given,
157 ensuring that the result is NUL-terminated even if it had to be truncated.
160 dest = pointer to destination buffer containing a null-terminated string
161 src = pointer to null-terminated source string
162 len = length of destination buffer (the actual length, not one less).
165 int, The number of bytes copied, 0 or more.
169 ckstrncat(char * dest, const char * src, int len)
171 ckstrncat(dest,src,len) char * dest, * src; int len;
172 #endif /* CK_ANSIC */
176 register char * s1, * s2;
177 #endif /* NOCKSTRNCPY */
178 if (len < 1 || !src || !dest) { /* Nothing or nowhere to copy */
179 if (dest) *dest = NUL;
184 for (i = 0, j = strlen(dest); src[i] && (i < len-j-1); i++)
190 while (*s1++) j++; /* j = strlen(dest); */
191 s1--; /* (back up over NUL) */
195 while (*s2++) i++; /* i = strlen(src); */
203 strncpy(&dest[j],src,i);
205 j = i; /* This should be a bit faster... */
206 s2 = (char *)src; /* depends on strcpy implementation; */
207 while ((*s1++ = *s2++) && j--) /* at least it shouldn't be slower. */
209 dest[len-1] = NUL; /* In case of early exit. */
212 #endif /* NOCKSTRNCPY */
216 /* C K M A K M S G */
219 Constructs a message from up to 4 pieces with length checking.
220 Result is always NUL terminated. Call with:
221 buf: Pointer to buffer for constructing message.
222 len: Length of buffer.
223 s1-s4: String pointers (can be NULL).
225 0: Nothing was copied.
226 n: (positive number) n bytes copied, all args copied successfully.
227 -n: n bytes were copied, destination buffer not big enough for all.
229 ckmakxmsg() -- accepts 12 string args.
230 ckitoa(), ckltoa(), ckctoa(), ckitox(), etc.
231 Use ckmak[x]msg() plus ck?to?() as a safe replacement for sprintf().
235 ckmakmsg(char * buf, int len, char *s1, char *s2, char *s3, char *s4)
237 ckmakmsg(buf,len,s1,s2,s3,s4) char *buf, *s1, *s2, *s3, *s4; int len;
238 #endif /* CK_ANSIC */
244 if (!buf) return(n); /* No destination */
245 if (len < 1) return(n); /* No size */
247 s = buf; /* Point to destination */
248 a[0] = s1; a[1] = s2; a[2] = s3; a[3] = s4; /* Array of source strings */
249 for (i = 0; i < 4; i++) { /* Loop thru array */
250 p = a[i]; /* Point to this element */
251 if (p) { /* If pointer not null */
252 n = ckstrncpy(s,p,len); /* Copy safely */
253 m += n; /* Accumulate total */
254 if (p[n]) /* Didn't get whole thing? */
255 return(-m); /* return indicating buffer full */
256 len -= n; /* Deduct from space left */
257 s += n; /* Otherwise advance dest pointer */
260 return(m); /* Return total bytes copied */
264 /* C K M A K X M S G */
266 /* Exactly like ckmakmsg(), but accepts 12 string arguments. */
270 ckmakxmsg(char * buf, int len,
271 char *s1, char *s2, char *s3, char *s4, char *s5, char *s6,
272 char *s7, char *s8, char *s9, char *s10, char *s11, char *s12)
274 ckmakxmsg(buf,len,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12)
275 char *buf, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9, *s10, *s11, *s12;
277 #endif /* CK_ANSIC */
284 if (!buf) return(n); /* No destination */
285 if (len < 1) return(n); /* No size */
287 s = buf; /* Point to destination */
288 a[0] = s1; a[1] = s2; a[2] = s3; a[3] = s4; /* Source-string array */
289 a[4] = s5; a[5] = s6; a[6] = s7; a[7] = s8;
290 a[8] = s9; a[9] = s10; a[10] = s11; a[11] = s12;
291 for (i = 0; i < 12; i++) { /* Loop thru array */
292 p = a[i]; /* Point to this element */
293 if (p) { /* If pointer not null */
294 n = ckstrncpy(s,p,len); /* Copy safely */
295 m += n; /* Accumulate total */
296 if (p[n]) /* Didn't get whole thing? */
297 return(-m); /* return indicating buffer full */
298 len -= n; /* Deduct from space left */
299 s += n; /* Otherwise advance dest pointer */
302 return(m); /* Return total bytes copied */
305 /* C H A R T O S T R */
307 /* Converts a character to a string, interpreting controls. */
310 chartostr(x) int x; { /* Call with char x */
311 static char buf[2]; /* Returns string pointer. */
316 if (x > 127 && x < 161)
317 return(c1tab[x - 128]);
321 buf[0] = (unsigned)(x & 0xff);
327 /* Returns the rightmost character of the given null-terminated string */
330 ckrchar(s) char * s; {
331 register CHAR c = '\0', *p;
333 if (!p) p = (CHAR *)""; /* Null pointer == empty string */
335 while (*p) /* Crawl to end of string */
337 return((unsigned)(c & 0xff)); /* Return final character */
340 /* C K S T R C H R */
342 /* Replacement for strchr(), which is not universal. */
344 s = pointer to string to look in.
345 c = character to look for.
347 NULL if c not found in s or upon any kind of error, or:
348 pointer to first occurrence of c in s, searching from left to right.
352 ckstrchr(char * s, char c)
354 ckstrchr(s,c) char *s, c;
355 #endif /* CK_ANSIC */
359 while (*s && *s != c)
361 return((*s == c) ? s : NULL);
364 /* C K S T R R C H R */
366 /* Replacement for strrchr(), which is not universal. */
368 s = pointer to string to look in.
369 c = character to look for.
371 NULL if c not found in s or upon any kind of error, or:
372 pointer to first occurrence of c in s, searching from right to left.
376 ckstrrchr(char * s, char c)
378 ckstrrchr(s,c) char *s, c;
379 #endif /* CK_ANSIC */
393 /* C K S T R P B R K -- Portable replacement for strpbrk() */
395 /* Returns pointer to first char in s1 that is also in s2, or NULL */
398 ckstrpbrk(s1, s2) char * s1, * s2; {
400 if (!s1 || !s2) return(NULL);
401 if (!*s1 || !*s2) return(NULL);
402 while ((c1 = *s1++)) {
404 while ((c2 = *s3++)) {
412 /* C K L O W E R -- Lowercase a string IN PLACE */
414 /* Returns the length of the string */
417 cklower(s) char *s; {
421 if (isupper(*s)) *s = (char) tolower(*s);
427 /* C K U P P E R -- Uppercase a string IN PLACE */
429 /* Returns the length of the string */
432 ckupper(s) char *s; {
436 if (islower(*s)) *s = (char) toupper(*s);
442 /* C K L T O A -- Long to string -- FOR DISCIPLINED USE ONLY */
445 static char numbuf[NUMBUF+32] = { NUL, NUL };
446 static int numbp = 0;
448 ckltoa() and ckitoa() are like atol() and atoi() in the reverse direction,
449 returning a pointer to the string representation of the given number without
450 the caller having to worry about allocating or defining a buffer first.
451 They manage their own internal buffer, so successive calls return different
452 pointers. However, to keep memory consumption from growing without bound,
453 the buffer recycles itself. So after several hundred calls (depending on
454 the size of the numbers), some of the earlier pointers might well find
455 themselves referencing something different. Moral: You can't win in C.
456 Therefore, these routines are intended mainly for generating numeric strings
457 for short-term use, e.g. for passing numbers in string form as parameters to
458 functions. For long-term use, the result must be copied to a safe place.
465 #endif /* CK_ANSIC */
467 char buf[32]; /* Internal working buffer */
469 int k, x, len = 0, sign = 0;
470 if (n < 0L) { /* Sign */
475 for (k = 30; k > 0; k--) { /* Convert number to string */
482 if (sign) buf[--k] = '-'; /* Add sign if necessary */
484 if (len + numbp > NUMBUF)
489 while ((*p++ = *s++)) ; /* Copy */
492 return(q); /* Return pointer */
495 /* C K U L T O A -- Unsigned long to string */
499 ckultoa(unsigned long n)
501 ckultoa(n) unsigned long n;
502 #endif /* CK_ANSIC */
504 char buf[32]; /* Internal working buffer */
508 for (k = 30; k > 0; k--) { /* Convert number to string */
516 if (len + numbp > NUMBUF)
521 while ((*p++ = *s++)) ; /* Copy */
523 return(q); /* Return pointer */
528 ckltox(long n) /* Long int to "0x.." hex string */
531 #endif /* CK_ANSIC */
533 char buf[32]; /* Internal working buffer */
534 char *p, *q, *s, *bp = buf + 2;
538 sprintf(bp, "%lx", n);
541 sprintf(bp, "0%lx", n);
545 if (numbp + k >= NUMBUF)
550 while ((*p++ = *s++)) ; /* Copy */
553 return(q); /* Return pointer */
557 /* C K F S T O A -- File Size (or offset) to string */
559 /* This is just like ckltoa() except for the data type of the argument. */
560 /* It's mainly for printing file sizes without having to know their data */
561 /* type, so we don't have to hardware "%ld" or "%lld" into printf()s. */
562 /* Works for 32 or 64 bits, according to CK_OFF_T definition. */
568 ckfstoa(n) CK_OFF_T n;
569 #endif /* CK_ANSIC */
571 char buf[32]; /* Internal working buffer */
573 int k, x, len = 0, sign = 0;
575 if (n < (CK_OFF_T)0) { /* Sign */
579 buf[31] = NUL; /* 2^63-1 is about 20 decimal digits */
580 for (k = 30; k > 0; k--) { /* Convert number to string */
581 x = n % (CK_OFF_T)10;
584 ckstrncpy(&buf[23],"OVERFLOW",32);
590 n = n / (CK_OFF_T)10;
594 if (sign) buf[--k] = '-'; /* Add sign if necessary */
596 if (len + numbp > NUMBUF)
601 while ((*p++ = *s++)) ; /* Copy */
604 return(q); /* Return pointer */
607 /* C K A T O F S -- String to File Size (or offset) */
609 /* This is the inverse of ckfstoa(), a replacement for atol() that works */
610 /* for either 32-bit or 64-bit arguments, according to CK_OFF_T definition. */
611 /* Like atol(), there is no error indication. */
618 #endif /* CK_ANSIC */
620 CK_OFF_T result = (CK_OFF_T)0;
622 while (*s && (*s == SP || *s == HT)) s++;
628 while (isdigit(*s)) {
629 result = (result * (CK_OFF_T)10) + (CK_OFF_T)(*s - '0');
632 return(minus ? -result : result);
635 /* C K I T O A -- Int to string -- FOR DISCIPLINED USE ONLY */
638 ckitoa(n) int n; { /* See comments with ckltoa(). */
645 char * /* Unsigned int to string */
646 ckuitoa(n) unsigned int n; {
653 ckitox(n) int n; { /* Int to hex */
661 ckctoa(char c) /* Char to string */
667 static int current = 0;
671 buf[current++] = '\0';
672 return((char *)(buf + current - 2));
677 ckctox(CHAR c, int flag) /* Unsigned char to hex */
679 ckctox(c, flag) CHAR c; int flag;
683 static int current = 0;
690 if (!flag && isupper(rxdigits[x]))
691 h = tolower(rxdigits[x]);
695 if (!flag && isupper(rxdigits[x]))
696 h = tolower(rxdigits[x]);
698 buf[current++] = '\0';
699 return((char *)(buf + current - 3));
702 /* C K I N D E X -- C-Kermit's index function */
704 We can't depend on C libraries to have one, so here is our own.
706 s1 - String to look for.
707 s2 - String to look in.
708 t - Offset from right or left of s2, 0 based; -1 for rightmost char in s2.
709 r - 0 for left-to-right search, non-0 for right-to-left.
710 icase 0 for case independence, non-0 if alphabetic case matters.
711 Returns 0 if string not found, otherwise a 1-based result.
712 Also returns 0 on any kind of error, e.g. junk parameters.
715 ckindex(s1,s2,t,r,icase) char *s1, *s2; int t, r, icase; {
716 int len1 = 0, len2 = 0, i, j, x, ot = t; /* ot = original t */
719 if (!s1 || !s2) return(0);
721 while (*s++) len1++; /* length of string to look for */
723 while (*s++) len2++; /* length of string to look in */
725 if (t < 0) t = len2 - 1;
727 j = len2 - len1; /* length difference */
729 if (j < 0 || (r == 0 && t > j)) /* search string is longer */
731 if (r == 0) { /* Index */
732 s = s2 + t; /* Point to beginning of target */
733 for (i = 0; i <= (j - t); i++) { /* Now compare */
734 x = ckstrcmp(s1,s++,len1,icase);
738 } else { /* Reverse Index */
739 i = len2 - len1; /* Where to start looking */
740 if (ot > 0) /* Figure in offset if any */
742 for (j = i; j > -1; j--) {
743 if (!ckstrcmp(s1,&s2[j],len1,icase))
750 /* C K S T R S T R -- Portable replacement for strstr() */
752 /* Returns pointer to first occurrence of s1 in s2, or NULL */
755 ckstrstr(s1, s2) char * s1, * s2; {
757 k = ckindex(s2,s1,0,0,1);
758 return((k < 1) ? NULL : &s1[k-1]);
762 /* B R S T R I P -- Strip enclosing braces from arg string, in place. */
765 Pointer to string that can be poked.
767 Pointer to string without enclosing braces.
768 If original string was not braced, this is the arg pointer;
769 otherwise it is 1 + the arg pointer, with the matching closing
770 brace zero'd out. If the string starts with a brace but does
771 not end with a matching brace, the original pointer to the original
772 string is returned. If the arg pointer is NULL, a pointer to an
773 empty string is returned.
777 /* This is the original version, handling only braces */
780 brstrip(p) char *p; {
784 x = (int)strlen(p) - 1;
794 /* New version handles braces and doublequotes */
795 /* WARNING: this function writes into its argument, it always has. */
798 brstrip(p) char *p; {
800 if (*p == '{' || (*p == '"' && dblquo)) {
802 x = (int)strlen(p) - 1;
804 if ((*p == '{' && p[x] == '}') ||
805 (*p == '"' && p[x] == '"')) {
806 if (x > 0 && p[x-1] != CMDQ) {
819 /* Even newer experimental version -- breaks many things */
822 fnstrip(p) char *p; {
824 extern int cmd_quoting; /* Bad - no externs allowed! */
833 for (j = 0; j < len; j++ ) {
835 (!cmd_quoting || j == 0 || p[j-1] != CMDQ)) {
836 for (n = 1, i = j+1; i < len; i++ ) {
837 if (p[i] == '{' && (!cmd_quoting || p[i-1] != CMDQ))
839 else if (p[i] == '}' && (!cmd_quoting || p[i-1] != CMDQ)) {
841 for (k = j; k < i - 1; k++)
843 for (; i < len; i++ )
852 if (n == 1) { /* Implied right brace at end of field */
853 for (k = j; k < len; k++)
857 } else if (*p == '"') {
861 for (j = 0; j < len; j++) {
863 (!cmd_quoting || j == 0 || p[j-1] != CMDQ)) {
866 for (i = j + 1; i < len; i++) {
867 if (p[i] == '"' && (!cmd_quoting || p[i-1] != CMDQ)) {
870 for (k = j; k < i - 1; k++)
880 if (n == 1) { /* Implied double quote at end of field */
881 for (k = j; k < len; k++ )
892 Not used -- Note: these not only write into their arg, but write past
896 brace(fn) char *fn; {
899 for (p = fn; *p; p++) {
927 /* d q u o t e -- Puts doublequotes around arg in place. */
930 Pointer to buffer and its total length and flag = 0 to use
931 doublequotes, 1 to use braces.
933 Number: length of result.
936 dquote(fn, len, flag) char *fn; int len; int flag; {
937 int spaces = 0, k = 0;
943 for (p = fn; *p; p++) {
954 *p = flag ? '{' : '"';
965 *p = flag ? '}' : '"';
973 /* U N T A B I F Y --- Untabify s1 into s2, assuming tabs every 8 space */
976 untabify(s1,s2,max) char * s1, * s2; int max; {
979 for (i = 0, k = 0; k < x; k++) {
990 for (j = 0; j < z && i < max; j++)
998 /* M A K E L I S T --- Breaks {{s1}{s2}..{sn}} into an array of strings */
1001 s = pointer to string to break up.
1002 list = array of string pointers.
1003 len = number of elements in array.
1004 NOTE: The array must be preinitialized to all NULL pointers.
1005 If any array element is not NULL, it is assumed to have been malloc'd
1006 and is therefore freed. Do NOT call this function with an uninitialized
1007 array, or with an array that has had any static elements assigned to it.
1010 makelist(s,list,len) char * s; char *list[]; int len; {
1011 int i, n, q, bc = 0;
1012 char *p = NULL, *s2 = NULL;
1013 debug(F110,"makelist s",s,0);
1014 if (!s) { /* Check for null or empty string */
1023 if ((s2 = (char *)malloc(n+1))) { /* Safe copy for poking */
1024 strcpy(s2,s); /* (no need for ckstrncpy here) */
1027 s = brstrip(s); /* Strip braces */
1028 n = strlen(s); /* Get length */
1029 if (*s != '{') { /* Outer braces only */
1030 if ((p = (char *)malloc(n+1))) { /* So just one pattern */
1031 strcpy(p,s); /* (no need for ckstrncpy here) */
1039 q = 0; /* Inner ones too */
1040 i = 0; /* so a list of patterns. */
1042 while (*s && i < len) {
1043 if (*s == CMDQ) { /* Quote... */
1049 if (*s == '{' && !q) { /* Opening brace */
1050 if (bc++ == 0) { /* Beginning of a group */
1053 } else { /* It's a brace inside the group */
1058 } else if (*s == '}' && !q) { /* Closing brace */
1059 if (--bc == 0) { /* End of a group */
1061 debug(F111,"makelist element",p,i);
1064 if ((list[i] = (char *)malloc(n+1))) {
1065 ckstrncpy(list[i],p,n+1); /* Note: n+1 */
1068 while (*s == SP) s++;
1072 } else { /* Within a group */
1076 } else { /* Regular character */
1082 if (*p && i < len) { /* Last one */
1085 if ((list[i] = (char *)malloc(n+1))) {
1086 ckstrncpy(list[i],p,n+1);
1087 debug(F111,"makelist last element",p,i);
1090 i++; /* Clear out the rest of the list */
1091 for ( ; i < len; i++) {
1100 M A K E S T R -- Creates a dynamically allocated string.
1102 Makes a new copy of string s and sets pointer p to its address.
1103 Handles degenerate cases, like when buffers overlap or are the same,
1104 one or both arguments are NULL, etc.
1106 The source string is assumed to be NUL-terminated. Therefore it can not
1107 be a UCS-2 string or arbitrary binary data.
1109 The target pointer must be either NULL or else a pointer to a previously
1110 malloc'ed buffer. If not, expect a core dump or segmentation fault.
1112 Note: The caller can tell whether this routine failed as follows:
1115 if (q & !p) { makestr() failed };
1117 Really this routine should have returned a length, but since it doesn't
1118 we set the global variable makestrlen to the length of the result string.
1124 makestr(char **p, const char *s)
1126 makestr(p,s) char **p, *s;
1132 register const char * s2;
1135 #endif /* CK_ANSIC */
1138 if (*p == s) /* The two pointers are the same. */
1139 return; /* Don't do anything. */
1141 if (!s) { /* New definition is null? */
1142 if (*p) /* Free old storage. */
1144 *p = NULL; /* Return null pointer. */
1148 s2 = s; /* Maybe new string will fit */
1152 This is a fairly big win, allowing us to skip the malloc() and free if the
1153 destination string already exists and is not shorter than the source string.
1154 But it doesn't allow for possible overlap of source and destination.
1156 if (*p) { /* into old storage... */
1160 if (!(*p2++ = *s2++))
1167 #endif /* COMMENT */
1172 while (*s2++) x++; /* Get (rest of) length of s. */
1174 if (x >= 0) { /* Get length, even of empty string. */
1175 q = malloc(x+1); /* Get and point to temp storage. */
1177 makestrlen = x; /* Remember length for stats */
1178 s2 = s; /* Point back to beginning of source */
1179 q2 = q; /* Copy dest pointer to increment... */
1180 while ((*q2++ = *s2++)) ; /* Instead of calling strcpy(). */
1182 Note: HP flexelint says that the above loop can result in creation (++) and
1183 access (*) of out-of-bounds pointers. I really don't see it.
1187 else { /* This would be a really bad error */
1188 char tmp[24]; /* So get a good record of it. */
1190 ckstrncpy(tmp,s,20);
1191 strcpy(tmp+20,"...");
1194 strcpy(tmp,s); /* We already checked the length */
1196 debug(F110,"MAKESTR MALLOC FAILURE ",tmp,0);
1200 q = NULL; /* Length of string is zero */
1202 if (*p) /* Now free the original storage. */
1207 /* X M A K E S T R -- Non-destructive makestr() if s is NULL. */
1211 xmakestr(char **p, const char *s)
1213 xmakestr(p,s) char **p, *s;
1216 if (s) makestr(p,s);
1220 /* C K M E M C P Y -- Portable (but slow) memcpy() */
1222 /* Copies n bytes from s to p, allowing for overlap. */
1223 /* For use when real memcpy() not available. */
1226 ckmemcpy(p,s,n) char *p, *s; int n; {
1231 if (!s || !p || n <= 0 || p == s) /* Verify args */
1233 x = p - s; /* Check for overlap */
1236 if (x < n) { /* They overlap */
1238 if (!(p = (char *)malloc(n))) /* So use a temporary buffer */
1241 for (i = 0; i < n; i++) /* Copy n bytes */
1243 if (q) { /* If we used a temporary buffer */
1244 for (i = 0; i < n; i++) /* copy from it to destination */
1246 if (p) free(p); /* and free the temporary buffer */
1249 #endif /* USE_MEMCPY */
1252 /* C K S T R C M P -- String comparison with case-matters selection */
1254 Call with pointers to the two strings, s1 and s2, a length, n,
1255 and c == 0 for caseless comparison, nonzero for case matters.
1256 Call with n == -1 to compare without a length limit.
1257 Compares up to n characters of the two strings and returns:
1261 Note: case handling is only as good as isupper() and tolower().
1264 ckstrcmp(s1,s2,n,c) char *s1, *s2; register int n, c; {
1265 register CHAR t1, t2;
1266 if (n == 0) return(0);
1267 if (!s1) s1 = ""; /* Watch out for null pointers. */
1269 if (!*s1) return(*s2 ? -1 : 0);
1270 if (!*s2) return(1);
1272 t1 = (CHAR) *s1++; /* Get next character from each. */
1274 if (!t1) return(t2 ? -1 : 0);
1276 if (!c) { /* If case doesn't matter */
1277 if (isupper(t1)) t1 = tolower(t1); /* Convert case. */
1278 if (isupper(t2)) t2 = tolower(t2);
1280 if (t1 < t2) return(-1); /* s1 < s2 */
1281 if (t1 > t2) return(1); /* s1 > s2 */
1283 return(0); /* They're equal */
1286 /* C K S T R P R E -- Caseless string prefix comparison */
1288 /* Returns position of the first char in the 2 strings that doesn't match */
1291 ckstrpre(s1,s2) char *s1, *s2; {
1299 if (!t1 || !t2) return(n);
1300 if (isupper(t1)) t1 = tolower(t1);
1301 if (isupper(t2)) t2 = tolower(t2);
1310 /* C K M A T C H -- Match a string against a pattern */
1313 pattern to be matched.
1314 string to look for the pattern in.
1315 icase is 1 if case-sensitive, 0 otherwise.
1318 1 = Match strings starting with '.'
1319 0 = Don't match them (used with UNIX filenames).
1321 1 = File globbing (dirseps are fences);
1322 0 = Dirseps are not fences.
1324 1 = Allow ^ and $ anchors at beginning and end of pattern.
1325 0 = Don't allow them (normal case for filename matching).
1326 Bit 3 (and beyond): Undefined.
1327 Works only with NUL-terminated strings.
1328 Pattern may contain any number of ? and/or *.
1329 If CKREGEX is defined, also [abc], [a-z], and/or {string,string,...}.
1330 (Note: REGEX is a misnomer, see below.)
1333 0 if string does not match pattern,
1334 >= 1, the 1-based position in the string where the match was found.
1337 Find a way to identify the piece of the string that matched the pattern,
1338 as in Snobol "LINE (PAT . RESULT)". This is now partially done by
1339 setting matchpos and matchend (except matchend needs some tuning). But
1340 these are useless unless a copy of the string is kept, or a copy of the
1341 matching part is made. But that would be too costly in performance --
1342 this routine has to be fast because it's used for wildcard expansion.
1345 Patterns are not the same as regular expressions, in which '*' means
1346 0 or more repetitions of the preceding item. For example "a*b" as a
1347 pattern matches any string that starts with 'a' and ends with 'b'; as a
1348 regular expression it matches any string of zero or more a's followed by
1349 one b. Regular expressions are especially useful in matching strings of
1350 (say) digits, or letters, e.g. "[0-9]*" matches any string of digits.
1351 So far, Kermit doesn't do this.
1353 static char * mypat = NULL; /* For rewriting pattern */
1354 static int matchpos = 0;
1356 static int matchdepth = 0;
1357 static int stringpos = 0;
1358 static char * ostring = NULL;
1360 #define MATCHRETURN(x,y) { rc=y; where=x; goto xckmatch; }
1361 static char * lastpat = NULL;
1363 static int xxflag = 0; /* Global bailout flag for ckmatch() */
1366 ispattern(s) char * s; {
1367 int quote = 0, sbflag = 0, sb = 0, cbflag = 0, cb = 0;
1370 if (*s == '^') return(1);
1371 while ((c = *s++)) {
1380 if (c == '*') return(1);
1381 if (c == '?') return(1);
1382 /* Unquoted brackets or braces must match */
1383 if (c == '[') { sbflag++; sb++; continue; }
1384 if (c == ']') { sb--; continue; }
1385 if (c == '{') { cbflag++; cb++; continue; }
1386 if (c == '}') { cb--; continue; }
1387 if (!*s && c == '$') return(1);
1389 return(sbflag || cbflag);
1393 ckmatch(pattern, string, icase, opts) char *pattern,*string; int icase, opts; {
1394 int q = 0, i = 0, k = -1, x, flag = 0;
1395 int rc = 0; /* Return code */
1398 CHAR cp; /* Current character from pattern */
1399 CHAR cs; /* Current character from string */
1400 char * patstart; /* Start of pattern */
1401 int plen, dot, globbing, xstar = 0;
1402 int bronly = 0; /* Whole pattern is {a,b,c,...} */
1404 debug(F111,"CKMATCH ENTRY pat opt",pattern,opts);
1405 debug(F111,"CKMATCH ENTRY str dep",string,matchdepth);
1406 /* debug(F101,"CKMATCH ENTRY icase","",icase); */
1408 globbing = opts & 2;
1410 if (!string) string = "";
1411 if (!pattern) pattern = "";
1413 if (!*pattern) { /* Empty pattern matches anything */
1414 matchdepth++; /* (it wasn't incremented yet) */
1416 } else if (!*string) {
1419 patstart = pattern; /* Remember beginning of pattern */
1421 if (matchdepth == 0) { /* Top-level call? */
1423 stringpos = 0; /* Reset indices etc. */
1428 if (*pattern == '{') /* Entire pattern is {a,b.c} */
1429 bronly = 1; /* Maybe */
1430 dot = (opts & 1) || /* Match leading dot (if file) */
1431 ((opts & 2) == 0) || /* always if not file */
1432 (pattern[0] == '.'); /* or if pattern starts with '.' */
1434 plen = strlen(pattern); /* Length of pattern */
1435 /* This would be used in calculating length of matching segment */
1436 if (plen > 0) /* User's pattern ends with '*' */
1437 if (pattern[plen - 1] == '*')
1439 if (pattern[0] == '*') { /* User's pattern starts with '*' */
1441 debug(F111,"CKMATCH 1",string, matchpos);
1443 if (opts & 4) { /* ^..$ allowed (top level only) */
1444 /* Rewrite pattern to account for ^..$ anchoring... */
1446 if (mypat) free(mypat); /* Get space for "*pattern*" */
1447 mypat = (char *)malloc(plen + 4);
1448 if (mypat) { /* Got space? */
1449 char * s = pattern, * p = mypat; /* Set working pointers */
1450 if (*s == '^') { /* First source char is ^ */
1451 s++; /* so skip past it */
1452 } else if (*s != '*') { /* otherwise */
1453 *p++ = '*'; /* prepend '*' to pattern */
1455 while (*s) { /* Copy rest of pattern */
1456 if (!*(s+1)) { /* Final pattern character? */
1457 if (*s != '$') { /* If it's not '$' */
1458 *p++ = *s; /* Copy it into the pattern */
1459 if (*s++ != '*') /* And if it's also not '*' */
1460 *p++ = '*'; /* append '*'. */
1463 } else /* Not final character */
1464 *p++ = *s++; /* Just copy it */
1466 *p = NUL; /* Terminate the new pattern */
1467 pattern = mypat; /* Make the switch */
1469 debug(F110,"CKMATCH INIT pat",pattern,0);
1472 matchdepth++; /* Now increment call depth */
1475 if (!dot) { /* For UNIX file globbing */
1476 if (*string == '.' && *pattern != '.' && !matchdot) {
1479 *pattern != '{' && *pattern != '['
1482 #endif /* CKREGEX */
1484 debug(F110,"ckmatch skip",string,0);
1492 cp = *pattern; /* Character from pattern */
1493 cs = *string; /* Character from string */
1496 debug(F000,"CKMATCH pat cp",pattern,cp);
1497 debug(F000,"CKMATCH str cs",string,cs);
1498 #endif /* COMMENT */
1500 if (!cs) { /* End of string - done. */
1501 x = (!cp || (cp == '*' && !*(pattern+1))) ? 1 : 0;
1504 matchpos = stringpos;
1505 debug(F111,"CKMATCH A",string, matchpos);
1507 matchend = stringpos;
1508 MATCHRETURN(2,matchpos);
1510 debug(F111,"CKMATCH ZERO d",string, matchpos);
1512 MATCHRETURN(16,matchpos);
1514 if (!icase) { /* If ignoring case */
1515 if (isupper(cp)) /* convert both to lowercase. */
1520 if (q) { /* This character was quoted */
1521 debug(F000,"CKMATCH QUOTED",pattern,cp);
1522 q = 0; /* Turn off quote flag */
1524 if (cs == cp) { /* Compare directly */
1525 if (!matchpos) { /* Matches */
1526 matchpos = stringpos;
1527 debug(F111,"CKMATCH \\ new match",string, matchpos);
1530 } else { /* Doesn't match */
1531 pattern = lastpat; /* Back up the pattern */
1533 debug(F111,"CKMATCH \\ no match",pattern, matchpos);
1539 if (cp == CMDQ && !q) { /* Quote in pattern */
1540 debug(F000,"CKMATCH QUOTE",pattern,cp);
1541 q = 1; /* Set flag */
1542 pattern++; /* Advance to next pattern character */
1543 continue; /* and continue. */
1545 if (cs && cp == '?') { /* '?' matches any char */
1547 matchpos = stringpos;
1548 debug(F111,"CKMATCH D",string, matchpos);
1550 debug(F110,"CKMATCH ? pat",pattern,0);
1551 debug(F110,"CKMATCH ? str",string,0);
1557 } else if (cp == '[') { /* Have bracket */
1558 int q = 0; /* My own private q */
1559 char * psave = NULL; /* and backup pointer */
1560 CHAR clist[256]; /* Character list from brackets */
1563 for (i = 0; i < 256; i++) /* memset() etc not portable */
1565 psave = ++pattern; /* Where pattern starts */
1566 debug(F111,"CKMATCH [] ",pattern-1, matchpos);
1567 for (flag = 0; !flag; pattern++) { /* Loop thru pattern */
1568 c = (CHAR)*pattern; /* Current char */
1569 debug(F000,">>> pattern char","",c);
1570 if (q) { /* Quote within brackets */
1575 if (!icase) /* Case conversion */
1578 switch (c) { /* Handle unquoted character */
1579 case NUL: /* End of string */
1580 MATCHRETURN(4,0); /* No matching ']' so fail */
1581 case CMDQ: /* Next char is quoted */
1582 q = 1; /* Set flag */
1583 continue; /* and continue. */
1584 case '-': /* A range is specified */
1585 c1 = (pattern > psave) ? (CHAR)*(pattern-1) : NUL;
1586 c2 = (CHAR)*(pattern+1); /* IGNORE OUT-OF-BOUNDS WARNING */
1587 if (c2 == ']') c2 = NUL; /* (it can't happen) */
1588 if (c1 == NUL) c1 = c2;
1589 for (c = c1; c <= c2; c++) {
1593 clist[toupper(c)] = 1;
1594 } else if (isupper(c)) {
1595 clist[tolower(c)] = 1;
1600 case ']': /* End of bracketed sequence */
1601 flag = 1; /* Done with FOR loop */
1602 break; /* Compare what we have */
1603 default: /* Just a char */
1604 clist[c] = 1; /* Record it */
1607 clist[toupper(c)] = 1;
1608 } else if (isupper(c)) {
1609 clist[tolower(c)] = 1;
1615 debug(F000,">>> cs","",cs);
1616 debug(F101,">>> clist[cs]","",clist[cs]);
1617 debug(F000,">>> string",string,*string);
1619 if (!clist[(unsigned)cs]) { /* No match? */
1620 if (!*string) { /* This clause 16 Jun 2005 */
1621 MATCHRETURN(5,0); /* Nope, done. */
1624 We need to fail here if the [clist] is not allowed to float.
1625 The [clist] is not allowed to float if it is not preceded
1626 by an asterisk, right? 30 Dec 2005.
1631 string++; /* From here to end added 2005/6/15 */
1633 pattern = lastpat; /* Back up pattern */
1634 k = ckmatch(pattern,string,icase,opts);
1635 if (xxflag) MATCHRETURN(0,0);
1636 if (!matchpos && k > 0)
1637 matchpos = stringpos;
1638 MATCHRETURN(5, (*string) ? matchpos : 0);
1641 matchpos = stringpos;
1642 debug(F111,"CKMATCH [] match",string, matchpos);
1644 string++; /* Yes, advance string pointer */
1646 continue; /* and go on. */
1647 } else if (cp == '{') { /* Braces enclosing list of strings */
1648 char * p, * s, * s2, * buf = NULL;
1651 debug(F111,"CKMATCH {} ",string, matchpos);
1652 for (p = pattern++; *p; p++) {
1653 if (*p == '{') bc++;
1654 if (*p == '}') bc--;
1657 if (bc != 0) { /* Braces don't match */
1658 MATCHRETURN(6,0); /* Fail */
1659 } else { /* Braces do match */
1660 int q = 0, done = 0;
1661 len = *p ? strlen(p+1) : 0; /* Length of rest of pattern */
1664 if (bronly && (matchdepth != 1))
1666 n = p - pattern; /* Size of list in braces */
1667 if ((buf = (char *)malloc(n+1))) { /* Copy so we can poke it */
1670 ckstrncpy(buf,pattern,n+1);
1671 sofar = string - ostring - matchpos + 1;
1672 if (sofar < 0) sofar = 0;
1673 debug(F111,"CKMATCH .. string",string,sofar);
1674 debug(F111,"CKMATCH .. ostring",ostring,sofar);
1676 for (s = s2 = buf; 1; s++) { /* Loop through segments */
1678 if (q) { /* This char is quoted */
1684 if (*s == CMDQ && !q) { /* Quote next char */
1688 if (!*s || *s == ',') { /* End of this segment */
1690 if (!*s) /* If end of buffer */
1691 done = 1; /* then end of last segment */
1692 *s = NUL; /* Overwrite comma with NUL */
1693 debug(F111,"CKMATCH {} segment",s2,done);
1694 tplen = n + len + sofar + 2;
1695 if (!*s2) { /* Empty segment, no advancement */
1697 } else if ((tp = (char *)malloc(tplen))) {
1701 &ostring[matchpos-1] :
1705 ckstrncpy(tp,pp,sofar+1);
1707 ckstrncpy(tp,pp,sofar);
1712 ckstrncpy(&tp[1],pp,sofar+1);
1714 ckstrncpy(&tp[1],pp,sofar);
1716 ckstrncat(tp,s2,tplen); /* Current segment */
1717 ckstrncat(tp,p+1,tplen); /* rest of pattern */
1719 debug(F101,"CKMATCH {} matchpos","",matchpos);
1724 debug(F111,"CKMATCH {} tp",tp,matchpos);
1725 debug(F111,"CKMATCH {} string",
1727 debug(F111,"CKMATCH {} ostring",
1731 /* If segment starts with dot */
1732 /* then set matchdot option.. */
1734 if (*s2 == '.') opts2 |= 1;
1735 debug(F111,"CKMATCH {} recursing",s2,opts2);
1737 (string > ostring) ?
1738 &ostring[savpos-1] : string,
1742 debug(F101,"CKMATCH {} k","",k);
1743 debug(F101,"CKMATCH {} savpos","",savpos);
1748 if (xxflag) MATCHRETURN(0,0);
1752 if (k > 0) { /* If it matched we're done */
1755 } else { /* Malloc failure */
1758 if (k) { /* Successful comparison */
1760 matchpos = stringpos;
1761 debug(F111,"CKMATCH {} match",
1764 string += n-1; /* Advance pointers */
1768 if (done) /* If no more segments */
1769 break; /* break out of segment loop. */
1770 s2 = s+1; /* Otherwise, on to next segment */
1777 #endif /* CKREGEX */
1778 } else if (cp == '*') { /* Pattern char is asterisk */
1780 char * p, * s = NULL; /* meaning match anything */
1782 havestar++; /* The rest can float */
1783 while (*pattern == '*') /* Collapse successive asterisks */
1785 psave = pattern; /* First non-asterisk after asterisk */
1786 lastpat = pattern - 1; /* Ditto, global */
1787 debug(F111,"CKMATCH * ",string,matchpos);
1788 for (n = 0, p = psave; *p; p++,n++) { /* Find next meta char */
1790 if (*p == '?' || *p == '*' || *p == CMDQ
1792 || *p == '[' || *p == '{'
1793 #endif /* CKREGEX */
1802 && (*p == '.' || *p == ']' ||
1803 *p == '<' || *p == '>' ||
1804 *p == ':' || *p == ';')
1811 #endif /* STRATUS */
1812 #endif /* datageneral */
1814 #endif /* UNIXOROSK */
1817 #endif /* GLOBBING */
1820 debug(F111,"CKMATCH * n string",string,n);
1821 debug(F111,"CKMATCH * n pattrn",pattern,n);
1822 debug(F111,"CKMATCH * n p",p,n);
1823 if (n > 0) { /* Literal string to match */
1824 s = (char *)malloc(n+1);
1826 ckstrncpy(s,psave,n+1); /* Copy cuz no poking original */
1828 k = ckindex(s,string,0,0,icase); /* 1-based index() */
1829 debug(F110,"CKMATCH * Index() string",string,0);
1830 debug(F110,"CKMATCH * Index() pattrn",s,0);
1831 debug(F101,"CKMATCH * Index() result","",k);
1832 } else { /* String is right-anchored */
1833 k = ckindex(s,string,-1,1,icase); /* rindex() */
1834 debug(F111,"CKMATCH * Rindex()",string,k);
1835 debug(F110,"CKMATCH * Rindex() pattrn",s,0);
1836 debug(F101,"CKMATCH * Rindex() result","",k);
1842 debug(F111,"CKMATCH * stringpos matchpos",
1843 ckitoa(stringpos), matchpos);
1845 matchpos = string - ostring + k;
1846 debug(F111,"CKMATCH * new match ", string, matchpos);
1848 string += k + n - 1;
1849 stringpos += k + n - 1;
1851 debug(F111,"CKMATCH * new string", string, stringpos);
1852 debug(F110,"CKMATCH * new pattrn", pattern, 0);
1855 } else if (!*p) { /* Asterisk at end matches the rest */
1856 if (!globbing) { /* (if not filename globbing) */
1858 matchpos = stringpos;
1859 debug(F111,"CKMATCH *$ ",string, matchpos);
1861 matchend = stringpos;
1862 MATCHRETURN(9,matchpos);
1866 if (globbing /* Filespec so don't cross fields */
1868 && *string == '/' || *string == '\\' ||
1875 && (*string == '.' || *string == ']' ||
1876 *string == '<' || *string == '>' ||
1877 *string == ':' || *string == ';')
1885 && *string == '/' /* (catch-all) */
1886 #endif /* STRATUS */
1887 #endif /* datageneral */
1889 #endif /* UNIXOROSK */
1892 matchend = stringpos;
1896 matchpos = stringpos;
1897 debug(F111,"CKMATCH *$ match",string, matchpos);
1902 #endif /* GLOBBING */
1904 matchpos = stringpos;
1905 debug(F111,"CKMATCH ** match",string, matchpos);
1907 matchend = stringpos;
1908 MATCHRETURN(11,matchpos);
1910 } else { /* A meta char follows asterisk */
1912 MATCHRETURN(17, matchpos = 0);
1914 /* This is more elegant but it doesn't work. */
1918 k = ckmatch(p,string,icase,opts);
1920 while (*string && ((k = ckmatch(p,string,icase,opts)) < 1)) {
1921 if (xxflag) MATCHRETURN(0,0);
1925 if (!*string && k < 1) {
1927 Definitely no match so we set a global flag to inibit further backing up
1928 and retrying by previous incarnations, since they don't see that the string
1929 and/or pattern, which are on the stack, have been exhausted at this level.
1932 debug(F111,"CKMATCH DEFINITELY NO MATCH",p,k);
1935 #endif /* COMMENT */
1936 debug(F111,"CKMATCH *<meta> k",string, k);
1937 if (!matchpos && k > 0) {
1938 matchpos = stringpos;
1939 debug(F111,"CKMATCH *<meta> matchpos",string, matchpos);
1941 MATCHRETURN(12, (*string) ? matchpos : 0);
1943 } else if (cs == cp) {
1948 matchpos = stringpos;
1949 debug(F111,"CKMATCH cs=cp",string, matchpos);
1965 ckmakxmsg(msgbuf,256,
1973 " string=",string,NULL,NULL
1975 debug(F110,msgbuf,string,0);
1983 /* I S F L O A T -- Verify that arg represents a floating-point number */
1986 Portable replacement for atof(), strtod(), scanf(), etc.
1989 s = pointer to string
1990 flag == 0 means entire string must be a (floating-pointing) number.
1991 flag != 0 means to terminate scan on first character that is not legal.
1994 1 if result is a legal number;
1995 2 if result has a fractional part;
1996 0 if not or if string empty.
1999 Sets global floatval to floating-point value if successful.
2001 Number need not contain a decimal point -- integer is subcase of float.
2002 Scientific notation not supported.
2004 CKFLOAT floatval = 0.0; /* For returning value */
2007 isfloat(s,flag) char *s; int flag; {
2011 CKFLOAT d = 0.0, f = 0.0;
2016 while (isspace(*s)) s++;
2018 if (*s == '-') { /* Handle optional sign */
2021 } else if (*s == '+')
2023 while ((c = *s++)) { /* Handle numeric part */
2025 case 0: /* Mantissa... */
2027 f = f * 10.0 + (CKFLOAT)(c - '0');
2029 } else if (c == '.') {
2034 if (flag) /* Not digit or period */
2035 goto done; /* break if flag != 0 */
2036 return(0); /* otherwise fail. */
2037 case 1: /* Fraction... */
2040 f += (CKFLOAT)(c - '0') / d;
2044 if (flag) /* Illegal character */
2045 goto done; /* Break */
2046 return(0); /* or fail, depending on flag */
2050 if (sign) f = 0.0 - f; /* Apply sign to result */
2051 floatval = f; /* Set result */
2052 return(d ? 2 : 1); /* Succeed */
2054 #endif /* CKFLOAT */
2056 /* Sorting routines... */
2058 /* S H _ S O R T -- Shell sort -- sorts string array s in place. */
2061 Highly defensive and relatively quick.
2062 Uses shell sort algorithm.
2065 s = pointer to array of strings.
2066 p = pointer to a second array to sort in parallel s, or NULL for none.
2067 n = number of elements in s.
2068 k = position of key.
2069 r = ascending lexical order if zero, reverse lexical order if nonzero.
2070 c = 0 for case independence, 1 for case matters, 2 for numeric.
2072 If k is past the right end of a string, the string is considered empty
2073 for comparison purposes.
2076 To sort a piece of an array, call with s pointing to the first element
2077 and n the number of elements to sort.
2080 None. Always succeeds, unless any of s[0]..s[n-1] are bad pointers,
2081 in which case memory violations are possible, but C offers no defense
2082 against this, so no way to gracefully return an error code.
2085 sh_sort(s,p,n,k,r,c) char **s, **p; int n, k, r, c; {
2087 char *t, *t1, *t2, *u = NULL;
2092 #endif /* CKFLOAT */
2094 if (!s) return; /* Nothing to sort? */
2095 if (n < 2) return; /* Not enough elements to sort? */
2096 if (k < 0) k = 0; /* Key */
2098 m = n; /* Initial group size is whole array */
2100 m = m / 2; /* Divide group size in half */
2101 if (m < 1) /* Small as can be, so done */
2103 for (j = 0; j < n-m; j++) { /* Sort each group */
2104 t = t2 = s[j+m]; /* Compare this one... */
2105 if (!t) /* But if it's NULL */
2106 t2 = ""; /* make it the empty string */
2107 if (p) /* Handle parallel array, if any */
2110 if ((int)strlen(t2) < k) /* If key too big */
2111 t2 = ""; /* make key the empty string */
2112 else /* Key is in string */
2113 t2 = t + k; /* so point to key position */
2115 for (i = j; i >= 0; i -= m) { /* Loop thru comparands s[i..]*/
2117 if (!t1) /* Same deal */
2120 if ((int)strlen(t1) < k)
2125 if (c == 2) { /* Numeric comparison */
2130 if (isfloat(t1,1)) {
2155 #endif /* CKFLOAT */
2157 x = ckstrcmp(t1,t2,-1,c); /* Compare */
2159 if (r == 0 && x < 0)
2161 if (r != 0 && x > 0)
2164 if (p) p[i+m] = p[i];
2173 /* C K R A D I X -- Radix converter */
2176 s: a number in string format.
2177 in: int, specifying the radix of s, 2-36.
2178 out: int, specifying the radix to convert to, 2-36.
2180 NULL on error (illegal radix, illegal number, etc.).
2181 "-1" on overflow (number too big for unsigned long).
2182 Otherwise: Pointer to result.
2185 ckradix(s,in,out) char * s; int in, out; {
2186 char c, *r = rxresult;
2189 unsigned long zz = 0L;
2193 To get 64 bits on 32-bit hardware we use off_t, but there
2194 is no unsigned version of off_t, so we lose the ability to
2197 CK_OFF_T zz = (CK_OFF_T)0;
2198 CK_OFF_T z = (CK_OFF_T)0;
2199 #endif /* COMMENT */
2201 if (in < 2 || in > 36) /* Verify legal input radix */
2203 if (out < 2 || out > 36) /* and output radix. */
2205 if (*s == '+') { /* Get sign if any */
2207 } else if (*s == '-') {
2211 while (*s == SP || *s == '0') /* Trim leading blanks or 0's */
2214 For detecting overflow, we use a signed copy of the unsigned long
2215 accumulator. If it goes negative, we know we'll overflow NEXT time
2218 for (; *s; s++) { /* Convert from input radix to */
2219 c = *s; /* unsigned long */
2220 if (islower(c)) c = toupper(c);
2221 if (c >= '0' && c <= '9')
2223 else if (c >= 'A' && c <= 'Z')
2227 if (d >= in) /* Check for illegal digit */
2230 if (z < 0L) /* Clever(?) overflow detector */
2234 if (!zz) return("0");
2235 r = &rxresult[RXRESULT]; /* Convert from unsigned long */
2236 *r-- = NUL; /* to output radix. */
2237 while (zz > 0 && r > rxresult) {
2238 d = zz % (unsigned)out;
2240 zz = zz / (unsigned)out;
2242 if (minus) *r-- = '-'; /* Replace original sign */
2243 return((char *)(r+1));
2247 /* Base-64 conversion routines */
2249 static char b64[] = { /* Encoding vector */
2251 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
2253 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S',
2254 'T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l',
2255 'm','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4',
2256 '5','6','7','8','9','+','/','=','\0'
2259 static int b64tbl[] = { /* Decoding vector */
2260 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
2261 -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
2262 -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
2263 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
2264 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
2265 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
2266 -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2267 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
2268 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2269 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2270 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2271 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2272 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2273 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2274 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2275 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
2279 B 8 T O B 6 4 -- Converts 8-bit data to Base64 encoding.
2282 s = Pointer to 8-bit data;
2283 n = Number of source bytes to encode (SEE NOTE).
2284 If it's a null-terminated string, you can use -1 here.
2285 out = Address of output buffer.
2286 len = Length of output buffer (should > 4/3 longer than input).
2289 >= 0 if OK, number of bytes placed in output buffer,
2290 with the subsequent byte set to NUL if space permits.
2291 -1 on error (output buffer space exhausted).
2294 If this function is to be called repeatedly, e.g. to encode a data
2295 stream a chunk at a time, the source length must be a multiple of 3
2296 in all calls but the final one to avoid the generation of extraneous
2297 pad characters that would throw the decoder out of sync. When encoding
2298 only a single string, this is not a consideration. No internal state
2299 is kept, so there is no reset function.
2302 b8tob64(s,n,out,len) char * s,* out; int n, len; {
2303 int b3, b4, i, x = 0;
2306 if (n < 0) n = strlen(s);
2308 for (i = 0; i < n; i += 3,x += 4) { /* Loop through source bytes */
2310 t = (unsigned)((unsigned)((unsigned int)s[i] & 0xff) << 8);
2311 if (n - 1 > i) { /* Do we have another after this? */
2312 t |= (unsigned)(s[i+1] & 0xff); /* Yes, OR it in */
2313 b3 = 1; /* And remember */
2315 t <<= 8; /* Move over */
2316 if (n - 2 > i) { /* Another one after that? */
2317 t |= (unsigned)(s[i+2] & 0xff); /* Yes, OR it in */
2318 b4 = 1; /* and remember */
2320 if (x + 4 > len) /* Check output space */
2322 out[x+3] = b64[b4 ? (t & 0x3f) : 64]; /* 64 = code for '=' */
2324 out[x+2] = b64[b3 ? (t & 0x3f) : 64];
2326 out[x+1] = b64[t & 0x3f];
2328 out[x] = b64[t & 0x3f];
2330 if (x < len) out[x] = NUL; /* Null-terminate the string */
2336 B 6 4 T O B 8 -- Converts Base64 string to 8-bit data.
2339 s = pointer to Base64 string (whitespace ignored).
2340 n = length of string, or -1 if null terminated, or 0 to reset.
2341 out = address of output buffer.
2342 len = length of output buffer.
2345 >= 0 if OK, number of bytes placed in output buffer,
2346 with the subsequent byte set to NUL if space permits.
2348 -1 = output buffer too small for input.
2349 -2 = input contains illegal characters.
2350 -3 = internal coding error.
2353 Can be called repeatedly to decode a Base64 stream, one chunk at a
2354 time. However, if it is to be called for multiple streams in
2355 succession, its internal state must be reset at the beginning of
2359 b64tob8(s,n,out,len) char * s,* out; int n, len; { /* Decode */
2360 static int bits = 0;
2361 static unsigned int r = 0;
2365 if (n == 0) { /* Reset state */
2370 x = (n < 0) ? strlen(s) : n; /* Source length */
2372 n = ((x + 3) / 4) * 3; /* Compute destination length */
2373 if (x > 0 && s[x-1] == '=') n--; /* Account for padding */
2374 if (x > 1 && s[x-2] == '=') n--;
2375 if (n > len) /* Destination not big enough */
2376 return(-1); /* Fail */
2378 for (i = 0; i < x; i++) { /* Loop thru source */
2379 c = (CHAR)s[i]; /* Next char */
2380 t = b64tbl[c]; /* Code for this char */
2381 if (t == -2) { /* Whitespace or Ctrl */
2384 } else if (t == -1) { /* Illegal code */
2385 return(-2); /* Fail. */
2386 } else if (t > 63 || t < 0) /* Illegal value */
2387 return(-3); /* fail. */
2388 bits += 6; /* Count bits */
2389 r <<= 6; /* Make space */
2390 r |= (unsigned) t; /* OR in new code */
2391 if (bits >= 8) { /* Have a byte yet? */
2392 bits -= 8; /* Output it */
2393 c = (unsigned) ((r >> bits) & 0xff);
2397 if (k < len) out[k] = NUL; /* Null-terminate in case it's */
2398 return(k); /* a text string */
2402 /* C H K N U M -- See if argument string is an integer */
2404 /* Returns 1 if OK, zero if not OK */
2405 /* If OK, string should be acceptable to atoi() or atol() or ckatofs() */
2406 /* Allows leading space, sign */
2409 chknum(s) char *s; { /* Check Numeric String */
2410 int x = 0; /* Flag for past leading space */
2411 int y = 0; /* Flag for digit seen */
2413 debug(F110,"chknum",s,0);
2416 while ((c = *s++)) { /* For each character in the string */
2418 case SP: /* Allow leading spaces */
2420 if (x == 0) continue;
2422 case '+': /* Allow leading sign */
2427 default: /* After that, only decimal digits */
2428 if (c >= '0' && c <= '9') {
2438 /* R D I G I T S -- Verify that all characters in arg ARE DIGITS */
2440 /* Returns 1 if so, 0 if not or if string is empty */
2443 rdigits(s) char *s; {
2446 if (!isdigit(*s)) return(0);
2452 /* P A R N A M -- Return parity name */
2459 #endif /* CK_ANSIC */
2462 case 'e': return("even");
2463 case 'o': return("odd");
2464 case 'm': return("mark");
2465 case 's': return("space");
2466 case 0: return("none");
2467 default: return("invalid");
2471 char * /* Convert seconds to hh:mm:ss */
2476 #endif /* CK_ANSIC */
2478 static char buf[10];
2480 h = x / 3600L; /* Hours */
2482 m = x / 60L; /* Minutes */
2483 s = x % 60L; /* Seconds */
2485 sprintf(buf,"%02ld:%02ld:%02ld",h,m,s);
2488 return((char *)buf);
2491 /* L S E T -- Set s into p, right padding to length n with char c; */
2493 s is a NUL-terminated string.
2494 If length(s) > n, only n bytes are moved.
2495 The result is NOT NUL terminated unless c == NUL and length(s) < n.
2496 The intended of this routine is for filling in fixed-length record fields.
2499 lset(p,s,n,c) char *s; char *p; int n; int c; {
2503 #endif /* USE_MEMCPY */
2512 for (i = 0; i < x; i++)
2516 #endif /* USE_MEMCPY */
2519 /* R S E T -- Right-adjust s in p, left padding to length n with char c */
2522 rset(p,s,n,c) char *s; char *p; int n; int c; {
2526 #endif /* USE_MEMCPY */
2534 for (i = 0; i < (n - x); i++)
2538 #endif /* USE_MEMCPY */
2541 /* U L O N G T O H E X -- Unsigned long to hex */
2544 Converts unsigned long arg to hex and returns string pointer to
2545 rightmost n hex digits left padded with 0's. Allows for longs
2546 up to 64 bits. Returns pointer to result.
2550 ulongtohex( unsigned long z, int n )
2552 ulongtohex(z,n) unsigned long z; int n;
2553 #endif /* CK_ANSIC */
2555 static char hexbuf[17];
2556 int i = 16, x, k = 0;
2559 k = 2 * (sizeof(long));
2560 for (i = 0; i < n; i++) {
2561 if (i > k || z == 0) {
2566 hexbuf[15-i] = x + ((x < 10) ? '0' : 0x37);
2569 return((char *)(&hexbuf[16-i]));
2572 /* H E X T O U L O N G -- Hex string to unsigned long */
2575 Converts n chars from s from hex to unsigned long.
2577 0L or positive, good result (0L is returned if arg is NULL or empty).
2578 -1L on error: non-hex arg or overflow.
2581 hextoulong(s,n) char *s; int n; {
2583 unsigned long result = 0L;
2598 if ((d == '0' || d == ' ')) {
2606 if (d >= '0' && d <= '9') {
2608 } else if (d >= 'A' && d <= 'F') {
2613 if (++count > (sizeof(long) * 2))
2615 result = (result << 4) | (d & 0x0f);
2621 c k s p l i t -- Splits a string into words, or:
2622 extracts a given word from a string.
2624 Allows for grouping.
2625 Operates on a copy of the string; does not alter the original string.
2626 All strings NUL-terminated.
2630 1 = split, 0 = word.
2631 n1 = desired word number if fc == 0.
2633 s2 = break string (NULL to accept default = all non-alphanum).
2634 s3 = include string (NULL to accept default = all alphanum).
2635 n2 = grouping mask (OR desired ones together):
2636 1 = doublequotes, 2 = braces, 4 = apostrophes,
2637 8 = parens, 16 = brackets, 32 = angle brackets,
2638 -1 = 63 = all of these.
2639 n3 = group quote character, ASCII value, used and tested only for
2640 LISP quote, e.g. (a 'b c '(d e f)).
2641 n4 = 0 to collapse adjacent separators;
2642 nonzero not to collapse them.
2645 Pointer to struct stringarray, with size:
2646 -1 = memory allocation error.
2647 -2 = too many words in string.
2648 n = number of words (0 or more).
2651 wordarray = array of pointers to n words (n == 1 if fc == 0), 1-based.
2652 Each pointer is to a malloc'd string. This array is recycled upon each
2653 call; if you need to keep the strings, make copies of them. This routine
2654 must have control of allocation and deallocation.
2656 If a given character is included in the include list, it is not treated
2657 as a separator or as a grouping character.
2659 Groups may be nested only if they are formed with braces, parens, or
2660 brackets, but not with quotes or apostrophes since ASCII quotes have no
2661 intrinsic handedness. Group-start and end characters are treated as
2662 separators even in the absence of other separators, so a string such as
2663 "a{b}c" results in three words, not one.
2665 Sample call to split a string into an array:
2666 struct stringarray * q;
2667 q = cksplit(1,0,s1,s2,s3,-1,0);
2668 q->a_size = size (>=0) or failure code (<0)
2669 q->a_head = pointer to array (elements 0 thru q->asize - 1).
2671 Sample call to extract word n from a string:
2672 struct stringarray * q;
2673 q = cksplit(0,n,s1,s2,s3,-1,0);
2674 q->a_size = size (1) or failure code (<0)
2675 q->a_head = pointer to array (element 1 is the desired word).
2680 #define ST_BW 0 /* Between Words */
2681 #define ST_IW 1 /* In Word */
2682 #define ST_IG 2 /* Start Group */
2684 /* Character Classes (bitmap) */
2686 #define CL_SEP 1 /* Separator */
2687 #define CL_OPN 2 /* Group Open */
2688 #define CL_CLS 4 /* Group Close */
2689 #define CL_DAT 8 /* Data */
2690 #define CL_QUO 16 /* Group quote */
2694 #define MAXWORDS 4096 /* Max number of words */
2695 #endif /* MAXWORDS */
2697 #define NESTMAX 64 /* Maximum nesting level */
2698 #endif /* NESTMAX */
2701 #define MAXWORDS 128 /* Max number of words */
2702 #endif /* MAXWORDS */
2704 #define NESTMAX 16 /* Maximum nesting level */
2705 #endif /* NESTMAX */
2706 #endif /* BIGBUFOK */
2708 /* static */ char ** wordarray = NULL; /* Result array of word pointers */
2710 static struct stringarray ck_sval = { /* Return value structure */
2711 NULL, /* Pointer to array */
2714 static int * wordsize = NULL;
2717 setword(n,s,len) int n, len; char * s; {
2723 if (!wordarray) { /* Allocate result array (only once) */
2724 if (!(wordarray = (char **)malloc((MAXWORDS+1) * sizeof(char *))))
2726 if (!(wordsize = (int *)malloc((MAXWORDS+1) * sizeof(int))))
2728 for (i = 0; i <= MAXWORDS; i++) { /* Initialize result array */
2729 wordarray[i] = NULL;
2733 if (wordsize[n] < len /* || !wordarray[n] */ ) {
2734 k = (len < 16) ? 16 : len + (len / 4);
2735 wordarray[n] = (char *) malloc(k+1);
2736 wordsize[n] = (wordarray[n]) ? k : 0;
2739 while ((*p++ = *s++) && k-- > 0) ;
2741 } else if (len > 0) {
2742 k = wordsize[n]; /* (In case len arg is a lie) */
2744 while ((*p++ = *s++) && k-- > 0) {
2746 if ((*(s-1) == CMDQ) && *s != CMDQ) {
2750 #endif /* COMMENT */
2752 } else if (wordarray[n]) {
2753 wordarray[n][0] = NUL;
2757 static char * splitbuf = NULL;
2758 static int nsplitbuf = 0;
2760 /* n4 = 1 to NOT collapse adjacent separators */
2763 struct stringarray *
2764 cksplit(fc,n1,s1,s2,s3,n2,n3,n4) int fc,n1,n2,n3,n4; char *s1, *s2, *s3; {
2765 int splitting = 0; /* What I was asked to do */
2766 int i, k, ko = 0, n, x, max = MAXWORDS; /* Workers */
2767 char * s = NULL, * ss, * p; /* Workers */
2768 char * sep = ""; /* Default break set */
2769 char * notsep = ""; /* Default include set */
2770 int grouping = 0; /* Grouping option */
2771 char * gr_opn = "\"{'([<"; /* Group open brackets */
2772 char * gr_cls = "\"}')]>"; /* Group close brackets */
2773 int gr_stk[NESTMAX]; /* Nesting bracket stack */
2774 int gr_lvl = 0; /* Nesting level */
2775 int wordnum = 0; /* Current word number */
2776 CHAR c = 'A'; /* Current char (dummy start value) */
2777 int class = 0; /* Current character class */
2778 int state = ST_BW; /* Current FSA state */
2779 int len = 0; /* Length of current word */
2780 int slen = 0; /* Length of s1 */
2781 int gquote = 0; /* Quoted group */
2782 int cquote = 0; /* Quoted character */
2783 int collapse = 1; /* Collapse adjacent separators */
2784 int all = 0; /* s3 == ALL */
2785 int csv = 0; /* s3 == CSV */
2786 int tsv = 0; /* s3 == TSV */
2789 unsigned int hex80 = 128;
2790 unsigned int hexff = 255;
2791 CHAR notsepbuf[256];
2793 notsepbuf[0] = NUL; /* Keep set for "ALL" */
2794 if (n4) collapse = 0; /* Don't collapse */
2796 for (i = 0; i < NESTMAX; i++) /* Initialize nesting stack */
2800 ck_sval.a_head = wordarray; /* Initialize return value struct */
2803 if (!s1) s1 = ""; /* s1 = source string */
2804 if (!*s1) { /* If none, nothing to do */
2807 splitting = fc; /* Our job */
2808 if (splitting) { /* If splitting n = word count */
2809 n = 0; /* Initialize it */
2810 } else { /* Otherwise */
2812 if (n1 < 1) { /* If 0 (or less) */
2813 ck_sval.a_size = 0; /* nothing to do. */
2817 if (n1 == 0) { /* If 0 */
2818 ck_sval.a_size = 0; /* nothing to do. */
2821 #endif /* COMMENT */
2822 n = n1; /* n = desired word number. */
2824 slen = 0; /* Get length of s1 */
2825 debug(F111,"cksplit",s1,n);
2830 while (*p++) slen++; /* Make pokeable copy of s1 */
2831 if (!splitbuf || slen > nsplitbuf) { /* Allocate buffer if needed */
2835 xx = (slen < 255) ? 255 : xx + (xx / 4);
2836 debug(F101,"cksplit splitbuf","",xx);
2837 splitbuf = (char *)malloc(xx+1);
2838 if (!splitbuf) { /* Memory allocation failure... */
2839 ck_sval.a_size = -1;
2842 nsplitbuf = xx; /* Remember size of buffer */
2846 The idea is to just copy the string into splitbuf (if it exists). This
2847 gives us both the copy and the length so we only need to grovel through the
2848 string once in most cases. Only when splitbuf doesn't exist or is too short
2849 do we re-malloc(), which should be very infrequent so who cares if we have
2850 to go through the string again in that case.
2854 if (splitbuf) { /* Make pokeable copy of s1 */
2855 while ((*s++ = *p++) && (slen++ < nsplitbuf)) /* Try to copy */
2858 if (!splitbuf || slen >= nsplitbuf) { /* Need to do more... */
2860 if (splitbuf) /* Free previous buf if any */
2862 while (*p++) slen++; /* Get (rest of) length */
2863 xx = (slen < 255) ? 255 : slen + (slen / 4); /* Size of new buffer */
2864 splitbuf = (char *)malloc(xx+1); /* Allocate it */
2865 if (!splitbuf) { /* Memory allocation failure... */
2866 ck_sval.a_size = -1;
2869 nsplitbuf = xx; /* Remember (new) buffer size */
2872 while ((*s++ = *p++)) ;
2875 #endif /* COMMENT */
2877 sep = s2; /* s2 = break set */
2879 notsep = s3; /* s3 = include set */
2882 } else if ((all = !ckstrcmp(notsep,"ALL",3,1)) ||
2883 (csv = !ckstrcmp(notsep,"CSV",3,1)) ||
2884 (tsv = !ckstrcmp(notsep,"TSV",3,1))) {
2885 int i, flag; CHAR c;
2888 if (!all && (csv || tsv)) {
2896 for (i = 1; i < 256; i++) {
2899 while (c = *ss++ && !flag) {
2902 if (!flag) notsepbuf[n++] = c;
2905 notsep = (char *)notsepbuf;
2906 debug(F110,"CKMATCH NOTSEPBUF ALL",notsep,0);
2908 if (*s && csv) { /* For CSV skip leading whitespace */
2909 while (*s == SP || *s == HT)
2913 if (n2 == 0 && csv) n2 = 1; /* CSV implies doublequote grouping */
2914 if (n2 < 0) n2 = 63; /* n2 = grouping mask */
2916 p = ""; /* Pointer to current word */
2917 while (c) { /* Loop through string */
2920 if (!csv && !tsv) { /* fdc 2010-12-30 */
2921 /* In CSV and TSV splitting, backslash is not special */
2922 if (!cquote && c == CMDQ) { /* If CMDQ */
2923 cquote++; /* next one is quoted */
2924 goto nextc; /* go get it */
2928 if (cquote && c == CMDQ) { /* Quoted CMDQ is special */
2929 if (state != ST_BW) { /* because it can still separate */
2931 while (s2 > p) { *s2 = *(s2-1); s2--; }
2936 if (cquote) { /* Other quoted character */
2937 if (state != ST_BW) { /* can't separate or group */
2939 while (s2 > p) { *s2 = *(s2-1); s2--; }
2942 class = CL_DAT; /* so treat it as data */
2945 } else { /* Character is not quoted */
2946 if (!all && c < SP) { /* Get its class */
2947 x = 0; /* x == 0 means "is separator" */
2948 } else if (*sep) { /* Break set given */
2950 while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */
2952 } else { /* Default break set is */
2953 x = ((c >= 'a' && c <= 'z') || /* all but alphanumerics */
2954 (c >= '0' && c <= '9') ||
2955 (c >= 'A' && c <= 'Z') ||
2956 ((unsigned int)c >= hex80 && (unsigned int)c <= hexff)
2959 if (x == 0 && *notsep && c) { /* Include set if given */
2961 while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */
2964 if (c == n3 && grouping && state == ST_BW) { /* Group quote? */
2967 class = x ? CL_DAT : CL_SEP; /* Class = data or separator */
2969 if (grouping) { /* Grouping? */
2970 int j; /* Look for group start */
2971 for (k = 0; k < 6; k++) { /* Group-start char? */
2972 j = 1 << k; /* Get its mask bit value */
2974 if (c == gr_opn[k]) { /* Selected group opener? */
2977 if (c == '"' || c == '\'') { /* These can also */
2978 class |= CL_CLS; /* be closers */
2981 } else if (c == gr_cls[k]) { /* Group closer? */
2989 debug(F000,"cksplit char",s,c);
2990 debug(F101,"cksplit state","",state);
2991 debug(F101,"cksplit class","",class);
2993 switch (state) { /* State switcher... */
2994 case ST_BW: /* BETWEENWORDS */
2995 if (class & CL_OPN) { /* Group opener */
2996 if (gr_lvl == 0 && !gquote) { /* If not in group */
2997 p = s; /* point to beginning of word */
2999 gr_lvl++; /* Push closer on nesting stack */
3000 if (gr_lvl >= NESTMAX)
3002 gr_stk[gr_lvl] = gr_cls[ko];
3004 state = ST_IG; /* Switch to INGROUP state */
3005 } else if (class & CL_DAT) { /* Data character */
3006 gr_lvl = 0; /* Clear group nesting stack */
3009 p = s; /* Point to beginning of word */
3010 if (gquote) { /* Adjust for quote */
3016 state = ST_IW; /* Switch to INWORD state */
3017 } else if (class & CL_QUO) { /* Group quote */
3018 gquote = gr_lvl+1; /* Remember quoted level */
3025 case ST_IW: /* INWORD (but not in a group) */
3026 if (class & CL_SEP) { /* Ends on any kind of separator */
3027 *s = NUL; /* Terminate this word */
3028 if (csv) { /* If comma-separated list */
3029 char * s2 = s; /* discard surrounding spaces */
3030 while (s2 > splitbuf) { /* first backwards... */
3032 if (*s2 == SP || *s2 == HT)
3037 s2 = s+1; /* Then forwards... */
3038 while (*s2 && (*s2 == SP || *s2 == HT))
3042 if (!csv || prevstate != ST_IG) {
3043 wordnum++; /* Count it */
3044 if (splitting || n < 0) { /* Dispose of it appropriately */
3045 if (wordnum > max) { /* Add to array if splitting */
3046 ck_sval.a_size = -2;
3049 /* This inelegant bit corrects an edge condition */
3050 if (csv && !*p && (!c || !*(s+1))) {
3053 setword(wordnum,p,len);
3055 } else if (wordnum == n) { /* Searching for word n */
3062 state = ST_BW; /* Switch to BETWEENWORDS state */
3067 case ST_IG: /* INGROUP */
3068 if (class & CL_CLS) { /* Have group closer? */
3072 while ((*s2 = *(s2+1))) s2++;
3077 if (c == gr_stk[gr_lvl]) { /* Does it match current opener? */
3078 gr_lvl--; /* Yes, pop stack */
3079 if (gr_lvl < 0) /* Don't pop it too much */
3081 if (gr_lvl == 0) { /* If at top of stack */
3085 *s = NUL; /* we have word. */
3086 wordnum++; /* Count and dispose of it. */
3088 if (splitting || n < 0) {
3089 if (wordnum > max) {
3090 ck_sval.a_size = -2;
3093 setword(wordnum,p+1,len);
3094 } else if (wordnum == n) {
3100 state = ST_BW; /* Switch to BETWEENWORDS state */
3103 if (gr_lvl < gquote)
3106 } else if (class & CL_OPN) { /* Have group opener */
3107 gr_lvl++; /* Push on nesting stack */
3108 if (gr_lvl >= NESTMAX) goto xxsplit;
3109 gr_stk[gr_lvl] = gr_cls[ko];
3114 s++; /* Next char */
3119 if (gr_lvl > 0) { /* In case of an unclosed group */
3120 if (splitting || n < 0) { /* make it the last word. */
3121 if (++wordnum > max) {
3122 ck_sval.a_size = -2;
3125 setword(wordnum,p+1,len);
3126 } else if (wordnum == n) { /* Counting from left */
3130 } else if (n < 0 && (wordnum + n > -1)) { /* Counting from right */
3131 char * s = wordarray[wordnum + n + 1];
3133 setword(1,s,strlen(s));
3138 if (!splitting) { /* Fword... */
3139 if (n < 0 && (wordnum + n > -1)) { /* Counting from right */
3140 char * s = wordarray[wordnum + n + 1];
3142 setword(1,s,strlen(s));
3146 setword(1,NULL,0); /* From left... */
3147 ck_sval.a_size = 0; /* but there weren't n words */
3149 } else { /* Succeed otherwise */
3150 ck_sval.a_size = wordnum;
3152 Always put a null element at the end of the array. It does no harm in
3153 the normal case, and it's required if we're making an argv[] array to
3154 pass to execvp(). This element is not included in the count.
3156 if (wordnum < MAXWORDS)
3157 setword(wordnum+1,NULL,0);
3161 for (i = 1; i <= wordnum; i++)
3162 debug(F111,"cksplit result",wordarray[i],i);
3167 xxsplit: /* Error return */
3168 ck_sval.a_size = -2;
3173 ckhexbytetoint() expects a string of two hex characters,
3174 returns the int equivalent or -1 on error.
3178 ckhexbytetoint( char * s )
3180 ckhexbytetoint(s) char * s;
3181 #endif /* CK_ANSIC */
3185 if ((int)strlen(s) != 2) return(-1);
3186 for (i = 0; i < 2; i++) {
3188 if (!c[i]) return(-1);
3189 if (islower(c[i])) c[i] = toupper(c[i]);
3190 if (c[i] >= '0' && c[i] <= '9') {
3192 } else if (c[i] >= 'A' && c[i] <= 'F') {
3198 return(c[0] * 16 + c[1]);
3202 /* End of ckclib.c */