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