https://github.com/cran/gstat
Revision 1b2742a3d71abc7fbde73ca141b23e1375f185bf authored by Edzer Pebesma on 30 October 2011, 00:00:00 UTC, committed by Gabor Csardi on 30 October 2011, 00:00:00 UTC
1 parent f056b6c
Raw File
Tip revision: 1b2742a3d71abc7fbde73ca141b23e1375f185bf authored by Edzer Pebesma on 30 October 2011, 00:00:00 UTC
version 1.0-9
Tip revision: 1b2742a
userio.c
/*
    Gstat, a program for geostatistical modelling, prediction and simulation
    Copyright 1992, 2011 (C) Edzer Pebesma

    Edzer Pebesma, edzer.pebesma@uni-muenster.de
	Institute for Geoinformatics (ifgi), University of Münster 
	Weseler Straße 253, 48151 Münster, Germany. Phone: +49 251 
	8333081, Fax: +49 251 8339763  http://ifgi.uni-muenster.de 

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version. As a special exception, linking 
    this program with the Qt library is permitted.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

    (read also the files COPYING and Copyright)
*/

/*
 * userio.c: i/o routines for error, warning, log and progress messages
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <math.h>

#include "defs.h"

#ifdef PCRCALC
# define efclose fclose
#else
# include "err.h"
#endif

#include "debug.h"
#include "utils.h"
#include "version.h"
#include "userio.h"

#ifdef USING_R
void Rprintf(const char *, ...);
void Rf_error(const char *, ...);
# define is_openf(f) (f != NULL)
#else
# define is_openf(f) (f != NULL && f != stdout && f != stderr)
#endif


static FILE *logfile = NULL;

static struct {
	void (*warning_handler)(const char *mess);
	void (*error_handler)(const char *mess, int level);
	void (*printlog_handler)(const char *mess);
	void (*progress_handler)(unsigned int this, unsigned int total);
} gstat_handler = { NULL, NULL, NULL, NULL };

static void (*old_progress_handler)(unsigned int this, unsigned int total) 
		= NULL;

static STRING_BUFFER 
	*error_prefix = NULL, 
	*error_message = NULL, 
	*warning_message = NULL;

static enum Gstat_errno gstat_errno;

const char *error_messages[MAX_ERRNO+1] = {
/* 0 */		"%s",
/* 1 */		"bug in function `%s'",
/* 2 */		"variable not set: %s",
/* 3 */		"variable outside valid range: %s",
/* 4 */		"value not allowed for: %s",
/* 5 */		"no filename set %s",
/* 6 */		"write failed on file `%s'",
/* 7 */		"read failed on file `%s'",
/* 9 */		"cannot read real value from `%s'",
/* 9 */		"cannot read integer from `%s'",
/* 10 */	"syntax error: %s",
/* 11 */	"illegal option or missing argument on `%s'",
/* 12 */	"domain (math) error on `%s'",
/* 13 */	"out of dynamic memory %s",
/* 14 */	"i/o error: %s",
/* 15 */	"no command file%s",
/* 16 */	"%s user interface not compiled in this version",
/* 17 */	"writing to pipe `%s' failed",
/* 18 */	"reading from pipe `%s' failed",
/* 19 */    "function call prevented by secure mode%s",
/* 20 */	"matrix library error: %s",
/* 21 */	"extdbase error: %s"
};

void init_userio(int use_stdio) {
	if (use_stdio) {
#ifdef USING_R
		set_gstat_log_file(NULL);
#else
		set_gstat_log_file(stdout);
#endif
		set_gstat_warning_handler(default_warning);
		set_gstat_error_handler(default_error);
		set_gstat_log_handler(default_printlog);
		set_gstat_progress_handler(default_progress);
	} else {
		/* ... */
	}
	error_prefix    = resize_strbuf(error_prefix, ERROR_BUFFER_SIZE);
	error_message   = resize_strbuf(error_message, ERROR_BUFFER_SIZE);
	warning_message = resize_strbuf(warning_message, ERROR_BUFFER_SIZE);
	error_prefix->str[0] = error_message->str[0] = 
			warning_message->str[0] = '\0';
}

/*
 * error handling function -- print message and error to string, and
 * call error message handler.
 */
