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