]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - misc.c
Various tweaks to header comments to remind me which bits are meant to be
[PuTTY.git] / 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 */