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