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 &nbsp; but some versions of NCSA Mosaic don't grok it */
90 #define HTML_ESC_SPC	"&#20;"		/* HTML for blank space */
91 #else
92 #define HTML_ESC_SPC	"&nbsp;"	/* 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("&lt;", fp);
3099 			k = 0;
3100 			break;
3101 		case '>':
3102 			(void) fputs("&gt;", fp);
3103 			k = 0;
3104 			break;
3105 		case '&':
3106 			(void) fputs("&amp;", fp);
3107 			k = 0;
3108 			break;
3109 		case '"':
3110 			(void) fputs("&quot;", 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