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