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