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 }