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