Patch from Kurt Roeckx: apparently on Debian amd64, the ut_time member of
[u/mdw/putty] / misc.c
CommitLineData
f4ff9455 1/*
2 * Platform-independent routines shared between all PuTTY programs.
3 */
4
374330e2 5#include <stdio.h>
6#include <stdlib.h>
1709795f 7#include <stdarg.h>
57356d63 8#include <ctype.h>
5471d09a 9#include <assert.h>
374330e2 10#include "putty.h"
11
03f64569 12/* ----------------------------------------------------------------------
13 * String handling routines.
14 */
15
57356d63 16char *dupstr(const char *s)
03f64569 17{
6d113886 18 char *p = NULL;
19 if (s) {
20 int len = strlen(s);
21 p = snewn(len + 1, char);
22 strcpy(p, s);
23 }
03f64569 24 return p;
25}
26
27/* Allocate the concatenation of N strings. Terminate arg list with NULL. */
57356d63 28char *dupcat(const char *s1, ...)
03f64569 29{
30 int len;
31 char *p, *q, *sn;
32 va_list ap;
33
34 len = strlen(s1);
35 va_start(ap, s1);
36 while (1) {
37 sn = va_arg(ap, char *);
38 if (!sn)
39 break;
40 len += strlen(sn);
41 }
42 va_end(ap);
43
3d88e64d 44 p = snewn(len + 1, char);
03f64569 45 strcpy(p, s1);
46 q = p + strlen(p);
47
48 va_start(ap, s1);
49 while (1) {
50 sn = va_arg(ap, char *);
51 if (!sn)
52 break;
53 strcpy(q, sn);
54 q += strlen(q);
55 }
56 va_end(ap);
57
58 return p;
59}
60
57356d63 61/*
62 * Do an sprintf(), but into a custom-allocated buffer.
63 *
64 * Irritatingly, we don't seem to be able to do this portably using
65 * vsnprintf(), because there appear to be issues with re-using the
66 * same va_list for two calls, and the excellent C99 va_copy is not
67 * yet widespread. Bah. Instead I'm going to do a horrid, horrid
68 * hack, in which I trawl the format string myself, work out the
69 * maximum length of each format component, and resize the buffer
70 * before printing it.
71 */
72char *dupprintf(const char *fmt, ...)
73{
74 char *ret;
75 va_list ap;
76 va_start(ap, fmt);
77 ret = dupvprintf(fmt, ap);
78 va_end(ap);
79 return ret;
80}
81char *dupvprintf(const char *fmt, va_list ap)
82{
83 char *buf;
84 int len, size;
85
3d88e64d 86 buf = snewn(512, char);
57356d63 87 size = 512;
88
89 while (1) {
90#ifdef _WINDOWS
91#define vsnprintf _vsnprintf
92#endif
93 len = vsnprintf(buf, size, fmt, ap);
94 if (len >= 0 && len < size) {
95 /* This is the C99-specified criterion for snprintf to have
96 * been completely successful. */
97 return buf;
98 } else if (len > 0) {
99 /* This is the C99 error condition: the returned length is
100 * the required buffer size not counting the NUL. */
101 size = len + 1;
102 } else {
103 /* This is the pre-C99 glibc error condition: <0 means the
104 * buffer wasn't big enough, so we enlarge it a bit and hope. */
105 size += 512;
106 }
3d88e64d 107 buf = sresize(buf, size, char);
57356d63 108 }
109}
110
03f64569 111/* ----------------------------------------------------------------------
1549e076 112 * Base64 encoding routine. This is required in public-key writing
113 * but also in HTTP proxy handling, so it's centralised here.
114 */
115
116void base64_encode_atom(unsigned char *data, int n, char *out)
117{
118 static const char base64_chars[] =
119 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
120
121 unsigned word;
122
123 word = data[0] << 16;
124 if (n > 1)
125 word |= data[1] << 8;
126 if (n > 2)
127 word |= data[2];
128 out[0] = base64_chars[(word >> 18) & 0x3F];
129 out[1] = base64_chars[(word >> 12) & 0x3F];
130 if (n > 1)
131 out[2] = base64_chars[(word >> 6) & 0x3F];
132 else
133 out[2] = '=';
134 if (n > 2)
135 out[3] = base64_chars[word & 0x3F];
136 else
137 out[3] = '=';
138}
139
140/* ----------------------------------------------------------------------
5471d09a 141 * Generic routines to deal with send buffers: a linked list of
142 * smallish blocks, with the operations
143 *
144 * - add an arbitrary amount of data to the end of the list
145 * - remove the first N bytes from the list
146 * - return a (pointer,length) pair giving some initial data in
147 * the list, suitable for passing to a send or write system
148 * call
7983f47e 149 * - retrieve a larger amount of initial data from the list
5471d09a 150 * - return the current size of the buffer chain in bytes
151 */
152
153#define BUFFER_GRANULE 512
154
155struct bufchain_granule {
156 struct bufchain_granule *next;
157 int buflen, bufpos;
158 char buf[BUFFER_GRANULE];
159};
160
161void bufchain_init(bufchain *ch)
162{
163 ch->head = ch->tail = NULL;
164 ch->buffersize = 0;
165}
166
167void bufchain_clear(bufchain *ch)
168{
169 struct bufchain_granule *b;
170 while (ch->head) {
171 b = ch->head;
172 ch->head = ch->head->next;
173 sfree(b);
174 }
175 ch->tail = NULL;
176 ch->buffersize = 0;
177}
178
179int bufchain_size(bufchain *ch)
180{
181 return ch->buffersize;
182}
183
e0e7dff8 184void bufchain_add(bufchain *ch, const void *data, int len)
5471d09a 185{
e0e7dff8 186 const char *buf = (const char *)data;
5471d09a 187
bfa5400d 188 if (len == 0) return;
189
5471d09a 190 ch->buffersize += len;
191
192 if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) {
193 int copylen = min(len, BUFFER_GRANULE - ch->tail->buflen);
194 memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen);
195 buf += copylen;
196 len -= copylen;
197 ch->tail->buflen += copylen;
198 }
199 while (len > 0) {
200 int grainlen = min(len, BUFFER_GRANULE);
201 struct bufchain_granule *newbuf;
3d88e64d 202 newbuf = snew(struct bufchain_granule);
5471d09a 203 newbuf->bufpos = 0;
204 newbuf->buflen = grainlen;
205 memcpy(newbuf->buf, buf, grainlen);
206 buf += grainlen;
207 len -= grainlen;
208 if (ch->tail)
209 ch->tail->next = newbuf;
210 else
211 ch->head = ch->tail = newbuf;
212 newbuf->next = NULL;
213 ch->tail = newbuf;
214 }
215}
216
217void bufchain_consume(bufchain *ch, int len)
218{
7983f47e 219 struct bufchain_granule *tmp;
220
5471d09a 221 assert(ch->buffersize >= len);
7983f47e 222 while (len > 0) {
223 int remlen = len;
224 assert(ch->head != NULL);
225 if (remlen >= ch->head->buflen - ch->head->bufpos) {
226 remlen = ch->head->buflen - ch->head->bufpos;
227 tmp = ch->head;
228 ch->head = tmp->next;
229 sfree(tmp);
230 if (!ch->head)
231 ch->tail = NULL;
232 } else
233 ch->head->bufpos += remlen;
234 ch->buffersize -= remlen;
235 len -= remlen;
5471d09a 236 }
237}
238
239void bufchain_prefix(bufchain *ch, void **data, int *len)
240{
241 *len = ch->head->buflen - ch->head->bufpos;
242 *data = ch->head->buf + ch->head->bufpos;
243}
244
7983f47e 245void bufchain_fetch(bufchain *ch, void *data, int len)
246{
247 struct bufchain_granule *tmp;
248 char *data_c = (char *)data;
249
250 tmp = ch->head;
251
252 assert(ch->buffersize >= len);
253 while (len > 0) {
254 int remlen = len;
255
256 assert(tmp != NULL);
257 if (remlen >= tmp->buflen - tmp->bufpos)
258 remlen = tmp->buflen - tmp->bufpos;
259 memcpy(data_c, tmp->buf + tmp->bufpos, remlen);
260
261 tmp = tmp->next;
262 len -= remlen;
263 data_c += remlen;
264 }
265}
266
03f64569 267/* ----------------------------------------------------------------------
b191636d 268 * My own versions of malloc, realloc and free. Because I want
269 * malloc and realloc to bomb out and exit the program if they run
270 * out of memory, realloc to reliably call malloc if passed a NULL
271 * pointer, and free to reliably do nothing if passed a NULL
272 * pointer. We can also put trace printouts in, if we need to; and
273 * we can also replace the allocator with an ElectricFence-like
274 * one.
275 */
276
277#ifdef MINEFIELD
d0912d1f 278void *minefield_c_malloc(size_t size);
279void minefield_c_free(void *p);
280void *minefield_c_realloc(void *p, size_t size);
281#endif
374330e2 282
283#ifdef MALLOC_LOG
284static FILE *fp = NULL;
285
d7da76ca 286static char *mlog_file = NULL;
287static int mlog_line = 0;
288
32874aea 289void mlog(char *file, int line)
290{
d7da76ca 291 mlog_file = file;
292 mlog_line = line;
c662dbc0 293 if (!fp) {
374330e2 294 fp = fopen("putty_mem.log", "w");
c662dbc0 295 setvbuf(fp, NULL, _IONBF, BUFSIZ);
296 }
374330e2 297 if (fp)
32874aea 298 fprintf(fp, "%s:%d: ", file, line);
374330e2 299}
300#endif
301
32874aea 302void *safemalloc(size_t size)
303{
b191636d 304 void *p;
305#ifdef MINEFIELD
32874aea 306 p = minefield_c_malloc(size);
b191636d 307#else
32874aea 308 p = malloc(size);
b191636d 309#endif
374330e2 310 if (!p) {
d7da76ca 311 char str[200];
312#ifdef MALLOC_LOG
313 sprintf(str, "Out of memory! (%s:%d, size=%d)",
314 mlog_file, mlog_line, size);
1b2ef365 315 fprintf(fp, "*** %s\n", str);
316 fclose(fp);
d7da76ca 317#else
318 strcpy(str, "Out of memory!");
319#endif
1709795f 320 modalfatalbox(str);
374330e2 321 }
322#ifdef MALLOC_LOG
323 if (fp)
324 fprintf(fp, "malloc(%d) returns %p\n", size, p);
325#endif
326 return p;
327}
328
32874aea 329void *saferealloc(void *ptr, size_t size)
330{
374330e2 331 void *p;
b191636d 332 if (!ptr) {
333#ifdef MINEFIELD
32874aea 334 p = minefield_c_malloc(size);
b191636d 335#else
32874aea 336 p = malloc(size);
b191636d 337#endif
338 } else {
339#ifdef MINEFIELD
32874aea 340 p = minefield_c_realloc(ptr, size);
b191636d 341#else
32874aea 342 p = realloc(ptr, size);
b191636d 343#endif
344 }
374330e2 345 if (!p) {
d7da76ca 346 char str[200];
347#ifdef MALLOC_LOG
348 sprintf(str, "Out of memory! (%s:%d, size=%d)",
349 mlog_file, mlog_line, size);
1b2ef365 350 fprintf(fp, "*** %s\n", str);
351 fclose(fp);
d7da76ca 352#else
353 strcpy(str, "Out of memory!");
354#endif
1709795f 355 modalfatalbox(str);
374330e2 356 }
357#ifdef MALLOC_LOG
358 if (fp)
359 fprintf(fp, "realloc(%p,%d) returns %p\n", ptr, size, p);
360#endif
361 return p;
362}
363
32874aea 364void safefree(void *ptr)
365{
374330e2 366 if (ptr) {
367#ifdef MALLOC_LOG
368 if (fp)
369 fprintf(fp, "free(%p)\n", ptr);
370#endif
b191636d 371#ifdef MINEFIELD
32874aea 372 minefield_c_free(ptr);
b191636d 373#else
32874aea 374 free(ptr);
b191636d 375#endif
374330e2 376 }
377#ifdef MALLOC_LOG
378 else if (fp)
379 fprintf(fp, "freeing null pointer - no action taken\n");
380#endif
381}
c82bda52 382
03f64569 383/* ----------------------------------------------------------------------
384 * Debugging routines.
385 */
386
c82bda52 387#ifdef DEBUG
d0912d1f 388extern void dputs(char *); /* defined in per-platform *misc.c */
db9c0f86 389
d0912d1f 390void debug_printf(char *fmt, ...)
32874aea 391{
57356d63 392 char *buf;
db9c0f86 393 va_list ap;
394
395 va_start(ap, fmt);
57356d63 396 buf = dupvprintf(fmt, ap);
32874aea 397 dputs(buf);
57356d63 398 sfree(buf);
c82bda52 399 va_end(ap);
400}
db9c0f86 401
402
32874aea 403void debug_memdump(void *buf, int len, int L)
404{
db9c0f86 405 int i;
406 unsigned char *p = buf;
765c4200 407 char foo[17];
db9c0f86 408 if (L) {
409 int delta;
d0912d1f 410 debug_printf("\t%d (0x%x) bytes:\n", len, len);
db9c0f86 411 delta = 15 & (int) p;
412 p -= delta;
413 len += delta;
414 }
415 for (; 0 < len; p += 16, len -= 16) {
32874aea 416 dputs(" ");
417 if (L)
d0912d1f 418 debug_printf("%p: ", p);
32874aea 419 strcpy(foo, "................"); /* sixteen dots */
db9c0f86 420 for (i = 0; i < 16 && i < len; ++i) {
421 if (&p[i] < (unsigned char *) buf) {
32874aea 422 dputs(" "); /* 3 spaces */
765c4200 423 foo[i] = ' ';
db9c0f86 424 } else {
d0912d1f 425 debug_printf("%c%02.2x",
32874aea 426 &p[i] != (unsigned char *) buf
427 && i % 4 ? '.' : ' ', p[i]
428 );
765c4200 429 if (p[i] >= ' ' && p[i] <= '~')
32874aea 430 foo[i] = (char) p[i];
db9c0f86 431 }
432 }
765c4200 433 foo[i] = '\0';
d0912d1f 434 debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo);
db9c0f86 435 }
436}
437
32874aea 438#endif /* def DEBUG */