1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* util/support/k5buf.c - string buffer functions */
5 * Copyright 2008 Massachusetts Institute of Technology.
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
29 * Can't include krb5.h here, or k5-int.h which includes it, because krb5.h
30 * needs to be generated with error tables, after util/et, which builds after
33 #include "k5-platform.h"
38 * Structure invariants:
40 * buftype is K5BUF_FIXED, K5BUF_DYNAMIC, K5BUF_DYNAMIC_ZAP, or K5BUF_ERROR
41 * if buftype is K5BUF_ERROR, the other fields are NULL or 0
42 * if buftype is not K5BUF_ERROR:
48 /* Return a character pointer to the current end of buf. */
50 endptr(struct k5buf *buf)
52 return (char *)buf->data + buf->len;
56 set_error(struct k5buf *buf)
58 buf->buftype = K5BUF_ERROR;
60 buf->space = buf->len = 0;
64 * Make sure there is room for LEN more characters in BUF, in addition to the
65 * null terminator and what's already in there. Return true on success. On
66 * failure, set the error flag and return false.
69 ensure_space(struct k5buf *buf, size_t len)
74 if (buf->buftype == K5BUF_ERROR)
76 if (buf->space - 1 - buf->len >= len) /* Enough room already. */
78 if (buf->buftype == K5BUF_FIXED) /* Can't resize a fixed buffer. */
80 assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP);
81 new_space = buf->space * 2;
82 while (new_space - buf->len - 1 < len) {
83 if (new_space > SIZE_MAX / 2)
87 if (buf->buftype == K5BUF_DYNAMIC_ZAP) {
88 /* realloc() could leave behind a partial copy of sensitive data. */
89 new_data = malloc(new_space);
92 memcpy(new_data, buf->data, buf->len);
93 new_data[buf->len] = '\0';
94 zap(buf->data, buf->len);
97 new_data = realloc(buf->data, new_space);
101 buf->data = new_data;
102 buf->space = new_space;
106 if (buf->buftype == K5BUF_DYNAMIC_ZAP)
107 zap(buf->data, buf->len);
108 if (buf->buftype == K5BUF_DYNAMIC_ZAP || buf->buftype == K5BUF_DYNAMIC)
115 k5_buf_init_fixed(struct k5buf *buf, char *data, size_t space)
118 buf->buftype = K5BUF_FIXED;
126 k5_buf_init_dynamic(struct k5buf *buf)
128 buf->buftype = K5BUF_DYNAMIC;
130 buf->data = malloc(buf->space);
131 if (buf->data == NULL) {
140 k5_buf_init_dynamic_zap(struct k5buf *buf)
142 k5_buf_init_dynamic(buf);
143 if (buf->buftype == K5BUF_DYNAMIC)
144 buf->buftype = K5BUF_DYNAMIC_ZAP;
148 k5_buf_add(struct k5buf *buf, const char *data)
150 k5_buf_add_len(buf, data, strlen(data));
154 k5_buf_add_len(struct k5buf *buf, const void *data, size_t len)
156 if (!ensure_space(buf, len))
159 memcpy(endptr(buf), data, len);
165 k5_buf_add_vfmt(struct k5buf *buf, const char *fmt, va_list ap)
172 if (buf->buftype == K5BUF_ERROR)
174 remaining = buf->space - buf->len;
176 if (buf->buftype == K5BUF_FIXED) {
177 /* Format the data directly into the fixed buffer. */
178 r = vsnprintf(endptr(buf), remaining, fmt, ap);
179 if (SNPRINTF_OVERFLOW(r, remaining))
182 buf->len += (unsigned int) r;
186 /* Optimistically format the data directly into the dynamic buffer. */
187 assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP);
189 r = vsnprintf(endptr(buf), remaining, fmt, apcopy);
191 if (!SNPRINTF_OVERFLOW(r, remaining)) {
192 buf->len += (unsigned int) r;
197 /* snprintf correctly told us how much space is required. */
198 if (!ensure_space(buf, r))
200 remaining = buf->space - buf->len;
201 r = vsnprintf(endptr(buf), remaining, fmt, ap);
202 if (SNPRINTF_OVERFLOW(r, remaining)) /* Shouldn't ever happen. */
205 buf->len += (unsigned int) r;
209 /* It's a pre-C99 snprintf implementation, or something else went wrong.
210 * Fall back to asprintf. */
211 r = vasprintf(&tmp, fmt, ap);
216 if (ensure_space(buf, r)) {
217 /* Copy the temporary string into buf, including terminator. */
218 memcpy(endptr(buf), tmp, r + 1);
221 if (buf->buftype == K5BUF_DYNAMIC_ZAP)
222 zap(tmp, strlen(tmp));
227 k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
232 k5_buf_add_vfmt(buf, fmt, ap);
237 k5_buf_get_space(struct k5buf *buf, size_t len)
239 if (!ensure_space(buf, len))
243 return endptr(buf) - len;
247 k5_buf_truncate(struct k5buf *buf, size_t len)
249 if (buf->buftype == K5BUF_ERROR)
251 assert(len <= buf->len);
257 k5_buf_status(struct k5buf *buf)
259 return (buf->buftype == K5BUF_ERROR) ? ENOMEM : 0;
263 k5_buf_free(struct k5buf *buf)
265 if (buf->buftype == K5BUF_ERROR)
267 assert(buf->buftype == K5BUF_DYNAMIC || buf->buftype == K5BUF_DYNAMIC_ZAP);
268 if (buf->buftype == K5BUF_DYNAMIC_ZAP)
269 zap(buf->data, buf->len);