Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 618 lines (554 sloc) 15.146 kb
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
1 /* message.c */
2 /* Copyright 1995 by Steve Kirkendall */
3
3a9bb55 Martin Dietze Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
4 char id_message[] = "$Id: message.c,v 2.39 1999/04/08 22:11:43 steve Exp $";
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
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];
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
18 static FILE *fperr, *fpinfo;
19 static BOOLEAN msghiding;
20
21 /* redirect messages to a log file. If "filename" is NULL then revert to
22 * the normal reporting (stdout and stderr)
23 */
24 void msglog(filename)
25 char *filename;
26 {
27 /* if previously redirected, then stop redirection now */
28 if (fperr != NULL && fperr != stderr)
29 fclose(fperr);
30
31 /* open the log file, if any */
32 fperr = fpinfo = filename ? fopen(filename, "w") : NULL;
33 }
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
34
3a9bb55 Martin Dietze Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
35 /* These are used for generating an error message which includes a file name
36 * and line number, whenever possible.
37 */
38 static char *scriptnamedup;
39 static long scriptline;
40 static BOOLEAN scriptknown;
41
42 /* This is called by other code to inform the message module of the location
43 * of the current command. This allows the message module to report the file
44 * name (or some other name, if not from a file) and line number (usually)
45 * as part of an error message. When executing commands from a buffer or
46 * scanned string, "mark" is the value returned by scanmark() for the scanning
47 * pointer; otherwise, "mark" should be NULL. For strings, the "name" value
48 * should describe the origin of the string: an alias name, or something else
49 * for other strings.
50 */
51 void msgscriptline(MARK mark, char *name)
52 {
53 char newname[300];
54
55 if (!mark || !markbuffer(mark))
56 {
57 if (name)
58 {
59 if (!scriptnamedup || strcmp(scriptnamedup, name))
60 {
61 free(scriptnamedup);
62 sprintf(newname, "\"%s\"", name);
63 scriptnamedup = strdup(newname);
64 if (!mark || mark->offset == 0)
65 scriptline = 1;
66 else
67 scriptline++;
68 }
69 scriptknown = True;
70 return;
71 }
72 else if (!scriptnamedup)
73 scriptnamedup = strdup("unknown script");
74 scriptline++;
75 scriptknown = False;
76 return;
77 }
78 if (o_filename(markbuffer(mark)))
79 strcpy(newname, tochar8(o_filename(markbuffer(mark))));
80 else
81 sprintf(newname, "(%s)", tochar8(o_bufname(markbuffer(mark))));
82 if (!scriptnamedup || strcmp(newname, scriptnamedup))
83 {
84 free(scriptnamedup);
85 scriptnamedup = strdup(newname);
86 }
87 scriptline = markline(mark);
88 scriptknown = True;
89 }
90
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
91 /* Copy a message into static verbose[] buffer, declared at the top of this
92 * file. If a buffer named "Elvis messages" exists, translate the message via
93 * that buffer along the way.
94 */
95 static void translate(terse)
96 char *terse; /* terse form of error message */
97 {
98 BUFFER buf; /* the "Elvis messages" buffer */
99 MARKBUF mark; /* the start of the buffer */
100 CHAR *scan; /* used for scanning the buffer */
101 CHAR *build; /* used for copying chars into the verbose[] buffer */
102 BOOLEAN bol; /* are we at the start of a line? */
103 int match; /* used for counting characters that match */
104
105 /* Copy the terse string into the verbose buffer, as a default */
3a9bb55 Martin Dietze Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
106 for (build = verbose, match = 0;
107 build < &verbose[QTY(verbose) - 1] && terse[match];
108 )
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
109 {
110 *build++ = terse[match++];
111 }
112 *build = '\0';
113
114 /* if the "terse" option is on, then we're done */
115 if (o_terse)
116 {
117 return;
118 }
119
120 /* Find the "Elvis messages" buffer. If it doesn't exist, then
121 * no more translation is necessary.
122 */
123 buf = buffind(toCHAR(MSG_BUF));
124 if (!buf)
125 {
126 return;
127 }
128
129 /* Scan the buffer for a line which starts with the terse message
130 * followed by a colon. If there is no such line, then we're done.
131 */
132 for (scanalloc(&scan, marktmp(mark, buf, 0L)), match = 0, bol = True;
133 scan;
134 scannext(&scan))
135 {
136 /* if this is a newline, then set "bol" and zero "match" */
137 if (*scan == '\n')
138 {
139 bol = True;
140 match = 0;
141 continue;
142 }
143
144 /* if we're in the middle of a match, then check to see if
145 * this position in "Elvis messages" matches the terse message.
146 */
147 if (match >= 0)
148 {
149 if (*scan != (terse[match] ? terse[match] : ':'))
150 {
151 match = -1;
152 continue;
153 }
154 if (!terse[match])
155 {
156 break;
157 }
158 match++;
159 }
160 }
161
162 /* if we get here and "scan" isn't NULL, then we've found the line
163 * that translates this terse message and "scan" is pointing at the
164 * ':' that marks the end of the terse text. Copy the verbose text
165 * after the ':' into the verbose[] variable.
166 */
167 if (scan)
168 {
169 /* skip the ':' */
170 scannext(&scan);
171
172 /* at this point, the previous character was not a newline */
173 bol = False;
174
175 /* copy the verbose message from the buffer */
176 for (build = verbose; build < &verbose[QTY(verbose) - 1]; )
177 {
178 /* if non-whitespace, then copy the character */
179 if (*scan != ' ' && *scan != '\t' && *scan != '\n')
180 {
181 *build++ = *scan;
182 scannext(&scan);
183 continue;
184 }
185
186 /* skip whitespace */
187 while (scan && (*scan == ' ' || *scan == '\t' || *scan == '\n'))
188 {
189 bol = (*scan == '\n') ? True : False;
190 scannext(&scan);
191 }
192
193 /* if this non-whitespace character appeared right after
194 * a newline, then we're done. This is because an
195 * unindented line in this file always marks the start
196 * of the next terse message. We're also done if we
197 * hit the end of the buffer.
198 */
199 if (!scan || bol)
200 {
201 break;
202 }
203
204 /* whitespace is converted into a single blank
205 * character, except at the very beginning of the
206 * message where it is deleted completely.
207 */
208 if (build != verbose)
209 {
210 *build++ = ' ';
211 }
212 }
213 *build = '\0';
214 }
215
216 /* clean up */
217 scanfree(&scan);
218 }
219
220
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
221 /* Set the message hiding flag to a given value & return its previous value */
222 BOOLEAN msghide(hide)
223 BOOLEAN hide; /* should we hide messages? (else reveal them) */
224 {
225 BOOLEAN previous = msghiding;
226
227 msghiding = hide;
228 return previous;
229 }
230
231
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
232 /* output a message via the GUI. Before calling the GUI, it subjects
233 * the terse message to a series of transformations. First, the
234 * buffer "Elvis messages" is scanned to perform a user-configurable
235 * transformation, such as translating it into a native language.
236 * Then the message is evaluated via the calculate() function with
237 * its "asmsg" parameter set to True.
238 *
239 * The arg[] array passed into calculate() is built from the extra arguments
240 * supplied to msg. The beginning of the terse string can begin with a list
241 * of characters enclosed in square brackets to indicate how the arguments
242 * are to be converted to text strings. The conversion letters are:
243 * d The argument is a (long int), to be shown as a decimal number
244 * s The argument is a (char *)
245 * S The argument is a (CHAR *)
246 * c The argument is a (char), to be shown as a string of length 1
247 * C The argument is a (CHAR), to be shown as a string of length 1
248 * If no bracketted list appears at the start of the string, then it is assumed
249 * that the message has no extra arguments.
250 *
251 * For example, msg(MSG_info, "[s]\$1=$1, list=(list)", "foo") will output
252 * "$1=foo, list=false"
253 */
254 #if USE_PROTOTYPES
255 void msg(MSGIMP imp, char *terse, ...)
256 {
257 #else
258 void msg(imp, terse, va_alist)
259 MSGIMP imp; /* message type */
260 char *terse; /* terse form of message (may contain %s or %d) */
261 va_dcl
262 {
263 #endif
264 va_list argptr;
265 CHAR *scan;
266 CHAR *arg[10];
267 char text[12], *str;
268 int i;
269 BUFFER buf;
270 MARKBUF mark;
271 BOOLEAN ding;
272
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
273 /* if fperr and fpinfo are NULL, then use stderr and stdout */
274 if (!fperr)
275 {
276 fperr = stderr;
277 fpinfo = stdout;
278 }
279
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
280 /* can't nest msg() calls. If another call is in progress, exit now */
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
281 if (*verbose || (msghiding && (imp == MSG_ERROR || imp == MSG_WARNING)))
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
282 {
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
283 if (imp == MSG_FATAL)
284 {
285 fprintf(fperr, "%s\n", terse);
286 o_tempsession = False;
287 sesclose();
288 if (gui) (*gui->term)();
289 #ifdef NDEBUG
290 exit(1);
291 #else
292 abort();
293 #endif
294 }
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
295 return;
296 }
297
298 /* Convert any arguments to CHAR strings */
299 if (*terse == '[')
300 {
301 #if USE_PROTOTYPES
302 va_start(argptr, terse);
303 #else
304 va_start(argptr);
305 #endif
306 for (i = 0, terse++; *terse != ']'; i++, terse++)
307 {
308 assert(i < QTY(arg));
309
310 /* convert argument to a CHAR string */
311 switch (*terse)
312 {
313 case 'd':
314 sprintf(text, "%ld", va_arg(argptr, long));
315 arg[i] = toCHAR(text);
316 break;
317
318 case 'S':
319 arg[i] = va_arg(argptr, CHAR *);
320 if (!arg[i])
321 arg[i] = toCHAR("NULL");
322 break;
323
324 case 's':
325 str = va_arg(argptr, char *);
326 if (!str)
327 str = "NULL";
328 arg[i] = toCHAR(str);
329 break;
330
331 case 'c':
332 text[0] = va_arg(argptr, _char_);
333 text[1] = '\0';
334 arg[i] = toCHAR(text);
335 break;
336
337 case 'C':
338 text[0] = va_arg(argptr, _CHAR_);
339 text[1] = '\0';
340 arg[i] = toCHAR(text);
341 break;
342
343 default:
344 /* elvis source code should never have a
345 * bad format code.
346 */
347 abort();
348 }
349
350 /* dynamically allocate a copy of that string. This
351 * is done because some parameter types use the text[]
352 * buffer, and a later argument may need to reuse that
353 * buffer.
354 */
355 arg[i] = CHARdup(arg[i]);
356 }
357 va_end(argptr);
358 arg[i] = NULL;
359
360 /* move the terse pointer past the closing ']' character */
361 assert(*terse == ']');
362 terse++;
363 }
364 else /* no bracketted list at start of terse string */
365 {
366 /* no extra arguments */
367 arg[0] = NULL;
368 }
369
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
370 if (imp == MSG_FATAL && !arg[0])
371 {
372 /* set "scan" to the message text */
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
373 scan = toCHAR(terse);
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
374 }
375 else
376 {
377 /* translate the terse message via "Elvis messages" buffer */
378 translate(terse);
379
380 /* expand any arguments or option names */
381 scan = calculate(verbose, arg, True);
382 if (!scan)
383 scan = calculate(toCHAR(terse), arg, True);
384 if (!scan)
385 scan = toCHAR(terse);
386 }
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
387
388 /* If it starts with a ^G character, then ring the bell. Also
389 * ring the bell if errorbells or warningbells is set
390 */
391 switch (imp)
392 {
393 case MSG_ERROR: ding = o_errorbells; break;
394 case MSG_WARNING: ding = o_warningbells; break;
395 default: ding = False;
396 }
397 if (*scan == ELVCTRL('G'))
398 {
399 scan++;
400 ding = True;
401 }
402
3a9bb55 Martin Dietze Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
403 /* if warning or error from a script, then put a line number a the
404 * start of the message.
405 */
406 if (scan != verbose
407 && (o_verbose >= 1 && scriptnamedup)
408 && (o_verbose >= 8
409 || ((imp == MSG_WARNING || imp == MSG_ERROR || imp == MSG_FATAL)
410 && (strlen(scriptnamedup) != strlen(EX_BUF) + 2 || strncmp(scriptnamedup + 1, EX_BUF, strlen(EX_BUF))))))
411 {
412 sprintf(tochar8(verbose), "%s, line %ld%s: ",
413 scriptnamedup, scriptline, scriptknown ? "" : "?");
414 }
415 else
416 *verbose = '\0';
417
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
418 /* copy the string into verbose[] */
3a9bb55 Martin Dietze Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
419 CHARncat(verbose, scan, QTY(verbose) - 1 - CHARlen(verbose));
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
420 verbose[QTY(verbose) - 1] = '\0';
421
422 /* free the arg[] strings */
423 for (i = 0; arg[i]; i++)
424 {
425 safefree(arg[i]);
426 }
427
428 /* Status and fatal messages are shown immediately, without flushing
429 * the message buffer. During the initialization phase, other messages
430 * may also be output immediately.
431 */
432 if (!verbose[0] && imp != MSG_FATAL)
433 {
434 /* ignore it. No output */
435 }
3a9bb55 Martin Dietze Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
436 else if ((o_verbose >= 1 && !windefault)
437 || !gui
438 /* || (eventcounter <= 1 && imp == MSG_ERROR)*/
439 || imp == MSG_STATUS
440 || imp == MSG_FATAL)
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
441 {
442 /* show the message */
443 if (gui && windefault)
444 {
445 /* Either the GUI will show it, or we will */
446 if (!gui->msg || !(*gui->msg)(windefault->gw, imp, verbose, (int)(scan - verbose)))
447 {
448 /* we have to show it... on bottom of window? */
449 drawmsg(windefault, imp, verbose, (int)CHARlen(verbose));
450 }
451
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
452 /* For fatal error messages, also write it to fperr */
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
453 if (imp == MSG_FATAL)
454 {
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
455 fprintf(fperr, "%s\n", verbose);
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
456 }
457 }
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
458 else if (!gui || !gui->msg || !gui->exonly || !(*gui->msg)(0, imp, verbose, (int)(scan - verbose)))
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
459 {
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
460 /* no GUI yet, so just write it to fpinfo/fperr */
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
461 if (imp == MSG_FATAL)
462 {
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
463 fprintf(fperr, "%s\n", verbose);
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
464 }
465 else
466 {
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
467 fprintf(fpinfo, "%s\r\n", verbose);
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
468 }
469 }
470
471 /* clean up & exit */
472 if (imp == MSG_FATAL)
473 {
474 o_tempsession = False;
475 sesclose();
476 if (gui) (*gui->term)();
477 #ifdef NDEBUG
478 exit(1);
479 #else
480 abort();
481 #endif
482 }
483 }
484 else
485 {
486 /* append the message to the message buffer */
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
487 buf = bufalloc(toCHAR(MSGQUEUE_BUF), 0, True);
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
488 (void)marktmp(mark, buf, o_bufchars(buf));
489 bufreplace(&mark, &mark, toCHAR("\n"), 1L);
490 bufreplace(&mark, &mark, verbose, (long)CHARlen(verbose));
491 bufreplace(&mark, &mark, toCHAR(imp>=MSG_ERROR ? "n" : " "), 1L);
492 }
493
494 /* if error, then alert the terminal */
495 if (imp >= MSG_ERROR)
496 {
497 mapalert();
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
498 if ((optflags(o_exitcode) & OPT_SET) == 0 && eventcounter <= 1)
499 {
500 o_exitcode = 1;
501 }
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
502 }
503
504 /* if we're supposed to ring the bell, and this GUI has a bell,
505 * then ring it.
506 */
507 if (ding && gui && gui->beep && windefault)
508 {
509 guibeep(windefault);
510 }
511
512 /* Zero the first byte of verbose[], so we can tell that we aren't
513 * in the middle of a message anymore.
514 */
515 *verbose = '\0';
516 }
517
518
519 /* This function flushes messages from the message queue to the current
520 * window. This function should be called before outputting ex text,
521 * before reading keystrokes, and when exiting elvis, after the GUI has
522 * been shut down but before the session file has been closed.
523 */
524 void msgflush()
525 {
526 BUFFER buf;
527 MARK mark, end;
528 CHAR *cp;
529 int len;
530 MSGIMP imp;
531
532 /* if we have a GUI but no windows yet, then delay output */
533 if (gui && !windefault)
534 {
535 return;
536 }
537
538 /* locate the message queue buffer, if any. If it doesn't exist,
539 * or is empty, then we're done!
540 */
541 buf = buffind(toCHAR(MSGQUEUE_BUF));
542 if (!buf || o_bufchars(buf) == 0)
543 {
544 return;
545 }
546
547 /* Copy each line into the "verbose" buffer. For each one, display
548 * the message as either info or an error.
549 */
550 mark = markalloc(buf, 0);
551 for (scanalloc(&cp, mark), len = 0; cp; scannext(&cp))
552 {
553 if (*cp == '\n')
554 {
555 verbose[len] = '\0';
556 imp = (verbose[0]=='*' ? MSG_ERROR : MSG_INFO);
557
558 /* show the message */
559 if (gui && windefault)
560 {
561 /* Either the GUI will show it, or we will */
562 if (!gui->msg || !(*gui->msg)(windefault->gw, imp, verbose + 1, len - 1))
563 {
564 /* we have to show it... on bottom of window? */
565 drawmsg(windefault, imp, verbose + 1, len - 1);
566 }
567 }
568 else
569 {
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
570 /* no GUI yet, so just write it to fpinfo/fperr */
571 fprintf(imp >= MSG_ERROR ? fperr : fpinfo,
cf92e3b Martin Dietze Import Elvis 2.0 (written by Steve Kirkendall)
authored
572 "%s\n", verbose + 1);
573 }
574 len = 0;
575 }
576 else if (len < QTY(verbose) - 2)
577 {
578 verbose[len++] = *cp;
579 }
580 }
581 scanfree(&cp);
582
583 /* cleanup */
584 end = markalloc(buf, o_bufchars(buf));
585 bufreplace(mark, end, NULL, 0);
586 markfree(mark);
587 markfree(end);
588 *verbose = '\0';
589 }
8d1ac0c Martin Dietze Import Elvis 2.1 (written by Steve Kirkendall)
authored
590
591
592 /* Translate a simple word or phrase, and return a dynamically-allocated
593 * copy of the result. This also has the side-effect of converting a
594 * (char *) to a (CHAR *).
595 *
596 * This is used mostly for setting some options to locale-sensitive defaults
597 */
598 CHAR *msgtranslate(word)
599 char *word;
600 {
601 CHAR *ret;
602
603 /* Translate it */
604 translate(word);
605
606 /* Make a dynamic copy of it */
607 ret = CHARkdup(verbose);
608
609 /* Zero the first byte of verbose[] so msg() doesn't think we're
610 * doing a message. That's important because msg() skips translation
611 * if an error message occurs while evaluating another message.
612 */
613 verbose[0] = '\0';
614
615 /* return the dynamically-allocated copy */
616 return ret;
617 }
Something went wrong with that request. Please try again.