1 /* $OpenBSD: msg.c,v 1.8 2001/01/29 01:58:30 niklas Exp $ */ 2 3 /*- 4 * Copyright (c) 1991, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * Copyright (c) 1991, 1993, 1994, 1995, 1996 7 * Keith Bostic. All rights reserved. 8 * 9 * See the LICENSE file for redistribution information. 10 */ 11 12 #include "config.h" 13 14 #ifndef lint 15 static const char sccsid[] = "@(#)msg.c 10.48 (Berkeley) 9/15/96"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 #include <sys/types.h> /* XXX: param.h may not have included types.h */ 20 #include <sys/queue.h> 21 #include <sys/stat.h> 22 #include <sys/time.h> 23 24 #include <bitstring.h> 25 #include <ctype.h> 26 #include <errno.h> 27 #include <fcntl.h> 28 #include <limits.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 34 #ifdef __STDC__ 35 #include <stdarg.h> 36 #else 37 #include <varargs.h> 38 #endif 39 40 #include "common.h" 41 #include "../vi/vi.h" 42 43 /* 44 * msgq -- 45 * Display a message. 46 * 47 * PUBLIC: void msgq __P((SCR *, mtype_t, const char *, ...)); 48 */ 49 void 50 #ifdef __STDC__ 51 msgq(SCR *sp, mtype_t mt, const char *fmt, ...) 52 #else 53 msgq(sp, mt, fmt, va_alist) 54 SCR *sp; 55 mtype_t mt; 56 const char *fmt; 57 va_dcl 58 #endif 59 { 60 #ifndef NL_ARGMAX 61 #define __NL_ARGMAX 20 /* Set to 9 by System V. */ 62 struct { 63 const char *str; /* String pointer. */ 64 size_t arg; /* Argument number. */ 65 size_t prefix; /* Prefix string length. */ 66 size_t skip; /* Skipped string length. */ 67 size_t suffix; /* Suffix string length. */ 68 } str[__NL_ARGMAX]; 69 #endif 70 static int reenter; /* STATIC: Re-entrancy check. */ 71 CHAR_T ch; 72 GS *gp; 73 size_t blen, cnt1, cnt2, len, mlen, nlen, soff; 74 const char *p, *t, *u; 75 char *bp, *mp, *rbp, *s_rbp; 76 va_list ap; 77 78 /* 79 * !!! 80 * It's possible to enter msg when there's no screen to hold the 81 * message. If sp is NULL, ignore the special cases and put the 82 * message out to stderr. 83 */ 84 if (sp == NULL) { 85 gp = NULL; 86 if (mt == M_BERR) 87 mt = M_ERR; 88 else if (mt == M_VINFO) 89 mt = M_INFO; 90 } else { 91 gp = sp->gp; 92 switch (mt) { 93 case M_BERR: 94 if (F_ISSET(sp, SC_VI) && !O_ISSET(sp, O_VERBOSE)) { 95 F_SET(gp, G_BELLSCHED); 96 return; 97 } 98 mt = M_ERR; 99 break; 100 case M_VINFO: 101 if (!O_ISSET(sp, O_VERBOSE)) 102 return; 103 mt = M_INFO; 104 /* FALLTHROUGH */ 105 case M_INFO: 106 if (F_ISSET(sp, SC_EX_SILENT)) 107 return; 108 break; 109 case M_ERR: 110 case M_SYSERR: 111 break; 112 default: 113 abort(); 114 } 115 } 116 117 /* 118 * It's possible to reenter msg when it allocates space. We're 119 * probably dead anyway, but there's no reason to drop core. 120 * 121 * XXX 122 * Yes, there's a race, but it should only be two instructions. 123 */ 124 if (reenter++) 125 return; 126 127 /* Get space for the message. */ 128 nlen = 1024; 129 if (0) { 130 retry: FREE_SPACE(sp, bp, blen); 131 nlen *= 2; 132 } 133 bp = NULL; 134 blen = 0; 135 GET_SPACE_GOTO(sp, bp, blen, nlen); 136 137 /* 138 * Error prefix. 139 * 140 * mp: pointer to the current next character to be written 141 * mlen: length of the already written characters 142 * blen: total length of the buffer 143 */ 144 #define REM (blen - mlen) 145 mp = bp; 146 mlen = 0; 147 if (mt == M_SYSERR) { 148 p = msg_cat(sp, "020|Error: ", &len); 149 if (REM < len) 150 goto retry; 151 memcpy(mp, p, len); 152 mp += len; 153 mlen += len; 154 } 155 156 /* 157 * If we're running an ex command that the user didn't enter, display 158 * the file name and line number prefix. 159 */ 160 if ((mt == M_ERR || mt == M_SYSERR) && 161 sp != NULL && gp != NULL && gp->if_name != NULL) { 162 for (p = gp->if_name; *p != '\0'; ++p) { 163 len = snprintf(mp, REM, "%s", KEY_NAME(sp, *p)); 164 mp += len; 165 if ((mlen += len) > blen) 166 goto retry; 167 } 168 len = snprintf(mp, REM, ", %d: ", gp->if_lno); 169 mp += len; 170 if ((mlen += len) > blen) 171 goto retry; 172 } 173 174 /* If nothing to format, we're done. */ 175 if (fmt == NULL) 176 goto nofmt; 177 fmt = msg_cat(sp, fmt, NULL); 178 179 #ifndef NL_ARGMAX 180 /* 181 * Nvi should run on machines that don't support the numbered argument 182 * specifications (%[digit]*$). We do this by reformatting the string 183 * so that we can hand it to vsprintf(3) and it will use the arguments 184 * in the right order. When vsprintf returns, we put the string back 185 * into the right order. It's undefined, according to SVID III, to mix 186 * numbered argument specifications with the standard style arguments, 187 * so this should be safe. 188 * 189 * In addition, we also need a character that is known to not occur in 190 * any vi message, for separating the parts of the string. As callers 191 * of msgq are responsible for making sure that all the non-printable 192 * characters are formatted for printing before calling msgq, we use a 193 * random non-printable character selected at terminal initialization 194 * time. This code isn't fast by any means, but as messages should be 195 * relatively short and normally have only a few arguments, it won't be 196 * too bad. Regardless, nobody has come up with any other solution. 197 * 198 * The result of this loop is an array of pointers into the message 199 * string, with associated lengths and argument numbers. The array 200 * is in the "correct" order, and the arg field contains the argument 201 * order. 202 */ 203 for (p = fmt, soff = 0; soff < __NL_ARGMAX;) { 204 for (t = p; *p != '\0' && *p != '%'; ++p); 205 if (*p == '\0') 206 break; 207 ++p; 208 if (!isdigit(*p)) { 209 if (*p == '%') 210 ++p; 211 continue; 212 } 213 for (u = p; *++p != '\0' && isdigit(*p);); 214 if (*p != '$') 215 continue; 216 217 /* Up to, and including the % character. */ 218 str[soff].str = t; 219 str[soff].prefix = u - t; 220 221 /* Up to, and including the $ character. */ 222 str[soff].arg = atoi(u); 223 str[soff].skip = (p - u) + 1; 224 if (str[soff].arg >= __NL_ARGMAX) 225 goto ret; 226 227 /* Up to, and including the conversion character. */ 228 for (u = p; (ch = *++p) != '\0';) 229 if (isalpha(ch) && 230 strchr("diouxXfeEgGcspn", ch) != NULL) 231 break; 232 str[soff].suffix = p - u; 233 if (ch != '\0') 234 ++p; 235 ++soff; 236 } 237 238 /* If no magic strings, we're done. */ 239 if (soff == 0) 240 goto format; 241 242 /* Get space for the reordered strings. */ 243 if ((rbp = malloc(nlen)) == NULL) 244 goto ret; 245 s_rbp = rbp; 246 247 /* 248 * Reorder the strings into the message string based on argument 249 * order. 250 * 251 * !!! 252 * We ignore arguments that are out of order, i.e. if we don't find 253 * an argument, we continue. Assume (almost certainly incorrectly) 254 * that whoever created the string knew what they were doing. 255 * 256 * !!! 257 * Brute force "sort", but since we don't expect more than one or two 258 * arguments in a string, the setup cost of a fast sort will be more 259 * expensive than the loop. 260 */ 261 for (cnt1 = 1; cnt1 <= soff; ++cnt1) 262 for (cnt2 = 0; cnt2 < soff; ++cnt2) 263 if (cnt1 == str[cnt2].arg) { 264 memmove(s_rbp, str[cnt2].str, str[cnt2].prefix); 265 memmove(s_rbp + str[cnt2].prefix, 266 str[cnt2].str + str[cnt2].prefix + 267 str[cnt2].skip, str[cnt2].suffix); 268 s_rbp += str[cnt2].prefix + str[cnt2].suffix; 269 *s_rbp++ = 270 gp == NULL ? DEFAULT_NOPRINT : gp->noprint; 271 break; 272 } 273 *s_rbp = '\0'; 274 fmt = rbp; 275 #endif 276 277 format: /* Format the arguments into the string. */ 278 #ifdef __STDC__ 279 va_start(ap, fmt); 280 #else 281 va_start(ap); 282 #endif 283 len = vsnprintf(mp, REM, fmt, ap); 284 va_end(ap); 285 if (len >= nlen) 286 goto retry; 287 288 #ifndef NL_ARGMAX 289 if (soff == 0) 290 goto nofmt; 291 292 /* 293 * Go through the resulting string, and, for each separator character 294 * separated string, enter its new starting position and length in the 295 * array. 296 */ 297 for (p = t = mp, cnt1 = 1, 298 ch = gp == NULL ? DEFAULT_NOPRINT : gp->noprint; *p != '\0'; ++p) 299 if (*p == ch) { 300 for (cnt2 = 0; cnt2 < soff; ++cnt2) 301 if (str[cnt2].arg == cnt1) 302 break; 303 str[cnt2].str = t; 304 str[cnt2].prefix = p - t; 305 t = p + 1; 306 ++cnt1; 307 } 308 309 /* 310 * Reorder the strings once again, putting them back into the 311 * message buffer. 312 * 313 * !!! 314 * Note, the length of the message gets decremented once for 315 * each substring, when we discard the separator character. 316 */ 317 for (s_rbp = rbp, cnt1 = 0; cnt1 < soff; ++cnt1) { 318 memmove(rbp, str[cnt1].str, str[cnt1].prefix); 319 rbp += str[cnt1].prefix; 320 --len; 321 } 322 memmove(mp, s_rbp, rbp - s_rbp); 323 324 /* Free the reordered string memory. */ 325 free(s_rbp); 326 #endif 327 328 nofmt: mp += len; 329 if ((mlen += len) > blen) 330 goto retry; 331 if (mt == M_SYSERR) { 332 len = snprintf(mp, REM, ": %s", strerror(errno)); 333 mp += len; 334 if ((mlen += len) > blen) 335 goto retry; 336 mt = M_ERR; 337 } 338 339 /* Add trailing newline. */ 340 if ((mlen += 1) > blen) 341 goto retry; 342 *mp = '\n'; 343 344 if (sp != NULL) 345 (void)ex_fflush(sp); 346 if (gp != NULL) 347 gp->scr_msg(sp, mt, bp, mlen); 348 else 349 (void)fprintf(stderr, "%.*s", (int)mlen, bp); 350 351 /* Cleanup. */ 352 ret: FREE_SPACE(sp, bp, blen); 353 alloc_err: 354 reenter = 0; 355 } 356 357 /* 358 * msgq_str -- 359 * Display a message with an embedded string. 360 * 361 * PUBLIC: void msgq_str __P((SCR *, mtype_t, char *, char *)); 362 */ 363 void 364 msgq_str(sp, mtype, str, fmt) 365 SCR *sp; 366 mtype_t mtype; 367 char *str, *fmt; 368 { 369 int nf, sv_errno; 370 char *p; 371 372 if (str == NULL) { 373 msgq(sp, mtype, fmt); 374 return; 375 } 376 377 sv_errno = errno; 378 p = msg_print(sp, str, &nf); 379 errno = sv_errno; 380 msgq(sp, mtype, fmt, p); 381 if (nf) 382 FREE_SPACE(sp, p, 0); 383 } 384 385 /* 386 * mod_rpt -- 387 * Report on the lines that changed. 388 * 389 * !!! 390 * Historic vi documentation (USD:15-8) claimed that "The editor will also 391 * always tell you when a change you make affects text which you cannot see." 392 * This wasn't true -- edit a large file and do "100d|1". We don't implement 393 * this semantic since it requires tracking each line that changes during a 394 * command instead of just keeping count. 395 * 396 * Line counts weren't right in historic vi, either. For example, given the 397 * file: 398 * abc 399 * def 400 * the command 2d}, from the 'b' would report that two lines were deleted, 401 * not one. 402 * 403 * PUBLIC: void mod_rpt __P((SCR *)); 404 */ 405 void 406 mod_rpt(sp) 407 SCR *sp; 408 { 409 static char * const action[] = { 410 "293|added", 411 "294|changed", 412 "295|deleted", 413 "296|joined", 414 "297|moved", 415 "298|shifted", 416 "299|yanked", 417 }; 418 static char * const lines[] = { 419 "300|line", 420 "301|lines", 421 }; 422 recno_t total; 423 u_long rptval; 424 int first, cnt; 425 size_t blen, len, tlen; 426 const char *t; 427 char * const *ap; 428 char *bp, *p; 429 430 /* Change reports are turned off in batch mode. */ 431 if (F_ISSET(sp, SC_EX_SILENT)) 432 return; 433 434 /* Reset changing line number. */ 435 sp->rptlchange = OOBLNO; 436 437 /* 438 * Don't build a message if not enough changed. 439 * 440 * !!! 441 * And now, a vi clone test. Historically, vi reported if the number 442 * of changed lines was > than the value, not >=, unless it was a yank 443 * command, which used >=. No lie. Furthermore, an action was never 444 * reported for a single line action. This is consistent for actions 445 * other than yank, but yank didn't report single line actions even if 446 * the report edit option was set to 1. In addition, setting report to 447 * 0 in the 4BSD historic vi was equivalent to setting it to 1, for an 448 * unknown reason (this bug was fixed in System III/V at some point). 449 * I got complaints, so nvi conforms to System III/V historic practice 450 * except that we report a yank of 1 line if report is set to 1. 451 */ 452 #define ARSIZE(a) sizeof(a) / sizeof (*a) 453 #define MAXNUM 25 454 rptval = O_VAL(sp, O_REPORT); 455 for (cnt = 0, total = 0; cnt < ARSIZE(action); ++cnt) 456 total += sp->rptlines[cnt]; 457 if (total == 0) 458 return; 459 if (total <= rptval && sp->rptlines[L_YANKED] < rptval) { 460 for (cnt = 0; cnt < ARSIZE(action); ++cnt) 461 sp->rptlines[cnt] = 0; 462 return; 463 } 464 465 /* Build and display the message. */ 466 GET_SPACE_GOTO(sp, bp, blen, sizeof(action) * MAXNUM + 1); 467 for (p = bp, first = 1, tlen = 0, 468 ap = action, cnt = 0; cnt < ARSIZE(action); ++ap, ++cnt) 469 if (sp->rptlines[cnt] != 0) { 470 if (first) 471 first = 0; 472 else { 473 *p++ = ';'; 474 *p++ = ' '; 475 tlen += 2; 476 } 477 len = snprintf(p, MAXNUM, "%lu ", sp->rptlines[cnt]); 478 p += len; 479 tlen += len; 480 t = msg_cat(sp, 481 lines[sp->rptlines[cnt] == 1 ? 0 : 1], &len); 482 memcpy(p, t, len); 483 p += len; 484 tlen += len; 485 *p++ = ' '; 486 ++tlen; 487 t = msg_cat(sp, *ap, &len); 488 memcpy(p, t, len); 489 p += len; 490 tlen += len; 491 sp->rptlines[cnt] = 0; 492 } 493 494 /* Add trailing newline. */ 495 *p = '\n'; 496 ++tlen; 497 498 (void)ex_fflush(sp); 499 sp->gp->scr_msg(sp, M_INFO, bp, tlen); 500 501 FREE_SPACE(sp, bp, blen); 502 alloc_err: 503 return; 504 505 #undef ARSIZE 506 #undef MAXNUM 507 } 508 509 /* 510 * msgq_status -- 511 * Report on the file's status. 512 * 513 * PUBLIC: void msgq_status __P((SCR *, recno_t, u_int)); 514 */ 515 void 516 msgq_status(sp, lno, flags) 517 SCR *sp; 518 recno_t lno; 519 u_int flags; 520 { 521 static int poisoned; 522 recno_t last; 523 size_t blen, len; 524 int cnt, needsep; 525 const char *t; 526 char **ap, *bp, *np, *p, *s; 527 528 /* Get sufficient memory. */ 529 len = strlen(sp->frp->name); 530 GET_SPACE_GOTO(sp, bp, blen, len * MAX_CHARACTER_COLUMNS + 128); 531 p = bp; 532 533 /* Copy in the filename. */ 534 for (p = bp, t = sp->frp->name; *t != '\0'; ++t) { 535 len = KEY_LEN(sp, *t); 536 memcpy(p, KEY_NAME(sp, *t), len); 537 p += len; 538 } 539 np = p; 540 *p++ = ':'; 541 *p++ = ' '; 542 543 /* Copy in the argument count. */ 544 if (F_ISSET(sp, SC_STATUS_CNT) && sp->argv != NULL) { 545 for (cnt = 0, ap = sp->argv; *ap != NULL; ++ap, ++cnt); 546 if (cnt > 1) { 547 (void)sprintf(p, 548 msg_cat(sp, "317|%d files to edit", NULL), cnt); 549 p += strlen(p); 550 *p++ = ':'; 551 *p++ = ' '; 552 } 553 F_CLR(sp, SC_STATUS_CNT); 554 } 555 556 /* 557 * See nvi/exf.c:file_init() for a description of how and when the 558 * read-only bit is set. 559 * 560 * !!! 561 * The historic display for "name changed" was "[Not edited]". 562 */ 563 needsep = 0; 564 if (F_ISSET(sp->frp, FR_NEWFILE)) { 565 F_CLR(sp->frp, FR_NEWFILE); 566 t = msg_cat(sp, "021|new file", &len); 567 memcpy(p, t, len); 568 p += len; 569 needsep = 1; 570 } else { 571 if (F_ISSET(sp->frp, FR_NAMECHANGE)) { 572 t = msg_cat(sp, "022|name changed", &len); 573 memcpy(p, t, len); 574 p += len; 575 needsep = 1; 576 } 577 if (needsep) { 578 *p++ = ','; 579 *p++ = ' '; 580 } 581 if (F_ISSET(sp->ep, F_MODIFIED)) 582 t = msg_cat(sp, "023|modified", &len); 583 else 584 t = msg_cat(sp, "024|unmodified", &len); 585 memcpy(p, t, len); 586 p += len; 587 needsep = 1; 588 } 589 if (F_ISSET(sp->frp, FR_UNLOCKED)) { 590 if (needsep) { 591 *p++ = ','; 592 *p++ = ' '; 593 } 594 t = msg_cat(sp, "025|UNLOCKED", &len); 595 memcpy(p, t, len); 596 p += len; 597 needsep = 1; 598 } 599 if (O_ISSET(sp, O_READONLY)) { 600 if (needsep) { 601 *p++ = ','; 602 *p++ = ' '; 603 } 604 t = msg_cat(sp, "026|readonly", &len); 605 memcpy(p, t, len); 606 p += len; 607 needsep = 1; 608 } 609 if (needsep) { 610 *p++ = ':'; 611 *p++ = ' '; 612 } 613 if (LF_ISSET(MSTAT_SHOWLAST)) { 614 if (db_last(sp, &last)) 615 return; 616 if (last == 0) { 617 t = msg_cat(sp, "028|empty file", &len); 618 memcpy(p, t, len); 619 p += len; 620 } else { 621 t = msg_cat(sp, "027|line %lu of %lu [%ld%%]", &len); 622 (void)sprintf(p, t, lno, last, (lno * 100) / last); 623 p += strlen(p); 624 } 625 } else { 626 t = msg_cat(sp, "029|line %lu", &len); 627 (void)sprintf(p, t, lno); 628 p += strlen(p); 629 } 630 #ifdef DEBUG 631 (void)sprintf(p, " (pid %lu)", (u_long)getpid()); 632 p += strlen(p); 633 #endif 634 *p++ = '\n'; 635 len = p - bp; 636 637 /* 638 * There's a nasty problem with long path names. Cscope and tags files 639 * can result in long paths and vi will request a continuation key from 640 * the user as soon as it starts the screen. Unfortunately, the user 641 * has already typed ahead, and chaos results. If we assume that the 642 * characters in the filenames and informational messages only take a 643 * single screen column each, we can trim the filename. 644 * 645 * XXX 646 * Status lines get put up at fairly awkward times. For example, when 647 * you do a filter read (e.g., :read ! echo foo) in the top screen of a 648 * split screen, we have to repaint the status lines for all the screens 649 * below the top screen. We don't want users having to enter continue 650 * characters for those screens. Make it really hard to screw this up. 651 */ 652 s = bp; 653 if (LF_ISSET(MSTAT_TRUNCATE) && len > sp->cols) { 654 for (; s < np && (*s != '/' || (p - s) > sp->cols - 3); ++s); 655 if (s == np) { 656 s = p - (sp->cols - 5); 657 *--s = ' '; 658 } 659 *--s = '.'; 660 *--s = '.'; 661 *--s = '.'; 662 len = p - s; 663 } 664 665 /* Flush any waiting ex messages. */ 666 (void)ex_fflush(sp); 667 668 sp->gp->scr_msg(sp, M_INFO, s, len); 669 670 FREE_SPACE(sp, bp, blen); 671 alloc_err: 672 return; 673 } 674 675 /* 676 * msg_open -- 677 * Open the message catalogs. 678 * 679 * PUBLIC: int msg_open __P((SCR *, char *)); 680 */ 681 int 682 msg_open(sp, file) 683 SCR *sp; 684 char *file; 685 { 686 /* 687 * !!! 688 * Assume that the first file opened is the system default, and that 689 * all subsequent ones user defined. Only display error messages 690 * if we can't open the user defined ones -- it's useful to know if 691 * the system one wasn't there, but if nvi is being shipped with an 692 * installed system, the file will be there, if it's not, then the 693 * message will be repeated every time nvi is started up. 694 */ 695 static int first = 1; 696 DB *db; 697 DBT data, key; 698 recno_t msgno; 699 char *p, *t, buf[MAXPATHLEN]; 700 701 if ((p = strrchr(file, '/')) != NULL && p[1] == '\0' && 702 ((t = getenv("LC_MESSAGES")) != NULL && t[0] != '\0' || 703 (t = getenv("LANG")) != NULL && t[0] != '\0')) { 704 (void)snprintf(buf, sizeof(buf), "%s%s", file, t); 705 p = buf; 706 } else 707 p = file; 708 if ((db = dbopen(p, 709 O_NONBLOCK | O_RDONLY, 0, DB_RECNO, NULL)) == NULL) { 710 if (first) { 711 first = 0; 712 return (1); 713 } 714 msgq_str(sp, M_SYSERR, p, "%s"); 715 return (1); 716 } 717 718 /* 719 * Test record 1 for the magic string. The msgq call is here so 720 * the message catalog build finds it. 721 */ 722 #define VMC "VI_MESSAGE_CATALOG" 723 key.data = &msgno; 724 key.size = sizeof(recno_t); 725 msgno = 1; 726 if (db->get(db, &key, &data, 0) != 0 || 727 data.size != sizeof(VMC) - 1 || 728 memcmp(data.data, VMC, sizeof(VMC) - 1)) { 729 (void)db->close(db); 730 if (first) { 731 first = 0; 732 return (1); 733 } 734 msgq_str(sp, M_ERR, p, 735 "030|The file %s is not a message catalog"); 736 return (1); 737 } 738 first = 0; 739 740 if (sp->gp->msg != NULL) 741 (void)sp->gp->msg->close(sp->gp->msg); 742 sp->gp->msg = db; 743 return (0); 744 } 745 746 /* 747 * msg_close -- 748 * Close the message catalogs. 749 * 750 * PUBLIC: void msg_close __P((GS *)); 751 */ 752 void 753 msg_close(gp) 754 GS *gp; 755 { 756 if (gp->msg != NULL) 757 (void)gp->msg->close(gp->msg); 758 } 759 760 /* 761 * msg_cont -- 762 * Return common continuation messages. 763 * 764 * PUBLIC: const char *msg_cmsg __P((SCR *, cmsg_t, size_t *)); 765 */ 766 const char * 767 msg_cmsg(sp, which, lenp) 768 SCR *sp; 769 cmsg_t which; 770 size_t *lenp; 771 { 772 switch (which) { 773 case CMSG_CONF: 774 return (msg_cat(sp, "268|confirm? [ynq]", lenp)); 775 case CMSG_CONT: 776 return (msg_cat(sp, "269|Press any key to continue: ", lenp)); 777 case CMSG_CONT_EX: 778 return (msg_cat(sp, 779 "270|Press any key to continue [: to enter more ex commands]: ", 780 lenp)); 781 case CMSG_CONT_R: 782 return (msg_cat(sp, "161|Press Enter to continue: ", lenp)); 783 case CMSG_CONT_S: 784 return (msg_cat(sp, "275| cont?", lenp)); 785 case CMSG_CONT_Q: 786 return (msg_cat(sp, 787 "271|Press any key to continue [q to quit]: ", lenp)); 788 default: 789 abort(); 790 } 791 /* NOTREACHED */ 792 } 793 794 /* 795 * msg_cat -- 796 * Return a single message from the catalog, plus its length. 797 * 798 * !!! 799 * Only a single catalog message can be accessed at a time, if multiple 800 * ones are needed, they must be copied into local memory. 801 * 802 * PUBLIC: const char *msg_cat __P((SCR *, const char *, size_t *)); 803 */ 804 const char * 805 msg_cat(sp, str, lenp) 806 SCR *sp; 807 const char *str; 808 size_t *lenp; 809 { 810 GS *gp; 811 DBT data, key; 812 recno_t msgno; 813 814 /* 815 * If it's not a catalog message, i.e. has doesn't have a leading 816 * number and '|' symbol, we're done. 817 */ 818 if (isdigit(str[0]) && 819 isdigit(str[1]) && isdigit(str[2]) && str[3] == '|') { 820 key.data = &msgno; 821 key.size = sizeof(recno_t); 822 msgno = atoi(str); 823 824 /* 825 * XXX 826 * Really sleazy hack -- we put an extra character on the 827 * end of the format string, and then we change it to be 828 * the nul termination of the string. There ought to be 829 * a better way. Once we can allocate multiple temporary 830 * memory buffers, maybe we can use one of them instead. 831 */ 832 gp = sp == NULL ? NULL : sp->gp; 833 if (gp != NULL && gp->msg != NULL && 834 gp->msg->get(gp->msg, &key, &data, 0) == 0 && 835 data.size != 0) { 836 if (lenp != NULL) 837 *lenp = data.size - 1; 838 ((char *)data.data)[data.size - 1] = '\0'; 839 return (data.data); 840 } 841 str = &str[4]; 842 } 843 if (lenp != NULL) 844 *lenp = strlen(str); 845 return (str); 846 } 847 848 /* 849 * msg_print -- 850 * Return a printable version of a string, in allocated memory. 851 * 852 * PUBLIC: char *msg_print __P((SCR *, const char *, int *)); 853 */ 854 char * 855 msg_print(sp, s, needfree) 856 SCR *sp; 857 const char *s; 858 int *needfree; 859 { 860 size_t blen, nlen; 861 const char *cp; 862 char *bp, *ep, *p, *t; 863 864 *needfree = 0; 865 866 for (cp = s; *cp != '\0'; ++cp) 867 if (!isprint(*cp)) 868 break; 869 if (*cp == '\0') 870 return ((char *)s); /* SAFE: needfree set to 0. */ 871 872 nlen = 0; 873 if (0) { 874 retry: if (sp == NULL) 875 free(bp); 876 else 877 FREE_SPACE(sp, bp, blen); 878 needfree = 0; 879 } 880 nlen += 256; 881 if (sp == NULL) { 882 if ((bp = malloc(nlen)) == NULL) 883 goto alloc_err; 884 } else 885 GET_SPACE_GOTO(sp, bp, blen, nlen); 886 if (0) { 887 alloc_err: return (""); 888 } 889 *needfree = 1; 890 891 for (p = bp, ep = (bp + blen) - 1, cp = s; *cp != '\0' && p < ep; ++cp) 892 for (t = KEY_NAME(sp, *cp); *t != '\0' && p < ep; *p++ = *t++); 893 if (p == ep) 894 goto retry; 895 *p = '\0'; 896 return (bp); 897 } 898