Imported Upstream version 1.8.7
[debian/sudo] / common / lbuf.c
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 "error.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 }