xref: /netbsd-src/external/bsd/file/dist/src/funcs.c (revision 627f7eb200a4419d89b531d55fccd2ee3ffdcde0)
1 /*	$NetBSD: funcs.c,v 1.19 2021/04/09 19:11:42 christos Exp $	*/
2 
3 /*
4  * Copyright (c) Christos Zoulas 2003.
5  * All Rights Reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice immediately at the beginning of the file, without modification,
12  *    this list of conditions, and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 #include "file.h"
30 
31 #ifndef	lint
32 #if 0
33 FILE_RCSID("@(#)$File: funcs.c,v 1.121 2021/02/05 22:29:07 christos Exp $")
34 #else
35 __RCSID("$NetBSD: funcs.c,v 1.19 2021/04/09 19:11:42 christos Exp $");
36 #endif
37 #endif	/* lint */
38 
39 #include "magic.h"
40 #include <assert.h>
41 #include <stdarg.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>	/* for pipe2() */
47 #endif
48 #if defined(HAVE_WCHAR_H)
49 #include <wchar.h>
50 #endif
51 #if defined(HAVE_WCTYPE_H)
52 #include <wctype.h>
53 #endif
54 #include <limits.h>
55 
56 #ifndef SIZE_MAX
57 #define SIZE_MAX	((size_t)~0)
58 #endif
59 
60 protected char *
61 file_copystr(char *buf, size_t blen, size_t width, const char *str)
62 {
63 	if (++width > blen)
64 		width = blen;
65 	strlcpy(buf, str, width);
66 	return buf;
67 }
68 
69 private void
70 file_clearbuf(struct magic_set *ms)
71 {
72 	free(ms->o.buf);
73 	ms->o.buf = NULL;
74 	ms->o.blen = 0;
75 }
76 
77 private int
78 file_checkfield(char *msg, size_t mlen, const char *what, const char **pp)
79 {
80 	const char *p = *pp;
81 	int fw = 0;
82 
83 	while (*p && isdigit((unsigned char)*p))
84 		fw = fw * 10 + (*p++ - '0');
85 
86 	*pp = p;
87 
88 	if (fw < 1024)
89 		return 1;
90 	if (msg)
91 		snprintf(msg, mlen, "field %s too large: %d", what, fw);
92 
93 	return 0;
94 }
95 
96 protected int
97 file_checkfmt(char *msg, size_t mlen, const char *fmt)
98 {
99 	for (const char *p = fmt; *p; p++) {
100 		if (*p != '%')
101 			continue;
102 		if (*++p == '%')
103 			continue;
104 		// Skip uninteresting.
105 		while (strchr("#0.'+- ", *p) != NULL)
106 			p++;
107 		if (*p == '*') {
108 			if (msg)
109 				snprintf(msg, mlen, "* not allowed in format");
110 			return -1;
111 		}
112 
113 		if (!file_checkfield(msg, mlen, "width", &p))
114 			return -1;
115 
116 		if (*p == '.') {
117 			p++;
118 			if (!file_checkfield(msg, mlen, "precision", &p))
119 				return -1;
120 		}
121 
122 		if (!isalpha((unsigned char)*p)) {
123 			if (msg)
124 				snprintf(msg, mlen, "bad format char: %c", *p);
125 			return -1;
126 		}
127 	}
128 	return 0;
129 }
130 
131 /*
132  * Like printf, only we append to a buffer.
133  */
134 protected int
135 file_vprintf(struct magic_set *ms, const char *fmt, va_list ap)
136 {
137 	int len;
138 	char *buf, *newstr;
139 	char tbuf[1024];
140 
141 	if (ms->event_flags & EVENT_HAD_ERR)
142 		return 0;
143 
144 	if (file_checkfmt(tbuf, sizeof(tbuf), fmt)) {
145 		file_clearbuf(ms);
146 		file_error(ms, 0, "Bad magic format `%s' (%s)", fmt, tbuf);
147 		return -1;
148 	}
149 
150 	len = vasprintf(&buf, fmt, ap);
151 	if (len < 0 || (size_t)len > 1024 || len + ms->o.blen > 1024 * 1024) {
152 		size_t blen = ms->o.blen;
153 		free(buf);
154 		file_clearbuf(ms);
155 		file_error(ms, 0, "Output buffer space exceeded %d+%zu", len,
156 		    blen);
157 		return -1;
158 	}
159 
160 	if (ms->o.buf != NULL) {
161 		len = asprintf(&newstr, "%s%s", ms->o.buf, buf);
162 		free(buf);
163 		if (len < 0)
164 			goto out;
165 		free(ms->o.buf);
166 		buf = newstr;
167 	}
168 	ms->o.buf = buf;
169 	ms->o.blen = len;
170 	return 0;
171 out:
172 	file_clearbuf(ms);
173 	file_error(ms, errno, "vasprintf failed");
174 	return -1;
175 }
176 
177 protected int
178 file_printf(struct magic_set *ms, const char *fmt, ...)
179 {
180 	int rv;
181 	va_list ap;
182 
183 	va_start(ap, fmt);
184 	rv = file_vprintf(ms, fmt, ap);
185 	va_end(ap);
186 	return rv;
187 }
188 
189 /*
190  * error - print best error message possible
191  */
192 /*VARARGS*/
193 __attribute__((__format__(__printf__, 3, 0)))
194 private void
195 file_error_core(struct magic_set *ms, int error, const char *f, va_list va,
196     size_t lineno)
197 {
198 	/* Only the first error is ok */
199 	if (ms->event_flags & EVENT_HAD_ERR)
200 		return;
201 	if (lineno != 0) {
202 		file_clearbuf(ms);
203 		(void)file_printf(ms, "line %" SIZE_T_FORMAT "u:", lineno);
204 	}
205 	if (ms->o.buf && *ms->o.buf)
206 		(void)file_printf(ms, " ");
207 	(void)file_vprintf(ms, f, va);
208 	if (error > 0)
209 		(void)file_printf(ms, " (%s)", strerror(error));
210 	ms->event_flags |= EVENT_HAD_ERR;
211 	ms->error = error;
212 }
213 
214 /*VARARGS*/
215 protected void
216 file_error(struct magic_set *ms, int error, const char *f, ...)
217 {
218 	va_list va;
219 	va_start(va, f);
220 	file_error_core(ms, error, f, va, 0);
221 	va_end(va);
222 }
223 
224 /*
225  * Print an error with magic line number.
226  */
227 /*VARARGS*/
228 protected void
229 file_magerror(struct magic_set *ms, const char *f, ...)
230 {
231 	va_list va;
232 	va_start(va, f);
233 	file_error_core(ms, 0, f, va, ms->line);
234 	va_end(va);
235 }
236 
237 protected void
238 file_oomem(struct magic_set *ms, size_t len)
239 {
240 	file_error(ms, errno, "cannot allocate %" SIZE_T_FORMAT "u bytes",
241 	    len);
242 }
243 
244 protected void
245 file_badseek(struct magic_set *ms)
246 {
247 	file_error(ms, errno, "error seeking");
248 }
249 
250 protected void
251 file_badread(struct magic_set *ms)
252 {
253 	file_error(ms, errno, "error reading");
254 }
255 
256 #ifndef COMPILE_ONLY
257 #define FILE_SEPARATOR "\n- "
258 
259 protected int
260 file_separator(struct magic_set *ms)
261 {
262 	return file_printf(ms, FILE_SEPARATOR);
263 }
264 
265 static void
266 trim_separator(struct magic_set *ms)
267 {
268 	size_t l;
269 
270 	if (ms->o.buf == NULL)
271 		return;
272 
273 	l = strlen(ms->o.buf);
274 	if (l < sizeof(FILE_SEPARATOR))
275 		return;
276 
277 	l -= sizeof(FILE_SEPARATOR) - 1;
278 	if (strcmp(ms->o.buf + l, FILE_SEPARATOR) != 0)
279 		return;
280 
281 	ms->o.buf[l] = '\0';
282 }
283 
284 static int
285 checkdone(struct magic_set *ms, int *rv)
286 {
287 	if ((ms->flags & MAGIC_CONTINUE) == 0)
288 		return 1;
289 	if (file_separator(ms) == -1)
290 		*rv = -1;
291 	return 0;
292 }
293 
294 protected int
295 file_default(struct magic_set *ms, size_t nb)
296 {
297 	if (ms->flags & MAGIC_MIME) {
298 		if ((ms->flags & MAGIC_MIME_TYPE) &&
299 		    file_printf(ms, "application/%s",
300 			nb ? "octet-stream" : "x-empty") == -1)
301 			return -1;
302 		return 1;
303 	}
304 	if (ms->flags & MAGIC_APPLE) {
305 		if (file_printf(ms, "UNKNUNKN") == -1)
306 			return -1;
307 		return 1;
308 	}
309 	if (ms->flags & MAGIC_EXTENSION) {
310 		if (file_printf(ms, "???") == -1)
311 			return -1;
312 		return 1;
313 	}
314 	return 0;
315 }
316 
317 /*
318  * The magic detection functions return:
319  *	 1: found
320  *	 0: not found
321  *	-1: error
322  */
323 /*ARGSUSED*/
324 protected int
325 file_buffer(struct magic_set *ms, int fd, struct stat *st,
326     const char *inname __attribute__ ((__unused__)),
327     const void *buf, size_t nb)
328 {
329 	int m = 0, rv = 0, looks_text = 0;
330 	const char *code = NULL;
331 	const char *code_mime = "binary";
332 	const char *def = "data";
333 	const char *ftype = NULL;
334 	char *rbuf = NULL;
335 	struct buffer b;
336 
337 	buffer_init(&b, fd, st, buf, nb);
338 	ms->mode = b.st.st_mode;
339 
340 	if (nb == 0) {
341 		def = "empty";
342 		goto simple;
343 	} else if (nb == 1) {
344 		def = "very short file (no magic)";
345 		goto simple;
346 	}
347 
348 	if ((ms->flags & MAGIC_NO_CHECK_ENCODING) == 0) {
349 		looks_text = file_encoding(ms, &b, NULL, 0,
350 		    &code, &code_mime, &ftype);
351 	}
352 
353 #ifdef __EMX__
354 	if ((ms->flags & MAGIC_NO_CHECK_APPTYPE) == 0 && inname) {
355 		m = file_os2_apptype(ms, inname, &b);
356 		if ((ms->flags & MAGIC_DEBUG) != 0)
357 			(void)fprintf(stderr, "[try os2_apptype %d]\n", m);
358 		switch (m) {
359 		case -1:
360 			return -1;
361 		case 0:
362 			break;
363 		default:
364 			return 1;
365 		}
366 	}
367 #endif
368 #if HAVE_FORK
369 	/* try compression stuff */
370 	if ((ms->flags & MAGIC_NO_CHECK_COMPRESS) == 0) {
371 		m = file_zmagic(ms, &b, inname);
372 		if ((ms->flags & MAGIC_DEBUG) != 0)
373 			(void)fprintf(stderr, "[try zmagic %d]\n", m);
374 		if (m) {
375 			goto done_encoding;
376 		}
377 	}
378 #endif
379 	/* Check if we have a tar file */
380 	if ((ms->flags & MAGIC_NO_CHECK_TAR) == 0) {
381 		m = file_is_tar(ms, &b);
382 		if ((ms->flags & MAGIC_DEBUG) != 0)
383 			(void)fprintf(stderr, "[try tar %d]\n", m);
384 		if (m) {
385 			if (checkdone(ms, &rv))
386 				goto done;
387 		}
388 	}
389 
390 	/* Check if we have a JSON file */
391 	if ((ms->flags & MAGIC_NO_CHECK_JSON) == 0) {
392 		m = file_is_json(ms, &b);
393 		if ((ms->flags & MAGIC_DEBUG) != 0)
394 			(void)fprintf(stderr, "[try json %d]\n", m);
395 		if (m) {
396 			if (checkdone(ms, &rv))
397 				goto done;
398 		}
399 	}
400 
401 	/* Check if we have a CSV file */
402 	if ((ms->flags & MAGIC_NO_CHECK_CSV) == 0) {
403 		m = file_is_csv(ms, &b, looks_text);
404 		if ((ms->flags & MAGIC_DEBUG) != 0)
405 			(void)fprintf(stderr, "[try csv %d]\n", m);
406 		if (m) {
407 			if (checkdone(ms, &rv))
408 				goto done;
409 		}
410 	}
411 
412 	/* Check if we have a CDF file */
413 	if ((ms->flags & MAGIC_NO_CHECK_CDF) == 0) {
414 		m = file_trycdf(ms, &b);
415 		if ((ms->flags & MAGIC_DEBUG) != 0)
416 			(void)fprintf(stderr, "[try cdf %d]\n", m);
417 		if (m) {
418 			if (checkdone(ms, &rv))
419 				goto done;
420 		}
421 	}
422 #ifdef BUILTIN_ELF
423 	if ((ms->flags & MAGIC_NO_CHECK_ELF) == 0 && nb > 5 && fd != -1) {
424 		file_pushbuf_t *pb;
425 		/*
426 		 * We matched something in the file, so this
427 		 * *might* be an ELF file, and the file is at
428 		 * least 5 bytes long, so if it's an ELF file
429 		 * it has at least one byte past the ELF magic
430 		 * number - try extracting information from the
431 		 * ELF headers that cannot easily be  extracted
432 		 * with rules in the magic file. We we don't
433 		 * print the information yet.
434 		 */
435 		if ((pb = file_push_buffer(ms)) == NULL)
436 			return -1;
437 
438 		rv = file_tryelf(ms, &b);
439 		rbuf = file_pop_buffer(ms, pb);
440 		if (rv == -1) {
441 			free(rbuf);
442 			rbuf = NULL;
443 		}
444 		if ((ms->flags & MAGIC_DEBUG) != 0)
445 			(void)fprintf(stderr, "[try elf %d]\n", m);
446 	}
447 #endif
448 
449 	/* try soft magic tests */
450 	if ((ms->flags & MAGIC_NO_CHECK_SOFT) == 0) {
451 		m = file_softmagic(ms, &b, NULL, NULL, BINTEST, looks_text);
452 		if ((ms->flags & MAGIC_DEBUG) != 0)
453 			(void)fprintf(stderr, "[try softmagic %d]\n", m);
454 		if (m == 1 && rbuf) {
455 			if (file_printf(ms, "%s", rbuf) == -1)
456 				goto done;
457 		}
458 		if (m) {
459 			if (checkdone(ms, &rv))
460 				goto done;
461 		}
462 	}
463 
464 	/* try text properties */
465 	if ((ms->flags & MAGIC_NO_CHECK_TEXT) == 0) {
466 
467 		m = file_ascmagic(ms, &b, looks_text);
468 		if ((ms->flags & MAGIC_DEBUG) != 0)
469 			(void)fprintf(stderr, "[try ascmagic %d]\n", m);
470 		if (m) {
471 			goto done;
472 		}
473 	}
474 
475 simple:
476 	/* give up */
477 	if (m == 0) {
478 		m = 1;
479 		rv = file_default(ms, nb);
480 		if (rv == 0)
481 			if (file_printf(ms, "%s", def) == -1)
482 				rv = -1;
483 	}
484  done:
485 	trim_separator(ms);
486 	if ((ms->flags & MAGIC_MIME_ENCODING) != 0) {
487 		if (ms->flags & MAGIC_MIME_TYPE)
488 			if (file_printf(ms, "; charset=") == -1)
489 				rv = -1;
490 		if (file_printf(ms, "%s", code_mime) == -1)
491 			rv = -1;
492 	}
493 #if HAVE_FORK
494  done_encoding:
495 #endif
496 	free(rbuf);
497 	buffer_fini(&b);
498 	if (rv)
499 		return rv;
500 
501 	return m;
502 }
503 #endif
504 
505 protected int
506 file_reset(struct magic_set *ms, int checkloaded)
507 {
508 	if (checkloaded && ms->mlist[0] == NULL) {
509 		file_error(ms, 0, "no magic files loaded");
510 		return -1;
511 	}
512 	file_clearbuf(ms);
513 	if (ms->o.pbuf) {
514 		free(ms->o.pbuf);
515 		ms->o.pbuf = NULL;
516 	}
517 	ms->event_flags &= ~EVENT_HAD_ERR;
518 	ms->error = -1;
519 	return 0;
520 }
521 
522 #define OCTALIFY(n, o)	\
523 	/*LINTED*/ \
524 	(void)(*(n)++ = '\\', \
525 	*(n)++ = ((CAST(uint32_t, *(o)) >> 6) & 3) + '0', \
526 	*(n)++ = ((CAST(uint32_t, *(o)) >> 3) & 7) + '0', \
527 	*(n)++ = ((CAST(uint32_t, *(o)) >> 0) & 7) + '0', \
528 	(o)++)
529 
530 protected const char *
531 file_getbuffer(struct magic_set *ms)
532 {
533 	char *pbuf, *op, *np;
534 	size_t psize, len;
535 
536 	if (ms->event_flags & EVENT_HAD_ERR)
537 		return NULL;
538 
539 	if (ms->flags & MAGIC_RAW)
540 		return ms->o.buf;
541 
542 	if (ms->o.buf == NULL)
543 		return NULL;
544 
545 	/* * 4 is for octal representation, + 1 is for NUL */
546 	len = strlen(ms->o.buf);
547 	if (len > (SIZE_MAX - 1) / 4) {
548 		file_oomem(ms, len);
549 		return NULL;
550 	}
551 	psize = len * 4 + 1;
552 	if ((pbuf = CAST(char *, realloc(ms->o.pbuf, psize))) == NULL) {
553 		file_oomem(ms, psize);
554 		return NULL;
555 	}
556 	ms->o.pbuf = pbuf;
557 
558 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
559 	{
560 		mbstate_t state;
561 		wchar_t nextchar;
562 		int mb_conv = 1;
563 		size_t bytesconsumed;
564 		char *eop;
565 		(void)memset(&state, 0, sizeof(mbstate_t));
566 
567 		np = ms->o.pbuf;
568 		op = ms->o.buf;
569 		eop = op + len;
570 
571 		while (op < eop) {
572 			bytesconsumed = mbrtowc(&nextchar, op,
573 			    CAST(size_t, eop - op), &state);
574 			if (bytesconsumed == CAST(size_t, -1) ||
575 			    bytesconsumed == CAST(size_t, -2)) {
576 				mb_conv = 0;
577 				break;
578 			}
579 
580 			if (iswprint(nextchar)) {
581 				(void)memcpy(np, op, bytesconsumed);
582 				op += bytesconsumed;
583 				np += bytesconsumed;
584 			} else {
585 				while (bytesconsumed-- > 0)
586 					OCTALIFY(np, op);
587 			}
588 		}
589 		*np = '\0';
590 
591 		/* Parsing succeeded as a multi-byte sequence */
592 		if (mb_conv != 0)
593 			return ms->o.pbuf;
594 	}
595 #endif
596 
597 	for (np = ms->o.pbuf, op = ms->o.buf; *op;) {
598 		if (isprint(CAST(unsigned char, *op))) {
599 			*np++ = *op++;
600 		} else {
601 			OCTALIFY(np, op);
602 		}
603 	}
604 	*np = '\0';
605 	return ms->o.pbuf;
606 }
607 
608 protected int
609 file_check_mem(struct magic_set *ms, unsigned int level)
610 {
611 	size_t len;
612 
613 	if (level >= ms->c.len) {
614 		len = (ms->c.len = 20 + level) * sizeof(*ms->c.li);
615 		ms->c.li = CAST(struct level_info *, (ms->c.li == NULL) ?
616 		    malloc(len) :
617 		    realloc(ms->c.li, len));
618 		if (ms->c.li == NULL) {
619 			file_oomem(ms, len);
620 			return -1;
621 		}
622 	}
623 	ms->c.li[level].got_match = 0;
624 #ifdef ENABLE_CONDITIONALS
625 	ms->c.li[level].last_match = 0;
626 	ms->c.li[level].last_cond = COND_NONE;
627 #endif /* ENABLE_CONDITIONALS */
628 	return 0;
629 }
630 
631 protected size_t
632 file_printedlen(const struct magic_set *ms)
633 {
634 	return ms->o.blen;
635 }
636 
637 protected int
638 file_replace(struct magic_set *ms, const char *pat, const char *rep)
639 {
640 	file_regex_t rx;
641 	int rc, rv = -1;
642 
643 	rc = file_regcomp(&rx, pat, REG_EXTENDED);
644 	if (rc) {
645 		file_regerror(&rx, rc, ms);
646 	} else {
647 		regmatch_t rm;
648 		int nm = 0;
649 		while (file_regexec(&rx, ms->o.buf, 1, &rm, 0) == 0) {
650 			ms->o.buf[rm.rm_so] = '\0';
651 			if (file_printf(ms, "%s%s", rep,
652 			    rm.rm_eo != 0 ? ms->o.buf + rm.rm_eo : "") == -1)
653 				goto out;
654 			nm++;
655 		}
656 		rv = nm;
657 	}
658 out:
659 	file_regfree(&rx);
660 	return rv;
661 }
662 
663 protected int
664 file_regcomp(file_regex_t *rx, const char *pat, int flags)
665 {
666 #ifdef USE_C_LOCALE
667 	rx->c_lc_ctype = newlocale(LC_CTYPE_MASK, "C", 0);
668 	assert(rx->c_lc_ctype != NULL);
669 	rx->old_lc_ctype = uselocale(rx->c_lc_ctype);
670 	assert(rx->old_lc_ctype != NULL);
671 #else
672 	rx->old_lc_ctype = setlocale(LC_CTYPE, NULL);
673 	assert(rx->old_lc_ctype != NULL);
674 	rx->old_lc_ctype = strdup(rx->old_lc_ctype);
675 	assert(rx->old_lc_ctype != NULL);
676 	(void)setlocale(LC_CTYPE, "C");
677 #endif
678 	rx->pat = pat;
679 
680 	return rx->rc = regcomp(&rx->rx, pat, flags);
681 }
682 
683 protected int
684 file_regexec(file_regex_t *rx, const char *str, size_t nmatch,
685     regmatch_t* pmatch, int eflags)
686 {
687 	assert(rx->rc == 0);
688 	/* XXX: force initialization because glibc does not always do this */
689 	if (nmatch != 0)
690 		memset(pmatch, 0, nmatch * sizeof(*pmatch));
691 	return regexec(&rx->rx, str, nmatch, pmatch, eflags);
692 }
693 
694 protected void
695 file_regfree(file_regex_t *rx)
696 {
697 	if (rx->rc == 0)
698 		regfree(&rx->rx);
699 #ifdef USE_C_LOCALE
700 	(void)uselocale(rx->old_lc_ctype);
701 	freelocale(rx->c_lc_ctype);
702 #else
703 	(void)setlocale(LC_CTYPE, rx->old_lc_ctype);
704 	free(rx->old_lc_ctype);
705 #endif
706 }
707 
708 protected void
709 file_regerror(file_regex_t *rx, int rc, struct magic_set *ms)
710 {
711 	char errmsg[512];
712 
713 	(void)regerror(rc, &rx->rx, errmsg, sizeof(errmsg));
714 	file_magerror(ms, "regex error %d for `%s', (%s)", rc, rx->pat,
715 	    errmsg);
716 }
717 
718 protected file_pushbuf_t *
719 file_push_buffer(struct magic_set *ms)
720 {
721 	file_pushbuf_t *pb;
722 
723 	if (ms->event_flags & EVENT_HAD_ERR)
724 		return NULL;
725 
726 	if ((pb = (CAST(file_pushbuf_t *, malloc(sizeof(*pb))))) == NULL)
727 		return NULL;
728 
729 	pb->buf = ms->o.buf;
730 	pb->blen = ms->o.blen;
731 	pb->offset = ms->offset;
732 
733 	ms->o.buf = NULL;
734 	ms->o.blen = 0;
735 	ms->offset = 0;
736 
737 	return pb;
738 }
739 
740 protected char *
741 file_pop_buffer(struct magic_set *ms, file_pushbuf_t *pb)
742 {
743 	char *rbuf;
744 
745 	if (ms->event_flags & EVENT_HAD_ERR) {
746 		free(pb->buf);
747 		free(pb);
748 		return NULL;
749 	}
750 
751 	rbuf = ms->o.buf;
752 
753 	ms->o.buf = pb->buf;
754 	ms->o.blen = pb->blen;
755 	ms->offset = pb->offset;
756 
757 	free(pb);
758 	return rbuf;
759 }
760 
761 /*
762  * convert string to ascii printable format.
763  */
764 protected char *
765 file_printable(char *buf, size_t bufsiz, const char *str, size_t slen)
766 {
767 	char *ptr, *eptr = buf + bufsiz - 1;
768 	const unsigned char *s = RCAST(const unsigned char *, str);
769 	const unsigned char *es = s + slen;
770 
771 	for (ptr = buf;  ptr < eptr && s < es && *s; s++) {
772 		if (isprint(*s)) {
773 			*ptr++ = *s;
774 			continue;
775 		}
776 		if (ptr >= eptr - 3)
777 			break;
778 		*ptr++ = '\\';
779 		*ptr++ = ((CAST(unsigned int, *s) >> 6) & 7) + '0';
780 		*ptr++ = ((CAST(unsigned int, *s) >> 3) & 7) + '0';
781 		*ptr++ = ((CAST(unsigned int, *s) >> 0) & 7) + '0';
782 	}
783 	*ptr = '\0';
784 	return buf;
785 }
786 
787 struct guid {
788 	uint32_t data1;
789 	uint16_t data2;
790 	uint16_t data3;
791 	uint8_t data4[8];
792 };
793 
794 protected int
795 file_parse_guid(const char *s, uint64_t *guid)
796 {
797 	struct guid *g = CAST(struct guid *, CAST(void *, guid));
798 	return sscanf(s,
799 	    "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
800 	    &g->data1, &g->data2, &g->data3, &g->data4[0], &g->data4[1],
801 	    &g->data4[2], &g->data4[3], &g->data4[4], &g->data4[5],
802 	    &g->data4[6], &g->data4[7]) == 11 ? 0 : -1;
803 }
804 
805 protected int
806 file_print_guid(char *str, size_t len, const uint64_t *guid)
807 {
808 	const struct guid *g = CAST(const struct guid *,
809 	    CAST(const void *, guid));
810 
811 	return snprintf(str, len, "%.8X-%.4hX-%.4hX-%.2hhX%.2hhX-"
812 	    "%.2hhX%.2hhX%.2hhX%.2hhX%.2hhX%.2hhX",
813 	    g->data1, g->data2, g->data3, g->data4[0], g->data4[1],
814 	    g->data4[2], g->data4[3], g->data4[4], g->data4[5],
815 	    g->data4[6], g->data4[7]);
816 }
817 
818 protected int
819 file_pipe_closexec(int *fds)
820 {
821 #ifdef HAVE_PIPE2
822 	return pipe2(fds, O_CLOEXEC);
823 #else
824 	if (pipe(fds) == -1)
825 		return -1;
826 	(void)fcntl(fds[0], F_SETFD, FD_CLOEXEC);
827 	(void)fcntl(fds[1], F_SETFD, FD_CLOEXEC);
828 	return 0;
829 #endif
830 }
831 
832 protected int
833 file_clear_closexec(int fd) {
834 	return fcntl(fd, F_SETFD, 0);
835 }
836 
837 protected char *
838 file_strtrim(char *str)
839 {
840 	char *last;
841 
842 	while (isspace(CAST(unsigned char, *str)))
843 		str++;
844 	last = str;
845 	while (*last)
846 		last++;
847 	--last;
848 	while (isspace(CAST(unsigned char, *last)))
849 		last--;
850 	*++last = '\0';
851 	return str;
852 }
853