Update documentation about commit message snippet
[TortoiseGit.git] / src / TortoisePlink / MISC.C
blobd652b3948224ed7e71b195c8ade084a218f22568
1 /*\r
2  * Platform-independent routines shared between all PuTTY programs.\r
3  */\r
4 \r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 #include <stdarg.h>\r
8 #include <limits.h>\r
9 #include <ctype.h>\r
10 #include <assert.h>\r
11 #include "putty.h"\r
13 /*\r
14  * Parse a string block size specification. This is approximately a\r
15  * subset of the block size specs supported by GNU fileutils:\r
16  *  "nk" = n kilobytes\r
17  *  "nM" = n megabytes\r
18  *  "nG" = n gigabytes\r
19  * All numbers are decimal, and suffixes refer to powers of two.\r
20  * Case-insensitive.\r
21  */\r
22 unsigned long parse_blocksize(const char *bs)\r
23 {\r
24     char *suf;\r
25     unsigned long r = strtoul(bs, &suf, 10);\r
26     if (*suf != '\0') {\r
27         while (*suf && isspace((unsigned char)*suf)) suf++;\r
28         switch (*suf) {\r
29           case 'k': case 'K':\r
30             r *= 1024ul;\r
31             break;\r
32           case 'm': case 'M':\r
33             r *= 1024ul * 1024ul;\r
34             break;\r
35           case 'g': case 'G':\r
36             r *= 1024ul * 1024ul * 1024ul;\r
37             break;\r
38           case '\0':\r
39           default:\r
40             break;\r
41         }\r
42     }\r
43     return r;\r
44 }\r
46 /*\r
47  * Parse a ^C style character specification.\r
48  * Returns NULL in `next' if we didn't recognise it as a control character,\r
49  * in which case `c' should be ignored.\r
50  * The precise current parsing is an oddity inherited from the terminal\r
51  * answerback-string parsing code. All sequences start with ^; all except\r
52  * ^<123> are two characters. The ones that are worth keeping are probably:\r
53  *   ^?             127\r
54  *   ^@A-Z[\]^_     0-31\r
55  *   a-z            1-26\r
56  *   <num>          specified by number (decimal, 0octal, 0xHEX)\r
57  *   ~              ^ escape\r
58  */\r
59 char ctrlparse(char *s, char **next)\r
60 {\r
61     char c = 0;\r
62     if (*s != '^') {\r
63         *next = NULL;\r
64     } else {\r
65         s++;\r
66         if (*s == '\0') {\r
67             *next = NULL;\r
68         } else if (*s == '<') {\r
69             s++;\r
70             c = (char)strtol(s, next, 0);\r
71             if ((*next == s) || (**next != '>')) {\r
72                 c = 0;\r
73                 *next = NULL;\r
74             } else\r
75                 (*next)++;\r
76         } else if (*s >= 'a' && *s <= 'z') {\r
77             c = (*s - ('a' - 1));\r
78             *next = s+1;\r
79         } else if ((*s >= '@' && *s <= '_') || *s == '?' || (*s & 0x80)) {\r
80             c = ('@' ^ *s);\r
81             *next = s+1;\r
82         } else if (*s == '~') {\r
83             c = '^';\r
84             *next = s+1;\r
85         }\r
86     }\r
87     return c;\r
88 }\r
90 prompts_t *new_prompts(void *frontend)\r
91 {\r
92     prompts_t *p = snew(prompts_t);\r
93     p->prompts = NULL;\r
94     p->n_prompts = 0;\r
95     p->frontend = frontend;\r
96     p->data = NULL;\r
97     p->to_server = TRUE; /* to be on the safe side */\r
98     p->name = p->instruction = NULL;\r
99     p->name_reqd = p->instr_reqd = FALSE;\r
100     return p;\r
102 void add_prompt(prompts_t *p, char *promptstr, int echo)\r
104     prompt_t *pr = snew(prompt_t);\r
105     pr->prompt = promptstr;\r
106     pr->echo = echo;\r
107     pr->result = NULL;\r
108     pr->resultsize = 0;\r
109     p->n_prompts++;\r
110     p->prompts = sresize(p->prompts, p->n_prompts, prompt_t *);\r
111     p->prompts[p->n_prompts-1] = pr;\r
113 void prompt_ensure_result_size(prompt_t *pr, int newlen)\r
115     if ((int)pr->resultsize < newlen) {\r
116         char *newbuf;\r
117         newlen = newlen * 5 / 4 + 512; /* avoid too many small allocs */\r
119         /*\r
120          * We don't use sresize / realloc here, because we will be\r
121          * storing sensitive stuff like passwords in here, and we want\r
122          * to make sure that the data doesn't get copied around in\r
123          * memory without the old copy being destroyed.\r
124          */\r
125         newbuf = snewn(newlen, char);\r
126         memcpy(newbuf, pr->result, pr->resultsize);\r
127         smemclr(pr->result, pr->resultsize);\r
128         sfree(pr->result);\r
129         pr->result = newbuf;\r
130         pr->resultsize = newlen;\r
131     }\r
133 void prompt_set_result(prompt_t *pr, const char *newstr)\r
135     prompt_ensure_result_size(pr, strlen(newstr) + 1);\r
136     strcpy(pr->result, newstr);\r
138 void free_prompts(prompts_t *p)\r
140     size_t i;\r
141     for (i=0; i < p->n_prompts; i++) {\r
142         prompt_t *pr = p->prompts[i];\r
143         smemclr(pr->result, pr->resultsize); /* burn the evidence */\r
144         sfree(pr->result);\r
145         sfree(pr->prompt);\r
146         sfree(pr);\r
147     }\r
148     sfree(p->prompts);\r
149     sfree(p->name);\r
150     sfree(p->instruction);\r
151     sfree(p);\r
154 /* ----------------------------------------------------------------------\r
155  * String handling routines.\r
156  */\r
158 char *dupstr(const char *s)\r
160     char *p = NULL;\r
161     if (s) {\r
162         int len = strlen(s);\r
163         p = snewn(len + 1, char);\r
164         strcpy(p, s);\r
165     }\r
166     return p;\r
169 /* Allocate the concatenation of N strings. Terminate arg list with NULL. */\r
170 char *dupcat(const char *s1, ...)\r
172     int len;\r
173     char *p, *q, *sn;\r
174     va_list ap;\r
176     len = strlen(s1);\r
177     va_start(ap, s1);\r
178     while (1) {\r
179         sn = va_arg(ap, char *);\r
180         if (!sn)\r
181             break;\r
182         len += strlen(sn);\r
183     }\r
184     va_end(ap);\r
186     p = snewn(len + 1, char);\r
187     strcpy(p, s1);\r
188     q = p + strlen(p);\r
190     va_start(ap, s1);\r
191     while (1) {\r
192         sn = va_arg(ap, char *);\r
193         if (!sn)\r
194             break;\r
195         strcpy(q, sn);\r
196         q += strlen(q);\r
197     }\r
198     va_end(ap);\r
200     return p;\r
203 void burnstr(char *string)             /* sfree(str), only clear it first */\r
205     if (string) {\r
206         smemclr(string, strlen(string));\r
207         sfree(string);\r
208     }\r
211 int toint(unsigned u)\r
213     /*\r
214      * Convert an unsigned to an int, without running into the\r
215      * undefined behaviour which happens by the strict C standard if\r
216      * the value overflows. You'd hope that sensible compilers would\r
217      * do the sensible thing in response to a cast, but actually I\r
218      * don't trust modern compilers not to do silly things like\r
219      * assuming that _obviously_ you wouldn't have caused an overflow\r
220      * and so they can elide an 'if (i < 0)' test immediately after\r
221      * the cast.\r
222      *\r
223      * Sensible compilers ought of course to optimise this entire\r
224      * function into 'just return the input value'!\r
225      */\r
226     if (u <= (unsigned)INT_MAX)\r
227         return (int)u;\r
228     else if (u >= (unsigned)INT_MIN)   /* wrap in cast _to_ unsigned is OK */\r
229         return INT_MIN + (int)(u - (unsigned)INT_MIN);\r
230     else\r
231         return INT_MIN; /* fallback; should never occur on binary machines */\r
234 /*\r
235  * Do an sprintf(), but into a custom-allocated buffer.\r
236  * \r
237  * Currently I'm doing this via vsnprintf. This has worked so far,\r
238  * but it's not good, because vsnprintf is not available on all\r
239  * platforms. There's an ifdef to use `_vsnprintf', which seems\r
240  * to be the local name for it on Windows. Other platforms may\r
241  * lack it completely, in which case it'll be time to rewrite\r
242  * this function in a totally different way.\r
243  * \r
244  * The only `properly' portable solution I can think of is to\r
245  * implement my own format string scanner, which figures out an\r
246  * upper bound for the length of each formatting directive,\r
247  * allocates the buffer as it goes along, and calls sprintf() to\r
248  * actually process each directive. If I ever need to actually do\r
249  * this, some caveats:\r
250  * \r
251  *  - It's very hard to find a reliable upper bound for\r
252  *    floating-point values. %f, in particular, when supplied with\r
253  *    a number near to the upper or lower limit of representable\r
254  *    numbers, could easily take several hundred characters. It's\r
255  *    probably feasible to predict this statically using the\r
256  *    constants in <float.h>, or even to predict it dynamically by\r
257  *    looking at the exponent of the specific float provided, but\r
258  *    it won't be fun.\r
259  * \r
260  *  - Don't forget to _check_, after calling sprintf, that it's\r
261  *    used at most the amount of space we had available.\r
262  * \r
263  *  - Fault any formatting directive we don't fully understand. The\r
264  *    aim here is to _guarantee_ that we never overflow the buffer,\r
265  *    because this is a security-critical function. If we see a\r
266  *    directive we don't know about, we should panic and die rather\r
267  *    than run any risk.\r
268  */\r
269 char *dupprintf(const char *fmt, ...)\r
271     char *ret;\r
272     va_list ap;\r
273     va_start(ap, fmt);\r
274     ret = dupvprintf(fmt, ap);\r
275     va_end(ap);\r
276     return ret;\r
278 char *dupvprintf(const char *fmt, va_list ap)\r
280     char *buf;\r
281     int len, size;\r
283     buf = snewn(512, char);\r
284     size = 512;\r
286     while (1) {\r
287 #ifdef _WINDOWS\r
288 #define vsnprintf _vsnprintf\r
289 #endif\r
290 #ifdef va_copy\r
291         /* Use the `va_copy' macro mandated by C99, if present.\r
292          * XXX some environments may have this as __va_copy() */\r
293         va_list aq;\r
294         va_copy(aq, ap);\r
295         len = vsnprintf(buf, size, fmt, aq);\r
296         va_end(aq);\r
297 #else\r
298         /* Ugh. No va_copy macro, so do something nasty.\r
299          * Technically, you can't reuse a va_list like this: it is left\r
300          * unspecified whether advancing a va_list pointer modifies its\r
301          * value or something it points to, so on some platforms calling\r
302          * vsnprintf twice on the same va_list might fail hideously\r
303          * (indeed, it has been observed to).\r
304          * XXX the autoconf manual suggests that using memcpy() will give\r
305          *     "maximum portability". */\r
306         len = vsnprintf(buf, size, fmt, ap);\r
307 #endif\r
308         if (len >= 0 && len < size) {\r
309             /* This is the C99-specified criterion for snprintf to have\r
310              * been completely successful. */\r
311             return buf;\r
312         } else if (len > 0) {\r
313             /* This is the C99 error condition: the returned length is\r
314              * the required buffer size not counting the NUL. */\r
315             size = len + 1;\r
316         } else {\r
317             /* This is the pre-C99 glibc error condition: <0 means the\r
318              * buffer wasn't big enough, so we enlarge it a bit and hope. */\r
319             size += 512;\r
320         }\r
321         buf = sresize(buf, size, char);\r
322     }\r
325 /*\r
326  * Read an entire line of text from a file. Return a buffer\r
327  * malloced to be as big as necessary (caller must free).\r
328  */\r
329 char *fgetline(FILE *fp)\r
331     char *ret = snewn(512, char);\r
332     int size = 512, len = 0;\r
333     while (fgets(ret + len, size - len, fp)) {\r
334         len += strlen(ret + len);\r
335         if (ret[len-1] == '\n')\r
336             break;                     /* got a newline, we're done */\r
337         size = len + 512;\r
338         ret = sresize(ret, size, char);\r
339     }\r
340     if (len == 0) {                    /* first fgets returned NULL */\r
341         sfree(ret);\r
342         return NULL;\r
343     }\r
344     ret[len] = '\0';\r
345     return ret;\r
348 /* ----------------------------------------------------------------------\r
349  * Base64 encoding routine. This is required in public-key writing\r
350  * but also in HTTP proxy handling, so it's centralised here.\r
351  */\r
353 void base64_encode_atom(unsigned char *data, int n, char *out)\r
355     static const char base64_chars[] =\r
356         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";\r
358     unsigned word;\r
360     word = data[0] << 16;\r
361     if (n > 1)\r
362         word |= data[1] << 8;\r
363     if (n > 2)\r
364         word |= data[2];\r
365     out[0] = base64_chars[(word >> 18) & 0x3F];\r
366     out[1] = base64_chars[(word >> 12) & 0x3F];\r
367     if (n > 1)\r
368         out[2] = base64_chars[(word >> 6) & 0x3F];\r
369     else\r
370         out[2] = '=';\r
371     if (n > 2)\r
372         out[3] = base64_chars[word & 0x3F];\r
373     else\r
374         out[3] = '=';\r
377 /* ----------------------------------------------------------------------\r
378  * Generic routines to deal with send buffers: a linked list of\r
379  * smallish blocks, with the operations\r
380  * \r
381  *  - add an arbitrary amount of data to the end of the list\r
382  *  - remove the first N bytes from the list\r
383  *  - return a (pointer,length) pair giving some initial data in\r
384  *    the list, suitable for passing to a send or write system\r
385  *    call\r
386  *  - retrieve a larger amount of initial data from the list\r
387  *  - return the current size of the buffer chain in bytes\r
388  */\r
390 #define BUFFER_MIN_GRANULE  512\r
392 struct bufchain_granule {\r
393     struct bufchain_granule *next;\r
394     char *bufpos, *bufend, *bufmax;\r
395 };\r
397 void bufchain_init(bufchain *ch)\r
399     ch->head = ch->tail = NULL;\r
400     ch->buffersize = 0;\r
403 void bufchain_clear(bufchain *ch)\r
405     struct bufchain_granule *b;\r
406     while (ch->head) {\r
407         b = ch->head;\r
408         ch->head = ch->head->next;\r
409         sfree(b);\r
410     }\r
411     ch->tail = NULL;\r
412     ch->buffersize = 0;\r
415 int bufchain_size(bufchain *ch)\r
417     return ch->buffersize;\r
420 void bufchain_add(bufchain *ch, const void *data, int len)\r
422     const char *buf = (const char *)data;\r
424     if (len == 0) return;\r
426     ch->buffersize += len;\r
428     while (len > 0) {\r
429         if (ch->tail && ch->tail->bufend < ch->tail->bufmax) {\r
430             int copylen = min(len, ch->tail->bufmax - ch->tail->bufend);\r
431             memcpy(ch->tail->bufend, buf, copylen);\r
432             buf += copylen;\r
433             len -= copylen;\r
434             ch->tail->bufend += copylen;\r
435         }\r
436         if (len > 0) {\r
437             int grainlen =\r
438                 max(sizeof(struct bufchain_granule) + len, BUFFER_MIN_GRANULE);\r
439             struct bufchain_granule *newbuf;\r
440             newbuf = smalloc(grainlen);\r
441             newbuf->bufpos = newbuf->bufend =\r
442                 (char *)newbuf + sizeof(struct bufchain_granule);\r
443             newbuf->bufmax = (char *)newbuf + grainlen;\r
444             newbuf->next = NULL;\r
445             if (ch->tail)\r
446                 ch->tail->next = newbuf;\r
447             else\r
448                 ch->head = newbuf;\r
449             ch->tail = newbuf;\r
450         }\r
451     }\r
454 void bufchain_consume(bufchain *ch, int len)\r
456     struct bufchain_granule *tmp;\r
458     assert(ch->buffersize >= len);\r
459     while (len > 0) {\r
460         int remlen = len;\r
461         assert(ch->head != NULL);\r
462         if (remlen >= ch->head->bufend - ch->head->bufpos) {\r
463             remlen = ch->head->bufend - ch->head->bufpos;\r
464             tmp = ch->head;\r
465             ch->head = tmp->next;\r
466             if (!ch->head)\r
467                 ch->tail = NULL;\r
468             sfree(tmp);\r
469         } else\r
470             ch->head->bufpos += remlen;\r
471         ch->buffersize -= remlen;\r
472         len -= remlen;\r
473     }\r
476 void bufchain_prefix(bufchain *ch, void **data, int *len)\r
478     *len = ch->head->bufend - ch->head->bufpos;\r
479     *data = ch->head->bufpos;\r
482 void bufchain_fetch(bufchain *ch, void *data, int len)\r
484     struct bufchain_granule *tmp;\r
485     char *data_c = (char *)data;\r
487     tmp = ch->head;\r
489     assert(ch->buffersize >= len);\r
490     while (len > 0) {\r
491         int remlen = len;\r
493         assert(tmp != NULL);\r
494         if (remlen >= tmp->bufend - tmp->bufpos)\r
495             remlen = tmp->bufend - tmp->bufpos;\r
496         memcpy(data_c, tmp->bufpos, remlen);\r
498         tmp = tmp->next;\r
499         len -= remlen;\r
500         data_c += remlen;\r
501     }\r
504 /* ----------------------------------------------------------------------\r
505  * My own versions of malloc, realloc and free. Because I want\r
506  * malloc and realloc to bomb out and exit the program if they run\r
507  * out of memory, realloc to reliably call malloc if passed a NULL\r
508  * pointer, and free to reliably do nothing if passed a NULL\r
509  * pointer. We can also put trace printouts in, if we need to; and\r
510  * we can also replace the allocator with an ElectricFence-like\r
511  * one.\r
512  */\r
514 #ifdef MINEFIELD\r
515 void *minefield_c_malloc(size_t size);\r
516 void minefield_c_free(void *p);\r
517 void *minefield_c_realloc(void *p, size_t size);\r
518 #endif\r
520 #ifdef MALLOC_LOG\r
521 static FILE *fp = NULL;\r
523 static char *mlog_file = NULL;\r
524 static int mlog_line = 0;\r
526 void mlog(char *file, int line)\r
528     mlog_file = file;\r
529     mlog_line = line;\r
530     if (!fp) {\r
531         fp = fopen("putty_mem.log", "w");\r
532         setvbuf(fp, NULL, _IONBF, BUFSIZ);\r
533     }\r
534     if (fp)\r
535         fprintf(fp, "%s:%d: ", file, line);\r
537 #endif\r
539 void *safemalloc(size_t n, size_t size)\r
541     void *p;\r
543     if (n > INT_MAX / size) {\r
544         p = NULL;\r
545     } else {\r
546         size *= n;\r
547         if (size == 0) size = 1;\r
548 #ifdef MINEFIELD\r
549         p = minefield_c_malloc(size);\r
550 #else\r
551         p = malloc(size);\r
552 #endif\r
553     }\r
555     if (!p) {\r
556         char str[200];\r
557 #ifdef MALLOC_LOG\r
558         sprintf(str, "Out of memory! (%s:%d, size=%d)",\r
559                 mlog_file, mlog_line, size);\r
560         fprintf(fp, "*** %s\n", str);\r
561         fclose(fp);\r
562 #else\r
563         strcpy(str, "Out of memory!");\r
564 #endif\r
565         modalfatalbox(str);\r
566     }\r
567 #ifdef MALLOC_LOG\r
568     if (fp)\r
569         fprintf(fp, "malloc(%d) returns %p\n", size, p);\r
570 #endif\r
571     return p;\r
574 void *saferealloc(void *ptr, size_t n, size_t size)\r
576     void *p;\r
578     if (n > INT_MAX / size) {\r
579         p = NULL;\r
580     } else {\r
581         size *= n;\r
582         if (!ptr) {\r
583 #ifdef MINEFIELD\r
584             p = minefield_c_malloc(size);\r
585 #else\r
586             p = malloc(size);\r
587 #endif\r
588         } else {\r
589 #ifdef MINEFIELD\r
590             p = minefield_c_realloc(ptr, size);\r
591 #else\r
592             p = realloc(ptr, size);\r
593 #endif\r
594         }\r
595     }\r
597     if (!p) {\r
598         char str[200];\r
599 #ifdef MALLOC_LOG\r
600         sprintf(str, "Out of memory! (%s:%d, size=%d)",\r
601                 mlog_file, mlog_line, size);\r
602         fprintf(fp, "*** %s\n", str);\r
603         fclose(fp);\r
604 #else\r
605         strcpy(str, "Out of memory!");\r
606 #endif\r
607         modalfatalbox(str);\r
608     }\r
609 #ifdef MALLOC_LOG\r
610     if (fp)\r
611         fprintf(fp, "realloc(%p,%d) returns %p\n", ptr, size, p);\r
612 #endif\r
613     return p;\r
616 void safefree(void *ptr)\r
618     if (ptr) {\r
619 #ifdef MALLOC_LOG\r
620         if (fp)\r
621             fprintf(fp, "free(%p)\n", ptr);\r
622 #endif\r
623 #ifdef MINEFIELD\r
624         minefield_c_free(ptr);\r
625 #else\r
626         free(ptr);\r
627 #endif\r
628     }\r
629 #ifdef MALLOC_LOG\r
630     else if (fp)\r
631         fprintf(fp, "freeing null pointer - no action taken\n");\r
632 #endif\r
635 /* ----------------------------------------------------------------------\r
636  * Debugging routines.\r
637  */\r
639 #ifdef DEBUG\r
640 extern void dputs(char *);             /* defined in per-platform *misc.c */\r
642 void debug_printf(char *fmt, ...)\r
644     char *buf;\r
645     va_list ap;\r
647     va_start(ap, fmt);\r
648     buf = dupvprintf(fmt, ap);\r
649     dputs(buf);\r
650     sfree(buf);\r
651     va_end(ap);\r
655 void debug_memdump(void *buf, int len, int L)\r
657     int i;\r
658     unsigned char *p = buf;\r
659     char foo[17];\r
660     if (L) {\r
661         int delta;\r
662         debug_printf("\t%d (0x%x) bytes:\n", len, len);\r
663         delta = 15 & (unsigned long int) p;\r
664         p -= delta;\r
665         len += delta;\r
666     }\r
667     for (; 0 < len; p += 16, len -= 16) {\r
668         dputs("  ");\r
669         if (L)\r
670             debug_printf("%p: ", p);\r
671         strcpy(foo, "................");        /* sixteen dots */\r
672         for (i = 0; i < 16 && i < len; ++i) {\r
673             if (&p[i] < (unsigned char *) buf) {\r
674                 dputs("   ");          /* 3 spaces */\r
675                 foo[i] = ' ';\r
676             } else {\r
677                 debug_printf("%c%02.2x",\r
678                         &p[i] != (unsigned char *) buf\r
679                         && i % 4 ? '.' : ' ', p[i]\r
680                     );\r
681                 if (p[i] >= ' ' && p[i] <= '~')\r
682                     foo[i] = (char) p[i];\r
683             }\r
684         }\r
685         foo[i] = '\0';\r
686         debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo);\r
687     }\r
690 #endif                          /* def DEBUG */\r
692 /*\r
693  * Determine whether or not a Conf represents a session which can\r
694  * sensibly be launched right now.\r
695  */\r
696 int conf_launchable(Conf *conf)\r
698     if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)\r
699         return conf_get_str(conf, CONF_serline)[0] != 0;\r
700     else\r
701         return conf_get_str(conf, CONF_host)[0] != 0;\r
704 char const *conf_dest(Conf *conf)\r
706     if (conf_get_int(conf, CONF_protocol) == PROT_SERIAL)\r
707         return conf_get_str(conf, CONF_serline);\r
708     else\r
709         return conf_get_str(conf, CONF_host);\r
712 #ifndef PLATFORM_HAS_SMEMCLR\r
713 /*\r
714  * Securely wipe memory.\r
715  *\r
716  * The actual wiping is no different from what memset would do: the\r
717  * point of 'securely' is to try to be sure over-clever compilers\r
718  * won't optimise away memsets on variables that are about to be freed\r
719  * or go out of scope. See\r
720  * https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/771-BSI.html\r
721  *\r
722  * Some platforms (e.g. Windows) may provide their own version of this\r
723  * function.\r
724  */\r
725 void smemclr(void *b, size_t n) {\r
726     volatile char *vp;\r
728     if (b && n > 0) {\r
729         /*\r
730          * Zero out the memory.\r
731          */\r
732         memset(b, 0, n);\r
734         /*\r
735          * Perform a volatile access to the object, forcing the\r
736          * compiler to admit that the previous memset was important.\r
737          *\r
738          * This while loop should in practice run for zero iterations\r
739          * (since we know we just zeroed the object out), but in\r
740          * theory (as far as the compiler knows) it might range over\r
741          * the whole object. (If we had just written, say, '*vp =\r
742          * *vp;', a compiler could in principle have 'helpfully'\r
743          * optimised the memset into only zeroing out the first byte.\r
744          * This should be robust.)\r
745          */\r
746         vp = b;\r
747         while (*vp) vp++;\r
748     }\r
750 #endif\r