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