GRASS GIS 7 Programmer's Manual  7.2.2(2017)-exported
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[2000]; /* G_asprintf does not work */
76 
77  vsprintf(buffer, template, ap);
78 
79  print_error(buffer, type);
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: message
270  *
271  * \return 0
272  */
274 {
275  ext_error = 0; /* Roger Bivand 17 June 2000 */
276 }
277 
278 /* Print info to stderr and optionally to log file and optionally send mail */
279 static void print_error(const char *msg, const int type)
280 {
281  int fatal, format;
282 
283  if (type == ERR)
284  fatal = TRUE;
285  else /* WARN */
286  fatal = FALSE;
287 
288  if ((type == MSG || type == WARN || type == ERR) && ext_error) { /* Function defined by application */
289  ext_error(msg, fatal);
290  }
291  else {
292  G_init_logging();
293  format = G_info_format();
294 
295  if (type == WARN || type == ERR)
296  log_error(msg, fatal);
297 
298  if (format == G_INFO_FORMAT_SILENT)
299  return;
300 
301  if (format != G_INFO_FORMAT_GUI) {
302  if (format != G_INFO_FORMAT_PLAIN) {
303  char *w;
304  int len, lead;
305 
306  fprintf(stderr, "%s", prefix_std[type]);
307  len = lead = strlen(prefix_std[type]);
308  w = (char *)msg;
309 
310  while (print_word(stderr, &w, &len, lead)) ;
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) && getenv("GRASS_ERROR_MAIL")) { /* Mail */
324  mail_msg(msg, fatal);
325  }
326  }
327  else { /* GUI */
328  print_sentence(stderr, type, msg);
329  }
330  }
331 }
332 
333 static void log_error(const char *msg, int fatal)
334 {
335  char cwd[GPATH_MAX];
336  time_t clock;
337  const char *gisbase;
338 
339  /* get time */
340  clock = time(NULL);
341 
342  /* get current working directory */
343  getcwd(cwd, sizeof(cwd));
344 
345  /* write the error log file */
346  if ((gisbase = G_gisbase()))
347  write_error(msg, fatal, clock, cwd);
348 }
349 
350 void G_init_logging(void)
351 {
352  static int initialized;
353  char *fstr;
354 
355  if (G_is_initialized(&initialized))
356  return;
357 
358  G_init_counter(&message_id, 1);
359 
360  prefix_std[0] = "";
361  prefix_std[1] = _("WARNING: ");
362  prefix_std[2] = _("ERROR: ");
363 
364  logfile = getenv("GIS_ERROR_LOG");
365  if (!logfile) {
366  char buf[GPATH_MAX];
367  sprintf(buf, "%s/GIS_ERROR_LOG", G__home());
368  logfile = G_store(buf);
369  }
370 
371  fstr = getenv("GRASS_MESSAGE_FORMAT");
372 
373  if (fstr && G_strcasecmp(fstr, "gui") == 0)
374  grass_info_format = G_INFO_FORMAT_GUI;
375  else if (fstr && G_strcasecmp(fstr, "silent") == 0)
376  grass_info_format = G_INFO_FORMAT_SILENT;
377  else if (fstr && G_strcasecmp(fstr, "plain") == 0)
378  grass_info_format = G_INFO_FORMAT_PLAIN;
379  else
380  grass_info_format = G_INFO_FORMAT_STANDARD;
381 
382  G_initialize_done(&initialized);
383 }
384 
385 /* Write a message to the log file */
386 static int write_error(const char *msg, int fatal,
387  time_t clock, const char *cwd)
388 {
389  FILE *log;
390 
391  G_init_logging();
392 
393  log = fopen(logfile, "r");
394  if (!log)
395  /* GIS_ERROR_LOG file is not readable or does not exist */
396  return 1;
397 
398  log = freopen(logfile, "a", log);
399  if (!log)
400  /* the user doesn't have write permission */
401  return 1;
402 
403  fprintf(log, "-------------------------------------\n");
404  fprintf(log, "%-10s %s\n", "program:", G_program_name());
405  fprintf(log, "%-10s %s\n", "user:", G_whoami());
406  fprintf(log, "%-10s %s\n", "cwd:", cwd);
407  fprintf(log, "%-10s %s\n", "date:", ctime(&clock));
408  fprintf(log, "%-10s %s\n", fatal ? "error:" : "warning:", msg);
409  fprintf(log, "-------------------------------------\n");
410 
411  fclose(log);
412 
413  return 0;
414 }
415 
416 /* Mail a message */
417 static void mail_msg(const char *msg, int fatal)
418 {
419  struct Popen mail;
420  FILE *fp = G_open_mail(&mail);
421 
422  if (fp)
423  fprintf(fp, "GIS %s: %s\n", fatal ? "ERROR" : "WARNING", msg);
424 
425  G_close_mail(&mail);
426 }
427 
428 /* Print one word, new line if necessary */
429 static int print_word(FILE * fd, char **word, int *len, const int lead)
430 {
431  int wlen, start, totlen;
432  int nl;
433  char *w, *b;
434 
435  start = *len;
436  w = *word;
437 
438  nl = 0;
439  while (*w == ' ' || *w == '\t' || *w == '\n')
440  if (*w++ == '\n')
441  nl++;
442 
443  wlen = 0;
444  for (b = w; *b != 0 && *b != ' ' && *b != '\t' && *b != '\n'; b++)
445  wlen++;
446 
447  if (wlen == 0) {
448  fprintf(fd, "\n");
449  return 0;
450  }
451 
452  if (start > lead) { /* add space */
453  totlen = start + wlen + 1;
454  }
455  else {
456  totlen = start + wlen;
457  }
458 
459  if (nl != 0 || totlen > 75) {
460  while (--nl > 0)
461  fprintf(fd, "\n");
462  fprintf(fd, "\n%*s", lead, "");
463  start = lead;
464  }
465 
466  if (start > lead) {
467  fprintf(fd, " ");
468  start++;
469  }
470 
471  *len = start + wlen;
472 
473  fwrite(w, 1, wlen, fd);
474  w += wlen;
475 
476  *word = w;
477 
478  return 1;
479 }
480 
481 /* Print one message, prefix inserted before each new line */
482 static void print_sentence(FILE * fd, const int type, const char *msg)
483 {
484  char prefix[100];
485  const char *start;
486  int id = G_counter_next(&message_id);
487 
488  switch (type) {
489  case MSG:
490  sprintf(prefix, "GRASS_INFO_MESSAGE(%d,%d): ", getpid(), id);
491  break;
492  case WARN:
493  sprintf(prefix, "GRASS_INFO_WARNING(%d,%d): ", getpid(), id);
494  break;
495  case ERR:
496  sprintf(prefix, "GRASS_INFO_ERROR(%d,%d): ", getpid(), id);
497  break;
498  }
499 
500  start = msg;
501 
502  fprintf(stderr, "\n");
503  while (*start != '\0') {
504  const char *next = start;
505 
506  fprintf(fd, "%s", prefix);
507 
508  while (*next != '\0') {
509  next++;
510 
511  if (*next == '\n') {
512  next++;
513  break;
514  }
515  }
516 
517  fwrite(start, 1, next - start, fd);
518  fprintf(fd, "\n");
519  start = next;
520  }
521  fprintf(stderr, "GRASS_INFO_END(%d,%d)\n", getpid(), id);
522 }
523 
524 /*!
525  * \brief Get current message format
526  *
527  * Maybe set to either "standard" or "gui" (normally GRASS takes care)
528  *
529  * \return grass_info_format value
530  */
531 int G_info_format(void)
532 {
533  G_init_logging();
534 
535  return grass_info_format;
536 }
int G_info_format(void)
Get current message format.
Definition: gis/error.c:531
int G_strcasecmp(const char *x, const char *y)
String compare ignoring case (upper or lower)
Definition: strings.c:46
const char * G__home(void)
Get user&#39;s home directory (internal use only)
Definition: home.c:53
void G_important_message(const char *msg,...)
Print a message to stderr even in brief mode (verbosity=1)
Definition: gis/error.c:130
int G_sleep_on_error(int flag)
Turn on/off no_sleep flag.
Definition: gis/error.c:242
void G_init_counter(struct Counter *c, int v)
Definition: counter.c:38
#define WARN
A warning message.
Definition: gis/error.c:38
#define FALSE
Definition: dbfopen.c:117
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
FILE * G_open_mail(struct Popen *mail)
Definition: pager.c:45
char * G_store(const char *s)
Copy string to allocated memory.
Definition: strings.c:86
int G_is_initialized(int *p)
Definition: counter.c:59
#define NULL
Definition: ccmath.h:32
void G_initialize_done(int *p)
Definition: counter.c:76
void G_fatal_error(const char *msg,...)
Print a fatal error message to stderr.
Definition: gis/error.c:159
int G_suppress_warnings(int flag)
Suppress printing a warning message to stderr.
Definition: gis/error.c:222
double b
#define TRUE
Definition: dbfopen.c:118
void G_close_mail(struct Popen *mail)
Definition: pager.c:65
void G_sleep(unsigned int seconds)
Definition: sleep.c:11
#define ERR
A fatal error message.
Definition: gis/error.c:44
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
const char * G_program_name(void)
Return module name.
Definition: progrm_nme.c:28
int G_verbose_std(void)
Get standard verbosity level.
Definition: verbose.c:86
int G_verbose(void)
Get current verbosity level.
Definition: verbose.c:55
jmp_buf * G_fatal_longjmp(int enable)
Definition: gis/error.c:67
void G_init_logging(void)
Definition: gis/error.c:350
const char * G_whoami(void)
Gets user&#39;s name.
Definition: whoami.c:35
void G_message(const char *msg,...)
Print a message to stderr.
Definition: gis/error.c:89
void G__call_error_handlers(void)
Call available error handlers (internal use only)
Definition: handler.c:101
void G_unset_error_routine(void)
After this call subsequent error messages will be handled in the default method.
Definition: gis/error.c:273
int G_verbose_min(void)
Get min verbosity level.
Definition: verbose.c:96
int G_counter_next(struct Counter *c)
Definition: counter.c:46
const char * G_gisbase(void)
Get full path name of the top level module directory.
Definition: gisbase.c:41
void G_warning(const char *msg,...)
Print a warning message to stderr.
Definition: gis/error.c:203
#define MSG
A message.
Definition: gis/error.c:32