/**
 * Error reporting utilities. Under Windows applications compiled in "window"
 * mode (not in "console") it is recommended to always initialize this module
 * by calling the error_init() function before trying to write to standard
 * output or standard error or invoking any other function of this module
 * otherwise messages might get lost in some situations. This does not affect
 * "console" applications nor "window" application started from a console.
 * For more about this issue, see:
 * 
 * INFO: Calling CRT Output Routines from a GUI Application,
 * Article ID: 105305 - Last Review: Nov 21, 2006 - Revision: 1.
 * Quote: "To use C Run-time output routines, such as printf(), from a GUI
 * application, it is necessary to create a console. [...]".
 * https://support.microsoft.com/en-us/help/105305/info-calling-crt-output-routines-from-a-gui-application
 * 
 * @file
 * @author Umberto Salsi <salsi@icosaedro.it>
 * @version $Date: 2017/09/09 23:54:33 $
 */

#ifndef ERROR_H
#define	ERROR_H

#include <stdlib.h>

#ifdef error_IMPORT
	#define EXTERN
#else
	#define EXTERN extern
#endif

/**
 * Name of the program, to be reported along with the error messages.
 * Client program may retrieve from here its name to build other customized
 * messages.
 */
EXTERN char * error_prog_name
#ifdef error_IMPORT
	= "UNDEFINED"
#endif
;

/**
 * Flag to enable debugging in the program.
 */
EXTERN int debug
#ifdef error_IMPORT
	= 0
#endif
;

/**
 * Initializes this module. Under Linux, this merely sets the error_prog_name
 * global variable by copying the pointer, normally retrieved from the
 * argv[0] parameters of the main() function. Under Windows this also checks
 * if a terminal is available where to send standard output and standard error;
 * if not, creates a terminal needed to preserve messages sent to these files.
 * @param prog_name Name of this program to display along with any error
 * message hereafter.
 */
EXTERN void error_init(char *prog_name);

/**
 * Reports an internal unexpected error to stderr then aborts the program.
 * This kind of errors indicate a bug in the program.
 * @param fmt Format descriptor, just like in "printf".
 * @param ... Optional parameters for the format descriptor, each argument
 * matching a format descriptor of 'fmt'. If no optional arguments are required,
 * at least one must be given anyway, for example a simple zero:
 * <pre>error_internal("unimplemented", 0);</pre>
 */
#define error_internal(fmt, ...) \
	{ error_internal_PRIVATE(__FILE__, __LINE__, fmt, __VA_ARGS__);\
	abort(); /* makes happy gcc -Wall */ }

/**
 * Reports an external error to stderr then exits the program with code 1.
 * This kind of errors may indicate an invalid configuration, system limitation,
 * unexpected system behavior, bad response from a remote server, invalid user
 * data.
 * @param fmt Format descriptor, just like in "printf".
 * @param ... Optional parameters for the format descriptor.
 */
EXTERN void error_external(char *fmt, ...);

/**
 * Reports a fatal error generated by a system function that also sets errno to
 * stderr, then exits the program with code 1. Uses the current value of errno
 * to add the corresponding description to the message. This kind of errors
 * indicate a system limitation, failed access to the file system, invalid
 * configuration of the program, failed access to a remote server.
 * @param fmt Format descriptor, just like in "printf".
 * @param ... Optional parameters for the format descriptor.
 */
EXTERN void error_system(char *fmt, ...);

/**
 * USE THE MACRO.
 */
EXTERN void error_internal_PRIVATE(char *file, int line, char *fmt, ...);

#undef EXTERN
#endif	/* ERROR_H */

