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