1 /*- 2 * Copyright (c) 1991, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 1991, 1993, 1994, 1995, 1996 5 * Keith Bostic. All rights reserved. 6 * 7 * See the LICENSE file for redistribution information. 8 */ 9 10 #include "config.h" 11 12 #ifndef lint 13 static const char sccsid[] = "@(#)msg.c 10.48 (Berkeley) 9/15/96"; 14 #endif /* not lint */ 15 16 #include <sys/param.h> 17 #include <sys/types.h> /* XXX: param.h may not have included types.h */ 18 #include <sys/queue.h> 19 #include <sys/stat.h> 20 #include <sys/time.h> 21 22 #include <bitstring.h> 23 #include <ctype.h> 24 #include <errno.h> 25 #include <fcntl.h> 26 #include <limits.h> 27 #include <stdio.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <unistd.h> 31 32 #ifdef __STDC__ 33 #include <stdarg.h> 34 #else 35 #include <varargs.h> 36 #endif 37 38 #include "common.h" 39 #include "../vi/vi.h" 40 41 /* 42 * msgq -- 43 * Display a message. 44 * 45 * PUBLIC: void msgq __P((SCR *, mtype_t, const char *, ...)); 46 */ 47 void 48 #ifdef __STDC__ 49 msgq(SCR *sp, mtype_t mt, const char *fmt, ...) 50 #else 51 msgq(sp, mt, fmt, va_alist) 52 SCR *sp; 53 mtype_t mt; 54 const char *fmt; 55 va_dcl 56 #endif 57 { 58 #ifndef NL_ARGMAX 59 #define __NL_ARGMAX 20 /* Set to 9 by System V. */ 60 struct { 61 const char *str; /* String pointer. */ 62 size_t arg; /* Argument number. */ 63 size_t prefix; /* Prefix string length. */ 64 size_t skip; /* Skipped string length. */ 65 size_t suffix; /* Suffix string length. */ 66 } str[__NL_ARGMAX]; 67 #endif 68 static int reenter; /* STATIC: Re-entrancy check. */ 69 CHAR_T ch; 70 GS *gp; 71 size_t blen, cnt1, cnt2, len, mlen, nlen, soff; 72 const char *p, *t, *u; 73 char *bp, *mp, *rbp, *s_rbp; 74 va_list ap; 75 76 /* 77 * !!! 78 * It's possible to enter msg when there's no screen to hold the 79 * message. If sp is NULL, ignore the special cases and put the 80 * message out to stderr. 81 */ 82 if (sp == NULL) { 83 gp = NULL; 84 if (mt == M_BERR) 85 mt = M_ERR; 86 else if (mt == M_VINFO) 87 mt = M_INFO; 88 } else { 89 gp = sp->gp; 90 switch (mt) { 91 case M_BERR: 92 if (F_ISSET(sp, SC_VI) && !O_ISSET(sp, O_VERBOSE)) { 93 F_SET(gp, G_BELLSCHED); 94 return; 95 } 96 mt = M_ERR; 97 break; 98 case M_VINFO: 99 if (!O_ISSET(sp, O_VERBOSE)) 100 return; 101 mt = M_INFO; 102 /* FALLTHROUGH */ 103 case M_INFO: 104 if (F_ISSET(sp, SC_EX_SILENT)) 105 return; 106 break; 107 case M_ERR: 108 case M_SYSERR: 109 break; 110 default: 111 abort(); 112 } 113 } 114 115 /* 116 * It's possible to reenter msg when it allocates space. We're 117 * probably dead anyway, but there's no reason to drop core. 118 * 119 * XXX 120 * Yes, there's a race, but it should only be two instructions. 121 */ 122 if (reenter++) 123 return; 124 125 /* Get space for the message. */ 126 nlen = 1024; 127 if (0) { 128 retry: FREE_SPACE(sp, bp, blen); 129 nlen *= 2; 130 } 131 bp = NULL; 132 blen = 0; 133 GET_SPACE_GOTO(sp, bp, blen, nlen); 134 135 /* 136 * Error prefix. 137 * 138 * mp: pointer to the current next character to be written 139 * mlen: length of the already written characters 140 * blen: total length of the buffer 141 */ 142 #define REM (blen - mlen) 143 mp = bp; 144 mlen = 0; 145 if (mt == M_SYSERR) { 146 p = msg_cat(sp, "020|Error: ", &len); 147 if (REM < len) 148 goto retry; 149 memcpy(mp, p, len); 150 mp += len; 151 mlen += len; 152 } 153 154 /* 155 * If we're running an ex command that the user didn't enter, display 156 * the file name and line number prefix. 157 */ 158 if ((mt == M_ERR || mt == M_SYSERR) && 159 sp != NULL && gp != NULL && gp->if_name != NULL) { 160 for (p = gp->if_name; *p != '\0'; ++p) { 161 len = snprintf(mp, REM, "%s", KEY_NAME(sp, *p)); 162 mp += len; 163 if ((mlen += len) > blen) 164 goto retry; 165 } 166 len = snprintf(mp, REM, ", %d: ", gp->if_lno); 167 mp += len; 168 if ((mlen += len) > blen) 169 goto retry; 170 } 171 172 /* If nothing to format, we're done. */ 173 if (fmt == NULL) 174 goto nofmt; 175 fmt = msg_cat(sp, fmt, NULL); 176 177 #ifndef NL_ARGMAX 178 /* 179 * Nvi should run on machines that don't support the numbered argument 180 * specifications (%[digit]*$). We do this by reformatting the string 181 * so that we can hand it to vsprintf(3) and it will use the arguments 182 * in the right order. When vsprintf returns, we put the string back 183 * into the right order. It's undefined, according to SVID III, to mix 184 * numbered argument specifications with the standard style arguments, 185 * so this should be safe. 186 * 187 * In addition, we also need a character that is known to not occur in 188 * any vi message, for separating the parts of the string. As callers 189 * of msgq are responsible for making sure that all the non-printable 190 * characters are formatted for printing before calling msgq, we use a 191 * random non-printable character selected at terminal initialization 192 * time. This code isn't fast by any means, but as messages should be 193 * relatively short and normally have only a few arguments, it won't be 194 * too bad. Regardless, nobody has come up with any other solution. 195 * 196 * The result of this loop is an array of pointers into the message 197 * string, with associated lengths and argument numbers. The array 198 * is in the "correct" order, and the arg field contains the argument 199 * order. 200 */ 201 for (p = fmt, soff = 0; soff < __NL_ARGMAX;) { 202 for (t = p; *p != '\0' && *p != '%'; ++p); 203 if (*p == '\0') 204 break; 205 ++p; 206 if (!isdigit(*p)) { 207 if (*p == '%') 208 ++p; 209 continue; 210 } 211 for (u = p; *++p != '\0' && isdigit(*p);); 212 if (*p != '$') 213 continue; 214 215 /* Up to, and including the % character. */ 216 str[soff].str = t; 217 str[soff].prefix = u - t; 218 219 /* Up to, and including the $ character. */ 220 str[soff].arg = atoi(u); 221 str[soff].skip = (p - u) + 1; 222 if (str[soff].arg >= __NL_ARGMAX) 223 goto ret; 224 225 /* Up to, and including the conversion character. */ 226 for (u = p; (ch = *++p) != '\0';) 227 if (isalpha(ch) && 228 strchr("diouxXfeEgGcspn", ch) != NULL) 229 break; 230 str[soff].suffix = p - u; 231 if (ch != '\0') 232 ++p; 233 ++soff; 234 } 235 236 /* If no magic strings, we're done. */ 237 if (soff == 0) 238 goto format; 239 240 /* Get space for the reordered strings. */ 241 if ((rbp = malloc(nlen)) == NULL) 242 goto ret; 243 s_rbp = rbp; 244 245 /* 246 * Reorder the strings into the message string based on argument 247 * order. 248 * 249 * !!! 250 * We ignore arguments that are out of order, i.e. if we don't find 251 * an argument, we continue. Assume (almost certainly incorrectly) 252 * that whoever created the string knew what they were doing. 253 * 254 * !!! 255 * Brute force "sort", but since we don't expect more than one or two 256 * arguments in a string, the setup cost of a fast sort will be more 257 * expensive than the loop. 258 */ 259 for (cnt1 = 1; cnt1 <= soff; ++cnt1) 260 for (cnt2 = 0; cnt2 < soff; ++cnt2) 261 if (cnt1 == str[cnt2].arg) { 262 memmove(s_rbp, str[cnt2].str, str[cnt2].prefix); 263 memmove(s_rbp + str[cnt2].prefix, 264 str[cnt2].str + str[cnt2].prefix + 265 str[cnt2].skip, str[cnt2].suffix); 266 s_rbp += str[cnt2].prefix + str[cnt2].suffix; 267 *s_rbp++ = 268 gp == NULL ? DEFAULT_NOPRINT : gp->noprint; 269 break; 270 } 271 *s_rbp = '\0'; 272 fmt = rbp; 273 #endif 274 275 format: /* Format the arguments into the string. */ 276 #ifdef __STDC__ 277 va_start(ap, fmt); 278 #else 279 va_start(ap); 280 #endif 281 len = vsnprintf(mp, REM, fmt, ap); 282 va_end(ap); 283 if (len >= nlen) 284 goto retry; 285 286 #ifndef NL_ARGMAX 287 if (soff == 0) 288 goto nofmt; 289 290 /* 291 * Go through the resulting string, and, for each separator character 292 * separated string, enter its new starting position and length in the 293 * array. 294 */ 295 for (p = t = mp, cnt1 = 1, 296 ch = gp == NULL ? DEFAULT_NOPRINT : gp->noprint; *p != '\0'; ++p) 297 if (*p == ch) { 298 for (cnt2 = 0; cnt2 < soff; ++cnt2) 299 if (str[cnt2].arg == cnt1) 300 break; 301 str[cnt2].str = t; 302 str[cnt2].prefix = p - t; 303 t = p + 1; 304 ++cnt1; 305 } 306 307 /* 308 * Reorder the strings once again, putting them back into the 309 * message buffer. 310 * 311 * !!! 312 * Note, the length of the message gets decremented once for 313 * each substring, when we discard the separator character. 314 */ 315 for (s_rbp = rbp, cnt1 = 0; cnt1 < soff; ++cnt1) { 316 memmove(rbp, str[cnt1].str, str[cnt1].prefix); 317 rbp += str[cnt1].prefix; 318 --len; 319 } 320 memmove(mp, s_rbp, rbp - s_rbp); 321 322 /* Free the reordered string memory. */ 323 free(s_rbp); 324 #endif 325 326 nofmt: mp += len; 327 if ((mlen += len) > blen) 328 goto retry; 329 if (mt == M_SYSERR) { 330 len = snprintf(mp, REM, ": %s", strerror(errno)); 331 mp += len; 332 if ((mlen += len) > blen) 333 goto retry; 334 mt = M_ERR; 335 } 336 337 /* Add trailing newline. */ 338 if ((mlen += 1) > blen) 339 goto retry; 340 *mp = '\n'; 341 342 if (sp != NULL) 343 (void)ex_fflush(sp); 344 if (gp != NULL) 345 gp->scr_msg(sp, mt, bp, mlen); 346 else 347 (void)fprintf(stderr, "%.*s", (int)mlen, bp); 348 349 /* Cleanup. */ 350 ret: FREE_SPACE(sp, bp, blen); 351 alloc_err: 352 reenter = 0; 353 } 354 355 /* 356 * msgq_str -- 357 * Display a message with an embedded string. 358 * 359 * PUBLIC: void msgq_str __P((SCR *, mtype_t, char *, char *)); 360 */ 361 void 362 msgq_str(sp, mtype, str, fmt) 363 SCR *sp; 364 mtype_t mtype; 365 char *str, *fmt; 366 { 367 int nf, sv_errno; 368 char *p; 369 370 if (str == NULL) { 371 msgq(sp, mtype, fmt); 372 return; 373 } 374 375 sv_errno = errno; 376 p = msg_print(sp, str, &nf); 377 errno = sv_errno; 378 msgq(sp, mtype, fmt, p); 379 if (nf) 380 FREE_SPACE(sp, p, 0); 381 } 382 383 /* 384 * mod_rpt -- 385 * Report on the lines that changed. 386 * 387 * !!! 388 * Historic vi documentation (USD:15-8) claimed that "The editor will also 389 * always tell you when a change you make affects text which you cannot see." 390 * This wasn't true -- edit a large file and do "100d|1". We don't implement 391 * this semantic since it requires tracking each line that changes during a 392 * command instead of just keeping count. 393 * 394 * Line counts weren't right in historic vi, either. For example, given the 395 * file: 396 * abc 397 * def 398 * the command 2d}, from the 'b' would report that two lines were deleted, 399 * not one. 400 * 401 * PUBLIC: void mod_rpt __P((SCR *)); 402 */ 403 void 404 mod_rpt(sp) 405 SCR *sp; 406 { 407 static char * const action[] = { 408 "293|added", 409 "294|changed", 410 "295|deleted", 411 "296|joined", 412 "297|moved", 413 "298|shifted", 414 "299|yanked", 415 }; 416 static char * const lines[] = { 417 "300|line", 418 "301|lines", 419 }; 420 recno_t total; 421 u_long rptval; 422 int first, cnt; 423 size_t blen, len, tlen; 424 const char *t; 425 char * const *ap; 426 char *bp, *p; 427 428 /* Change reports are turned off in batch mode. */ 429 if (F_ISSET(sp, SC_EX_SILENT)) 430 return; 431 432 /* Reset changing line number. */ 433 sp->rptlchange = OOBLNO; 434 435 /* 436 * Don't build a message if not enough changed. 437 * 438 * !!! 439 * And now, a vi clone test. Historically, vi reported if the number 440 * of changed lines was > than the value, not >=, unless it was a yank 441 * command, which used >=. No lie. Furthermore, an action was never 442 * reported for a single line action. This is consistent for actions 443 * other than yank, but yank didn't report single line actions even if 444 * the report edit option was set to 1. In addition, setting report to 445 * 0 in the 4BSD historic vi was equivalent to setting it to 1, for an 446 * unknown reason (this bug was fixed in System III/V at some point). 447 * I got complaints, so nvi conforms to System III/V historic practice 448 * except that we report a yank of 1 line if report is set to 1. 449 */ 450 #define ARSIZE(a) sizeof(a) / sizeof (*a) 451 #define MAXNUM 25 452 rptval = O_VAL(sp, O_REPORT); 453 for (cnt = 0, total = 0; cnt < ARSIZE(action); ++cnt) 454 total += sp->rptlines[cnt]; 455 if (total == 0) 456 return; 457 if (total <= rptval && sp->rptlines[L_YANKED] < rptval) { 458 for (cnt = 0; cnt < ARSIZE(action); ++cnt) 459 sp->rptlines[cnt] = 0; 460 return; 461 } 462 463 /* Build and display the message. */ 464 GET_SPACE_GOTO(sp, bp, blen, sizeof(action) * MAXNUM + 1); 465 for (p = bp, first = 1, tlen = 0, 466 ap = action, cnt = 0; cnt < ARSIZE(action); ++ap, ++cnt) 467 if (sp->rptlines[cnt] != 0) { 468 if (first) 469 first = 0; 470 else { 471 *p++ = ';'; 472 *p++ = ' '; 473 tlen += 2; 474 } 475 len = snprintf(p, MAXNUM, "%lu ", 476 (u_long)sp->rptlines[cnt]); 477 p += len; 478 tlen += len; 479 t = msg_cat(sp, 480 lines[sp->rptlines[cnt] == 1 ? 0 : 1], &len); 481 memcpy(p, t, len); 482 p += len; 483 tlen += len; 484 *p++ = ' '; 485 ++tlen; 486 t = msg_cat(sp, *ap, &len); 487 memcpy(p, t, len); 488 p += len; 489 tlen += len; 490 sp->rptlines[cnt] = 0; 491 } 492 493 /* Add trailing newline. */ 494 *p = '\n'; 495 ++tlen; 496 497 (void)ex_fflush(sp); 498 sp->gp->scr_msg(sp, M_INFO, bp, tlen); 499 500 FREE_SPACE(sp, bp, blen); 501 alloc_err: 502 return; 503 504 #undef ARSIZE 505 #undef MAXNUM 506 } 507 508 /* 509 * msgq_status -- 510 * Report on the file's status. 511 * 512 * PUBLIC: void msgq_status __P((SCR *, recno_t, u_int)); 513 */ 514 void 515 msgq_status(sp, lno, flags) 516 SCR *sp; 517 recno_t lno; 518 u_int flags; 519 { 520 static int poisoned; 521 recno_t last; 522 size_t blen, len; 523 int cnt, needsep; 524 const char *t; 525 char **ap, *bp, *np, *p, *s; 526 527 /* Get sufficient memory. */ 528 len = strlen(sp->frp->name); 529 GET_SPACE_GOTO(sp, bp, blen, len * MAX_CHARACTER_COLUMNS + 128); 530 p = bp; 531 532 /* Copy in the filename. */ 533 for (p = bp, t = sp->frp->name; *t != '\0'; ++t) { 534 len = KEY_LEN(sp, *t); 535 memcpy(p, KEY_NAME(sp, *t), len); 536 p += len; 537 } 538 np = p; 539 *p++ = ':'; 540 *p++ = ' '; 541 542 /* Copy in the argument count. */ 543 if (F_ISSET(sp, SC_STATUS_CNT) && sp->argv != NULL) { 544 for (cnt = 0, ap = sp->argv; *ap != NULL; ++ap, ++cnt); 545 if (cnt > 1) { 546 (void)sprintf(p, 547 msg_cat(sp, "317|%d files to edit", NULL), cnt); 548 p += strlen(p); 549 *p++ = ':'; 550 *p++ = ' '; 551 } 552 F_CLR(sp, SC_STATUS_CNT); 553 } 554 555 /* 556 * See nvi/exf.c:file_init() for a description of how and when the 557 * read-only bit is set. 558 * 559 * !!! 560 * The historic display for "name changed" was "[Not edited]". 561 */ 562 needsep = 0; 563 if (F_ISSET(sp->frp, FR_NEWFILE)) { 564 F_CLR(sp->frp, FR_NEWFILE); 565 t = msg_cat(sp, "021|new file", &len); 566 memcpy(p, t, len); 567 p += len; 568 needsep = 1; 569 } else { 570 if (F_ISSET(sp->frp, FR_NAMECHANGE)) { 571 t = msg_cat(sp, "022|name changed", &len); 572 memcpy(p, t, len); 573 p += len; 574 needsep = 1; 575 } 576 if (needsep) { 577 *p++ = ','; 578 *p++ = ' '; 579 } 580 if (F_ISSET(sp->ep, F_MODIFIED)) 581 t = msg_cat(sp, "023|modified", &len); 582 else 583 t = msg_cat(sp, "024|unmodified", &len); 584 memcpy(p, t, len); 585 p += len; 586 needsep = 1; 587 } 588 if (F_ISSET(sp->frp, FR_UNLOCKED)) { 589 if (needsep) { 590 *p++ = ','; 591 *p++ = ' '; 592 } 593 t = msg_cat(sp, "025|UNLOCKED", &len); 594 memcpy(p, t, len); 595 p += len; 596 needsep = 1; 597 } 598 if (O_ISSET(sp, O_READONLY)) { 599 if (needsep) { 600 *p++ = ','; 601 *p++ = ' '; 602 } 603 t = msg_cat(sp, "026|readonly", &len); 604 memcpy(p, t, len); 605 p += len; 606 needsep = 1; 607 } 608 if (needsep) { 609 *p++ = ':'; 610 *p++ = ' '; 611 } 612 if (LF_ISSET(MSTAT_SHOWLAST)) { 613 if (db_last(sp, &last)) 614 return; 615 if (last == 0) { 616 t = msg_cat(sp, "028|empty file", &len); 617 memcpy(p, t, len); 618 p += len; 619 } else { 620 t = msg_cat(sp, "027|line %lu of %lu [%lu%%]", &len); 621 (void)sprintf(p, t, (u_long)lno, (u_long)last, 622 (u_long)(lno * 100) / last); 623 p += strlen(p); 624 } 625 } else { 626 t = msg_cat(sp, "029|line %lu", &len); 627 (void)sprintf(p, t, (u_long)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