void gstat_error(char *fname, int line,
	enum Gstat_errno err_nr, const char *msg) {
	char s[30], *buf;
	int len;

	assert(err_nr <= MAX_ERRNO);
	gstat_errno = err_nr;

	if (error_prefix->str[0] != '\0')
		save_strcat(error_message, error_prefix->str);

	save_strcat(error_message, "gstat: ");
	len = strlen(error_message->str);
	buf = error_message->str + len;
#ifdef HAVE_SNPRINTF
	snprintf(buf, ERROR_BUFFER_SIZE - len,
		error_messages[err_nr], save_string(msg));
#else
	sprintf(buf, error_messages[err_nr], save_string(msg));
#endif
	if (DEBUG_DUMP || err_nr == ER_NULL) { /* print file&line */
		save_strcat(error_message, " (");
		save_strcat(error_message, fname);
		sprintf(s, ", line %d)", line);
		save_strcat(error_message, s);
	}

	if (err_nr == ER_NULL) {
		save_strcat(error_message, "\nVersion info: ");
		save_strcat(error_message, GSTAT_OS);
		save_strcat(error_message, " ");
		save_strcat(error_message, VERSION);
		save_strcat(error_message,
			"\nThis is a bug. Please send the above information, along with\n");
		save_strcat(error_message,
			"the information necessary to reproduce this bug to ");
		save_strcat(error_message, GSTAT_EMAIL);
	}

	gstat_handler.error_handler(error_message->str, err_nr);
	error_message->str[0] = '\0';
	return;
}

/* wrapper function for ErrClo(optopt), in case of error command line option */
void gstat_clo_error(char *f, int l, enum Gstat_errno err, int a) {
	static char s[2];
	sprintf(s, "%c", a);
	gstat_error(f, l, err, s);
} 

/* message() calls for messages preceding a call to ErrMsg() */
void message(char *fmt, ...) {
	va_list args;
	/* char *buf = NULL; */

	va_start(args, fmt);
#ifdef HAVE_VSNPRINTF
	vsnprintf(error_prefix->str, ERROR_BUFFER_SIZE, fmt, args);
#else
	vsprintf(error_prefix->str, fmt, args);
#endif
	va_end(args);
	/* buf = NULL; */
}

/* print a warning message to string, and call warning message handler */
void pr_warning(char *fmt, ...) {
	va_list args;
	char *buf = NULL;

	if (warning_message->max_length < 11)
		resize_strbuf(warning_message, 11);

	warning_message->str[0] = '\0';
	save_strcat(warning_message, "Warning: ");

	buf = warning_message->str + 9;

	va_start(args, fmt);
#ifdef HAVE_VSNPRINTF
	vsnprintf(buf, ERROR_BUFFER_SIZE - 9, fmt, args);
#else
	vsprintf(buf, fmt, args);
#endif
	va_end(args);

	gstat_handler.warning_handler(warning_message->str);
}

void print_progress(unsigned int current, unsigned int total) {
	gstat_handler.progress_handler(current, total);
}

/* get the value of gstat errno */
enum Gstat_errno get_gstat_errno(void) {
	return gstat_errno;
}

/* set the internal gstat errno to NO_ERROR, and reset error mesages */
void reset_gstat_errno(void) {
	assert(error_prefix);
	assert(error_message);

	gstat_errno = ER_NOERROR;
	error_prefix->str[0] = '\0';
	error_message->str[0] = '\0';
}

void set_gstat_warning_handler(void (*warning_fn)(const char *message)) {
	gstat_handler.warning_handler = warning_fn;
}

void set_gstat_error_handler(void (*error_fn)(const char *message, int level)) {
	gstat_handler.error_handler = error_fn;
}

void set_gstat_log_handler(void (*logprint)(const char *str)) {
	gstat_handler.printlog_handler = logprint;
}

void set_gstat_progress_handler(
		void (*progress)(unsigned int this, unsigned int total)) {
	gstat_handler.progress_handler = progress;
}

void push_gstat_progress_handler(
		void (*progress)(unsigned int this, unsigned int total)) {

	assert(old_progress_handler == NULL);

	old_progress_handler = gstat_handler.progress_handler;
	set_gstat_progress_handler(progress);
}

void pop_gstat_progress_handler(void) {

	assert(old_progress_handler != NULL);

	set_gstat_progress_handler(old_progress_handler);
	old_progress_handler = NULL;
}

const char *get_gstat_error_message(void) {
	return (const char *) error_message->str;
}

void print_to_logfile_if_open(const char *mess) {

	if (is_openf(logfile))
#ifdef USING_R
		Rprintf("%s", mess);
#else
		fprintf(logfile, "%s", mess);
#endif 
}

void default_warning(const char *mess) {

	print_to_logfile_if_open(mess);

#ifdef USING_R
	Rprintf("%s\n", mess);
#else
	fprintf(stderr, "%s\n", mess);
#endif
	return;
}

