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