1 /* $NetBSD: funcs.c,v 1.20 2022/09/24 20:21:46 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.132 2022/09/24 19:17:26 christos Exp $") 34 #else 35 __RCSID("$NetBSD: funcs.c,v 1.20 2022/09/24 20:21:46 christos Exp $"); 36 #endif 37 #endif /* lint */ 38 39 #include "magic.h" 40 #include <assert.h> 41 #include <stdarg.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <ctype.h> 45 #ifdef HAVE_UNISTD_H 46 #include <unistd.h> /* for pipe2() */ 47 #endif 48 #if defined(HAVE_WCHAR_H) 49 #include <wchar.h> 50 #endif 51 #if defined(HAVE_WCTYPE_H) 52 #include <wctype.h> 53 #endif 54 #include <limits.h> 55 56 #ifndef SIZE_MAX 57 #define SIZE_MAX ((size_t)~0) 58 #endif 59 60 protected char * 61 file_copystr(char *buf, size_t blen, size_t width, const char *str) 62 { 63 if (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 private void 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 private int 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 protected int 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 protected int 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 protected int 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 private void 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 protected void 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 protected void 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 protected void 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 protected void 249 file_badseek(struct magic_set *ms) 250 { 251 file_error(ms, errno, "error seeking"); 252 } 253 254 protected void 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 protected int 264 file_separator(struct magic_set *ms) 265 { 266 return file_printf(ms, FILE_SEPARATOR); 267 } 268 269 static void 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 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 protected int 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 protected int 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); 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 CDF file */ 417 if ((ms->flags & MAGIC_NO_CHECK_CDF) == 0) { 418 m = file_trycdf(ms, &b); 419 if ((ms->flags & MAGIC_DEBUG) != 0) 420 (void)fprintf(stderr, "[try cdf %d]\n", m); 421 if (m) { 422 if (checkdone(ms, &rv)) 423 goto done; 424 } 425 } 426 #ifdef BUILTIN_ELF 427 if ((ms->flags & MAGIC_NO_CHECK_ELF) == 0 && nb > 5 && fd != -1) { 428 file_pushbuf_t *pb; 429 /* 430 * We matched something in the file, so this 431 * *might* be an ELF file, and the file is at 432 * least 5 bytes long, so if it's an ELF file 433 * it has at least one byte past the ELF magic 434 * number - try extracting information from the 435 * ELF headers that cannot easily be extracted 436 * with rules in the magic file. We we don't 437 * print the information yet. 438 */ 439 if ((pb = file_push_buffer(ms)) == NULL) 440 return -1; 441 442 rv = file_tryelf(ms, &b); 443 rbuf = file_pop_buffer(ms, pb); 444 if (rv == -1) { 445 free(rbuf); 446 rbuf = NULL; 447 } 448 if ((ms->flags & MAGIC_DEBUG) != 0) 449 (void)fprintf(stderr, "[try elf %d]\n", m); 450 } 451 #endif 452 453 /* try soft magic tests */ 454 if ((ms->flags & MAGIC_NO_CHECK_SOFT) == 0) { 455 m = file_softmagic(ms, &b, NULL, NULL, BINTEST, looks_text); 456 if ((ms->flags & MAGIC_DEBUG) != 0) 457 (void)fprintf(stderr, "[try softmagic %d]\n", m); 458 if (m == 1 && rbuf) { 459 if (file_printf(ms, "%s", rbuf) == -1) 460 goto done; 461 } 462 if (m) { 463 if (checkdone(ms, &rv)) 464 goto done; 465 } 466 } 467 468 /* try text properties */ 469 if ((ms->flags & MAGIC_NO_CHECK_TEXT) == 0) { 470 471 m = file_ascmagic(ms, &b, looks_text); 472 if ((ms->flags & MAGIC_DEBUG) != 0) 473 (void)fprintf(stderr, "[try ascmagic %d]\n", m); 474 if (m) { 475 goto done; 476 } 477 } 478 479 simple: 480 /* give up */ 481 if (m == 0) { 482 m = 1; 483 rv = file_default(ms, nb); 484 if (rv == 0) 485 if (file_printf(ms, "%s", def) == -1) 486 rv = -1; 487 } 488 done: 489 trim_separator(ms); 490 if ((ms->flags & MAGIC_MIME_ENCODING) != 0) { 491 if (ms->flags & MAGIC_MIME_TYPE) 492 if (file_printf(ms, "; charset=") == -1) 493 rv = -1; 494 if (file_printf(ms, "%s", code_mime) == -1) 495 rv = -1; 496 } 497 #if HAVE_FORK 498 done_encoding: 499 #endif 500 free(rbuf); 501 buffer_fini(&b); 502 if (rv) 503 return rv; 504 505 return m; 506 } 507 #endif 508 509 protected int 510 file_reset(struct magic_set *ms, int checkloaded) 511 { 512 if (checkloaded && ms->mlist[0] == NULL) { 513 file_error(ms, 0, "no magic files loaded"); 514 return -1; 515 } 516 file_clearbuf(ms); 517 if (ms->o.pbuf) { 518 free(ms->o.pbuf); 519 ms->o.pbuf = NULL; 520 } 521 ms->event_flags &= ~EVENT_HAD_ERR; 522 ms->error = -1; 523 return 0; 524 } 525 526 #define OCTALIFY(n, o) \ 527 /*LINTED*/ \ 528 (void)(*(n)++ = '\\', \ 529 *(n)++ = ((CAST(uint32_t, *(o)) >> 6) & 3) + '0', \ 530 *(n)++ = ((CAST(uint32_t, *(o)) >> 3) & 7) + '0', \ 531 *(n)++ = ((CAST(uint32_t, *(o)) >> 0) & 7) + '0', \ 532 (o)++) 533 534 protected const char * 535 file_getbuffer(struct magic_set *ms) 536 { 537 char *pbuf, *op, *np; 538 size_t psize, len; 539 540 if (ms->event_flags & EVENT_HAD_ERR) 541 return NULL; 542 543 if (ms->flags & MAGIC_RAW) 544 return ms->o.buf; 545 546 if (ms->o.buf == NULL) 547 return NULL; 548 549 /* * 4 is for octal representation, + 1 is for NUL */ 550 len = strlen(ms->o.buf); 551 if (len > (SIZE_MAX - 1) / 4) { 552 file_oomem(ms, len); 553 return NULL; 554 } 555 psize = len * 4 + 1; 556 if ((pbuf = CAST(char *, realloc(ms->o.pbuf, psize))) == NULL) { 557 file_oomem(ms, psize); 558 return NULL; 559 } 560 ms->o.pbuf = pbuf; 561 562 #if defined(HAVE_WCHAR_H) && defined(HAVE_MBRTOWC) && defined(HAVE_WCWIDTH) 563 { 564 mbstate_t state; 565 wchar_t nextchar; 566 int mb_conv = 1; 567 size_t bytesconsumed; 568 char *eop; 569 (void)memset(&state, 0, sizeof(mbstate_t)); 570 571 np = ms->o.pbuf; 572 op = ms->o.buf; 573 eop = op + len; 574 575 while (op < eop) { 576 bytesconsumed = mbrtowc(&nextchar, op, 577 CAST(size_t, eop - op), &state); 578 if (bytesconsumed == CAST(size_t, -1) || 579 bytesconsumed == CAST(size_t, -2)) { 580 mb_conv = 0; 581 break; 582 } 583 584 if (iswprint(nextchar)) { 585 (void)memcpy(np, op, bytesconsumed); 586 op += bytesconsumed; 587 np += bytesconsumed; 588 } else { 589 while (bytesconsumed-- > 0) 590 OCTALIFY(np, op); 591 } 592 } 593 *np = '\0'; 594 595 /* Parsing succeeded as a multi-byte sequence */ 596 if (mb_conv != 0) 597 return ms->o.pbuf; 598 } 599 #endif 600 601 for (np = ms->o.pbuf, op = ms->o.buf; *op;) { 602 if (isprint(CAST(unsigned char, *op))) { 603 *np++ = *op++; 604 } else { 605 OCTALIFY(np, op); 606 } 607 } 608 *np = '\0'; 609 return ms->o.pbuf; 610 } 611 612 protected int 613 file_check_mem(struct magic_set *ms, unsigned int level) 614 { 615 size_t len; 616 617 if (level >= ms->c.len) { 618 len = (ms->c.len = 20 + level) * sizeof(*ms->c.li); 619 ms->c.li = CAST(struct level_info *, (ms->c.li == NULL) ? 620 malloc(len) : 621 realloc(ms->c.li, len)); 622 if (ms->c.li == NULL) { 623 file_oomem(ms, len); 624 return -1; 625 } 626 } 627 ms->c.li[level].got_match = 0; 628 #ifdef ENABLE_CONDITIONALS 629 ms->c.li[level].last_match = 0; 630 ms->c.li[level].last_cond = COND_NONE; 631 #endif /* ENABLE_CONDITIONALS */ 632 return 0; 633 } 634 635 protected size_t 636 file_printedlen(const struct magic_set *ms) 637 { 638 return ms->o.blen; 639 } 640 641 protected int 642 file_replace(struct magic_set *ms, const char *pat, const char *rep) 643 { 644 file_regex_t rx; 645 int rc, rv = -1; 646 647 rc = file_regcomp(ms, &rx, pat, REG_EXTENDED); 648 if (rc == 0) { 649 regmatch_t rm; 650 int nm = 0; 651 while (file_regexec(ms, &rx, ms->o.buf, 1, &rm, 0) == 0) { 652 ms->o.buf[rm.rm_so] = '\0'; 653 if (file_printf(ms, "%s%s", rep, 654 rm.rm_eo != 0 ? ms->o.buf + rm.rm_eo : "") == -1) 655 goto out; 656 nm++; 657 } 658 rv = nm; 659 } 660 out: 661 file_regfree(&rx); 662 return rv; 663 } 664 665 private int 666 check_regex(struct magic_set *ms, const char *pat) 667 { 668 char sbuf[512]; 669 unsigned char oc = '\0'; 670 671 for (const char *p = pat; *p; p++) { 672 unsigned char c = *p; 673 // Avoid repetition 674 if (c == oc && strchr("?*+{", c) != NULL) { 675 size_t len = strlen(pat); 676 file_magwarn(ms, 677 "repetition-operator operand `%c' " 678 "invalid in regex `%s'", c, 679 file_printable(ms, sbuf, sizeof(sbuf), pat, len)); 680 return -1; 681 } 682 oc = c; 683 if (isprint(c) || isspace(c) || c == '\b' 684 || c == 0x8a) // XXX: apple magic fixme 685 continue; 686 size_t len = strlen(pat); 687 file_magwarn(ms, 688 "non-ascii characters in regex \\%#o `%s'", 689 c, file_printable(ms, sbuf, sizeof(sbuf), pat, len)); 690 return -1; 691 } 692 return 0; 693 } 694 695 protected int 696 file_regcomp(struct magic_set *ms file_locale_used, file_regex_t *rx, 697 const char *pat, int flags) 698 { 699 if (check_regex(ms, pat) == -1) 700 return -1; 701 702 #ifdef USE_C_LOCALE 703 locale_t old = uselocale(ms->c_lc_ctype); 704 assert(old != NULL); 705 #else 706 char old[1024]; 707 strlcpy(old, setlocale(LC_CTYPE, NULL), sizeof(old)); 708 (void)setlocale(LC_CTYPE, "C"); 709 #endif 710 int rc; 711 rc = regcomp(rx, pat, flags); 712 713 #ifdef USE_C_LOCALE 714 uselocale(old); 715 #else 716 (void)setlocale(LC_CTYPE, old); 717 #endif 718 if (rc > 0 && (ms->flags & MAGIC_CHECK)) { 719 char errmsg[512], buf[512]; 720 721 (void)regerror(rc, rx, errmsg, sizeof(errmsg)); 722 file_magerror(ms, "regex error %d for `%s', (%s)", rc, 723 file_printable(ms, buf, sizeof(buf), pat, strlen(pat)), 724 errmsg); 725 } 726 return rc; 727 } 728 729 /*ARGSUSED*/ 730 protected int 731 file_regexec(struct magic_set *ms file_locale_used, file_regex_t *rx, 732 const char *str, size_t nmatch, regmatch_t* pmatch, int eflags) 733 { 734 #ifdef USE_C_LOCALE 735 locale_t old = uselocale(ms->c_lc_ctype); 736 assert(old != NULL); 737 #else 738 char old[1024]; 739 strlcpy(old, setlocale(LC_CTYPE, NULL), sizeof(old)); 740 (void)setlocale(LC_CTYPE, "C"); 741 #endif 742 int rc; 743 /* XXX: force initialization because glibc does not always do this */ 744 if (nmatch != 0) 745 memset(pmatch, 0, nmatch * sizeof(*pmatch)); 746 rc = regexec(rx, str, nmatch, pmatch, eflags); 747 #ifdef USE_C_LOCALE 748 uselocale(old); 749 #else 750 (void)setlocale(LC_CTYPE, old); 751 #endif 752 return rc; 753 } 754 755 protected void 756 file_regfree(file_regex_t *rx) 757 { 758 regfree(rx); 759 } 760 761 protected file_pushbuf_t * 762 file_push_buffer(struct magic_set *ms) 763 { 764 file_pushbuf_t *pb; 765 766 if (ms->event_flags & EVENT_HAD_ERR) 767 return NULL; 768 769 if ((pb = (CAST(file_pushbuf_t *, malloc(sizeof(*pb))))) == NULL) 770 return NULL; 771 772 pb->buf = ms->o.buf; 773 pb->blen = ms->o.blen; 774 pb->offset = ms->offset; 775 776 ms->o.buf = NULL; 777 ms->o.blen = 0; 778 ms->offset = 0; 779 780 return pb; 781 } 782 783 protected char * 784 file_pop_buffer(struct magic_set *ms, file_pushbuf_t *pb) 785 { 786 char *rbuf; 787 788 if (ms->event_flags & EVENT_HAD_ERR) { 789 free(pb->buf); 790 free(pb); 791 return NULL; 792 } 793 794 rbuf = ms->o.buf; 795 796 ms->o.buf = pb->buf; 797 ms->o.blen = pb->blen; 798 ms->offset = pb->offset; 799 800 free(pb); 801 return rbuf; 802 } 803 804 /* 805 * convert string to ascii printable format. 806 */ 807 protected char * 808 file_printable(struct magic_set *ms, char *buf, size_t bufsiz, 809 const char *str, size_t slen) 810 { 811 char *ptr, *eptr = buf + bufsiz - 1; 812 const unsigned char *s = RCAST(const unsigned char *, str); 813 const unsigned char *es = s + slen; 814 815 for (ptr = buf; ptr < eptr && s < es && *s; s++) { 816 if ((ms->flags & MAGIC_RAW) != 0 || 817 (isprint(*s) && !isspace(*s))) { 818 *ptr++ = *s; 819 continue; 820 } 821 if (ptr >= eptr - 3) 822 break; 823 *ptr++ = '\\'; 824 *ptr++ = ((CAST(unsigned int, *s) >> 6) & 7) + '0'; 825 *ptr++ = ((CAST(unsigned int, *s) >> 3) & 7) + '0'; 826 *ptr++ = ((CAST(unsigned int, *s) >> 0) & 7) + '0'; 827 } 828 *ptr = '\0'; 829 return buf; 830 } 831 832 struct guid { 833 uint32_t data1; 834 uint16_t data2; 835 uint16_t data3; 836 uint8_t data4[8]; 837 }; 838 839 protected int 840 file_parse_guid(const char *s, uint64_t *guid) 841 { 842 struct guid *g = CAST(struct guid *, CAST(void *, guid)); 843 #ifndef WIN32 844 return sscanf(s, 845 "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx", 846 &g->data1, &g->data2, &g->data3, &g->data4[0], &g->data4[1], 847 &g->data4[2], &g->data4[3], &g->data4[4], &g->data4[5], 848 &g->data4[6], &g->data4[7]) == 11 ? 0 : -1; 849 #else 850 /* MS-Windows runtime doesn't support %hhx, except under 851 non-default __USE_MINGW_ANSI_STDIO. */ 852 uint16_t data16[8]; 853 int rv = sscanf(s, "%8x-%4hx-%4hx-%2hx%2hx-%2hx%2hx%2hx%2hx%2hx%2hx", 854 &g->data1, &g->data2, &g->data3, &data16[0], &data16[1], 855 &data16[2], &data16[3], &data16[4], &data16[5], 856 &data16[6], &data16[7]) == 11 ? 0 : -1; 857 int i; 858 for (i = 0; i < 8; i++) 859 g->data4[i] = data16[i]; 860 return rv; 861 #endif 862 } 863 864 protected int 865 file_print_guid(char *str, size_t len, const uint64_t *guid) 866 { 867 const struct guid *g = CAST(const struct guid *, 868 CAST(const void *, guid)); 869 870 #ifndef WIN32 871 return snprintf(str, len, "%.8X-%.4hX-%.4hX-%.2hhX%.2hhX-" 872 "%.2hhX%.2hhX%.2hhX%.2hhX%.2hhX%.2hhX", 873 g->data1, g->data2, g->data3, g->data4[0], g->data4[1], 874 g->data4[2], g->data4[3], g->data4[4], g->data4[5], 875 g->data4[6], g->data4[7]); 876 #else 877 return snprintf(str, len, "%.8X-%.4hX-%.4hX-%.2hX%.2hX-" 878 "%.2hX%.2hX%.2hX%.2hX%.2hX%.2hX", 879 g->data1, g->data2, g->data3, g->data4[0], g->data4[1], 880 g->data4[2], g->data4[3], g->data4[4], g->data4[5], 881 g->data4[6], g->data4[7]); 882 #endif 883 } 884 885 protected int 886 file_pipe_closexec(int *fds) 887 { 888 #ifdef HAVE_PIPE2 889 return pipe2(fds, O_CLOEXEC); 890 #else 891 if (pipe(fds) == -1) 892 return -1; 893 # ifdef F_SETFD 894 (void)fcntl(fds[0], F_SETFD, FD_CLOEXEC); 895 (void)fcntl(fds[1], F_SETFD, FD_CLOEXEC); 896 # endif 897 return 0; 898 #endif 899 } 900 901 protected int 902 file_clear_closexec(int fd) { 903 #ifdef F_SETFD 904 return fcntl(fd, F_SETFD, 0); 905 #else 906 return 0; 907 #endif 908 } 909 910 protected char * 911 file_strtrim(char *str) 912 { 913 char *last; 914 915 while (isspace(CAST(unsigned char, *str))) 916 str++; 917 last = str; 918 while (*last) 919 last++; 920 --last; 921 while (isspace(CAST(unsigned char, *last))) 922 last--; 923 *++last = '\0'; 924 return str; 925 } 926