Line data Source code
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/string/printf.h"
12 : #include "lib/err/torerr.h"
13 : #include "lib/cc/torint.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 161650 : tor_snprintf(char *str, size_t size, const char *format, ...)
28 : {
29 161650 : va_list ap;
30 161650 : int r;
31 161650 : va_start(ap,format);
32 161650 : r = tor_vsnprintf(str,size,format,ap);
33 161650 : va_end(ap);
34 161650 : return r;
35 : }
36 :
37 : /** Replacement for vsnprintf; behavior differs as tor_snprintf differs from
38 : * snprintf.
39 : */
40 : int
41 172563 : tor_vsnprintf(char *str, size_t size, const char *format, va_list args)
42 : {
43 172563 : int r;
44 172563 : if (size == 0)
45 : return -1; /* no place for the NUL */
46 172562 : if (size > SIZE_T_CEILING)
47 : return -1;
48 : #ifdef _WIN32
49 : r = _vsnprintf(str, size, format, args);
50 : #else
51 172562 : r = vsnprintf(str, size, format, args);
52 : #endif
53 172562 : str[size-1] = '\0';
54 172562 : if (r < 0 || r >= (ssize_t)size)
55 22 : 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 198274 : tor_asprintf(char **strp, const char *fmt, ...)
76 : {
77 198274 : int r;
78 198274 : va_list args;
79 198274 : va_start(args, fmt);
80 198274 : r = tor_vasprintf(strp, fmt, args);
81 198274 : va_end(args);
82 198274 : if (!*strp || r < 0) {
83 : /* LCOV_EXCL_START */
84 : raw_assert_unreached_msg("Internal error in asprintf");
85 : /* LCOV_EXCL_STOP */
86 : }
87 198274 : 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 230469 : tor_vasprintf(char **strp, const char *fmt, va_list args)
97 : {
98 : /* use a temporary variable in case *strp is in args. */
99 230469 : char *strp_tmp=NULL;
100 : #ifdef HAVE_VASPRINTF
101 : /* If the platform gives us one, use it. */
102 230469 : int r = vasprintf(&strp_tmp, fmt, args);
103 230469 : if (r < 0)
104 : *strp = NULL; // LCOV_EXCL_LINE -- no cross-platform way to force this
105 : else
106 230469 : *strp = strp_tmp;
107 230469 : 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 : }
|