text: /*
   * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
   * 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.
   *   * Neither the name of Redis nor the names of its contributors may be used
   *     to endorse or promote products derived from this software without
   *     specific prior written permission.
   *
   * 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 "fmacros.h"
  #include <stdlib.h>
  #include <stdio.h>
  #include <string.h>
  #include <ctype.h>
  #include <limits.h>
  #include <math.h>
  #include <unistd.h>
  #include <sys/time.h>
  #include <float.h>
  #include <stdint.h>
  #include <errno.h>
  #include <time.h>
  
  #include "util.h"
  #include "sha1.h"
  
  /* Glob-style pattern matching. */
  int stringmatchlen(const char *pattern, int patternLen,
          const char *string, int stringLen, int nocase)
  {
      while(patternLen && stringLen) {
          switch(pattern[0]) {
          case '*':
              while (pattern[1] == '*') {
                  pattern++;
                  patternLen--;
              }
              if (patternLen == 1)
                  return 1; /* match */
              while(stringLen) {
                  if (stringmatchlen(pattern+1, patternLen-1,
                              string, stringLen, nocase))
                      return 1; /* match */
                  string++;
                  stringLen--;
              }
              return 0; /* no match */
              break;
          case '?':
              if (stringLen == 0)
                  return 0; /* no match */
              string++;
              stringLen--;
              break;
          case '[':
          {
              int not, match;
  
              pattern++;
              patternLen--;
              not = pattern[0] == '^';
              if (not) {
                  pattern++;
                  patternLen--;
              }
              match = 0;
              while(1) {
                  if (pattern[0] == '\\' && patternLen >= 2) {
                      pattern++;
                      patternLen--;
                      if (pattern[0] == string[0])
                          match = 1;
                  } else if (pattern[0] == ']') {
                      break;
                  } else if (patternLen == 0) {
                      pattern--;
                      patternLen++;
                      break;
                  } else if (pattern[1] == '-' && patternLen >= 3) {
                      int start = pattern[0];
                      int end = pattern[2];
                      int c = string[0];
                      if (start > end) {
                          int t = start;
                          start = end;
                          end = t;
                      }
                      if (nocase) {
                          start = tolower(start);
                          end = tolower(end);
                          c = tolower(c);
                      }
                      pattern += 2;
                      patternLen -= 2;
                      if (c >= start && c <= end)
                          match = 1;
                  } else {
                      if (!nocase) {
                          if (pattern[0] == string[0])
                              match = 1;
                      } else {
                          if (tolower((int)pattern[0]) == tolower((int)string[0]))
                              match = 1;
                      }
                  }
                  pattern++;
                  patternLen--;
              }
              if (not)
                  match = !match;
              if (!match)
                  return 0; /* no match */
              string++;
              stringLen--;
              break;
          }
          case '\\':
              if (patternLen >= 2) {
                  pattern++;
                  patternLen--;
              }
              /* fall through */
          default:
              if (!nocase) {
                  if (pattern[0] != string[0])
                      return 0; /* no match */
              } else {
                  if (tolower((int)pattern[0]) != tolower((int)string[0]))
                      return 0; /* no match */
              }
              string++;
              stringLen--;
              break;
          }
          pattern++;
          patternLen--;
          if (stringLen == 0) {
              while(*pattern == '*') {
                  pattern++;
                  patternLen--;
              }
              break;
          }
      }
      if (patternLen == 0 && stringLen == 0)
          return 1;
      return 0;
  }
  
  int stringmatch(const char *pattern, const char *string, int nocase) {
      return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase);
  }
  
  /* Fuzz stringmatchlen() trying to crash it with bad input. */
  int stringmatchlen_fuzz_test(void) {
      char str[32];
      char pat[32];
      int cycles = 10000000;
      int total_matches = 0;
      while(cycles--) {
          int strlen = rand() % sizeof(str);
          int patlen = rand() % sizeof(pat);
          for (int j = 0; j < strlen; j++) str[j] = rand() % 128;
          for (int j = 0; j < patlen; j++) pat[j] = rand() % 128;
          total_matches += stringmatchlen(pat, patlen, str, strlen, 0);
      }
      return total_matches;
  }
  
  /* Convert a string representing an amount of memory into the number of
   * bytes, so for instance memtoll("1Gb") will return 1073741824 that is
   * (1024*1024*1024).
   *
   * On parsing error, if *err is not NULL, it's set to 1, otherwise it's
   * set to 0. On error the function return value is 0, regardless of the
   * fact 'err' is NULL or not. */
  long long memtoll(const char *p, int *err) {
      const char *u;
      char buf[128];
      long mul; /* unit multiplier */
      long long val;
      unsigned int digits;
  
      if (err) *err = 0;
  
      /* Search the first non digit character. */
      u = p;
      if (*u == '-') u++;
      while(*u && isdigit(*u)) u++;
      if (*u == '\0' || !strcasecmp(u,"b")) {
          mul = 1;
      } else if (!strcasecmp(u,"k")) {
          mul = 1000;
      } else if (!strcasecmp(u,"kb")) {
          mul = 1024;
      } else if (!strcasecmp(u,"m")) {
          mul = 1000*1000;
      } else if (!strcasecmp(u,"mb")) {
          mul = 1024*1024;
      } else if (!strcasecmp(u,"g")) {
          mul = 1000L*1000*1000;
      } else if (!strcasecmp(u,"gb")) {
          mul = 1024L*1024*1024;
      } else {
          if (err) *err = 1;
          return 0;
      }
  
      /* Copy the digits into a buffer, we'll use strtoll() to convert
       * the digit (without the unit) into a number. */
      digits = u-p;
      if (digits >= sizeof(buf)) {
          if (err) *err = 1;
          return 0;
      }
      memcpy(buf,p,digits);
      buf[digits] = '\0';
  
      char *endptr;
      errno = 0;
      val = strtoll(buf,&endptr,10);
      if ((val == 0 && errno == EINVAL) || *endptr != '\0') {
          if (err) *err = 1;
          return 0;
      }
      return val*mul;
  }
  
  /* Return the number of digits of 'v' when converted to string in radix 10.
   * See ll2string() for more information. */
  uint32_t digits10(uint64_t v) {
      if (v < 10) return 1;
      if (v < 100) return 2;
      if (v < 1000) return 3;
      if (v < 1000000000000UL) {
          if (v < 100000000UL) {
              if (v < 1000000) {
                  if (v < 10000) return 4;
                  return 5 + (v >= 100000);
              }
              return 7 + (v >= 10000000UL);
          }
          if (v < 10000000000UL) {
              return 9 + (v >= 1000000000UL);
          }
          return 11 + (v >= 100000000000UL);
      }
      return 12 + digits10(v / 1000000000000UL);
  }
  
  /* Like digits10() but for signed values. */
  uint32_t sdigits10(int64_t v) {
      if (v < 0) {
          /* Abs value of LLONG_MIN requires special handling. */
          uint64_t uv = (v != LLONG_MIN) ?
                        (uint64_t)-v : ((uint64_t) LLONG_MAX)+1;
          return digits10(uv)+1; /* +1 for the minus. */
      } else {
          return digits10(v);
      }
  }
  
  /* Convert a long long into a string. Returns the number of
   * characters needed to represent the number.
   * If the buffer is not big enough to store the string, 0 is returned.
   *
   * Based on the following article (that apparently does not provide a
   * novel approach but only publicizes an already used technique):
   *
   * https://www.facebook.com/notes/facebook-engineering/three-optimization-tips-for-c/10151361643253920
   *
   * Modified in order to handle signed integers since the original code was
   * designed for unsigned integers. */
  int ll2string(char *dst, size_t dstlen, long long svalue) {
      static const char digits[201] =
          "0001020304050607080910111213141516171819"
          "2021222324252627282930313233343536373839"
          "4041424344454647484950515253545556575859"
          "6061626364656667686970717273747576777879"
          "8081828384858687888990919293949596979899";
      int negative;
      unsigned long long value;
  
      /* The main loop works with 64bit unsigned integers for simplicity, so
       * we convert the number here and remember if it is negative. */
      if (svalue < 0) {
          if (svalue != LLONG_MIN) {
              value = -svalue;
          } else {
              value = ((unsigned long long) LLONG_MAX)+1;
          }
          negative = 1;
      } else {
          value = svalue;
          negative = 0;
      }
  
      /* Check length. */
      uint32_t const length = digits10(value)+negative;
      if (length >= dstlen) return 0;
  
      /* Null term. */
      uint32_t next = length;
      dst[next] = '\0';
      next--;
      while (value >= 100) {
          int const i = (value % 100) * 2;
          value /= 100;
          dst[next] = digits[i + 1];
          dst[next - 1] = digits[i];
          next -= 2;
      }
  
      /* Handle last 1-2 digits. */
      if (value < 10) {
          dst[next] = '0' + (uint32_t) value;
      } else {
          int i = (uint32_t) value * 2;
          dst[next] = digits[i + 1];
          dst[next - 1] = digits[i];
      }
  
      /* Add sign. */
      if (negative) dst[0] = '-';
      return length;
  }
  
  /* Convert a string into a long long. Returns 1 if the string could be parsed
   * into a (non-overflowing) long long, 0 otherwise. The value will be set to
   * the parsed value when appropriate.
   *
   * Note that this function demands that the string strictly represents
   * a long long: no spaces or other characters before or after the string
   * representing the number are accepted, nor zeroes at the start if not
   * for the string "0" representing the zero number.
   *
   * Because of its strictness, it is safe to use this function to check if
   * you can convert a string into a long long, and obtain back the string
   * from the number without any loss in the string representation. */
  int string2ll(const char *s, size_t slen, long long *value) {
      const char *p = s;
      size_t plen = 0;
      int negative = 0;
      unsigned long long v;
  
      /* A zero length string is not a valid number. */
      if (plen == slen)
          return 0;
  
      /* Special case: first and only digit is 0. */
      if (slen == 1 && p[0] == '0') {
          if (value != NULL) *value = 0;
          return 1;
      }
  
      /* Handle negative numbers: just set a flag and continue like if it
       * was a positive number. Later convert into negative. */
      if (p[0] == '-') {
          negative = 1;
          p++; plen++;
  
          /* Abort on only a negative sign. */
          if (plen == slen)
              return 0;
      }
  
      /* First digit should be 1-9, otherwise the string should just be 0. */
      if (p[0] >= '1' && p[0] <= '9') {
          v = p[0]-'0';
          p++; plen++;
      } else {
          return 0;
      }
  
      /* Parse all the other digits, checking for overflow at every step. */
      while (plen < slen && p[0] >= '0' && p[0] <= '9') {
          if (v > (ULLONG_MAX / 10)) /* Overflow. */
              return 0;
          v *= 10;
  
          if (v > (ULLONG_MAX - (p[0]-'0'))) /* Overflow. */
              return 0;
          v += p[0]-'0';
  
          p++; plen++;
      }
  
      /* Return if not all bytes were used. */
      if (plen < slen)
          return 0;
  
      /* Convert to negative if needed, and do the final overflow check when
       * converting from unsigned long long to long long. */
      if (negative) {
          if (v > ((unsigned long long)(-(LLONG_MIN+1))+1)) /* Overflow. */
              return 0;
          if (value != NULL) *value = -v;
      } else {
          if (v > LLONG_MAX) /* Overflow. */
              return 0;
          if (value != NULL) *value = v;
      }
      return 1;
  }
  
  /* Convert a string into a long. Returns 1 if the string could be parsed into a
   * (non-overflowing) long, 0 otherwise. The value will be set to the parsed
   * value when appropriate. */
  int string2l(const char *s, size_t slen, long *lval) {
      long long llval;
  
      if (!string2ll(s,slen,&llval))
          return 0;
  
      if (llval < LONG_MIN || llval > LONG_MAX)
          return 0;
  
      *lval = (long)llval;
      return 1;
  }
  
  /* Convert a string into a double. Returns 1 if the string could be parsed
   * into a (non-overflowing) double, 0 otherwise. The value will be set to
   * the parsed value when appropriate.
   *
   * Note that this function demands that the string strictly represents
   * a double: no spaces or other characters before or after the string
   * representing the number are accepted. */
  int string2ld(const char *s, size_t slen, long double *dp) {
      char buf[256];
      long double value;
      char *eptr;
  
      if (slen >= sizeof(buf)) return 0;
      memcpy(buf,s,slen);
      buf[slen] = '\0';
  
      errno = 0;
      value = strtold(buf, &eptr);
      if (isspace(buf[0]) || eptr[0] != '\0' ||
          (errno == ERANGE &&
              (value == HUGE_VAL || value == -HUGE_VAL || value == 0)) ||
          errno == EINVAL ||
          isnan(value))
          return 0;
  
      if (dp) *dp = value;
      return 1;
  }
  
  /* Convert a double to a string representation. Returns the number of bytes
   * required. The representation should always be parsable by strtod(3).
   * This function does not support human-friendly formatting like ld2string
   * does. It is intended mainly to be used inside t_zset.c when writing scores
   * into a ziplist representing a sorted set. */
  int d2string(char *buf, size_t len, double value) {
      if (isnan(value)) {
          len = snprintf(buf,len,"nan");
      } else if (isinf(value)) {
          if (value < 0)
              len = snprintf(buf,len,"-inf");
          else
              len = snprintf(buf,len,"inf");
      } else if (value == 0) {
          /* See: http://en.wikipedia.org/wiki/Signed_zero, "Comparisons". */
          if (1.0/value < 0)
              len = snprintf(buf,len,"-0");
          else
              len = snprintf(buf,len,"0");
      } else {
  #if (DBL_MANT_DIG >= 52) && (LLONG_MAX == 0x7fffffffffffffffLL)
          /* Check if the float is in a safe range to be casted into a
           * long long. We are assuming that long long is 64 bit here.
           * Also we are assuming that there are no implementations around where
           * double has precision < 52 bit.
           *
           * Under this assumptions we test if a double is inside an interval
           * where casting to long long is safe. Then using two castings we
           * make sure the decimal part is zero. If all this is true we use
           * integer printing function that is much faster. */
          double min = -4503599627370495; /* (2^52)-1 */
          double max = 4503599627370496; /* -(2^52) */
          if (value > min && value < max && value == ((double)((long long)value)))
              len = ll2string(buf,len,(long long)value);
          else
  #endif
              len = snprintf(buf,len,"%.17g",value);
      }
  
      return len;
  }
  
  /* Convert a long double into a string. If humanfriendly is non-zero
   * it does not use exponential format and trims trailing zeroes at the end,
   * however this results in loss of precision. Otherwise exp format is used
   * and the output of snprintf() is not modified.
   *
   * The function returns the length of the string or zero if there was not
   * enough buffer room to store it. */
  int ld2string(char *buf, size_t len, long double value, int humanfriendly) {
      size_t l;
  
      if (isinf(value)) {
          /* Libc in odd systems (Hi Solaris!) will format infinite in a
           * different way, so better to handle it in an explicit way. */
          if (len < 5) return 0; /* No room. 5 is "-inf\0" */
          if (value > 0) {
              memcpy(buf,"inf",3);
              l = 3;
          } else {
              memcpy(buf,"-inf",4);
              l = 4;
          }
      } else if (humanfriendly) {
          /* We use 17 digits precision since with 128 bit floats that precision
           * after rounding is able to represent most small decimal numbers in a
           * way that is "non surprising" for the user (that is, most small
           * decimal numbers will be represented in a way that when converted
           * back into a string are exactly the same as what the user typed.) */
          l = snprintf(buf,len,"%.17Lf", value);
          if (l+1 > len) return 0; /* No room. */
          /* Now remove trailing zeroes after the '.' */
          if (strchr(buf,'.') != NULL) {
              char *p = buf+l-1;
              while(*p == '0') {
                  p--;
                  l--;
              }
              if (*p == '.') l--;
          }
      } else {
          l = snprintf(buf,len,"%.17Lg", value);
          if (l+1 > len) return 0; /* No room. */
      }
      buf[l] = '\0';
      return l;
  }
  
  /* Get random bytes, attempts to get an initial seed from /dev/urandom and
   * the uses a one way hash function in counter mode to generate a random
   * stream. However if /dev/urandom is not available, a weaker seed is used.
   *
   * This function is not thread safe, since the state is global. */
  void getRandomBytes(unsigned char *p, size_t len) {
      /* Global state. */
      static int seed_initialized = 0;
      static unsigned char seed[20]; /* The SHA1 seed, from /dev/urandom. */
      static uint64_t counter = 0; /* The counter we hash with the seed. */
  
      if (!seed_initialized) {
          /* Initialize a seed and use SHA1 in counter mode, where we hash
           * the same seed with a progressive counter. For the goals of this
           * function we just need non-colliding strings, there are no
           * cryptographic security needs. */
          FILE *fp = fopen("/dev/urandom","r");
          if (fp == NULL || fread(seed,sizeof(seed),1,fp) != 1) {
              /* Revert to a weaker seed, and in this case reseed again
               * at every call.*/
              for (unsigned int j = 0; j < sizeof(seed); j++) {
                  struct timeval tv;
                  gettimeofday(&tv,NULL);
                  pid_t pid = getpid();
                  seed[j] = tv.tv_sec ^ tv.tv_usec ^ pid ^ (long)fp;
              }
          } else {
              seed_initialized = 1;
          }
          if (fp) fclose(fp);
      }
  
      while(len) {
          unsigned char digest[20];
          SHA1_CTX ctx;
          unsigned int copylen = len > 20 ? 20 : len;
  
          SHA1Init(&ctx);
          SHA1Update(&ctx, seed, sizeof(seed));
          SHA1Update(&ctx, (unsigned char*)&counter,sizeof(counter));
          SHA1Final(digest, &ctx);
          counter++;
  
          memcpy(p,digest,copylen);
          len -= copylen;
          p += copylen;
      }
  }
  
  /* Generate the Redis "Run ID", a SHA1-sized random number that identifies a
   * given execution of Redis, so that if you are talking with an instance
   * having run_id == A, and you reconnect and it has run_id == B, you can be
   * sure that it is either a different instance or it was restarted. */
  void getRandomHexChars(char *p, size_t len) {
      char *charset = "0123456789abcdef";
      size_t j;
  
      getRandomBytes((unsigned char*)p,len);
      for (j = 0; j < len; j++) p[j] = charset[p[j] & 0x0F];
  }
  
  /* Given the filename, return the absolute path as an SDS string, or NULL
   * if it fails for some reason. Note that "filename" may be an absolute path
   * already, this will be detected and handled correctly.
   *
   * The function does not try to normalize everything, but only the obvious
   * case of one or more "../" appearing at the start of "filename"
   * relative path. */
  sds getAbsolutePath(char *filename) {
      char cwd[1024];
      sds abspath;
      sds relpath = sdsnew(filename);
  
      relpath = sdstrim(relpath," \r\n\t");
      if (relpath[0] == '/') return relpath; /* Path is already absolute. */
  
      /* If path is relative, join cwd and relative path. */
      if (getcwd(cwd,sizeof(cwd)) == NULL) {
          sdsfree(relpath);
          return NULL;
      }
      abspath = sdsnew(cwd);
      if (sdslen(abspath) && abspath[sdslen(abspath)-1] != '/')
          abspath = sdscat(abspath,"/");
  
      /* At this point we have the current path always ending with "/", and
       * the trimmed relative path. Try to normalize the obvious case of
       * trailing ../ elements at the start of the path.
       *
       * For every "../" we find in the filename, we remove it and also remove
       * the last element of the cwd, unless the current cwd is "/". */
      while (sdslen(relpath) >= 3 &&
             relpath[0] == '.' && relpath[1] == '.' && relpath[2] == '/')
      {
          sdsrange(relpath,3,-1);
          if (sdslen(abspath) > 1) {
              char *p = abspath + sdslen(abspath)-2;
              int trimlen = 1;
  
              while(*p != '/') {
                  p--;
                  trimlen++;
              }
              sdsrange(abspath,0,-(trimlen+1));
          }
      }
  
      /* Finally glue the two parts together. */
      abspath = sdscatsds(abspath,relpath);
      sdsfree(relpath);
      return abspath;
  }
  
  /*
   * Gets the proper timezone in a more portable fashion
   * i.e timezone variables are linux specific.
   */
  
  unsigned long getTimeZone(void) {
  #ifdef __linux__
      return timezone;
  #else
      struct timeval tv;
      struct timezone tz;
  
      gettimeofday(&tv, &tz);
  
      return tz.tz_minuteswest * 60UL;
  #endif
  }
  
  /* Return true if the specified path is just a file basename without any
   * relative or absolute path. This function just checks that no / or \
   * character exists inside the specified path, that's enough in the
   * environments where Redis runs. */
  int pathIsBaseName(char *path) {
      return strchr(path,'/') == NULL && strchr(path,'\\') == NULL;
  }
  
  #ifdef REDIS_TEST
  #include <assert.h>
  
  static void test_string2ll(void) {
      char buf[32];
      long long v;
  
      /* May not start with +. */
      strcpy(buf,"+1");
      assert(string2ll(buf,strlen(buf),&v) == 0);
  
      /* Leading space. */
      strcpy(buf," 1");
      assert(string2ll(buf,strlen(buf),&v) == 0);
  
      /* Trailing space. */
      strcpy(buf,"1 ");
      assert(string2ll(buf,strlen(buf),&v) == 0);
  
      /* May not start with 0. */
      strcpy(buf,"01");
      assert(string2ll(buf,strlen(buf),&v) == 0);
  
      strcpy(buf,"-1");
      assert(string2ll(buf,strlen(buf),&v) == 1);
      assert(v == -1);
  
      strcpy(buf,"0");
      assert(string2ll(buf,strlen(buf),&v) == 1);
      assert(v == 0);
  
      strcpy(buf,"1");
      assert(string2ll(buf,strlen(buf),&v) == 1);
      assert(v == 1);
  
      strcpy(buf,"99");
      assert(string2ll(buf,strlen(buf),&v) == 1);
      assert(v == 99);
  
      strcpy(buf,"-99");
      assert(string2ll(buf,strlen(buf),&v) == 1);
      assert(v == -99);
  
      strcpy(buf,"-9223372036854775808");
      assert(string2ll(buf,strlen(buf),&v) == 1);
      assert(v == LLONG_MIN);
  
      strcpy(buf,"-9223372036854775809"); /* overflow */
      assert(string2ll(buf,strlen(buf),&v) == 0);
  
      strcpy(buf,"9223372036854775807");
      assert(string2ll(buf,strlen(buf),&v) == 1);
      assert(v == LLONG_MAX);
  
      strcpy(buf,"9223372036854775808"); /* overflow */
      assert(string2ll(buf,strlen(buf),&v) == 0);
  }
  
  static void test_string2l(void) {
      char buf[32];
      long v;
  
      /* May not start with +. */
      strcpy(buf,"+1");
      assert(string2l(buf,strlen(buf),&v) == 0);
  
      /* May not start with 0. */
      strcpy(buf,"01");
      assert(string2l(buf,strlen(buf),&v) == 0);
  
      strcpy(buf,"-1");
      assert(string2l(buf,strlen(buf),&v) == 1);
      assert(v == -1);
  
      strcpy(buf,"0");
      assert(string2l(buf,strlen(buf),&v) == 1);
      assert(v == 0);
  
      strcpy(buf,"1");
      assert(string2l(buf,strlen(buf),&v) == 1);
      assert(v == 1);
  
      strcpy(buf,"99");
      assert(string2l(buf,strlen(buf),&v) == 1);
      assert(v == 99);
  
      strcpy(buf,"-99");
      assert(string2l(buf,strlen(buf),&v) == 1);
      assert(v == -99);
  
  #if LONG_MAX != LLONG_MAX
      strcpy(buf,"-2147483648");
      assert(string2l(buf,strlen(buf),&v) == 1);
      assert(v == LONG_MIN);
  
      strcpy(buf,"-2147483649"); /* overflow */
      assert(string2l(buf,strlen(buf),&v) == 0);
  
      strcpy(buf,"2147483647");
      assert(string2l(buf,strlen(buf),&v) == 1);
      assert(v == LONG_MAX);
  
      strcpy(buf,"2147483648"); /* overflow */
      assert(string2l(buf,strlen(buf),&v) == 0);
  #endif
  }
  
  static void test_ll2string(void) {
      char buf[32];
      long long v;
      int sz;
  
      v = 0;
      sz = ll2string(buf, sizeof buf, v);
      assert(sz == 1);
      assert(!strcmp(buf, "0"));
  
      v = -1;
      sz = ll2string(buf, sizeof buf, v);
      assert(sz == 2);
      assert(!strcmp(buf, "-1"));
  
      v = 99;
      sz = ll2string(buf, sizeof buf, v);
      assert(sz == 2);
      assert(!strcmp(buf, "99"));
  
      v = -99;
      sz = ll2string(buf, sizeof buf, v);
      assert(sz == 3);
      assert(!strcmp(buf, "-99"));
  
      v = -2147483648;
      sz = ll2string(buf, sizeof buf, v);
      assert(sz == 11);
      assert(!strcmp(buf, "-2147483648"));
  
      v = LLONG_MIN;
      sz = ll2string(buf, sizeof buf, v);
      assert(sz == 20);
      assert(!strcmp(buf, "-9223372036854775808"));
  
      v = LLONG_MAX;
      sz = ll2string(buf, sizeof buf, v);
      assert(sz == 19);
      assert(!strcmp(buf, "9223372036854775807"));
  }
  
  #define UNUSED(x) (void)(x)
  int utilTest(int argc, char **argv) {
      UNUSED(argc);
      UNUSED(argv);
  
      test_string2ll();
      test_string2l();
      test_ll2string();
      return 0;
  }
  #endif
  