GRASS GIS 8 Programmer's Manual  8.4.0dev(2024)-7413740dd8
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; */
47 static int (*ext_error)(const char *, int); /* Roger Bivand 17 June 2000 */
48 static int no_warn = FALSE;
49 static int no_sleep = TRUE;
50 
51 static int grass_info_format;
52 static char *logfile;
53 static char *prefix_std[3];
54 static struct Counter message_id;
55 
56 static int print_word(FILE *, char **, int *, const int);
57 static void print_sentence(FILE *, const int, const char *);
58 static void print_error(const char *, const int);
59 static void mail_msg(const char *, int);
60 static int write_error(const char *, int, time_t, const char *);
61 static void log_error(const char *, int);
62 
63 static int fatal_longjmp;
64 static jmp_buf fatal_jmp_buf;
65 
66 jmp_buf *G_fatal_longjmp(int enable)
67 {
68  fatal_longjmp = enable;
69  return &fatal_jmp_buf;
70 }
71 
72 static 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  */
89 void 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  */
108 void 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  */
130 void 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  */
159 void 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  */
203 void 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  */
222 int G_suppress_warnings(int flag)
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  */
242 int 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  */
260 void 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 */
280 static 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 {
294  G_init_logging();
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 
337 static 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 
355 void G_init_logging(void)
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  snprintf(buf, GPATH_MAX, "%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 */
392 static int write_error(const char *msg, int fatal, time_t clock,
393  const char *cwd)
394 {
395  FILE *log;
396 
397  G_init_logging();
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 */
423 static 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 */
435 static 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 */
488 static 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  */
537 int G_info_format(void)
538 {
539  G_init_logging();
540 
541  return grass_info_format;
542 }
#define NULL
Definition: ccmath.h:32
void G_free(void *)
Free allocated memory.
Definition: gis/alloc.c:150
FILE * G_open_mail(struct Popen *)
Definition: pager.c:45
const char * G_whoami(void)
Gets user's name.
Definition: gis/whoami.c:35
void G_close_mail(struct Popen *)
Definition: pager.c:65
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
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:47
int G_counter_next(struct Counter *)
Definition: counter.c:46
const char * G_gisbase(void)
Get full path name of the top level module directory.
Definition: gisbase.c:39
const char * G_program_name(void)
Return module name.
Definition: progrm_nme.c:28
void G_init_counter(struct Counter *, int)
Definition: counter.c:38
int G_verbose_std(void)
Get standard verbosity level.
Definition: verbose.c:91
int G_vasprintf(char **, const char *, va_list)
Safe replacement for asprintf().
Definition: asprintf.c:41
char * G_store(const char *)
Copy string to allocated memory.
Definition: strings.c:87
jmp_buf * G_fatal_longjmp(int enable)
Definition: gis/error.c:66
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
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
void G__call_error_handlers(void)
Call available error handlers (internal use only)
Definition: gis/handler.c:102
#define G_INFO_FORMAT_GUI
Definition: gis.h:386
#define G_INFO_FORMAT_PLAIN
Definition: gis.h:388
#define GPATH_MAX
Definition: gis.h:194
#define TRUE
Definition: gis.h:79
#define FALSE
Definition: gis.h:83
#define G_INFO_FORMAT_STANDARD
Definition: gis.h:384
#define G_INFO_FORMAT_SILENT
Definition: gis.h:387
#define _(str)
Definition: glocale.h:10
const char * G__home(void)
Get user's home directory (internal use only)
Definition: home.c:53
double b
Definition: r_raster.c:39
Definition: gis.h:616
Definition: gis.h:620
FILE * fp
Definition: gis.h:621