master xplshn/aruu / shared / libtcutil / tcutil.c
   1#include "../../cmd/dev/config.h"
   2#include "table.h"
   3#include "tcutil.h"
   4#include "version.h"
   5
   6#include <assert.h>
   7#include <ctype.h>
   8#include <limits.h> /* CHAR_BIT */
   9#include <stdarg.h>
  10#include <stdlib.h> /* malloc */
  11#include <string.h> /* strcmp */
  12#include <sys/stat.h>
  13
  14/* riscv toolchain lacks getline, provides __getline instead */
  15#if defined(__riscv) && !defined(__TC)
  16static inline ssize_t
  17getline(char **lineptr, size_t *n, FILE *stream)
  18{
  19  return __getline(lineptr, n, stream);
  20}
  21#endif
  22
  23/* character classification */
  24
  25int
  26isalnum_(int c)
  27{
  28  return isalnum(c) || c == '_';
  29}
  30
  31int
  32isutf8first(int c_)
  33{
  34  unsigned int c = c_;
  35  if (c < 0xc0)
  36    return 0;
  37  if (c >= 0xfc)
  38    return 6;
  39  if (c >= 0xf8)
  40    return 5;
  41  if (c >= 0xf0)
  42    return 4;
  43  if (c >= 0xe0)
  44    return 3;
  45  return 2;
  46}
  47
  48int
  49isutf8follow(int c)
  50{
  51  return c >= 0x80 && c < 0xc0;
  52}
  53
  54int
  55isoctal(int c)
  56{
  57  return '0' <= c && c <= '7';
  58}
  59
  60int
  61xvalue(char c)
  62{
  63  return ('0' <= c && c <= '9')   ? c - '0'
  64         : ('A' <= c && c <= 'F') ? c - ('A' - 10)
  65         : ('a' <= c && c <= 'f') ? c - ('a' - 10)
  66                                  : -1;
  67}
  68
  69int
  70starts_with(const char *str, const char *prefix)
  71{
  72  size_t len = strlen(prefix);
  73  return strncmp(str, prefix, len) == 0;
  74}
  75
  76int
  77most_significant_bit(size_t x)
  78{
  79  int bit;
  80
  81  assert(x > 0);
  82#if defined(__GNUC__) || defined(__TC)
  83  if (sizeof(size_t) == sizeof(unsigned int)) {
  84    return (int)(sizeof(x) * CHAR_BIT - 1) - __builtin_clz(x);
  85  } else if (sizeof(size_t) == sizeof(unsigned long)) {
  86    return (int)(sizeof(x) * CHAR_BIT - 1) - __builtin_clzl(x);
  87  }
  88#endif
  89  for (bit = 0;; ++bit) {
  90    x >>= 1;
  91    if (x <= 0)
  92      return bit;
  93  }
  94}
  95
  96/* string and path helpers */
  97
  98void *
  99read_or_die(FILE *fp, void *buf, long offset, size_t size, const char *msg)
 100{
 101  if (buf == NULL)
 102    buf = malloc_or_die(size);
 103  if ((offset >= 0 && fseek(fp, offset, SEEK_SET) != 0) || fread(buf, 1, size, fp) != size)
 104    error(msg);
 105  return buf;
 106}
 107
 108void *
 109malloc_or_die(size_t size)
 110{
 111  void *p = malloc(size);
 112  if (p == NULL) {
 113    fprintf(stderr, "memory overflow\n");
 114    exit(1);
 115  }
 116  return p;
 117}
 118
 119void *
 120calloc_or_die(size_t size)
 121{
 122  void *p = calloc(1, size);
 123  if (p == NULL) {
 124    fprintf(stderr, "memory overflow\n");
 125    exit(1);
 126  }
 127  return p;
 128}
 129
 130void *
 131realloc_or_die(void *ptr, size_t size)
 132{
 133  void *p = realloc(ptr, size);
 134  if (p == NULL) {
 135    fprintf(stderr, "memory overflow\n");
 136    exit(1);
 137  }
 138  return p;
 139}
 140
 141const struct Name *
 142alloc_label(void)
 143{
 144  static int label_no;
 145  char       buf[2 + sizeof(int) * 3 + 1];
 146
 147  ++label_no;
 148  snprintf(buf, sizeof(buf), "l.%04d", label_no);
 149  return alloc_name(buf, NULL, 1);
 150}
 151
 152ssize_t
 153getline_chomp(char **lineptr, size_t *n, FILE *stream)
 154{
 155  ssize_t len;
 156  char   *line;
 157
 158  len = getline(lineptr, n, stream);
 159  if (len > 0) {
 160    line = *lineptr;
 161    /* chomp CR, LF, CR+LF */
 162    if (line[len - 1] == '\n')
 163      line[--len] = '\0';
 164    if (len > 0 && line[len - 1] == '\r')
 165      line[--len] = '\0';
 166  }
 167  return len;
 168}
 169
 170static ssize_t
 171getline_cat(char **lineptr, size_t *n, FILE *stream, size_t curlen)
 172{
 173  char   *nextline = NULL;
 174  size_t  capa     = 0;
 175  ssize_t len;
 176  char   *oldline;
 177  char   *reallocated;
 178
 179  len = getline_chomp(&nextline, &capa, stream);
 180  if (len == -1)
 181    return -1;
 182  if (len > 0) {
 183    oldline     = *lineptr;
 184    reallocated = realloc(oldline, curlen + len + 1);
 185    if (reallocated == NULL)
 186      return -1;
 187
 188    memcpy(reallocated + curlen, nextline, len + 1);
 189    *lineptr = reallocated;
 190    *n       = curlen + len; /* nul not included */
 191    free(nextline);
 192  }
 193  return curlen + len;
 194}
 195
 196ssize_t
 197getline_cont(char **lineptr, size_t *capa, FILE *stream, int *plineno)
 198{
 199  int     lineno = *plineno;
 200  ssize_t len;
 201  ssize_t nextlen;
 202
 203  len = getline_chomp(lineptr, capa, stream);
 204  if (len != -1) {
 205    /* handle backslash continuation */
 206    while (++lineno, len > 0 && (*lineptr)[len - 1] == '\\') {
 207      (*lineptr)[--len] = '\0';
 208      nextlen           = getline_cat(lineptr, capa, stream, len);
 209      if (nextlen == -1)
 210        break;
 211      len = nextlen;
 212    }
 213  }
 214  *plineno = lineno;
 215  return len;
 216}
 217
 218int
 219is_fullpath(const char *filename)
 220{
 221  const char *p;
 222
 223  if (*filename != '/')
 224    return 0;
 225  for (p = filename;;) {
 226    p = strstr(p, "/..");
 227    if (p == NULL)
 228      return 1;
 229    if (p[3] == '/' || p[3] == '\0')
 230      return 0;
 231    p += 3;
 232  }
 233}
 234
 235char *
 236join_paths(const char *paths[])
 237{
 238  struct StringBuffer sb;
 239  const char        **pp;
 240  const char         *p;
 241  const char         *last_path;
 242  int                 parent_count;
 243  enum Top {
 244    OTHER,
 245    ROOTDIR, /* / */
 246    CURDIR,  /*  */
 247  };
 248  enum Top top;
 249
 250  sb_init(&sb);
 251  parent_count = 0;
 252  top          = OTHER;
 253  last_path    = NULL;
 254
 255  for (pp = paths; (p = *pp++) != NULL;) {
 256    const char *q;
 257    int         end;
 258    ptrdiff_t   len;
 259
 260    last_path = p;
 261    if (*p == '/') { /* absolute, reset */
 262      sb_init(&sb);
 263      parent_count = 0;
 264      top          = ROOTDIR;
 265    }
 266
 267    for (end = 0; !end;) {
 268      while (*p == '/')
 269        ++p;
 270      if (*p == '\0')
 271        break;
 272
 273      q = strchr(p, '/');
 274      if (q == NULL) { /* last component */
 275        q   = p + strlen(p);
 276        end = 1;
 277      }
 278      len = q - p;
 279      if (len == 1 && *p == '.') {
 280        if (sb.elems->len == 0 && top == OTHER)
 281          top = CURDIR;
 282      } else if (len == 2 && strncmp(p, "..", 2) == 0) {
 283        if (sb.elems->len > 0) {
 284          void *elem = vec_pop(sb.elems);
 285          free(elem);
 286        } else {
 287          if (top == ROOTDIR)
 288            return NULL; /* illegal */
 289          ++parent_count;
 290          top = OTHER;
 291        }
 292      } else {
 293        sb_append(&sb, p, q);
 294      }
 295      p = q;
 296    }
 297  }
 298
 299  for (; parent_count > 0; --parent_count)
 300    sb_prepend(&sb, "..", NULL);
 301  switch (top) {
 302    case OTHER:
 303    case CURDIR:
 304      if (sb.elems->len == 0)
 305        sb_prepend(&sb, ".", NULL);
 306      break;
 307    case ROOTDIR:
 308      sb_prepend(&sb, sb.elems->len > 0 ? "" : "/", NULL);
 309      break;
 310  }
 311  if (last_path != NULL) {
 312    size_t len = strlen(last_path);
 313    if (len > 0 && last_path[len - 1] == '/' && !(top == ROOTDIR && sb.elems->len == 1))
 314      sb_append(&sb, "", NULL);
 315  }
 316  return sb_join(&sb, "/");
 317}
 318
 319char *
 320get_ext(const char *filename)
 321{
 322  const char *last_slash;
 323  char       *dot;
 324
 325  last_slash = strrchr(filename, '/');
 326  if (last_slash == NULL)
 327    last_slash = filename;
 328  dot = strrchr(last_slash, '.');
 329  return dot != NULL ? (char *)&dot[1] : (char *)&last_slash[strlen(last_slash)];
 330}
 331
 332char *
 333change_ext(const char *path, const char *ext)
 334{
 335  const char *p;
 336  const char *q;
 337  size_t      len;
 338  size_t      ext_len;
 339  char       *s;
 340
 341  p = strrchr(path, '/');
 342  if (p == NULL)
 343    p = path;
 344
 345  q       = strrchr(p, '.');
 346  len     = q != NULL ? (size_t)(q - path) : strlen(path);
 347  ext_len = strlen(ext);
 348  s       = malloc(len + 1 + ext_len + 1);
 349  if (s != NULL) {
 350    memcpy(s, path, len);
 351    s[len] = '.';
 352    strcpy(s + (len + 1), ext);
 353  }
 354  return s;
 355}
 356
 357void
 358put_padding(FILE *fp, long start)
 359{
 360  long cur;
 361  long size;
 362  long i;
 363
 364  cur = ftell(fp);
 365  if (cur < 0)
 366    return; /* todo: error */
 367  if (start > cur) {
 368    size = start - cur;
 369    for (i = 0; i < size; ++i)
 370      fputc(0x00, fp);
 371  }
 372}
 373
 374int
 375is_file(const char *path)
 376{
 377  struct stat st;
 378  return stat(path, &st) == 0 && S_ISREG(st.st_mode); /* follows symlinks */
 379}
 380
 381void
 382show_version(const char *exe, int arch)
 383{
 384  /* order must match TC_ARCH_* constants in config.h */
 385  static const char *const archs[] = {
 386      NULL,
 387      "x86_64",
 388      "arm64",
 389      "riscv64",
 390      "wasm",
 391  };
 392
 393  if (exe == NULL) {
 394    printf("%s\n", VERSION);
 395  } else if (arch <= 0) {
 396    printf("%s version %s\n", exe, VERSION);
 397  } else {
 398    printf(
 399        "%s version %s\n"
 400        "Target: %s\n",
 401        exe,
 402        VERSION,
 403        archs[arch]
 404    );
 405  }
 406}
 407
 408_Noreturn void
 409error(const char *fmt, ...)
 410{
 411  va_list ap;
 412  va_start(ap, fmt);
 413  vfprintf(stderr, fmt, ap);
 414  va_end(ap);
 415  fprintf(stderr, "\n");
 416  exit(1);
 417}
 418
 419void
 420show_error_line(const char *line, const char *p, int len)
 421{
 422  size_t pos;
 423  size_t i;
 424
 425  fprintf(stderr, "%s\n", line);
 426  pos = p - line;
 427  if (pos <= strlen(line)) {
 428    for (i = 0; i < pos; ++i)
 429      fputc(line[i] == '\t' ? '\t' : ' ', stderr);
 430    fprintf(stderr, "^");
 431    for (i = 1; i < (size_t)len; ++i)
 432      fprintf(stderr, "~");
 433    fprintf(stderr, "\n");
 434  }
 435}
 436
 437/* value range checks for immediate operands */
 438
 439int
 440is_im8(int64_t x)
 441{
 442  return x <= (((int64_t)1 << 7) - 1) && x >= -((int64_t)1 << 7);
 443}
 444
 445int
 446is_im16(int64_t x)
 447{
 448  return x <= (((int64_t)1 << 15) - 1) && x >= -((int64_t)1 << 15);
 449}
 450
 451int
 452is_im32(int64_t x)
 453{
 454  return x <= (((int64_t)1 << 31) - 1) && x >= -((int64_t)1 << 31);
 455}
 456
 457const char *
 458skip_whitespaces(const char *s)
 459{
 460  for (;;) {
 461    while (isspace(*s))
 462      ++s;
 463    /* gas allows block comments and line comments wherever whitespace is valid */
 464    if (s[0] == '/' && s[1] == '*') {
 465      s += 2;
 466      while (*s != '\0' && !(s[0] == '*' && s[1] == '/'))
 467        ++s;
 468      if (*s != '\0')
 469        s += 2;
 470      continue;
 471    }
 472    if (s[0] == '/' && s[1] == '/') {
 473      while (*s != '\0')
 474        ++s;
 475      continue;
 476    }
 477    break;
 478  }
 479  return s;
 480}
 481
 482/* detect block comment start without consuming it via skip_whitespaces */
 483const char *
 484block_comment_start(const char *p)
 485{
 486  while (isspace(*p))
 487    ++p;
 488  return (*p == '/' && p[1] == '*') ? p : NULL;
 489}
 490
 491const char *
 492block_comment_end(const char *p)
 493{
 494  for (;;) {
 495    p = strchr(p, '*');
 496    if (p == NULL)
 497      return NULL;
 498    if (*(++p) == '/')
 499      return p + 1;
 500  }
 501}
 502
 503int64_t
 504wrap_value(int64_t value, int size, int is_unsigned)
 505{
 506  if (is_unsigned) {
 507    switch (size) {
 508      case 1:
 509        value = (uint8_t)value;
 510        break;
 511      case 2:
 512        value = (uint16_t)value;
 513        break;
 514      case 4:
 515        value = (uint32_t)value;
 516        break;
 517      default:
 518        break;
 519    }
 520  } else {
 521    switch (size) {
 522      case 1:
 523        value = (int8_t)value;
 524        break;
 525      case 2:
 526        value = (int16_t)value;
 527        break;
 528      case 4:
 529        value = (int32_t)value;
 530        break;
 531      default:
 532        break;
 533    }
 534  }
 535  return value;
 536}
 537
 538/* container: growable pointer vector */
 539
 540struct Vector *
 541new_vector(void)
 542{
 543  struct Vector *vec = malloc_or_die(sizeof(struct Vector));
 544  vec_init(vec);
 545  return vec;
 546}
 547
 548void
 549free_vector(struct Vector *vec)
 550{
 551  free(vec->data);
 552  free(vec);
 553}
 554
 555void
 556vec_init(struct Vector *vec)
 557{
 558  vec->data     = NULL;
 559  vec->capacity = 0;
 560  vec->len      = 0;
 561}
 562
 563void
 564vec_clear(struct Vector *vec)
 565{
 566  vec->len = 0;
 567}
 568
 569void
 570vec_push(struct Vector *vec, const void *elem)
 571{
 572  if (vec->capacity <= vec->len) {
 573    if (vec->capacity <= 0)
 574      vec->capacity = 16;
 575    else
 576      vec->capacity <<= 1;
 577    vec->data = realloc_or_die(vec->data, sizeof(*vec->data) * vec->capacity);
 578  }
 579  vec->data[vec->len++] = (void *)elem;
 580}
 581
 582void *
 583vec_pop(struct Vector *vec)
 584{
 585  return vec->len > 0 ? vec->data[--vec->len] : NULL;
 586}
 587
 588void
 589vec_insert(struct Vector *vec, int pos, const void *elem)
 590{
 591  int len = vec->len;
 592
 593  if (pos < 0 || pos > len)
 594    return;
 595
 596  if (pos < len) {
 597    vec_push(vec, NULL);
 598    memmove(&vec->data[pos + 1], &vec->data[pos], sizeof(void *) * (len - pos));
 599    vec->data[pos] = (void *)elem;
 600  } else {
 601    vec_push(vec, elem);
 602  }
 603}
 604
 605void
 606vec_remove_at(struct Vector *vec, int index)
 607{
 608  int d;
 609
 610  if (index < 0 || index >= vec->len)
 611    return;
 612  d = vec->len - index - 1;
 613  if (d > 0)
 614    memmove(&vec->data[index], &vec->data[index + 1], d * sizeof(*vec->data));
 615  --vec->len;
 616}
 617
 618int
 619vec_contains(struct Vector *vec, void *elem)
 620{
 621  int i, len;
 622
 623  len = vec->len;
 624  for (i = 0; i < len; ++i) {
 625    if (vec->data[i] == elem)
 626      return 1;
 627  }
 628  return 0;
 629}
 630
 631void
 632vec_concat(struct Vector *dst, const struct Vector *src)
 633{
 634  int i;
 635
 636  for (i = 0; i < src->len; ++i)
 637    vec_push(dst, src->data[i]);
 638}
 639
 640/* container: growable byte buffer */
 641
 642void
 643data_release(struct DataStorage *data)
 644{
 645  if (data->chunk_stack != NULL) {
 646    free_vector(data->chunk_stack);
 647    data->chunk_stack = NULL;
 648  }
 649  if (data->buf != NULL) {
 650    free(data->buf);
 651    data_init(data);
 652  }
 653}
 654
 655void
 656data_init(struct DataStorage *data)
 657{
 658  data->chunk_stack = NULL;
 659  data->buf         = NULL;
 660  data->capacity    = 0;
 661  data->len         = 0;
 662}
 663
 664void
 665data_reserve(struct DataStorage *data, size_t capacity)
 666{
 667  const size_t min_cap = 16;
 668  size_t       c;
 669
 670  if (data->capacity < capacity) {
 671    c = data->capacity << 1;
 672    if (c > capacity)
 673      capacity = c;
 674    if (min_cap > capacity)
 675      capacity = min_cap;
 676    data->buf      = realloc_or_die(data->buf, sizeof(*data->buf) * capacity);
 677    data->capacity = capacity;
 678  }
 679}
 680
 681void
 682data_insert(struct DataStorage *data, ssize_t pos_, const void *buf, size_t size)
 683{
 684  size_t pos;
 685  size_t newlen;
 686
 687  pos = pos_ == -1 ? data->len : (size_t)pos_;
 688  assert(pos <= data->len);
 689  newlen = data->len + size;
 690  data_reserve(data, newlen);
 691  if (pos < data->len)
 692    memmove(data->buf + pos + size, data->buf + pos, data->len - pos);
 693  memcpy(data->buf + pos, buf, size);
 694  data->len = newlen;
 695}
 696
 697void
 698data_append(struct DataStorage *data, const void *buf, size_t size)
 699{
 700  data_insert(data, -1, buf, size);
 701}
 702
 703void
 704data_push(struct DataStorage *data, unsigned char c)
 705{
 706  unsigned char buf[1] = {c};
 707  data_insert(data, -1, buf, 1);
 708}
 709
 710void
 711data_align(struct DataStorage *data, int align)
 712{
 713  size_t len;
 714  size_t aligned_len;
 715  size_t add;
 716  void  *zero;
 717
 718  len         = data->len;
 719  aligned_len = ALIGN(len, align);
 720  add         = aligned_len - len;
 721  if (add <= 0)
 722    return;
 723
 724  zero = calloc_or_die(add);
 725  data_append(data, zero, add);
 726  free(zero);
 727
 728  assert(data->len == aligned_len);
 729}
 730
 731void
 732data_concat(struct DataStorage *dst, struct DataStorage *src)
 733{
 734  data_insert(dst, -1, src->buf, src->len);
 735}
 736
 737void
 738data_leb128(struct DataStorage *data, ssize_t pos, int64_t val)
 739{
 740  unsigned char buf[12], *p = buf;
 741  const int64_t max = (int64_t)1 << 6;
 742
 743  for (;;) {
 744    if (val < max && val >= -max) {
 745      *p++ = val & 0x7f;
 746      data_insert(data, pos, buf, p - buf);
 747      return;
 748    }
 749    *p++ = (val & 0x7f) | 0x80;
 750    val >>= 7;
 751  }
 752}
 753
 754void
 755data_uleb128(struct DataStorage *data, ssize_t pos, uint64_t val)
 756{
 757  unsigned char  buf[12], *p = buf;
 758  const uint64_t max = (uint64_t)1 << 7;
 759
 760  for (;;) {
 761    if (val < max) {
 762      *p++ = val & 0x7f;
 763      data_insert(data, pos, buf, p - buf);
 764      return;
 765    }
 766    *p++ = (val & 0x7f) | 0x80;
 767    val >>= 7;
 768  }
 769}
 770
 771void
 772data_string(struct DataStorage *data, const void *str, size_t len)
 773{
 774  data_uleb128(data, -1, len);
 775  data_append(data, (const unsigned char *)str, len);
 776}
 777
 778void
 779data_varint32(struct DataStorage *data, ssize_t pos, int64_t val)
 780{
 781  unsigned char buf[5], *p = buf;
 782  int           i;
 783
 784  for (i = 0; i < 4; ++i) {
 785    *p++ = (val & 0x7f) | 0x80;
 786    val >>= 7;
 787  }
 788  *p++ = val & 0x7f;
 789  data_insert(data, pos, buf, p - buf);
 790}
 791
 792void
 793data_varuint32(struct DataStorage *data, ssize_t pos, uint64_t val)
 794{
 795  unsigned char buf[5], *p = buf;
 796  int           i;
 797
 798  for (i = 0; i < 4; ++i) {
 799    *p++ = (val & 0x7f) | 0x80;
 800    val >>= 7;
 801  }
 802  *p++ = val & 0x7f;
 803  data_insert(data, pos, buf, p - buf);
 804}
 805
 806void
 807data_open_chunk(struct DataStorage *data)
 808{
 809  struct Vector *stack = data->chunk_stack;
 810  if (stack == NULL)
 811    data->chunk_stack = stack = new_vector();
 812  vec_push(stack, INT2VOIDP(data->len));
 813}
 814
 815void
 816data_close_chunk(struct DataStorage *data, ssize_t num)
 817{
 818  struct Vector *stack = data->chunk_stack;
 819  size_t         pos;
 820
 821  assert(stack != NULL && stack->len > 0);
 822  pos = VOIDP2INT(vec_pop(stack));
 823  if (num == (ssize_t)-1)
 824    num = data->len - pos;
 825  data_uleb128(data, pos, num);
 826}
 827
 828/* container: rope-style string builder */
 829
 830struct StringElement {
 831  const char *start;
 832  size_t      len;
 833};
 834
 835void
 836sb_init(struct StringBuffer *sb)
 837{
 838  sb->elems = new_vector();
 839}
 840
 841void
 842sb_clear(struct StringBuffer *sb)
 843{
 844  vec_clear(sb->elems);
 845}
 846
 847int
 848sb_empty(struct StringBuffer *sb)
 849{
 850  return sb->elems->len == 0;
 851}
 852
 853void
 854sb_insert(struct StringBuffer *sb, int pos, const char *start, const char *end)
 855{
 856  struct StringElement *elem = malloc(sizeof(*elem));
 857
 858  if (elem != NULL) {
 859    elem->start = start;
 860    elem->len   = end != NULL ? (size_t)(end - start) : strlen(start);
 861    assert(0 <= pos && pos <= sb->elems->len);
 862    vec_insert(sb->elems, pos, elem);
 863  }
 864}
 865
 866char *
 867sb_join(struct StringBuffer *sb, const char *separator)
 868{
 869  size_t total_len;
 870  int    count;
 871  int    i;
 872  size_t sepalen;
 873  char  *str;
 874  char  *p;
 875
 876  total_len = 0;
 877  count     = sb->elems->len;
 878  for (i = 0; i < count; ++i) {
 879    struct StringElement *elem = sb->elems->data[i];
 880    total_len += elem->len;
 881  }
 882  sepalen = separator != NULL ? strlen(separator) : 0;
 883  if (count > 0 && sepalen > 0)
 884    total_len += sepalen * (count - 1);
 885
 886  str = malloc(total_len + 1);
 887  if (str != NULL) {
 888    p = str;
 889    for (i = 0; i < count; ++i) {
 890      struct StringElement *elem;
 891      if (i > 0 && sepalen > 0) {
 892        memcpy(p, separator, sepalen);
 893        p += sepalen;
 894      }
 895      elem = sb->elems->data[i];
 896      memcpy(p, elem->start, elem->len);
 897      p += elem->len;
 898    }
 899    *p = '\0';
 900  }
 901  return str;
 902}
 903
 904/* string escaping for assembler output */
 905
 906static const char *
 907escape_hex(int c)
 908{
 909  char *s = malloc_or_die(5);
 910  /* hex escapes are not length-limited so following hex chars would
 911 * be consumed, must use octal which is always 3 digits */
 912  snprintf(s, 5, "\\%03o", c & 0xff);
 913  return s;
 914}
 915
 916static const char *
 917escape(int c)
 918{
 919  switch (c) {
 920    case '\0':
 921      return "\\000"; /* octal, at most 3 digits in assembler */
 922    case '\n':
 923      return "\\n";
 924    case '\r':
 925      return "\\r";
 926    case '\t':
 927      return "\\t";
 928    case '"':
 929      return "\\\"";
 930    case '\\':
 931      return "\\\\";
 932    default:
 933      if (c < 0x20 || c >= 0x7f)
 934        return escape_hex(c);
 935      return NULL;
 936  }
 937}
 938
 939void
 940escape_string(const char *str, size_t size, struct StringBuffer *sb)
 941{
 942  const char *s, *p;
 943  const char *end;
 944  const char *e;
 945  int         hex;
 946
 947  end = str + size;
 948  hex = 0;
 949  for (s = p = str; p < end; ++p) {
 950    if (hex && isxdigit(*p)) {
 951      e = escape_hex(*p);
 952    } else {
 953      e = escape(*p);
 954      if (e == NULL) {
 955        hex = 0;
 956        continue;
 957      }
 958    }
 959
 960    if (p > s)
 961      sb_append(sb, s, p);
 962    sb_append(sb, e, NULL);
 963    s   = p + 1;
 964    hex = e[1] == 'x';
 965  }
 966  if (p > s) {
 967    assert(!hex);
 968    sb_append(sb, s, p);
 969  }
 970}
 971
 972/* option parser: long-option style for toolchain commands */
 973
 974int   optind;
 975int   optopt;
 976int   opterr = 1;
 977char *optarg;
 978
 979int
 980optparse(int argc, char *const argv[], const struct option *opts)
 981{
 982#define ERROR(...)                                                                                 \
 983  do {                                                                                             \
 984    if (opterr)                                                                                    \
 985      fprintf(stderr, __VA_ARGS__);                                                                \
 986  } while (0)
 987
 988  char  *arg;
 989  char  *p;
 990  char  *q;
 991  char   c;
 992  int    opt;
 993  size_t len;
 994
 995  if (optind == 0)
 996    optind = 1;
 997
 998  if (optind >= argc)
 999    return -1;
1000
1001  optarg = NULL;
1002  optopt = 0;
1003
1004  arg = argv[optind];
1005  p   = arg;
1006  if (*p != '-' || p[1] == '\0')
1007    return -1;
1008
1009  p += 1;
1010  ++optind;
1011  for (; opts->name != NULL; ++opts) {
1012    len = strlen(opts->name);
1013    if (strncmp(p, opts->name, len) == 0) {
1014      opt = opts->val;
1015      if (opt == 0)
1016        opt = arg[1];
1017      q = p + len;
1018      c = *q;
1019      if (opts->has_arg) {
1020        if (c != '\0') {
1021          optarg = q + (c == '=' ? 1 : 0);
1022        } else if (opts->has_arg == required_argument) {
1023          if (optind < argc) {
1024            optarg = argv[optind++];
1025          } else {
1026            ERROR(
1027                "%s: option '--%s' "
1028                "requires an argument\n",
1029                argv[0],
1030                opts->name
1031            );
1032            break;
1033          }
1034        }
1035      } else {
1036        if (c != '\0') {
1037          if (c != '=')
1038            continue;
1039          ERROR(
1040              "%s: option '--%s' doesn't allow "
1041              "an argument\n",
1042              argv[0],
1043              opts->name
1044          );
1045          break;
1046        }
1047      }
1048      return opt;
1049    }
1050  }
1051
1052  optopt = arg[1];
1053  return '?';
1054#undef ERROR
1055}