1: /*
2: * Copyright (c) 2007-2013 Todd C. Miller <Todd.Miller@courtesan.com>
3: *
4: * Permission to use, copy, modify, and distribute this software for any
5: * purpose with or without fee is hereby granted, provided that the above
6: * copyright notice and this permission notice appear in all copies.
7: *
8: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
16: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17: */
18:
19: #include <config.h>
20:
21: #include <sys/types.h>
22: #include <stdio.h>
23: #ifdef STDC_HEADERS
24: # include <stdlib.h>
25: # include <stddef.h>
26: #else
27: # ifdef HAVE_STDLIB_H
28: # include <stdlib.h>
29: # endif
30: #endif /* STDC_HEADERS */
31: #ifdef HAVE_STRING_H
32: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
33: # include <memory.h>
34: # endif
35: # include <string.h>
36: #endif /* HAVE_STRING_H */
37: #ifdef HAVE_STRINGS_H
38: # include <strings.h>
39: #endif /* HAVE_STRINGS_H */
40: #ifdef HAVE_UNISTD_H
41: # include <unistd.h>
42: #endif /* HAVE_UNISTD_H */
43: #include <ctype.h>
44:
45: #include "missing.h"
46: #include "alloc.h"
47: #include "fatal.h"
48: #include "lbuf.h"
49: #include "sudo_debug.h"
50:
51: void
52: lbuf_init(struct lbuf *lbuf, int (*output)(const char *),
53: int indent, const char *continuation, int cols)
54: {
55: debug_decl(lbuf_init, SUDO_DEBUG_UTIL)
56:
57: lbuf->output = output;
58: lbuf->continuation = continuation;
59: lbuf->indent = indent;
60: lbuf->cols = cols;
61: lbuf->len = 0;
62: lbuf->size = 0;
63: lbuf->buf = NULL;
64:
65: debug_return;
66: }
67:
68: void
69: lbuf_destroy(struct lbuf *lbuf)
70: {
71: debug_decl(lbuf_destroy, SUDO_DEBUG_UTIL)
72:
73: efree(lbuf->buf);
74: lbuf->buf = NULL;
75:
76: debug_return;
77: }
78:
79: static void
80: lbuf_expand(struct lbuf *lbuf, size_t extra)
81: {
82: if (lbuf->len + extra + 1 >= lbuf->size) {
83: do {
84: lbuf->size += 256;
85: } while (lbuf->len + extra + 1 >= lbuf->size);
86: lbuf->buf = erealloc(lbuf->buf, lbuf->size);
87: }
88: }
89:
90: /*
91: * Parse the format and append strings, only %s and %% escapes are supported.
92: * Any characters in set are quoted with a backslash.
93: */
94: void
95: lbuf_append_quoted(struct lbuf *lbuf, const char *set, const char *fmt, ...)
96: {
97: va_list ap;
98: int len;
99: char *cp, *s;
100: debug_decl(lbuf_append_quoted, SUDO_DEBUG_UTIL)
101:
102: va_start(ap, fmt);
103: while (*fmt != '\0') {
104: if (fmt[0] == '%' && fmt[1] == 's') {
105: if ((s = va_arg(ap, char *)) == NULL)
106: goto done;
107: while ((cp = strpbrk(s, set)) != NULL) {
108: len = (int)(cp - s);
109: lbuf_expand(lbuf, len + 2);
110: memcpy(lbuf->buf + lbuf->len, s, len);
111: lbuf->len += len;
112: lbuf->buf[lbuf->len++] = '\\';
113: lbuf->buf[lbuf->len++] = *cp;
114: s = cp + 1;
115: }
116: if (*s != '\0') {
117: len = strlen(s);
118: lbuf_expand(lbuf, len);
119: memcpy(lbuf->buf + lbuf->len, s, len);
120: lbuf->len += len;
121: }
122: fmt += 2;
123: continue;
124: }
125: lbuf_expand(lbuf, 2);
126: if (strchr(set, *fmt) != NULL)
127: lbuf->buf[lbuf->len++] = '\\';
128: lbuf->buf[lbuf->len++] = *fmt++;
129: }
130: done:
131: if (lbuf->size != 0)
132: lbuf->buf[lbuf->len] = '\0';
133: va_end(ap);
134:
135: debug_return;
136: }
137:
138: /*
139: * Parse the format and append strings, only %s and %% escapes are supported.
140: */
141: void
142: lbuf_append(struct lbuf *lbuf, const char *fmt, ...)
143: {
144: va_list ap;
145: int len;
146: char *s;
147: debug_decl(lbuf_append, SUDO_DEBUG_UTIL)
148:
149: va_start(ap, fmt);
150: while (*fmt != '\0') {
151: if (fmt[0] == '%' && fmt[1] == 's') {
152: if ((s = va_arg(ap, char *)) == NULL)
153: goto done;
154: len = strlen(s);
155: lbuf_expand(lbuf, len);
156: memcpy(lbuf->buf + lbuf->len, s, len);
157: lbuf->len += len;
158: fmt += 2;
159: continue;
160: }
161: lbuf_expand(lbuf, 1);
162: lbuf->buf[lbuf->len++] = *fmt++;
163: }
164: done:
165: if (lbuf->size != 0)
166: lbuf->buf[lbuf->len] = '\0';
167: va_end(ap);
168:
169: debug_return;
170: }
171:
172: static void
173: lbuf_println(struct lbuf *lbuf, char *line, int len)
174: {
175: char *cp, save;
176: int i, have, contlen;
177: debug_decl(lbuf_println, SUDO_DEBUG_UTIL)
178:
179: contlen = lbuf->continuation ? strlen(lbuf->continuation) : 0;
180:
181: /*
182: * Print the buffer, splitting the line as needed on a word
183: * boundary.
184: */
185: cp = line;
186: have = lbuf->cols;
187: while (cp != NULL && *cp != '\0') {
188: char *ep = NULL;
189: int need = len - (int)(cp - line);
190:
191: if (need > have) {
192: have -= contlen; /* subtract for continuation char */
193: if ((ep = memrchr(cp, ' ', have)) == NULL)
194: ep = memchr(cp + have, ' ', need - have);
195: if (ep != NULL)
196: need = (int)(ep - cp);
197: }
198: if (cp != line) {
199: /* indent continued lines */
200: /* XXX - build up string instead? */
201: for (i = 0; i < lbuf->indent; i++)
202: lbuf->output(" ");
203: }
204: /* NUL-terminate cp for the output function and restore afterwards */
205: save = cp[need];
206: cp[need] = '\0';
207: lbuf->output(cp);
208: cp[need] = save;
209: cp = ep;
210:
211: /*
212: * If there is more to print, reset have, incremement cp past
213: * the whitespace, and print a line continuaton char if needed.
214: */
215: if (cp != NULL) {
216: have = lbuf->cols - lbuf->indent;
217: ep = line + len;
218: while (cp < ep && isblank((unsigned char)*cp)) {
219: cp++;
220: }
221: if (contlen)
222: lbuf->output(lbuf->continuation);
223: }
224: lbuf->output("\n");
225: }
226:
227: debug_return;
228: }
229:
230: /*
231: * Print the buffer with word wrap based on the tty width.
232: * The lbuf is reset on return.
233: */
234: void
235: lbuf_print(struct lbuf *lbuf)
236: {
237: char *cp, *ep;
238: int len;
239: debug_decl(lbuf_print, SUDO_DEBUG_UTIL)
240:
241: if (lbuf->buf == NULL || lbuf->len == 0)
242: goto done;
243:
244: /* For very small widths just give up... */
245: len = lbuf->continuation ? strlen(lbuf->continuation) : 0;
246: if (lbuf->cols <= lbuf->indent + len + 20) {
247: if (lbuf->len > 0) {
248: lbuf->buf[lbuf->len] = '\0';
249: lbuf->output(lbuf->buf);
250: if (lbuf->buf[lbuf->len - 1] != '\n')
251: lbuf->output("\n");
252: }
253: goto done;
254: }
255:
256: /* Print each line in the buffer */
257: for (cp = lbuf->buf; cp != NULL && *cp != '\0'; ) {
258: if (*cp == '\n') {
259: lbuf->output("\n");
260: cp++;
261: } else {
262: len = lbuf->len - (cp - lbuf->buf);
263: if ((ep = memchr(cp, '\n', len)) != NULL)
264: len = (int)(ep - cp);
265: if (len)
266: lbuf_println(lbuf, cp, len);
267: cp = ep ? ep + 1 : NULL;
268: }
269: }
270:
271: done:
272: lbuf->len = 0; /* reset the buffer for re-use. */
273:
274: debug_return;
275: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>