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