update changelog
[debian/sudo] / lbuf.c
1 /*
2  * Copyright (c) 2007-2010 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 <sys/ioctl.h>
24 #include <stdio.h>
25 #ifdef STDC_HEADERS
26 # include <stdlib.h>
27 # include <stddef.h>
28 #else
29 # ifdef HAVE_STDLIB_H
30 #  include <stdlib.h>
31 # endif
32 #endif /* STDC_HEADERS */
33 #ifdef HAVE_STRING_H
34 # include <string.h>
35 #endif /* HAVE_STRING_H */
36 #ifdef HAVE_STRINGS_H
37 # include <strings.h>
38 #endif /* HAVE_STRINGS_H */
39 #ifdef HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif /* HAVE_UNISTD_H */
42 #include <ctype.h>
43 #ifdef HAVE_TERMIOS_H
44 # include <termios.h>
45 #else
46 # ifdef HAVE_TERMIO_H
47 #  include <termio.h>
48 # endif
49 #endif
50
51 #include "sudo.h"
52 #include "lbuf.h"
53
54 #if !defined(TIOCGSIZE) && defined(TIOCGWINSZ)
55 # define TIOCGSIZE      TIOCGWINSZ
56 # define ttysize        winsize
57 # define ts_cols        ws_col
58 #endif
59
60 int
61 get_ttycols()
62 {
63     char *p;
64     int cols;
65 #ifdef TIOCGSIZE
66     struct ttysize win;
67
68     if (ioctl(STDERR_FILENO, TIOCGSIZE, &win) == 0 && win.ts_cols != 0)
69         return((int)win.ts_cols);
70 #endif
71
72     /* Fall back on $COLUMNS. */
73     if ((p = getenv("COLUMNS")) == NULL || (cols = atoi(p)) <= 0)
74         cols = 80;
75     return(cols);
76 }
77
78 void
79 lbuf_init(lbuf, output, indent, continuation)
80     struct lbuf *lbuf;
81     int (*output)__P((const char *));
82     int indent;
83     const char *continuation;
84 {
85     lbuf->output = output;
86     lbuf->continuation = continuation;
87     lbuf->indent = indent;
88     lbuf->cols = get_ttycols();
89     lbuf->len = 0;
90     lbuf->size = 0;
91     lbuf->buf = NULL;
92 }
93
94 void
95 lbuf_destroy(lbuf)
96     struct lbuf *lbuf;
97 {
98     efree(lbuf->buf);
99     lbuf->buf = NULL;
100 }
101
102 /*
103  * Append strings to the buffer, expanding it as needed.
104  */
105 void
106 #ifdef __STDC__
107 lbuf_append_quoted(struct lbuf *lbuf, const char *set, ...)
108 #else
109 lbuf_append_quoted(lbuf, set, va_alist)
110         struct lbuf *lbuf;
111         const char *set;
112         va_dcl
113 #endif
114 {
115     va_list ap;
116     int len = 0;
117     char *cp, *s;
118
119 #ifdef __STDC__
120     va_start(ap, set);
121 #else
122     va_start(ap);
123 #endif
124     while ((s = va_arg(ap, char *)) != NULL) {
125         len += strlen(s);
126         for (cp = s; (cp = strpbrk(cp, set)) != NULL; cp++)
127             len++;
128     }
129     va_end(ap);
130
131     /* Expand buffer as needed. */
132     if (lbuf->len + len >= lbuf->size) {
133         do {
134             lbuf->size += 256;
135         } while (lbuf->len + len >= lbuf->size);
136         lbuf->buf = erealloc(lbuf->buf, lbuf->size);
137     }
138
139 #ifdef __STDC__
140     va_start(ap, set);
141 #else
142     va_start(ap);
143 #endif
144     /* Append each string. */
145     while ((s = va_arg(ap, char *)) != NULL) {
146         while ((cp = strpbrk(s, set)) != NULL) {
147             len = (int)(cp - s);
148             memcpy(lbuf->buf + lbuf->len, s, len);
149             lbuf->len += len;
150             lbuf->buf[lbuf->len++] = '\\';
151             lbuf->buf[lbuf->len++] = *cp;
152             s = cp + 1;
153         }
154         if (*s != '\0') {
155             len = strlen(s);
156             memcpy(lbuf->buf + lbuf->len, s, len);
157             lbuf->len += len;
158         }
159     }
160     lbuf->buf[lbuf->len] = '\0';
161     va_end(ap);
162 }
163
164 /*
165  * Append strings to the buffer, expanding it as needed.
166  */
167 void
168 #ifdef __STDC__
169 lbuf_append(struct lbuf *lbuf, ...)
170 #else
171 lbuf_append(lbuf, va_alist)
172         struct lbuf *lbuf;
173         va_dcl
174 #endif
175 {
176     va_list ap;
177     int len = 0;
178     char *s;
179
180 #ifdef __STDC__
181     va_start(ap, lbuf);
182 #else
183     va_start(ap);
184 #endif
185     while ((s = va_arg(ap, char *)) != NULL)
186         len += strlen(s);
187     va_end(ap);
188
189     /* Expand buffer as needed. */
190     if (lbuf->len + len >= lbuf->size) {
191         do {
192             lbuf->size += 256;
193         } while (lbuf->len + len >= lbuf->size);
194         lbuf->buf = erealloc(lbuf->buf, lbuf->size);
195     }
196
197 #ifdef __STDC__
198     va_start(ap, lbuf);
199 #else
200     va_start(ap);
201 #endif
202     /* Append each string. */
203     while ((s = va_arg(ap, char *)) != NULL) {
204         len = strlen(s);
205         memcpy(lbuf->buf + lbuf->len, s, len);
206         lbuf->len += len;
207     }
208     lbuf->buf[lbuf->len] = '\0';
209     va_end(ap);
210 }
211
212 static void
213 lbuf_println(lbuf, line, len)
214     struct lbuf *lbuf;
215     char *line;
216     int len;
217 {
218     char *cp, save;
219     int i, have, contlen;
220
221     contlen = lbuf->continuation ? strlen(lbuf->continuation) : 0;
222
223     /*
224      * Print the buffer, splitting the line as needed on a word
225      * boundary.
226      */
227     cp = line;
228     have = lbuf->cols;
229     while (cp != NULL && *cp != '\0') {
230         char *ep = NULL;
231         int need = len - (int)(cp - line);
232
233         if (need > have) {
234             have -= contlen;            /* subtract for continuation char */
235             if ((ep = memrchr(cp, ' ', have)) == NULL)
236                 ep = memchr(cp + have, ' ', need - have);
237             if (ep != NULL)
238                 need = (int)(ep - cp);
239         }
240         if (cp != line) {
241             /* indent continued lines */
242             /* XXX - build up string instead? */
243             for (i = 0; i < lbuf->indent; i++)
244                 lbuf->output(" ");
245         }
246         /* NUL-terminate cp for the output function and restore afterwards */
247         save = cp[need];
248         cp[need] = '\0';
249         lbuf->output(cp);
250         cp[need] = save;
251         cp = ep;
252
253         /*
254          * If there is more to print, reset have, incremement cp past
255          * the whitespace, and print a line continuaton char if needed.
256          */
257         if (cp != NULL) {
258             have = lbuf->cols - lbuf->indent;
259             ep = line + len;
260             while (cp < ep && isblank((unsigned char)*cp)) {
261                 cp++;
262             }
263             if (contlen)
264                 lbuf->output(lbuf->continuation);
265         }
266         lbuf->output("\n");
267     }
268 }
269
270 /*
271  * Print the buffer with word wrap based on the tty width.
272  * The lbuf is reset on return.
273  */
274 void
275 lbuf_print(lbuf)
276     struct lbuf *lbuf;
277 {
278     char *cp, *ep;
279     int len, contlen;
280
281     contlen = lbuf->continuation ? strlen(lbuf->continuation) : 0;
282
283     /* For very small widths just give up... */
284     if (lbuf->cols <= lbuf->indent + contlen + 20) {
285         lbuf->output(lbuf->buf);
286         lbuf->output("\n");
287         goto done;
288     }
289
290     /* Print each line in the buffer */
291     for (cp = lbuf->buf; cp != NULL && *cp != '\0'; ) {
292         if (*cp == '\n') {
293             lbuf->output("\n");
294             cp++;
295         } else {
296             ep = memchr(cp, '\n', lbuf->len - (cp - lbuf->buf));
297             len = ep ? (int)(ep - cp) : lbuf->len;
298             lbuf_println(lbuf, cp, len);
299             cp = ep ? ep + 1 : NULL;
300         }
301     }
302
303 done:
304     lbuf->len = 0;              /* reset the buffer for re-use. */
305 }