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