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