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