dialog_util.c revision 274116
11638Srgrimes/*- 21638Srgrimes * Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org> 31638Srgrimes * All rights reserved. 41638Srgrimes * 51638Srgrimes * Redistribution and use in source and binary forms, with or without 61638Srgrimes * modification, are permitted provided that the following conditions 71638Srgrimes * are met: 81638Srgrimes * 1. Redistributions of source code must retain the above copyright 91638Srgrimes * notice, this list of conditions and the following disclaimer. 101638Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111638Srgrimes * notice, this list of conditions and the following disclaimer in the 121638Srgrimes * documentation and/or other materials provided with the distribution. 131638Srgrimes * 141638Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 151638Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 161638Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 171638Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 181638Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 191638Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 201638Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 211638Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 221638Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231638Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241638Srgrimes * SUCH DAMAGE. 251638Srgrimes */ 261638Srgrimes 271638Srgrimes#include <sys/cdefs.h> 281638Srgrimes__FBSDID("$FreeBSD: head/lib/libdpv/dialog_util.c 274116 2014-11-04 23:46:01Z dteske $"); 291638Srgrimes 301638Srgrimes#include <sys/ioctl.h> 311638Srgrimes 321638Srgrimes#include <ctype.h> 331638Srgrimes#include <err.h> 341638Srgrimes#include <fcntl.h> 351638Srgrimes#include <limits.h> 361638Srgrimes#include <spawn.h> 371638Srgrimes#include <stdio.h> 381638Srgrimes#include <stdlib.h> 391638Srgrimes#include <string.h> 401638Srgrimes#include <termios.h> 411638Srgrimes#include <unistd.h> 421638Srgrimes 431638Srgrimes#include "dialog_util.h" 441638Srgrimes#include "dpv.h" 451638Srgrimes#include "dpv_private.h" 461638Srgrimes 471638Srgrimesextern char **environ; 481638Srgrimes 491638Srgrimes#define TTY_DEFAULT_ROWS 24 501638Srgrimes#define TTY_DEFAULT_COLS 80 511638Srgrimes 521638Srgrimes/* [X]dialog(1) characteristics */ 531638Srgrimesuint8_t dialog_test = 0; 541638Srgrimesuint8_t use_dialog = 0; 551638Srgrimesuint8_t use_libdialog = 1; 561638Srgrimesuint8_t use_xdialog = 0; 571638Srgrimesuint8_t use_color = 1; 581638Srgrimeschar dialog[PATH_MAX] = DIALOG; 591638Srgrimes 601638Srgrimes/* [X]dialog(1) functionality */ 611638Srgrimeschar *title = NULL; 621638Srgrimeschar *backtitle = NULL; 631638Srgrimesint dheight = 0; 641638Srgrimesint dwidth = 0; 651638Srgrimeschar *dargv[64] = { NULL }; 661638Srgrimes 671638Srgrimes/* TTY/Screen characteristics */ 681638Srgrimesstatic struct winsize *maxsize = NULL; 691638Srgrimes 701638Srgrimes/* Function prototypes */ 711638Srgrimesstatic void tty_maxsize_update(void); 721638Srgrimesstatic void x11_maxsize_update(void); 731638Srgrimes 741638Srgrimes/* 751638Srgrimes * Update row/column fields of `maxsize' global (used by dialog_maxrows() and 761638Srgrimes * dialog_maxcols()). If the `maxsize' pointer is NULL, it will be initialized. 771638Srgrimes * The `ws_row' and `ws_col' fields of `maxsize' are updated to hold current 781638Srgrimes * maximum height and width (respectively) for a dialog(1) widget based on the 791638Srgrimes * active TTY size. 801638Srgrimes * 811638Srgrimes * This function is called automatically by dialog_maxrows/cols() to reflect 821638Srgrimes * changes in terminal size in-between calls. 831638Srgrimes */ 841638Srgrimesstatic void 851638Srgrimestty_maxsize_update(void) 861638Srgrimes{ 871638Srgrimes int fd = STDIN_FILENO; 881638Srgrimes struct termios t; 891638Srgrimes 901638Srgrimes if (maxsize == NULL) { 911638Srgrimes if ((maxsize = malloc(sizeof(struct winsize))) == NULL) 921638Srgrimes errx(EXIT_FAILURE, "Out of memory?!"); 931638Srgrimes memset((void *)maxsize, '\0', sizeof(struct winsize)); 941638Srgrimes } 951638Srgrimes 961638Srgrimes if (!isatty(fd)) 971638Srgrimes fd = open("/dev/tty", O_RDONLY); 981638Srgrimes if ((tcgetattr(fd, &t) < 0) || (ioctl(fd, TIOCGWINSZ, maxsize) < 0)) { 991638Srgrimes maxsize->ws_row = TTY_DEFAULT_ROWS; 1001638Srgrimes maxsize->ws_col = TTY_DEFAULT_COLS; 1011638Srgrimes } 1021638Srgrimes} 1031638Srgrimes 1041638Srgrimes/* 1051638Srgrimes * Update row/column fields of `maxsize' global (used by dialog_maxrows() and 1061638Srgrimes * dialog_maxcols()). If the `maxsize' pointer is NULL, it will be initialized. 1071638Srgrimes * The `ws_row' and `ws_col' fields of `maxsize' are updated to hold current 1081638Srgrimes * maximum height and width (respectively) for an Xdialog(1) widget based on 1091638Srgrimes * the active video resolution of the X11 environment. 1101638Srgrimes * 1111638Srgrimes * This function is called automatically by dialog_maxrows/cols() to initialize 1121638Srgrimes * `maxsize'. Since video resolution changes are less common and more obtrusive 1131638Srgrimes * than changes to terminal size, the dialog_maxrows/cols() functions only call 1141638Srgrimes * this function when `maxsize' is set to NULL. 1151638Srgrimes */ 1161638Srgrimesstatic void 1171638Srgrimesx11_maxsize_update(void) 1181638Srgrimes{ 1191638Srgrimes FILE *f = NULL; 1201638Srgrimes char *cols; 1211638Srgrimes char *cp; 1221638Srgrimes char *rows; 1231638Srgrimes char cmdbuf[LINE_MAX]; 1241638Srgrimes char rbuf[LINE_MAX]; 1251638Srgrimes 1261638Srgrimes if (maxsize == NULL) { 1271638Srgrimes if ((maxsize = malloc(sizeof(struct winsize))) == NULL) 1281638Srgrimes errx(EXIT_FAILURE, "Out of memory?!"); 1291638Srgrimes memset((void *)maxsize, '\0', sizeof(struct winsize)); 1301638Srgrimes } 1311638Srgrimes 1321638Srgrimes /* Assemble the command necessary to get X11 sizes */ 1331638Srgrimes snprintf(cmdbuf, LINE_MAX, "%s --print-maxsize 2>&1", dialog); 1341638Srgrimes 1351638Srgrimes fflush(STDIN_FILENO); /* prevent popen(3) from seeking on stdin */ 1361638Srgrimes 1371638Srgrimes if ((f = popen(cmdbuf, "r")) == NULL) { 1381638Srgrimes if (debug) 139 warnx("WARNING! Command `%s' failed", cmdbuf); 140 return; 141 } 142 143 /* Read in the line returned from Xdialog(1) */ 144 if ((fgets(rbuf, LINE_MAX, f) == NULL) || (pclose(f) < 0)) 145 return; 146 147 /* Check for X11-related errors */ 148 if (strncmp(rbuf, "Xdialog: Error", 14) == 0) 149 return; 150 151 /* Parse expected output: MaxSize: YY, XXX */ 152 if ((rows = strchr(rbuf, ' ')) == NULL) 153 return; 154 if ((cols = strchr(rows, ',')) != NULL) { 155 /* strtonum(3) doesn't like trailing junk */ 156 *(cols++) = '\0'; 157 if ((cp = strchr(cols, '\n')) != NULL) 158 *cp = '\0'; 159 } 160 161 /* Convert to unsigned short */ 162 maxsize->ws_row = (unsigned short)strtonum( 163 rows, 0, USHRT_MAX, (const char **)NULL); 164 maxsize->ws_col = (unsigned short)strtonum( 165 cols, 0, USHRT_MAX, (const char **)NULL); 166} 167 168/* 169 * Return the current maximum height (rows) for an [X]dialog(1) widget. 170 */ 171int 172dialog_maxrows(void) 173{ 174 175 if (use_xdialog && maxsize == NULL) 176 x11_maxsize_update(); /* initialize maxsize for GUI */ 177 else if (!use_xdialog) 178 tty_maxsize_update(); /* update maxsize for TTY */ 179 return (maxsize->ws_row); 180} 181 182/* 183 * Return the current maximum width (cols) for an [X]dialog(1) widget. 184 */ 185int 186dialog_maxcols(void) 187{ 188 189 if (use_xdialog && maxsize == NULL) 190 x11_maxsize_update(); /* initialize maxsize for GUI */ 191 else if (!use_xdialog) 192 tty_maxsize_update(); /* update maxsize for TTY */ 193 194 if (use_dialog || use_libdialog) { 195 if (use_shadow) 196 return (maxsize->ws_col - 2); 197 else 198 return (maxsize->ws_col); 199 } else 200 return (maxsize->ws_col); 201} 202 203/* 204 * Return the current maximum width (cols) for the terminal. 205 */ 206int 207tty_maxcols(void) 208{ 209 210 if (use_xdialog && maxsize == NULL) 211 x11_maxsize_update(); /* initialize maxsize for GUI */ 212 else if (!use_xdialog) 213 tty_maxsize_update(); /* update maxsize for TTY */ 214 215 return (maxsize->ws_col); 216} 217 218/* 219 * Spawn an [X]dialog(1) `--gauge' box with a `--prompt' value of init_prompt. 220 * Writes the resulting process ID to the pid_t pointed at by `pid'. Returns a 221 * file descriptor (int) suitable for writing data to the [X]dialog(1) instance 222 * (data written to the file descriptor is seen as standard-in by the spawned 223 * [X]dialog(1) process). 224 */ 225int 226dialog_spawn_gauge(char *init_prompt, pid_t *pid) 227{ 228 char dummy_init[2] = ""; 229 char *cp; 230 int height; 231 int width; 232 int error; 233 posix_spawn_file_actions_t action; 234#if DIALOG_SPAWN_DEBUG 235 unsigned int i; 236#endif 237 unsigned int n = 0; 238 int stdin_pipe[2] = { -1, -1 }; 239 240 /* Override `dialog' with a path from ENV_DIALOG if provided */ 241 if ((cp = getenv(ENV_DIALOG)) != NULL) 242 snprintf(dialog, PATH_MAX, "%s", cp); 243 244 /* For Xdialog(1), set ENV_XDIALOG_HIGH_DIALOG_COMPAT */ 245 setenv(ENV_XDIALOG_HIGH_DIALOG_COMPAT, "1", 1); 246 247 /* Constrain the height/width */ 248 height = dialog_maxrows(); 249 if (backtitle != NULL) 250 height -= use_shadow ? 5 : 4; 251 if (dheight < height) 252 height = dheight; 253 width = dialog_maxcols(); 254 if (dwidth < width) 255 width = dwidth; 256 257 /* Populate argument array */ 258 dargv[n++] = dialog; 259 if (title != NULL) { 260 if ((dargv[n] = malloc(8)) == NULL) 261 errx(EXIT_FAILURE, "Out of memory?!"); 262 sprintf(dargv[n++], "--title"); 263 dargv[n++] = title; 264 } 265 if (backtitle != NULL) { 266 if ((dargv[n] = malloc(12)) == NULL) 267 errx(EXIT_FAILURE, "Out of memory?!"); 268 sprintf(dargv[n++], "--backtitle"); 269 dargv[n++] = backtitle; 270 } 271 if (use_color) { 272 if ((dargv[n] = malloc(11)) == NULL) 273 errx(EXIT_FAILURE, "Out of memory?!"); 274 sprintf(dargv[n++], "--colors"); 275 } 276 if (use_xdialog) { 277 if ((dargv[n] = malloc(7)) == NULL) 278 errx(EXIT_FAILURE, "Out of memory?!"); 279 sprintf(dargv[n++], "--left"); 280 281 /* 282 * NOTE: Xdialog(1)'s `--wrap' appears to be broken for the 283 * `--gauge' widget prompt-updates. Add it anyway (in-case it 284 * gets fixed in some later release). 285 */ 286 if ((dargv[n] = malloc(7)) == NULL) 287 errx(EXIT_FAILURE, "Out of memory?!"); 288 sprintf(dargv[n++], "--wrap"); 289 } 290 if ((dargv[n] = malloc(8)) == NULL) 291 errx(EXIT_FAILURE, "Out of memory?!"); 292 sprintf(dargv[n++], "--gauge"); 293 dargv[n++] = use_xdialog ? dummy_init : init_prompt; 294 if ((dargv[n] = malloc(40)) == NULL) 295 errx(EXIT_FAILURE, "Out of memory?!"); 296 snprintf(dargv[n++], 40, "%u", height); 297 if ((dargv[n] = malloc(40)) == NULL) 298 errx(EXIT_FAILURE, "Out of memory?!"); 299 snprintf(dargv[n++], 40, "%u", width); 300 dargv[n] = NULL; 301 302 /* Open a pipe(2) to communicate with [X]dialog(1) */ 303 if (pipe(stdin_pipe) < 0) 304 err(EXIT_FAILURE, "%s: pipe(2)", __func__); 305 306 /* Fork [X]dialog(1) process */ 307#if DIALOG_SPAWN_DEBUG 308 fprintf(stderr, "%s: spawning `", __func__); 309 for (i = 0; i < n; i++) { 310 if (i == 0) 311 fprintf(stderr, "%s", dargv[i]); 312 else if (*dargv[i] == '-' && *(dargv[i] + 1) == '-') 313 fprintf(stderr, " %s", dargv[i]); 314 else 315 fprintf(stderr, " \"%s\"", dargv[i]); 316 } 317 fprintf(stderr, "'\n"); 318#endif 319 posix_spawn_file_actions_init(&action); 320 posix_spawn_file_actions_adddup2(&action, stdin_pipe[0], STDIN_FILENO); 321 posix_spawn_file_actions_addclose(&action, stdin_pipe[1]); 322 error = posix_spawnp(pid, dialog, &action, 323 (const posix_spawnattr_t *)NULL, dargv, environ); 324 if (error != 0) 325 err(EXIT_FAILURE, "%s: posix_spawnp(3)", __func__); 326 327 /* NB: Do not free(3) *dargv[], else SIGSEGV */ 328 329 return (stdin_pipe[1]); 330} 331 332/* 333 * Returns the number of lines in buffer pointed to by `prompt'. Takes both 334 * newlines and escaped-newlines into account. 335 */ 336unsigned int 337dialog_prompt_numlines(const char *prompt, uint8_t nlstate) 338{ 339 uint8_t nls = nlstate; /* See dialog_prompt_nlstate() */ 340 const char *cp = prompt; 341 unsigned int nlines = 1; 342 343 if (prompt == NULL || *prompt == '\0') 344 return (0); 345 346 while (*cp != '\0') { 347 if (use_dialog) { 348 if (strncmp(cp, "\\n", 2) == 0) { 349 cp++; 350 nlines++; 351 nls = TRUE; /* See declaration comment */ 352 } else if (*cp == '\n') { 353 if (!nls) 354 nlines++; 355 nls = FALSE; /* See declaration comment */ 356 } 357 } else if (use_libdialog) { 358 if (*cp == '\n') 359 nlines++; 360 } else if (strncmp(cp, "\\n", 2) == 0) { 361 cp++; 362 nlines++; 363 } 364 cp++; 365 } 366 367 return (nlines); 368} 369 370/* 371 * Returns the length in bytes of the longest line in buffer pointed to by 372 * `prompt'. Takes newlines and escaped newlines into account. Also discounts 373 * dialog(1) color escape codes if enabled (via `use_color' global). 374 */ 375unsigned int 376dialog_prompt_longestline(const char *prompt, uint8_t nlstate) 377{ 378 uint8_t backslash = 0; 379 uint8_t nls = nlstate; /* See dialog_prompt_nlstate() */ 380 const char *p = prompt; 381 int longest = 0; 382 int n = 0; 383 384 /* `prompt' parameter is required */ 385 if (prompt == NULL) 386 return (0); 387 if (*prompt == '\0') 388 return (0); /* shortcut */ 389 390 /* Loop until the end of the string */ 391 while (*p != '\0') { 392 /* dialog(1) and dialog(3) will render literal newlines */ 393 if (use_dialog || use_libdialog) { 394 if (*p == '\n') { 395 if (!use_libdialog && nls) 396 n++; 397 else { 398 if (n > longest) 399 longest = n; 400 n = 0; 401 } 402 nls = FALSE; /* See declaration comment */ 403 p++; 404 continue; 405 } 406 } 407 408 /* Check for backslash character */ 409 if (*p == '\\') { 410 /* If second backslash, count as a single-char */ 411 if ((backslash ^= 1) == 0) 412 n++; 413 } else if (backslash) { 414 if (*p == 'n' && !use_libdialog) { /* new line */ 415 /* NB: dialog(3) ignores escaped newlines */ 416 nls = TRUE; /* See declaration comment */ 417 if (n > longest) 418 longest = n; 419 n = 0; 420 } else if (use_color && *p == 'Z') { 421 if (*++p != '\0') 422 p++; 423 backslash = 0; 424 continue; 425 } else /* [X]dialog(1)/dialog(3) only expand those */ 426 n += 2; 427 428 backslash = 0; 429 } else 430 n++; 431 p++; 432 } 433 if (n > longest) 434 longest = n; 435 436 return (longest); 437} 438 439/* 440 * Returns a pointer to the last line in buffer pointed to by `prompt'. Takes 441 * both newlines (if using dialog(1) versus Xdialog(1)) and escaped newlines 442 * into account. If no newlines (escaped or otherwise) appear in the buffer, 443 * `prompt' is returned. If passed a NULL pointer, returns NULL. 444 */ 445char * 446dialog_prompt_lastline(char *prompt, uint8_t nlstate) 447{ 448 uint8_t nls = nlstate; /* See dialog_prompt_nlstate() */ 449 char *lastline; 450 char *p; 451 452 if (prompt == NULL) 453 return (NULL); 454 if (*prompt == '\0') 455 return (prompt); /* shortcut */ 456 457 lastline = p = prompt; 458 while (*p != '\0') { 459 /* dialog(1) and dialog(3) will render literal newlines */ 460 if (use_dialog || use_libdialog) { 461 if (*p == '\n') { 462 if (use_libdialog || !nls) 463 lastline = p + 1; 464 nls = FALSE; /* See declaration comment */ 465 } 466 } 467 /* dialog(3) does not expand escaped newlines */ 468 if (use_libdialog) { 469 p++; 470 continue; 471 } 472 if (*p == '\\' && *(p + 1) != '\0' && *(++p) == 'n') { 473 nls = TRUE; /* See declaration comment */ 474 lastline = p + 1; 475 } 476 p++; 477 } 478 479 return (lastline); 480} 481 482/* 483 * Returns the number of extra lines generated by wrapping the text in buffer 484 * pointed to by `prompt' within `ncols' columns (for prompts, this should be 485 * dwidth - 4). Also discounts dialog(1) color escape codes if enabled (via 486 * `use_color' global). 487 */ 488int 489dialog_prompt_wrappedlines(char *prompt, int ncols, uint8_t nlstate) 490{ 491 uint8_t backslash = 0; 492 uint8_t nls = nlstate; /* See dialog_prompt_nlstate() */ 493 char *cp; 494 char *p = prompt; 495 int n = 0; 496 int wlines = 0; 497 498 /* `prompt' parameter is required */ 499 if (p == NULL) 500 return (0); 501 if (*p == '\0') 502 return (0); /* shortcut */ 503 504 /* Loop until the end of the string */ 505 while (*p != '\0') { 506 /* dialog(1) and dialog(3) will render literal newlines */ 507 if (use_dialog || use_libdialog) { 508 if (*p == '\n') { 509 if (use_dialog || !nls) 510 n = 0; 511 nls = FALSE; /* See declaration comment */ 512 } 513 } 514 515 /* Check for backslash character */ 516 if (*p == '\\') { 517 /* If second backslash, count as a single-char */ 518 if ((backslash ^= 1) == 0) 519 n++; 520 } else if (backslash) { 521 if (*p == 'n' && !use_libdialog) { /* new line */ 522 /* NB: dialog(3) ignores escaped newlines */ 523 nls = TRUE; /* See declaration comment */ 524 n = 0; 525 } else if (use_color && *p == 'Z') { 526 if (*++p != '\0') 527 p++; 528 backslash = 0; 529 continue; 530 } else /* [X]dialog(1)/dialog(3) only expand those */ 531 n += 2; 532 533 backslash = 0; 534 } else 535 n++; 536 537 /* Did we pass the width barrier? */ 538 if (n > ncols) { 539 /* 540 * Work backward to find the first whitespace on-which 541 * dialog(1) will wrap the line (but don't go before 542 * the start of this line). 543 */ 544 cp = p; 545 while (n > 1 && !isspace(*cp)) { 546 cp--; 547 n--; 548 } 549 if (n > 0 && isspace(*cp)) 550 p = cp; 551 wlines++; 552 n = 1; 553 } 554 555 p++; 556 } 557 558 return (wlines); 559} 560 561/* 562 * Returns zero if the buffer pointed to by `prompt' contains an escaped 563 * newline but only if appearing after any/all literal newlines. This is 564 * specific to dialog(1) and does not apply to Xdialog(1). 565 * 566 * As an attempt to make shell scripts easier to read, dialog(1) will "eat" 567 * the first literal newline after an escaped newline. This however has a bug 568 * in its implementation in that rather than allowing `\\n\n' to be treated 569 * similar to `\\n' or `\n', dialog(1) expands the `\\n' and then translates 570 * the following literal newline (with or without characters between [!]) into 571 * a single space. 572 * 573 * If you want to be compatible with Xdialog(1), it is suggested that you not 574 * use literal newlines (they aren't supported); but if you have to use them, 575 * go right ahead. But be forewarned... if you set $DIALOG in your environment 576 * to something other than `cdialog' (our current dialog(1)), then it should 577 * do the same thing w/respect to how to handle a literal newline after an 578 * escaped newline (you could do no wrong by translating every literal newline 579 * into a space but only when you've previously encountered an escaped one; 580 * this is what dialog(1) is doing). 581 * 582 * The ``newline state'' (or nlstate for short; as I'm calling it) is helpful 583 * if you plan to combine multiple strings into a single prompt text. In lead- 584 * up to this procedure, a common task is to calculate and utilize the widths 585 * and heights of each piece of prompt text to later be combined. However, if 586 * (for example) the first string ends in a positive newline state (has an 587 * escaped newline without trailing literal), the first literal newline in the 588 * second string will be mangled. 589 * 590 * The return value of this function should be used as the `nlstate' argument 591 * to dialog_*() functions that require it to allow accurate calculations in 592 * the event such information is needed. 593 */ 594uint8_t 595dialog_prompt_nlstate(const char *prompt) 596{ 597 const char *cp; 598 599 if (prompt == NULL) 600 return 0; 601 602 /* 603 * Work our way backward from the end of the string for efficiency. 604 */ 605 cp = prompt + strlen(prompt); 606 while (--cp >= prompt) { 607 /* 608 * If we get to a literal newline first, this prompt ends in a 609 * clean state for rendering with dialog(1). Otherwise, if we 610 * get to an escaped newline first, this prompt ends in an un- 611 * clean state (following literal will be mangled; see above). 612 */ 613 if (*cp == '\n') 614 return (0); 615 else if (*cp == 'n' && --cp > prompt && *cp == '\\') 616 return (1); 617 } 618 619 return (0); /* no newlines (escaped or otherwise) */ 620} 621 622/* 623 * Free allocated items initialized by tty_maxsize_update() and 624 * x11_maxsize_update() 625 */ 626void 627dialog_maxsize_free(void) 628{ 629 if (maxsize != NULL) { 630 free(maxsize); 631 maxsize = NULL; 632 } 633} 634