1 char * cklibv = "C-Kermit library, 9.0.059, 14 Sep 2021";
2 
3 #define CKCLIB_C
4 
5 /* C K C L I B . C  --  C-Kermit Library routines. */
6 
7 /*
8   Author: Frank da Cruz <fdc@columbia.edu>,
9   Columbia University Academic Information Systems, New York City.
10 
11   Copyright (C) 1999, 2021,
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.
15 */
16 
17 /*
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.
22 
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.
66 
67   Prototypes are in ckclib.h.
68 
69   Note: This module should not contain any extern declarations.
70 */
71 #include "ckcsym.h"
72 #include "ckcdeb.h"
73 #include "ckcasc.h"
74 
75 /* Public variables */
76 
77 int dblquo = 1; /* Nonzero if doublequotes can be used for grouping */
78 
79 char *
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"
85 };
86 
87 char *
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"
93 };
94 
95 #define RXRESULT 127
96 static char rxdigits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
97 static char rxresult[RXRESULT+1];
98 
99 /*  C K S T R N C P Y */
100 
101 /*
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.
104 
105   Call with:
106     dest = pointer to destination buffer
107     src  = pointer to source string
108     len  = length of destination buffer (the actual length, not one less).
109 
110   Returns:
111     int, The number of bytes copied, 0 or more.
112 
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() 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.
120 
121   Use ckstrncpy() when you want to:
122    . Copy a NUL-terminated string into a buffer without overrun, truncating
123       it if necessary to fit in the buffer, and null-terminating it.
124    . Get the length of the string back.
125 
126   Use strncpy() when you want to:
127    . Copy a piece of a string.
128 */
129 int
130 #ifdef CK_ANSIC
ckstrncpy(char * dest,const char * src,int len)131 ckstrncpy(char * dest, const char * src, int len)
132 #else
133 ckstrncpy(dest,src,len) char * dest, * src; int len;
134 #endif /* CK_ANSIC */
135 {
136     int i;
137     if (len < 1 || !src || !dest) {	/* Nothing or nowhere to copy */
138 	if (dest) *dest = NUL;
139 	return(0);
140     }
141 #ifndef NOCKSTRNCPY
142     for (i = 0; src[i] && (i < len-1); i++) /* Args OK, copy */
143       dest[i] = src[i];
144     dest[i] = NUL;
145 #else
146     i = strlen(src);
147     if (i > len) i = len;
148     strncpy(dest,src,i);
149     dest[len] = NUL;
150 #endif /* NOCKSTRNCPY */
151     return(i);
152 }
153 
154 /*  C K S T R N C A T */
155 
156 /*
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.
159 
160   Call with:
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).
164 
165   Returns:
166     int, The number of bytes copied, 0 or more.
167 */
168 int
169 #ifdef CK_ANSIC
ckstrncat(char * dest,const char * src,int len)170 ckstrncat(char * dest, const char * src, int len)
171 #else
172 ckstrncat(dest,src,len) char * dest, * src; int len;
173 #endif /* CK_ANSIC */
174 {
175     register int i, j;
176 #ifdef NOCKSTRNCPY
177     register char * s1, * s2;
178 #endif /* NOCKSTRNCPY */
179     if (len < 1 || !src || !dest) {	/* Nothing or nowhere to copy */
180 	if (dest) *dest = NUL;
181 	return(0);
182     }
183 #ifndef NOCKSTRNCPY
184     /* Args OK, copy */
185     for (i = 0, j = strlen(dest); src[i] && (i < len-j-1); i++)
186       dest[i+j] = src[i];
187     dest[i+j] = NUL;
188 #else
189     j = 0;
190     s1 = dest;
191     while (*s1++) j++;			/* j = strlen(dest); */
192     s1--;				/* (back up over NUL) */
193 
194     i = 0;
195     s2 = (char *)src;
196     while (*s2++) i++;			/* i = strlen(src); */
197 
198     if (i > (len-j))
199       i = len - j;
200     if (i <= 0)
201       return(0);
202 
203 #ifdef COMMENT
204     strncpy(&dest[j],src,i);
205 #else
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.  */
209       ;
210     dest[len-1] = NUL;			/* In case of early exit. */
211 #endif /* COMMENT */
212 
213 #endif /* NOCKSTRNCPY */
214     return(i);
215 }
216 
217 /*  C K M A K M S G  */
218 
219 /*
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).
225    Returns:
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.
229    Also see:
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().
233 */
234 int
235 #ifdef CK_ANSIC
ckmakmsg(char * buf,int len,char * s1,char * s2,char * s3,char * s4)236 ckmakmsg(char * buf, int len, char *s1, char *s2, char *s3, char *s4)
237 #else /* CK_ANSIC */
238 ckmakmsg(buf,len,s1,s2,s3,s4) char *buf, *s1, *s2, *s3, *s4; int len;
239 #endif /* CK_ANSIC */
240 {
241     int i, n = 0, m = 0;
242     char *s;
243     char *p, *a[4];
244 
245     if (!buf) return(n);		/* No destination */
246     if (len < 1) return(n);		/* No size */
247 
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 */
259 	}
260     }
261     return(m);				/* Return total bytes copied */
262 }
263 
264 
265 /*  C K M A K X M S G  */
266 
267 /*  Exactly like ckmakmsg(), but accepts 12 string arguments. */
268 
269 int
270 #ifdef CK_ANSIC
ckmakxmsg(char * buf,int len,char * s1,char * s2,char * s3,char * s4,char * s5,char * s6,char * s7,char * s8,char * s9,char * s10,char * s11,char * s12)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)
274 #else /* CK_ANSIC */
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;
277   int len;
278 #endif /* CK_ANSIC */
279 {
280     int i, n = 0, m = 0;
281     char *s;
282     char *p, *a[12];
283 
284 
285     if (!buf) return(n);		/* No destination */
286     if (len < 1) return(n);		/* No size */
287 
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 */
301 	}
302     }
303     return(m);				/* Return total bytes copied */
304 }
305 
306 /*  C H A R T O S T R  */
307 
308 /*  Converts a character to a string, interpreting controls.  */
309 
310 char *
chartostr(x)311 chartostr(x) int x; {			/* Call with char x */
312     static char buf[2];			/* Returns string pointer. */
313     if (x < 32)
314       return(ccntab[x]);
315     if (x == 127)
316       return("DEL");
317     if (x > 127 && x < 161)
318       return(c1tab[x - 128]);
319     if (x == 0xAD)
320       return("SHY");
321     buf[1] = NUL;
322     buf[0] = (unsigned)(x & 0xff);
323     return((char *)buf);
324 }
325 
326 /*  C K R C H A R */
327 
328 /*  Returns the rightmost character of the given null-terminated string */
329 
330 int
ckrchar(s)331 ckrchar(s) char * s; {
332     register CHAR c = '\0', *p;
333     p = (CHAR *)s;
334     if (!p) p = (CHAR *)"";		/* Null pointer == empty string */
335     if (!*p) return(0);
336     while (*p)				/* Crawl to end of string */
337       c = *p++;
338     return((unsigned)(c & 0xff));	/* Return final character */
339 }
340 
341 /*  C K S T R C H R  */
342 
343 /*  Replacement for strchr(), which is not universal.  */
344 /*  Call with:
345      s = pointer to string to look in.
346      c = character to look for.
347     Returns:
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.
350 */
351 char *
352 #ifdef CK_ANSIC
ckstrchr(char * s,char c)353 ckstrchr(char * s, char c)
354 #else
355 ckstrchr(s,c) char *s, c;
356 #endif /* CK_ANSIC */
357 /* ckstrchr */ {
358     if (!s)
359       return(NULL);
360     while (*s && *s != c)
361       s++;
362     return((*s == c) ? s : NULL);
363 }
364 
365 /*  C K S T R R C H R  */
366 
367 /*  Replacement for strrchr(), which is not universal.  */
368 /*  Call with:
369      s = pointer to string to look in.
370      c = character to look for.
371     Returns:
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.
374 */
375 char *
376 #ifdef CK_ANSIC
ckstrrchr(char * s,char c)377 ckstrrchr(char * s, char c)
378 #else
379 ckstrrchr(s,c) char *s, c;
380 #endif /* CK_ANSIC */
381 /* ckstrchr */ {
382     char * s2 = NULL;
383     if (!s)
384       return(NULL);
385     while (*s) {
386 	if (*s == c)
387 	  s2 = s;
388 	s++;
389     }
390     return(s2);
391 }
392 
393 
394 /* C K S T R P B R K  --  Portable replacement for strpbrk()  */
395 
396 /* Returns pointer to first char in s1 that is also in s2, or NULL */
397 
398 char *
ckstrpbrk(s1,s2)399 ckstrpbrk(s1, s2) char * s1, * s2; {
400     char c1, c2, * s3;
401     if (!s1 || !s2) return(NULL);
402     if (!*s1 || !*s2) return(NULL);
403     while ((c1 = *s1++)) {
404 	s3 = s2;
405 	while ((c2 = *s3++)) {
406 	    if (c2 == c1)
407 	      return(s1-1);
408 	}
409     }
410     return(NULL);
411 }
412 
413 /*  C K L O W E R  --  Lowercase a string IN PLACE */
414 
415 /* Returns the length of the string */
416 
417 int
cklower(s)418 cklower(s) char *s; {
419     int n = 0;
420     if (!s) return(0);
421     while (*s) {
422         if (isupper(*s)) *s = (char) tolower(*s);
423         s++, n++;
424     }
425     return(n);
426 }
427 
428 /*  C K U P P E R  --  Uppercase a string IN PLACE */
429 
430 /* Returns the length of the string */
431 
432 int
ckupper(s)433 ckupper(s) char *s; {
434     int n = 0;
435     if (!s) return(0);
436     while (*s) {
437         if (islower(*s)) *s = (char) toupper(*s);
438         s++, n++;
439     }
440     return(n);
441 }
442 
443 /*  C K L T O A  --  Long to string  --  FOR DISCIPLINED USE ONLY  */
444 
445 #define NUMBUF 1024
446 static char numbuf[NUMBUF+32] = { NUL, NUL };
447 static int numbp = 0;
448 /*
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.
460 */
461 char *
462 #ifdef CK_ANSIC
ckltoa(long n)463 ckltoa(long n)
464 #else
465 ckltoa(n) long n;
466 #endif /* CK_ANSIC */
467 /* ckltoa */ {
468     char buf[32];			/* Internal working buffer */
469     char * p, * s, * q;
470     int k, x, len = 0, sign = 0;
471     if (n < 0L) {			/* Sign */
472 	n = 0L - n;
473 	sign = 1;
474     }
475     buf[31] = NUL;
476     for (k = 30; k > 0; k--) {		/* Convert number to string */
477 	x = n % 10L;
478 	buf[k] = x + '0';
479 	n = n / 10L;
480 	if (!n)
481 	  break;
482     }
483     if (sign) buf[--k] = '-';		/* Add sign if necessary */
484     len = 31 - k;
485     if (len + numbp > NUMBUF)
486       numbp = 0;
487     p = numbuf + numbp;
488     q = p;
489     s = buf + k;
490     while ((*p++ = *s++)) ;		/* Copy */
491     *p++ = NUL;
492     numbp += len+1;
493     return(q);				/* Return pointer */
494 }
495 
496 /*  C K U L T O A  --  Unsigned long to string  */
497 
498 char *
499 #ifdef CK_ANSIC
ckultoa(unsigned long n)500 ckultoa(unsigned long n)
501 #else
502 ckultoa(n) unsigned long n;
503 #endif /* CK_ANSIC */
504 /* ckultoa */ {
505     char buf[32];			/* Internal working buffer */
506     char * p, * s, * q;
507     int k, x, len = 0;
508     buf[31] = NUL;
509     for (k = 30; k > 0; k--) {		/* Convert number to string */
510 	x = n % 10L;
511 	buf[k] = x + '0';
512 	n = n / 10L;
513 	if (!n)
514 	  break;
515     }
516     len = 31 - k;
517     if (len + numbp > NUMBUF)
518       numbp = 0;
519     p = numbuf + numbp;
520     q = p;
521     s = buf + k;
522     while ((*p++ = *s++)) ;		/* Copy */
523     numbp += len+1;
524     return(q);				/* Return pointer */
525 }
526 
527 char *
528 #ifdef CK_ANSIC
ckltox(long n)529 ckltox(long n)				/* Long int to "0x.." hex string */
530 #else
531 ckltox(n) long n;
532 #endif /* CK_ANSIC */
533 /* ckltox */ {
534     char buf[32];			/* Internal working buffer */
535     char *p, *q, *s, *bp = buf + 2;
536     int k;
537     buf[0] = '0';
538     buf[1] = 'x';
539     sprintf(bp, "%lx", n);
540     k = strlen(bp);
541     if (k&1) {
542 	sprintf(bp, "0%lx", n);
543 	k++;
544     }
545     k += 2;				/* "0x" */
546     if (numbp + k >= NUMBUF)
547       numbp = 0;
548     p = numbuf + numbp;
549     q = p;
550     s = buf;
551     while ((*p++ = *s++)) ;		/* Copy */
552     *p++ = NUL;
553     numbp += k+1;
554     return(q);				/* Return pointer */
555 }
556 
557 
558 /*  C K F S T O A  --  File Size (or offset) to string  */
559 
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. */
564 
565 char *
566 #ifdef CK_ANSIC
ckfstoa(CK_OFF_T n)567 ckfstoa(CK_OFF_T n)
568 #else
569 ckfstoa(n) CK_OFF_T n;
570 #endif /* CK_ANSIC */
571 /* ckfstoa */ {
572     char buf[32];			/* Internal working buffer */
573     char * p, * s, * q;
574     int k, x, len = 0, sign = 0;
575 
576     if (n < (CK_OFF_T)0) {		/* Sign */
577 	n = (CK_OFF_T)0 - n;
578 	sign = 1;
579     }
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;
583 	if (x < 0) {
584 	    /* x += 10; */
585 	    ckstrncpy(&buf[23],"OVERFLOW",32);
586 	    sign = 0;
587 	    k = 23;
588 	    break;
589 	}
590 	buf[k] = x + '0';
591 	n = n / (CK_OFF_T)10;
592 	if (!n)
593 	  break;
594     }
595     if (sign) buf[--k] = '-';		/* Add sign if necessary */
596     len = 31 - k;
597     if (len + numbp > NUMBUF)
598       numbp = 0;
599     p = numbuf + numbp;
600     q = p;
601     s = buf + k;
602     while ((*p++ = *s++)) ;		/* Copy */
603     *p++ = NUL;
604     numbp += len+1;
605     return(q);				/* Return pointer */
606 }
607 
608 /*  C K A T O F S  --  String to File Size (or offset) */
609 
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. */
613 
614 CK_OFF_T
615 #ifdef CK_ANSIC
ckatofs(char * s)616 ckatofs(char * s)
617 #else
618 ckatofs(s) char * s;
619 #endif /* CK_ANSIC */
620 /* ckatofs */ {
621     CK_OFF_T result = (CK_OFF_T)0;
622     int minus = 0;
623     while (*s && (*s == SP || *s == HT)) s++;
624     if (*s == '+') s++;
625     if (*s == '-') {
626 	minus = 1;
627 	s++;
628     }
629     while (isdigit(*s)) {
630 	result = (result * (CK_OFF_T)10) + (CK_OFF_T)(*s - '0');
631 	s++;
632     }
633     return(minus ? -result : result);
634 }
635 
636 /*  C K I T O A  --  Int to string  -- FOR DISCIPLINED USE ONLY  */
637 
638 char *
ckitoa(n)639 ckitoa(n) int n; {			/* See comments with ckltoa(). */
640     long nn;
641     nn = n;
642     return(ckltoa(nn));
643 }
644 
645 
646 char *					/* Unsigned int to string */
ckuitoa(n)647 ckuitoa(n) unsigned int n; {
648     unsigned long nn;
649     nn = n;
650     return(ckultoa(nn));
651 }
652 
653 char *
ckitox(n)654 ckitox(n) int n; {			/* Int to hex */
655     long nn;
656     nn = n;
657     return(ckltox(nn));
658 }
659 
660 char *
661 #ifdef CK_ANSIC
ckctoa(char c)662 ckctoa(char c)				/* Char to string */
663 #else
664 ckctoa(c) char c;
665 #endif
666 /* ckctoa */ {
667     static char buf[32];
668     static int current = 0;
669     if (current >= 30)
670       current = 0;
671     buf[current++] = c;
672     buf[current++] = '\0';
673     return((char *)(buf + current - 2));
674 }
675 
676 char *
677 #ifdef CK_ANSIC
ckctox(CHAR c,int flag)678 ckctox(CHAR c, int flag)		/* Unsigned char to hex */
679 #else
680 ckctox(c, flag) CHAR c; int flag;
681 #endif
682 /* ckctox */ {
683     static char buf[48];
684     static int current = 0;
685     int x;
686     char h;
687     if (current > 45)
688       current = 0;
689     x = (c >> 4) & 0x0f;
690     h = rxdigits[x];
691     if (!flag && isupper(rxdigits[x]))
692       h = tolower(rxdigits[x]);
693     buf[current++] = h;
694     x = c & 0x0f;
695     h = rxdigits[x];
696     if (!flag && isupper(rxdigits[x]))
697       h = tolower(rxdigits[x]);
698     buf[current++] = h;
699     buf[current++] = '\0';
700     return((char *)(buf + current - 3));
701 }
702 
703 /*  C K I N D E X  --  C-Kermit's index function  */
704 /*
705   We can't depend on C libraries to have one, so here is our own.
706   Call with:
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.
714 */
715 int
ckindex(s1,s2,t,r,icase)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 */
718     char * s;
719 
720     if (!s1 || !s2) return(0);
721     s = s1;
722     while (*s++) len1++;		/* length of string to look for */
723     s = s2;
724     while (*s++) len2++;		/* length of string to look in */
725     s = s2;
726     if (t < 0) t = len2 - 1;
727 
728     j = len2 - len1;			/* length difference */
729 
730     if (j < 0 || (r == 0 && t > j))	/* search string is longer */
731       return(0);
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);
736 	    if (!x)
737 	      return(i+1+t);
738 	    s++;
739 	}
740     } else {				/* Reverse Index */
741         i = len2 - len1;		/* Where to start looking */
742         if (ot > 0)			/* Figure in offset if any */
743 	  i -= t;
744 	for (j = i; j > -1; j--) {
745 	    if (!ckstrcmp(s1,&s2[j],len1,icase))
746 	      return(j+1);
747 	}
748     }
749     return(0);
750 }
751 
752 /*  C K S T R S T R  --  Portable replacement for strstr()  */
753 
754 /*  Returns pointer to first occurrence of s1 in s2, or NULL */
755 
756 char *
ckstrstr(s1,s2)757 ckstrstr(s1, s2) char * s1, * s2; {
758     int k;
759     k = ckindex(s2,s1,0,0,1);
760     return((k < 1) ? NULL : &s1[k-1]);
761 }
762 
763 
764 /*  B R S T R I P  --  Strip enclosing braces from arg string, in place. */
765 /*
766   Call with:
767     Pointer to string that can be poked.
768   Returns:
769     Pointer to string without enclosing braces.
770     If original string was not braced, this is the arg pointer;
771     otherwise it is 1 + the arg pointer, with the matching closing
772     brace zero'd out.  If the string starts with a brace but does
773     not end with a matching brace, the original pointer to the original
774     string is returned.  If the arg pointer is NULL, a pointer to an
775     empty string is returned.
776 */
777 #ifdef COMMENT
778 
779 /* This is the original version, handling only braces */
780 
781 char *
brstrip(p)782 brstrip(p) char *p; {
783     if (!p) return("");
784     if (*p == '{') {
785 	int x;
786 	x = (int)strlen(p) - 1;
787 	if (p[x] == '}') {
788 	    p[x] = NUL;
789 	    p++;
790 	}
791     }
792     return(p);
793 }
794 
795 #else
796 /* New version handles braces and doublequotes */
797 /* WARNING: this function writes into its argument, it always has. */
798 
799 char *
brstrip(p)800 brstrip(p) char *p; {
801     if (!p) return("");
802     if (*p == '{' || (*p == '"' && dblquo)) {
803 	int x;
804 	x = (int)strlen(p) - 1;
805 	if (x > 0) {
806 	    if ((*p == '{' && p[x] == '}') ||
807 		(*p == '"' && p[x] == '"')) {
808 		if (x > 0 && p[x-1] != CMDQ) {
809 		    p[x] = NUL;
810 		    p++;
811 		}
812 	    }
813 	}
814     }
815     return(p);
816 }
817 #endif /* COMMENT */
818 
819 #ifdef COMMENT
820 
821 /* Even newer experimental version -- breaks many things */
822 
823 char *
fnstrip(p)824 fnstrip(p) char *p; {
825     int i, j, k, n, len;
826     extern int cmd_quoting;		/* Bad - no externs allowed! */
827 
828     if (!p)
829       return("");
830 
831     if (*p == '{') {
832         len = strlen(p);
833         n = 0;
834 
835         for (j = 0; j < len; j++ ) {
836             if (p[j] == '{' &&
837 		(!cmd_quoting || j == 0 || p[j-1] != CMDQ)) {
838                 for (n = 1, i = j+1; i < len; i++ ) {
839                     if (p[i] == '{' && (!cmd_quoting || p[i-1] != CMDQ))
840 		      n++;
841                     else if (p[i] == '}' && (!cmd_quoting || p[i-1] != CMDQ)) {
842                         if (--n == 0) {
843                             for (k = j; k < i - 1; k++)
844 			      p[k] = p[k+1];
845                             for (; i < len; i++ )
846 			      p[i-1] = p[i+1];
847                             len -= 2;
848                             j = i - 1;
849                         }
850                     }
851                 }
852             }
853         }
854         if (n == 1) { /* Implied right brace at end of field */
855             for (k = j; k < len; k++)
856 	      p[k] = p[k+1];
857             len -= 1;
858         }
859     } else if (*p == '"') {
860         len = strlen(p);
861         n = 0;
862 
863         for (j = 0; j < len; j++) {
864             if (p[j] == '"' &&
865 		(!cmd_quoting || j == 0 || p[j-1] != CMDQ)) {
866                 n++;
867 
868                 for (i = j + 1; i < len; i++) {
869                     if (p[i] == '"' && (!cmd_quoting || p[i-1] != CMDQ)) {
870                         n--;
871 
872                         for (k = j; k < i - 1; k++)
873 			  p[k] = p[k+1];
874                         for (; i < len; i++)
875 			  p[i-1] = p[i+1];
876                         len -= 2;
877                         j = i - 1;
878                     }
879                 }
880             }
881         }
882         if (n == 1) { /* Implied double quote at end of field */
883             for (k = j; k < len; k++ )
884 	      p[k] = p[k+1];
885             len -= 1;
886         }
887     }
888     return(p);
889 }
890 #endif /* COMMENT */
891 
892 #ifdef COMMENT
893 /*
894   Not used -- Note: these not only write into their arg, but write past
895   past the end.
896 */
897 char *
brace(fn)898 brace(fn) char *fn; {
899     int spaces = 0;
900     char * p, ch, ch2;
901     for (p = fn; *p; p++) {
902 	if (*p == SP) {
903 	    spaces = 1;
904 	    break;
905 	}
906     }
907     if (spaces) {
908         p = fn;
909         ch = *p;
910         *p = '{';
911         p++;
912 
913         while (*p) {
914             ch2 = *p;
915             *p = ch;
916             ch = ch2;
917             p++;
918         }
919         *p = ch;
920         p++;
921         *p = '}';
922         p++;
923         *p = '\0';
924     }
925     return(fn);
926 }
927 #endif /* COMMENT */
928 
929 /* d q u o t e  --  Puts doublequotes around arg in place. */
930 /*
931    Call with:
932      Pointer to buffer and its total length and flag = 0 to use
933      doublequotes, 1 to use braces.
934    Returns:
935      Number: length of result.
936 */
937 int
dquote(fn,len,flag)938 dquote(fn, len, flag) char *fn; int len; int flag; {
939     int spaces = 0, k = 0;
940     char * p, ch, ch2;
941     if (!fn)
942       return(0);
943 
944     k = strlen(fn);
945     for (p = fn; *p; p++) {
946 	if (*p == SP) {
947             spaces = 1;
948             break;
949         }
950     }
951     if (spaces) {
952 	if (k + 2 >= len)
953 	  return(k);
954         p = fn;
955         ch = *p;
956         *p = flag ? '{' : '"';
957         p++;
958 
959         while (*p) {
960             ch2 = *p;
961             *p = ch;
962             ch = ch2;
963             p++;
964         }
965         *p = ch;
966         p++;
967         *p = flag ? '}' : '"';
968         p++;
969         *p = '\0';
970     }
971     return(k+2);
972 }
973 
974 
975 /*  U N T A B I F Y  ---  Untabify s1 into s2, assuming tabs every 8 space */
976 
977 int
untabify(s1,s2,max)978 untabify(s1,s2,max) char * s1, * s2; int max; {
979     int i, j, k, x, z;
980     x = strlen(s1);
981     for (i = 0, k = 0; k < x; k++) {
982 	if (s1[k] != '\t') {
983 	    if (i >= max-1) {
984 		s2[max-1] = '\0';
985 		return(-1);
986 	    }
987 	    s2[i++] = s1[k];
988 	    continue;
989 	}
990 	z = 8 - i%8;
991 	if (z == 0) z = 8;
992 	for (j = 0; j < z && i < max; j++)
993 	  s2[i++] = ' ';
994     }
995     s2[i] = '\0';
996     return(0);
997 }
998 
999 
1000 /*  M A K E L I S T  ---  Breaks {{s1}{s2}..{sn}} into an array of strings */
1001 /*
1002   Call with:
1003     s    = pointer to string to break up.
1004     list = array of string pointers.
1005     len  = number of elements in array.
1006   NOTE: The array must be preinitialized to all NULL pointers.
1007   If any array element is not NULL, it is assumed to have been malloc'd
1008   and is therefore freed.  Do NOT call this function with an uninitialized
1009   array, or with an array that has had any static elements assigned to it.
1010 */
1011 VOID
makelist(s,list,len)1012 makelist(s,list,len) char * s; char *list[]; int len; {
1013     int i, n, q, bc = 0;
1014     char *p = NULL, *s2 = NULL;
1015     debug(F110,"makelist s",s,0);
1016     if (!s) {				/* Check for null or empty string */
1017 	list[0] = NULL;
1018 	return;
1019     }
1020     n = strlen(s);
1021     if (n == 0) {
1022 	list[0] = NULL;
1023 	return;
1024     }
1025     if ((s2 = (char *)malloc(n+1))) {	/* Safe copy for poking */
1026 	strcpy(s2,s);			/* (no need for ckstrncpy here) */
1027 	s = s2;
1028     }
1029     s = brstrip(s);			/* Strip braces */
1030     n = strlen(s);			/* Get length */
1031     if (*s != '{') {			/* Outer braces only */
1032 	if ((p = (char *)malloc(n+1))) { /* So just one pattern */
1033 	    strcpy(p,s);		/* (no need for ckstrncpy here) */
1034 	    if (list[0])
1035 	      free(list[0]);
1036 	    list[0] = p;
1037 	}
1038 	if (s2) free(s2);
1039 	return;
1040     }
1041     q = 0;				/* Inner ones too */
1042     i = 0;				/* so a list of patterns. */
1043     n = 0;
1044     while (*s && i < len) {
1045 	if (*s == CMDQ) {		/* Quote... */
1046 	    q = 1;
1047 	    s++;
1048 	    n++;
1049 	    continue;
1050 	}
1051 	if (*s == '{' && !q) {		/* Opening brace */
1052 	    if (bc++ == 0) {		/* Beginning of a group */
1053 		p = ++s;
1054 		n = 0;
1055 	    } else {			/* It's a brace inside the group */
1056 		n++;
1057 		s++;
1058 	    }
1059 	    continue;
1060 	} else if (*s == '}' && !q) {	/* Closing brace */
1061 	    if (--bc == 0) {		/* End of a group */
1062 		*s++ = NUL;
1063 		debug(F111,"makelist element",p,i);
1064 		if (list[i])
1065 		  free(list[i]);
1066 		if ((list[i] = (char *)malloc(n+1))) {
1067 		    ckstrncpy(list[i],p,n+1); /* Note: n+1 */
1068 		    i++;
1069 		}
1070 		while (*s == SP) s++;
1071 		p = s;
1072 		n = 0;
1073 		continue;
1074 	    } else {			/* Within a group */
1075 		n++;
1076 		s++;
1077 	    }
1078 	} else {			/* Regular character */
1079 	    q = 0;
1080 	    s++;
1081 	    n++;
1082 	}
1083     }
1084     if (*p && i < len) {		/* Last one */
1085 	if (list[i])
1086 	  free(list[i]);
1087 	if ((list[i] = (char *)malloc(n+1))) {
1088 	    ckstrncpy(list[i],p,n+1);
1089 	    debug(F111,"makelist last element",p,i);
1090 	}
1091     }
1092     i++;				/* Clear out the rest of the list */
1093     for ( ; i < len; i++) {
1094 	if (list[i])
1095 	  free (list[i]);
1096 	list[i] = NULL;
1097     }
1098     if (s2) free(s2);
1099 }
1100 
1101 /*
1102    M A K E S T R  --  Creates a dynamically allocated string.
1103 
1104    Makes a new copy of string s and sets pointer p to its address.
1105    Handles degenerate cases, like when buffers overlap or are the same,
1106    one or both arguments are NULL, etc.
1107 
1108    The source string is assumed to be NUL-terminated.  Therefore it can not
1109    be a UCS-2 string or arbitrary binary data.
1110 
1111    The target pointer must be either NULL or else a pointer to a previously
1112    malloc'ed buffer.  If not, expect a core dump or segmentation fault.
1113 
1114    Note: The caller can tell whether this routine failed as follows:
1115 
1116      malloc(&p,q);
1117      if (q & !p) { makestr() failed };
1118 
1119    Really this routine should have returned a length, but since it doesn't
1120    we set the global variable makestrlen to the length of the result string.
1121 */
1122 int makestrlen = 0;
1123 
1124 VOID
1125 #ifdef CK_ANSIC
makestr(char ** p,const char * s)1126 makestr(char **p, const char *s)
1127 #else
1128 makestr(p,s) char **p, *s;
1129 #endif
1130 /* makestr */ {
1131     int x = 0;
1132     char *q = NULL;
1133 #ifdef CK_ANSIC
1134     register const char * s2;
1135 #else
1136     register char * s2;
1137 #endif /* CK_ANSIC */
1138     register char * q2;
1139 
1140     if (*p == s)			/* The two pointers are the same. */
1141       return;				/* Don't do anything. */
1142 
1143     if (!s) {				/* New definition is null? */
1144 	if (*p)				/* Free old storage. */
1145 	  free(*p);
1146 	*p = NULL;			/* Return null pointer. */
1147 	makestrlen = 0;
1148 	return;
1149     }
1150     s2 = s;				/* Maybe new string will fit */
1151 
1152 #ifdef COMMENT
1153 /*
1154   This is a fairly big win, allowing us to skip the malloc() and free if the
1155   destination string already exists and is not shorter than the source string.
1156   But it doesn't allow for possible overlap of source and destination.
1157 */
1158     if (*p) {				/* into old storage... */
1159 	char * p2 = *p;
1160 	char c;
1161 	while (c = *p2) {
1162 	    if (!(*p2++ = *s2++))
1163 	      break;
1164 	    x++;
1165 	}
1166 	makestrlen = x;
1167 	if (c) return;
1168     }
1169 #endif /* COMMENT */
1170 
1171 /* Didn't fit */
1172 
1173     x = 0;
1174     while (*s2++) x++;			/* Get (rest of) length of s.  */
1175 
1176     if (x >= 0) {			/* Get length, even of empty string. */
1177 	q = malloc(x+1);		/* Get and point to temp storage. */
1178 	if (q) {
1179 	    makestrlen = x;		/* Remember length for stats */
1180 	    s2 = s;			/* Point back to beginning of source */
1181 	    q2 = q;			/* Copy dest pointer to increment... */
1182 	    while ((*q2++ = *s2++)) ;	/* Instead of calling strcpy(). */
1183 /*
1184   Note: HP flexelint says that the above loop can result in creation (++) and
1185   access (*) of out-of-bounds pointers.  I really don't see it.
1186 */
1187 	}
1188 #ifdef DEBUG
1189 	else {				/* This would be a really bad error */
1190 	    char tmp[24];		/* So get a good record of it. */
1191 	    if (x > 23) {
1192 		ckstrncpy(tmp,s,20);
1193 		strcpy(tmp+20,"...");
1194 		tmp[23] = NUL;
1195 	    } else {
1196 		strcpy(tmp,s);		/* We already checked the length */
1197 	    }
1198 	    debug(F110,"MAKESTR MALLOC FAILURE ",tmp,0);
1199 	}
1200 #endif /* DEBUG */
1201     } else
1202       q = NULL;				/* Length of string is zero */
1203 
1204     if (*p)				/* Now free the original storage. */
1205       free(*p);
1206     *p = q;
1207 }
1208 
1209 /*  X M A K E S T R  --  Non-destructive makestr() if s is NULL.  */
1210 
1211 VOID
1212 #ifdef CK_ANSIC
xmakestr(char ** p,const char * s)1213 xmakestr(char **p, const char *s)
1214 #else
1215 xmakestr(p,s) char **p, *s;
1216 #endif
1217 /* xmakestr */ {
1218     if (s) makestr(p,s);
1219 }
1220 
1221 #ifndef USE_MEMCPY
1222 /* C K M E M C P Y  --  Portable (but slow) memcpy() */
1223 
1224 /* Copies n bytes from s to p, allowing for overlap. */
1225 /* For use when real memcpy() not available. */
1226 
1227 VOID
ckmemcpy(p,s,n)1228 ckmemcpy(p,s,n) char *p, *s; int n; {
1229     char * q = NULL;
1230     register int i;
1231     int x;
1232 
1233     if (!s || !p || n <= 0 || p == s)	/* Verify args */
1234       return;
1235     x = p - s;				/* Check for overlap */
1236     if (x < 0)
1237       x = 0 - x;
1238     if (x < n) {			/* They overlap */
1239 	q = p;
1240 	if (!(p = (char *)malloc(n)))	/* So use a temporary buffer */
1241 	  return;
1242     }
1243     for (i = 0; i < n; i++)		/* Copy n bytes */
1244       p[i] = s[i];
1245     if (q) {				/* If we used a temporary buffer */
1246 	for (i = 0; i < n; i++)		/* copy from it to destination */
1247 	  q[i] = p[i];
1248 	if (p) free(p);			/* and free the temporary buffer */
1249     }
1250 }
1251 #endif /* USE_MEMCPY */
1252 
1253 
1254 /*  C K S T R C M P  --  String comparison with case-matters selection */
1255 /*
1256   Call with pointers to the two strings, s1 and s2, a length, n,
1257   and c == 0 for caseless comparison, nonzero for case matters.
1258   Call with n == -1 to compare without a length limit.
1259   Compares up to n characters of the two strings and returns:
1260     1 if s1 > s2
1261     0 if s1 = s2
1262    -1 if s1 < s2
1263   Note: case handling is only as good as isupper() and tolower().
1264   Modifed 2013-12-06 to be somewhat locale-aware.
1265 */
1266 int
ckstrcmp(s1,s2,n,c)1267 ckstrcmp(s1,s2,n,c) char *s1, *s2; register int n, c; {
1268     register CHAR t1, t2;
1269 #ifdef HAVE_LOCALE
1270     int rc;
1271     char t1buf[2], t2buf[2];
1272     t1buf[1] = NUL;
1273     t2buf[1] = NUL;
1274 #endif /* HAVE_LOCALE */
1275 
1276     if (n == 0) return(0);
1277     if (!s1) s1 = "";			/* Watch out for null pointers. */
1278     if (!s2) s2 = "";
1279     if (!*s1) return(*s2 ? -1 : 0);
1280     if (!*s2) return(1);
1281     while (n--) {
1282 	t1 = (CHAR) *s1++;		/* Get next character from each. */
1283 	t2 = (CHAR) *s2++;
1284 	if (!t1) return(t2 ? -1 : 0);
1285 	if (!t2) return(1);
1286 	if (!c) {			/* If case doesn't matter */
1287 	    if (isupper(t1)) t1 = tolower(t1); /* Convert case. */
1288 	    if (isupper(t2)) t2 = tolower(t2);
1289 	}
1290 #ifdef HAVE_LOCALE
1291 /*
1292   This only works for single-byte character sets but it's better than
1293   nothing, because previously this routine worked right only for ASCII.
1294 */
1295         t1buf[0] = t1; 			/* Convert chars to strings */
1296         t2buf[0] = t2;
1297 	if ((rc = strcoll(t1buf,t2buf))) return(rc);
1298 #else
1299 	if (t1 < t2) return(-1);	/* s1 < s2 */
1300 	if (t1 > t2) return(1);		/* s1 > s2 */
1301 #endif /* HAVE_LOCALE */
1302     }
1303     return(0);				/* They're equal */
1304 }
1305 
1306 /*  C K S T R P R E  --  Caseless string prefix comparison  */
1307 
1308 /* Returns position of the first char in the 2 strings that doesn't match */
1309 
1310 int
ckstrpre(s1,s2)1311 ckstrpre(s1,s2) char *s1, *s2; {
1312     CHAR t1, t2;
1313     int n = 0;
1314     if (!s1) s1 = "";
1315     if (!s2) s2 = "";
1316     while (1) {
1317 	t1 = (CHAR) *s1++;
1318 	t2 = (CHAR) *s2++;
1319 	if (!t1 || !t2) return(n);
1320 	if (isupper(t1)) t1 = tolower(t1);
1321 	if (isupper(t2)) t2 = tolower(t2);
1322 	if (t1 != t2)
1323 	  return(n);
1324 	n++;
1325     }
1326 }
1327 
1328 #define GLOBBING
1329 
1330 /*  C K M A T C H  --  Match a string against a pattern  */
1331 /*
1332   Call with:
1333     pattern to be matched.
1334     string to look for the pattern in.
1335     icase is 1 if case-sensitive, 0 otherwise.
1336     opts is a bitmask:
1337       Bit 0 (=1):
1338 	1 = Match strings starting with '.'
1339 	0 = Don't match them (used with UNIX filenames).
1340       Bit 1 (=2):
1341 	1 = File globbing (dirseps are fences);
1342 	0 = Dirseps are not fences.
1343       Bit 2 (=4):
1344 	1 = Allow ^ and $ anchors at beginning and end of pattern.
1345 	0 = Don't allow them (normal case for filename matching).
1346       Bit 3 (and beyond): Undefined.
1347   Works only with NUL-terminated strings.
1348   Pattern may contain any number of ? and/or *.
1349   If CKREGEX is defined, also [abc], [a-z], and/or {string,string,...}.
1350   (Note: REGEX is a misnomer, see below.)
1351 
1352   Returns:
1353     0 if string does not match pattern,
1354     >= 1, the 1-based position in the string where the match was found.
1355 
1356   To be done:
1357     Find a way to identify the piece of the string that matched the pattern,
1358     as in Snobol "LINE (PAT . RESULT)".  This is now partially done by
1359     setting matchpos and matchend (except matchend needs some tuning).  But
1360     these are useless unless a copy of the string is kept, or a copy of the
1361     matching part is made.  But that would be too costly in performance --
1362     this routine has to be fast because it's used for wildcard expansion.
1363 
1364   Note:
1365     Patterns are not the same as regular expressions, in which '*' means
1366     0 or more repetitions of the preceding item.  For example "a*b" as a
1367     pattern matches any string that starts with 'a' and ends with 'b'; as a
1368     regular expression it matches any string of zero or more a's followed by
1369     one b.  Regular expressions are especially useful in matching strings of
1370     (say) digits, or letters, e.g. "[0-9]*" matches any string of digits.
1371     So far, Kermit doesn't do this.
1372 */
1373 static char * mypat = NULL;		/* For rewriting pattern */
1374 static int matchpos = 0;
1375 int matchend = 0;
1376 static int matchdepth = 0;
1377 static int stringpos = 0;
1378 static char * ostring = NULL;
1379 
1380 #define MATCHRETURN(x,y) { rc=y; where=x; goto xckmatch; }
1381 static char * lastpat = NULL;
1382 
1383 static int xxflag = 0;			/* Global bailout flag for ckmatch() */
1384 
1385 int
ispattern(s)1386 ispattern(s) char * s; {
1387     int quote = 0, sbflag = 0, sb = 0, cbflag = 0, cb = 0;
1388 
1389     char c = 0;
1390     if (*s == '^') return(1);
1391     while ((c = *s++)) {
1392 	if (quote) {
1393 	    quote = 0;
1394 	    continue;
1395 	}
1396 	if (c == '\\') {
1397 	    quote = 1;
1398 	    continue;
1399 	}
1400 	if (c == '*') return(1);
1401 	if (c == '?') return(1);
1402 	/* Unquoted brackets or braces must match */
1403 	if (c == '[') { sbflag++; sb++; continue; }
1404 	if (c == ']') { sb--; continue; }
1405 	if (c == '{') { cbflag++; cb++; continue; }
1406 	if (c == '}') { cb--; continue; }
1407 	if (!*s && c == '$') return(1);
1408     }
1409     return(sbflag || cbflag);
1410 }
1411 
1412 int
ckmatch(pattern,string,icase,opts)1413 ckmatch(pattern, string, icase, opts) char *pattern,*string; int icase, opts; {
1414     int q = 0, i = 0, k = -1, x, flag = 0;
1415     int rc = 0;				/* Return code */
1416     int havestar = 0;
1417     int where = -1;
1418     CHAR cp;				/* Current character from pattern */
1419     CHAR cs;				/* Current character from string */
1420     char * patstart;			/* Start of pattern */
1421     int plen, dot, globbing, xstar = 0;
1422     int bronly = 0;			/* Whole pattern is {a,b,c,...} */
1423 
1424     debug(F111,"CKMATCH ENTRY pat opt",pattern,opts);
1425     debug(F111,"CKMATCH ENTRY str dep",string,matchdepth);
1426     /* debug(F101,"CKMATCH ENTRY icase","",icase); */
1427 
1428     globbing = opts & 2;
1429 
1430     if (!string) string = "";
1431     if (!pattern) pattern = "";
1432 
1433     if (!*pattern) {			/* Empty pattern matches anything */
1434 	matchdepth++;			/* (it wasn't incremented yet) */
1435 	MATCHRETURN(0,1);
1436     } else if (!*string) {
1437 	MATCHRETURN(0,0);
1438     }
1439     patstart = pattern;			/* Remember beginning of pattern */
1440 
1441     if (matchdepth == 0) {		/* Top-level call? */
1442 	xxflag = 0;
1443 	stringpos = 0;			/* Reset indices etc. */
1444 	matchpos = 0;
1445 	matchend = 0;
1446 	ostring = string;
1447 	lastpat = pattern;
1448 	if (*pattern == '{')		/* Entire pattern is {a,b.c} */
1449 	  bronly = 1;			/* Maybe */
1450 	dot = (opts & 1) ||		/* Match leading dot (if file) */
1451 	    ((opts & 2) == 0) ||	/* always if not file */
1452 	    (pattern[0] == '.');	/* or if pattern starts with '.' */
1453 
1454 	plen = strlen(pattern);		/* Length of pattern */
1455 /* This would be used in calculating length of matching segment */
1456 	if (plen > 0)			/* User's pattern ends with '*' */
1457 	  if (pattern[plen - 1] == '*')
1458 	    xstar = 1;
1459 	if (pattern[0] == '*') {	/* User's pattern starts with '*' */
1460 	    matchpos = 1;
1461 	    debug(F111,"CKMATCH 1",string, matchpos);
1462 	}
1463 	if (opts & 4) {			/* ^..$ allowed (top level only) */
1464 	    /* Rewrite pattern to account for ^..$ anchoring... */
1465 
1466 	    if (mypat) free(mypat);	/* Get space for "*pattern*" */
1467 	    mypat = (char *)malloc(plen + 4);
1468 	    if (mypat) {		/* Got space? */
1469 		char * s = pattern, * p = mypat; /* Set working pointers */
1470 		if (*s == '^') {	/* First source char is ^ */
1471 		    s++;		/* so skip past it */
1472 		} else if (*s != '*') {	/* otherwise */
1473 		    *p++ = '*';		/* prepend '*' to pattern */
1474 		}
1475 		while (*s) {		/* Copy rest of pattern */
1476 		    if (!*(s+1)) {	/* Final pattern character? */
1477 			if (*s != '$') { /* If it's not '$' */
1478 			    *p++ = *s;	/* Copy it into the pattern */
1479 			    if (*s++ != '*') /* And if it's also not '*' */
1480 			      *p++ = '*'; /* append '*'. */
1481 			}
1482 			break;		/* Done */
1483 		    } else		/* Not final character */
1484 		      *p++ = *s++;	/* Just copy it */
1485 		}
1486 		*p = NUL;		/* Terminate the new pattern */
1487 		pattern = mypat;	/* Make the switch */
1488 	    }
1489 	    debug(F110,"CKMATCH INIT pat",pattern,0);
1490 	}
1491     }
1492     matchdepth++;			/* Now increment call depth */
1493 
1494 #ifdef UNIX
1495     if (!dot) {				/* For UNIX file globbing */
1496 	if (*string == '.' && *pattern != '.' && !matchdot) {
1497 	    if (
1498 #ifdef CKREGEX
1499 		*pattern != '{' && *pattern != '['
1500 #else
1501 		1
1502 #endif /* CKREGEX */
1503 		) {
1504 		debug(F110,"ckmatch skip",string,0);
1505 		MATCHRETURN(1,0);
1506 	    }
1507 	}
1508     }
1509 #endif /* UNIX */
1510     while (1) {
1511 	k++;
1512 	cp = *pattern;			/* Character from pattern */
1513 	cs = *string;			/* Character from string */
1514 
1515 #ifdef COMMENT
1516 	debug(F000,"CKMATCH pat cp",pattern,cp);
1517 	debug(F000,"CKMATCH str cs",string,cs);
1518 #endif /* COMMENT */
1519 
1520 	if (!cs) {			/* End of string - done. */
1521 	    x = (!cp || (cp == '*' && !*(pattern+1))) ? 1 : 0;
1522 	    if (x) {
1523 		if (!matchpos) {
1524 		    matchpos = stringpos;
1525 		    debug(F111,"CKMATCH A",string, matchpos);
1526 		}
1527 		matchend = stringpos;
1528 		MATCHRETURN(2,matchpos);
1529 	    }
1530 	    debug(F111,"CKMATCH ZERO d",string, matchpos);
1531 	    matchpos = 0;
1532 	    MATCHRETURN(16,matchpos);
1533 	}
1534         if (!icase) {			/* If ignoring case */
1535 	    if (isupper(cp))		/* convert both to lowercase. */
1536 	      cp = tolower(cp);
1537 	    if (isupper(cs))
1538 	      cs = tolower(cs);
1539         }
1540 	if (q) {			/* This character was quoted */
1541 	    debug(F000,"CKMATCH QUOTED",pattern,cp);
1542 	    q = 0;			/* Turn off quote flag */
1543 
1544 	    if (cs == cp) {		/* Compare directly */
1545 		if (!matchpos) {	/* Matches */
1546 		    matchpos = stringpos;
1547 		    debug(F111,"CKMATCH \\ new match",string, matchpos);
1548 		}
1549 		pattern++;
1550 	    } else {			/* Doesn't match */
1551 		pattern = lastpat;	/* Back up the pattern */
1552 		matchpos = 0;
1553 		debug(F111,"CKMATCH \\ no match",pattern, matchpos);
1554 	    }
1555 	    string++;
1556 	    stringpos++;
1557 	    continue;
1558 	}
1559 	if (cp == CMDQ && !q) {		/* Quote in pattern */
1560 	    debug(F000,"CKMATCH QUOTE",pattern,cp);
1561 	    q = 1;			/* Set flag */
1562 	    pattern++;			/* Advance to next pattern character */
1563 	    continue;			/* and continue. */
1564 	}
1565 	if (cs && cp == '?') {		/* '?' matches any char */
1566 	    if (!matchpos) {
1567 		matchpos = stringpos;
1568 		debug(F111,"CKMATCH D",string, matchpos);
1569 	    }
1570 	    debug(F110,"CKMATCH ? pat",pattern,0);
1571 	    debug(F110,"CKMATCH ? str",string,0);
1572 	    pattern++;
1573 	    string++;
1574 	    stringpos++;
1575 	    continue;
1576 #ifdef CKREGEX
1577 	} else if (cp == '[') {		/* Have bracket */
1578 	    int q = 0;			/* My own private q */
1579 	    char * psave = NULL;	/* and backup pointer */
1580 	    CHAR clist[256];		/* Character list from brackets */
1581 	    CHAR c, c1, c2;
1582 
1583 	    for (i = 0; i < 256; i++)	/* memset() etc not portable */
1584 	      clist[i] = NUL;
1585 	    psave = ++pattern;		/* Where pattern starts */
1586 	    debug(F111,"CKMATCH [] ",pattern-1, matchpos);
1587 	    for (flag = 0; !flag; pattern++) { /* Loop thru pattern */
1588 		c = (CHAR)*pattern;	/* Current char */
1589 		debug(F000,">>> pattern char","",c);
1590 		if (q) {		/* Quote within brackets */
1591 		    q = 0;
1592 		    clist[c] = 1;
1593 		    continue;
1594 		}
1595 		if (!icase)		/* Case conversion */
1596 		  if (isupper(c))
1597 		    c = tolower(c);
1598 		switch (c) {		/* Handle unquoted character */
1599 		  case NUL:		/* End of string */
1600 		    MATCHRETURN(4,0);	/* No matching ']' so fail */
1601 		  case CMDQ:		/* Next char is quoted */
1602 		    q = 1;		/* Set flag */
1603 		    continue;		/* and continue. */
1604 		  case '-':		/* A range is specified */
1605 		    c1 = (pattern > psave) ? (CHAR)*(pattern-1) : NUL;
1606 		    c2 = (CHAR)*(pattern+1); /* IGNORE OUT-OF-BOUNDS WARNING */
1607 		    if (c2 == ']') c2 = NUL; /* (it can't happen) */
1608 		    if (c1 == NUL) c1 = c2;
1609 		    for (c = c1; c <= c2; c++) {
1610 			clist[c] = 1;
1611 			if (!icase) {
1612 			    if (islower(c)) {
1613 				clist[toupper(c)] = 1;
1614 			    } else if (isupper(c)) {
1615 				clist[tolower(c)] = 1;
1616 			    }
1617 			}
1618 		    }
1619 		    continue;
1620 		  case ']':		/* End of bracketed sequence */
1621 		    flag = 1;		/* Done with FOR loop */
1622 		    break;		/* Compare what we have */
1623 		  default:		/* Just a char */
1624 		    clist[c] = 1;	/* Record it */
1625 		    if (!icase) {
1626 			if (islower(c)) {
1627 			    clist[toupper(c)] = 1;
1628 			} else if (isupper(c)) {
1629 			    clist[tolower(c)] = 1;
1630 			}
1631 		    }
1632 		    continue;
1633 		}
1634 	    }
1635 	    debug(F000,">>> cs","",cs);
1636 	    debug(F101,">>> clist[cs]","",clist[cs]);
1637 	    debug(F000,">>> string",string,*string);
1638 
1639 	    if (!clist[(unsigned)cs]) {	/* No match? */
1640 		if (!*string) {		/* This clause 16 Jun 2005 */
1641 		    MATCHRETURN(5,0);	/* Nope, done. */
1642 		}
1643 /*
1644   We need to fail here if the [clist] is not allowed to float.
1645   The [clist] is not allowed to float if it is not preceded
1646   by an asterisk, right?  30 Dec 2005.
1647 */
1648 		if (!havestar) {
1649 		    MATCHRETURN(500,0);
1650 		}
1651 		string++;		/* From here to end added 2005/6/15 */
1652 		stringpos++;
1653 		pattern = lastpat;	/* Back up pattern */
1654 		k = ckmatch(pattern,string,icase,opts);
1655 		if (xxflag) MATCHRETURN(0,0);
1656 		if (!matchpos && k > 0)
1657 		  matchpos = stringpos;
1658 		MATCHRETURN(5, (*string) ? matchpos : 0);
1659 	    }
1660 	    if (!matchpos) {
1661 		matchpos = stringpos;
1662 		debug(F111,"CKMATCH [] match",string, matchpos);
1663 	    }
1664 	    string++;			/* Yes, advance string pointer */
1665 	    stringpos++;
1666 	    continue;			/* and go on. */
1667 	} else if (cp == '{') {		/* Braces enclosing list of strings */
1668 	    char * p, * s, * s2, * buf = NULL;
1669 	    int n, bc = 0;
1670 	    int len = 0;
1671 	    debug(F111,"CKMATCH {} ",string, matchpos);
1672 	    for (p = pattern++; *p; p++) {
1673 		if (*p == '{') bc++;
1674 		if (*p == '}') bc--;
1675 		if (bc < 1) break;
1676 	    }
1677 	    if (bc != 0) {		/* Braces don't match */
1678 		MATCHRETURN(6,0);	/* Fail */
1679 	    } else {			/* Braces do match */
1680 		int q = 0, done = 0;
1681 		len = *p ? strlen(p+1) : 0; /* Length of rest of pattern */
1682 		if (len)
1683 		  bronly = 0;
1684 		if (bronly && (matchdepth != 1))
1685 		  bronly = 0;
1686 		n = p - pattern;	    /* Size of list in braces */
1687 		if ((buf = (char *)malloc(n+1))) { /* Copy so we can poke it */
1688 		    char * tp = NULL;
1689 		    int k, sofar;
1690 		    ckstrncpy(buf,pattern,n+1);
1691 		    sofar = string - ostring - matchpos + 1;
1692 		    if (sofar < 0) sofar = 0;
1693 		    debug(F111,"CKMATCH .. string",string,sofar);
1694 		    debug(F111,"CKMATCH .. ostring",ostring,sofar);
1695 		    n = 0;
1696 		    for (s = s2 = buf; 1; s++) { /* Loop through segments */
1697 			n++;
1698 			if (q) {	/* This char is quoted */
1699 			    q = 0;
1700 			    if (!*s)
1701 			      done = 1;
1702 			    continue;
1703 			}
1704 			if (*s == CMDQ && !q) {	/* Quote next char */
1705 			    q = 1;
1706 			    continue;
1707 			}
1708 			if (!*s || *s == ',') {	/* End of this segment */
1709 			    int tplen = 0;
1710 			    if (!*s)	/* If end of buffer */
1711 			      done = 1;	/* then end of last segment */
1712 			    *s = NUL;	/* Overwrite comma with NUL */
1713 			    debug(F111,"CKMATCH {} segment",s2,done);
1714 			    tplen = n + len + sofar + 2;
1715 			    if (!*s2) {	/* Empty segment, no advancement */
1716 				k = 0;
1717 			    } else if ((tp = (char *)malloc(tplen))) {
1718 				int savpos, opts2;
1719 				char * pp;
1720 				pp = matchpos > 0 ?
1721 				    &ostring[matchpos-1] :
1722 				    ostring;
1723 				if (bronly) {
1724 				    if (matchpos > 0)
1725 				      ckstrncpy(tp,pp,sofar+1);
1726 				    else
1727 				      ckstrncpy(tp,pp,sofar);
1728 				} else {
1729 				    tp[0] = '*';
1730 				    tp[1] = NUL;
1731 				    if (matchpos > 0)
1732 				      ckstrncpy(&tp[1],pp,sofar+1);
1733 				    else
1734 				      ckstrncpy(&tp[1],pp,sofar);
1735 				}
1736 				ckstrncat(tp,s2,tplen); /* Current segment */
1737 				ckstrncat(tp,p+1,tplen); /* rest of pattern */
1738 
1739 				debug(F101,"CKMATCH {} matchpos","",matchpos);
1740 				savpos = matchpos;
1741 				matchpos = 0;
1742 #ifdef DEBUG
1743 				if (deblog) {
1744 				    debug(F111,"CKMATCH {} tp",tp,matchpos);
1745 				    debug(F111,"CKMATCH {} string",
1746 					  string,matchpos);
1747 				    debug(F111,"CKMATCH {} ostring",
1748 					  ostring,savpos);
1749 				}
1750 #endif /* DEBUG */
1751 				/* If segment starts with dot */
1752 				/* then set matchdot option.. */
1753 				opts2 = opts;
1754 				if (*s2 == '.') opts2 |= 1;
1755 				debug(F111,"CKMATCH {} recursing",s2,opts2);
1756 				k = ckmatch(tp,
1757 					    (string > ostring) ?
1758 					    &ostring[savpos-1] : string,
1759 					    icase,opts2);
1760 #ifdef DEBUG
1761 				if (deblog) {
1762 				    debug(F101,"CKMATCH {} k","",k);
1763 				    debug(F101,"CKMATCH {} savpos","",savpos);
1764 				}
1765 #endif /* DEBUG */
1766 				free(tp);
1767 				tp = NULL;
1768 				if (xxflag) MATCHRETURN(0,0);
1769 				if (k == 0) {
1770 				    matchpos = savpos;
1771 				}
1772 				if (k > 0) { /* If it matched we're done */
1773 				    MATCHRETURN(7,k);
1774 				}
1775 			    } else {	/* Malloc failure */
1776 				MATCHRETURN(14,0);
1777 			    }
1778 			    if (k) {	/* Successful comparison */
1779 				if (!matchpos) {
1780 				    matchpos = stringpos;
1781 				    debug(F111,"CKMATCH {} match",
1782 					  string, matchpos);
1783 				}
1784 				string += n-1; /* Advance pointers */
1785 				pattern = p+1;
1786 				break;
1787 			    }
1788 			    if (done)	/* If no more segments */
1789 			      break;	/* break out of segment loop. */
1790 			    s2 = s+1;	/* Otherwise, on to next segment */
1791 			    n = 0;
1792 			}
1793 		    }
1794 		    free(buf);
1795 		}
1796 	    }
1797 #endif /* CKREGEX */
1798 	} else if (cp == '*') {		/* Pattern char is asterisk */
1799 	    char * psave;
1800 	    char * p, * s = NULL;	/* meaning match anything */
1801 	    int k, n, q = 0;
1802 	    havestar++;			/* The rest can float */
1803 	    while (*pattern == '*')	/* Collapse successive asterisks */
1804 	      pattern++;
1805 	    psave = pattern;		/* First non-asterisk after asterisk */
1806 	    lastpat = pattern - 1;	/* Ditto, global */
1807 	    debug(F111,"CKMATCH * ",string,matchpos);
1808 	    for (n = 0, p = psave; *p; p++,n++) { /* Find next meta char */
1809 		if (!q) {
1810 		    if (*p == '?' || *p == '*' || *p == CMDQ
1811 #ifdef CKREGEX
1812 			|| *p == '[' || *p == '{'
1813 #endif /* CKREGEX */
1814 			)
1815 		      break;
1816 #ifdef GLOBBING
1817 		    if (globbing
1818 #ifdef UNIXOROSK
1819 			&& *p == '/'
1820 #else
1821 #ifdef VMS
1822 			&& (*p == '.' || *p == ']' ||
1823 			    *p == '<' || *p == '>' ||
1824 			    *p == ':' || *p == ';')
1825 #else
1826 #ifdef datageneral
1827 			&& *p == ':'
1828 #else
1829 #ifdef STRATUS
1830 			&& *p == '>'
1831 #endif /* STRATUS */
1832 #endif /* datageneral */
1833 #endif /* VMS */
1834 #endif /* UNIXOROSK */
1835 			)
1836 		      break;
1837 #endif /* GLOBBING */
1838 		}
1839 	    }
1840 	    debug(F111,"CKMATCH * n string",string,n);
1841 	    debug(F111,"CKMATCH * n pattrn",pattern,n);
1842 	    debug(F111,"CKMATCH * n p",p,n);
1843 	    if (n > 0) {		/* Literal string to match  */
1844 		s = (char *)malloc(n+1);
1845 		if (s) {
1846 		    ckstrncpy(s,psave,n+1); /* Copy cuz no poking original */
1847 		    if (*p) {
1848 			k = ckindex(s,string,0,0,icase); /* 1-based index() */
1849 			debug(F110,"CKMATCH * Index() string",string,0);
1850 			debug(F110,"CKMATCH * Index() pattrn",s,0);
1851 			debug(F101,"CKMATCH * Index() result","",k);
1852 		    } else {		/* String is right-anchored */
1853 			k = ckindex(s,string,-1,1,icase); /* rindex() */
1854 			debug(F111,"CKMATCH * Rindex()",string,k);
1855 			debug(F110,"CKMATCH * Rindex() pattrn",s,0);
1856 			debug(F101,"CKMATCH * Rindex() result","",k);
1857 		    }
1858 		    free(s);
1859 		    if (k < 1) {
1860 			MATCHRETURN(8,0);
1861 		    }
1862 		    debug(F111,"CKMATCH * stringpos matchpos",
1863 			  ckitoa(stringpos), matchpos);
1864 		    if (!matchpos) {
1865 			matchpos = string - ostring + k;
1866 			debug(F111,"CKMATCH * new match ", string, matchpos);
1867 		    }
1868 		    string += k + n - 1;
1869 		    stringpos += k + n - 1;
1870 		    pattern += n;
1871 		    debug(F111,"CKMATCH * new string", string, stringpos);
1872 		    debug(F110,"CKMATCH * new pattrn", pattern, 0);
1873 		    continue;
1874 		}
1875 	    } else if (!*p) {		/* Asterisk at end matches the rest */
1876 		if (!globbing) {	/* (if not filename globbing) */
1877 		    if (!matchpos) {
1878 			matchpos = stringpos;
1879 			debug(F111,"CKMATCH *$ ",string, matchpos);
1880 		    }
1881 		    matchend = stringpos;
1882 		    MATCHRETURN(9,matchpos);
1883 		}
1884 #ifdef GLOBBING
1885 		while (*string) {
1886 		    if (globbing	/* Filespec so don't cross fields */
1887 #ifdef OS2
1888 			&& *string == '/' || *string == '\\' ||
1889 			*string == ':'
1890 #else
1891 #ifdef UNIXOROSK
1892 			&& *string == '/'
1893 #else
1894 #ifdef VMS
1895 			&& (*string == '.' || *string == ']' ||
1896 			    *string == '<' || *string == '>' ||
1897 			    *string == ':' || *string == ';')
1898 #else
1899 #ifdef datageneral
1900 			&& *string == ':'
1901 #else
1902 #ifdef STRATUS
1903 			&& *string == '>'
1904 #else
1905 			&& *string == '/' /* (catch-all) */
1906 #endif /* STRATUS */
1907 #endif /* datageneral */
1908 #endif /* VMS */
1909 #endif /* UNIXOROSK */
1910 #endif /* OS2 */
1911 			) {
1912 			matchend = stringpos;
1913 			MATCHRETURN(10,0);
1914 		    }
1915 		    if (!matchpos) {
1916 			matchpos = stringpos;
1917 			debug(F111,"CKMATCH *$ match",string, matchpos);
1918 		    }
1919 		    string++;
1920 		    stringpos++;
1921 		}
1922 #endif /* GLOBBING */
1923 		if (!matchpos) {
1924 		    matchpos = stringpos;
1925 		    debug(F111,"CKMATCH ** match",string, matchpos);
1926 		}
1927 		matchend = stringpos;
1928 		MATCHRETURN(11,matchpos);
1929 
1930 	    } else {			/* A meta char follows asterisk */
1931 		if (!*string)
1932 		  MATCHRETURN(17, matchpos = 0);
1933 #ifdef COMMENT
1934 		/* This is more elegant but it doesn't work. */
1935 		p--;
1936 		string++;
1937 		stringpos++;
1938 		k = ckmatch(p,string,icase,opts);
1939 #else
1940 		while (*string && ((k = ckmatch(p,string,icase,opts)) < 1)) {
1941 		    if (xxflag) MATCHRETURN(0,0);
1942 		    string++;
1943 		    stringpos++;
1944 		}
1945 		if (!*string && k < 1) {
1946 /*
1947   Definitely no match so we set a global flag to inibit further backing up
1948   and retrying by previous incarnations, since they don't see that the string
1949   and/or pattern, which are on the stack, have been exhausted at this level.
1950 */
1951 		    xxflag++;
1952 		    debug(F111,"CKMATCH DEFINITELY NO MATCH",p,k);
1953 		    MATCHRETURN(91,0);
1954 		}
1955 #endif	/* COMMENT */
1956 		debug(F111,"CKMATCH *<meta> k",string, k);
1957 		if (!matchpos && k > 0) {
1958 		    matchpos = stringpos;
1959 		    debug(F111,"CKMATCH *<meta> matchpos",string, matchpos);
1960 		}
1961 		MATCHRETURN(12, (*string) ? matchpos : 0);
1962 	    }
1963 	} else if (cs == cp) {
1964 	    pattern++;
1965 	    string++;
1966 	    stringpos++;
1967 	    if (!matchpos) {
1968 		matchpos = stringpos;
1969 		debug(F111,"CKMATCH cs=cp",string, matchpos);
1970 	    }
1971 	    continue;
1972 	} else {
1973 	    MATCHRETURN(13,0);
1974 	}
1975     }
1976   xckmatch:
1977     {
1978 #ifdef DEBUG
1979 	char msgbuf[256];
1980 #endif /* DEBUG */
1981 	if (matchdepth > 0)
1982 	  matchdepth--;
1983 	matchpos = rc;
1984 #ifdef DEBUG
1985 	ckmakxmsg(msgbuf,256,
1986 		  "CKMATCH RETURN[",
1987 		  ckitoa(where),
1988 		  "] matchpos=",
1989 		  ckitoa(matchpos),
1990 		  " matchdepth=",
1991 		  ckitoa(matchdepth),
1992 		  " pat=",pattern,
1993 		  " string=",string,NULL,NULL
1994 		  );
1995 	debug(F110,msgbuf,string,0);
1996 #endif /* DEBUG */
1997 	return(rc);
1998     }
1999 }
2000 
2001 
2002 #ifdef CKFLOAT
2003 /*  I S F L O A T  -- Verify that arg represents a floating-point number */
2004 
2005 /*
2006   Portable replacement for atof(), strtod(), scanf(), etc.
2007 
2008   Call with:
2009     s = pointer to string
2010     flag == 0 means entire string must be a (floating-pointing) number.
2011     flag != 0 means to terminate scan on first character that is not legal.
2012 
2013   Returns:
2014     1 if result is a legal number;
2015     2 if result has a fractional part;
2016     0 if not or if string empty.
2017 
2018   Side effect:
2019     Sets global floatval to floating-point value if successful.
2020 
2021   Number need not contain a decimal point -- integer is subcase of float.
2022   Scientific notation not supported.
2023 */
2024 CKFLOAT floatval = 0.0;			/* For returning value */
2025 
2026 int
isfloat(s,flag)2027 isfloat(s,flag) char *s; int flag; {
2028     int state = 0;
2029     int sign = 0;
2030     char c;
2031     CKFLOAT d = 0.0, f = 0.0;
2032 
2033     if (!s) return(0);
2034     if (!*s) return(0);
2035 
2036     while (isspace(*s)) s++;
2037 
2038     if (*s == '-') {			/* Handle optional sign */
2039 	sign = 1;
2040 	s++;
2041     } else if (*s == '+')
2042       s++;
2043     while ((c = *s++)) {		/* Handle numeric part */
2044 	switch (state) {
2045 	  case 0:			/* Mantissa... */
2046 	    if (isdigit(c)) {
2047 		f = f * 10.0 + (CKFLOAT)(c - '0');
2048 		continue;
2049 	    } else if (c == '.') {
2050 		state = 1;
2051 		d = 1.0;
2052 		continue;
2053 	    }
2054 	    if (flag)			/* Not digit or period */
2055 	      goto done;		/* break if flag != 0 */
2056 	    return(0);			/* otherwise fail. */
2057 	  case 1:			/* Fraction... */
2058 	    if (isdigit(c)) {
2059 		d *= 10.0;
2060 		f += (CKFLOAT)(c - '0') / d;
2061 		continue;
2062 	    }
2063 	  default:
2064 	    if (flag)			/* Illegal character */
2065 	      goto done;		/* Break */
2066 	    return(0);			/* or fail, depending on flag */
2067 	}
2068     }
2069   done:
2070     if (sign) f = 0.0 - f;		/* Apply sign to result */
2071     floatval = f;			/* Set result */
2072     return(d ? 2 : 1);			/* Succeed */
2073 }
2074 
2075 /*
2076   c k r o u n d  --  Rounds a floating point number or an integer.
2077 
2078   fpnum:
2079     Floating-point number to round.
2080   places:
2081     Positive...To how many decimal places.
2082     Zero.......Round to integer.
2083     Negative...-1 = nearest ten, -2 = nearest 100, -3 = nearest thousand, etc.
2084   obuf
2085     Output buffer for string result if desired.
2086   obuflen
2087     Length of output buffer.
2088   Returns:
2089     Result as CKFLOAT (which is not going to be as exact as the string result)
2090     And the exact result in the string output buffer, if one was specified.
2091 */
2092 CKFLOAT
2093 #ifdef CK_ANSIC
ckround(CKFLOAT fpnum,int places,char * obuf,int obuflen)2094 ckround(CKFLOAT fpnum, int places, char *obuf, int obuflen)
2095 #else
2096 ckround(fpnum,places,obuf,obuflen)
2097  CKFLOAT fpnum; int places, obuflen; char *obuf;
2098 #endif /* CK_ANSIC */
2099 /* ckround  */ {
2100     char *s, *s2, *d;
2101     int i, p, len, x, n, digits;
2102     int carry = 0;
2103     int minus = 0;
2104     char buf[400];
2105     char * number;
2106     CKFLOAT value;
2107     extern int fp_digits;
2108 
2109     /* Should use snprintf() here but it's not portable */
2110 
2111     sprintf(buf,"%200.100f",fpnum);	/* Make string version to work with. */
2112     debug(F110,"ckround buf",buf,0);
2113     number = (char *) buf;		/* Make pointer to it */
2114 
2115     p = places;				/* Precision */
2116     d = (char *)0;			/* Pointer to decimal or string end */
2117 
2118     s = number;				/* Fix number... */
2119     while (*s == ' ' || *s == '\011') s++; /* Strip leading whitespace */
2120     if (*s == '+') s++;			   /* Skip leading plus sign*/
2121     number = s;				   /* Start of number */
2122     if (*s == '-') { minus++; number++; s++; } /* Remember if negative */
2123 
2124     s = number;				/* Don't allow false precision */
2125     n = 0;
2126     while (*s && *s != '.') s++, n++;   /* Find decimal */
2127 
2128     if (p + n > fp_digits)		/* Too many digits */
2129       p = fp_digits - n;		/* Don't ask for bogus precision */
2130     if (p < 0) p = 0;			/* But don't ask for less than zero */
2131     if (n > fp_digits)			/* Integer part has too many digits */
2132       *s = 0;				/* but we can't truncate it */
2133     else				/* Magnitude is OK */
2134       number[fp_digits+1] = 0;		/* Truncate fractional part. */
2135 
2136     len = (int)strlen(number);		/* Length of non-bogus number */
2137     d = s;				/* Pointer to decimal point */
2138     if (p > 0) {			/* Rounding the fractional part */
2139 	if (n + p < len) {		/* If it's not already shorter */
2140 	    if (*s == '.') s++;		/* Skip past decimal */
2141 	    s += p;			/* Go to desired spot */
2142 	    if (*s > '4' && *s <= '9')	/* Check value of digit */
2143 	      carry = 1;
2144 	    *s = 0;			/* And end the string */
2145 	    s--;			/* Point to last digit */
2146 	}
2147     } else if (p == 0) {		/* Rounding to integer */
2148 	if (*s == '.') {
2149 	    *s = 0;			/* erase the decimal point */
2150 	    if (*(s+1)) {		/* and there is a factional part */
2151 		if (*(s+1) > '4' && *(s+1) <= '9') /* Check for carry */
2152 		  carry = 1;
2153 	    }
2154 	    s--;			/* Point to last digit */
2155 	}
2156     } else {				/* Rounding the integer part */
2157 	if (s + p > number) {		/* as in "the nearest hundred" */
2158 	    s += p;			/* Go left to desired digit */
2159 	    *d = 0;			/* Discard fraction */
2160 	    carry = 0;
2161 	    if (*s > '4')		/* Check first digit of fraction */
2162 	      carry = 1;		/* and set carry flag */
2163 	    s2 = s;
2164 	    while (s2 < d)		/* Fill in the rest with zeros */
2165 	      *s2++ = '0';
2166 	    s--;			/* Point to last digit */
2167 	}
2168     }
2169     if (carry) {			/* Handle carry, if any */
2170         while (s >= number) {
2171             if (*s == '.') {		/* Skip backwards over decimal */
2172                 s--;
2173                 continue;
2174             }
2175             *s += 1;			/* Add 1 to current digit */
2176             carry = 0;
2177             if (*s <= '9') 		/* If result is 9 or less */
2178 	      break;			/* we're done */
2179             *s = '0';			/* Otherwise put 0 */
2180             carry = 1;			/* carry the 1 */
2181             s--;			/* and back up to next digit */
2182 	}
2183     }
2184     if (minus) number--;                /* Back up to minus sign, if any. */
2185 #ifdef __alpha
2186     sscanf(number,"%f",&value);		/* Convert back to floating point */
2187 #else
2188     sscanf(number,"%lf",&value);        /* Convert back to floating point */
2189 #endif
2190     if (obuf) strncpy(obuf,number,obuflen); /* Set string result */
2191     return(value);			    /* Return floating-point result */
2192 }
2193 
2194 #endif /* CKFLOAT */
2195 
2196 /* Sorting routines... */
2197 
2198 /* S H _ S O R T  --  Shell sort -- sorts string array s in place. */
2199 
2200 /*
2201   Highly defensive and relatively quick.
2202   Uses shell sort algorithm.
2203 
2204   Args:
2205    s = pointer to array of strings.
2206    p = pointer to a second array to sort in parallel s, or NULL for none.
2207    n = number of elements in s.
2208    k = position of key.
2209    r = ascending lexical order if zero, reverse lexical order if nonzero.
2210    c = 0 for case independence, 1 for case matters, 2 for numeric.
2211 
2212   If k is past the right end of a string, the string is considered empty
2213   for comparison purposes.
2214 
2215   Hint:
2216    To sort a piece of an array, call with s pointing to the first element
2217    and n the number of elements to sort.
2218 
2219   Return value:
2220    None.  Always succeeds, unless any of s[0]..s[n-1] are bad pointers,
2221    in which case memory violations are possible, but C offers no defense
2222    against this, so no way to gracefully return an error code.
2223 */
2224 VOID
sh_sort(s,p,n,k,r,c)2225 sh_sort(s,p,n,k,r,c) char **s, **p; int n, k, r, c; {
2226     int m, i, j, x;
2227     char *t, *t1, *t2, *u = NULL;
2228 #ifdef CKFLOAT
2229     CKFLOAT f1, f2;
2230 #else
2231     long n1, n2;
2232 #endif /* CKFLOAT */
2233 
2234     if (!s) return;			/* Nothing to sort? */
2235     if (n < 2) return;			/* Not enough elements to sort? */
2236     if (k < 0) k = 0;			/* Key */
2237 
2238     m = n;				/* Initial group size is whole array */
2239     while (1) {
2240 	m = m / 2;			/* Divide group size in half */
2241 	if (m < 1)			/* Small as can be, so done */
2242 	  break;
2243 	for (j = 0; j < n-m; j++) {	/* Sort each group */
2244 	    t = t2 = s[j+m];		/* Compare this one... */
2245 	    if (!t)			/* But if it's NULL */
2246 	      t2 = "";			/* make it the empty string */
2247 	    if (p)			/* Handle parallel array, if any */
2248 	      u = p[j+m];
2249 	    if (k > 0 && *t2) {
2250 		if ((int)strlen(t2) < k) /* If key too big */
2251 		  t2 = "";		/* make key the empty string */
2252 		else			/* Key is in string */
2253 		  t2 = t + k;		/* so point to key position */
2254 	    }
2255 	    for (i = j; i >= 0; i -= m) { /* Loop thru comparands s[i..]*/
2256 		t1 = s[i];
2257 		if (!t1)		/* Same deal */
2258 		  t1 = "";
2259 		if (k > 0 && *t1) {
2260 		    if ((int)strlen(t1) < k)
2261 		      t1 = "";
2262 		    else
2263 		      t1 = s[i]+k;
2264 		}
2265 		if (c == 2) {		/* Numeric comparison */
2266 		    x = 0;
2267 #ifdef CKFLOAT
2268 		    f2 = 0.0;
2269 		    f1 = 0.0;
2270 		    if (isfloat(t1,1)) {
2271 			f1 = floatval;
2272 			if (isfloat(t2,1))
2273 			  f2 = floatval;
2274 			else
2275 			  f1 = 0.0;
2276 		    }
2277 		    if (f2 < f1)
2278 		      x = 1;
2279 		    else
2280 		      x = -1;
2281 #else
2282 		    n2 = 0L;
2283 		    n1 = 0L;
2284 		    if (rdigits(t1)) {
2285 			n1 = atol(t1);
2286 			if (rdigits(t2))
2287 			  n2 = atol(t2);
2288 			else
2289 			  n1 = 0L;
2290 		    }
2291 		    if (n2 < n1)
2292 		      x = 1;
2293 		    else
2294 		      x = -1;
2295 #endif /* CKFLOAT */
2296 		} else {
2297 		    x = ckstrcmp(t1,t2,-1,c); /* Compare */
2298 		}
2299 		if (r == 0 && x < 0)
2300 		  break;
2301 		if (r != 0 && x > 0)
2302 		  break;
2303 		s[i+m] = s[i];
2304 		if (p) p[i+m] = p[i];
2305 	    }
2306 	    s[i+m] = t;
2307 	    if (p) p[i+m] = u;
2308 	}
2309     }
2310 }
2311 
2312 
2313 /* C K R A D I X  --  Radix converter */
2314 /*
2315    Call with:
2316      s:   a number in string format.
2317      in:  int, specifying the radix of s, 2-36.
2318      out: int, specifying the radix to convert to, 2-36.
2319    Returns:
2320      NULL on error (illegal radix, illegal number, etc.).
2321      "-1" on overflow (number too big for unsigned long).
2322      Otherwise: Pointer to result.
2323 */
2324 char *
ckradix(s,in,out)2325 ckradix(s,in,out) char * s; int in, out; {
2326     char c, *r = rxresult;
2327     int d, minus = 0;
2328 #ifdef COMMENT
2329     unsigned long zz = 0L;
2330     long z = 0L;
2331 #else
2332     /*
2333       To get 64 bits on 32-bit hardware we use off_t, but there
2334       is no unsigned version of off_t, so we lose the ability to
2335       detect overflow.
2336     */
2337     CK_OFF_T zz = (CK_OFF_T)0;
2338     CK_OFF_T z = (CK_OFF_T)0;
2339 #endif	/* COMMENT */
2340 
2341     if (in < 2 || in > 36)		/* Verify legal input radix */
2342       return(NULL);
2343     if (out < 2 || out > 36)		/* and output radix. */
2344       return(NULL);
2345     if (*s == '+') {			/* Get sign if any */
2346 	s++;
2347     } else if (*s == '-') {
2348 	minus++;
2349 	s++;
2350     }
2351     while (*s == SP || *s == '0')	/* Trim leading blanks or 0's */
2352       s++;
2353 /*
2354   For detecting overflow, we use a signed copy of the unsigned long
2355   accumulator.  If it goes negative, we know we'll overflow NEXT time
2356   through the loop.
2357 */
2358     for (; *s;  s++) {			/* Convert from input radix to */
2359 	c = *s;				/* unsigned long */
2360 	if (islower(c)) c = toupper(c);
2361 	if (c >= '0' && c <= '9')
2362 	  d = c - '0';
2363 	else if (c >= 'A' && c <= 'Z')
2364 	  d = c - 'A' + 10;
2365 	else
2366 	  return(NULL);
2367 	if (d >= in)			/* Check for illegal digit */
2368 	  return(NULL);
2369 	zz = zz * in + d;
2370 	if (z < 0L)			/* Clever(?) overflow detector */
2371 	  return("-1");
2372         z = zz;
2373     }
2374     if (!zz) return("0");
2375     r = &rxresult[RXRESULT];		/* Convert from unsigned long */
2376     *r-- = NUL;				/* to output radix. */
2377     while (zz > 0 && r > rxresult) {
2378 	d = zz % (unsigned)out;
2379 	*r-- = rxdigits[d];
2380 	zz = zz / (unsigned)out;
2381     }
2382     if (minus) *r-- = '-';		/* Replace original sign */
2383     return((char *)(r+1));
2384 }
2385 
2386 #ifndef NOB64
2387 /* Base-64 conversion routines */
2388 
2389 static char b64[] = {			/* Encoding vector */
2390 #ifdef pdp11
2391   "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
2392 #else
2393   'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S',
2394   'T','U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i','j','k','l',
2395   'm','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4',
2396   '5','6','7','8','9','+','/','=','\0'
2397 #endif /* pdp11 */
2398 };
2399 static int b64tbl[] = {			/* Decoding vector */
2400     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
2401     -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
2402     -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
2403     52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1,
2404     -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
2405     15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
2406     -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2407     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
2408     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2409     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2410     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2411     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2412     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2413     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2414     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2415     -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
2416 };
2417 
2418 /*
2419    B 8 T O B 6 4  --  Converts 8-bit data to Base64 encoding.
2420 
2421    Call with:
2422      s   = Pointer to 8-bit data;
2423      n   = Number of source bytes to encode (SEE NOTE).
2424            If it's a null-terminated string, you can use -1 here.
2425      out = Address of output buffer.
2426      len = Length of output buffer (should > 4/3 longer than input).
2427 
2428    Returns:
2429      >= 0 if OK, number of bytes placed in output buffer,
2430           with the subsequent byte set to NUL if space permits.
2431      -1 on error (output buffer space exhausted).
2432 
2433    NOTE:
2434      If this function is to be called repeatedly, e.g. to encode a data
2435      stream a chunk at a time, the source length must be a multiple of 3
2436      in all calls but the final one to avoid the generation of extraneous
2437      pad characters that would throw the decoder out of sync.  When encoding
2438      only a single string, this is not a consideration.  No internal state
2439      is kept, so there is no reset function.
2440 */
2441 int
b8tob64(s,n,out,len)2442 b8tob64(s,n,out,len) char * s,* out; int n, len; {
2443     int b3, b4, i, x = 0;
2444     unsigned int t;
2445 
2446     if (n < 0) n = strlen(s);
2447 
2448     for (i = 0; i < n; i += 3,x += 4) { /* Loop through source bytes */
2449 	b3 = b4 = 0;
2450 	t = (unsigned)((unsigned)((unsigned int)s[i] & 0xff) << 8);
2451 	if (n - 1 > i) {		/* Do we have another after this? */
2452             t |= (unsigned)(s[i+1] & 0xff); /* Yes, OR it in */
2453             b3 = 1;			/* And remember */
2454         }
2455         t <<= 8;			/* Move over */
2456         if (n - 2 > i) {		/* Another one after that? */
2457             t |= (unsigned)(s[i+2] & 0xff); /* Yes, OR it in */
2458             b4 = 1;			/* and remember */
2459         }
2460 	if (x + 4 > len)		/* Check output space */
2461 	  return(-1);
2462 	out[x+3] = b64[b4 ? (t & 0x3f) : 64]; /* 64 = code for '=' */
2463         t >>= 6;
2464         out[x+2] = b64[b3 ? (t & 0x3f) : 64];
2465         t >>= 6;
2466         out[x+1] = b64[t & 0x3f];
2467         t >>= 6;
2468         out[x]   = b64[t & 0x3f];
2469     }
2470     if (x < len) out[x] = NUL;		/* Null-terminate the string */
2471     return(x);
2472 }
2473 
2474 
2475 /*
2476    B 6 4 T O B 8  --  Converts Base64 string to 8-bit data.
2477 
2478    Call with:
2479      s   = pointer to Base64 string (whitespace ignored).
2480      n   = length of string, or -1 if null terminated, or 0 to reset.
2481      out = address of output buffer.
2482      len = length of output buffer.
2483 
2484    Returns:
2485      >= 0 if OK, number of bytes placed in output buffer,
2486           with the subsequent byte set to NUL if space permits.
2487      <  0 on error:
2488        -1 = output buffer too small for input.
2489        -2 = input contains illegal characters.
2490        -3 = internal coding error.
2491 
2492    NOTE:
2493      Can be called repeatedly to decode a Base64 stream, one chunk at a
2494      time.  However, if it is to be called for multiple streams in
2495      succession, its internal state must be reset at the beginning of
2496      the new stream.
2497 */
2498 int
b64tob8(s,n,out,len)2499 b64tob8(s,n,out,len) char * s,* out; int n, len; { /* Decode */
2500     static int bits = 0;
2501     static unsigned int r = 0;
2502     int i, k = 0, x, t;
2503     unsigned char c;
2504 
2505     if (n == 0) {			/* Reset state */
2506 	bits = 0;
2507 	r = 0;
2508 	return(0);
2509     }
2510     x = (n < 0) ? strlen(s) : n;	/* Source length */
2511 
2512     n = ((x + 3) / 4) * 3;		/* Compute destination length */
2513     if (x > 0 && s[x-1] == '=') n--;	/* Account for padding */
2514     if (x > 1 && s[x-2] == '=') n--;
2515     if (n > len)			/* Destination not big enough */
2516       return(-1);			/* Fail */
2517 
2518     for (i = 0; i < x; i++) {		/* Loop thru source */
2519 	c = (CHAR)s[i];			/* Next char */
2520         t = b64tbl[c];			/* Code for this char */
2521 	if (t == -2) {			/* Whitespace or Ctrl */
2522 	    n--;			/* Ignore */
2523 	    continue;
2524 	} else if (t == -1) {		/* Illegal code */
2525 	    return(-2);			/* Fail. */
2526 	} else if (t > 63 || t < 0)	/* Illegal value */
2527 	  return(-3);			/* fail. */
2528 	bits += 6;			/* Count bits */
2529 	r <<= 6;			/* Make space */
2530 	r |= (unsigned) t;		/* OR in new code */
2531 	if (bits >= 8) {		/* Have a byte yet? */
2532 	    bits -= 8;			/* Output it */
2533 	    c = (unsigned) ((r >> bits) & 0xff);
2534 	    out[k++] = c;
2535 	}
2536     }
2537     if (k < len) out[k] = NUL;		/* Null-terminate in case it's */
2538     return(k);				/* a text string */
2539 }
2540 #endif /* NOB64 */
2541 
2542 /* C H K N U M  --  See if argument string is an integer  */
2543 
2544 /* Returns 1 if OK, zero if not OK */
2545 /* If OK, string should be acceptable to atoi() or atol() or ckatofs() */
2546 /* Allows leading space, sign */
2547 
2548 int
chknum(s)2549 chknum(s) char *s; {			/* Check Numeric String */
2550     int x = 0;				/* Flag for past leading space */
2551     int y = 0;				/* Flag for digit seen */
2552     char c;
2553     debug(F110,"chknum",s,0);
2554     if (!s) return(0);
2555     if (!*s) return(0);
2556     while ((c = *s++)) {		/* For each character in the string */
2557 	switch (c) {
2558 	  case SP:			/* Allow leading spaces */
2559 	  case HT:
2560 	    if (x == 0) continue;
2561 	    else return(0);
2562 	  case '+':			/* Allow leading sign */
2563 	  case '-':
2564 	    if (x == 0) x = 1;
2565 	    else return(0);
2566 	    break;
2567 	  default:			/* After that, only decimal digits */
2568 	    if (c >= '0' && c <= '9') {
2569 		x = y = 1;
2570 		continue;
2571 	    } else return(0);
2572 	}
2573     }
2574     return(y);
2575 }
2576 
2577 
2578 /*  R D I G I T S  -- Verify that all characters in arg ARE DIGITS  */
2579 
2580 /*  Returns 1 if so, 0 if not or if string is empty */
2581 
2582 int
rdigits(s)2583 rdigits(s) char *s; {
2584     if (!s) return(0);
2585     do {
2586         if (!isdigit(*s)) return(0);
2587         s++;
2588     } while (*s);
2589     return(1);
2590 }
2591 
2592 /*  P A R N A M  --  Return parity name */
2593 
2594 char *
2595 #ifdef CK_ANSIC
parnam(char c)2596 parnam(char c)
2597 #else
2598 parnam(c) char c;
2599 #endif /* CK_ANSIC */
2600 /* parnam */ {
2601     switch (c) {
2602 	case 'e': return("even");
2603 	case 'o': return("odd");
2604 	case 'm': return("mark");
2605 	case 's': return("space");
2606 	case 0:   return("none");
2607 	default:  return("invalid");
2608     }
2609 }
2610 
2611 char *					/* Convert seconds to hh:mm:ss */
2612 #ifdef CK_ANSIC
hhmmss(long x)2613 hhmmss(long x)
2614 #else
2615 hhmmss(x) long x;
2616 #endif /* CK_ANSIC */
2617 /* hhmmss(x) */ {
2618     static char buf[60];
2619     long s, h, m;
2620     h = x / 3600L;			/* Hours */
2621     x = x % 3600L;
2622     m = x / 60L;			/* Minutes */
2623     s = x % 60L;			/* Seconds */
2624     if (x > -1L)
2625       sprintf(buf,"%02ld:%02ld:%02ld",h,m,s);
2626     else
2627       buf[0] = NUL;
2628     return((char *)buf);
2629 }
2630 
2631 /* L S E T  --  Set s into p, right padding to length n with char c; */
2632 /*
2633    s is a NUL-terminated string.
2634    If length(s) > n, only n bytes are moved.
2635    The result is NOT NUL terminated unless c == NUL and length(s) < n.
2636    The intended of this routine is for filling in fixed-length record fields.
2637 */
2638 VOID
lset(p,s,n,c)2639 lset(p,s,n,c) char *s; char *p; int n; int c; {
2640     int x;
2641 #ifndef USE_MEMCPY
2642     int i;
2643 #endif /* USE_MEMCPY */
2644     if (!s) s = "";
2645     x = strlen(s);
2646     if (x > n) x = n;
2647 #ifdef USE_MEMCPY
2648     memcpy(p,s,x);
2649     if (n > x)
2650       memset(p+x,c,n-x);
2651 #else
2652     for (i = 0; i < x; i++)
2653       *p++ = *s++;
2654     for (; i < n; i++)
2655       *p++ = c;
2656 #endif /* USE_MEMCPY */
2657 }
2658 
2659 /* R S E T  --  Right-adjust s in p, left padding to length n with char c */
2660 
2661 VOID
rset(p,s,n,c)2662 rset(p,s,n,c) char *s; char *p; int n; int c; {
2663     int x;
2664 #ifndef USE_MEMCPY
2665     int i;
2666 #endif /* USE_MEMCPY */
2667     if (!s) s = "";
2668     x = strlen(s);
2669     if (x > n) x = n;
2670 #ifdef USE_MEMCPY
2671     memset(p,c,n-x);
2672     memcpy(p+n-x,s,x);
2673 #else
2674     for (i = 0; i < (n - x); i++)
2675       *p++ = c;
2676     for (; i < n; i++)
2677       *p++ = *s++;
2678 #endif /* USE_MEMCPY */
2679 }
2680 
2681 /*  U L O N G T O H E X  --  Unsigned long to hex  */
2682 
2683 /*
2684   Converts unsigned long arg to hex and returns string pointer to
2685   rightmost n hex digits left padded with 0's.  Allows for longs
2686   up to 64 bits.  Returns pointer to result.
2687 */
2688 char *
2689 #ifdef CK_ANSIC
ulongtohex(unsigned long z,int n)2690 ulongtohex( unsigned long z, int n )
2691 #else
2692 ulongtohex(z,n) unsigned long z; int n;
2693 #endif	/* CK_ANSIC */
2694 /* ulongtohex */ {
2695     static char hexbuf[17];
2696     int i = 16, x, k = 0;
2697     hexbuf[16] = '\0';
2698     if (n > 16) n = 16;
2699     k = 2 * (sizeof(long));
2700     for (i = 0; i < n; i++) {
2701 	if (i > k || z == 0) {
2702 	    hexbuf[15-i] = '0';
2703 	} else {
2704 	    x = z & 0x0f;
2705 	    z = z >> 4;
2706 	    hexbuf[15-i] = x + ((x < 10) ? '0' : 0x37);
2707 	}
2708     }
2709     return((char *)(&hexbuf[16-i]));
2710 }
2711 
2712 /*  H E X T O U L O N G  --  Hex string to unsigned long  */
2713 
2714 /*
2715   Converts n chars from s from hex to unsigned long.
2716   Returns:
2717    0L or positive, good result (0L is returned if arg is NULL or empty).
2718   -1L on error: non-hex arg or overflow.
2719 */
2720 long
hextoulong(s,n)2721 hextoulong(s,n) char *s; int n; {
2722     char buf[64];
2723     unsigned long result = 0L;
2724     int d, count = 0;
2725     int flag = 0;
2726     if (!s) s = "";
2727     if (!*s) {
2728 	return(0L);
2729     }
2730     if (n < 1)
2731       return(0L);
2732     if (n > 63) n = 63;
2733     strncpy(buf,s,n);
2734     buf[n] = '\0';
2735     s = buf;
2736     while (*s) {
2737 	d = *s++;
2738 	if ((d == '0' || d == ' ')) {
2739 	    if (!flag)
2740 	      continue;
2741 	} else {
2742 	    flag = 1;
2743 	}
2744 	if (islower(d))
2745 	  d = toupper(d);
2746 	if (d >= '0' && d <= '9') {
2747 	    d -= 0x30;
2748 	} else if (d >= 'A' && d <= 'F') {
2749 	    d -= 0x37;
2750 	} else {
2751 	    return(-1L);
2752 	}
2753 	if (++count > (sizeof(long) * 2))
2754 	  return(-1L);
2755 	result = (result << 4) | (d & 0x0f);
2756     }
2757     return(result);
2758 }
2759 
2760 /*
2761   c k s p l i t  --  Splits a string into words, or:
2762                      extracts a given word from a string.
2763 
2764   Allows for grouping.
2765   Operates on a copy of the string; does not alter the original string.
2766   All strings NUL-terminated.
2767 
2768   Call with:
2769     fc = function code:
2770          1 = split, 0 = word.
2771     n1 = desired word number if fc == 0.
2772     s1 = source string.
2773     s2 = break string (NULL to accept default = all non-alphanum).
2774     s3 = include string (NULL to accept default = all alphanum).
2775     n2 = grouping mask (OR desired ones together):
2776          1 = doublequotes,  2 = braces,    4 = apostrophes,
2777          8 = parens,       16 = brackets, 32 = angle brackets,
2778         -1 = 63 = all of these.
2779     n3 = if positive: group quote character, ASCII value,
2780          used and tested only for LISP quote, e.g. (a 'b c '(d e f)).
2781     n4 = 0 to collapse adjacent separators;
2782          nonzero not to collapse them.
2783     n5 - nonzero means '\' is not a quoting character.
2784 
2785   Returns:
2786     Pointer to struct stringarray, with size:
2787       -1 = memory allocation error.
2788       -2 = too many words in string.
2789        n = number of words (0 or more).
2790 
2791   With:
2792     wordarray = array of pointers to n words (n == 1 if fc == 0), 1-based.
2793     Each pointer is to a malloc'd string.  This array is recycled upon each
2794     call; if you need to keep the strings, make copies of them.  This routine
2795     must have control of allocation and deallocation.
2796 
2797   If a given character is included in the include list, it is not treated
2798   as a separator or as a grouping character.
2799 
2800   Groups may be nested only if they are formed with braces, parens, or
2801   brackets, but not with quotes or apostrophes since ASCII quotes have no
2802   intrinsic handedness.  Group-start and end characters are treated as
2803   separators even in the absence of other separators, so a string such as
2804   "a{b}c" results in three words, not one.
2805 
2806   Sample call to split a string into an array:
2807     struct stringarray * q;
2808     q = cksplit(1,0,s1,s2,s3,-1,0);
2809         q->a_size = size (>=0) or failure code (<0)
2810         q->a_head = pointer to array (elements 0 thru q->asize - 1).
2811 
2812   Sample call to extract word n from a string:
2813     struct stringarray * q;
2814     q = cksplit(0,n,s1,s2,s3,-1,0);
2815         q->a_size = size (1) or failure code (<0)
2816         q->a_head = pointer to array (element 1 is the desired word).
2817 */
2818 
2819 /* States */
2820 
2821 #define ST_BW  0			/* Between Words */
2822 #define ST_IW  1			/* In Word */
2823 #define ST_IG  2			/* Start Group */
2824 
2825 /* Character Classes (bitmap) */
2826 
2827 #define CL_SEP 1			/* Separator */
2828 #define CL_OPN 2			/* Group Open */
2829 #define CL_CLS 4			/* Group Close */
2830 #define CL_DAT 8			/* Data */
2831 #define CL_QUO 16			/* Group quote */
2832 
2833 #ifdef BIGBUFOK
2834 #ifndef MAXWORDS
2835 #define MAXWORDS 4096			/* Max number of words */
2836 #endif /* MAXWORDS */
2837 #ifndef NESTMAX
2838 #define NESTMAX 64			/* Maximum nesting level */
2839 #endif /* NESTMAX */
2840 #else
2841 #ifndef MAXWORDS
2842 #define MAXWORDS 128			/* Max number of words */
2843 #endif /* MAXWORDS */
2844 #ifndef NESTMAX
2845 #define NESTMAX 16			/* Maximum nesting level */
2846 #endif /* NESTMAX */
2847 #endif /* BIGBUFOK */
2848 
2849 /* static */ char ** wordarray = NULL;	/* Result array of word pointers */
2850 
2851 static struct stringarray ck_sval =  {	/* Return value structure */
2852     NULL,				/* Pointer to array */
2853     0					/* Size */
2854 };
2855 static int * wordsize = NULL;
2856 
2857 static VOID
setword(n,s,len)2858 setword(n,s,len) int n, len; char * s; {
2859     register char * p;
2860     register int i, k;
2861 
2862     if (!s) s = "";
2863 
2864     if (!wordarray) {			/* Allocate result array (only once) */
2865 	if (!(wordarray = (char **)malloc((MAXWORDS+1) * sizeof(char *))))
2866 	  return;
2867 	if (!(wordsize = (int *)malloc((MAXWORDS+1) * sizeof(int))))
2868 	  return;
2869 	for (i = 0; i <= MAXWORDS; i++)	{ /* Initialize result array */
2870 	    wordarray[i] = NULL;
2871 	    wordsize[i] = 0;
2872 	}
2873     }
2874     if (wordsize[n] < len /* || !wordarray[n] */ ) {
2875 	k = (len < 16) ? 16 : len + (len / 4);
2876 	wordarray[n] = (char *) malloc(k+1);
2877 	wordsize[n] = (wordarray[n]) ? k : 0;
2878 	if (wordarray[n]) {
2879 	    p = wordarray[n];
2880 	    while ((*p++ = *s++) && k-- > 0) ;
2881 	}
2882     } else if (len > 0) {
2883 	k = wordsize[n];		/* (In case len arg is a lie) */
2884 	p = wordarray[n];
2885 	while ((*p++ = *s++) && k-- > 0) {
2886 #ifdef COMMENT
2887 	    if ((*(s-1) == CMDQ) && *s != CMDQ) {
2888 		k++;
2889 		p--;
2890 	    }
2891 #endif /* COMMENT */
2892 	}
2893     } else if (wordarray[n]) {
2894 	wordarray[n][0] = NUL;
2895     }
2896 }
2897 
2898 static char * splitbuf = NULL;
2899 static int nsplitbuf = 0;
2900 
2901 /* n4 = 1 to NOT collapse adjacent separators */
2902 
2903 struct stringarray *
cksplit(fc,n1,s1,s2,s3,n2,n3,n4,n5)2904 cksplit(fc,n1,s1,s2,s3,n2,n3,n4,n5)
2905     int fc,n1,n2,n3,n4,n5; char *s1, *s2, *s3; {
2906     int splitting = 0;			/* What I was asked to do */
2907     int i, k, ko = 0, n, x, max = MAXWORDS; /* Workers */
2908     char * s = NULL, * ss, * p;		/* Workers */
2909     char * sep = "";			/* Default break set */
2910     char * notsep = "";			/* Default include set */
2911     int    grouping = 0;		/* Grouping option */
2912     char * gr_opn = "\"{'([<";		/* Group open brackets */
2913     char * gr_cls = "\"}')]>";		/* Group close brackets */
2914     int    gr_stk[NESTMAX];		/* Nesting bracket stack */
2915     int    gr_lvl = 0;			/* Nesting level */
2916     int    wordnum = 0;			/* Current word number */
2917     CHAR   c = 'A';			/* Current char (dummy start value) */
2918     int    class = 0;			/* Current character class */
2919     int    state = ST_BW;		/* Current FSA state */
2920     int    len = 0;			/* Length of current word */
2921     int    slen = 0;			/* Length of s1 */
2922     int    gquote = 0;			/* Quoted group */
2923     int    cquote = 0;			/* Quoted character */
2924     int    collapse = 1;		/* Collapse adjacent separators */
2925     int    all = 0;			/* s3 == ALL */
2926     int    csv = 0;			/* s3 == CSV */
2927     int    tsv = 0;			/* s3 == TSV */
2928     int    prevstate = -1;
2929 
2930     unsigned int hex80 = 128;
2931     unsigned int hexff = 255;
2932     CHAR notsepbuf[256];
2933 
2934     notsepbuf[0] = NUL;			/* Keep set for "ALL" */
2935     if (n4) collapse = 0;		/* Don't collapse */
2936 
2937     for (i = 0; i < NESTMAX; i++)	/* Initialize nesting stack */
2938       gr_stk[i] = 0;
2939 
2940     setword(1,NULL,0);
2941     ck_sval.a_head = wordarray;		/* Initialize return value struct */
2942     ck_sval.a_size = 0;
2943 
2944     if (!s1) s1 = "";			/* s1 = source string */
2945     if (!*s1) {				/* If none, nothing to do */
2946 	return(&ck_sval);
2947     }
2948     splitting = fc;			/* Our job */
2949     if (splitting) {			/* If splitting n = word count */
2950 	n = 0;				/* Initialize it */
2951     } else {				/* Otherwise */
2952 	if (n1 == 0) {			/* If 0 */
2953 	    ck_sval.a_size = 0;		/* nothing to do. */
2954 	    return(&ck_sval);
2955 	}
2956 	n = n1;				/* n = desired word number. */
2957     }
2958     slen = 0;				/* Get length of s1 */
2959     debug(F111,"cksplit",s1,n);
2960 
2961     p = s1;
2962 /*
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.
2968 */
2969     p = s1;
2970     s = splitbuf;
2971     if (splitbuf) {			/* Make pokeable copy of s1 */
2972 	while ((*s++ = *p++) && (slen++ < nsplitbuf)) /* Try to copy */
2973 	  ;
2974     }
2975     if (!splitbuf || slen >= nsplitbuf) { /* Need to do more... */
2976 	int xx;
2977 	if (splitbuf)			/* Free previous buf if any */
2978 	  free(splitbuf);
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;
2984 	    return(&ck_sval);
2985 	}
2986 	nsplitbuf = xx;			/* Remember (new) buffer size */
2987 	s = splitbuf;
2988 	p = s1;
2989 	while ((*s++ = *p++)) ;
2990     }
2991     s = splitbuf;
2992     sep = s2;				/* s2 = break set */
2993     if (!sep) sep = "";
2994     debug(F110,"cksplit sep",sep,0);
2995     notsep = s3;			/* s3 = include set */
2996     debug(F110,"cksplit notsep",notsep,0);
2997     if (!notsep) {
2998 	notsep = "";
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;
3003 	int n = 0;
3004 	char * ss = sep;
3005 	if (!all && (csv || tsv)) {
3006 	    all = 1;
3007 	    collapse = 0;
3008 	}
3009         debug(F101,"cksplit csv","",csv);
3010         debug(F101,"cksplit tsv","",tsv);
3011         debug(F101,"cksplit all","",all);
3012         debug(F110,"cksplit ss sep",ss,0);
3013 	for (i = 1; i < 256; i++) {
3014 	    flag = 0;
3015 	    ss = sep;
3016 	    while ((c = *ss++) && !flag) {
3017 		if (c == (CHAR)i) flag++;
3018 	    }
3019 	    if (!flag) {
3020                 notsepbuf[n++] = (CHAR)i;
3021             }
3022 	}
3023 	notsepbuf[n] = NUL;
3024 	notsep = (char *)notsepbuf;
3025 	debug(F110,"CKMATCH SEPBUF ALL",sep,0);
3026 	debug(F110,"CKMATCH NOTSEPBUF ALL",notsep,0);
3027     }
3028     if (*s && csv) {			/* For CSV skip leading whitespace */
3029         while (*s == SP || *s == HT)
3030 	  s++;
3031 	c = *s;
3032     }
3033     if (n2 == 0 && csv) n2 = 1;		/* CSV implies doublequote grouping */
3034     if (n2 < 0) n2 = 63;		/* n2 = grouping mask */
3035     grouping = n2;
3036     p = "";				/* Pointer to current word */
3037 
3038     /* TSV is a special case - no quoting or grouping or recursion */
3039     if (tsv) {				/* Tab-separated values list */
3040 	/* This block: 2014/01/31 - fdc */
3041         if (!*sep) sep = "\011";	/* Default separator is Tab */
3042 	p = s;				/* Point to first element */
3043     }
3044     while (c) {				/* Loop through string */
3045 	c = *s;			  	/* Get next char */
3046 	if (tsv) {			/* Tab-separated-value list? */
3047 	    /* This block: 2014/01/31 - fdc */
3048 	    ss = sep;			/* Get break set */
3049 	    while (*ss && *ss != c) ss++; /* See if c is in it */
3050 	    if (*ss == c) {		/* Is this the/a break character? */
3051 		*s = NUL;		/* Yes, terminate this word */
3052 		wordnum++;		/* Count it */
3053 		if (splitting) {	/* fsplit().... */
3054 		    setword(wordnum,p,len); /* Add a new element */
3055 		    p = s+1;		/* Point to beginning of next */
3056 		    len = 0;		/* and reset the length */
3057 		} else if (wordnum == n) { /* fword() counting from left... */
3058 		    setword(1,p+1,len);
3059 		    ck_sval.a_size = 1;
3060 		    return(&ck_sval);
3061 		} else 	if (n < 0 && (wordnum + n > -1)) { /* or from right */
3062 		    char * s = wordarray[wordnum + n + 1];
3063 		    if (!s) s = "";
3064 		    setword(1,s,strlen(s));
3065 		    ck_sval.a_size = 1;
3066 		    return(&ck_sval);
3067 		}
3068 	    } else len++;
3069 	    goto nextc;
3070 	}
3071 	class = 0;
3072 	if (!csv && !tsv && !n5) {      /* fdc 2010-12-30..2017-04-26 */
3073 	    /* In CSV and TSV splitting, backslash is not special */
3074 	    if (!cquote && c == CMDQ) {	/* If CMDQ */
3075 		cquote++;		/* next one is quoted */
3076 		goto nextc;		/* go get it */
3077 	    }
3078 	}
3079 	if (cquote && c == CMDQ) {	/* Quoted CMDQ is special */
3080 	    if (state != ST_BW) {	/* because it can still separate */
3081 		char * s2 = s-1;
3082 		while (s2 > p) { *s2 = *(s2-1); s2--; }
3083 		p++;
3084 	    }
3085 	    cquote = 0;
3086 	}
3087 	if (cquote) {			/* Other quoted character */
3088 	    if (state != ST_BW) {	/* can't separate or group */
3089 		char * s2 = s-1;
3090 		while (s2 > p) { *s2 = *(s2-1); s2--; }
3091 		p++;
3092 	    }
3093 	    class = CL_DAT;		/* so treat it as data */
3094 	    cquote = 0;
3095 	    x = 1;
3096 	} else {			/* Character is not quoted */
3097 	    if (!all && c < SP) {	/* Get its class */
3098 		x = 0;			/* x == 0 means "is separator" */
3099 	    } else if (*sep) {		/* Break set given */
3100 		ss = sep;
3101 		while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */
3102 		x = (*ss != c);
3103 	    } else {			/* Default break set is */
3104 		x = ((c >= 'a' && c <= 'z') || /* all but alphanumerics */
3105 		     (c >= '0' && c <= '9') ||
3106 		     (c >= 'A' && c <= 'Z') ||
3107 		     ((unsigned int)c >= hex80 && (unsigned int)c <= hexff)
3108 		     );
3109 	    }
3110 	    if (x == 0 && *notsep && c) { /* Include set if given */
3111 		ss = notsep;
3112 		while (*ss && *ss != c) ss++; /* (instead of ckstrchr()) */
3113 		x = (*ss == c);
3114 	    }
3115 	    if (c == n3 && grouping && state == ST_BW) { /* Group quote? */
3116 		class = CL_QUO;
3117 	    } else {
3118 		class = x ? CL_DAT : CL_SEP; /* Class = data or separator */
3119 	    }
3120 	    if (grouping) {		/* Grouping? */
3121 		int j;			/* Look for group start */
3122 		for (k = 0; k < 6; k++) { /* Group-start char? */
3123 		    j = 1 << k;		/* Get its mask bit value */
3124 		    if (grouping & j) {
3125 			if (c == gr_opn[k]) { /* Selected group opener? */
3126 			    ko = k;
3127 			    class |= CL_OPN;
3128 			    if (c == '"' || c == '\'') { /* These can also */
3129 				class |= CL_CLS;         /* be closers */
3130 				break;
3131 			    }
3132 			} else if (c == gr_cls[k]) { /* Group closer? */
3133 			    class |= CL_CLS;
3134 			    break;
3135 			}
3136 		    }
3137 		}
3138 	    }
3139 	}
3140 	debug(F000,"cksplit char",s,c);
3141 	debug(F101,"cksplit state","",state);
3142 	debug(F101,"cksplit class","",class);
3143 
3144 	switch (state) {		/* State switcher... */
3145 	  case ST_BW:			/* BETWEENWORDS */
3146 	    if (class & CL_OPN) {	/* Group opener */
3147 		if (gr_lvl == 0 && !gquote) { /* If not in group */
3148 		    p = s;		/* point to beginning of word */
3149 		}
3150 		gr_lvl++;		/* Push closer on nesting stack */
3151 		if (gr_lvl >= NESTMAX)
3152 		  goto xxsplit;
3153 		gr_stk[gr_lvl] = gr_cls[ko];
3154 		prevstate = state;
3155 		state = ST_IG;		/* Switch to INGROUP state */
3156 	    } else if (class & CL_DAT) { /* Data character */
3157 		gr_lvl = 0;		/* Clear group nesting stack */
3158 		gr_stk[gr_lvl] = 0;
3159 		len = 0;
3160 		p = s;			/* Point to beginning of word */
3161 		if (gquote) {		/* Adjust for quote */
3162 		    len++;
3163 		    p--;
3164 		    gquote = 0;
3165 		}
3166 		prevstate = state;
3167 		state = ST_IW;		/* Switch to INWORD state */
3168 	    } else if (class & CL_QUO) { /* Group quote */
3169 		gquote = gr_lvl+1;	/* Remember quoted level */
3170 		p = s - 1;
3171 		len = 1;
3172 	    }
3173 	    if (collapse)
3174 	      break;
3175 
3176 	  case ST_IW:			/* INWORD (but not in a group) */
3177 	    if (class & CL_SEP) {	/* Ends on any kind of separator */
3178 		*s = NUL;		/* Terminate this word */
3179 		if (csv) {		/* If comma-separated list */
3180 		    char * s2 = s;	/* discard surrounding spaces */
3181 		    while (s2 > splitbuf) { /* first backwards... */
3182 			s2--;
3183 			if (*s2 == SP || *s2 == HT)
3184 			  *s2 = NUL;
3185 			else
3186 			  break;
3187 		    }
3188 		    s2 = s+1;		/* Then forwards... */
3189 		    while (*s2 && (*s2 == SP || *s2 == HT))
3190 		      *s2++;
3191 		    s = s2-1;
3192 		}
3193 		if (!csv || prevstate != ST_IG) {
3194 		    wordnum++;		      /* Count it */
3195 		    if (splitting || n < 0) { /* Dispose of it appropriately */
3196 			if (wordnum > max) {  /* Add to array if splitting */
3197 			    ck_sval.a_size = -2;
3198 			    return(&ck_sval);
3199 			}
3200 			/* This inelegant bit corrects an edge condition */
3201 			if (csv && !*p && (!c || !*(s+1))) {
3202 			    wordnum--;
3203 			} else {
3204 			    setword(wordnum,p,len);
3205 			}
3206 		    } else if (wordnum == n) { /* Searching for word n */
3207 			setword(1,p,len);
3208 			ck_sval.a_size = 1;
3209 			return(&ck_sval);
3210 		    }
3211 		}
3212 		prevstate = state;
3213 		state = ST_BW;		/* Switch to BETWEENWORDS state */
3214 		len = 0;
3215 	    }
3216 	    break;
3217 
3218 	  case ST_IG:			/* INGROUP */
3219 	    if (class & CL_CLS) {	/* Have group closer? */
3220 		if (csv) {
3221 		    if (*(s+1) == c) {
3222 			char *s2 = s;
3223 			while ((*s2 = *(s2+1))) s2++;
3224 			s++;
3225 			c = *s;
3226 		    }
3227 		}
3228 		if (c == gr_stk[gr_lvl]) { /* Does it match current opener? */
3229 		    gr_lvl--;		   /* Yes, pop stack */
3230 		    if (gr_lvl < 0)	   /* Don't pop it too much */
3231 		      gr_lvl = 0;
3232 		    if (gr_lvl == 0) {	/* If at top of stack */
3233 			if (gquote)
3234 			  s++;
3235 			c = *s;
3236 			*s = NUL;	/* we have word. */
3237 			wordnum++;	/* Count and dispose of it. */
3238 			len--;
3239 			if (splitting || n < 0) {
3240 			    if (wordnum > max) {
3241 				ck_sval.a_size = -2;
3242 				return(&ck_sval);
3243 			    }
3244 			    setword(wordnum,p+1,len);
3245 			} else if (wordnum == n) {
3246 			    setword(1,p+1,len);
3247 			    ck_sval.a_size = 1;
3248 			    return(&ck_sval);
3249 			}
3250 			prevstate = state;
3251 			state = ST_BW;	/* Switch to BETWEENWORDS state */
3252 			len = 0;
3253 		    }
3254 		    if (gr_lvl < gquote)
3255 		      gquote = 0;
3256 		}
3257 	    } else if (class & CL_OPN) { /* Have group opener */
3258 		gr_lvl++;		 /* Push on nesting stack */
3259 		if (gr_lvl >= NESTMAX) goto xxsplit;
3260 		gr_stk[gr_lvl] = gr_cls[ko];
3261 	    }
3262 	} /* switch */
3263 
3264       nextc:
3265 	s++;				/* Next char */
3266 	if (state)
3267 	  len++;
3268     } /* while (c) */
3269 
3270     if (gr_lvl > 0) {			/* In case of an unclosed group */
3271 	if (splitting || n < 0) {	/* make it the last word. */
3272 	    if (++wordnum > max) {
3273 		ck_sval.a_size = -2;
3274 		return(&ck_sval);
3275 	    }
3276 	    setword(wordnum,p+1,len);
3277 	} else if (wordnum == n) {	/* Counting from left */
3278 	    setword(1,p+1,len);
3279 	    ck_sval.a_size = 1;
3280 	    return(&ck_sval);
3281 	} else 	if (n < 0 && (wordnum + n > -1)) { /* Counting from right */
3282 	    char * s = wordarray[wordnum + n + 1];
3283 	    if (!s) s = "";
3284 	    setword(1,s,strlen(s));
3285 	    ck_sval.a_size = 1;
3286 	    return(&ck_sval);
3287         }
3288     }
3289     if (!splitting) {			/* Fword... */
3290 	if (n < 0 && (wordnum + n > -1)) { /* Counting from right */
3291 	    char * s = wordarray[wordnum + n + 1];
3292 	    if (!s) s = "";
3293 	    setword(1,s,strlen(s));
3294 	    ck_sval.a_size = 1;
3295 	    return(&ck_sval);
3296         }
3297 	setword(1,NULL,0);		/* From left... */
3298 	ck_sval.a_size = 0;		/* but there weren't n words */
3299 	return(&ck_sval);
3300     } else {				/* Succeed otherwise */
3301 	ck_sval.a_size = wordnum;
3302 /*
3303   Always put a null element at the end of the array.  It does no harm in
3304   the normal case, and it's required if we're making an argv[] array to
3305   pass to execvp().  This element is not included in the count.
3306 */
3307 	if (wordnum < MAXWORDS)
3308           setword(wordnum+1,NULL,0);
3309     }
3310 #ifdef DEBUG
3311     if (deblog) {
3312 	for (i = 1; i <= wordnum; i++)
3313 	  debug(F111,"cksplit result",wordarray[i],i);
3314     }
3315 #endif /* DEBUG */
3316     return(&ck_sval);
3317 
3318   xxsplit:				/* Error return */
3319     ck_sval.a_size = -2;
3320     return(&ck_sval);
3321 }
3322 
3323 /*
3324   ckhexbytetoint() expects a string of two hex characters,
3325   returns the int equivalent or -1 on error.
3326 */
3327 int
3328 #ifdef CK_ANSIC
ckhexbytetoint(char * s)3329 ckhexbytetoint( char * s )
3330 #else
3331 ckhexbytetoint(s) char * s;
3332 #endif	/* CK_ANSIC */
3333 {
3334     int i, c[2];
3335     if (!s) return(-1);
3336     if ((int)strlen(s) != 2) return(-1);
3337     for (i = 0; i < 2; i++) {
3338 	c[i] = *s++;
3339 	if (!c[i]) return(-1);
3340 	if (islower(c[i])) c[i] = toupper(c[i]);
3341 	if (c[i] >= '0' && c[i] <= '9') {
3342 	    c[i] -= 0x30;
3343 	} else if (c[i] >= 'A' && c[i] <= 'F') {
3344 	    c[i] -= 0x37;
3345 	} else {
3346 	    return(-1);
3347 	}
3348     }
3349     return(c[0] * 16 + c[1]);
3350 }
3351 
3352 
3353 /* End of ckclib.c */
3354