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