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 33FILE_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 60file_protected char * 61file_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 72file_private void 73file_clearbuf(struct magic_set *ms) 74{ 75 free(ms->o.buf); 76 ms->o.buf = NULL; 77 ms->o.blen = 0; 78} 79 80file_private int 81file_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 99file_protected int 100file_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 */ 138file_protected int 139file_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; 175out: 176 file_clearbuf(ms); 177 file_error(ms, errno, "vasprintf failed"); 178 return -1; 179} 180 181file_protected int 182file_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))) 198file_private void 199file_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*/ 219file_protected void 220file_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*/ 232file_protected void 233file_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 241file_protected void 242file_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 248file_protected void 249file_badseek(struct magic_set *ms) 250{ 251 file_error(ms, errno, "error seeking"); 252} 253 254file_protected void 255file_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 263file_protected int 264file_separator(struct magic_set *ms) 265{ 266 return file_printf(ms, FILE_SEPARATOR); 267} 268 269static void 270trim_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 288static int 289checkdone(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 298file_protected int 299file_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*/ 328file_protected int 329file_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 490simple: 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 520file_protected int 521file_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 545file_protected const char * 546file_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 623file_protected int 624file_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 646file_protected size_t 647file_printedlen(const struct magic_set *ms) 648{ 649 return ms->o.blen; 650} 651 652file_protected int 653file_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 } 671out: 672 file_regfree(&rx); 673 return rv; 674} 675 676file_private int 677check_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 707file_protected int 708file_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*/ 742file_protected int 743file_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 767file_protected void 768file_regfree(file_regex_t *rx) 769{ 770 regfree(rx); 771} 772 773file_protected file_pushbuf_t * 774file_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 795file_protected char * 796file_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 */ 819file_protected char * 820file_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 843struct guid { 844 uint32_t data1; 845 uint16_t data2; 846 uint16_t data3; 847 uint8_t data4[8]; 848}; 849 850file_protected int 851file_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 875file_protected int 876file_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 896file_protected int 897file_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 914file_protected int 915file_clear_closexec(int fd) { 916#ifdef F_SETFD 917 return fcntl(fd, F_SETFD, 0); 918#else 919 return 0; 920#endif 921} 922 923file_protected char * 924file_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