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 64int 65get_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 82void 83lbuf_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 98void 99lbuf_destroy(lbuf) 100 struct lbuf *lbuf; 101{ 102 efree(lbuf->buf); 103 lbuf->buf = NULL; 104} 105 106/* 107 * Parse the format and append strings, only %s and %% escapes are supported. 108 * Any characters in set are quoted with a backslash. 109 */ 110void 111#ifdef __STDC__ 112lbuf_append_quoted(struct lbuf *lbuf, const char *set, const char *fmt, ...) 113#else 114lbuf_append_quoted(lbuf, set, fmt, va_alist) 115 struct lbuf *lbuf; 116 const char *set; 117 const char *fmt; 118 va_dcl 119#endif 120{ 121 va_list ap; 122 int len; 123 char *cp, *s = NULL; 124 125#ifdef __STDC__ 126 va_start(ap, fmt); 127#else 128 va_start(ap); 129#endif 130 while (*fmt != '\0') { 131 len = 1; 132 if (fmt[0] == '%' && fmt[1] == 's') { 133 s = va_arg(ap, char *); 134 len = strlen(s); 135 } 136 /* Assume worst case that all chars must be escaped. */ 137 if (lbuf->len + (len * 2) + 1 >= lbuf->size) { 138 do { 139 lbuf->size += 256; 140 } while (lbuf->len + len + 1 >= lbuf->size); 141 lbuf->buf = erealloc(lbuf->buf, lbuf->size); 142 } 143 if (*fmt == '%') { 144 if (*(++fmt) == 's') { 145 while ((cp = strpbrk(s, set)) != NULL) { 146 len = (int)(cp - s); 147 memcpy(lbuf->buf + lbuf->len, s, len); 148 lbuf->len += len; 149 lbuf->buf[lbuf->len++] = '\\'; 150 lbuf->buf[lbuf->len++] = *cp; 151 s = cp + 1; 152 } 153 if (*s != '\0') { 154 len = strlen(s); 155 memcpy(lbuf->buf + lbuf->len, s, len); 156 lbuf->len += len; 157 } 158 fmt++; 159 continue; 160 } 161 } 162 if (strchr(set, *fmt) != NULL) 163 lbuf->buf[lbuf->len++] = '\\'; 164 lbuf->buf[lbuf->len++] = *fmt++; 165 } 166 lbuf->buf[lbuf->len] = '\0'; 167 va_end(ap); 168} 169 170/* 171 * Parse the format and append strings, only %s and %% escapes are supported. 172 */ 173void 174#ifdef __STDC__ 175lbuf_append(struct lbuf *lbuf, const char *fmt, ...) 176#else 177lbuf_append(lbuf, fmt, va_alist) 178 struct lbuf *lbuf; 179 const char *fmt; 180 va_dcl 181#endif 182{ 183 va_list ap; 184 int len; 185 char *s = NULL; 186 187#ifdef __STDC__ 188 va_start(ap, fmt); 189#else 190 va_start(ap); 191#endif 192 while (*fmt != '\0') { 193 len = 1; 194 if (fmt[0] == '%' && fmt[1] == 's') { 195 s = va_arg(ap, char *); 196 len = strlen(s); 197 } 198 if (lbuf->len + len + 1 >= lbuf->size) { 199 do { 200 lbuf->size += 256; 201 } while (lbuf->len + len + 1 >= lbuf->size); 202 lbuf->buf = erealloc(lbuf->buf, lbuf->size); 203 } 204 if (*fmt == '%') { 205 if (*(++fmt) == 's') { 206 memcpy(lbuf->buf + lbuf->len, s, len); 207 lbuf->len += len; 208 fmt++; 209 continue; 210 } 211 } 212 lbuf->buf[lbuf->len++] = *fmt++; 213 } 214 lbuf->buf[lbuf->len] = '\0'; 215 va_end(ap); 216} 217 218static void 219lbuf_println(lbuf, line, len) 220 struct lbuf *lbuf; 221 char *line; 222 int len; 223{ 224 char *cp, save; 225 int i, have, contlen; 226 227 contlen = lbuf->continuation ? strlen(lbuf->continuation) : 0; 228 229 /* 230 * Print the buffer, splitting the line as needed on a word 231 * boundary. 232 */ 233 cp = line; 234 have = lbuf->cols; 235 while (cp != NULL && *cp != '\0') { 236 char *ep = NULL; 237 int need = len - (int)(cp - line); 238 239 if (need > have) { 240 have -= contlen; /* subtract for continuation char */ 241 if ((ep = memrchr(cp, ' ', have)) == NULL) 242 ep = memchr(cp + have, ' ', need - have); 243 if (ep != NULL) 244 need = (int)(ep - cp); 245 } 246 if (cp != line) { 247 /* indent continued lines */ 248 /* XXX - build up string instead? */ 249 for (i = 0; i < lbuf->indent; i++) 250 lbuf->output(" "); 251 } 252 /* NUL-terminate cp for the output function and restore afterwards */ 253 save = cp[need]; 254 cp[need] = '\0'; 255 lbuf->output(cp); 256 cp[need] = save; 257 cp = ep; 258 259 /* 260 * If there is more to print, reset have, incremement cp past 261 * the whitespace, and print a line continuaton char if needed. 262 */ 263 if (cp != NULL) { 264 have = lbuf->cols - lbuf->indent; 265 ep = line + len; 266 while (cp < ep && isblank((unsigned char)*cp)) { 267 cp++; 268 } 269 if (contlen) 270 lbuf->output(lbuf->continuation); 271 } 272 lbuf->output("\n"); 273 } 274} 275 276/* 277 * Print the buffer with word wrap based on the tty width. 278 * The lbuf is reset on return. 279 */ 280void 281lbuf_print(lbuf) 282 struct lbuf *lbuf; 283{ 284 char *cp, *ep; 285 int len; 286 287 if (lbuf->buf == NULL || lbuf->len == 0) 288 goto done; 289 290 /* For very small widths just give up... */ 291 len = lbuf->continuation ? strlen(lbuf->continuation) : 0; 292 if (lbuf->cols <= lbuf->indent + len + 20) { 293 lbuf->buf[lbuf->len] = '\0'; 294 lbuf->output(lbuf->buf); 295 goto done; 296 } 297 298 /* Print each line in the buffer */ 299 for (cp = lbuf->buf; cp != NULL && *cp != '\0'; ) { 300 if (*cp == '\n') { 301 lbuf->output("\n"); 302 cp++; 303 } else { 304 len = lbuf->len - (cp - lbuf->buf); 305 if ((ep = memchr(cp, '\n', len)) != NULL) 306 len = (int)(ep - cp); 307 if (len) 308 lbuf_println(lbuf, cp, len); 309 cp = ep ? ep + 1 : NULL; 310 } 311 } 312 313done: 314 lbuf->len = 0; /* reset the buffer for re-use. */ 315} 316