void default_error(const char *mess, int level) {

	print_to_logfile_if_open(mess);

#ifdef USING_R
	Rf_error("%s\n", mess);
#else
	fprintf(stderr, "%s\n", mess);
	exit(level == 0 ? -1 : level);
#endif
}

void printlog(const char *fmt, ...) {
	STRING_BUFFER *s;
	va_list args;

	s = resize_strbuf(NULL, ERROR_BUFFER_SIZE);

	va_start(args, fmt);
#ifdef HAVE_VSNPRINTF
	vsnprintf(s->str, ERROR_BUFFER_SIZE, fmt, args);
#else
	vsprintf(s->str, fmt, args);
#endif
	va_end(args);

	gstat_handler.printlog_handler(s->str);
	free_strbuf(s);
}

void default_printlog(const char *mess) {

	if (DEBUG_SILENT)
		return;

	if (is_openf(logfile))
		print_to_logfile_if_open(mess);
	else
#ifndef USING_R
		Rprintf("%s", mess);
#else
		printf("%s", mess);
#endif
}

int set_gstat_log_file(FILE *f) {
	int retval;

	if (f == NULL) {
		if (is_openf(logfile)) {
			retval = efclose(logfile);
			logfile = NULL;
			return retval;
		} else {
			logfile = NULL;
			return 1;
		}
	} else
		logfile = f;
	return 0;
}

void default_progress(unsigned int current, unsigned int total) {
	static int perc_last = -1, sec_last = -1;
	int perc, sec;
	static time_t start;

	if (total <= 0 || DEBUG_SILENT)
		return;

	if (sec_last == -1) {
		start = time(NULL);
		sec_last = 0;
	}
	perc = floor(100.0 * current / total);
	if (perc != perc_last) { /* another percentage -> calculate time: */
		if (current == total) { /* 100% done, reset: */
#ifdef USING_R
			Rprintf("\r%3d%% done\n", 100);
#else
			fprintf(stderr, "\r%3d%% done\n", 100);
#endif
			perc_last = sec_last = -1;
		} else {
			sec = difftime(time(NULL), start);
			if (sec != sec_last) { /* another second -- don't print too often */
#ifdef USING_R
				Rprintf("\r%3d%% done", perc);
#else
				fprintf(stderr, "\r%3d%% done", perc);
#endif
				perc_last = perc;
				sec_last = sec;
			}
		}
	}
}

#ifndef PCRCALC
/**************************** meschach error functions ****************/

#define SING_ERR \
"Read the manual at http://www.gstat.org/ ;\n\
look for: Trouble shooting -> Error messages -> From meschach"

#define MEM_ERR \
"In case you are trying to do global kriging (i.e., no neighbourhood\n\
parameters like `radius' or `max' were specified) with a large data set,\n\
reduce the neighbourhood size and use local kriging. In case you are\n\
fitting a variogram model to a large data set with REML, try another\n\
fitting method."

#define FP_ERR \
"This error may arise from using _very_ large or very small values for\n\
data values, variograms or coordinates. Try to rescale them to a\n\
reasonable range."

void setup_meschach_error_handler(int using_R) {
 	int code;
 	char *err, *hint = "", buf[100];

#ifndef PCRCALC
 	/* set up meschach error handler: */
 	if ((code = setjmp(restart)) == 0) {
		set_err_flag(using_R ? EF_R_ERROR /* avoid longjmp */
			: EF_JUMP /* make meschach jump on errors */  );
 	} else {
 		/* setjmp() returned non-zero, so we returned from a longjmp(): */
 		switch (code) {
 			case E_MEM:  /* run out of memory */
 				err = "virtual memory exhausted";
 				hint = MEM_ERR;
 				break;
 			case E_SING:  /* singular matrix occurred */
 				err = "singular matrix";
 				hint = SING_ERR;
 				break;
 			case E_POSDEF: /* non-positive definite matrix */
 				err = "non-positive definite matrix";
 				hint = "";
 				break;
 			case E_SIGNAL: /* floating point signal */
 				err = "floating point exception";
 				hint = FP_ERR;
 				break;
 			default:
 				sprintf(buf, "unknown, error code %d", code);
 				err = buf;
 				hint = "";
 				break;
 		}
		printlog("\ngstat caught an error that occurred in the matrix library,\n");
 		printlog("the reason for it was: %s\n\n", err);
		if (*hint)
			printlog("HINT: %s\n\n", hint);
 		ErrMsg(ER_MESCHACH, err);
 	} 
#endif /* PCRCALC */
}
#endif

void no_progress(unsigned int current, unsigned int total) {
	return;
}
back to top