text: /*
   * Copyright (C) 2010 The Android Open Source Project
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *  * Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *  * Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   */
  
  #include <stdarg.h>
  #include <string.h>
  #include <errno.h>
  #include <unistd.h>
  #include <stdint.h>
  #include <stddef.h>
  #include "linker_format.h"
  #include "linker_debug.h"
  
  /* define UNIT_TESTS to build this file as a single executable that runs
   * the formatter's unit tests
   */
  #define xxUNIT_TESTS
  
  /*** Generic output sink
   ***/
  
  typedef struct {
      void *opaque;
      void (*send)(void *opaque, const char *data, int len);
  } Out;
  
  static void
  out_send(Out *o, const void *data, size_t len)
  {
      o->send(o->opaque, data, (int)len);
  }
  
  static void
  out_send_repeat(Out *o, char ch, int count)
  {
      char pad[8];
      const int padSize = (int)sizeof(pad);
  
      memset(pad, ch, sizeof(pad));
      while (count > 0) {
          int avail = count;
          if (avail > padSize) {
              avail = padSize;
          }
          o->send(o->opaque, pad, avail);
          count -= avail;
      }
  }
  
  /* forward declaration */
  static void
  out_vformat(Out *o, const char *format, va_list args);
  
  /*** Bounded buffer output
   ***/
  
  typedef struct {
      Out out[1];
      char *buffer;
      char *pos;
      char *end;
      int total;
  } BufOut;
  
  static void
  buf_out_send(void *opaque, const char *data, int len)
  {
      BufOut *bo = opaque;
  
      if (len < 0)
          len = strlen(data);
  
      bo->total += len;
  
      while (len > 0) {
          int avail = bo->end - bo->pos;
          if (avail == 0)
              break;
          if (avail > len)
              avail = len;
          memcpy(bo->pos, data, avail);
          bo->pos += avail;
          bo->pos[0] = '\0';
          len -= avail;
      }
  }
  
  static Out*
  buf_out_init(BufOut *bo, char *buffer, size_t size)
  {
      if (size == 0)
          return NULL;
  
      bo->out->opaque = bo;
      bo->out->send   = buf_out_send;
      bo->buffer      = buffer;
      bo->end         = buffer + size - 1;
      bo->pos         = bo->buffer;
      bo->pos[0]      = '\0';
      bo->total       = 0;
  
      return bo->out;
  }
  
  static int
  buf_out_length(BufOut *bo)
  {
      return bo->total;
  }
  
  static int
  vformat_buffer(char *buff, size_t buffsize, const char *format, va_list args)
  {
      BufOut bo;
      Out *out;
  
      out = buf_out_init(&bo, buff, buffsize);
      if (out == NULL)
          return 0;
  
      out_vformat(out, format, args);
  
      return buf_out_length(&bo);
  }
  
  int
  format_buffer(char *buff, size_t buffsize, const char *format, ...)
  {
      va_list args;
      int ret;
  
      va_start(args, format);
      ret = vformat_buffer(buff, buffsize, format, args);
      va_end(args);
  
      return ret;
  }
  
  /* The __stack_chk_fail() function calls __libc_android_log_print()
   * which calls vsnprintf().
   *
   * We define our version of the function here to avoid dragging
   * about 25 KB of C library routines related to formatting.
   */
  int
  vsnprintf(char *buff, size_t bufsize, const char *format, va_list args)
  {
      return format_buffer(buff, bufsize, format, args);
  }
  
  #if LINKER_DEBUG
  
  #if !LINKER_DEBUG_TO_LOG
  
  /*** File descriptor output
   ***/
  
  typedef struct {
      Out out[1];
      int fd;
      int total;
  } FdOut;
  
  static void
  fd_out_send(void *opaque, const char *data, int len)
  {
      FdOut *fdo = opaque;
  
      if (len < 0)
          len = strlen(data);
  
      while (len > 0) {
          int ret = write(fdo->fd, data, len);
          if (ret < 0) {
              if (errno == EINTR)
                  continue;
              break;
          }
          data += ret;
          len -= ret;
          fdo->total += ret;
      }
  }
  
  static Out*
  fd_out_init(FdOut *fdo, int  fd)
  {
      fdo->out->opaque = fdo;
      fdo->out->send = fd_out_send;
      fdo->fd = fd;
      fdo->total = 0;
  
      return fdo->out;
  }
  
  static int
  fd_out_length(FdOut *fdo)
  {
      return fdo->total;
  }
  
  
  int
  format_fd(int fd, const char *format, ...)
  {
      FdOut fdo;
      Out* out;
      va_list args;
  
      out = fd_out_init(&fdo, fd);
      if (out == NULL)
          return 0;
  
      va_start(args, format);
      out_vformat(out, format, args);
      va_end(args);
  
      return fd_out_length(&fdo);
  }
  
  #else /* LINKER_DEBUG_TO_LOG */
  
  /*** Log output
   ***/
  
  /* We need our own version of __libc_android_log_vprint, otherwise
   * the log output is completely broken. Probably due to the fact
   * that the C library is not initialized yet.
   *
   * You can test that by setting CUSTOM_LOG_VPRINT to 0
   */
  #define  CUSTOM_LOG_VPRINT  1
  
  #if CUSTOM_LOG_VPRINT
  
  #include <unistd.h>
  #include <fcntl.h>
  #include <sys/uio.h>
  
  static int log_vprint(int prio, const char *tag, const char *fmt, va_list  args)
  {
      char buf[1024];
      int result;
      static int log_fd = -1;
  
      result = vformat_buffer(buf, sizeof buf, fmt, args);
  
      if (log_fd < 0) {
          log_fd = open("/dev/log/main", O_WRONLY);
          if (log_fd < 0)
              return result;
      }
  
      {
          ssize_t ret;
          struct iovec vec[3];
  
          vec[0].iov_base = (unsigned char *) &prio;
          vec[0].iov_len = 1;
          vec[1].iov_base = (void *) tag;
          vec[1].iov_len = strlen(tag) + 1;
          vec[2].iov_base = (void *) buf;
          vec[2].iov_len = strlen(buf) + 1;
  
          do {
              ret = writev(log_fd, vec, 3);
          } while ((ret < 0) && (errno == EINTR));
      }
      return result;
  }
  
  #define  __libc_android_log_vprint  log_vprint
  
  #else /* !CUSTOM_LOG_VPRINT */
  
  extern int __libc_android_log_vprint(int  prio, const char* tag, const char*  format, va_list ap);
  
  #endif /* !CUSTOM_LOG_VPRINT */
  
  int
  format_log(int prio, const char *tag, const char *format, ...)
  {
      int ret;
      va_list  args;
      va_start(args, format);
      ret = __libc_android_log_vprint(prio, tag, format, args);
      va_end(args);
      return ret;
  }
  
  #endif /* LINKER_DEBUG_TO_LOG */
  
  #endif /* LINKER_DEBUG */
  
  /*** formatted output implementation
   ***/
  
  /* Parse a decimal string from 'format + *ppos',
   * return the value, and writes the new position past
   * the decimal string in '*ppos' on exit.
   *
   * NOTE: Does *not* handle a sign prefix.
   */
  static unsigned
  parse_decimal(const char *format, int *ppos)
  {
      const char* p = format + *ppos;
      unsigned result = 0;
  
      for (;;) {
          int ch = *p;
          unsigned d = (unsigned)(ch - '0');
  
          if (d >= 10U)
              break;
  
          result = result*10 + d;
          p++;
      }
      *ppos = p - format;
      return result;
  }
  
  /* write an octal/decimal/number into a bounded buffer.
   * assumes that bufsize > 0, and 'digits' is a string of
   * digits of at least 'base' values.
   */
  static void
  format_number(char *buffer, size_t bufsize, uint64_t value, int base, const char *digits)
  {
      char *pos = buffer;
      char *end = buffer + bufsize - 1;
  
      /* generate digit string in reverse order */
      while (value) {
          unsigned d = value % base;
          value /= base;
          if (pos < end) {
              *pos++ = digits[d];
          }
      }
  
      /* special case for 0 */
      if (pos == buffer) {
          if (pos < end) {
              *pos++ = '0';
          }
      }
      pos[0] = '\0';
  
      /* now reverse digit string in-place */
      end = pos - 1;
      pos = buffer;
      while (pos < end) {
          int ch = pos[0];
          pos[0] = end[0];
          end[0] = (char) ch;
          pos++;
          end--;
      }
  }
  
  /* Write an integer (octal or decimal) into a buffer, assumes buffsize > 2 */
  static void
  format_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSigned)
  {
      if (isSigned && (int64_t)value < 0) {
          buffer[0] = '-';
          buffer += 1;
          buffsize -= 1;
          value = (uint64_t)(-(int64_t)value);
      }
  
      format_number(buffer, buffsize, value, base, "0123456789");
  }
  
  /* Write an octal into a buffer, assumes buffsize > 2 */
  static void
  format_octal(char *buffer, size_t buffsize, uint64_t value, int isSigned)
  {
      format_integer(buffer, buffsize, value, 8, isSigned);
  }
  
  /* Write a decimal into a buffer, assumes buffsize > 2 */
  static void
  format_decimal(char *buffer, size_t buffsize, uint64_t value, int isSigned)
  {
      format_integer(buffer, buffsize, value, 10, isSigned);
  }
  
  /* Write an hexadecimal into a buffer, isCap is true for capital alphas.
   * Assumes bufsize > 2 */
  static void
  format_hex(char *buffer, size_t buffsize, uint64_t value, int isCap)
  {
      const char *digits = isCap ? "0123456789ABCDEF" : "0123456789abcdef";
  
      format_number(buffer, buffsize, value, 16, digits);
  }
  
  
  /* Perform formatted output to an output target 'o' */
  static void
  out_vformat(Out *o, const char *format, va_list args)
  {
      int nn = 0, mm;
      int padZero = 0;
      int padLeft = 0;
      char sign = '\0';
      int width = -1;
      int prec  = -1;
      size_t bytelen = sizeof(int);
      const char*  str;
      int slen;
      char buffer[32];  /* temporary buffer used to format numbers */
  
      for (;;) {
          char  c;
  
          /* first, find all characters that are not 0 or '%' */
          /* then send them to the output directly */
          mm = nn;
          do {
              c = format[mm];
              if (c == '\0' || c == '%')
                  break;
              mm++;
          } while (1);
  
          if (mm > nn) {
              out_send(o, format+nn, mm-nn);
              nn = mm;
          }
  
          /* is this it ? then exit */
          if (c == '\0')
              break;
  
          /* nope, we are at a '%' modifier */
          nn++;  // skip it
  
          /* parse flags */
          for (;;) {
              c = format[nn++];
              if (c == '\0') {  /* single trailing '%' ? */
                  c = '%';
                  out_send(o, &c, 1);
                  return;
              }
              else if (c == '0') {
                  padZero = 1;
                  continue;
              }
              else if (c == '-') {
                  padLeft = 1;
                  continue;
              }
              else if (c == ' ' || c == '+') {
                  sign = c;
                  continue;
              }
              break;
          }
  
          /* parse field width */
          if ((c >= '0' && c <= '9')) {
              nn --;
              width = (int)parse_decimal(format, &nn);
              c = format[nn++];
          }
  
          /* parse precision */
          if (c == '.') {
              prec = (int)parse_decimal(format, &nn);
              c = format[nn++];
          }
  
          /* length modifier */
          switch (c) {
          case 'h':
              bytelen = sizeof(short);
              if (format[nn] == 'h') {
                  bytelen = sizeof(char);
                  nn += 1;
              }
              c = format[nn++];
              break;
          case 'l':
              bytelen = sizeof(long);
              if (format[nn] == 'l') {
                  bytelen = sizeof(long long);
                  nn += 1;
              }
              c = format[nn++];
              break;
          case 'z':
              bytelen = sizeof(size_t);
              c = format[nn++];
              break;
          case 't':
              bytelen = sizeof(ptrdiff_t);
              c = format[nn++];
              break;
          case 'p':
              bytelen = sizeof(void*);
              c = format[nn++];
          default:
              ;
          }
  
          /* conversion specifier */
          if (c == 's') {
              /* string */
              str = va_arg(args, const char*);
          } else if (c == 'c') {
              /* character */
              /* NOTE: char is promoted to int when passed through the stack */
              buffer[0] = (char) va_arg(args, int);
              buffer[1] = '\0';
              str = buffer;
          } else if (c == 'p') {
              uint64_t  value = (uint64_t)(ptrdiff_t) va_arg(args, void*);
              buffer[0] = '0';
              buffer[1] = 'x';
              format_hex(buffer + 2, sizeof buffer-2, value, 0);
              str = buffer;
          } else {
              /* integers - first read value from stack */
              uint64_t value;
              int isSigned = (c == 'd' || c == 'i' || c == 'o');
  
              /* NOTE: int8_t and int16_t are promoted to int when passed
               *       through the stack
               */
              switch (bytelen) {
              case 1: value = (uint8_t)  va_arg(args, int); break;
              case 2: value = (uint16_t) va_arg(args, int); break;
              case 4: value = va_arg(args, uint32_t); break;
              case 8: value = va_arg(args, uint64_t); break;
              default: return;  /* should not happen */
              }
  
              /* sign extension, if needed */
              if (isSigned) {
                  int shift = 64 - 8*bytelen;
                  value = (uint64_t)(((int64_t)(value << shift)) >> shift);
              }
  
              /* format the number properly into our buffer */
              switch (c) {
              case 'i': case 'd':
                  format_integer(buffer, sizeof buffer, value, 10, isSigned);
                  break;
              case 'o':
                  format_integer(buffer, sizeof buffer, value, 8, isSigned);
                  break;
              case 'x': case 'X':
                  format_hex(buffer, sizeof buffer, value, (c == 'X'));
                  break;
              default:
                  buffer[0] = '\0';
              }
              /* then point to it */
              str = buffer;
          }
  
          /* if we are here, 'str' points to the content that must be
           * outputted. handle padding and alignment now */
  
          slen = strlen(str);
  
          if (slen < width && !padLeft) {
              char padChar = padZero ? '0' : ' ';
              out_send_repeat(o, padChar, width - slen);
          }
  
          out_send(o, str, slen);
  
          if (slen < width && padLeft) {
              char padChar = padZero ? '0' : ' ';
              out_send_repeat(o, padChar, width - slen);
          }
      }
  }
  
  
  #ifdef UNIT_TESTS
  
  #include <stdio.h>
  
  static int   gFails = 0;
  
  #define  MARGIN  40
  
  #define  UTEST_CHECK(condition,message) \
      printf("Checking %-*s: ", MARGIN, message); fflush(stdout); \
      if (!(condition)) { \
          printf("KO\n"); \
          gFails += 1; \
      } else { \
          printf("ok\n"); \
      }
  
  static void
  utest_BufOut(void)
  {
      char buffer[16];
      BufOut bo[1];
      Out* out;
      int ret;
  
      buffer[0] = '1';
      out = buf_out_init(bo, buffer, sizeof buffer);
      UTEST_CHECK(buffer[0] == '\0', "buf_out_init clears initial byte");
      out_send(out, "abc", 3);
      UTEST_CHECK(!memcmp(buffer, "abc", 4), "out_send() works with BufOut");
      out_send_repeat(out, 'X', 4);
      UTEST_CHECK(!memcmp(buffer, "abcXXXX", 8), "out_send_repeat() works with BufOut");
      buffer[sizeof buffer-1] = 'x';
      out_send_repeat(out, 'Y', 2*sizeof(buffer));
      UTEST_CHECK(buffer[sizeof buffer-1] == '\0', "overflows always zero-terminates");
  
      out = buf_out_init(bo, buffer, sizeof buffer);
      out_send_repeat(out, 'X', 2*sizeof(buffer));
      ret = buf_out_length(bo);
      UTEST_CHECK(ret == 2*sizeof(buffer), "correct size returned on overflow");
  }
  
  static void
  utest_expect(const char*  result, const char*  format, ...)
  {
      va_list args;
      BufOut bo[1];
      char buffer[256];
      Out* out = buf_out_init(bo, buffer, sizeof buffer);
  
      printf("Checking %-*s: ", MARGIN, format); fflush(stdout);
      va_start(args, format);
      out_vformat(out, format, args);
      va_end(args);
  
      if (strcmp(result, buffer)) {
          printf("KO. got '%s' expecting '%s'\n", buffer, result);
          gFails += 1;
      } else {
          printf("ok. got '%s'\n", result);
      }
  }
  
  int  main(void)
  {
      utest_BufOut();
      utest_expect("", "");
      utest_expect("a", "a");
      utest_expect("01234", "01234", "");
      utest_expect("01234", "%s", "01234");
      utest_expect("aabbcc", "aa%scc", "bb");
      utest_expect("a", "%c", 'a');
      utest_expect("1234", "%d", 1234);
      utest_expect("-8123", "%d", -8123);
      utest_expect("16", "%hd", 0x7fff0010);
      utest_expect("16", "%hhd", 0x7fffff10);
      utest_expect("68719476736", "%lld", 0x1000000000);
      utest_expect("70000", "%ld", 70000);
      utest_expect("0xb0001234", "%p", (void*)0xb0001234);
      utest_expect("12ab", "%x", 0x12ab);
      utest_expect("12AB", "%X", 0x12ab);
      utest_expect("00123456", "%08x", 0x123456);
      utest_expect("01234", "0%d", 1234);
      utest_expect(" 1234", "%5d", 1234);
      utest_expect("01234", "%05d", 1234);
      utest_expect("    1234", "%8d", 1234);
      utest_expect("1234    ", "%-8d", 1234);
      utest_expect("abcdef     ", "%-11s", "abcdef");
      utest_expect("something:1234", "%s:%d", "something", 1234);
      return gFails != 0;
  }
  
  #endif /* UNIT_TESTS */
  