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