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