1133359Sobrien/*
2133359Sobrien * Copyright (c) Christos Zoulas 2003.
3133359Sobrien * All Rights Reserved.
4191736Sobrien *
5133359Sobrien * Redistribution and use in source and binary forms, with or without
6133359Sobrien * modification, are permitted provided that the following conditions
7133359Sobrien * are met:
8133359Sobrien * 1. Redistributions of source code must retain the above copyright
9133359Sobrien *    notice immediately at the beginning of the file, without modification,
10133359Sobrien *    this list of conditions, and the following disclaimer.
11133359Sobrien * 2. Redistributions in binary form must reproduce the above copyright
12133359Sobrien *    notice, this list of conditions and the following disclaimer in the
13133359Sobrien *    documentation and/or other materials provided with the distribution.
14191736Sobrien *
15133359Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18133359Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
19133359Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25133359Sobrien * SUCH DAMAGE.
26133359Sobrien */
27133359Sobrien#include "file.h"
28191736Sobrien
29191736Sobrien#ifndef	lint
30328875SeadlerFILE_RCSID("@(#)$File: funcs.c,v 1.93 2017/08/28 13:39:18 christos Exp $")
31191736Sobrien#endif	/* lint */
32191736Sobrien
33133359Sobrien#include "magic.h"
34267843Sdelphij#include <assert.h>
35133359Sobrien#include <stdarg.h>
36133359Sobrien#include <stdlib.h>
37133359Sobrien#include <string.h>
38133359Sobrien#include <ctype.h>
39159764Sobrien#if defined(HAVE_WCHAR_H)
40159764Sobrien#include <wchar.h>
41159764Sobrien#endif
42169942Sobrien#if defined(HAVE_WCTYPE_H)
43169942Sobrien#include <wctype.h>
44169942Sobrien#endif
45169962Sobrien#if defined(HAVE_LIMITS_H)
46169962Sobrien#include <limits.h>
47169962Sobrien#endif
48133359Sobrien
49186690Sobrien#ifndef SIZE_MAX
50186690Sobrien#define SIZE_MAX	((size_t)~0)
51159764Sobrien#endif
52159764Sobrien
53133359Sobrien/*
54186690Sobrien * Like printf, only we append to a buffer.
55133359Sobrien */
56133359Sobrienprotected int
57186690Sobrienfile_vprintf(struct magic_set *ms, const char *fmt, va_list ap)
58133359Sobrien{
59186690Sobrien	int len;
60186690Sobrien	char *buf, *newstr;
61133359Sobrien
62267843Sdelphij	if (ms->event_flags & EVENT_HAD_ERR)
63267843Sdelphij		return 0;
64186690Sobrien	len = vasprintf(&buf, fmt, ap);
65186690Sobrien	if (len < 0)
66175296Sobrien		goto out;
67169962Sobrien
68186690Sobrien	if (ms->o.buf != NULL) {
69186690Sobrien		len = asprintf(&newstr, "%s%s", ms->o.buf, buf);
70186690Sobrien		free(buf);
71186690Sobrien		if (len < 0)
72175296Sobrien			goto out;
73186690Sobrien		free(ms->o.buf);
74186690Sobrien		buf = newstr;
75133359Sobrien	}
76186690Sobrien	ms->o.buf = buf;
77133359Sobrien	return 0;
78175296Sobrienout:
79328875Seadler	fprintf(stderr, "vasprintf failed (%s)", strerror(errno));
80175296Sobrien	return -1;
81133359Sobrien}
82133359Sobrien
83186690Sobrienprotected int
84186690Sobrienfile_printf(struct magic_set *ms, const char *fmt, ...)
85186690Sobrien{
86186690Sobrien	int rv;
87186690Sobrien	va_list ap;
88186690Sobrien
89186690Sobrien	va_start(ap, fmt);
90186690Sobrien	rv = file_vprintf(ms, fmt, ap);
91186690Sobrien	va_end(ap);
92186690Sobrien	return rv;
93186690Sobrien}
94186690Sobrien
95133359Sobrien/*
96133359Sobrien * error - print best error message possible
97133359Sobrien */
98133359Sobrien/*VARARGS*/
99267843Sdelphij__attribute__((__format__(__printf__, 3, 0)))
100169962Sobrienprivate void
101169962Sobrienfile_error_core(struct magic_set *ms, int error, const char *f, va_list va,
102226048Sobrien    size_t lineno)
103133359Sobrien{
104133359Sobrien	/* Only the first error is ok */
105191736Sobrien	if (ms->event_flags & EVENT_HAD_ERR)
106133359Sobrien		return;
107169962Sobrien	if (lineno != 0) {
108186690Sobrien		free(ms->o.buf);
109186690Sobrien		ms->o.buf = NULL;
110290152Sdelphij		file_printf(ms, "line %" SIZE_T_FORMAT "u:", lineno);
111169962Sobrien	}
112290152Sdelphij	if (ms->o.buf && *ms->o.buf)
113290152Sdelphij		file_printf(ms, " ");
114191736Sobrien	file_vprintf(ms, f, va);
115186690Sobrien	if (error > 0)
116186690Sobrien		file_printf(ms, " (%s)", strerror(error));
117191736Sobrien	ms->event_flags |= EVENT_HAD_ERR;
118133359Sobrien	ms->error = error;
119133359Sobrien}
120133359Sobrien
121169962Sobrien/*VARARGS*/
122169962Sobrienprotected void
123169962Sobrienfile_error(struct magic_set *ms, int error, const char *f, ...)
124169962Sobrien{
125169962Sobrien	va_list va;
126169962Sobrien	va_start(va, f);
127169962Sobrien	file_error_core(ms, error, f, va, 0);
128169962Sobrien	va_end(va);
129169962Sobrien}
130133359Sobrien
131169962Sobrien/*
132169962Sobrien * Print an error with magic line number.
133169962Sobrien */
134169962Sobrien/*VARARGS*/
135133359Sobrienprotected void
136169962Sobrienfile_magerror(struct magic_set *ms, const char *f, ...)
137169962Sobrien{
138169962Sobrien	va_list va;
139169962Sobrien	va_start(va, f);
140169962Sobrien	file_error_core(ms, 0, f, va, ms->line);
141169962Sobrien	va_end(va);
142169962Sobrien}
143169962Sobrien
144169962Sobrienprotected void
145169942Sobrienfile_oomem(struct magic_set *ms, size_t len)
146133359Sobrien{
147226048Sobrien	file_error(ms, errno, "cannot allocate %" SIZE_T_FORMAT "u bytes",
148226048Sobrien	    len);
149133359Sobrien}
150133359Sobrien
151133359Sobrienprotected void
152133359Sobrienfile_badseek(struct magic_set *ms)
153133359Sobrien{
154133359Sobrien	file_error(ms, errno, "error seeking");
155133359Sobrien}
156133359Sobrien
157133359Sobrienprotected void
158133359Sobrienfile_badread(struct magic_set *ms)
159133359Sobrien{
160133359Sobrien	file_error(ms, errno, "error reading");
161133359Sobrien}
162133359Sobrien
163133359Sobrien#ifndef COMPILE_ONLY
164284778Sdelphij
165284778Sdelphijstatic int
166284778Sdelphijcheckdone(struct magic_set *ms, int *rv)
167284778Sdelphij{
168284778Sdelphij	if ((ms->flags & MAGIC_CONTINUE) == 0)
169284778Sdelphij		return 1;
170284778Sdelphij	if (file_printf(ms, "\n- ") == -1)
171284778Sdelphij		*rv = -1;
172284778Sdelphij	return 0;
173284778Sdelphij}
174284778Sdelphij
175284778Sdelphij/*ARGSUSED*/
176133359Sobrienprotected int
177284778Sdelphijfile_buffer(struct magic_set *ms, int fd, const char *inname __attribute__ ((__unused__)),
178226048Sobrien    const void *buf, size_t nb)
179133359Sobrien{
180191736Sobrien	int m = 0, rv = 0, looks_text = 0;
181186690Sobrien	const unsigned char *ubuf = CAST(const unsigned char *, buf);
182191736Sobrien	unichar *u8buf = NULL;
183191736Sobrien	size_t ulen;
184191736Sobrien	const char *code = NULL;
185191736Sobrien	const char *code_mime = "binary";
186267843Sdelphij	const char *type = "application/octet-stream";
187267843Sdelphij	const char *def = "data";
188267843Sdelphij	const char *ftype = NULL;
189169962Sobrien
190175296Sobrien	if (nb == 0) {
191267843Sdelphij		def = "empty";
192267843Sdelphij		type = "application/x-empty";
193267843Sdelphij		goto simple;
194175296Sobrien	} else if (nb == 1) {
195267843Sdelphij		def = "very short file (no magic)";
196267843Sdelphij		goto simple;
197175296Sobrien	}
198175296Sobrien
199191736Sobrien	if ((ms->flags & MAGIC_NO_CHECK_ENCODING) == 0) {
200191736Sobrien		looks_text = file_encoding(ms, ubuf, nb, &u8buf, &ulen,
201267843Sdelphij		    &code, &code_mime, &ftype);
202191736Sobrien	}
203191736Sobrien
204169962Sobrien#ifdef __EMX__
205175296Sobrien	if ((ms->flags & MAGIC_NO_CHECK_APPTYPE) == 0 && inname) {
206290152Sdelphij		m = file_os2_apptype(ms, inname, buf, nb);
207290152Sdelphij		if ((ms->flags & MAGIC_DEBUG) != 0)
208290152Sdelphij			(void)fprintf(stderr, "[try os2_apptype %d]\n", m);
209290152Sdelphij		switch (m) {
210175296Sobrien		case -1:
211175296Sobrien			return -1;
212175296Sobrien		case 0:
213175296Sobrien			break;
214175296Sobrien		default:
215175296Sobrien			return 1;
216175296Sobrien		}
217169962Sobrien	}
218169962Sobrien#endif
219226048Sobrien#if HAVE_FORK
220175296Sobrien	/* try compression stuff */
221290152Sdelphij	if ((ms->flags & MAGIC_NO_CHECK_COMPRESS) == 0) {
222290152Sdelphij		m = file_zmagic(ms, fd, inname, ubuf, nb);
223290152Sdelphij		if ((ms->flags & MAGIC_DEBUG) != 0)
224290152Sdelphij			(void)fprintf(stderr, "[try zmagic %d]\n", m);
225290152Sdelphij		if (m) {
226267843Sdelphij			goto done_encoding;
227133359Sobrien		}
228290152Sdelphij	}
229226048Sobrien#endif
230191736Sobrien	/* Check if we have a tar file */
231290152Sdelphij	if ((ms->flags & MAGIC_NO_CHECK_TAR) == 0) {
232290152Sdelphij		m = file_is_tar(ms, ubuf, nb);
233290152Sdelphij		if ((ms->flags & MAGIC_DEBUG) != 0)
234290152Sdelphij			(void)fprintf(stderr, "[try tar %d]\n", m);
235290152Sdelphij		if (m) {
236284778Sdelphij			if (checkdone(ms, &rv))
237284778Sdelphij				goto done;
238191736Sobrien		}
239290152Sdelphij	}
240191736Sobrien
241191736Sobrien	/* Check if we have a CDF file */
242290152Sdelphij	if ((ms->flags & MAGIC_NO_CHECK_CDF) == 0) {
243290152Sdelphij		m = file_trycdf(ms, fd, ubuf, nb);
244290152Sdelphij		if ((ms->flags & MAGIC_DEBUG) != 0)
245290152Sdelphij			(void)fprintf(stderr, "[try cdf %d]\n", m);
246290152Sdelphij		if (m) {
247284778Sdelphij			if (checkdone(ms, &rv))
248284778Sdelphij				goto done;
249191736Sobrien		}
250290152Sdelphij	}
251191736Sobrien
252191736Sobrien	/* try soft magic tests */
253309848Sdelphij	if ((ms->flags & MAGIC_NO_CHECK_SOFT) == 0) {
254300899Sdelphij		m = file_softmagic(ms, ubuf, nb, NULL, NULL, BINTEST,
255300899Sdelphij		    looks_text);
256290152Sdelphij		if ((ms->flags & MAGIC_DEBUG) != 0)
257290152Sdelphij			(void)fprintf(stderr, "[try softmagic %d]\n", m);
258290152Sdelphij		if (m) {
259169962Sobrien#ifdef BUILTIN_ELF
260191736Sobrien			if ((ms->flags & MAGIC_NO_CHECK_ELF) == 0 && m == 1 &&
261191736Sobrien			    nb > 5 && fd != -1) {
262191736Sobrien				/*
263191736Sobrien				 * We matched something in the file, so this
264191736Sobrien				 * *might* be an ELF file, and the file is at
265191736Sobrien				 * least 5 bytes long, so if it's an ELF file
266191736Sobrien				 * it has at least one byte past the ELF magic
267191736Sobrien				 * number - try extracting information from the
268191736Sobrien				 * ELF headers that cannot easily * be
269191736Sobrien				 * extracted with rules in the magic file.
270191736Sobrien				 */
271290152Sdelphij				m = file_tryelf(ms, fd, ubuf, nb);
272290152Sdelphij				if ((ms->flags & MAGIC_DEBUG) != 0)
273290152Sdelphij					(void)fprintf(stderr, "[try elf %d]\n",
274290152Sdelphij					    m);
275191736Sobrien			}
276191736Sobrien#endif
277284778Sdelphij			if (checkdone(ms, &rv))
278284778Sdelphij				goto done;
279191736Sobrien		}
280309848Sdelphij	}
281191736Sobrien
282234250Sobrien	/* try text properties */
283191736Sobrien	if ((ms->flags & MAGIC_NO_CHECK_TEXT) == 0) {
284191736Sobrien
285290152Sdelphij		m = file_ascmagic(ms, ubuf, nb, looks_text);
286290152Sdelphij		if ((ms->flags & MAGIC_DEBUG) != 0)
287290152Sdelphij			(void)fprintf(stderr, "[try ascmagic %d]\n", m);
288290152Sdelphij		if (m) {
289284778Sdelphij			if (checkdone(ms, &rv))
290284778Sdelphij				goto done;
291191736Sobrien		}
292175296Sobrien	}
293191736Sobrien
294267843Sdelphijsimple:
295191736Sobrien	/* give up */
296191736Sobrien	m = 1;
297300899Sdelphij	if (ms->flags & MAGIC_MIME) {
298300899Sdelphij		if ((ms->flags & MAGIC_MIME_TYPE) &&
299300899Sdelphij		    file_printf(ms, "%s", type) == -1)
300300899Sdelphij			rv = -1;
301300899Sdelphij	} else if (ms->flags & MAGIC_APPLE) {
302300899Sdelphij		if (file_printf(ms, "UNKNUNKN") == -1)
303300899Sdelphij			rv = -1;
304300899Sdelphij	} else if (ms->flags & MAGIC_EXTENSION) {
305300899Sdelphij		if (file_printf(ms, "???") == -1)
306300899Sdelphij			rv = -1;
307300899Sdelphij	} else {
308300899Sdelphij		if (file_printf(ms, "%s", def) == -1)
309300899Sdelphij			rv = -1;
310191736Sobrien	}
311191736Sobrien done:
312191736Sobrien	if ((ms->flags & MAGIC_MIME_ENCODING) != 0) {
313191736Sobrien		if (ms->flags & MAGIC_MIME_TYPE)
314191736Sobrien			if (file_printf(ms, "; charset=") == -1)
315191736Sobrien				rv = -1;
316191736Sobrien		if (file_printf(ms, "%s", code_mime) == -1)
317191736Sobrien			rv = -1;
318191736Sobrien	}
319267843Sdelphij#if HAVE_FORK
320267843Sdelphij done_encoding:
321267843Sdelphij#endif
322234250Sobrien	free(u8buf);
323191736Sobrien	if (rv)
324191736Sobrien		return rv;
325191736Sobrien
326175296Sobrien	return m;
327133359Sobrien}
328133359Sobrien#endif
329133359Sobrien
330133359Sobrienprotected int
331328875Seadlerfile_reset(struct magic_set *ms, int checkloaded)
332133359Sobrien{
333328875Seadler	if (checkloaded && ms->mlist[0] == NULL) {
334133359Sobrien		file_error(ms, 0, "no magic files loaded");
335133359Sobrien		return -1;
336133359Sobrien	}
337192348Sdelphij	if (ms->o.buf) {
338192348Sdelphij		free(ms->o.buf);
339192348Sdelphij		ms->o.buf = NULL;
340192348Sdelphij	}
341192348Sdelphij	if (ms->o.pbuf) {
342192348Sdelphij		free(ms->o.pbuf);
343192348Sdelphij		ms->o.pbuf = NULL;
344192348Sdelphij	}
345191736Sobrien	ms->event_flags &= ~EVENT_HAD_ERR;
346133359Sobrien	ms->error = -1;
347133359Sobrien	return 0;
348133359Sobrien}
349133359Sobrien
350159764Sobrien#define OCTALIFY(n, o)	\
351169942Sobrien	/*LINTED*/ \
352169942Sobrien	(void)(*(n)++ = '\\', \
353159764Sobrien	*(n)++ = (((uint32_t)*(o) >> 6) & 3) + '0', \
354159764Sobrien	*(n)++ = (((uint32_t)*(o) >> 3) & 7) + '0', \
355159764Sobrien	*(n)++ = (((uint32_t)*(o) >> 0) & 7) + '0', \
356169942Sobrien	(o)++)
357159764Sobrien
358133359Sobrienprotected const char *
359133359Sobrienfile_getbuffer(struct magic_set *ms)
360133359Sobrien{
361169962Sobrien	char *pbuf, *op, *np;
362169962Sobrien	size_t psize, len;
363133359Sobrien
364191736Sobrien	if (ms->event_flags & EVENT_HAD_ERR)
365133359Sobrien		return NULL;
366133359Sobrien
367133359Sobrien	if (ms->flags & MAGIC_RAW)
368133359Sobrien		return ms->o.buf;
369133359Sobrien
370191736Sobrien	if (ms->o.buf == NULL)
371191736Sobrien		return NULL;
372191736Sobrien
373169962Sobrien	/* * 4 is for octal representation, + 1 is for NUL */
374186690Sobrien	len = strlen(ms->o.buf);
375186690Sobrien	if (len > (SIZE_MAX - 1) / 4) {
376169962Sobrien		file_oomem(ms, len);
377169962Sobrien		return NULL;
378169962Sobrien	}
379169962Sobrien	psize = len * 4 + 1;
380186690Sobrien	if ((pbuf = CAST(char *, realloc(ms->o.pbuf, psize))) == NULL) {
381186690Sobrien		file_oomem(ms, psize);
382186690Sobrien		return NULL;
383133359Sobrien	}
384186690Sobrien	ms->o.pbuf = pbuf;
385133359Sobrien
386159764Sobrien#if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH)
387159764Sobrien	{
388159764Sobrien		mbstate_t state;
389159764Sobrien		wchar_t nextchar;
390159764Sobrien		int mb_conv = 1;
391159764Sobrien		size_t bytesconsumed;
392159764Sobrien		char *eop;
393159764Sobrien		(void)memset(&state, 0, sizeof(mbstate_t));
394159764Sobrien
395159764Sobrien		np = ms->o.pbuf;
396159764Sobrien		op = ms->o.buf;
397186690Sobrien		eop = op + len;
398159764Sobrien
399159764Sobrien		while (op < eop) {
400169942Sobrien			bytesconsumed = mbrtowc(&nextchar, op,
401169942Sobrien			    (size_t)(eop - op), &state);
402159764Sobrien			if (bytesconsumed == (size_t)(-1) ||
403159764Sobrien			    bytesconsumed == (size_t)(-2)) {
404159764Sobrien				mb_conv = 0;
405159764Sobrien				break;
406159764Sobrien			}
407159764Sobrien
408169942Sobrien			if (iswprint(nextchar)) {
409159764Sobrien				(void)memcpy(np, op, bytesconsumed);
410159764Sobrien				op += bytesconsumed;
411159764Sobrien				np += bytesconsumed;
412159764Sobrien			} else {
413159764Sobrien				while (bytesconsumed-- > 0)
414159764Sobrien					OCTALIFY(np, op);
415159764Sobrien			}
416159764Sobrien		}
417159764Sobrien		*np = '\0';
418159764Sobrien
419159764Sobrien		/* Parsing succeeded as a multi-byte sequence */
420159764Sobrien		if (mb_conv != 0)
421159764Sobrien			return ms->o.pbuf;
422159764Sobrien	}
423159764Sobrien#endif
424159764Sobrien
425226048Sobrien	for (np = ms->o.pbuf, op = ms->o.buf; *op;) {
426133359Sobrien		if (isprint((unsigned char)*op)) {
427226048Sobrien			*np++ = *op++;
428133359Sobrien		} else {
429159764Sobrien			OCTALIFY(np, op);
430133359Sobrien		}
431133359Sobrien	}
432133359Sobrien	*np = '\0';
433133359Sobrien	return ms->o.pbuf;
434133359Sobrien}
435159764Sobrien
436169962Sobrienprotected int
437169962Sobrienfile_check_mem(struct magic_set *ms, unsigned int level)
438169962Sobrien{
439169962Sobrien	size_t len;
440169962Sobrien
441169962Sobrien	if (level >= ms->c.len) {
442284778Sdelphij		len = (ms->c.len = 20 + level) * sizeof(*ms->c.li);
443186690Sobrien		ms->c.li = CAST(struct level_info *, (ms->c.li == NULL) ?
444186690Sobrien		    malloc(len) :
445186690Sobrien		    realloc(ms->c.li, len));
446169962Sobrien		if (ms->c.li == NULL) {
447169962Sobrien			file_oomem(ms, len);
448169962Sobrien			return -1;
449169962Sobrien		}
450169962Sobrien	}
451169962Sobrien	ms->c.li[level].got_match = 0;
452169962Sobrien#ifdef ENABLE_CONDITIONALS
453169962Sobrien	ms->c.li[level].last_match = 0;
454169962Sobrien	ms->c.li[level].last_cond = COND_NONE;
455169962Sobrien#endif /* ENABLE_CONDITIONALS */
456169962Sobrien	return 0;
457169962Sobrien}
458226048Sobrien
459226048Sobrienprotected size_t
460226048Sobrienfile_printedlen(const struct magic_set *ms)
461226048Sobrien{
462226048Sobrien	return ms->o.buf == NULL ? 0 : strlen(ms->o.buf);
463226048Sobrien}
464226048Sobrien
465226048Sobrienprotected int
466226048Sobrienfile_replace(struct magic_set *ms, const char *pat, const char *rep)
467226048Sobrien{
468267843Sdelphij	file_regex_t rx;
469267843Sdelphij	int rc, rv = -1;
470226048Sobrien
471267843Sdelphij	rc = file_regcomp(&rx, pat, REG_EXTENDED);
472226048Sobrien	if (rc) {
473267843Sdelphij		file_regerror(&rx, rc, ms);
474226048Sobrien	} else {
475226048Sobrien		regmatch_t rm;
476226048Sobrien		int nm = 0;
477267843Sdelphij		while (file_regexec(&rx, ms->o.buf, 1, &rm, 0) == 0) {
478226048Sobrien			ms->o.buf[rm.rm_so] = '\0';
479226048Sobrien			if (file_printf(ms, "%s%s", rep,
480226048Sobrien			    rm.rm_eo != 0 ? ms->o.buf + rm.rm_eo : "") == -1)
481267843Sdelphij				goto out;
482226048Sobrien			nm++;
483226048Sobrien		}
484267843Sdelphij		rv = nm;
485226048Sobrien	}
486267843Sdelphijout:
487267843Sdelphij	file_regfree(&rx);
488267843Sdelphij	return rv;
489226048Sobrien}
490267843Sdelphij
491267843Sdelphijprotected int
492267843Sdelphijfile_regcomp(file_regex_t *rx, const char *pat, int flags)
493267843Sdelphij{
494276415Sdelphij#ifdef USE_C_LOCALE
495276415Sdelphij	rx->c_lc_ctype = newlocale(LC_CTYPE_MASK, "C", 0);
496276415Sdelphij	assert(rx->c_lc_ctype != NULL);
497276415Sdelphij	rx->old_lc_ctype = uselocale(rx->c_lc_ctype);
498267843Sdelphij	assert(rx->old_lc_ctype != NULL);
499300899Sdelphij#else
500300899Sdelphij	rx->old_lc_ctype = setlocale(LC_CTYPE, "C");
501276415Sdelphij#endif
502267843Sdelphij	rx->pat = pat;
503267843Sdelphij
504267843Sdelphij	return rx->rc = regcomp(&rx->rx, pat, flags);
505267843Sdelphij}
506267843Sdelphij
507267843Sdelphijprotected int
508267843Sdelphijfile_regexec(file_regex_t *rx, const char *str, size_t nmatch,
509267843Sdelphij    regmatch_t* pmatch, int eflags)
510267843Sdelphij{
511267843Sdelphij	assert(rx->rc == 0);
512328875Seadler	/* XXX: force initialization because glibc does not always do this */
513328875Seadler	memset(pmatch, 0, nmatch * sizeof(*pmatch));
514267843Sdelphij	return regexec(&rx->rx, str, nmatch, pmatch, eflags);
515267843Sdelphij}
516267843Sdelphij
517267843Sdelphijprotected void
518267843Sdelphijfile_regfree(file_regex_t *rx)
519267843Sdelphij{
520267843Sdelphij	if (rx->rc == 0)
521267843Sdelphij		regfree(&rx->rx);
522276415Sdelphij#ifdef USE_C_LOCALE
523276415Sdelphij	(void)uselocale(rx->old_lc_ctype);
524276415Sdelphij	freelocale(rx->c_lc_ctype);
525300899Sdelphij#else
526300899Sdelphij	(void)setlocale(LC_CTYPE, rx->old_lc_ctype);
527276415Sdelphij#endif
528267843Sdelphij}
529267843Sdelphij
530267843Sdelphijprotected void
531267843Sdelphijfile_regerror(file_regex_t *rx, int rc, struct magic_set *ms)
532267843Sdelphij{
533267843Sdelphij	char errmsg[512];
534267843Sdelphij
535267843Sdelphij	(void)regerror(rc, &rx->rx, errmsg, sizeof(errmsg));
536267843Sdelphij	file_magerror(ms, "regex error %d for `%s', (%s)", rc, rx->pat,
537267843Sdelphij	    errmsg);
538267843Sdelphij}
539275668Sdelphij
540275668Sdelphijprotected file_pushbuf_t *
541275668Sdelphijfile_push_buffer(struct magic_set *ms)
542275668Sdelphij{
543275668Sdelphij	file_pushbuf_t *pb;
544275668Sdelphij
545275668Sdelphij	if (ms->event_flags & EVENT_HAD_ERR)
546275668Sdelphij		return NULL;
547275668Sdelphij
548275668Sdelphij	if ((pb = (CAST(file_pushbuf_t *, malloc(sizeof(*pb))))) == NULL)
549275668Sdelphij		return NULL;
550275668Sdelphij
551275668Sdelphij	pb->buf = ms->o.buf;
552275668Sdelphij	pb->offset = ms->offset;
553275668Sdelphij
554275668Sdelphij	ms->o.buf = NULL;
555275668Sdelphij	ms->offset = 0;
556275668Sdelphij
557275668Sdelphij	return pb;
558275668Sdelphij}
559275668Sdelphij
560275668Sdelphijprotected char *
561275668Sdelphijfile_pop_buffer(struct magic_set *ms, file_pushbuf_t *pb)
562275668Sdelphij{
563275668Sdelphij	char *rbuf;
564275668Sdelphij
565275668Sdelphij	if (ms->event_flags & EVENT_HAD_ERR) {
566275668Sdelphij		free(pb->buf);
567275668Sdelphij		free(pb);
568275668Sdelphij		return NULL;
569275668Sdelphij	}
570275668Sdelphij
571275668Sdelphij	rbuf = ms->o.buf;
572275668Sdelphij
573275668Sdelphij	ms->o.buf = pb->buf;
574275668Sdelphij	ms->offset = pb->offset;
575275668Sdelphij
576275668Sdelphij	free(pb);
577275668Sdelphij	return rbuf;
578275668Sdelphij}
579277592Sdelphij
580277592Sdelphij/*
581277592Sdelphij * convert string to ascii printable format.
582277592Sdelphij */
583277592Sdelphijprotected char *
584277592Sdelphijfile_printable(char *buf, size_t bufsiz, const char *str)
585277592Sdelphij{
586277592Sdelphij	char *ptr, *eptr;
587277592Sdelphij	const unsigned char *s = (const unsigned char *)str;
588277592Sdelphij
589277592Sdelphij	for (ptr = buf, eptr = ptr + bufsiz - 1; ptr < eptr && *s; s++) {
590277592Sdelphij		if (isprint(*s)) {
591277592Sdelphij			*ptr++ = *s;
592277592Sdelphij			continue;
593277592Sdelphij		}
594277592Sdelphij		if (ptr >= eptr - 3)
595277592Sdelphij			break;
596277592Sdelphij		*ptr++ = '\\';
597284778Sdelphij		*ptr++ = ((CAST(unsigned int, *s) >> 6) & 7) + '0';
598284778Sdelphij		*ptr++ = ((CAST(unsigned int, *s) >> 3) & 7) + '0';
599284778Sdelphij		*ptr++ = ((CAST(unsigned int, *s) >> 0) & 7) + '0';
600277592Sdelphij	}
601277592Sdelphij	*ptr = '\0';
602277592Sdelphij	return buf;
603277592Sdelphij}
604