1 /*
2 * util.c - Common utility routines for xmcd, cda and libdi.
3 *
4 * xmcd - Motif(R) CD Audio Player/Ripper
5 * cda - Command-line CD Audio Player/Ripper
6 * libdi - CD Audio Device Interface Library
7 *
8 *
9 * Copyright (C) 1993-2004 Ti Kan
10 * E-mail: xmcd@amb.org
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 *
26 */
27 #ifndef lint
28 static char *_util_c_ident_ = "@(#)util.c 6.224 04/03/23";
29 #endif
30
31 #include "common_d/appenv.h"
32 #include "common_d/util.h"
33
34 #if defined(USE_SELECT) && defined(_AIX)
35 #include <sys/select.h>
36 #endif
37
38 #ifdef USE_PTHREAD_DELAY_NP
39 #include <pthread.h>
40 #endif
41
42 #ifdef HAS_ICONV
43 #include <iconv.h>
44 #include <locale.h>
45 #include <langinfo.h>
46 #endif
47
48 #ifdef __VMS
49 #include <fscndef.h>
50 STATIC int context;
51 #else
52 /* Defines used by util_runcmd */
53 #define STR_SHPATH "/bin/sh" /* Path to shell */
54 #define STR_SHNAME "sh" /* Name of shell */
55 #define STR_SHARG "-c" /* Shell arg */
56 #endif
57
58
59 /* URL protocols used by util_urlchk */
60 STATIC struct {
61 char *protocol;
62 bool_t islocal;
63 } url_protolist[] = {
64 { "http://", FALSE },
65 { "https://", FALSE },
66 { "ftp://", FALSE },
67 { "file:", TRUE },
68 { "mailto:", FALSE },
69 { "news:", FALSE },
70 { "snews:", FALSE },
71 { "gopher://", FALSE },
72 { "wais://", FALSE },
73 #ifdef _URL_EXTPROTOS /* Define this if desired */
74 { "nntp:", FALSE },
75 { "telnet:", FALSE },
76 { "rlogin:", FALSE },
77 { "tn3270:", FALSE },
78 { "data:", FALSE },
79 { "ldap:", FALSE },
80 { "ldaps:", FALSE },
81 { "castanet:", FALSE },
82 #endif
83 { NULL, FALSE }
84 };
85
86
87 /* Defines used by util_html_fputs() */
88 #ifdef BUGGY_BROWSER
89 /* Should always use but some versions of NCSA Mosaic don't grok it */
90 #define HTML_ESC_SPC "" /* HTML for blank space */
91 #else
92 #define HTML_ESC_SPC " " /* HTML for blank space */
93 #endif
94
95 #define CHARS_PER_TAB 8 /* # chars in a tab */
96
97
98 /* Signature string to denote an error has occurred while converting
99 * a UTF-8 string to ISO8859-1. Used by util_utf8_to_iso8859().
100 */
101 #define UTF8_ISO8859_ERR_SIG "[UTF-8 -> ISO8859-1 Error] "
102
103
104 /* Defines used by util_assign64() and util_assign32() */
105 #if _BYTE_ORDER_ == _B_ENDIAN_
106 #define LO_WORD 1
107 #define HI_WORD 0
108 #else
109 #define LO_WORD 0
110 #define HI_WORD 1
111 #endif
112
113
114 extern appdata_t app_data;
115 extern FILE *errfp;
116
117
118 STATIC uid_t ouid = 30001; /* Default to something safe */
119 STATIC gid_t ogid = 30001; /* Default to something safe */
120 STATIC struct utsname un; /* utsname */
121
122 /*
123 * Data used by util_text_reduce()
124 */
125 STATIC int excnt,
126 delcnt,
127 *exlen,
128 *dellen;
129 STATIC char **exclude_words;
130 STATIC char *delete_words[] = {
131 "\\n", "\\r", "\\t"
132 };
133
134 /*
135 * For util_monname()
136 */
137 STATIC char *mon_name[] = {
138 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
139 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
140 };
141
142
143
144 /***********************
145 * private routines *
146 ***********************/
147
148
149 /*
150 * util_utf8_to_iso8859
151 * UTF-8 to ISO8859 string conversion function. The output
152 * string buffer will be internally allocated in this function
153 * and should be freed by the caller via MEM_FREE when done.
154 * Note that due to the byte-oriented nature of ISO8859 encoding,
155 * Any malformed UTF-8 character, or a UTF-character that would
156 * evaluate to a value greater than 0xfd are converted into an
157 * underscore '_'.
158 *
159 * Args:
160 * from - The input UTF-8 string
161 * to - The output ISO8859 string address pointer
162 * roe - Revert-on-error boolean flag. Controls the behavior when
163 * a conversion error occurs:
164 * FALSE - A partial conversion will take place, the
165 * unconvertable characters will be replaced by an
166 * underscore '_' character.
167 * TRUE - The output string will be the UTF8_ISO8859_ERR_SIG
168 * signature, followed by a verbatim copy of the
169 * unconverted input UTF-8 string.
170 *
171 * Return:
172 * TRUE - success
173 * FALSE - failure
174 */
175 STATIC bool_t
util_utf8_to_iso8859(char * from,char ** to,bool_t roe)176 util_utf8_to_iso8859(char *from, char **to, bool_t roe)
177 {
178 unsigned char *p,
179 *q,
180 *tobuf;
181 bool_t err;
182
183 tobuf = (unsigned char *) MEM_ALLOC(
184 "iso8859_to_buf",
185 strlen(from) + strlen(UTF8_ISO8859_ERR_SIG) + 1
186 );
187 if (tobuf == NULL) {
188 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
189 *to = NULL;
190 return FALSE;
191 }
192
193 p = (unsigned char *) from;
194 q = (unsigned char *) tobuf;
195 err = FALSE;
196 while (*p != '\0') {
197 if (*p <= 0x7f) {
198 /* Simple US-ASCII character: just copy it */
199 *q++ = *p++;
200 }
201 else if (*p >= 0xc0 && *p <= 0xfd) {
202 int n = 0;
203
204 if ((*p & 0xe0) == 0xc0) {
205 /* Two byte sequence */
206 n = 2;
207 if (*(p+1) >= 0x80 && *(p+1) <= 0xfd) {
208 /* OK */
209 *q = (((*p & 0x03) << 6) |
210 (*(p+1) & 0x3f));
211
212 if ((*p & 0x1c) != 0) {
213 /* Decodes to more than 8 bits */
214 *q = '_';
215 err = TRUE;
216 DBGPRN(DBG_CDI|DBG_GEN)(errfp,
217 "Notice: utf8_to_iso8859: "
218 "Skipping unsupported character.\n"
219 );
220 }
221 else if (*q <= 0x7f) {
222 /* Not the shortest encoding:
223 * illegal.
224 */
225 *q = '_';
226 err = TRUE;
227 DBGPRN(DBG_CDI|DBG_GEN)(errfp,
228 "Notice: utf8_to_iso8859: "
229 "Skipping illegal character.\n"
230 );
231 }
232 }
233 else {
234 /* Malformed UTF-8 character */
235 *q = '_';
236 err = TRUE;
237 DBGPRN(DBG_CDI|DBG_GEN)(errfp,
238 "Notice: utf8_to_iso8859: "
239 "Skipping malformed sequence.\n"
240 );
241 }
242 q++;
243 }
244 else if ((*p & 0xf0) == 0xe0) {
245 /* Three-byte sequence */
246 n = 3;
247 }
248 else if ((*p & 0xf8) == 0xf0) {
249 /* Four-byte sequence */
250 n = 4;
251 }
252 else if ((*p & 0xfc) == 0xf8) {
253 /* Five-byte sequence */
254 n = 5;
255 }
256 else if ((*p & 0xfe) == 0xfc) {
257 /* Six-byte sequence */
258 n = 6;
259 }
260
261 if (n > 2) {
262 *q++ = '_';
263 err = TRUE;
264 DBGPRN(DBG_CDI|DBG_GEN)(errfp,
265 "Notice: utf8_to_iso8859: "
266 "Skipping unsupported character.\n"
267 );
268 }
269
270 while (n > 0) {
271 if (*(++p) == '\0')
272 break;
273 n--;
274 }
275 }
276 else {
277 /* Malformed UTF-8 sequence: skip */
278 p++;
279 err = TRUE;
280 DBGPRN(DBG_CDI|DBG_GEN)(errfp,
281 "Notice: utf8_to_iso8859: "
282 "Skipping malformed sequence.\n"
283 );
284 }
285 }
286 *q = '\0';
287
288 if (err && (app_data.debug & (DBG_CDI|DBG_GEN)) != 0) {
289 util_dbgdump(
290 "The string that contains the error",
291 (byte_t *) from,
292 strlen(from)
293 );
294 (void) fputs("\n", errfp);
295 }
296
297 if (err && roe) {
298 /* Revert to input string on error, but prepend
299 * it with an conversion error signature string.
300 */
301 (void) sprintf((char *) tobuf, "%s%s",
302 UTF8_ISO8859_ERR_SIG, from);
303 }
304
305 *to = NULL;
306 if (!util_newstr(to, (char *) tobuf)) {
307 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
308 MEM_FREE(tobuf);
309 return FALSE;
310 }
311 MEM_FREE(tobuf);
312
313 return TRUE;
314 }
315
316
317 /*
318 * util_iso8859_to_utf8
319 * ISO8859 to UTF-8 string conversion function. The output
320 * string buffer will be internally allocated in this function
321 * and should be freed by the caller via MEM_FREE when done.
322 *
323 * Args:
324 * from - The input ISO8859 string
325 * to - The output UTF-8 string address pointer
326 *
327 * Return:
328 * TRUE - success
329 * FALSE - failure
330 */
331 STATIC bool_t
util_iso8859_to_utf8(char * from,char ** to)332 util_iso8859_to_utf8(char *from, char **to)
333 {
334 unsigned char *p,
335 *q,
336 *tobuf;
337 size_t n;
338
339 /* If the string has the UTF8_ISO8859_ERR_SIG signature at the
340 * beginning we can assume that the rest of the string is already
341 * encoded in UTF-8, so just skip the signature and copy the rest.
342 */
343 n = strlen(UTF8_ISO8859_ERR_SIG);
344 if (strncmp(from, UTF8_ISO8859_ERR_SIG, n) == 0) {
345 *to = NULL;
346 if (!util_newstr(to, from + n)) {
347 (void) fprintf(errfp, "Error: %s\n",
348 app_data.str_nomemory);
349 return FALSE;
350 }
351 return TRUE;
352 }
353
354 tobuf = (unsigned char *) MEM_ALLOC(
355 "utf8_to_buf", strlen(from) * 6 + 1
356 );
357 if (tobuf == NULL) {
358 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
359 *to = NULL;
360 return FALSE;
361 }
362
363 p = (unsigned char *) from;
364 q = (unsigned char *) tobuf;
365 while (*p != '\0') {
366 if (*p <= 0x7f) {
367 /* Simple US-ASCII character: just copy it */
368 *q++ = *p++;
369 }
370 else {
371 /* ISO8859 is byte-oriented, so this needs to
372 * only handle the "extended ASCII" characters
373 * 0xc0 to 0xfd.
374 */
375 *q++ = (0xc0 | ((int) (0xc0 & *p) >> 6));
376 *q++ = 0x80 | (*p & 0x3f);
377 p++;
378 }
379 }
380 *q = '\0';
381
382 *to = NULL;
383 if (!util_newstr(to, (char *) tobuf)) {
384 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
385 MEM_FREE(tobuf);
386 return FALSE;
387 }
388 MEM_FREE(tobuf);
389
390 return TRUE;
391 }
392
393
394 #ifdef __VMS
395
396 /*
397 * util_vms_dirconv
398 * Convert VMS directory notation from disk:[a.b.c] to disk:[a.b]c.dir
399 * syntax.
400 *
401 * Args:
402 * path - Input directory path string
403 *
404 * Return:
405 * The output path string. The string buffer should be deallocated
406 * by the caller when done. If an error is encountered, NULL is
407 * returned.
408 */
409 STATIC char *
util_vms_dirconv(char * path)410 util_vms_dirconv(char *path)
411 {
412 char *cp,
413 *cp2,
414 *cp3,
415 *buf,
416 tmp[STR_BUF_SZ];
417
418 buf = (char *) MEM_ALLOC("vms_dirconv", strlen(path) + 16);
419 if (buf == NULL) {
420 (void) fprintf(errfp, "Error: Out of memory\n");
421 _exit(1);
422 }
423 (void) strcpy(buf, path);
424
425 if ((cp = strchr(buf, DIR_BEG)) != NULL) {
426 if ((cp2 = strrchr(buf, DIR_END)) != NULL) {
427 if ((cp3 = strrchr(buf, DIR_SEP)) != NULL) {
428 if (util_strcasecmp(cp3, ".dir") == 0) {
429 /* Already in the desired form */
430 return (buf);
431 }
432 *cp2 = '\0';
433 *cp3 = DIR_END;
434 (void) strcat(cp3, ".dir");
435 return (buf);
436 }
437 else {
438 if ((cp = strchr(buf, ':')) == NULL)
439 cp = buf;
440 else
441 cp++;
442
443 if (strcmp(cp, "[000000]") == 0)
444 return (buf);
445
446 *cp2 = '\0';
447 (void) strcpy(tmp, cp+1);
448 (void) sprintf(cp, "[000000]%s.dir", tmp);
449 return (buf);
450 }
451 }
452 else {
453 /* Invalid path */
454 return NULL;
455 }
456 }
457 else {
458 if ((cp2 = strrchr(buf, DIR_SEP)) != NULL) {
459 if ((cp3 = strchr(buf, ':')) == NULL)
460 cp3 = buf;
461 else
462 cp3++;
463
464 if (util_strcasecmp(cp2, ".dir") == 0) {
465 (void) sprintf(tmp, "[000000]%s", cp3);
466 strcpy(cp3, tmp);
467 return (buf);
468 }
469 else {
470 /* Invalid path */
471 return NULL;
472 }
473 }
474 else {
475 if ((cp3 = strchr(buf, ':')) != NULL) {
476 (void) strcpy(cp3+1, "[000000]");
477 return (buf);
478 }
479
480 /* Invalid path */
481 return NULL;
482 }
483 }
484 /*NOTREACHED*/
485 }
486
487 #endif /* __VMS */
488
489
490 /*
491 * util_lconv_open
492 * Open localization for character set conversion.
493 *
494 * Args:
495 * from - Locale to convert from, or NULL to use the default locale
496 * to - Locale to convert to, or NULL to use the default locale
497 *
498 * Return:
499 * Locale descriptor to be used with util_do_lconv.
500 */
501 void *
util_lconv_open(char * from,char * to)502 util_lconv_open(char *from, char *to)
503 {
504 #ifdef HAS_ICONV
505 iconv_t desc;
506
507 if (from == NULL && to == NULL)
508 return NULL;
509
510 if (from == NULL)
511 from = nl_langinfo(CODESET);
512 if (to == NULL)
513 to = nl_langinfo(CODESET);
514
515 if (from == NULL || *from == '\0' || to == NULL || *to == '\0')
516 /* Conversion not necessary and not supported */
517 return NULL;
518
519 if (strcmp(from, to) == 0)
520 /* Conversion not necessary */
521 return NULL;
522
523 if ((desc = iconv_open(to, from)) == (iconv_t) -1) {
524 DBGPRN(DBG_CDI|DBG_GEN)(errfp,
525 "\"%s\" -> \"%s\": Charset conversion not supported.\n",
526 from, to
527 );
528 return NULL;
529 }
530
531 return ((void *) desc);
532 #else
533 return NULL;
534 #endif
535 }
536
537
538 /*
539 * util_lconv_close
540 * Close localization for character set conversion
541 *
542 * Args:
543 * desc - Locale descriptor returned from util_lconv_open.
544 *
545 * Return:
546 * Nothing.
547 */
548 void
util_lconv_close(void * desc)549 util_lconv_close(void *desc)
550 {
551 #ifdef HAS_ICONV
552 (void) iconv_close((iconv_t) desc);
553 #endif
554 }
555
556
557 /*
558 * util_do_lconv
559 * Perform localization for character set conversion. Note that
560 * this will work only for byte-oriented strings. Multi-byte
561 * strings will not display correctly.
562 *
563 * Args:
564 * desc - Locale descriptor returned from util_lconv_open.
565 * from - String to convert.
566 * to - Result string, the storage is internally allocated and should
567 * be freed via MEM_FREE() when done.
568 *
569 * Return:
570 * TRUE - success
571 * FALSE - failure
572 */
573 STATIC bool_t
util_do_lconv(void * desc,char * from,char ** to)574 util_do_lconv(void *desc, char *from, char **to)
575 {
576 #ifdef HAS_ICONV
577 int flen,
578 tlen,
579 llen,
580 ulen;
581 char *tobuf,
582 *p;
583 bool_t err = FALSE;
584
585 flen = strlen(from);
586 tlen = ((flen + 3) & ~3) + 1;
587 llen = tlen - 1;
588 ulen = 0;
589
590 if (desc == NULL)
591 return FALSE;
592
593 if ((tobuf = p = (char *) MEM_ALLOC("lconv_to", tlen)) == NULL) {
594 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
595 return FALSE;
596 }
597
598 while (iconv(desc, (void *) &from, (size_t *) &flen,
599 &p, (size_t *) &llen) == (size_t) -1) {
600
601 switch (errno) {
602 case E2BIG:
603 /* Grow the output buffer and retry */
604 ulen = p - tobuf;
605 tlen = ((tlen - 1) << 1) + 1;
606 tobuf = (char *) MEM_REALLOC("lconv_to", tobuf, tlen);
607 if (tobuf == NULL) {
608 (void) fprintf(errfp, "Error: %s\n",
609 app_data.str_nomemory
610 );
611 *to = NULL;
612 return FALSE;
613 }
614 llen = tlen - ulen - 1;
615 p = tobuf + ulen;
616 break;
617
618 case EINVAL:
619 DBGPRN(DBG_CDI|DBG_GEN)(errfp,
620 "iconv: Charset conversion: "
621 "Input string error.\n"
622 );
623 err = TRUE;
624 break;
625
626 case EILSEQ:
627 DBGPRN(DBG_CDI|DBG_GEN)(errfp,
628 "iconv: Notice: Charset conversion: "
629 "Skipping unsupported sequence.\n"
630 );
631 /* Skip and try to process the rest of the string */
632 from++;
633 flen = strlen(from);
634 break;
635
636 default:
637 DBGPRN(DBG_CDI|DBG_GEN)(errfp,
638 "iconv: Charset conversion error: "
639 "errno=%d\n", errno
640 );
641 err = TRUE;
642 break;
643 }
644
645 if (err)
646 break;
647 }
648 *p = '\0';
649
650 if (err && (app_data.debug & (DBG_CDI|DBG_GEN)) != 0) {
651 util_dbgdump(
652 "The string that contains the error",
653 (byte_t *) from,
654 strlen(from)
655 );
656 (void) fputs("\n", errfp);
657 }
658
659 *to = NULL;
660 if (!util_newstr(to, (char *) tobuf)) {
661 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
662 MEM_FREE(tobuf);
663 return FALSE;
664 }
665 MEM_FREE(tobuf);
666
667 return TRUE;
668 #else
669 return FALSE;
670 #endif
671 }
672
673
674 /*
675 * util_ckmkdir
676 * Check the specified directory path, if it doesn't exist,
677 * attempt to create it.
678 *
679 * Args:
680 * path - Directory path to check or create
681 * mode - Directory permissions
682 *
683 * Return:
684 * TRUE - success
685 * FALSE - failure
686 */
687 STATIC bool_t
util_ckmkdir(char * path,mode_t mode)688 util_ckmkdir(char *path, mode_t mode)
689 {
690 char *dpath = NULL;
691 struct stat stbuf;
692
693 #ifdef __VMS
694 if ((dpath = util_vms_dirconv(path)) == NULL)
695 return FALSE;
696 #else
697 if (!util_newstr(&dpath, path))
698 return FALSE;
699 #endif
700
701 if (stat(dpath, &stbuf) < 0) {
702 if (errno == ENOENT) {
703 if (mkdir(path, mode) < 0) {
704 MEM_FREE(dpath);
705 return FALSE;
706 }
707 (void) chmod(path, mode);
708 }
709 else {
710 MEM_FREE(dpath);
711 return FALSE;
712 }
713 }
714 else if (!S_ISDIR(stbuf.st_mode)) {
715 MEM_FREE(dpath);
716 return FALSE;
717 }
718
719 MEM_FREE(dpath);
720 return TRUE;
721 }
722
723
724 /***********************
725 * public routines *
726 ***********************/
727
728
729 /*
730 * util_init
731 * Initialize the libutil module. This should be called before
732 * the calling program does a setuid.
733 *
734 * Args:
735 * None
736 *
737 * Return:
738 * Nothing
739 */
740 void
util_init(void)741 util_init(void)
742 {
743 /* Save original uid and gid */
744 ouid = getuid();
745 ogid = getgid();
746
747 /* Set uname */
748 if (uname(&un) < 0) {
749 DBGPRN(DBG_GEN)(errfp, "uname(2) failed (errno=%d)\n", errno);
750 }
751
752 #ifdef _SCO_SVR3
753 (void) strcpy(un.sysname, "SCO_SV");
754 #else
755 if (un.sysname[0] == '\0')
756 (void) strcpy(un.sysname, "Unknown");
757 #endif
758 }
759
760
761 /*
762 * util_start
763 * Start up the libutil module.
764 *
765 * Args:
766 * None
767 *
768 * Return:
769 * Nothing
770 */
771 void
util_start(void)772 util_start(void)
773 {
774 int n,
775 i;
776 char *p,
777 *q;
778 char c;
779
780 /* Initializations for util_text_reduce() */
781 p = app_data.exclude_words;
782 n = 1;
783 if (p != NULL) {
784 for (; *p != '\0'; p++) {
785 if (isspace((int) *p)) {
786 n++;
787 while (isspace((int) *p))
788 p++;
789 }
790 }
791 }
792 excnt = n;
793
794 exclude_words = (char **) MEM_ALLOC(
795 "exclude_words",
796 excnt * sizeof(char *)
797 );
798 if (exclude_words == NULL) {
799 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
800 _exit(1);
801 }
802
803 p = app_data.exclude_words;
804 if (p == NULL)
805 exclude_words[0] = "";
806 else {
807 n = 0;
808 for (q = p; *q != '\0'; q++) {
809 if (!isspace((int) *q))
810 continue;
811 c = *q;
812 *q = '\0';
813 exclude_words[n] = (char *) MEM_ALLOC(
814 "exclude_words[n]",
815 strlen(p) + 1
816 );
817 if (exclude_words[n] == NULL) {
818 (void) fprintf(errfp, "Error: %s\n",
819 app_data.str_nomemory
820 );
821 _exit(1);
822 }
823 (void) strcpy(exclude_words[n], p);
824 n++;
825 *q = c;
826 p = q + 1;
827 while (isspace((int) *p))
828 p++;
829 q = p;
830 }
831 exclude_words[n] = (char *) MEM_ALLOC(
832 "exclude_words[n]",
833 strlen(p) + 1
834 );
835 if (exclude_words[n] == NULL) {
836 (void) fprintf(errfp, "Error: %s\n",
837 app_data.str_nomemory
838 );
839 _exit(1);
840 }
841 (void) strcpy(exclude_words[n], p);
842 }
843
844 exlen = (int *) MEM_ALLOC("exlen", sizeof(int) * excnt);
845 if (exlen == NULL) {
846 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
847 _exit(1);
848 }
849
850 for (i = 0; i < excnt; i++)
851 exlen[i] = strlen(exclude_words[i]);
852
853 delcnt = sizeof(delete_words) / sizeof(char *);
854
855 dellen = (int *) MEM_ALLOC("dellen", sizeof(int) * delcnt);
856 if (dellen == NULL) {
857 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
858 _exit(1);
859 }
860
861 for (i = 0; i < delcnt; i++)
862 dellen[i] = strlen(delete_words[i]);
863 }
864
865
866 /*
867 * util_onsig
868 * Generic signal handler. This is intended to break a
869 * thread from a blocked system call and no more.
870 *
871 * Args:
872 * signo - The signal number
873 *
874 * Return:
875 * Nothing.
876 */
877 /*ARGSUSED*/
878 void
util_onsig(int signo)879 util_onsig(int signo)
880 {
881 /* Do nothing */
882
883 #if !defined(USE_SIGACTION) && !defined(BSDCOMPAT)
884 /* Reset handler */
885 (void) util_signal(signo, util_onsig);
886 #endif
887 }
888
889
890 /*
891 * util_signal
892 * Wrapper around signal disposition functions to avoid
893 * the scattering of OS-dependent code.
894 *
895 * Args:
896 * Same as signal(2).
897 * sig - The signal number
898 * disp - The signal disposition
899 *
900 * Return:
901 * The previous signal disposition.
902 */
903 void
util_signal(int sig,void (* disp)(int))904 (*util_signal(int sig, void (*disp)(int)))(int)
905 {
906 #ifdef USE_SIGACTION
907 struct sigaction sigact,
908 oldact;
909
910 sigact.sa_handler = disp;
911 sigemptyset(&sigact.sa_mask);
912 sigact.sa_flags = 0;
913 if (sigaction(sig, &sigact, &oldact) < 0) {
914 perror("util_signal: sigaction failed");
915 _exit(1);
916 }
917 return (oldact.sa_handler);
918 #endif
919 #ifdef USE_SIGSET
920 return (sigset(sig, disp));
921 #endif
922 #ifdef USE_SIGNAL
923 return (signal(sig, disp));
924 #endif
925 }
926
927
928 /*
929 * util_seteuid
930 * Wrapper for the seteuid(2) system call, and deal with platform
931 * support variations.
932 *
933 * Args:
934 * euid - New effective uid
935 */
936 int
util_seteuid(uid_t euid)937 util_seteuid(uid_t euid)
938 {
939 #ifdef USE_SETEUID
940 return (seteuid(euid));
941 #endif
942 #ifdef USE_SETREUID
943 return (setreuid(-1, euid));
944 #endif
945 #ifdef USE_SETRESUID
946 return (setresuid(-1, euid, -1));
947 #endif
948 #if !defined(USE_SETEUID) && !defined(USE_SETREUID) && !defined(USE_SETRESUID)
949 errno = ENOSYS;
950 return -1;
951 #endif
952 }
953
954
955 /*
956 * util_setegid
957 * Wrapper for the setegid(2) system call, and deal with platform
958 * support variations.
959 *
960 * Args:
961 * egid - New effective gid
962 */
963 int
util_setegid(gid_t egid)964 util_setegid(gid_t egid)
965 {
966 #ifdef USE_SETEGID
967 return (setegid(egid));
968 #endif
969 #ifdef USE_SETREGID
970 return (setregid(-1, egid));
971 #endif
972 #ifdef USE_SETRESGID
973 return (setresgid(-1, egid, -1));
974 #endif
975 #if !defined(USE_SETEGID) && !defined(USE_SETREGID) && !defined(USE_SETRESGID)
976 errno = ENOSYS;
977 return -1;
978 #endif
979 }
980
981
982 /*
983 * util_newstr
984 * Allocate memory and make a copy of a text string. Make sure
985 * *ptr is not uninitialized. It must either be NULL or point to
986 * a memory location previously allocated with MEM_ALLOC (in which
987 * case it will be freed). *ptr must never point to statically-allocated
988 * or stack space. Also, if str is the null string, no memory will
989 * be allocated and *ptr will be set to NULL.
990 *
991 * Args:
992 * ptr - Location of pointer of the new string.
993 * str - The source string.
994 *
995 * Return:
996 * TRUE - success
997 * FALSE - failure
998 */
999 bool_t
util_newstr(char ** ptr,char * str)1000 util_newstr(char **ptr, char *str)
1001 {
1002 #ifdef MEM_DEBUG
1003 char name[24];
1004 #endif
1005 if (ptr == NULL)
1006 return FALSE;
1007
1008 if (*ptr != NULL)
1009 MEM_FREE(*ptr);
1010
1011 if (str == NULL || str[0] == '\0') {
1012 *ptr = NULL;
1013 return TRUE;
1014 }
1015
1016 #ifdef MEM_DEBUG
1017 (void) strncpy(name, str, sizeof(name)-1);
1018 name[sizeof(name)-1] = '\0';
1019
1020 if ((*ptr = (char *) MEM_ALLOC(name, strlen(str)+1)) == NULL)
1021 return FALSE;
1022 #else
1023 if ((*ptr = (char *) MEM_ALLOC("util_newstr", strlen(str)+1)) == NULL)
1024 return FALSE;
1025 #endif
1026
1027 (void) strcpy(*ptr, str);
1028
1029 return TRUE;
1030 }
1031
1032
1033 /*
1034 * util_get_ouid
1035 * Get original user ID
1036 *
1037 * Args:
1038 * None
1039 *
1040 * Return:
1041 * Original uid value.
1042 */
1043 uid_t
util_get_ouid(void)1044 util_get_ouid(void)
1045 {
1046 return (ouid);
1047 }
1048
1049
1050 /*
1051 * util_get_ogid
1052 * Get original group ID
1053 *
1054 * Args:
1055 * None
1056 *
1057 * Return:
1058 * Original gid value.
1059 */
1060 gid_t
util_get_ogid(void)1061 util_get_ogid(void)
1062 {
1063 return (ogid);
1064 }
1065
1066
1067 /*
1068 * util_set_ougid
1069 * Change user ID and group ID to original setting.
1070 *
1071 * Args:
1072 * None.
1073 *
1074 * Return:
1075 * TRUE on success, FALSE on failure.
1076 */
1077 bool_t
util_set_ougid(void)1078 util_set_ougid(void)
1079 {
1080 /* Force uid and gid to original setting */
1081 if (setuid(ouid) < 0) {
1082 DBGPRN(DBG_GEN)(errfp, "Failed to set uid.\n");
1083 return FALSE;
1084 }
1085 if (setgid(ogid) < 0) {
1086 DBGPRN(DBG_GEN)(errfp, "Failed to set gid.\n");
1087 return FALSE;
1088 }
1089 return (TRUE);
1090 }
1091
1092
1093 /*
1094 * util_get_uname
1095 * Get the utsname structure for the running system
1096 * (See uname(2)).
1097 *
1098 * Args:
1099 * None
1100 *
1101 * Return:
1102 * Pointer to the utsname structure.
1103 */
1104 struct utsname *
util_get_uname(void)1105 util_get_uname(void)
1106 {
1107 return (&un);
1108 }
1109
1110
1111 /*
1112 * util_ltobcd
1113 * 32-bit integer to BCD conversion routine
1114 *
1115 * Args:
1116 * n - 32-bit integer
1117 *
1118 * Return:
1119 * BCD representation of n
1120 */
1121 sword32_t
util_ltobcd(sword32_t n)1122 util_ltobcd(sword32_t n)
1123 {
1124 return ((n % 10) | ((n / 10) << 4));
1125 }
1126
1127
1128 /*
1129 * util_bcdtol
1130 * BCD to 32-bit integer conversion routine
1131 *
1132 * Args:
1133 * n - BCD value
1134 *
1135 * Return:
1136 * integer representation of n
1137 */
1138 sword32_t
util_bcdtol(sword32_t n)1139 util_bcdtol(sword32_t n)
1140 {
1141 return ((n & 0x0f) + ((n >> 4) * 10));
1142 }
1143
1144
1145 /*
1146 * util_stob
1147 * String to boolean conversion routine
1148 *
1149 * Args:
1150 * s - text string "True", "true", "False" or "false"
1151 *
1152 * Return:
1153 * Boolean value representing the string
1154 */
1155 bool_t
util_stob(char * s)1156 util_stob(char *s)
1157 {
1158 if (strcmp(s, "True") == 0 || strcmp(s, "true") == 0 ||
1159 strcmp(s, "TRUE") == 0)
1160 return TRUE;
1161
1162 return FALSE;
1163 }
1164
1165
1166 /*
1167 * util_basename
1168 * Return the basename of a file path
1169 *
1170 * Args:
1171 * path - The file path string
1172 *
1173 * Return:
1174 * The basename string. This string is dynamically allocated in this
1175 * this function and should be freed by the caller using MEM_FREE() when
1176 * done. NULL is returned on errors.
1177 */
1178 char *
util_basename(char * path)1179 util_basename(char *path)
1180 {
1181 char *p,
1182 *q,
1183 *tmpbuf;
1184 #ifndef __VMS
1185 int i;
1186 #endif
1187
1188 if (path == NULL)
1189 return NULL;
1190
1191 tmpbuf = (char *) MEM_ALLOC("util_basename_tmpbuf", strlen(path) + 1);
1192 if (tmpbuf == NULL)
1193 return NULL;
1194
1195 (void) strcpy(tmpbuf, path);
1196
1197 #ifndef __VMS
1198 i = strlen(tmpbuf)-1;
1199 if (tmpbuf[i] == DIR_END)
1200 tmpbuf[i] = '\0'; /* Zap trailing '/' */
1201 #endif
1202
1203 if ((p = strrchr(tmpbuf, DIR_END)) == NULL)
1204 return (tmpbuf);
1205
1206 p++;
1207
1208 #ifdef __VMS
1209 if (*p == '\0') {
1210 /* The supplied path is a directory - special handling */
1211 *--p = '\0';
1212 if ((q = strrchr(tmpbuf, DIR_SEP)) != NULL)
1213 p = q + 1;
1214 else if ((q = strrchr(tmpbuf, DIR_BEG)) != NULL)
1215 p = q + 1;
1216 else if ((q = strrchr(tmpbuf, ':')) != NULL)
1217 p = q + 1;
1218 else {
1219 p = tmpbuf;
1220 *p = '\0';
1221 }
1222 }
1223 #endif
1224
1225 q = NULL;
1226 if (!util_newstr(&q, p)) {
1227 MEM_FREE(tmpbuf);
1228 return NULL;
1229 }
1230
1231 MEM_FREE(tmpbuf);
1232 return (q);
1233 }
1234
1235
1236 /*
1237 * util_dirname
1238 * Return the dirname of a file path
1239 *
1240 * Args:
1241 * path - The file path string
1242 *
1243 * Return:
1244 * The dirname string. This string is dynamically allocated in this
1245 * this function and should be freed by the caller using MEM_FREE() when
1246 * done. NULL is returned on errors.
1247 */
1248 char *
util_dirname(char * path)1249 util_dirname(char *path)
1250 {
1251 char *p,
1252 *q,
1253 *tmpbuf;
1254
1255 if (path == NULL)
1256 return NULL;
1257
1258 tmpbuf = (char *) MEM_ALLOC("util_basename_tmpbuf", strlen(path) + 1);
1259 if (tmpbuf == NULL)
1260 return NULL;
1261
1262 (void) strcpy(tmpbuf, path);
1263
1264 if ((p = strrchr(tmpbuf, DIR_END)) == NULL) {
1265 q = NULL;
1266 if (!util_newstr(&q, CUR_DIR)) {
1267 MEM_FREE(tmpbuf);
1268 return NULL;
1269 }
1270 return (q);
1271 }
1272
1273 #ifdef __VMS
1274 if (*++p == '\0') {
1275 /* The supplied path is a directory - special handling */
1276 if ((q = strrchr(tmpbuf, DIR_SEP)) != NULL) {
1277 *q = DIR_END;
1278 *++q = '\0';
1279 }
1280 else if ((q = strrchr(tmpbuf, ':')) != NULL)
1281 *++q = '\0';
1282 else
1283 p = tmpbuf;
1284 }
1285 *p = '\0';
1286 #else
1287 if (p == tmpbuf)
1288 *++p = '\0';
1289 else
1290 *p = '\0';
1291 #endif
1292
1293 q = NULL;
1294 if (!util_newstr(&q, tmpbuf)) {
1295 MEM_FREE(tmpbuf);
1296 return NULL;
1297 }
1298 MEM_FREE(tmpbuf);
1299 return (q);
1300 }
1301
1302
1303 /*
1304 * util_loginname
1305 * Return the login name of the current user
1306 *
1307 * Args:
1308 * None.
1309 *
1310 * Return:
1311 * The login name string.
1312 */
1313 char *
util_loginname(void)1314 util_loginname(void)
1315 {
1316 #if defined(__VMS) || defined(INSECURE)
1317 char *cp;
1318 #endif
1319
1320 #ifdef __VMS
1321 cp = getlogin();
1322 if (cp != NULL)
1323 return (cp);
1324 #else
1325 struct passwd *pw;
1326
1327 /* Get login name from the password file if possible */
1328 setpwent();
1329 if ((pw = getpwuid(ouid)) != NULL) {
1330 endpwent();
1331 return (pw->pw_name);
1332 }
1333 endpwent();
1334
1335 #ifdef INSECURE
1336 /* Try the LOGNAME environment variable */
1337 if ((cp = (char *) getenv("LOGNAME")) != NULL)
1338 return (cp);
1339
1340 /* Try the USER environment variable */
1341 if ((cp = (char *) getenv("USER")) != NULL)
1342 return (cp);
1343 #endif /* INSECURE */
1344
1345 #endif /* __VMS */
1346
1347 /* If we still can't get the login name, just set it
1348 * to "nobody" (shrug).
1349 */
1350 return ("nobody");
1351 }
1352
1353
1354 /*
1355 * util_homedir
1356 * Return the home directory path of a user given the uid
1357 *
1358 * Args:
1359 * uid - The uid of the user
1360 *
1361 * Return:
1362 * The home directory path name string
1363 */
1364 char *
util_homedir(uid_t uid)1365 util_homedir(uid_t uid)
1366 {
1367 #ifndef __VMS
1368 struct passwd *pw;
1369 #ifdef INSECURE
1370 char *cp;
1371 #endif
1372
1373 /* Get home directory from the password file if possible */
1374 setpwent();
1375 if ((pw = getpwuid(uid)) != NULL) {
1376 endpwent();
1377 return (pw->pw_dir);
1378 }
1379 endpwent();
1380
1381 #ifdef INSECURE
1382 /* Try the HOME environment variable */
1383 if (uid == ouid && (cp = (char *) getenv("HOME")) != NULL)
1384 return (cp);
1385 #endif /* INSECURE */
1386
1387 /* If we still can't get the home directory, just set it to the
1388 * current directory (shrug).
1389 */
1390 return (CUR_DIR);
1391 #else
1392 char *cp;
1393 static char buf[FILE_PATH_SZ];
1394
1395 if ((cp = (char *) getenv("HOME")) != NULL &&
1396 (int) strlen(cp) < sizeof(buf)) {
1397 (void) strcpy(buf, cp);
1398 buf[strlen(buf)-1] = '\0'; /* Drop the "]" */
1399 }
1400 else
1401 (void) strcpy(buf, "SYS$DISK:[");
1402
1403 return (buf);
1404 #endif /* __VMS */
1405 }
1406
1407
1408 /*
1409 * util_uhomedir
1410 * Return the home directory path of a user given the name
1411 *
1412 * Args:
1413 * name - The name of the user
1414 *
1415 * Return:
1416 * The home directory path name string
1417 */
1418 char *
util_uhomedir(char * name)1419 util_uhomedir(char *name)
1420 {
1421 #ifndef __VMS
1422 struct passwd *pw;
1423
1424 /* Get home directory from the password file if possible */
1425 setpwent();
1426 if ((pw = getpwnam(name)) != NULL) {
1427 endpwent();
1428 return (pw->pw_dir);
1429 }
1430 endpwent();
1431
1432 /* If we still can't get the home directory, just set it to the
1433 * current directory (shrug).
1434 */
1435 return (CUR_DIR);
1436 #else
1437 char *cp;
1438 static char buf[FILE_PATH_SZ];
1439
1440 if ((cp = (char *) getenv("HOME")) != NULL &&
1441 (int) strlen(cp) < FILE_PATH_SZ) {
1442 (void) strcpy(buf, cp);
1443 buf[strlen(buf)-1] = '\0'; /* Drop the "]" */
1444 }
1445 else
1446 (void) strcpy(buf, "SYS$DISK:[");
1447
1448 return (buf);
1449 #endif
1450 }
1451
1452
1453 /*
1454 * util_dirstat
1455 * Wrapper for the stat(2) or lstat(2) system call. To be used
1456 * only for directories. Use stat() or LSTAT directory for other
1457 * file types. This function takes care of OS platform
1458 * idiosyncrasies.
1459 *
1460 * Args:
1461 * path - The directory path to stat
1462 * stbufp - Return buffer
1463 * use_lstat - Specifies whether to use lstat(2) if available.
1464 *
1465 * Return:
1466 * 0 - success
1467 * -1 - failure (errno set)
1468 */
1469 int
util_dirstat(char * path,struct stat * stbufp,bool_t use_lstat)1470 util_dirstat(char *path, struct stat *stbufp, bool_t use_lstat)
1471 {
1472 #ifdef __VMS
1473 int ret;
1474 char *p = util_vms_dirconv(path);
1475
1476 if (p == NULL) {
1477 errno = ENOTDIR;
1478 return -1;
1479 }
1480
1481 ret = stat(p, stbufp);
1482
1483 MEM_FREE(p);
1484 return (ret);
1485 #else
1486 if (use_lstat)
1487 return (LSTAT(path, stbufp));
1488 else
1489 return (stat(path, stbufp));
1490 #endif
1491 }
1492
1493
1494 /*
1495 * util_mkdir
1496 * Wrapper for the mkdir() call. Will make all needed parent
1497 * directories leading to the target directory if they don't
1498 * already exist.
1499 *
1500 * Args:
1501 * path - The directory path to make
1502 * mode - The permissions
1503 *
1504 * Return:
1505 * TRUE - mkdir succeeded or directory already exists
1506 * FALSE - mkdir failed
1507 */
1508 bool_t
util_mkdir(char * path,mode_t mode)1509 util_mkdir(char *path, mode_t mode)
1510 {
1511 char *cp,
1512 *mypath;
1513 #ifdef __VMS
1514 char *cp2,
1515 dev[STR_BUF_SZ],
1516 vpath[STR_BUF_SZ],
1517 chkpath[STR_BUF_SZ];
1518 #endif
1519
1520 mypath = NULL;
1521 if (!util_newstr(&mypath, path)) {
1522 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
1523 _exit(1);
1524 }
1525
1526 #ifndef __VMS
1527 /* UNIX */
1528
1529 /* Make parent directories, if needed */
1530 cp = mypath;
1531 while (*(cp+1) == DIR_BEG)
1532 cp++; /* Skip extra initial '/'s if absolute path */
1533
1534 while ((cp = strchr(cp+1, DIR_SEP)) != NULL) {
1535 *cp = '\0';
1536
1537 if (!util_ckmkdir(mypath, mode)) {
1538 MEM_FREE(mypath);
1539 return FALSE;
1540 }
1541
1542 *cp = DIR_SEP;
1543 }
1544
1545 if (!util_ckmkdir(mypath, mode)) {
1546 MEM_FREE(mypath);
1547 return FALSE;
1548 }
1549
1550 (void) chmod(mypath, mode);
1551
1552 #else
1553 /* VMS */
1554
1555 dev[0] = vpath[0] = '\0';
1556
1557 /* Zap out the version number */
1558 if ((cp = strchr(mypath, ';')) != NULL)
1559 *cp = '\0';
1560
1561 /* Separate into disk and path parts */
1562
1563 cp = mypath;
1564 if ((cp = strchr(cp+1, ':')) == NULL)
1565 cp = mypath;
1566 else {
1567 *cp = '\0';
1568 (void) strcpy(dev, mypath);
1569 *cp++ = ':';
1570 }
1571
1572 /* Check path syntax, and convert from
1573 * disk:[a.b.c]d.dir syntax to disk:[a.b.c.d]
1574 * if needed.
1575 */
1576 cp2 = cp;
1577 if ((cp = strchr(cp, DIR_BEG)) == NULL) {
1578 cp = cp2;
1579 if ((cp2 = strrchr(cp+1, '.')) != NULL) {
1580 if (util_strcasecmp(cp2, ".dir") == 0) {
1581 *cp2 = '\0';
1582 (void) strcpy(vpath, cp);
1583 }
1584 else {
1585 (void) fprintf(errfp, "util_mkdir: "
1586 "Illegal directory path: %s\n",
1587 mypath
1588 );
1589 return FALSE;
1590 }
1591 }
1592 }
1593 else {
1594 (void) strcpy(vpath, ++cp);
1595 if ((cp = strrchr(vpath, DIR_END)) != NULL) {
1596 *cp ='\0';
1597 if ((cp2 = strchr(cp+1, '.')) != NULL) {
1598 if (util_strcasecmp(cp2, ".dir") == 0) {
1599 *cp2 = '\0';
1600 *cp = DIR_SEP;
1601 }
1602 else {
1603 (void) fprintf(errfp, "util_mkdir: "
1604 "Illegal directory path: %s\n",
1605 mypath
1606 );
1607 return FALSE;
1608 }
1609 }
1610 else if (*(cp+1) != '\0') {
1611 (void) fprintf(errfp, "util_mkdir: "
1612 "Illegal directory path: %s\n",
1613 mypath
1614 );
1615 return FALSE;
1616 }
1617 }
1618 }
1619
1620 cp = vpath;
1621 while ((cp = strchr(cp, DIR_SEP)) != NULL) {
1622 *cp = '\0';
1623
1624 (void) sprintf(chkpath, "%s%s[%s]",
1625 dev, dev[0] == '\0' ? "" : ":", vpath
1626 );
1627
1628 *cp++ = DIR_SEP;
1629
1630 if (!util_ckmkdir(chkpath, mode)) {
1631 MEM_FREE(mypath);
1632 return FALSE;
1633 }
1634 }
1635
1636 if (vpath[0] == '\0') {
1637 (void) sprintf(chkpath, "%s%s[000000]",
1638 dev, dev[0] == '\0' ? "" : ":"
1639 );
1640
1641 if (!util_ckmkdir(chkpath, mode)) {
1642 MEM_FREE(mypath);
1643 return FALSE;
1644 }
1645
1646 (void) chmod(chkpath, mode);
1647 }
1648 else if (strcmp(vpath, "000000") != 0) {
1649 (void) sprintf(chkpath, "%s%s[%s]",
1650 dev, dev[0] == '\0' ? "" : ":", vpath
1651 );
1652
1653 if (!util_ckmkdir(chkpath, mode)) {
1654 MEM_FREE(mypath);
1655 return FALSE;
1656 }
1657
1658 (void) chmod(chkpath, mode);
1659 }
1660
1661 #endif /* __VMS */
1662
1663 MEM_FREE(mypath);
1664 return TRUE;
1665 }
1666
1667
1668 /*
1669 * util_setperm
1670 * Set the file permissions of a file.
1671 *
1672 * Args:
1673 * path - file path name
1674 *
1675 * Return:
1676 * Nothing
1677 */
1678 void
util_setperm(char * path,char * modestr)1679 util_setperm(char *path, char *modestr)
1680 {
1681 unsigned int mode;
1682
1683 (void) sscanf(modestr, "%o", &mode);
1684
1685 /* Make sure the file is at least readable to the user just
1686 * in case mode is bogus.
1687 */
1688 mode |= S_IRUSR;
1689
1690 /* Turn off extraneous bits */
1691 mode &= ~(S_ISUID | S_ISGID | S_IXUSR | S_IXGRP | S_IXOTH);
1692
1693 /* Set file permission */
1694 (void) chmod(path, (mode_t) mode);
1695 }
1696
1697
1698 /*
1699 * util_monname
1700 * Convert an interger month to an abbreviated 3-letter month
1701 * name string.
1702 *
1703 * Args:
1704 * mon - The integer month (0 is January, 11 is December).
1705 *
1706 * Return:
1707 * The month name string
1708 */
1709 char *
util_monname(int mon)1710 util_monname(int mon)
1711 {
1712 if (mon < 0 || mon >= 12)
1713 return ("???");
1714 return (mon_name[mon]);
1715 }
1716
1717
1718 /*
1719 * util_isexecutable
1720 * Verify executability, given a path to a program
1721 *
1722 * Args:
1723 * path - the absolute path to the program executable
1724 *
1725 * Return:
1726 * TRUE - It is executable
1727 * FALSE - It is not executable
1728 */
1729 bool_t
util_isexecutable(char * path)1730 util_isexecutable(char *path)
1731 {
1732 #ifdef __VMS
1733 return TRUE; /* shrug */
1734 #else
1735 char **cp;
1736 struct group *gr;
1737 struct stat stbuf;
1738
1739 if (stat(path, &stbuf) < 0 || !S_ISREG(stbuf.st_mode))
1740 /* Cannot access file or file is not regular */
1741 return FALSE;
1742
1743 if (ouid == 0)
1744 /* Root can execute any file */
1745 return TRUE;
1746
1747 if ((stbuf.st_mode & S_IXUSR) == S_IXUSR && ouid == stbuf.st_uid)
1748 /* The file is executable, and is owned by the user */
1749 return TRUE;
1750
1751 if ((stbuf.st_mode & S_IXGRP) == S_IXGRP) {
1752 if (ogid == stbuf.st_gid)
1753 /* The file is group executable, and the
1754 * user's current gid matches the group.
1755 */
1756 return TRUE;
1757
1758 setgrent();
1759 if ((gr = getgrgid(stbuf.st_gid)) != NULL) {
1760 for (cp = gr->gr_mem; cp != NULL && *cp != '\0'; cp++) {
1761 /* The file is group executable, and the
1762 * user is a member of that group.
1763 */
1764 if (strcmp(*cp, util_loginname()) == 0)
1765 return TRUE;
1766 }
1767 }
1768 endgrent();
1769 }
1770
1771 if ((stbuf.st_mode & S_IXOTH) == S_IXOTH)
1772 /* The file is executable by everyone */
1773 return TRUE;
1774
1775 return FALSE;
1776 #endif /* __VMS */
1777 }
1778
1779
1780 /*
1781 * util_findcmd
1782 * Given a command name, return the full path name to it.
1783 *
1784 * Args:
1785 * cmd - The command name string
1786 *
1787 * Return:
1788 * The full path name to the command, or NULL if not found.
1789 * The string buffer is dynamically allocated in this function
1790 * and should be deallocated by the caller using MEM_FREE() when
1791 * done.
1792 */
1793 char *
util_findcmd(char * cmd)1794 util_findcmd(char *cmd)
1795 {
1796 #ifdef __VMS
1797 return NULL; /* shrug */
1798 #else
1799 char *p,
1800 *q,
1801 *r,
1802 *env,
1803 *path,
1804 c,
1805 c2;
1806
1807 if (cmd == NULL)
1808 return NULL;
1809
1810 /* Cut out just the argv[0] portion */
1811 c = '\0';
1812 if ((p = strchr(cmd,' ')) != NULL || (p = strchr(cmd,'\t')) != NULL) {
1813 c = *p;
1814 *p = '\0';
1815 }
1816
1817 if (cmd[0] == '/') {
1818 /* Absolute path specified */
1819 if (!util_isexecutable(cmd)) {
1820 if (p != NULL)
1821 *p = c;
1822 return NULL;
1823 }
1824
1825 path = NULL;
1826 if (!util_newstr(&path, cmd)) {
1827 if (p != NULL)
1828 *p = c;
1829 return NULL;
1830 }
1831
1832 return (path);
1833 }
1834
1835 /* Relative path: walk PATH and look for the executable */
1836 if ((env = getenv("PATH")) == NULL) {
1837 if (p != NULL)
1838 *p = c;
1839 /* PATH unknown */
1840 return NULL;
1841 }
1842
1843 /* Walk the PATH */
1844 for (q = env; (r = strchr(q, ':')) != NULL; *r = c2, q = ++r) {
1845 c2 = *r;
1846 *r = '\0';
1847
1848 path = (char *) MEM_ALLOC(
1849 "findcmd_path",
1850 strlen(q) + strlen(cmd) + 2
1851 );
1852 if (path == NULL)
1853 return NULL; /* shrug */
1854
1855 (void) sprintf(path, "%s/%s", q, cmd);
1856
1857 if (util_isexecutable(path)) {
1858 *r = c2;
1859 if (p != NULL)
1860 *p = c;
1861 return (path);
1862 }
1863
1864 MEM_FREE(path);
1865 }
1866 /* Check last component in PATH */
1867 path = (char *) MEM_ALLOC(
1868 "findcmd_path",
1869 strlen(q) + strlen(cmd) + 2
1870 );
1871 if (path == NULL) {
1872 if (p != NULL)
1873 *p = c;
1874 return NULL; /* shrug */
1875 }
1876
1877 (void) sprintf(path, "%s/%s", q, cmd);
1878
1879 if (util_isexecutable(path)) {
1880 if (p != NULL)
1881 *p = c;
1882 return (path);
1883 }
1884
1885 MEM_FREE(path);
1886 if (p != NULL)
1887 *p = c;
1888
1889 return NULL;
1890
1891 #endif /* __VMS */
1892 }
1893
1894
1895 /*
1896 * util_checkcmd
1897 * Check a command for sanity before running it.
1898 *
1899 * Args:
1900 * cmd - The command string
1901 *
1902 * Return:
1903 * TRUE - Command is sane
1904 * FALSE - Command is not sane
1905 */
1906 bool_t
util_checkcmd(char * cmd)1907 util_checkcmd(char *cmd)
1908 {
1909 #ifdef __VMS
1910 return TRUE; /* shrug */
1911 #else
1912 char *path;
1913
1914 if ((path = util_findcmd(cmd)) == NULL)
1915 return FALSE;
1916
1917 MEM_FREE(path);
1918 return TRUE;
1919 #endif /* __VMS */
1920 }
1921
1922
1923 /*
1924 * util_runcmd
1925 * Set uid and gid to the original user and spawn an external command.
1926 *
1927 * Args:
1928 * cmd - Command string.
1929 * workproc - Function to call when waiting for child process,
1930 * or NULL if no workproc.
1931 * workarg - Argument to pass to workproc.
1932 *
1933 * Return:
1934 * The exit status of the command.
1935 */
1936 int
util_runcmd(char * cmd,void (* workproc)(int),int workarg)1937 util_runcmd(char *cmd, void (*workproc)(int), int workarg)
1938 {
1939 #ifndef __VMS
1940 int ret,
1941 fd;
1942 pid_t cpid;
1943 waitret_t wstat;
1944
1945 if (!util_checkcmd(cmd))
1946 return EXE_ERR;
1947
1948 /* Fork child to invoke external command */
1949 switch (cpid = FORK()) {
1950 case 0:
1951 break;
1952
1953 case -1:
1954 /* Fork failed */
1955 perror("util_runcmd: fork() failed");
1956 return EXE_ERR;
1957
1958 default:
1959 /* Parent process: wait for child to exit */
1960 ret = util_waitchild(cpid, app_data.srv_timeout + 5,
1961 workproc, workarg, TRUE, &wstat);
1962 if (ret < 0)
1963 ret = (errno << 8);
1964 else if (WIFEXITED(wstat))
1965 ret = WEXITSTATUS(wstat);
1966 else if (WIFSIGNALED(wstat))
1967 ret = (WTERMSIG(wstat) << 8) | 0xff;
1968 else
1969 ret = EXE_ERR;
1970
1971 DBGPRN(DBG_GEN)(errfp, "\nCommand exit status %d\n", ret);
1972 return (ret);
1973 }
1974
1975 /* Force uid and gid to original setting */
1976 if (!util_set_ougid())
1977 _exit(errno);
1978
1979 /* Child process */
1980 for (fd = 3; fd < 255; fd++) {
1981 /* Close unneeded file descriptors */
1982 (void) close(fd);
1983 }
1984
1985 /* Restore SIGTERM */
1986 (void) util_signal(SIGTERM, SIG_DFL);
1987
1988 /* Exec a shell to run the command */
1989 DBGPRN(DBG_GEN)(errfp, "Command: [%s]\n", cmd);
1990 (void) execl(STR_SHPATH, STR_SHNAME, STR_SHARG, cmd, NULL);
1991 _exit(EXE_ERR);
1992 /*NOTREACHED*/
1993 #else
1994 int ret;
1995
1996 /* Do the command */
1997 DBGPRN(DBG_GEN)(errfp, "Command: [%s]\n", cmd);
1998 ret = system(cmd);
1999
2000 DBGPRN(DBG_GEN)(errfp, "Command exit status %d\n", ret);
2001 return (ret);
2002 #endif /* __VMS */
2003 }
2004
2005
2006 /*
2007 * util_isqrt
2008 * Fast integer-based square root routine
2009 *
2010 * Args:
2011 * n - The integer value whose square-root is to be taken
2012 *
2013 * Return:
2014 * Resultant square-root integer value
2015 */
2016 int
util_isqrt(int n)2017 util_isqrt(int n)
2018 {
2019 int a, b, c, as, bs;
2020
2021 a = 1;
2022 b = 1;
2023 while (a <= n) {
2024 a = a << 2;
2025 b = b << 1;
2026 }
2027 as = 0;
2028 bs = 0;
2029 while (b > 1 && n > 0) {
2030 a = a >> 2;
2031 b = b >> 1;
2032 c = n - (as | a);
2033 if (c >= 0) {
2034 n = c;
2035 as |= (a << 1);
2036 bs |= b;
2037 }
2038 as >>= 1;
2039 }
2040
2041 return (bs);
2042 }
2043
2044
2045 /*
2046 * util_blktomsf
2047 * CD logical block to MSF conversion routine
2048 *
2049 * Args:
2050 * blk - The logical block address
2051 * ret_min - Minute (return)
2052 * ret_sec - Second (return)
2053 * ret_frame - Frame (return)
2054 * offset - Additional logical block address offset
2055 *
2056 * Return:
2057 * Nothing.
2058 */
2059 void
util_blktomsf(sword32_t blk,byte_t * ret_min,byte_t * ret_sec,byte_t * ret_frame,sword32_t offset)2060 util_blktomsf(
2061 sword32_t blk,
2062 byte_t *ret_min,
2063 byte_t *ret_sec,
2064 byte_t *ret_frame,
2065 sword32_t offset)
2066 {
2067 sword32_t n = blk + offset;
2068
2069 *ret_min = (byte_t) ((n / FRAME_PER_SEC) / 60);
2070 *ret_sec = (byte_t) ((n / FRAME_PER_SEC) % 60);
2071 *ret_frame = (byte_t) (n % FRAME_PER_SEC);
2072 }
2073
2074
2075 /*
2076 * util_msftoblk
2077 * CD MSF to logical block conversion routine
2078 *
2079 * Args:
2080 * min - Minute
2081 * sec - Second
2082 * frame - Frame
2083 * ret_blk - The logical block address (return)
2084 * offset - Additional logical block address offset
2085 *
2086 * Return:
2087 * Nothing.
2088 */
2089 void
util_msftoblk(byte_t min,byte_t sec,byte_t frame,sword32_t * ret_blk,sword32_t offset)2090 util_msftoblk(
2091 byte_t min,
2092 byte_t sec,
2093 byte_t frame,
2094 sword32_t *ret_blk,
2095 sword32_t offset)
2096 {
2097 *ret_blk = (FRAME_PER_SEC * (min * 60 + sec) + frame - offset);
2098 }
2099
2100
2101 /*
2102 * util_xlate_blk
2103 * Translate the CD-ROM drive's native LBA/length into an
2104 * LBA/length that is based on 2048-byte blocks. This is
2105 * needed to support drives that are configured to a block
2106 * size other than 2048 (such as older Sun or SGI).
2107 *
2108 * Args:
2109 * val - The LBA address or length to translate
2110 *
2111 * Return:
2112 * The converted LBA.
2113 */
2114 sword32_t
util_xlate_blk(sword32_t val)2115 util_xlate_blk(sword32_t val)
2116 {
2117 /* If the address is a "signed" negative 3-byte integer, extend
2118 * it to 4 bytes.
2119 */
2120 if (val & 0x800000)
2121 val |= 0xff000000;
2122
2123 if (app_data.drv_blksz < STD_CDROM_BLKSZ)
2124 val /= (STD_CDROM_BLKSZ / app_data.drv_blksz);
2125 else if (app_data.drv_blksz > STD_CDROM_BLKSZ)
2126 val *= (app_data.drv_blksz / STD_CDROM_BLKSZ);
2127
2128 return (val);
2129 }
2130
2131
2132 /*
2133 * util_unxlate_blk
2134 * Translate an LBA/length that is based on 2048-byte blocks
2135 * into the CD-ROM drive's native LBA/length. This is needed
2136 * to support drives that are configured to a block size other
2137 * than 2048 (such as older Sun or SGI).
2138 *
2139 * Args:
2140 * val - The LBA address or length to translate
2141 *
2142 * Return:
2143 * The converted LBA
2144 */
2145 sword32_t
util_unxlate_blk(sword32_t val)2146 util_unxlate_blk(sword32_t val)
2147 {
2148 if (app_data.drv_blksz < STD_CDROM_BLKSZ)
2149 val *= (STD_CDROM_BLKSZ / app_data.drv_blksz);
2150 else if (app_data.drv_blksz > STD_CDROM_BLKSZ)
2151 val /= (app_data.drv_blksz / STD_CDROM_BLKSZ);
2152
2153 #if 1
2154 /* If the address is negative, set it to zero */
2155 if (val & 0x80000000)
2156 val = 0;
2157 #else
2158 /* If the address is negative, reduce to a signed negative
2159 * 3-byte integer.
2160 */
2161 if (val & 0x80000000) {
2162 val &= ~0xff000000;
2163 val |= 0x800000;
2164 }
2165 #endif
2166
2167 return (val);
2168 }
2169
2170
2171 /*
2172 * util_taper_vol
2173 * Translate the volume level based on the configured taper
2174 * characteristics.
2175 *
2176 * Args:
2177 * v - The linear volume value.
2178 *
2179 * Return:
2180 * The curved volume value.
2181 */
2182 int
util_taper_vol(int v)2183 util_taper_vol(int v)
2184 {
2185 int a,
2186 b,
2187 c;
2188
2189 c = MAX_VOL >> 1;
2190
2191 switch (app_data.vol_taper) {
2192 case 1:
2193 /* squared taper */
2194 a = SQR(v);
2195 b = a / MAX_VOL;
2196 if ((a % MAX_VOL) >= c)
2197 b++;
2198 return (b);
2199 case 2:
2200 /* inverse-squared taper */
2201 a = SQR(MAX_VOL - v);
2202 b = a / MAX_VOL;
2203 if ((a % MAX_VOL) >= c)
2204 b++;
2205 return (MAX_VOL - b);
2206 case 0:
2207 default:
2208 /* linear taper */
2209 return (v);
2210 }
2211 /*NOTREACHED*/
2212 }
2213
2214
2215 /*
2216 * util_untaper_vol
2217 * Translate the volume level based on the configured taper
2218 * characteristics.
2219 *
2220 * Args:
2221 * v - The curved volume value.
2222 *
2223 * Return:
2224 * The linear volume value.
2225 */
2226 int
util_untaper_vol(int v)2227 util_untaper_vol(int v)
2228 {
2229 switch (app_data.vol_taper) {
2230 case 1:
2231 /* squared taper */
2232 return (util_isqrt(v * 100));
2233 case 2:
2234 /* inverse-squared taper */
2235 return (MAX_VOL - util_isqrt(SQR(MAX_VOL) - (MAX_VOL * v)));
2236 case 0:
2237 default:
2238 /* linear taper */
2239 return (v);
2240 }
2241 /*NOTREACHED*/
2242 }
2243
2244
2245 /*
2246 * util_scale_vol
2247 * Scale logical audio volume value (0-100) to an 8-bit value
2248 * (0-0xff) range.
2249 *
2250 * Args:
2251 * v - The logical volume value
2252 *
2253 * Return:
2254 * The scaled volume value
2255 */
2256 int
util_scale_vol(int v)2257 util_scale_vol(int v)
2258 {
2259 /* Convert logical audio volume value to 8-bit volume */
2260 return ((v * (0xff - app_data.base_scsivol) / MAX_VOL) +
2261 app_data.base_scsivol);
2262 }
2263
2264
2265 /*
2266 * util_unscale_vol
2267 * Scale an 8-bit audio volume parameter value (0-0xff) to the
2268 * logical volume value (0-100).
2269 *
2270 * Args:
2271 * v - The 8-bit volume value
2272 *
2273 * Return:
2274 * The logical volume value
2275 */
2276 int
util_unscale_vol(int v)2277 util_unscale_vol(int v)
2278 {
2279 int val;
2280
2281 /* Convert 8-bit audio volume value to logical volume */
2282 val = (v - app_data.base_scsivol) * MAX_VOL /
2283 (0xff - app_data.base_scsivol);
2284
2285 return ((val < 0) ? 0 : val);
2286 }
2287
2288
2289 /*
2290 * util_delayms
2291 * Suspend execution for the specified number of milliseconds
2292 *
2293 * Args:
2294 * msec - The number of milliseconds
2295 *
2296 * Return:
2297 * Nothing.
2298 */
2299 void
util_delayms(unsigned long msec)2300 util_delayms(unsigned long msec)
2301 {
2302 #ifdef USE_SELECT
2303 struct timeval to;
2304
2305 to.tv_sec = (long) msec / 1000;
2306 to.tv_usec = ((long) msec % 1000) * 1000;
2307
2308 (void) select(0, NULL, NULL, NULL, &to);
2309 #else
2310 #ifdef USE_POLL
2311 (void) poll(NULL, 0, (int) msec);
2312 #else
2313 #ifdef USE_NAP
2314 (void) nap((long) msec);
2315 #else
2316 #ifdef USE_USLEEP
2317 (void) usleep((long) msec * 1000);
2318 #else
2319 #ifdef USE_NANOSLEEP
2320 struct timespec ts;
2321
2322 ts.tv_sec = (long) msec / 1000;
2323 ts.tv_nsec = ((long) msec % 1000) * 1000000;
2324
2325 (void) nanosleep(&ts, NULL);
2326 #else
2327 #ifdef USE_PTHREAD_DELAY_NP
2328 struct timespec ts;
2329
2330 ts.tv_sec = (long) msec / 1000;
2331 ts.tv_nsec = ((long) msec % 1000) * 1000000;
2332
2333 (void) pthread_delay_np(&ts);
2334 #else
2335 /* shrug: Rounded to the nearest second, with a minimum of 1 second */
2336 if (msec < 1000)
2337 (void) sleep(1);
2338 else
2339 (void) sleep(((unsigned int) msec + 500) / 1000);
2340 #endif /* USE_PTHREAD_DELAY_NP */
2341 #endif /* USE_NANOSLEEP */
2342 #endif /* USE_USLEEP */
2343 #endif /* USE_NAP */
2344 #endif /* USE_POLL */
2345 #endif /* USE_SELECT */
2346 }
2347
2348
2349 /*
2350 * util_waitchild
2351 * Wrapper for waitpid(2), allowing timeouts and running work functions
2352 * while we wait for the child to exit.
2353 *
2354 * Args:
2355 * cpid - The child pid to wait on.
2356 * tmout - Timeout in seconds.
2357 * workproc - Work function to run while waiting (can be NULL).
2358 * workarg - Argument passed to workfunc.
2359 * itimer - Whether to use the itimer facility to interrupt the
2360 * wait and run the workproc periodically. If this is
2361 * FALSE, and a workproc is specified, then alarm() is
2362 * used instead.
2363 * wstatp - Pointer to the location where the wait status is returned
2364 * back to the caller.
2365 *
2366 * Return:
2367 * Return value from the waitpid call.
2368 */
2369 int
util_waitchild(pid_t cpid,time_t tmout,void (* workproc)(int),int workarg,bool_t itimer,waitret_t * wstatp)2370 util_waitchild(
2371 pid_t cpid,
2372 time_t tmout,
2373 void (*workproc)(int),
2374 int workarg,
2375 bool_t itimer,
2376 waitret_t *wstatp
2377 )
2378 {
2379 int saverr = 0,
2380 sigcnt,
2381 ret;
2382 time_t begin,
2383 now;
2384 unsigned int oa = 0;
2385 void (*oh)(int) = NULL;
2386 #ifdef HAS_ITIMER
2387 struct itimerval it,
2388 oit;
2389 #endif
2390 #ifdef USE_SIGPROCMASK
2391 sigset_t sigs;
2392
2393 (void) sigemptyset(&sigs);
2394 (void) sigaddset(&sigs, SIGCHLD);
2395 (void) sigaddset(&sigs, SIGALRM);
2396 (void) sigaddset(&sigs, SIGPIPE);
2397 #endif /* USE_SIGPROCMASK */
2398 #ifdef USE_SIGBLOCK
2399 int sigs,
2400 osigs;
2401
2402 sigs = sigmask(SIGCHLD) | sigmask(SIGALRM) | sigmask(SIGPIPE);
2403 #endif /* USE_SIGBLOCK */
2404
2405 if (workproc != NULL) {
2406 /* Use SIGALRM to interrupt the wait */
2407 oh = util_signal(SIGALRM, util_onsig);
2408
2409 #ifdef HAS_ITIMER
2410 if (itimer) {
2411 it.it_interval.tv_sec = 0;
2412 it.it_interval.tv_usec = 100000;
2413 it.it_value.tv_sec = 1;
2414 it.it_value.tv_usec = 0;
2415
2416 (void) setitimer(ITIMER_REAL, &it, &oit);
2417 }
2418 else
2419 #endif
2420 {
2421 oa = alarm(1);
2422 }
2423 }
2424
2425 sigcnt = 0;
2426 begin = time(NULL);
2427 while ((ret = WAITPID(cpid, wstatp, 0)) != cpid) {
2428 if (workproc != NULL) {
2429 #ifdef HAS_ITIMER
2430 if (!itimer)
2431 #endif
2432 (void) alarm(0);
2433 }
2434
2435 if (ret < 0) {
2436 /* Allow for a few spurious returns */
2437 if (errno == ECHILD && ++sigcnt > 5) {
2438 saverr = errno;
2439 break;
2440 }
2441 if (errno != EINTR) {
2442 saverr = errno;
2443 break;
2444 }
2445 }
2446 else if (ret > 0) {
2447 /* We should not get here */
2448 DBGPRN(DBG_GEN)(errfp,
2449 "\nutil_waitchild: waitpid returned invalid pid "
2450 "(expect=%d, got=%d)\n",
2451 (int) cpid, ret
2452 );
2453 saverr = EINVAL;
2454 ret = -1;
2455 break;
2456 }
2457
2458 now = time(NULL);
2459 if ((now - begin) > tmout) {
2460 /* Timeout: kill child */
2461 (void) kill(cpid, SIGTERM);
2462 DBGPRN(DBG_GEN)(errfp,
2463 "\nutil_waitchild: timed out waiting for pid=%d\n",
2464 (int) cpid
2465 );
2466 }
2467
2468 if (workproc != NULL) {
2469 /* Block SIGCHLD while we do work */
2470 #ifdef USE_SIGPROCMASK
2471 (void) sigprocmask(SIG_BLOCK, &sigs, NULL);
2472 #endif
2473 #ifdef USE_SIGBLOCK
2474 oldsigs = sigblock(sigs);
2475 #endif
2476
2477 /* Do some work */
2478 (*workproc)(workarg);
2479
2480 /* Unblock SIGCHLD */
2481 #ifdef USE_SIGPROCMASK
2482 (void) sigprocmask(SIG_UNBLOCK, &sigs, NULL);
2483 #endif
2484 #ifdef USE_SIGBLOCK
2485 (void) sigsetmask(oldsigs);
2486 #endif
2487
2488 #ifdef HAS_ITIMER
2489 if (!itimer)
2490 #endif
2491 (void) alarm(1);
2492 }
2493 }
2494
2495 if (workproc != NULL) {
2496 #ifdef HAS_ITIMER
2497 if (itimer)
2498 (void) setitimer(ITIMER_REAL, &oit, NULL);
2499 else
2500 #endif
2501 (void) alarm(oa);
2502
2503 (void) util_signal(SIGALRM, oh);
2504 }
2505
2506 errno = saverr;
2507 return ((bool_t) ret == 0);
2508 }
2509
2510
2511 /*
2512 * util_strstr
2513 * If s2 is a substring of s1, return a pointer to s2 in s1. Otherwise,
2514 * return NULL.
2515 *
2516 * Args:
2517 * s1 - The first text string.
2518 * s2 - The second text string.
2519 *
2520 * Return:
2521 * Pointer to the beginning of the substring, or NULL if not found.
2522 */
2523 char *
util_strstr(char * s1,char * s2)2524 util_strstr(char *s1, char *s2)
2525 {
2526 int n;
2527
2528 if (s1 == NULL || s2 == NULL)
2529 return NULL;
2530
2531 n = strlen(s2);
2532 for (; *s1 != '\0'; s1++) {
2533 if (strncmp(s1, s2, n) == 0)
2534 return s1;
2535 }
2536 return NULL;
2537 }
2538
2539
2540 /*
2541 * util_strcasecmp
2542 * Compare two strings a la strcmp(), except it is case-insensitive.
2543 *
2544 * Args:
2545 * s1 - The first text string.
2546 * s2 - The second text string.
2547 *
2548 * Return:
2549 * Compare value. See strcmp(3).
2550 */
2551 int
util_strcasecmp(char * s1,char * s2)2552 util_strcasecmp(char *s1, char *s2)
2553 {
2554 char *buf1,
2555 *buf2,
2556 *p;
2557 int ret;
2558
2559 if (s1 == NULL || s2 == NULL)
2560 return 0;
2561
2562 /* Allocate tmp buffers */
2563 buf1 = (char *) MEM_ALLOC("strcasecmp_buf1", strlen(s1)+1);
2564 buf2 = (char *) MEM_ALLOC("strcasecmp_buf2", strlen(s2)+1);
2565 if (buf1 == NULL || buf2 == NULL) {
2566 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
2567 _exit(1);
2568 }
2569
2570 /* Convert both strings to lower case and store in tmp buffer */
2571 for (p = buf1; *s1 != '\0'; s1++, p++)
2572 *p = (char) ((isupper((int) *s1)) ? tolower((int) *s1) : *s1);
2573 *p = '\0';
2574 for (p = buf2; *s2 != '\0'; s2++, p++)
2575 *p = (char) ((isupper((int) *s2)) ? tolower((int) *s2) : *s2);
2576 *p = '\0';
2577
2578 ret = strcmp(buf1, buf2);
2579
2580 MEM_FREE(buf1);
2581 MEM_FREE(buf2);
2582
2583 return (ret);
2584 }
2585
2586
2587 /*
2588 * util_strncasecmp
2589 * Compare two strings a la strncmp(), except it is case-insensitive.
2590 *
2591 * Args:
2592 * s1 - The first text string.
2593 * s2 - The second text string.
2594 * n - number of characters to compare.
2595 *
2596 * Return:
2597 * Compare value. See strncmp(3).
2598 */
2599 int
util_strncasecmp(char * s1,char * s2,int n)2600 util_strncasecmp(char *s1, char *s2, int n)
2601 {
2602 char *buf1,
2603 *buf2,
2604 *p;
2605 int ret;
2606
2607 if (s1 == NULL || s2 == NULL)
2608 return 0;
2609
2610 /* Allocate tmp buffers */
2611 buf1 = (char *) MEM_ALLOC("strncasecmp_buf1", strlen(s1)+1);
2612 buf2 = (char *) MEM_ALLOC("strncasecmp_buf2", strlen(s2)+1);
2613 if (buf1 == NULL || buf2 == NULL) {
2614 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
2615 _exit(1);
2616 }
2617
2618 /* Convert both strings to lower case and store in tmp buffer */
2619 for (p = buf1; *s1 != '\0'; s1++, p++)
2620 *p = (char) ((isupper((int) *s1)) ? tolower((int) *s1) : *s1);
2621 *p = '\0';
2622 for (p = buf2; *s2 != '\0'; s2++, p++)
2623 *p = (char) ((isupper((int) *s2)) ? tolower((int) *s2) : *s2);
2624 *p = '\0';
2625
2626 ret = strncmp(buf1, buf2, n);
2627
2628 MEM_FREE(buf1);
2629 MEM_FREE(buf2);
2630
2631 return (ret);
2632 }
2633
2634
2635 /*
2636 * util_text_reduce
2637 * Reduce a text string to become suitable for use in a keyword search
2638 * operation.
2639 *
2640 * Args:
2641 * str - The input text string
2642 *
2643 * Return:
2644 * The output text string. The string buffer is allocated internally
2645 * and should be freed by the caller via MEM_FREE(). If an error
2646 * occurs, NULL is returned.
2647 */
2648 char *
util_text_reduce(char * str)2649 util_text_reduce(char *str)
2650 {
2651 int i,
2652 lastex,
2653 *len;
2654 char last,
2655 next,
2656 *p1,
2657 *p2,
2658 *pr,
2659 **t,
2660 *newstr;
2661
2662 if ((newstr = (char *) MEM_ALLOC("text_reduce_newstr",
2663 strlen(str) + 1)) == NULL)
2664 return NULL;
2665
2666 p1 = str;
2667 p2 = newstr;
2668 pr = newstr;
2669
2670 last = ' ';
2671 lastex = -1;
2672
2673 while (*p1 != '\0') {
2674 next = *p1;
2675
2676 for (i = 0, t = delete_words, len = dellen; i < delcnt;
2677 i++, t++, len++) {
2678 if (strncmp(p1, *t, *len) == 0) {
2679 p1 += *len - 1;
2680 next = ' ';
2681 break;
2682 }
2683 }
2684
2685 if (!isalnum((int) last) && !isalnum((int) next)) {
2686 p1++;
2687 continue;
2688 }
2689
2690 for (i = 0, t = exclude_words, len = exlen; i < excnt;
2691 i++, t++, len++) {
2692 if (lastex != i && !isalnum((int) last) &&
2693 util_strncasecmp(p1, *t, *len) == 0 &&
2694 !isalnum((int) p1[*len])) {
2695 p1 += *len;
2696 lastex = i;
2697 break;
2698 }
2699 }
2700
2701 if (i < excnt)
2702 continue;
2703
2704 if (isalnum((int) next))
2705 *p2 = next;
2706 else
2707 *p2 = ' ';
2708
2709 last = next;
2710 p2++;
2711 p1++;
2712
2713 if (isalnum((int) next)) {
2714 lastex = -1;
2715 pr = p2;
2716 }
2717 }
2718
2719 *pr = '\0';
2720 return (newstr);
2721 }
2722
2723
2724 /*
2725 * util_cgi_xlate
2726 * Translate a keyword string into CGI form. It substitutes whitespaces
2727 * with the proper separator, conversion to lower case, handles non-
2728 * alphanumerdic character translations, etc.
2729 *
2730 * Args:
2731 * str - The input keywords string, separated by whitespace
2732 *
2733 * Return:
2734 * The output text string. The string buffer is allocated internally
2735 * and should be freed by the caller via MEM_FREE(). If an error
2736 * occurs, NULL is returned.
2737 */
2738 char *
util_cgi_xlate(char * str)2739 util_cgi_xlate(char *str)
2740 {
2741 char *p,
2742 *q,
2743 *new_str;
2744
2745 if ((q = new_str = (char *) MEM_ALLOC("cgi_xlate_newstr",
2746 (strlen(str) * 3) + 5)) == NULL)
2747 return NULL;
2748
2749 /* Skip leading whitespaces */
2750 p = str;
2751 while (isspace((int) *p))
2752 p++;
2753
2754 /* Process the string */
2755 while (*p != '\0') {
2756 if (isspace((int) *p)) {
2757 /* Skip remaining consecutive white spaces */
2758 while (isspace((int) *(++p)))
2759 ;
2760 if (*p == '\0')
2761 break; /* End of string reached */
2762 else {
2763 /* Substitute white spaces with separator */
2764 *q = '+';
2765 q++;
2766 }
2767 }
2768 else if ((ispunct((int) *p) &&
2769 *p != '_' && *p != '.' &&
2770 *p != '*' && *p != '@') ||
2771 (*p & 0x80)) {
2772 /* Need URL-encoding */
2773 (void) sprintf(q, "%%%02X", (int) (*p));
2774 q += 3;
2775 p++;
2776 }
2777 else if (isprint((int) *p)) {
2778 /* Printable character */
2779 *q = (char) (isupper((int) *p) ?
2780 tolower((int) *p) : (*p));
2781 q++;
2782 p++;
2783 }
2784 else
2785 p++;
2786 }
2787 *q = '\0';
2788
2789 return (new_str);
2790 }
2791
2792
2793 /*
2794 * util_urlchk
2795 * Check a URL for syntax and to see if it's local or remote.
2796 * Return a pointer to the actual start of the path name (past the
2797 * protocol, hostname and port portion if it's local), and the
2798 * length of the URL string.
2799 *
2800 * Args:
2801 * url - Ths URL string to check
2802 * filepath - The character pointer to set to the beginning of the
2803 * file path.
2804 * len - The length of the URL string
2805 *
2806 * Return:
2807 * A bitmap consisting of the following bits:
2808 * IS_LOCAL_URL
2809 * IS_REMOTE_URL
2810 * NEED_PREPEND_HTTP
2811 * NEED_PREPEND_FTP
2812 * NEED_PREPEND_MAILTO
2813 */
2814 int
util_urlchk(char * url,char ** filepath,int * len)2815 util_urlchk(char *url, char **filepath, int *len)
2816 {
2817 int i,
2818 ret;
2819 char *p,
2820 *q,
2821 *p1,
2822 *p2,
2823 *p3,
2824 *p4,
2825 *p5,
2826 *p6,
2827 *p7,
2828 *endstr,
2829 sav;
2830
2831 ret = 0;
2832 *filepath = url;
2833 endstr = url + strlen(url);
2834
2835 /* Look for the end of the URL in case we're in the beginning of
2836 * a multi-word string
2837 */
2838 if ((p2 = strchr(url, ' ')) == NULL)
2839 p2 = endstr;
2840 if ((p3 = strchr(url, '(')) == NULL)
2841 p3 = endstr;
2842 if ((p4 = strchr(url, ')')) == NULL)
2843 p4 = endstr;
2844 if ((p5 = strchr(url, '\t')) == NULL)
2845 p5 = endstr;
2846 if ((p6 = strchr(url, '\r')) == NULL)
2847 p6 = endstr;
2848 if ((p7 = strchr(url, '\n')) == NULL)
2849 p7 = endstr;
2850
2851 p1 = (p2 < p3) ? p2 : p3;
2852 p2 = (p4 < p5) ? p4 : p5;
2853 p3 = (p6 < p7) ? p6 : p7;
2854 p1 = (p1 < p2) ? p1 : p2;
2855 p1 = (p1 < p3) ? p1 : p3;
2856
2857 sav = *p1;
2858 *p1 = '\0';
2859
2860 /* Check to see if it's a URL */
2861 if (util_strncasecmp(url, "www.", 4) == 0) {
2862 /* Possibly: www.xyz.com */
2863 p = url + 4;
2864 if (*p == '\0') {
2865 /* Failed check: incomplete URL */
2866 ret = 0;
2867 }
2868 else
2869 ret = (NEED_PREPEND_HTTP | IS_REMOTE_URL);
2870 }
2871 else if (util_strncasecmp(url, "ftp.", 4) == 0) {
2872 /* Possibly: ftp.xyz.com */
2873 p = url + 4;
2874 if (*p == '\0') {
2875 /* Failed check: incomplete URL */
2876 ret = 0;
2877 }
2878 else
2879 ret = (NEED_PREPEND_FTP | IS_REMOTE_URL);
2880 }
2881 else if (util_strncasecmp(url, "mailto:", 7) != 0 &&
2882 (p = strchr(url, '@')) != NULL) {
2883 /* Possibly: user@xyz.com */
2884 if (p == url || *(++p) == '\0') {
2885 /* Failed check: @ is at beginning or end of word */
2886 ret = 0;
2887 }
2888 else
2889 ret = (NEED_PREPEND_MAILTO | IS_REMOTE_URL);
2890 }
2891 else {
2892 /* Check against list of URL protos */
2893 for (i = 0; url_protolist[i].protocol != NULL; i++) {
2894 if (util_strncasecmp(url, url_protolist[i].protocol,
2895 strlen(url_protolist[i].protocol)) != 0) {
2896 continue;
2897 }
2898
2899 if (url_protolist[i].islocal)
2900 ret = IS_LOCAL_URL;
2901 else
2902 ret = IS_REMOTE_URL;
2903 break;
2904 }
2905
2906 if (ret == 0) {
2907 /* Didn't match any URL protos: assume to be a
2908 * local file path but not a URL.
2909 */
2910 *len = strlen(url);
2911 *p1 = sav;
2912 return (ret);
2913 }
2914
2915 if (ret == IS_LOCAL_URL) {
2916 if (util_strncasecmp(url, "file://localhost", 16) == 0)
2917 p2 = url + 16;
2918 else if (util_strncasecmp(url, "file://", 7) == 0) {
2919 p2 = url + 7;
2920 if ((q = strchr(p2, '/')) != NULL)
2921 p2 = q + 1;
2922 else
2923 p2 += strlen(p2);
2924 }
2925 else if (util_strncasecmp(url, "file:", 5) == 0)
2926 p2 = url + 5;
2927
2928 *filepath = p2;
2929 }
2930
2931 if ((p = strchr(url, ':')) == NULL) {
2932 /* Failed check: no colon */
2933 ret = 0;
2934 }
2935 if (*(++p) == '\0') {
2936 /* Failed check: incomplete URL */
2937 ret = 0;
2938 }
2939 }
2940
2941 /* A general sanity check for remote URLs */
2942 if ((ret & IS_REMOTE_URL) &&
2943 ((p3 = strchr(p, '.')) == NULL || p3 == p || *(p3+1) == '\0' ||
2944 !isalnum((int) *(p3-1)) || !isalnum((int) *(p3+1)))) {
2945 /* Failed check: Expect at least one dot and there
2946 * has to be at least a valid character before and after
2947 * the dot.
2948 */
2949 ret = 0;
2950 }
2951
2952 *len = (ret == 0) ? 0 : (int) strlen(url);
2953 *p1 = sav;
2954
2955 return (ret);
2956 }
2957
2958
2959 /*
2960 * util_urlencode
2961 * Fix a string to become a legal URL. In particular, special character
2962 * encodings are performed.
2963 *
2964 * Args:
2965 * str - Input string
2966 *
2967 * Return:
2968 * Output string. This is internally allocated and should be
2969 * freed by the caller via MEM_FREE() when done.
2970 */
2971 char *
util_urlencode(char * str)2972 util_urlencode(char *str)
2973 {
2974 char *p,
2975 *q,
2976 *r;
2977
2978 if (str == NULL || str[0] == '\0')
2979 return NULL;
2980
2981 if ((p = (char *) MEM_ALLOC("urlenc", strlen(str) * 3 + 1)) == NULL)
2982 return NULL;
2983
2984 for (q = str, r = p; *q != '\0'; q++) {
2985 switch (*q) {
2986 case '%':
2987 case ' ':
2988 case '\t':
2989 case '?':
2990 case '"':
2991 case '`':
2992 case '\'':
2993 case '|':
2994 case '~':
2995 case '#':
2996 case ':':
2997 case ';':
2998 case '@':
2999 case '=':
3000 case '&':
3001 case '<':
3002 case '[':
3003 case ']':
3004 case '(':
3005 case ')':
3006 case '{':
3007 case '}':
3008 /* Encode to hex notation */
3009 (void) sprintf(r, "%%%02X", *q);
3010 r += 3;
3011 break;
3012 default:
3013 if ((unsigned int) *q <= 0x1f ||
3014 (unsigned int) *q >= 0x7f) {
3015 /* Control characters or extended ASCII */
3016 /* Encode to hex notation */
3017 (void) sprintf(r, "%%%02X", *q);
3018 r += 3;
3019 break;
3020 }
3021 /* Copy verbatim */
3022 *r++ = *q;
3023 break;
3024 }
3025 }
3026 *r = '\0';
3027
3028 return (p);
3029 }
3030
3031
3032 /*
3033 * util_html_fputs
3034 * Similar to fputs(3), but handles HTML escape sequence, newline and
3035 * tab translation. If specified, it also tries to recognizes URLs
3036 * in the string and turns them into links. In addition, the caller
3037 * may specify the font.
3038 *
3039 * Args:
3040 * str - The string to write
3041 * fp - The file stream pointer
3042 * urldet - Whether to look for URLs and turn them into links
3043 * fontname - The font name to use, or NULL to use default font
3044 * fontsize - The font size to use. or 0 to use default size
3045 *
3046 * Return:
3047 * Nothing.
3048 */
3049 void
util_html_fputs(char * str,FILE * fp,bool_t urldet,char * fontname,int fontsize)3050 util_html_fputs(
3051 char *str,
3052 FILE *fp,
3053 bool_t urldet,
3054 char *fontname,
3055 int fontsize
3056 )
3057 {
3058 int i,
3059 j,
3060 k,
3061 len,
3062 urlt;
3063 char *cp,
3064 *p;
3065
3066 if (fontname != NULL || fontsize != 0) {
3067 (void) fputs("<FONT", fp);
3068 if (fontname != NULL)
3069 (void) fprintf(fp, " FACE=\"%s\"", fontname);
3070 if (fontsize != 0)
3071 (void) fprintf(fp, " SIZE=\"%d\"", fontsize);
3072 (void) fputs(">\n", fp);
3073 }
3074
3075 i = k = 0;
3076 for (cp = str; *cp != '\0'; cp++) {
3077 switch (*cp) {
3078 case ' ':
3079 (void) fprintf(fp, "%s",
3080 (k > 0) ?
3081 HTML_ESC_SPC :
3082 ((*(cp + 1) == ' ') ? HTML_ESC_SPC : " ")
3083 );
3084 k++;
3085 break;
3086 case '\n':
3087 (void) fputs("<BR>\n", fp);
3088 i = -1;
3089 k = 0;
3090 break;
3091 case '\t':
3092 for (j = CHARS_PER_TAB; j > i; j--)
3093 (void) fputs(HTML_ESC_SPC, fp);
3094 i = -1;
3095 k = 0;
3096 break;
3097 case '<':
3098 (void) fputs("<", fp);
3099 k = 0;
3100 break;
3101 case '>':
3102 (void) fputs(">", fp);
3103 k = 0;
3104 break;
3105 case '&':
3106 (void) fputs("&", fp);
3107 k = 0;
3108 break;
3109 case '"':
3110 (void) fputs(""", fp);
3111 k = 0;
3112 break;
3113 default:
3114 if (!urldet ||
3115 (urlt = util_urlchk(cp, &p, &len)) == 0) {
3116 /* No URL check requested or not a valid URL */
3117 (void) fputc(*cp, fp);
3118 k = 0;
3119 break;
3120 }
3121
3122 /* Output URL and add hyperlink */
3123 if (urlt & NEED_PREPEND_HTTP)
3124 p = "http://";
3125 else if (urlt & NEED_PREPEND_FTP)
3126 p = "ftp://";
3127 else if (urlt & NEED_PREPEND_MAILTO)
3128 p = "mailto:";
3129 else
3130 p = "";
3131
3132 (void) fprintf(fp, "<A HREF=\"%s", p);
3133 (void) fwrite(cp, len, 1, fp);
3134 (void) fputs("\">", fp);
3135 (void) fwrite(cp, len, 1, fp);
3136 (void) fputs("</A>", fp);
3137
3138 i = ((i + len) % CHARS_PER_TAB) - 1;
3139
3140 cp += (len - 1);
3141 k = 0;
3142 break;
3143 }
3144 if (++i == CHARS_PER_TAB)
3145 i = 0;
3146 }
3147
3148 if (fontname != NULL || fontsize != 0)
3149 (void) fputs("\n</FONT>\n", fp);
3150 }
3151
3152
3153 /*
3154 * util_chset_open
3155 * Initialize character set conversion as specified.
3156 *
3157 * Args:
3158 * flags - CHSET_UTF8_TO_LOCALE or CHSET_LOCALE_TO_UTF8.
3159 *
3160 * Return:
3161 * A character set conversion descriptor pointer.
3162 */
3163 chset_conv_t *
util_chset_open(int flags)3164 util_chset_open(int flags)
3165 {
3166 chset_conv_t *cp;
3167
3168 cp = (chset_conv_t *)(void *) MEM_ALLOC(
3169 "chset_conv_t", sizeof(chset_conv_t)
3170 );
3171 if (cp == NULL) {
3172 (void) fprintf(errfp, "Error: %s\n", app_data.str_nomemory);
3173 return NULL;
3174 }
3175
3176 cp->flags = flags;
3177 cp->lconv_desc = NULL;
3178
3179 #ifdef HAS_ICONV
3180 if (app_data.chset_xlat == CHSET_XLAT_ICONV) {
3181 if (flags == CHSET_UTF8_TO_LOCALE) {
3182 cp->lconv_desc = util_lconv_open(
3183 app_data.lang_utf8, NULL
3184 );
3185 }
3186 else if (flags == CHSET_LOCALE_TO_UTF8) {
3187 cp->lconv_desc = util_lconv_open(
3188 NULL, app_data.lang_utf8
3189 );
3190 }
3191 }
3192 #endif
3193
3194 return (cp);
3195 }
3196
3197
3198 /*
3199 * util_chset_close
3200 * Shut down character set conversion.
3201 *
3202 * Args:
3203 * cp - Descriptor pointer previously returned by util_chset_open.
3204 *
3205 * Return:
3206 * Nothing.
3207 */
3208 void
util_chset_close(chset_conv_t * cp)3209 util_chset_close(chset_conv_t *cp)
3210 {
3211 if (cp != NULL) {
3212 if (cp->lconv_desc != NULL)
3213 util_lconv_close(cp->lconv_desc);
3214 MEM_FREE(cp);
3215 }
3216 }
3217
3218
3219 /*
3220 * util_chset_conv
3221 * Perform character set conversion on a string. The output string
3222 * storage is internally allocated and should be freed by the caller
3223 * via MEM_FREE when done.
3224 *
3225 * Args:
3226 * cp - Descriptor pointer previously returned by util_chset_open.
3227 * from - The input string
3228 * to - Address pointer to the output string
3229 * roe - Revert-on-error boolean flag. See util_utf8_to_iso8859()
3230 * for details.
3231 *
3232 * Returns:
3233 * TRUE - success
3234 * FALSE - failure
3235 */
3236 bool_t
util_chset_conv(chset_conv_t * cp,char * from,char ** to,bool_t roe)3237 util_chset_conv(chset_conv_t *cp, char *from, char **to, bool_t roe)
3238 {
3239 if (cp == NULL || to == NULL)
3240 return FALSE; /* Invalid arg */
3241
3242 if (*to != NULL) {
3243 MEM_FREE(*to);
3244 *to = NULL;
3245 }
3246
3247 if (from == NULL || *from == '\0') {
3248 /* Null or empty string */
3249 *to = NULL;
3250 return TRUE;
3251 }
3252
3253 switch (app_data.chset_xlat) {
3254 case CHSET_XLAT_ICONV:
3255 /* Use the iconv(3) library to do the conversion */
3256 if (cp->lconv_desc != NULL &&
3257 util_do_lconv(cp->lconv_desc, from, to)) {
3258 return TRUE;
3259 }
3260 /*FALLTHROUGH*/
3261
3262 case CHSET_XLAT_ISO8859:
3263 if (cp->flags == CHSET_UTF8_TO_LOCALE) {
3264 /* Use our own UTF-8 to ISO8859 conversion function */
3265 return (util_utf8_to_iso8859(from, to, roe));
3266 }
3267 else if (cp->flags == CHSET_LOCALE_TO_UTF8) {
3268 /* Use our own ISO8859 to UTF-8 conversion function */
3269 return (util_iso8859_to_utf8(from, to));
3270 }
3271 /*FALLTHROUGH*/
3272
3273 case CHSET_XLAT_NONE:
3274 default:
3275 /* No conversion specified: Just dup the string */
3276 return (util_newstr(to, from));
3277 }
3278 }
3279
3280
3281 /*
3282 * util_bswap16
3283 * 16-bit little-endian to big-endian byte-swap routine.
3284 * On a big-endian system architecture this routine has no effect.
3285 *
3286 * Args:
3287 * x - The data to be swapped
3288 *
3289 * Return:
3290 * The swapped data.
3291 */
3292 word16_t
util_bswap16(word16_t x)3293 util_bswap16(word16_t x)
3294 {
3295 #if _BYTE_ORDER_ == _L_ENDIAN_
3296 word16_t ret;
3297
3298 ret = (x & 0x00ff) << 8;
3299 ret |= (word16_t) (x & 0xff00) >> 8;
3300 return (ret);
3301 #else
3302 return (x);
3303 #endif
3304 }
3305
3306
3307 /*
3308 * util_bswap24
3309 * 24-bit little-endian to big-endian byte-swap routine.
3310 * On a big-endian system architecture this routine has no effect.
3311 *
3312 * Args:
3313 * x - The data to be swapped
3314 *
3315 * Return:
3316 * The swapped data.
3317 */
3318 word32_t
util_bswap24(word32_t x)3319 util_bswap24(word32_t x)
3320 {
3321 #if _BYTE_ORDER_ == _L_ENDIAN_
3322 word32_t ret;
3323
3324 ret = (x & 0x0000ff) << 16;
3325 ret |= (x & 0x00ff00);
3326 ret |= (x & 0xff0000) >> 16;
3327 return (ret);
3328 #else
3329 return (x);
3330 #endif
3331 }
3332
3333
3334 /*
3335 * util_bswap32
3336 * 32-bit little-endian to big-endian byte-swap routine.
3337 * On a big-endian system architecture this routine has no effect.
3338 *
3339 * Args:
3340 * x - The data to be swapped
3341 *
3342 * Return:
3343 * The swapped data.
3344 */
3345 word32_t
util_bswap32(word32_t x)3346 util_bswap32(word32_t x)
3347 {
3348 #if _BYTE_ORDER_ == _L_ENDIAN_
3349 word32_t ret;
3350
3351 ret = (x & 0x000000ff) << 24;
3352 ret |= (x & 0x0000ff00) << 8;
3353 ret |= (x & 0x00ff0000) >> 8;
3354 ret |= (x & 0xff000000) >> 24;
3355 return (ret);
3356 #else
3357 return (x);
3358 #endif
3359 }
3360
3361
3362 /*
3363 * util_bswap64
3364 * 64-bit little-endian to big-endian byte-swap routine.
3365 * On a big-endian system architecture this routine has no effect.
3366 *
3367 * Args:
3368 * x - The data to be swapped
3369 *
3370 * Return:
3371 * The swapped data.
3372 */
3373 word64_t
util_bswap64(word64_t x)3374 util_bswap64(word64_t x)
3375 {
3376 #if _BYTE_ORDER_ == _L_ENDIAN_
3377 word64_t ret;
3378
3379 #ifdef FAKE_64BIT
3380 ret.v[0] = util_bswap32(x.v[1]);
3381 ret.v[1] = util_bswap32(x.v[0]);
3382 #else
3383 #if (defined(__GNUC__) && __GNUC__ > 1) || defined(HAS_LONG_LONG)
3384 ret = (x & 0x00000000000000ffLL) << 56;
3385 ret |= (x & 0x000000000000ff00LL) << 40;
3386 ret |= (x & 0x0000000000ff0000LL) << 24;
3387 ret |= (x & 0x00000000ff000000LL) << 8;
3388 ret |= (x & 0x000000ff00000000LL) >> 8;
3389 ret |= (x & 0x0000ff0000000000LL) >> 24;
3390 ret |= (x & 0x00ff000000000000LL) >> 40;
3391 ret |= (x & 0xff00000000000000LL) >> 56;
3392 #else
3393 ret = (x & 0x00000000000000ff) << 56;
3394 ret |= (x & 0x000000000000ff00) << 40;
3395 ret |= (x & 0x0000000000ff0000) << 24;
3396 ret |= (x & 0x00000000ff000000) << 8;
3397 ret |= (x & 0x000000ff00000000) >> 8;
3398 ret |= (x & 0x0000ff0000000000) >> 24;
3399 ret |= (x & 0x00ff000000000000) >> 40;
3400 ret |= (x & 0xff00000000000000) >> 56;
3401 #endif /* __GNUC__ > 1 || HAS_LONG_LONG */
3402 #endif /* FAKE_64BIT */
3403 return (ret);
3404 #else
3405 return (x);
3406 #endif
3407 }
3408
3409
3410 /*
3411 * util_lswap16
3412 * 16-bit big-endian to little-endian byte-swap routine.
3413 * On a little-endian system architecture this routine has no effect.
3414 *
3415 * Args:
3416 * x - The data to be swapped
3417 *
3418 * Return:
3419 * The swapped data.
3420 */
3421 word16_t
util_lswap16(word16_t x)3422 util_lswap16(word16_t x)
3423 {
3424 #if _BYTE_ORDER_ == _L_ENDIAN_
3425 return (x);
3426 #else
3427 word16_t ret;
3428
3429 ret = (x & 0x00ff) << 8;
3430 ret |= (word16_t) (x & 0xff00) >> 8;
3431 return (ret);
3432 #endif
3433 }
3434
3435
3436 /*
3437 * util_lswap24
3438 * 24-bit big-endian to little-endian byte-swap routine.
3439 * On a little-endian system architecture this routine has no effect.
3440 *
3441 * Args:
3442 * x - The data to be swapped
3443 *
3444 * Return:
3445 * The swapped data.
3446 */
3447 word32_t
util_lswap24(word32_t x)3448 util_lswap24(word32_t x)
3449 {
3450 #if _BYTE_ORDER_ == _L_ENDIAN_
3451 return (x);
3452 #else
3453 word32_t ret;
3454
3455 ret = (x & 0x0000ff) << 16;
3456 ret |= (x & 0x00ff00);
3457 ret |= (x & 0xff0000) >> 16;
3458 return (ret);
3459 #endif
3460 }
3461
3462
3463 /*
3464 * util_lswap32
3465 * 32-bit big-endian to little-endian byte-swap routine.
3466 * On a little-endian system architecture this routine has no effect.
3467 *
3468 * Args:
3469 * x - The data to be swapped
3470 *
3471 * Return:
3472 * The swapped data.
3473 */
3474 word32_t
util_lswap32(word32_t x)3475 util_lswap32(word32_t x)
3476 {
3477 #if _BYTE_ORDER_ == _L_ENDIAN_
3478 return (x);
3479 #else
3480 word32_t ret;
3481
3482 ret = (x & 0x000000ff) << 24;
3483 ret |= (x & 0x0000ff00) << 8;
3484 ret |= (x & 0x00ff0000) >> 8;
3485 ret |= (x & 0xff000000) >> 24;
3486 return (ret);
3487 #endif
3488 }
3489
3490
3491 /*
3492 * util_lswap64
3493 * 64-bit big-endian to little-endian byte-swap routine.
3494 * On a little-endian system architecture this routine has no effect.
3495 *
3496 * Args:
3497 * x - The data to be swapped
3498 *
3499 * Return:
3500 * The swapped data.
3501 */
3502 word64_t
util_lswap64(word64_t x)3503 util_lswap64(word64_t x)
3504 {
3505 #if _BYTE_ORDER_ == _L_ENDIAN_
3506 return (x);
3507 #else
3508 word64_t ret;
3509
3510 #ifdef FAKE_64BIT
3511 ret.v[0] = util_bswap32(x.v[1]);
3512 ret.v[1] = util_bswap32(x.v[0]);
3513 #else
3514 ret = (x & 0x00000000000000ff) << 56;
3515 ret |= (x & 0x000000000000ff00) << 40;
3516 ret |= (x & 0x0000000000ff0000) << 24;
3517 ret |= (x & 0x00000000ff000000) << 8;
3518 ret |= (x & 0x000000ff00000000) >> 8;
3519 ret |= (x & 0x0000ff0000000000) >> 24;
3520 ret |= (x & 0x00ff000000000000) >> 40;
3521 ret |= (x & 0xff00000000000000) >> 56;
3522 #endif /* FAKE_64BIT */
3523 return (ret);
3524 #endif
3525 }
3526 /*
3527 * util_dbgdump
3528 * Dump a data buffer to screen.
3529 *
3530 * Args:
3531 * title - Message banner
3532 * data - Address of data
3533 * len - Number of bytes to dump
3534 *
3535 * Return:
3536 * Nothing.
3537 */
3538 void
util_dbgdump(char * title,byte_t * data,int len)3539 util_dbgdump(char *title, byte_t *data, int len)
3540 {
3541 int i, j, k, n,
3542 lines;
3543
3544 if (title == NULL || data == NULL || len <= 0)
3545 return;
3546
3547 (void) fprintf(errfp, "\n%s:", title);
3548
3549 lines = ((len - 1) / 16) + 1;
3550
3551 for (i = 0, k = 0; i < lines; i++) {
3552 (void) fprintf(errfp, "\n%04x ", k);
3553
3554 for (j = 0, n = k; j < 16; j++, k++) {
3555 if (k < len)
3556 (void) fprintf(errfp, "%02x ", *(data + k));
3557 else
3558 (void) fprintf(errfp, "-- ");
3559
3560 if (j == 7)
3561 (void) fprintf(errfp, " ");
3562 }
3563
3564 (void) fprintf(errfp, " ");
3565
3566 for (j = 0, k = n; j < 16; j++, k++) {
3567 if (k < len) {
3568 (void) fprintf(errfp, "%c",
3569 isprint(*(data + k)) ? *(data + k) : '.'
3570 );
3571 }
3572 else
3573 (void) fprintf(errfp, ".");
3574 }
3575 }
3576
3577 (void) fprintf(errfp, "\n");
3578 }
3579
3580
3581 #ifdef FAKE_64BIT
3582 /*
3583 * util_assign64
3584 * Copy a 32-bit integer into a "fake" 64-bit location. Sign-extend
3585 * the value if necessary. This assumes a 2's-compliment
3586 * representation of integer values.
3587 *
3588 * Args:
3589 * val - The source 32-bit integer value
3590 *
3591 * Return:
3592 * The 64-bit quantity.
3593 */
3594 word64_t
util_assign64(sword32_t val)3595 util_assign64(sword32_t val)
3596 {
3597 word64_t ret;
3598
3599 ret.v[LO_WORD] = (word32_t) val;
3600 if (val < 0)
3601 ret.v[HI_WORD] = (word32_t) 0xffffffff;
3602 else
3603 ret.v[HI_WORD] = 0;
3604
3605 return (ret);
3606 }
3607
3608
3609 /*
3610 * util_assign32
3611 * Copy a "fake" 64-bit integer into a 32-bit location.
3612 *
3613 * Args:
3614 * val - The source 64-bit integer value
3615 *
3616 * Return:
3617 * The 32-bit quantity.
3618 */
3619 word32_t
util_assign32(word64_t val)3620 util_assign32(word64_t val)
3621 {
3622 return (val.v[LO_WORD]);
3623 }
3624 #endif
3625
3626
3627 #ifdef __VMS
3628 /*
3629 * The following section provide UNIX-like functionality for Digital OpenVMS
3630 */
3631
3632 /* Function prototypes */
3633 extern void delete();
3634
3635 #ifdef VMS_USE_OWN_DIRENT
3636
3637 extern int LIB$FIND_FILE();
3638 extern void LIB$FIND_FILE_END();
3639 extern void SYS$FILESCAN();
3640
3641 typedef struct {
3642 short length;
3643 short component;
3644 int address;
3645 int term;
3646 } item_list;
3647
3648
3649 /*
3650 * util_opendir
3651 * Emulate a UNIX opendir by clearing the context value, and creating
3652 * the wild card search by appending *.* to the path name.
3653 * (See opendir(2) on UNIX systems)
3654 *
3655 * Args:
3656 * path - directory path to open
3657 *
3658 * Return:
3659 * Pointer to the DIR structure descriptor
3660 */
3661 DIR *
util_opendir(char * path)3662 util_opendir(char *path)
3663 {
3664 static DIR dir;
3665 static struct dirent ent;
3666
3667 context = 0;
3668 (void) sprintf(ent.d_name, "%s*.*", path);
3669 dir.dd_buf = &ent;
3670 return (&dir);
3671 }
3672
3673
3674 /*
3675 * util_closedir
3676 * Emulate a UNIX closedir by call LIB$FIND_FILE_END to close
3677 * the file context. (End the wild card search)
3678 * (See closedir(2) on UNIX systems)
3679 *
3680 * Args:
3681 * dp - pointer to the directory's DIR structure
3682 *
3683 * Return:
3684 * Nothing.
3685 */
3686 void
util_closedir(DIR * dp)3687 util_closedir(DIR *dp)
3688 {
3689 LIB$FIND_FILE_END(&context);
3690 }
3691
3692
3693 /*
3694 * util_readdir
3695 * Emulate a UNIX readdir by calling LIB$FIND_FILE, and SYS$FILESCAN
3696 * to return the file name back.
3697 * (See readdir(2) on UNIX systems)
3698 *
3699 * Args:
3700 * dp - pointer to the directory's DIR structure
3701 *
3702 * Return:
3703 * Pointer to the dirent structure pertaining to a directory entry
3704 */
3705 struct dirent *
util_readdir(DIR * dp)3706 util_readdir(DIR *dp)
3707 {
3708 int dir_desc[2],
3709 desc[2],
3710 i;
3711 char *p,
3712 *file[FILE_PATH_SZ];
3713 item_list list;
3714 static struct dirent ent;
3715
3716 desc[0] = FILE_PATH_SZ;
3717 desc[1] = (int) file;
3718
3719 dir_desc[0] = FILE_PATH_SZ;
3720 dir_desc[1] = (int) dp->dd_buf->d_name;
3721
3722 if (LIB$FIND_FILE(dir_desc, desc, &context) & 0x01) {
3723 list.length = 0;
3724 list.component = FSCN$_NAME;
3725 list.address = 0;
3726 list.term = 0;
3727
3728 SYS$FILESCAN(desc, &list, 0, 0, 0);
3729
3730 p = (char *) list.address;
3731 p[list.length] = '\0';
3732
3733 for (p = (char *) list.address; *p != '\0'; p++)
3734 *p = tolower(*p);
3735
3736 (void) strcpy(ent.d_name, (char *) list.address);
3737 return (&ent);
3738 }
3739 else
3740 return NULL;
3741 }
3742
3743 #endif /* VMS_USE_OWN_DIRENT */
3744
3745
3746 /*
3747 * util_waitpid
3748 * Emulate a UNIX waitpid by doing a wait call
3749 * (see waitpid(2) on UNIX systems)
3750 *
3751 * Args:
3752 * pid - process ID to wait for
3753 * statloc - pointer to wait status information
3754 * options - wait options
3755 * Return:
3756 * The process ID of the process that caused this call to stop
3757 * waiting.
3758 */
3759 pid_t
util_waitpid(pid_t pid,int * statloc,int options)3760 util_waitpid(pid_t pid, int *statloc, int options)
3761 {
3762 pid_t ret;
3763
3764 ret = wait(statloc);
3765
3766 /* Under VMS a vfork() call does not create a child process unless
3767 * a real process is created. In the cases where the child does
3768 * not follow the vfork with a system() or exec() call to create
3769 * a real subprocess, we need to fake things out.
3770 */
3771 if (ret < 0)
3772 ret = pid;
3773
3774 /* VMS returns a 1 for success. Patch it to zero to
3775 * make this function compatible with UNIX.
3776 */
3777 if (*statloc == 1)
3778 *statloc = 0;
3779
3780 return (ret);
3781 }
3782
3783
3784 /*
3785 * util_unlink
3786 * Emulate a UNIX unlink call
3787 * (See unlink(2) on UNIX systems)
3788 *
3789 * Args:
3790 * file - file path name to unlink
3791 *
3792 * Return:
3793 * 0 - Success
3794 * -1 - Failure
3795 */
3796 int
util_unlink(char * file)3797 util_unlink(char *file)
3798 {
3799 delete(file);
3800 return 0;
3801 }
3802
3803
3804 /*
3805 * util_link
3806 * Emulate a UNIX link call by copying FILE1 to FILE2
3807 * (See link(2) on UNIX systems)
3808 *
3809 * Args:
3810 * file1 - source file
3811 * file2 - destination file
3812 *
3813 * Return:
3814 * 0 - Success
3815 * -1 - Failure
3816 */
3817 int
util_link(char * file1,char * file2)3818 util_link(char *file1, char *file2)
3819 {
3820 FILE *fp1,
3821 *fp2;
3822 char buf[STR_BUF_SZ * 16];
3823
3824 fp1 = fopen(file1, "r");
3825 fp2 = fopen(file2, "w");
3826
3827 if (fp1 == NULL || fp2 == NULL)
3828 return -1;
3829
3830 while (fgets(buf, sizeof(buf), fp1) != NULL)
3831 (void) fprintf(fp2, "%s", buf);
3832
3833 (void) fclose(fp1);
3834 (void) fclose(fp2);
3835
3836 return 0;
3837 }
3838
3839
3840 /*
3841 * util_vms_urlconv
3842 * URL format translation function between UNIX-style and VMS-style
3843 * path name syntax. This is a hack to work around the inconsistent
3844 * URL formats required by the browser under different circumstances.
3845 * The caller should MEM_FREE() the return string buffer.
3846 *
3847 * Args:
3848 * url - The input URL string
3849 * dir - Conversion direction: UNIX_2_VMS or VMS_2_UNIX
3850 *
3851 * Return:
3852 * The converted URL string, or NULL is an error is encountered.
3853 */
3854 char *
util_vms_urlconv(char * url,int dir)3855 util_vms_urlconv(char *url, int dir)
3856 {
3857 int i;
3858 char *p1,
3859 *p2,
3860 *p3,
3861 *p4,
3862 *buf;
3863 bool_t first;
3864
3865 if (util_urlchk(url, &p1, &i) & IS_REMOTE_URL)
3866 return (url); /* Remote URL: don't touch it */
3867
3868 buf = (char *) MEM_ALLOC(
3869 "util_vms_urlconv",
3870 (strlen(url) * 2) + FILE_PATH_SZ
3871 );
3872 if (buf == NULL)
3873 return NULL;
3874
3875 buf[0] = '\0';
3876
3877 switch (dir) {
3878 case UNIX_2_VMS:
3879 first = TRUE;
3880 if (*p1 == '/')
3881 p1++;
3882
3883 for (i = 0; (p2 = strchr(p1, '/')) != NULL; i++) {
3884 *p2 = '\0';
3885
3886 if (i == 0 && (strchr(p1, '$') != NULL)) {
3887 (void) sprintf(buf, "%s:", p1);
3888 }
3889 else if (first) {
3890 first = FALSE;
3891 (void) sprintf(buf, "%s%c%s",
3892 buf, DIR_BEG, p1);
3893 }
3894 else {
3895 (void) sprintf(buf, "%s%c%s",
3896 buf, DIR_SEP, p1);
3897 }
3898
3899 *p2 = '/';
3900 p1 = p2 + 1;
3901 }
3902
3903 if (first) {
3904 if (strchr(p1, '$') != NULL)
3905 (void) sprintf(buf, "%s:", p1);
3906 else
3907 (void) sprintf(buf, "%s%s", buf, p1);
3908 }
3909 else
3910 (void) sprintf(buf, "%s%c%s", buf, DIR_END, p1);
3911
3912 break;
3913
3914 case VMS_2_UNIX:
3915 (void) strcpy(buf, "file:");
3916
3917 if ((p2 = strchr(p1, ':')) != NULL) {
3918 *p2 = '\0';
3919 (void) sprintf(buf, "%s//localhost/%s", buf, p1);
3920 *p2 = ':';
3921 p1 = p2 + 1;
3922 }
3923
3924 if ((p3 = strchr(p1, DIR_BEG)) != NULL) {
3925 p3++;
3926 if ((p4 = strchr(p3, DIR_END)) != NULL)
3927 *p4 = '\0';
3928
3929 while ((p1 = strchr(p3, DIR_SEP)) != NULL) {
3930 *p1 = '\0';
3931 (void) sprintf(buf, "%s/%s", buf, p3);
3932 *p1 = '.';
3933 p3 = p1 + 1;
3934 }
3935 (void) sprintf(buf, "%s/%s", buf, p3);
3936
3937 if (p4 != NULL) {
3938 *p4 = ']';
3939 p1 = p4 + 1;
3940 }
3941 }
3942
3943 if (p2 == NULL && p3 == NULL)
3944 (void) sprintf(buf, "%s%s", buf, p1);
3945 else
3946 (void) sprintf(buf, "%s/%s", buf, p1);
3947
3948 break;
3949
3950 default:
3951 MEM_FREE(buf);
3952 buf = NULL;
3953 break;
3954 }
3955
3956 return (buf);
3957 }
3958
3959 #endif /* __VMS */
3960
3961
3962 #ifdef MEM_DEBUG
3963 /*
3964 * For memory allocation debugging
3965 */
3966
3967
3968 /*
3969 * util_dbg_malloc
3970 * Wrapper for malloc(3).
3971 */
3972 void *
util_dbg_malloc(char * name,size_t size)3973 util_dbg_malloc(char *name, size_t size)
3974 {
3975 void *ptr;
3976
3977 if (size == 0) {
3978 (void) fprintf(stderr, "Malloc(%s, 0)\n", name);
3979 abort();
3980 }
3981
3982 ptr = _MEM_ALLOC(size);
3983 (void) fprintf(stderr, "Malloc(%s, %d) => 0x%x\n",
3984 name, (int) size, (int) ptr);
3985 return (ptr);
3986 }
3987
3988
3989 /*
3990 * util_dbg_realloc
3991 * Wrapper for realloc(3).
3992 */
3993 void *
util_dbg_realloc(char * name,void * ptr,size_t size)3994 util_dbg_realloc(char *name, void *ptr, size_t size)
3995 {
3996 void *nptr;
3997
3998 if (size == 0) {
3999 (void) fprintf(stderr, "Realloc(%s, 0x%x, 0)\n",
4000 name, (int) ptr);
4001 abort();
4002 }
4003
4004 nptr = _MEM_REALLOC(ptr, size);
4005 (void) fprintf(stderr, "Realloc(%s, 0x%x, %d) => 0x%x\n",
4006 name, (int) ptr, (int) size, (int) nptr);
4007 return (nptr);
4008 }
4009
4010
4011 /*
4012 * util_dbg_calloc
4013 * Wrapper for calloc(3).
4014 */
4015 void *
util_dbg_calloc(char * name,size_t nelem,size_t elsize)4016 util_dbg_calloc(char *name, size_t nelem, size_t elsize)
4017 {
4018 void *ptr;
4019
4020 if (nelem == 0 || elsize == 0) {
4021 (void) fprintf(stderr, "Calloc(%s, %d, %d)\n",
4022 name, (int) nelem, (int) elsize);
4023 abort();
4024 }
4025
4026 ptr = _MEM_CALLOC(nelem, elsize);
4027 (void) fprintf(stderr, "Calloc(%s, %d, %d) => 0x%x\n",
4028 name, (int) nelem, (int) elsize, (int) ptr);
4029 return (ptr);
4030 }
4031
4032
4033 /*
4034 * util_dbg_free
4035 * Wrapper for free(3).
4036 */
4037 void
util_dbg_free(void * ptr)4038 util_dbg_free(void *ptr)
4039 {
4040 (void) fprintf(stderr, "Free(0x%x)\n", ptr);
4041 _MEM_FREE(ptr);
4042 }
4043
4044 #endif /* MEM_DEBUG */
4045
4046