GRASS GIS 8 Programmer's Manual 8.3.2(2024)-exported
Loading...
Searching...
No Matches
gis/error.c
Go to the documentation of this file.
1/*!
2 * \file lib/gis/error.c
3 *
4 * \brief GIS Library - Error messages functions
5 *
6 * (C) 1999-2011 by the GRASS Development Team
7 *
8 * This program is free software under the GNU General Public
9 * License (>=v2). Read the file COPYING that comes with GRASS
10 * for details.
11 *
12 * \author USACERL and many others
13 */
14
15#include <stdlib.h>
16#include <string.h>
17#include <setjmp.h>
18#include <unistd.h>
19#include <time.h>
20#include <stdarg.h>
21#include <sys/types.h>
22#include <grass/glocale.h>
23#include <grass/gis.h>
24
25#include "gis_local_proto.h"
26
27/*!
28 * \def MSG
29 *
30 * \brief A message
31 */
32#define MSG 0
33/*!
34 * \def WARN
35 *
36 * \brief A warning message
37 */
38#define WARN 1
39/*!
40 * \def ERR
41 *
42 * \brief A fatal error message
43 */
44#define ERR 2
45
46/* static int (*error)() = 0; */
47static int (*ext_error)(const char *, int); /* Roger Bivand 17 June 2000 */
48static int no_warn = FALSE;
49static int no_sleep = TRUE;
50
51static int grass_info_format;
52static char *logfile;
53static char *prefix_std[3];
54static struct Counter message_id;
55
56static int print_word(FILE *, char **, int *, const int);
57static void print_sentence(FILE *, const int, const char *);
58static void print_error(const char *, const int);
59static void mail_msg(const char *, int);
60static int write_error(const char *, int, time_t, const char *);
61static void log_error(const char *, int);
62
63static int fatal_longjmp;
64static jmp_buf fatal_jmp_buf;
65
66jmp_buf *G_fatal_longjmp(int enable)
67{
68 fatal_longjmp = enable;
69 return &fatal_jmp_buf;
70}
71
72static void vfprint_error(int type, const char *template, va_list ap)
73{
74 char *buffer = NULL;
75
76 G_vasprintf(&buffer, template, ap);
77
78 print_error(buffer, type);
79 G_free(buffer);
80}
81
82/*!
83 * \brief Print a message to stderr
84 *
85 * The output format depends on environment variable GRASS_MESSAGE_FORMAT
86 *
87 * \param msg string (cannot be NULL)
88 */
89void G_message(const char *msg, ...)
90{
91 if (G_verbose() >= G_verbose_std()) {
92 va_list ap;
93
94 va_start(ap, msg);
95 vfprint_error(MSG, msg, ap);
96 va_end(ap);
97 }
98}
99
100/*!
101 * \brief Print a message to stderr but only if module is in verbose mode
102 *
103 * The output format depends on environment variables
104 * GRASS_MESSAGE_FORMAT and GRASS_VERBOSE
105 *
106 * \param msg string (cannot be NULL)
107 */
108void G_verbose_message(const char *msg, ...)
109{
110 if (G_verbose() > G_verbose_std()) {
111 va_list ap;
112
113 va_start(ap, msg);
114 vfprint_error(MSG, msg, ap);
115 va_end(ap);
116 }
117}
118
119/*!
120 * \brief Print a message to stderr even in brief mode (verbosity=1)
121 *
122 * Usually just G_percent()/G_clicker() would be shown at this level.
123 * This allows important non-error/warning messages to display as well.
124 *
125 * The output format depends on environment variables
126 * GRASS_MESSAGE_FORMAT and GRASS_VERBOSE
127 *
128 * \param msg string (cannot be NULL)
129 */
130void G_important_message(const char *msg, ...)
131{
132 if (G_verbose() > G_verbose_min()) {
133 va_list ap;
134
135 va_start(ap, msg);
136 vfprint_error(MSG, msg, ap);
137 va_end(ap);
138 }
139}
140
141/*!
142 * \brief Print a fatal error message to stderr
143 *
144 * The output format depends on environment variable
145 * GRASS_MESSAGE_FORMAT
146 *
147 * By default, the message is handled by an internal routine which
148 * prints the message to the screen. Using G_set_error_routine() the
149 * programmer can have the message handled by another routine. This is
150 * especially useful if the message should go to a particular location
151 * on the screen when using curses or to a location on a graphics
152 * device (monitor).
153 *
154 * \param msg string (cannot be NULL)
155
156 * \return Terminates with an exit status of EXIT_FAILURE if no external
157 * routine is specified by G_set_error_routine()
158 */
159void G_fatal_error(const char *msg, ...)
160{
161 static int busy;
162 va_list ap;
163
164 if (busy)
165 exit(EXIT_FAILURE);
166 busy = 1;
167
168 if (G_verbose() > -1) {
169 va_start(ap, msg);
170 vfprint_error(ERR, msg, ap);
171 va_end(ap);
172 }
173
174 if (fatal_longjmp) {
175 busy = 0;
176 longjmp(fatal_jmp_buf, 1);
177 }
178
180
181 /* Raise SIGABRT, useful for debugging only.
182 * Type "export GRASS_ABORT_ON_ERROR=1"
183 * to enable this feature using bash.
184 */
185 if (getenv("GRASS_ABORT_ON_ERROR"))
186 abort();
187
188 exit(EXIT_FAILURE);
189}
190
191/*!
192 * \brief Print a warning message to stderr
193 *
194 * The output format depends on environment variable
195 * GRASS_MESSAGE_FORMAT
196 *
197 * A warning message can be suppressed by G_suppress_warnings()
198 *
199 * \param msg string (cannot be NULL)
200 *
201 * \return
202 */
203void G_warning(const char *msg, ...)
204{
205 va_list ap;
206
207 if (no_warn || G_verbose() < 0)
208 return;
209
210 va_start(ap, msg);
211 vfprint_error(WARN, msg, ap);
212 va_end(ap);
213}
214
215/*!
216 * \brief Suppress printing a warning message to stderr
217 *
218 * \param flag a warning message will be suppressed if non-zero value is given
219 *
220 * \return previous flag
221 */
223{
224 int prev;
225
226 prev = no_warn;
227 no_warn = flag;
228 return prev;
229}
230
231/*!
232 * \brief Turn on/off no_sleep flag
233 *
234 * If <em>flag</em> is 0, then no pause will occur after printing an
235 * error or warning message. Otherwise the pause will occur.
236 *
237 * \param flag if non-zero/zero value is given G_sleep() will be
238 * activated/deactivated
239 *
240 * \return previous no_sleep value
241 */
242int G_sleep_on_error(int flag)
243{
244 int prev;
245
246 prev = !no_sleep;
247 no_sleep = !flag;
248 return prev;
249}
250
251/*!
252 * \brief Establishes error_routine as the routine that will handle
253 * the printing of subsequent error messages.
254 *
255 * \param error_routine routine will be called like this: error_routine(msg,
256 * fatal)
257 *
258 * \return
259 */
260void G_set_error_routine(int (*error_routine)(const char *, int))
261{
262 ext_error = error_routine; /* Roger Bivand 17 June 2000 */
263}
264
265/*!
266 * \brief After this call subsequent error messages will be handled in the
267 * default method.
268 *
269 * Error messages are printed directly to the screen: ERROR: message or WARNING:
270 * message
271 *
272 * \return 0
273 */
275{
276 ext_error = 0; /* Roger Bivand 17 June 2000 */
277}
278
279/* Print info to stderr and optionally to log file and optionally send mail */
280static void print_error(const char *msg, const int type)
281{
282 int fatal, format;
283
284 if (type == ERR)
285 fatal = TRUE;
286 else /* WARN */
287 fatal = FALSE;
288
289 if ((type == MSG || type == WARN || type == ERR) &&
290 ext_error) { /* Function defined by application */
291 ext_error(msg, fatal);
292 }
293 else {
295 format = G_info_format();
296
297 if (type == WARN || type == ERR)
298 log_error(msg, fatal);
299
300 if (format == G_INFO_FORMAT_SILENT)
301 return;
302
303 if (format != G_INFO_FORMAT_GUI) {
304 if (format != G_INFO_FORMAT_PLAIN) {
305 char *w;
306 int len, lead;
307
308 fprintf(stderr, "%s", prefix_std[type]);
309 len = lead = strlen(prefix_std[type]);
310 w = (char *)msg;
311
312 while (print_word(stderr, &w, &len, lead))
313 ;
314 }
315 else {
316 fprintf(stderr, "%s%s\n", prefix_std[type], msg);
317 }
318
319 if ((type != MSG) && isatty(fileno(stderr)) &&
320 (G_info_format() == G_INFO_FORMAT_STANDARD)) { /* Bell */
321 fprintf(stderr, "\7");
322 fflush(stderr);
323 if (!no_sleep)
324 G_sleep(5);
325 }
326 else if ((type == WARN || type == ERR) &&
327 getenv("GRASS_ERROR_MAIL")) { /* Mail */
328 mail_msg(msg, fatal);
329 }
330 }
331 else { /* GUI */
332 print_sentence(stderr, type, msg);
333 }
334 }
335}
336
337static void log_error(const char *msg, int fatal)
338{
339 char cwd[GPATH_MAX];
340 time_t clock;
341 const char *gisbase;
342
343 /* get time */
344 clock = time(NULL);
345
346 /* get current working directory */
347 if (getcwd(cwd, sizeof(cwd)) == NULL)
348 sprintf(cwd, "%s", _("unknown"));
349
350 /* write the error log file */
351 if ((gisbase = G_gisbase()))
352 write_error(msg, fatal, clock, cwd);
353}
354
356{
357 static int initialized;
358 char *fstr;
359
360 if (G_is_initialized(&initialized))
361 return;
362
363 G_init_counter(&message_id, 1);
364
365 prefix_std[0] = "";
366 prefix_std[1] = _("WARNING: ");
367 prefix_std[2] = _("ERROR: ");
368
369 logfile = getenv("GIS_ERROR_LOG");
370 if (!logfile) {
371 char buf[GPATH_MAX];
372
373 sprintf(buf, "%s/GIS_ERROR_LOG", G__home());
374 logfile = G_store(buf);
375 }
376
377 fstr = getenv("GRASS_MESSAGE_FORMAT");
378
379 if (fstr && G_strcasecmp(fstr, "gui") == 0)
380 grass_info_format = G_INFO_FORMAT_GUI;
381 else if (fstr && G_strcasecmp(fstr, "silent") == 0)
382 grass_info_format = G_INFO_FORMAT_SILENT;
383 else if (fstr && G_strcasecmp(fstr, "plain") == 0)
384 grass_info_format = G_INFO_FORMAT_PLAIN;
385 else
386 grass_info_format = G_INFO_FORMAT_STANDARD;
387
388 G_initialize_done(&initialized);
389}
390
391/* Write a message to the log file */
392static int write_error(const char *msg, int fatal, time_t clock,
393 const char *cwd)
394{
395 FILE *log;
396
398
399 log = fopen(logfile, "r");
400 if (!log)
401 /* GIS_ERROR_LOG file is not readable or does not exist */
402 return 1;
403
404 log = freopen(logfile, "a", log);
405 if (!log)
406 /* the user doesn't have write permission */
407 return 1;
408
409 fprintf(log, "-------------------------------------\n");
410 fprintf(log, "%-10s %s\n", "program:", G_program_name());
411 fprintf(log, "%-10s %s\n", "user:", G_whoami());
412 fprintf(log, "%-10s %s\n", "cwd:", cwd);
413 fprintf(log, "%-10s %s\n", "date:", ctime(&clock));
414 fprintf(log, "%-10s %s\n", fatal ? "error:" : "warning:", msg);
415 fprintf(log, "-------------------------------------\n");
416
417 fclose(log);
418
419 return 0;
420}
421
422/* Mail a message */
423static void mail_msg(const char *msg, int fatal)
424{
425 struct Popen mail;
426 FILE *fp = G_open_mail(&mail);
427
428 if (fp)
429 fprintf(fp, "GIS %s: %s\n", fatal ? "ERROR" : "WARNING", msg);
430
431 G_close_mail(&mail);
432}
433
434/* Print one word, new line if necessary */
435static int print_word(FILE *fd, char **word, int *len, const int lead)
436{
437 int wlen, start, totlen;
438 int nl;
439 char *w, *b;
440
441 start = *len;
442 w = *word;
443
444 nl = 0;
445 while (*w == ' ' || *w == '\t' || *w == '\n')
446 if (*w++ == '\n')
447 nl++;
448
449 wlen = 0;
450 for (b = w; *b != 0 && *b != ' ' && *b != '\t' && *b != '\n'; b++)
451 wlen++;
452
453 if (wlen == 0) {
454 fprintf(fd, "\n");
455 return 0;
456 }
457
458 if (start > lead) { /* add space */
459 totlen = start + wlen + 1;
460 }
461 else {
462 totlen = start + wlen;
463 }
464
465 if (nl != 0 || totlen > 75) {
466 while (--nl > 0)
467 fprintf(fd, "\n");
468 fprintf(fd, "\n%*s", lead, "");
469 start = lead;
470 }
471
472 if (start > lead) {
473 fprintf(fd, " ");
474 start++;
475 }
476
477 *len = start + wlen;
478
479 fwrite(w, 1, wlen, fd);
480 w += wlen;
481
482 *word = w;
483
484 return 1;
485}
486
487/* Print one message, prefix inserted before each new line */
488static void print_sentence(FILE *fd, const int type, const char *msg)
489{
490 char prefix[100];
491 const char *start;
492 int id = G_counter_next(&message_id);
493
494 switch (type) {
495 case MSG:
496 sprintf(prefix, "GRASS_INFO_MESSAGE(%d,%d): ", getpid(), id);
497 break;
498 case WARN:
499 sprintf(prefix, "GRASS_INFO_WARNING(%d,%d): ", getpid(), id);
500 break;
501 case ERR:
502 sprintf(prefix, "GRASS_INFO_ERROR(%d,%d): ", getpid(), id);
503 break;
504 }
505
506 start = msg;
507
508 fprintf(stderr, "\n");
509 while (*start != '\0') {
510 const char *next = start;
511
512 fprintf(fd, "%s", prefix);
513
514 while (*next != '\0') {
515 next++;
516
517 if (*next == '\n') {
518 next++;
519 break;
520 }
521 }
522
523 fwrite(start, 1, next - start, fd);
524 fprintf(fd, "\n");
525 start = next;
526 }
527 fprintf(stderr, "GRASS_INFO_END(%d,%d)\n", getpid(), id);
528}
529
530/*!
531 * \brief Get current message format
532 *
533 * Maybe set to either "standard" or "gui" (normally GRASS takes care)
534 *
535 * \return grass_info_format value
536 */
538{
540
541 return grass_info_format;
542}
void G_free(void *buf)
Free allocated memory.
Definition alloc.c:150
int G_vasprintf(char **out, const char *fmt, va_list ap)
Safe replacement for asprintf().
Definition asprintf.c:41
#define NULL
Definition ccmath.h:32
void G_initialize_done(int *p)
Definition counter.c:77
void G_init_counter(struct Counter *c, int v)
Definition counter.c:38
int G_is_initialized(int *p)
Definition counter.c:60
int G_counter_next(struct Counter *c)
Definition counter.c:46
#define TRUE
Definition dbfopen.c:75
#define FALSE
Definition dbfopen.c:74
double b
void G_verbose_message(const char *msg,...)
Print a message to stderr but only if module is in verbose mode.
Definition gis/error.c:108
int G_sleep_on_error(int flag)
Turn on/off no_sleep flag.
Definition gis/error.c:242
#define MSG
A message.
Definition gis/error.c:32
void G_set_error_routine(int(*error_routine)(const char *, int))
Establishes error_routine as the routine that will handle the printing of subsequent error messages.
Definition gis/error.c:260
#define WARN
A warning message.
Definition gis/error.c:38
void G_important_message(const char *msg,...)
Print a message to stderr even in brief mode (verbosity=1)
Definition gis/error.c:130
void G_fatal_error(const char *msg,...)
Print a fatal error message to stderr.
Definition gis/error.c:159
#define ERR
A fatal error message.
Definition gis/error.c:44
void G_message(const char *msg,...)
Print a message to stderr.
Definition gis/error.c:89
int G_suppress_warnings(int flag)
Suppress printing a warning message to stderr.
Definition gis/error.c:222
jmp_buf * G_fatal_longjmp(int enable)
Definition gis/error.c:66
void G_unset_error_routine(void)
After this call subsequent error messages will be handled in the default method.
Definition gis/error.c:274
void G_warning(const char *msg,...)
Print a warning message to stderr.
Definition gis/error.c:203
int G_info_format(void)
Get current message format.
Definition gis/error.c:537
void G_init_logging(void)
Definition gis/error.c:355
const char * G_gisbase(void)
Get full path name of the top level module directory.
Definition gisbase.c:39
void G__call_error_handlers(void)
Call available error handlers (internal use only)
Definition handler.c:102
const char * G__home(void)
Get user's home directory (internal use only)
Definition home.c:53
void G_close_mail(struct Popen *mail)
Definition pager.c:65
FILE * G_open_mail(struct Popen *mail)
Definition pager.c:45
const char * G_program_name(void)
Return module name.
Definition progrm_nme.c:28
void G_sleep(unsigned int seconds)
Definition sleep.c:11
int G_strcasecmp(const char *x, const char *y)
String compare ignoring case (upper or lower)
Definition strings.c:47
char * G_store(const char *s)
Copy string to allocated memory.
Definition strings.c:87
int G_verbose(void)
Get current verbosity level.
Definition verbose.c:60
int G_verbose_min(void)
Get min verbosity level.
Definition verbose.c:101
int G_verbose_std(void)
Get standard verbosity level.
Definition verbose.c:91
const char * G_whoami(void)
Gets user's name.
Definition whoami.c:35