Skip to content
Newer
Older
100644 463 lines (415 sloc) 10.7 KB
cf92e3b @mbert Import Elvis 2.0 (written by Steve Kirkendall)
authored Dec 10, 2011
1 /* message.c */
2 /* Copyright 1995 by Steve Kirkendall */
3
4 char id_message[] = "$Id: message.c,v 2.25 1996/06/28 01:37:02 steve Exp $";
5
6 #include "elvis.h"
7 #if USE_PROTOTYPES
8 # include <stdarg.h>
9 #else
10 # include <varargs.h>
11 #endif
12
13 #if USE_PROTOTYPES
14 static void translate(char *terse);
15 #endif
16
17 static CHAR verbose[200];
18
19 /* Copy a message into static verbose[] buffer, declared at the top of this
20 * file. If a buffer named "Elvis messages" exists, translate the message via
21 * that buffer along the way.
22 */
23 static void translate(terse)
24 char *terse; /* terse form of error message */
25 {
26 BUFFER buf; /* the "Elvis messages" buffer */
27 MARKBUF mark; /* the start of the buffer */
28 CHAR *scan; /* used for scanning the buffer */
29 CHAR *build; /* used for copying chars into the verbose[] buffer */
30 BOOLEAN bol; /* are we at the start of a line? */
31 int match; /* used for counting characters that match */
32
33 /* Copy the terse string into the verbose buffer, as a default */
34 for (build = verbose, match = 0; terse[match]; )
35 {
36 *build++ = terse[match++];
37 }
38 *build = '\0';
39
40 /* if the "terse" option is on, then we're done */
41 if (o_terse)
42 {
43 return;
44 }
45
46 /* Find the "Elvis messages" buffer. If it doesn't exist, then
47 * no more translation is necessary.
48 */
49 buf = buffind(toCHAR(MSG_BUF));
50 if (!buf)
51 {
52 return;
53 }
54
55 /* Scan the buffer for a line which starts with the terse message
56 * followed by a colon. If there is no such line, then we're done.
57 */
58 for (scanalloc(&scan, marktmp(mark, buf, 0L)), match = 0, bol = True;
59 scan;
60 scannext(&scan))
61 {
62 /* if this is a newline, then set "bol" and zero "match" */
63 if (*scan == '\n')
64 {
65 bol = True;
66 match = 0;
67 continue;
68 }
69
70 /* if we're in the middle of a match, then check to see if
71 * this position in "Elvis messages" matches the terse message.
72 */
73 if (match >= 0)
74 {
75 if (*scan != (terse[match] ? terse[match] : ':'))
76 {
77 match = -1;
78 continue;
79 }
80 if (!terse[match])
81 {
82 break;
83 }
84 match++;
85 }
86 }
87
88 /* if we get here and "scan" isn't NULL, then we've found the line
89 * that translates this terse message and "scan" is pointing at the
90 * ':' that marks the end of the terse text. Copy the verbose text
91 * after the ':' into the verbose[] variable.
92 */
93 if (scan)
94 {
95 /* skip the ':' */
96 scannext(&scan);
97
98 /* at this point, the previous character was not a newline */
99 bol = False;
100
101 /* copy the verbose message from the buffer */
102 for (build = verbose; build < &verbose[QTY(verbose) - 1]; )
103 {
104 /* if non-whitespace, then copy the character */
105 if (*scan != ' ' && *scan != '\t' && *scan != '\n')
106 {
107 *build++ = *scan;
108 scannext(&scan);
109 continue;
110 }
111
112 /* skip whitespace */
113 while (scan && (*scan == ' ' || *scan == '\t' || *scan == '\n'))
114 {
115 bol = (*scan == '\n') ? True : False;
116 scannext(&scan);
117 }
118
119 /* if this non-whitespace character appeared right after
120 * a newline, then we're done. This is because an
121 * unindented line in this file always marks the start
122 * of the next terse message. We're also done if we
123 * hit the end of the buffer.
124 */
125 if (!scan || bol)
126 {
127 break;
128 }
129
130 /* whitespace is converted into a single blank
131 * character, except at the very beginning of the
132 * message where it is deleted completely.
133 */
134 if (build != verbose)
135 {
136 *build++ = ' ';
137 }
138 }
139 *build = '\0';
140 }
141
142 /* clean up */
143 scanfree(&scan);
144 }
145
146
147 /* output a message via the GUI. Before calling the GUI, it subjects
148 * the terse message to a series of transformations. First, the
149 * buffer "Elvis messages" is scanned to perform a user-configurable
150 * transformation, such as translating it into a native language.
151 * Then the message is evaluated via the calculate() function with
152 * its "asmsg" parameter set to True.
153 *
154 * The arg[] array passed into calculate() is built from the extra arguments
155 * supplied to msg. The beginning of the terse string can begin with a list
156 * of characters enclosed in square brackets to indicate how the arguments
157 * are to be converted to text strings. The conversion letters are:
158 * d The argument is a (long int), to be shown as a decimal number
159 * s The argument is a (char *)
160 * S The argument is a (CHAR *)
161 * c The argument is a (char), to be shown as a string of length 1
162 * C The argument is a (CHAR), to be shown as a string of length 1
163 * If no bracketted list appears at the start of the string, then it is assumed
164 * that the message has no extra arguments.
165 *
166 * For example, msg(MSG_info, "[s]\$1=$1, list=(list)", "foo") will output
167 * "$1=foo, list=false"
168 */
169 #if USE_PROTOTYPES
170 void msg(MSGIMP imp, char *terse, ...)
171 {
172 #else
173 void msg(imp, terse, va_alist)
174 MSGIMP imp; /* message type */
175 char *terse; /* terse form of message (may contain %s or %d) */
176 va_dcl
177 {
178 #endif
179 va_list argptr;
180 CHAR *scan;
181 CHAR *arg[10];
182 char text[12], *str;
183 int i;
184 BUFFER buf;
185 MARKBUF mark;
186 BOOLEAN ding;
187
188 /* can't nest msg() calls. If another call is in progress, exit now */
189 if (*verbose)
190 {
191 return;
192 }
193
194 /* Convert any arguments to CHAR strings */
195 if (*terse == '[')
196 {
197 #if USE_PROTOTYPES
198 va_start(argptr, terse);
199 #else
200 va_start(argptr);
201 #endif
202 for (i = 0, terse++; *terse != ']'; i++, terse++)
203 {
204 assert(i < QTY(arg));
205
206 /* convert argument to a CHAR string */
207 switch (*terse)
208 {
209 case 'd':
210 sprintf(text, "%ld", va_arg(argptr, long));
211 arg[i] = toCHAR(text);
212 break;
213
214 case 'S':
215 arg[i] = va_arg(argptr, CHAR *);
216 if (!arg[i])
217 arg[i] = toCHAR("NULL");
218 break;
219
220 case 's':
221 str = va_arg(argptr, char *);
222 if (!str)
223 str = "NULL";
224 arg[i] = toCHAR(str);
225 break;
226
227 case 'c':
228 text[0] = va_arg(argptr, _char_);
229 text[1] = '\0';
230 arg[i] = toCHAR(text);
231 break;
232
233 case 'C':
234 text[0] = va_arg(argptr, _CHAR_);
235 text[1] = '\0';
236 arg[i] = toCHAR(text);
237 break;
238
239 default:
240 /* elvis source code should never have a
241 * bad format code.
242 */
243 abort();
244 }
245
246 /* dynamically allocate a copy of that string. This
247 * is done because some parameter types use the text[]
248 * buffer, and a later argument may need to reuse that
249 * buffer.
250 */
251 arg[i] = CHARdup(arg[i]);
252 }
253 va_end(argptr);
254 arg[i] = NULL;
255
256 /* move the terse pointer past the closing ']' character */
257 assert(*terse == ']');
258 terse++;
259 }
260 else /* no bracketted list at start of terse string */
261 {
262 /* no extra arguments */
263 arg[0] = NULL;
264 }
265
266 /* translate the terse message via "Elvis messages" buffer */
267 translate(terse);
268
269 /* expand any arguments or option names */
270 scan = calculate(verbose, arg, True);
271 if (!scan)
272 scan = calculate(toCHAR(terse), arg, True);
273 if (!scan)
274 scan = toCHAR(terse);
275
276 /* If it starts with a ^G character, then ring the bell. Also
277 * ring the bell if errorbells or warningbells is set
278 */
279 switch (imp)
280 {
281 case MSG_ERROR: ding = o_errorbells; break;
282 case MSG_WARNING: ding = o_warningbells; break;
283 default: ding = False;
284 }
285 if (*scan == ELVCTRL('G'))
286 {
287 scan++;
288 ding = True;
289 }
290
291 /* copy the string into verbose[] */
292 CHARncpy(verbose, scan, QTY(verbose) - 1);
293 verbose[QTY(verbose) - 1] = '\0';
294
295 /* free the arg[] strings */
296 for (i = 0; arg[i]; i++)
297 {
298 safefree(arg[i]);
299 }
300
301 /* Status and fatal messages are shown immediately, without flushing
302 * the message buffer. During the initialization phase, other messages
303 * may also be output immediately.
304 */
305 if (!verbose[0] && imp != MSG_FATAL)
306 {
307 /* ignore it. No output */
308 }
309 else if ((o_verbose && !windefault) || !gui
310 || (eventcounter <= 1 && imp == MSG_ERROR)
311 || imp == MSG_STATUS || imp == MSG_FATAL)
312 {
313 /* show the message */
314 if (gui && windefault)
315 {
316 /* Either the GUI will show it, or we will */
317 if (!gui->msg || !(*gui->msg)(windefault->gw, imp, verbose, (int)(scan - verbose)))
318 {
319 /* we have to show it... on bottom of window? */
320 drawmsg(windefault, imp, verbose, (int)CHARlen(verbose));
321 }
322
323 /* For fatal error messages, also write it to stderr */
324 if (imp == MSG_FATAL)
325 {
326 fprintf(stderr, "%s\n", verbose);
327 }
328 }
329 else
330 {
331 /* no GUI yet, so just write it to stdout/stderr */
332 #ifdef WIN16
333 fprintf(stderr, "%s\n", verbose);
334 #else
335 if (imp == MSG_FATAL)
336 {
337 fprintf(stderr, "%s\n", verbose);
338 }
339 else
340 {
341 printf("%s\r\n", verbose);
342 }
343 #endif
344 }
345
346 /* clean up & exit */
347 if (imp == MSG_FATAL)
348 {
349 o_tempsession = False;
350 sesclose();
351 if (gui) (*gui->term)();
352 #ifdef NDEBUG
353 exit(1);
354 #else
355 abort();
356 #endif
357 }
358 }
359 else
360 {
361 /* append the message to the message buffer */
362 buf = bufalloc(toCHAR(MSGQUEUE_BUF), 0);
363 o_internal(buf) = True;
364 (void)marktmp(mark, buf, o_bufchars(buf));
365 bufreplace(&mark, &mark, toCHAR("\n"), 1L);
366 bufreplace(&mark, &mark, verbose, (long)CHARlen(verbose));
367 bufreplace(&mark, &mark, toCHAR(imp>=MSG_ERROR ? "n" : " "), 1L);
368 }
369
370 /* if error, then alert the terminal */
371 if (imp >= MSG_ERROR)
372 {
373 mapalert();
374 o_exitcode = 1;
375 }
376
377 /* if we're supposed to ring the bell, and this GUI has a bell,
378 * then ring it.
379 */
380 if (ding && gui && gui->beep && windefault)
381 {
382 guibeep(windefault);
383 }
384
385 /* Zero the first byte of verbose[], so we can tell that we aren't
386 * in the middle of a message anymore.
387 */
388 *verbose = '\0';
389 }
390
391
392 /* This function flushes messages from the message queue to the current
393 * window. This function should be called before outputting ex text,
394 * before reading keystrokes, and when exiting elvis, after the GUI has
395 * been shut down but before the session file has been closed.
396 */
397 void msgflush()
398 {
399 BUFFER buf;
400 MARK mark, end;
401 CHAR *cp;
402 int len;
403 MSGIMP imp;
404
405 /* if we have a GUI but no windows yet, then delay output */
406 if (gui && !windefault)
407 {
408 return;
409 }
410
411 /* locate the message queue buffer, if any. If it doesn't exist,
412 * or is empty, then we're done!
413 */
414 buf = buffind(toCHAR(MSGQUEUE_BUF));
415 if (!buf || o_bufchars(buf) == 0)
416 {
417 return;
418 }
419
420 /* Copy each line into the "verbose" buffer. For each one, display
421 * the message as either info or an error.
422 */
423 mark = markalloc(buf, 0);
424 for (scanalloc(&cp, mark), len = 0; cp; scannext(&cp))
425 {
426 if (*cp == '\n')
427 {
428 verbose[len] = '\0';
429 imp = (verbose[0]=='*' ? MSG_ERROR : MSG_INFO);
430
431 /* show the message */
432 if (gui && windefault)
433 {
434 /* Either the GUI will show it, or we will */
435 if (!gui->msg || !(*gui->msg)(windefault->gw, imp, verbose + 1, len - 1))
436 {
437 /* we have to show it... on bottom of window? */
438 drawmsg(windefault, imp, verbose + 1, len - 1);
439 }
440 }
441 else
442 {
443 /* no GUI yet, so just write it to stdout/stderr */
444 fprintf(imp >= MSG_ERROR ? stderr : stdout,
445 "%s\n", verbose + 1);
446 }
447 len = 0;
448 }
449 else if (len < QTY(verbose) - 2)
450 {
451 verbose[len++] = *cp;
452 }
453 }
454 scanfree(&cp);
455
456 /* cleanup */
457 end = markalloc(buf, o_bufchars(buf));
458 bufreplace(mark, end, NULL, 0);
459 markfree(mark);
460 markfree(end);
461 *verbose = '\0';
462 }
Something went wrong with that request. Please try again.