source: trunk/third/nvi/common/msg.c @ 14302

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