Various tweaks to header comments to remind me which bits are meant to be
[u/mdw/putty] / misc.c
1 /*
2 * Platform-independent routines shared between all PuTTY programs.
3 */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdarg.h>
8 #include <ctype.h>
9 #include <assert.h>
10 #include "putty.h"
11
12 /* ----------------------------------------------------------------------
13 * String handling routines.
14 */
15
16 char *dupstr(const char *s)
17 {
18 char *p = NULL;
19 if (s) {
20 int len = strlen(s);
21 p = snewn(len + 1, char);
22 strcpy(p, s);
23 }
24 return p;
25 }
26
27 /* Allocate the concatenation of N strings. Terminate arg list with NULL. */
28 char *dupcat(const char *s1, ...)
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
44 p = snewn(len + 1, char);
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
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 */
72 char *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 }
81 char *dupvprintf(const char *fmt, va_list ap)
82 {
83 char *buf;
84 int len, size;
85
86 buf = snewn(512, char);
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 }
107 buf = sresize(buf, size, char);
108 }
109 }
110
111 /* ----------------------------------------------------------------------
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
116 void 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 /* ----------------------------------------------------------------------
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
149 * - retrieve a larger amount of initial data from the list
150 * - return the current size of the buffer chain in bytes
151 */
152
153 #define BUFFER_GRANULE 512
154
155 struct bufchain_granule {
156 struct bufchain_granule *next;
157 int buflen, bufpos;
158 char buf[BUFFER_GRANULE];
159 };
160
161 void bufchain_init(bufchain *ch)
162 {
163 ch->head = ch->tail = NULL;
164 ch->buffersize = 0;
165 }
166
167 void 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
179 int bufchain_size(bufchain *ch)
180 {
181 return ch->buffersize;
182 }
183
184 void bufchain_add(bufchain *ch, const void *data, int len)
185 {
186 const char *buf = (const char *)data;
187
188 if (len == 0) return;
189
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;
202 newbuf = snew(struct bufchain_granule);
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
217 void bufchain_consume(bufchain *ch, int len)
218 {
219 struct bufchain_granule *tmp;
220
221 assert(ch->buffersize >= len);
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;
236 }
237 }
238
239 void 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
245 void 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
267 /* ----------------------------------------------------------------------
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
278 void *minefield_c_malloc(size_t size);
279 void minefield_c_free(void *p);
280 void *minefield_c_realloc(void *p, size_t size);
281 #endif
282
283 #ifdef MALLOC_LOG
284 static FILE *fp = NULL;
285
286 static char *mlog_file = NULL;
287 static int mlog_line = 0;
288
289 void mlog(char *file, int line)
290 {
291 mlog_file = file;
292 mlog_line = line;
293 if (!fp) {
294 fp = fopen("putty_mem.log", "w");
295 setvbuf(fp, NULL, _IONBF, BUFSIZ);
296 }
297 if (fp)
298 fprintf(fp, "%s:%d: ", file, line);
299 }
300 #endif
301
302 void *safemalloc(size_t size)
303 {
304 void *p;
305 #ifdef MINEFIELD
306 p = minefield_c_malloc(size);
307 #else
308 p = malloc(size);
309 #endif
310 if (!p) {
311 char str[200];
312 #ifdef MALLOC_LOG
313 sprintf(str, "Out of memory! (%s:%d, size=%d)",
314 mlog_file, mlog_line, size);
315 fprintf(fp, "*** %s\n", str);
316 fclose(fp);
317 #else
318 strcpy(str, "Out of memory!");
319 #endif
320 modalfatalbox(str);
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
329 void *saferealloc(void *ptr, size_t size)
330 {
331 void *p;
332 if (!ptr) {
333 #ifdef MINEFIELD
334 p = minefield_c_malloc(size);
335 #else
336 p = malloc(size);
337 #endif
338 } else {
339 #ifdef MINEFIELD
340 p = minefield_c_realloc(ptr, size);
341 #else
342 p = realloc(ptr, size);
343 #endif
344 }
345 if (!p) {
346 char str[200];
347 #ifdef MALLOC_LOG
348 sprintf(str, "Out of memory! (%s:%d, size=%d)",
349 mlog_file, mlog_line, size);
350 fprintf(fp, "*** %s\n", str);
351 fclose(fp);
352 #else
353 strcpy(str, "Out of memory!");
354 #endif
355 modalfatalbox(str);
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
364 void safefree(void *ptr)
365 {
366 if (ptr) {
367 #ifdef MALLOC_LOG
368 if (fp)
369 fprintf(fp, "free(%p)\n", ptr);
370 #endif
371 #ifdef MINEFIELD
372 minefield_c_free(ptr);
373 #else
374 free(ptr);
375 #endif
376 }
377 #ifdef MALLOC_LOG
378 else if (fp)
379 fprintf(fp, "freeing null pointer - no action taken\n");
380 #endif
381 }
382
383 /* ----------------------------------------------------------------------
384 * Debugging routines.
385 */
386
387 #ifdef DEBUG
388 extern void dputs(char *); /* defined in per-platform *misc.c */
389
390 void debug_printf(char *fmt, ...)
391 {
392 char *buf;
393 va_list ap;
394
395 va_start(ap, fmt);
396 buf = dupvprintf(fmt, ap);
397 dputs(buf);
398 sfree(buf);
399 va_end(ap);
400 }
401
402
403 void debug_memdump(void *buf, int len, int L)
404 {
405 int i;
406 unsigned char *p = buf;
407 char foo[17];
408 if (L) {
409 int delta;
410 debug_printf("\t%d (0x%x) bytes:\n", len, len);
411 delta = 15 & (int) p;
412 p -= delta;
413 len += delta;
414 }
415 for (; 0 < len; p += 16, len -= 16) {
416 dputs(" ");
417 if (L)
418 debug_printf("%p: ", p);
419 strcpy(foo, "................"); /* sixteen dots */
420 for (i = 0; i < 16 && i < len; ++i) {
421 if (&p[i] < (unsigned char *) buf) {
422 dputs(" "); /* 3 spaces */
423 foo[i] = ' ';
424 } else {
425 debug_printf("%c%02.2x",
426 &p[i] != (unsigned char *) buf
427 && i % 4 ? '.' : ' ', p[i]
428 );
429 if (p[i] >= ' ' && p[i] <= '~')
430 foo[i] = (char) p[i];
431 }
432 }
433 foo[i] = '\0';
434 debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo);
435 }
436 }
437
438 #endif /* def DEBUG */