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