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