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