1: /*
2: * Copyright (c) 2007-2011 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 <sys/param.h>
23: #include <stdio.h>
24: #ifdef STDC_HEADERS
25: # include <stdlib.h>
26: # include <stddef.h>
27: #else
28: # ifdef HAVE_STDLIB_H
29: # include <stdlib.h>
30: # endif
31: #endif /* STDC_HEADERS */
32: #ifdef HAVE_STRING_H
33: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
34: # include <memory.h>
35: # endif
36: # include <string.h>
37: #endif /* HAVE_STRING_H */
38: #ifdef HAVE_STRINGS_H
39: # include <strings.h>
40: #endif /* HAVE_STRINGS_H */
41: #ifdef HAVE_UNISTD_H
42: # include <unistd.h>
43: #endif /* HAVE_UNISTD_H */
44: #include <ctype.h>
45:
46: #include "missing.h"
47: #include "alloc.h"
48: #include "error.h"
49: #include "lbuf.h"
50: #include "sudo_debug.h"
51:
52: void
53: lbuf_init(struct lbuf *lbuf, int (*output)(const char *),
54: int indent, const char *continuation, int cols)
55: {
56: debug_decl(lbuf_init, SUDO_DEBUG_UTIL)
57:
58: lbuf->output = output;
59: lbuf->continuation = continuation;
60: lbuf->indent = indent;
61: lbuf->cols = cols;
62: lbuf->len = 0;
63: lbuf->size = 0;
64: lbuf->buf = NULL;
65:
66: debug_return;
67: }
68:
69: void
70: lbuf_destroy(struct lbuf *lbuf)
71: {
72: debug_decl(lbuf_destroy, SUDO_DEBUG_UTIL)
73:
74: efree(lbuf->buf);
75: lbuf->buf = NULL;
76:
77: debug_return;
78: }
79:
80: /*
81: * Parse the format and append strings, only %s and %% escapes are supported.
82: * Any characters in set are quoted with a backslash.
83: */
84: void
85: lbuf_append_quoted(struct lbuf *lbuf, const char *set, const char *fmt, ...)
86: {
87: va_list ap;
88: int len;
89: char *cp, *s = NULL;
90: debug_decl(lbuf_append_quoted, SUDO_DEBUG_UTIL)
91:
92: va_start(ap, fmt);
93: while (*fmt != '\0') {
94: len = 1;
95: if (fmt[0] == '%' && fmt[1] == 's') {
96: s = va_arg(ap, char *);
97: len = strlen(s);
98: }
99: /* Assume worst case that all chars must be escaped. */
100: if (lbuf->len + (len * 2) + 1 >= lbuf->size) {
101: do {
102: lbuf->size += 256;
103: } while (lbuf->len + len + 1 >= lbuf->size);
104: lbuf->buf = erealloc(lbuf->buf, lbuf->size);
105: }
106: if (*fmt == '%') {
107: if (*(++fmt) == 's') {
108: while ((cp = strpbrk(s, set)) != NULL) {
109: len = (int)(cp - s);
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: memcpy(lbuf->buf + lbuf->len, s, len);
119: lbuf->len += len;
120: }
121: fmt++;
122: continue;
123: }
124: }
125: if (strchr(set, *fmt) != NULL)
126: lbuf->buf[lbuf->len++] = '\\';
127: lbuf->buf[lbuf->len++] = *fmt++;
128: }
129: lbuf->buf[lbuf->len] = '\0';
130: va_end(ap);
131:
132: debug_return;
133: }
134:
135: /*
136: * Parse the format and append strings, only %s and %% escapes are supported.
137: */
138: void
139: lbuf_append(struct lbuf *lbuf, const char *fmt, ...)
140: {
141: va_list ap;
142: int len;
143: char *s = NULL;
144: debug_decl(lbuf_append, SUDO_DEBUG_UTIL)
145:
146: va_start(ap, fmt);
147: while (*fmt != '\0') {
148: len = 1;
149: if (fmt[0] == '%' && fmt[1] == 's') {
150: s = va_arg(ap, char *);
151: len = strlen(s);
152: }
153: if (lbuf->len + len + 1 >= lbuf->size) {
154: do {
155: lbuf->size += 256;
156: } while (lbuf->len + len + 1 >= lbuf->size);
157: lbuf->buf = erealloc(lbuf->buf, lbuf->size);
158: }
159: if (*fmt == '%') {
160: if (*(++fmt) == 's') {
161: memcpy(lbuf->buf + lbuf->len, s, len);
162: lbuf->len += len;
163: fmt++;
164: continue;
165: }
166: }
167: lbuf->buf[lbuf->len++] = *fmt++;
168: }
169: lbuf->buf[lbuf->len] = '\0';
170: va_end(ap);
171:
172: debug_return;
173: }
174:
175: static void
176: lbuf_println(struct lbuf *lbuf, char *line, int len)
177: {
178: char *cp, save;
179: int i, have, contlen;
180: debug_decl(lbuf_println, SUDO_DEBUG_UTIL)
181:
182: contlen = lbuf->continuation ? strlen(lbuf->continuation) : 0;
183:
184: /*
185: * Print the buffer, splitting the line as needed on a word
186: * boundary.
187: */
188: cp = line;
189: have = lbuf->cols;
190: while (cp != NULL && *cp != '\0') {
191: char *ep = NULL;
192: int need = len - (int)(cp - line);
193:
194: if (need > have) {
195: have -= contlen; /* subtract for continuation char */
196: if ((ep = memrchr(cp, ' ', have)) == NULL)
197: ep = memchr(cp + have, ' ', need - have);
198: if (ep != NULL)
199: need = (int)(ep - cp);
200: }
201: if (cp != line) {
202: /* indent continued lines */
203: /* XXX - build up string instead? */
204: for (i = 0; i < lbuf->indent; i++)
205: lbuf->output(" ");
206: }
207: /* NUL-terminate cp for the output function and restore afterwards */
208: save = cp[need];
209: cp[need] = '\0';
210: lbuf->output(cp);
211: cp[need] = save;
212: cp = ep;
213:
214: /*
215: * If there is more to print, reset have, incremement cp past
216: * the whitespace, and print a line continuaton char if needed.
217: */
218: if (cp != NULL) {
219: have = lbuf->cols - lbuf->indent;
220: ep = line + len;
221: while (cp < ep && isblank((unsigned char)*cp)) {
222: cp++;
223: }
224: if (contlen)
225: lbuf->output(lbuf->continuation);
226: }
227: lbuf->output("\n");
228: }
229:
230: debug_return;
231: }
232:
233: /*
234: * Print the buffer with word wrap based on the tty width.
235: * The lbuf is reset on return.
236: */
237: void
238: lbuf_print(struct lbuf *lbuf)
239: {
240: char *cp, *ep;
241: int len;
242: debug_decl(lbuf_print, SUDO_DEBUG_UTIL)
243:
244: if (lbuf->buf == NULL || lbuf->len == 0)
245: goto done;
246:
247: /* For very small widths just give up... */
248: len = lbuf->continuation ? strlen(lbuf->continuation) : 0;
249: if (lbuf->cols <= lbuf->indent + len + 20) {
250: lbuf->buf[lbuf->len] = '\0';
251: lbuf->output(lbuf->buf);
252: goto done;
253: }
254:
255: /* Print each line in the buffer */
256: for (cp = lbuf->buf; cp != NULL && *cp != '\0'; ) {
257: if (*cp == '\n') {
258: lbuf->output("\n");
259: cp++;
260: } else {
261: len = lbuf->len - (cp - lbuf->buf);
262: if ((ep = memchr(cp, '\n', len)) != NULL)
263: len = (int)(ep - cp);
264: if (len)
265: lbuf_println(lbuf, cp, len);
266: cp = ep ? ep + 1 : NULL;
267: }
268: }
269:
270: done:
271: lbuf->len = 0; /* reset the buffer for re-use. */
272:
273: debug_return;
274: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>