Home | History | Annotate | Line # | Download | only in src
      1 /*	$NetBSD: funcs.c,v 1.22 2026/06/10 20:54:16 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.153 2026/05/25 14:07:05 christos Exp $")
     34 #else
     35 __RCSID("$NetBSD: funcs.c,v 1.22 2026/06/10 20:54:16 christos Exp $");
     36 #endif
     37 #endif	/* lint */
     38 
     39 #include "magic.h"
     40 #include "swap.h"
     41 #include <assert.h>
     42 #include <stdarg.h>
     43 #include <stdlib.h>
     44 #include <string.h>
     45 #include <ctype.h>
     46 #ifdef HAVE_UNISTD_H
     47 #include <unistd.h>	/* for pipe2() */
     48 #endif
     49 #if defined(HAVE_WCHAR_H)
     50 #include <wchar.h>
     51 #endif
     52 #if defined(HAVE_WCTYPE_H)
     53 #include <wctype.h>
     54 #endif
     55 #include <limits.h>
     56 
     57 #ifndef SIZE_MAX
     58 #define SIZE_MAX	((size_t)~0)
     59 #endif
     60 
     61 file_protected int
     62 file_bigendian(void)
     63 {
     64 	union {
     65 		unsigned short x;
     66 		unsigned char s[sizeof(unsigned short)];
     67 	} u;
     68 
     69 	u.x = 1;
     70 	return u.s[0] != 1;
     71 }
     72 
     73 file_protected char *
     74 file_copystr(char *buf, size_t blen, size_t width, const char *str)
     75 {
     76 	if (blen == 0)
     77 		return buf;
     78 	if (width >= blen)
     79 		width = blen - 1;
     80 	memcpy(buf, str, width);
     81 	buf[width] = '\0';
     82 	return buf;
     83 }
     84 
     85 file_private void
     86 file_clearbuf(struct magic_set *ms)
     87 {
     88 	free(ms->o.buf);
     89 	ms->o.buf = NULL;
     90 	ms->o.blen = 0;
     91 }
     92 
     93 file_private int
     94 file_checkfield(char *msg, size_t mlen, const char *what, const char **pp)
     95 {
     96 	const char *p = *pp;
     97 	int fw = 0;
     98 
     99 	while (*p && isdigit((unsigned char)*p)) {
    100 		fw = fw * 10 + (*p++ - '0');
    101 		if (fw > 1024)
    102 			break;
    103 	}
    104 
    105 	*pp = p;
    106 
    107 	if (fw < 1024)
    108 		return 1;
    109 	if (msg)
    110 		snprintf(msg, mlen, "field %s too large: %d", what, fw);
    111 
    112 	return 0;
    113 }
    114 
    115 file_protected int
    116 file_checkfmt(char *msg, size_t mlen, const char *fmt)
    117 {
    118 	const char *p;
    119 	for (p = fmt; *p; p++) {
    120 		if (*p != '%')
    121 			continue;
    122 		if (*++p == '%')
    123 			continue;
    124 		if (*p == '\0') {
    125 			if (msg)
    126 				snprintf(msg, mlen, "incomplete %% format");
    127 			return -1;
    128 		}
    129 		// Skip uninteresting.
    130 		while (*p != '\0' && strchr("#0.'+- ", *p) != NULL)
    131 			p++;
    132 		if (*p == '*') {
    133 			if (msg)
    134 				snprintf(msg, mlen, "* not allowed in format");
    135 			return -1;
    136 		}
    137 
    138 		if (!file_checkfield(msg, mlen, "width", &p))
    139 			return -1;
    140 
    141 		if (*p == '.') {
    142 			p++;
    143 			if (!file_checkfield(msg, mlen, "precision", &p))
    144 				return -1;
    145 		}
    146 
    147 		if (!isalpha((unsigned char)*p)) {
    148 			if (msg)
    149 				snprintf(msg, mlen, "bad format char: %c", *p);
    150 			return -1;
    151 		}
    152 	}
    153 	return 0;
    154 }
    155 
    156 /*
    157  * Like printf, only we append to a buffer.
    158  */
    159 file_protected int
    160 file_vprintf(struct magic_set *ms, const char *fmt, va_list ap)
    161 {
    162 	int len;
    163 	char *buf, *newstr;
    164 	char tbuf[1024];
    165 
    166 	if (ms->event_flags & EVENT_HAD_ERR)
    167 		return 0;
    168 
    169 	if (file_checkfmt(tbuf, sizeof(tbuf), fmt)) {
    170 		file_clearbuf(ms);
    171 		file_error(ms, 0, "Bad magic format `%s' (%s)", fmt, tbuf);
    172 		return -1;
    173 	}
    174 
    175 	len = vasprintf(&buf, fmt, ap);
    176 	if (len < 0 || (size_t)len > 1024 || len + ms->o.blen > 1024 * 1024) {
    177 		size_t blen = ms->o.blen;
    178 		free(buf);
    179 		file_clearbuf(ms);
    180 		file_error(ms, 0, "Output buffer space exceeded %d+%"
    181 		    SIZE_T_FORMAT "u", len, blen);
    182 		return -1;
    183 	}
    184 
    185 	if (ms->o.buf != NULL) {
    186 		len = asprintf(&newstr, "%s%s", ms->o.buf, buf);
    187 		free(buf);
    188 		if (len < 0)
    189 			goto out;
    190 		free(ms->o.buf);
    191 		buf = newstr;
    192 	}
    193 	ms->o.buf = buf;
    194 	ms->o.blen = len;
    195 	return 0;
    196 out:
    197 	file_clearbuf(ms);
    198 	file_error(ms, errno, "vasprintf failed");
    199 	return -1;
    200 }
    201 
    202 file_protected int
    203 file_printf(struct magic_set *ms, const char *fmt, ...)
    204 {
    205 	int rv;
    206 	va_list ap;
    207 
    208 	va_start(ap, fmt);
    209 	rv = file_vprintf(ms, fmt, ap);
    210 	va_end(ap);
    211 	return rv;
    212 }
    213 
    214 /*
    215  * error - print best error message possible
    216  */
    217 /*VARARGS*/
    218 __attribute__((__format__(__printf__, 3, 0)))
    219 file_private void
    220 file_error_core(struct magic_set *ms, int error, const char *f, va_list va,
    221     size_t lineno)
    222 {
    223 	/* Only the first error is ok */
    224 	if (ms->event_flags & EVENT_HAD_ERR)
    225 		return;
    226 	if (lineno != 0) {
    227 		file_clearbuf(ms);
    228 		(void)file_printf(ms, "line %" SIZE_T_FORMAT "u:", lineno);
    229 	}
    230 	if (ms->o.buf && *ms->o.buf)
    231 		(void)file_printf(ms, " ");
    232 	(void)file_vprintf(ms, f, va);
    233 	if (error > 0)
    234 		(void)file_printf(ms, " (%s)", strerror(error));
    235 	ms->event_flags |= EVENT_HAD_ERR;
    236 	ms->error = error;
    237 }
    238 
    239 /*VARARGS*/
    240 file_protected void
    241 file_error(struct magic_set *ms, int error, const char *f, ...)
    242 {
    243 	va_list va;
    244 	va_start(va, f);
    245 	file_error_core(ms, error, f, va, 0);
    246 	va_end(va);
    247 }
    248 
    249 /*
    250  * Print an error with magic line number.
    251  */
    252 /*VARARGS*/
    253 file_protected void
    254 file_magerror(struct magic_set *ms, const char *f, ...)
    255 {
    256 	va_list va;
    257 	va_start(va, f);
    258 	file_error_core(ms, 0, f, va, ms->line);
    259 	va_end(va);
    260 }
    261 
    262 file_protected void
    263 file_oomem(struct magic_set *ms, size_t len)
    264 {
    265 	file_error(ms, errno, "cannot allocate %" SIZE_T_FORMAT "u bytes",
    266 	    len);
    267 }
    268 
    269 file_protected void
    270 file_badseek(struct magic_set *ms)
    271 {
    272 	file_error(ms, errno, "error seeking");
    273 }
    274 
    275 file_protected void
    276 file_badread(struct magic_set *ms)
    277 {
    278 	file_error(ms, errno, "error reading");
    279 }
    280 
    281 #ifndef COMPILE_ONLY
    282 #define FILE_SEPARATOR "\n- "
    283 
    284 file_protected int
    285 file_separator(struct magic_set *ms)
    286 {
    287 	return file_printf(ms, FILE_SEPARATOR);
    288 }
    289 
    290 static void
    291 trim_separator(struct magic_set *ms)
    292 {
    293 	size_t l;
    294 
    295 	if (ms->o.buf == NULL)
    296 		return;
    297 
    298 	l = strlen(ms->o.buf);
    299 	if (l < sizeof(FILE_SEPARATOR))
    300 		return;
    301 
    302 	l -= sizeof(FILE_SEPARATOR) - 1;
    303 	if (strcmp(ms->o.buf + l, FILE_SEPARATOR) != 0)
    304 		return;
    305 
    306 	ms->o.buf[l] = '\0';
    307 }
    308 
    309 static int
    310 checkdone(struct magic_set *ms, int *rv)
    311 {
    312 	if ((ms->flags & MAGIC_CONTINUE) == 0)
    313 		return 1;
    314 	if (file_separator(ms) == -1)
    315 		*rv = -1;
    316 	return 0;
    317 }
    318 
    319 file_protected int
    320 file_default(struct magic_set *ms, size_t nb)
    321 {
    322 	if (ms->flags & MAGIC_MIME) {
    323 		if ((ms->flags & MAGIC_MIME_TYPE) &&
    324 		    file_printf(ms, "application/%s",
    325 			nb ? "octet-stream" : "x-empty") == -1)
    326 			return -1;
    327 		return 1;
    328 	}
    329 	if (ms->flags & MAGIC_APPLE) {
    330 		// This is not a typo: Type: UNKN Creator: UNKN
    331 		if (file_printf(ms, "UNKNUNKN") == -1)
    332 			return -1;
    333 		return 1;
    334 	}
    335 	if (ms->flags & MAGIC_EXTENSION) {
    336 		if (file_printf(ms, "???") == -1)
    337 			return -1;
    338 		return 1;
    339 	}
    340 	return 0;
    341 }
    342 
    343 /*
    344  * The magic detection functions return:
    345  *	 1: found
    346  *	 0: not found
    347  *	-1: error
    348  */
    349 /*ARGSUSED*/
    350 file_protected int
    351 file_buffer(struct magic_set *ms, int fd, struct stat *st,
    352     const char *inname __attribute__ ((__unused__)),
    353     const void *buf, size_t nb)
    354 {
    355 	int m = 0, rv = 0, looks_text = 0;
    356 	const char *code = NULL;
    357 	const char *code_mime = "binary";
    358 	const char *def = "data";
    359 	const char *ftype = NULL;
    360 	char *rbuf = NULL;
    361 	struct buffer b;
    362 
    363 	buffer_init(&b, fd, st, buf, nb);
    364 	ms->mode = b.st.st_mode;
    365 
    366 	if (nb == 0) {
    367 		def = "empty";
    368 		goto simple;
    369 	} else if (nb == 1) {
    370 		def = "very short file (no magic)";
    371 		goto simple;
    372 	}
    373 
    374 	if ((ms->flags & MAGIC_NO_CHECK_ENCODING) == 0) {
    375 		looks_text = file_encoding(ms, &b, NULL, 0,
    376 		    &code, &code_mime, &ftype);
    377 	}
    378 
    379 #ifdef __EMX__
    380 	if ((ms->flags & MAGIC_NO_CHECK_APPTYPE) == 0 && inname) {
    381 		m = file_os2_apptype(ms, inname, &b);
    382 		if ((ms->flags & MAGIC_DEBUG) != 0)
    383 			(void)fprintf(stderr, "[try os2_apptype %d]\n", m);
    384 		switch (m) {
    385 		case -1:
    386 			return -1;
    387 		case 0:
    388 			break;
    389 		default:
    390 			return 1;
    391 		}
    392 	}
    393 #endif
    394 #if HAVE_FORK
    395 	/* try compression stuff */
    396 	if ((ms->flags & MAGIC_NO_CHECK_COMPRESS) == 0) {
    397 		m = file_zmagic(ms, &b, inname);
    398 		if ((ms->flags & MAGIC_DEBUG) != 0)
    399 			(void)fprintf(stderr, "[try zmagic %d]\n", m);
    400 		if (m) {
    401 			goto done_encoding;
    402 		}
    403 	}
    404 #endif
    405 	/* Check if we have a tar file */
    406 	if ((ms->flags & MAGIC_NO_CHECK_TAR) == 0) {
    407 		m = file_is_tar(ms, &b);
    408 		if ((ms->flags & MAGIC_DEBUG) != 0)
    409 			(void)fprintf(stderr, "[try tar %d]\n", m);
    410 		if (m) {
    411 			if (checkdone(ms, &rv))
    412 				goto done;
    413 		}
    414 	}
    415 
    416 	/* Check if we have a JSON file */
    417 	if ((ms->flags & MAGIC_NO_CHECK_JSON) == 0) {
    418 		m = file_is_json(ms, &b);
    419 		if ((ms->flags & MAGIC_DEBUG) != 0)
    420 			(void)fprintf(stderr, "[try json %d]\n", m);
    421 		if (m) {
    422 			if (checkdone(ms, &rv))
    423 				goto done;
    424 		}
    425 	}
    426 
    427 	/* Check if we have a CSV file */
    428 	if ((ms->flags & MAGIC_NO_CHECK_CSV) == 0) {
    429 		m = file_is_csv(ms, &b, looks_text, code);
    430 		if ((ms->flags & MAGIC_DEBUG) != 0)
    431 			(void)fprintf(stderr, "[try csv %d]\n", m);
    432 		if (m) {
    433 			if (checkdone(ms, &rv))
    434 				goto done;
    435 		}
    436 	}
    437 
    438 	/* Check if we have a SIMH tape file */
    439 	if ((ms->flags & MAGIC_NO_CHECK_SIMH) == 0) {
    440 		m = file_is_simh(ms, &b);
    441 		if ((ms->flags & MAGIC_DEBUG) != 0)
    442 			(void)fprintf(stderr, "[try simh %d]\n", m);
    443 		if (m) {
    444 			if (checkdone(ms, &rv))
    445 				goto done;
    446 		}
    447 	}
    448 
    449 	/* Check if we have a CDF file */
    450 	if ((ms->flags & MAGIC_NO_CHECK_CDF) == 0) {
    451 		m = file_trycdf(ms, &b);
    452 		if ((ms->flags & MAGIC_DEBUG) != 0)
    453 			(void)fprintf(stderr, "[try cdf %d]\n", m);
    454 		if (m) {
    455 			if (checkdone(ms, &rv))
    456 				goto done;
    457 		}
    458 	}
    459 #ifdef BUILTIN_ELF
    460 	if ((ms->flags & MAGIC_NO_CHECK_ELF) == 0 && nb > 5 && fd != -1) {
    461 		file_pushbuf_t *pb;
    462 		/*
    463 		 * We matched something in the file, so this
    464 		 * *might* be an ELF file, and the file is at
    465 		 * least 5 bytes long, so if it's an ELF file
    466 		 * it has at least one byte past the ELF magic
    467 		 * number - try extracting information from the
    468 		 * ELF headers that cannot easily be  extracted
    469 		 * with rules in the magic file. We we don't
    470 		 * print the information yet.
    471 		 */
    472 		if ((pb = file_push_buffer(ms)) == NULL)
    473 			return -1;
    474 
    475 		rv = file_tryelf(ms, &b);
    476 		rbuf = file_pop_buffer(ms, pb);
    477 		if (rv == -1) {
    478 			free(rbuf);
    479 			rbuf = NULL;
    480 		}
    481 		if ((ms->flags & MAGIC_DEBUG) != 0)
    482 			(void)fprintf(stderr, "[try elf %d]\n", m);
    483 	}
    484 #endif
    485 
    486 	/* try soft magic tests */
    487 	if ((ms->flags & MAGIC_NO_CHECK_SOFT) == 0) {
    488 		m = file_softmagic(ms, &b, NULL, NULL, BINTEST, looks_text);
    489 		if ((ms->flags & MAGIC_DEBUG) != 0)
    490 			(void)fprintf(stderr, "[try softmagic %d]\n", m);
    491 		if (m == 1 && rbuf) {
    492 			if (file_printf(ms, "%s", rbuf) == -1)
    493 				goto done;
    494 		}
    495 		if (m) {
    496 			if (checkdone(ms, &rv))
    497 				goto done;
    498 		}
    499 	}
    500 
    501 	/* try text properties */
    502 	if ((ms->flags & MAGIC_NO_CHECK_TEXT) == 0) {
    503 
    504 		m = file_ascmagic(ms, &b, looks_text);
    505 		if ((ms->flags & MAGIC_DEBUG) != 0)
    506 			(void)fprintf(stderr, "[try ascmagic %d]\n", m);
    507 		if (m) {
    508 			goto done;
    509 		}
    510 	}
    511 
    512 simple:
    513 	/* give up */
    514 	if (m == 0) {
    515 		m = 1;
    516 		rv = file_default(ms, nb);
    517 		if (rv == 0)
    518 			if (file_printf(ms, "%s", def) == -1)
    519 				rv = -1;
    520 	}
    521  done:
    522 	trim_separator(ms);
    523 	if ((ms->flags & MAGIC_MIME_ENCODING) != 0) {
    524 		if (ms->flags & MAGIC_MIME_TYPE)
    525 			if (file_printf(ms, "; charset=") == -1)
    526 				rv = -1;
    527 		if (file_printf(ms, "%s", code_mime) == -1)
    528 			rv = -1;
    529 	}
    530 #if HAVE_FORK
    531  done_encoding:
    532 #endif
    533 	free(rbuf);
    534 	buffer_fini(&b);
    535 	if (rv)
    536 		return rv;
    537 
    538 	return m;
    539 }
    540 #endif
    541 
    542 file_protected int
    543 file_reset(struct magic_set *ms, int checkloaded)
    544 {
    545 	if (checkloaded && ms->mlist[0] == NULL) {
    546 		file_error(ms, 0, "no magic files loaded");
    547 		return -1;
    548 	}
    549 	file_clearbuf(ms);
    550 	if (ms->o.pbuf) {
    551 		free(ms->o.pbuf);
    552 		ms->o.pbuf = NULL;
    553 	}
    554 	ms->event_flags &= ~EVENT_HAD_ERR;
    555 	ms->error = -1;
    556 	return 0;
    557 }
    558 
    559 #define OCTALIFY(n, o)	\
    560 	/*LINTED*/ \
    561 	(void)(*(n)++ = '\\', \
    562 	*(n)++ = ((CAST(uint32_t, *(o)) >> 6) & 3) + '0', \
    563 	*(n)++ = ((CAST(uint32_t, *(o)) >> 3) & 7) + '0', \
    564 	*(n)++ = ((CAST(uint32_t, *(o)) >> 0) & 7) + '0', \
    565 	(o)++)
    566 
    567 file_protected const char *
    568 file_getbuffer(struct magic_set *ms)
    569 {
    570 	char *pbuf, *op, *np;
    571 	size_t psize, len;
    572 
    573 	if (ms->event_flags & EVENT_HAD_ERR)
    574 		return NULL;
    575 
    576 	if (ms->flags & MAGIC_RAW)
    577 		return ms->o.buf;
    578 
    579 	if (ms->o.buf == NULL)
    580 		return NULL;
    581 
    582 	/* * 4 is for octal representation, + 1 is for NUL */
    583 	len = strlen(ms->o.buf);
    584 	if (len > (SIZE_MAX - 1) / 4) {
    585 		file_oomem(ms, len);
    586 		return NULL;
    587 	}
    588 	psize = len * 4 + 1;
    589 	if ((pbuf = CAST(char *, realloc(ms->o.pbuf, psize))) == NULL) {
    590 		file_oomem(ms, psize);
    591 		return NULL;
    592 	}
    593 	ms->o.pbuf = pbuf;
    594 
    595 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
    596 	{
    597 		mbstate_t state;
    598 		wchar_t nextchar;
    599 		int mb_conv = 1;
    600 		size_t bytesconsumed;
    601 		char *eop;
    602 		(void)memset(&state, 0, sizeof(mbstate_t));
    603 
    604 		np = ms->o.pbuf;
    605 		op = ms->o.buf;
    606 		eop = op + len;
    607 
    608 		while (op < eop) {
    609 			bytesconsumed = mbrtowc(&nextchar, op,
    610 			    CAST(size_t, eop - op), &state);
    611 			if (bytesconsumed == CAST(size_t, -1) ||
    612 			    bytesconsumed == CAST(size_t, -2)) {
    613 				mb_conv = 0;
    614 				break;
    615 			}
    616 
    617 			if (iswprint(nextchar)) {
    618 				(void)memcpy(np, op, bytesconsumed);
    619 				op += bytesconsumed;
    620 				np += bytesconsumed;
    621 			} else {
    622 				while (bytesconsumed-- > 0)
    623 					OCTALIFY(np, op);
    624 			}
    625 		}
    626 		*np = '\0';
    627 
    628 		/* Parsing succeeded as a multi-byte sequence */
    629 		if (mb_conv != 0)
    630 			return ms->o.pbuf;
    631 	}
    632 #endif
    633 
    634 	for (np = ms->o.pbuf, op = ms->o.buf; *op;) {
    635 		if (isprint(CAST(unsigned char, *op))) {
    636 			*np++ = *op++;
    637 		} else {
    638 			OCTALIFY(np, op);
    639 		}
    640 	}
    641 	*np = '\0';
    642 	return ms->o.pbuf;
    643 }
    644 
    645 file_protected int
    646 file_check_mem(struct magic_set *ms, unsigned int level)
    647 {
    648 	size_t len;
    649 
    650 	if (level >= ms->c.len) {
    651 		len = (ms->c.len = 20 + level) * sizeof(*ms->c.li);
    652 		ms->c.li = CAST(struct level_info *, (ms->c.li == NULL) ?
    653 		    malloc(len) :
    654 		    realloc(ms->c.li, len));
    655 		if (ms->c.li == NULL) {
    656 			file_oomem(ms, len);
    657 			return -1;
    658 		}
    659 	}
    660 	ms->c.li[level].got_match = 0;
    661 #ifdef ENABLE_CONDITIONALS
    662 	ms->c.li[level].last_match = 0;
    663 	ms->c.li[level].last_cond = COND_NONE;
    664 #endif /* ENABLE_CONDITIONALS */
    665 	return 0;
    666 }
    667 
    668 file_protected size_t
    669 file_printedlen(const struct magic_set *ms)
    670 {
    671 	return ms->o.blen;
    672 }
    673 
    674 file_protected int
    675 file_replace(struct magic_set *ms, const char *pat, const char *rep)
    676 {
    677 	file_regex_t rx;
    678 	int nm;
    679 	regmatch_t rm;
    680 
    681 	nm = file_regcomp(ms, &rx, pat, REG_EXTENDED);
    682 	if (nm != 0)
    683 		return -1;
    684 
    685 	while (file_regexec(ms, &rx, ms->o.buf, 1, &rm, 0) == 0) {
    686 		ms->o.buf[rm.rm_so] = '\0';
    687 		if (file_printf(ms, "%s%s", rep,
    688 		    rm.rm_eo != 0 ? ms->o.buf + rm.rm_eo : "") == -1)
    689 			goto out;
    690 		nm++;
    691 	}
    692 out:
    693 	file_regfree(&rx);
    694 	return nm;
    695 }
    696 
    697 file_private int
    698 check_regex(struct magic_set *ms, const char *pat)
    699 {
    700 	char sbuf[512];
    701 	unsigned char oc = '\0';
    702 	const char *p;
    703 	unsigned long l;
    704 	static const char wild[] = "?*+{";
    705 
    706 	for (p = pat; *p; p++) {
    707 		unsigned char c = *p;
    708 		// Avoid repetition of wild characters
    709 		if (strchr(wild, oc) != NULL && strchr(wild, c) != NULL) {
    710 			size_t len = strlen(pat);
    711 			file_magwarn(ms,
    712 			    "repetition-operator operand `%c%c' "
    713 			    "invalid in regex `%s'", oc, c,
    714 			    file_printable(ms, sbuf, sizeof(sbuf), pat, len));
    715 			return -1;
    716 		}
    717 		if (c == '{') {
    718 			char *ep, *eep;
    719 
    720 			if (oc == '}') {
    721 				file_magwarn(ms, "cascading repetition "
    722 				    "operators in regex `%s'", pat);
    723 				return -1;
    724 			}
    725 			errno = 0;
    726 			l = strtoul(p + 1, &ep, 10);
    727 			if (ep != p + 1 && l > 1000)
    728 				goto bounds;
    729 			if (*ep == ',') {
    730 				l = strtoul(ep + 1, &eep, 10);
    731 				if (eep != ep + 1 && l > 1000)
    732 					goto bounds;
    733 			}
    734 		}
    735 		oc = c;
    736 		if (isprint(c) || isspace(c) || c == '\b'
    737 		    || c == 0x8a) // XXX: apple magic fixme
    738 			continue;
    739 		size_t len = strlen(pat);
    740 		file_magwarn(ms,
    741 		    "non-ascii characters in regex \\%#o `%s'",
    742 		    c, file_printable(ms, sbuf, sizeof(sbuf), pat, len));
    743 		return -1;
    744 	}
    745 	return 0;
    746 bounds:
    747 	file_magwarn(ms, "bounds too large %ld in regex `%s'", l, pat);
    748 	return -1;
    749 }
    750 
    751 file_protected int
    752 file_regcomp(struct magic_set *ms file_locale_used, file_regex_t *rx,
    753     const char *pat, int flags)
    754 {
    755 	if (check_regex(ms, pat) == -1)
    756 		return -1;
    757 
    758 #ifdef USE_C_LOCALE
    759 	locale_t old = uselocale(ms->c_lc_ctype);
    760 	assert(old != NULL);
    761 #else
    762 	char old[1024];
    763 	strlcpy(old, setlocale(LC_CTYPE, NULL), sizeof(old));
    764 	(void)setlocale(LC_CTYPE, "C");
    765 #endif
    766 	int rc;
    767 	rc = regcomp(rx, pat, flags);
    768 
    769 #ifdef USE_C_LOCALE
    770 	uselocale(old);
    771 #else
    772 	(void)setlocale(LC_CTYPE, old);
    773 #endif
    774 	if (rc > 0 && (ms->flags & MAGIC_CHECK)) {
    775 		char errmsg[512], buf[512];
    776 
    777 		(void)regerror(rc, rx, errmsg, sizeof(errmsg));
    778 		file_magerror(ms, "regex error %d for `%s', (%s)", rc,
    779 		    file_printable(ms, buf, sizeof(buf), pat, strlen(pat)),
    780 		    errmsg);
    781 	}
    782 	return rc;
    783 }
    784 
    785 /*ARGSUSED*/
    786 file_protected int
    787 file_regexec(struct magic_set *ms file_locale_used, file_regex_t *rx,
    788     const char *str, size_t nmatch, regmatch_t* pmatch, int eflags)
    789 {
    790 #ifdef USE_C_LOCALE
    791 	locale_t old = uselocale(ms->c_lc_ctype);
    792 	assert(old != NULL);
    793 #else
    794 	char old[1024];
    795 	strlcpy(old, setlocale(LC_CTYPE, NULL), sizeof(old));
    796 	(void)setlocale(LC_CTYPE, "C");
    797 #endif
    798 	int rc;
    799 	/* XXX: force initialization because glibc does not always do this */
    800 	if (nmatch != 0)
    801 		memset(pmatch, 0, nmatch * sizeof(*pmatch));
    802 	rc = regexec(rx, str, nmatch, pmatch, eflags);
    803 #ifdef USE_C_LOCALE
    804 	uselocale(old);
    805 #else
    806 	(void)setlocale(LC_CTYPE, old);
    807 #endif
    808 	return rc;
    809 }
    810 
    811 file_protected void
    812 file_regfree(file_regex_t *rx)
    813 {
    814 	regfree(rx);
    815 }
    816 
    817 file_protected file_pushbuf_t *
    818 file_push_buffer(struct magic_set *ms)
    819 {
    820 	file_pushbuf_t *pb;
    821 
    822 	if (ms->event_flags & EVENT_HAD_ERR)
    823 		return NULL;
    824 
    825 	if ((pb = (CAST(file_pushbuf_t *, malloc(sizeof(*pb))))) == NULL)
    826 		return NULL;
    827 
    828 	pb->buf = ms->o.buf;
    829 	pb->blen = ms->o.blen;
    830 	pb->offset = ms->offset;
    831 
    832 	ms->o.buf = NULL;
    833 	ms->o.blen = 0;
    834 	ms->offset = 0;
    835 
    836 	return pb;
    837 }
    838 
    839 file_protected char *
    840 file_pop_buffer(struct magic_set *ms, file_pushbuf_t *pb)
    841 {
    842 	char *rbuf;
    843 
    844 	if (ms->event_flags & EVENT_HAD_ERR) {
    845 		free(pb->buf);
    846 		free(pb);
    847 		return NULL;
    848 	}
    849 
    850 	rbuf = ms->o.buf;
    851 
    852 	ms->o.buf = pb->buf;
    853 	ms->o.blen = pb->blen;
    854 	ms->offset = pb->offset;
    855 
    856 	free(pb);
    857 	return rbuf;
    858 }
    859 
    860 /*
    861  * convert string to ascii printable format.
    862  */
    863 file_protected char *
    864 file_printable(struct magic_set *ms, char *buf, size_t bufsiz,
    865     const char *str, size_t slen)
    866 {
    867 	char *ptr, *eptr = buf + bufsiz - 1;
    868 	const unsigned char *s = RCAST(const unsigned char *, str);
    869 	const unsigned char *es = s + slen;
    870 
    871 	for (ptr = buf;  ptr < eptr && s < es && *s; s++) {
    872 		if ((ms->flags & MAGIC_RAW) != 0 || isprint(*s)) {
    873 			*ptr++ = *s;
    874 			continue;
    875 		}
    876 		if (ptr >= eptr - 3)
    877 			break;
    878 		*ptr++ = '\\';
    879 		*ptr++ = ((CAST(unsigned int, *s) >> 6) & 7) + '0';
    880 		*ptr++ = ((CAST(unsigned int, *s) >> 3) & 7) + '0';
    881 		*ptr++ = ((CAST(unsigned int, *s) >> 0) & 7) + '0';
    882 	}
    883 	*ptr = '\0';
    884 	return buf;
    885 }
    886 
    887 struct guid {
    888 	uint32_t data1;
    889 	uint16_t data2;
    890 	uint16_t data3;
    891 	uint8_t data4[8];
    892 };
    893 
    894 static char XDIGIT[]  = "0123456789abcdef";
    895 static int
    896 atox(const uint8_t c)
    897 {
    898 	uint8_t d = isupper(c) ? tolower(c) : c;
    899 	const char *q = d ? strchr(XDIGIT, isupper(c) ? tolower(c) : c) : NULL;
    900 	if (q == NULL)
    901 		return -1;
    902 	return q - XDIGIT;
    903 }
    904 
    905 static int
    906 getxvalue(void *p, const char *s, size_t n)
    907 {
    908 	uint64_t v = 0;
    909 	for (size_t i = 0; i < n; i++) {
    910 		int x = atox(s[i]);
    911 		if (x == -1)
    912 			return 0;
    913 		v = (v << 4) | x;
    914 	}
    915 	switch (n) {
    916 	case 8:
    917 		*(uint32_t *)p = v;
    918 		return 1;
    919 	case 4:
    920 		*(uint16_t *)p = v;
    921 		return 1;
    922 	case 2:
    923 		*(uint8_t *)p = v;
    924 		return 1;
    925 	default:
    926 		return 0;
    927 	}
    928 }
    929 
    930 file_protected int
    931 file_parse_guid(const char *s, uint64_t *guid)
    932 {
    933 	struct guid *g = CAST(struct guid *, CAST(void *, guid));
    934 
    935 	if (!getxvalue(&g->data1, s, 8) || s[8] != '-')
    936 		return -1;
    937 	s += 9;
    938 	if (!getxvalue(&g->data2, s, 4) || s[4] != '-')
    939 		return -1;
    940 	s += 5;
    941 	if (!getxvalue(&g->data3, s, 4) || s[4] != '-')
    942 		return -1;
    943 	s += 5;
    944 	if (!getxvalue(&g->data4[0], s, 2) ||
    945 	    !getxvalue(&g->data4[1], s + 2, 2) || s[4] != '-')
    946 		return -1;
    947 	s += 5;
    948 	if (!getxvalue(&g->data4[2], s, 2) ||
    949 	    !getxvalue(&g->data4[3], s + 2, 2) ||
    950 	    !getxvalue(&g->data4[4], s + 4, 2) ||
    951 	    !getxvalue(&g->data4[5], s + 6, 2) ||
    952 	    !getxvalue(&g->data4[6], s + 8, 2) ||
    953 	    !getxvalue(&g->data4[7], s + 10, 2))
    954 		return -1;
    955 
    956 	if (file_bigendian()) {
    957 		g->data1 = file_swap4(g->data1);
    958 		g->data2 = file_swap2(g->data2);
    959 		g->data3 = file_swap2(g->data3);
    960 
    961 	}
    962 	return 0;
    963 }
    964 
    965 file_private int
    966 file_print_guid(char *str, size_t len, const struct guid *g)
    967 {
    968 #ifndef WIN32
    969 	return snprintf(str, len, "%.8X-%.4hX-%.4hX-%.2hhX%.2hhX-"
    970 	    "%.2hhX%.2hhX%.2hhX%.2hhX%.2hhX%.2hhX",
    971 	    g->data1, g->data2, g->data3, g->data4[0], g->data4[1],
    972 	    g->data4[2], g->data4[3], g->data4[4], g->data4[5],
    973 	    g->data4[6], g->data4[7]);
    974 #else
    975 	return snprintf(str, len, "%.8X-%.4hX-%.4hX-%.2hX%.2hX-"
    976 	    "%.2hX%.2hX%.2hX%.2hX%.2hX%.2hX",
    977 	    g->data1, g->data2, g->data3, g->data4[0], g->data4[1],
    978 	    g->data4[2], g->data4[3], g->data4[4], g->data4[5],
    979 	    g->data4[6], g->data4[7]);
    980 #endif
    981 }
    982 
    983 file_protected int
    984 file_print_leguid(char *str, size_t len, const uint64_t *guid)
    985 {
    986 	const struct guid *g = CAST(const struct guid *,
    987 	    CAST(const void *, guid));
    988 	return file_print_guid(str, len, g);
    989 }
    990 
    991 file_protected int
    992 file_print_beguid(char *str, size_t len, const uint64_t *guid)
    993 {
    994 	const struct guid *g = CAST(const struct guid *,
    995 	    CAST(const void *, guid));
    996 	struct guid gg = *g;
    997 	gg.data1 = file_swap4(gg.data1);
    998 	gg.data2 = file_swap2(gg.data2);
    999 	gg.data3 = file_swap2(gg.data3);
   1000 	return file_print_guid(str, len, &gg);
   1001 }
   1002 
   1003 file_protected int
   1004 file_pipe_closexec(int *fds)
   1005 {
   1006 #ifdef __MINGW32__
   1007 	return 0;
   1008 #elif defined(HAVE_PIPE2)
   1009 	return pipe2(fds, O_CLOEXEC);
   1010 #else
   1011 	if (pipe(fds) == -1)
   1012 		return -1;
   1013 # ifdef F_SETFD
   1014 	(void)fcntl(fds[0], F_SETFD, FD_CLOEXEC);
   1015 	(void)fcntl(fds[1], F_SETFD, FD_CLOEXEC);
   1016 # endif
   1017 	return 0;
   1018 #endif
   1019 }
   1020 
   1021 file_protected int
   1022 file_clear_closexec(int fd) {
   1023 #ifdef F_SETFD
   1024 	return fcntl(fd, F_SETFD, 0);
   1025 #else
   1026 	return 0;
   1027 #endif
   1028 }
   1029 
   1030 file_protected char *
   1031 file_strtrim(char *str)
   1032 {
   1033 	char *last;
   1034 
   1035 	while (isspace(CAST(unsigned char, *str)))
   1036 		str++;
   1037 	last = str;
   1038 	while (*last)
   1039 		last++;
   1040 	if (last == str)
   1041 		return str;
   1042 	--last;
   1043 	while (isspace(CAST(unsigned char, *last)))
   1044 		last--;
   1045 	*++last = '\0';
   1046 	return str;
   1047 }
   1048