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