tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

printf.c (4841B)


      1 /* Copyright (c) 2003-2004, Roger Dingledine
      2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      4 /* See LICENSE for licensing information */
      5 
      6 /**
      7 * \file printf.c
      8 * \brief Compatibility wrappers around snprintf and its friends
      9 **/
     10 
     11 #include "lib/cc/torint.h"
     12 #include "lib/string/printf.h"
     13 #include "lib/err/torerr.h"
     14 #include "lib/malloc/malloc.h"
     15 
     16 #include <stdlib.h>
     17 #include <stdio.h>
     18 
     19 /** Replacement for snprintf.  Differs from platform snprintf in two
     20 * ways: First, always NUL-terminates its output.  Second, always
     21 * returns -1 if the result is truncated.  (Note that this return
     22 * behavior does <i>not</i> conform to C99; it just happens to be
     23 * easier to emulate "return -1" with conformant implementations than
     24 * it is to emulate "return number that would be written" with
     25 * non-conformant implementations.) */
     26 int
     27 tor_snprintf(char *str, size_t size, const char *format, ...)
     28 {
     29  va_list ap;
     30  int r;
     31  va_start(ap,format);
     32  r = tor_vsnprintf(str,size,format,ap);
     33  va_end(ap);
     34  return r;
     35 }
     36 
     37 /** Replacement for vsnprintf; behavior differs as tor_snprintf differs from
     38 * snprintf.
     39 */
     40 int
     41 tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
     42 {
     43  int r;
     44  if (size == 0)
     45    return -1; /* no place for the NUL */
     46  if (size > SIZE_T_CEILING)
     47    return -1;
     48 #if defined(_WIN32) && !defined(HAVE_VSNPRINTF)
     49  r = _vsnprintf(str, size, format, args);
     50 #else
     51  r = vsnprintf(str, size, format, args);
     52 #endif
     53  str[size-1] = '\0';
     54  if (r < 0 || r >= (ssize_t)size)
     55    return -1;
     56  return r;
     57 }
     58 
     59 /**
     60 * Portable asprintf implementation.  Does a printf() into a newly malloc'd
     61 * string.  Sets *<b>strp</b> to this string, and returns its length (not
     62 * including the terminating NUL character).
     63 *
     64 * You can treat this function as if its implementation were something like
     65   <pre>
     66     char buf[_INFINITY_];
     67     tor_snprintf(buf, sizeof(buf), fmt, args);
     68     *strp = tor_strdup(buf);
     69     return strlen(*strp):
     70   </pre>
     71 * Where _INFINITY_ is an imaginary constant so big that any string can fit
     72 * into it.
     73 */
     74 int
     75 tor_asprintf(char **strp, const char *fmt, ...)
     76 {
     77  int r;
     78  va_list args;
     79  va_start(args, fmt);
     80  r = tor_vasprintf(strp, fmt, args);
     81  va_end(args);
     82  if (!*strp || r < 0) {
     83    /* LCOV_EXCL_START */
     84    raw_assert_unreached_msg("Internal error in asprintf");
     85    /* LCOV_EXCL_STOP */
     86  }
     87  return r;
     88 }
     89 
     90 /**
     91 * Portable vasprintf implementation.  Does a printf() into a newly malloc'd
     92 * string.  Differs from regular vasprintf in the same ways that
     93 * tor_asprintf() differs from regular asprintf.
     94 */
     95 int
     96 tor_vasprintf(char **strp, const char *fmt, va_list args)
     97 {
     98  /* use a temporary variable in case *strp is in args. */
     99  char *strp_tmp=NULL;
    100 #ifdef HAVE_VASPRINTF
    101  /* If the platform gives us one, use it. */
    102  int r = vasprintf(&strp_tmp, fmt, args);
    103  if (r < 0)
    104    *strp = NULL; // LCOV_EXCL_LINE -- no cross-platform way to force this
    105  else
    106    *strp = strp_tmp;
    107  return r;
    108 #elif defined(HAVE__VSCPRINTF)
    109  /* On Windows, _vsnprintf won't tell us the length of the string if it
    110   * overflows, so we need to use _vcsprintf to tell how much to allocate */
    111  int len, r;
    112  va_list tmp_args;
    113  va_copy(tmp_args, args);
    114  len = _vscprintf(fmt, tmp_args);
    115  va_end(tmp_args);
    116  if (len < 0) {
    117    *strp = NULL;
    118    return -1;
    119  }
    120  strp_tmp = tor_malloc((size_t)len + 1);
    121  r = _vsnprintf(strp_tmp, (size_t)len+1, fmt, args);
    122  if (r != len) {
    123    tor_free(strp_tmp);
    124    *strp = NULL;
    125    return -1;
    126  }
    127  *strp = strp_tmp;
    128  return len;
    129 #else
    130  /* Everywhere else, we have a decent vsnprintf that tells us how many
    131   * characters we need.  We give it a try on a short buffer first, since
    132   * it might be nice to avoid the second vsnprintf call.
    133   */
    134  /* XXXX This code spent a number of years broken (see bug 30651). It is
    135   * possible that no Tor users actually run on systems without vasprintf() or
    136   * _vscprintf(). If so, we should consider removing this code. */
    137  char buf[128];
    138  int len, r;
    139  va_list tmp_args;
    140  va_copy(tmp_args, args);
    141  /* Use vsnprintf to retrieve needed length.  tor_vsnprintf() is not an
    142   * option here because it will simply return -1 if buf is not large enough
    143   * to hold the complete string.
    144   */
    145  len = vsnprintf(buf, sizeof(buf), fmt, tmp_args);
    146  va_end(tmp_args);
    147  buf[sizeof(buf) - 1] = '\0';
    148  if (len < 0) {
    149    *strp = NULL;
    150    return -1;
    151  }
    152  if (len < (int)sizeof(buf)) {
    153    *strp = tor_strdup(buf);
    154    return len;
    155  }
    156  strp_tmp = tor_malloc((size_t)len+1);
    157  /* use of tor_vsnprintf() will ensure string is null terminated */
    158  r = tor_vsnprintf(strp_tmp, (size_t)len+1, fmt, args);
    159  if (r != len) {
    160    tor_free(strp_tmp);
    161    *strp = NULL;
    162    return -1;
    163  }
    164  *strp = strp_tmp;
    165  return len;
    166 #endif /* defined(HAVE_VASPRINTF) || ... */
    167 }