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