diff --git a/dummy/dummy.cpp b/dummy/dummy.cpp index 3875f6dcba6..c2a247849ee 100644 --- a/dummy/dummy.cpp +++ b/dummy/dummy.cpp @@ -1,8 +1,6 @@ -#include - -#include "log.h" #include "timer.h" + int main(int argc, const char **argv) { - logDebug << currentDateTime(); + std::cout << currentDateTime(); } diff --git a/freeview/main.cpp b/freeview/main.cpp index 4a0bdaa33ed..af7458da5fc 100644 --- a/freeview/main.cpp +++ b/freeview/main.cpp @@ -36,7 +36,6 @@ #include "LineProf.h" #include #include -#include "error.h" #include #include #include @@ -46,6 +45,7 @@ #endif #include "fsinit.h" +#include "log.h" #include "chklc.h" @@ -108,16 +108,10 @@ void myMessageOutput(QtMsgType type, const char *msg) } #endif -void my_error_exit(int ecode) -{ - if (ecode != 0) - throw (ecode); -} - int main(int argc, char *argv[]) { Progname = argv[0]; - ErrorSetExitFunc(my_error_exit); + throwExceptions(true); putenv((char*)"SURFER_FRONTDOOR="); if (getenv("FS_DISABLE_LANG") == NULL) diff --git a/include/error.h b/include/error.h index 33b8805d66b..a3a5bb59b61 100644 --- a/include/error.h +++ b/include/error.h @@ -1,63 +1,6 @@ -/** - * @file error.h - * @brief error handling prototypes - * - */ -/* - * Original Author: Bruce Fischl - * CVS Revision Info: - * $Author: greve $ - * $Date: 2015/07/27 20:49:35 $ - * $Revision: 1.21 $ - * - * Copyright © 2011 The General Hospital Corporation (Boston, MA) "MGH" - * - * Terms and conditions for use, reproduction, distribution and contribution - * are found in the 'FreeSurfer Software License Agreement' contained - * in the file 'LICENSE' found in the FreeSurfer distribution, and here: - * - * https://surfer.nmr.mgh.harvard.edu/fswiki/FreeSurferSoftwareLicense - * - * Reporting: freesurfer@nmr.mgh.harvard.edu - * - */ +#pragma once - -#ifndef ERROR_H -#define ERROR_H - -#include -#include - -int ErrorInit(char *fname, - int (*vfprint)(FILE *fp, const char *fmt, va_list args), - int (*vprint)(const char *fmt, va_list args)) ; -int ErrorSetExitFunc(void (*exit_func)(int ecode)) ; -void ErrorExit(int ecode, const char *fmt, ...) ; -int ErrorPrintf(int ecode, const char *fmt, ...) ; -void SetErrorExitDoneFile(char *DoneFile); -int ErrorWriteDoneFile(char *DoneFile, int errorcode); -#define ErrorReturn(ret, args) { ErrorPrintf args ; return(ret) ; } - -#define ESCAPE ErrorExit -#define ErrorSet ErrorPrintf -/* error codes */ - -#define NO_ERROR 0 -#define ERROR_NONE NO_ERROR -#define ERROR_NO_FILE -1 -#define ERROR_NOFILE ERROR_NO_FILE -#define ERROR_NO_MEMORY -2 -#define ERROR_NOMEMORY ERROR_NO_MEMORY -#define ERROR_UNSUPPORTED -3 -#define ERROR_BADPARM -4 -#define ERROR_BAD_PARM ERROR_BADPARM -#define ERROR_BADFILE -5 -#define ERROR_BAD_FILE ERROR_BADFILE -#define ERROR_SIZE -6 -#define ERROR_BADLOOP -7 -#define ERROR_OUT_OF_BOUNDS -8 - -extern int Gerror ; /* global error value */ - -#endif +// Error functions have been moved to log.h - +// this file is a temporary bridge to avoid modifying every file that +// includes it, but eventally it should be removed. +#include "log.h" diff --git a/include/log.h b/include/log.h index 2d46cb86a6a..7e9bc46f9d5 100644 --- a/include/log.h +++ b/include/log.h @@ -1,48 +1,12 @@ -#ifndef LOG_H -#define LOG_H +#pragma once +#include #include +#include "fsinit.h" -/// \class StreamLogger -/// -/// A stream-style logging object for printing status messages. -/// -/// In general, this class shouldn't used directly. Status messages should -/// be printed via the standard logging macros. For example: -/// -/// logWarning << "exceeded standard iteration count"; -/// logError << "vertex has no neighbors"; -/// logFatal(1) << "could not find file"; - -class StreamLogger { -public: - enum MessageStatus {Warning, Error, Debug}; - - StreamLogger(MessageStatus status) : status(status), exitout(false) {}; - StreamLogger(MessageStatus status, int exitcode) : status(status), retcode(exitcode), exitout(true) {}; - ~StreamLogger(); - - template - StreamLogger& operator << (const T& t) { - ss << t; - return *this; - } - - // for std::endl - StreamLogger& operator << (std::ostream&(*f)(std::ostream&)) { - f(ss); - return *this; - } - -private: - MessageStatus status; - int retcode; - bool exitout; - std::ostringstream ss; -}; - -// terminal output colors + +// terminal colors namespace term { const char* black(); const char* red(); @@ -51,21 +15,92 @@ namespace term { const char* blue(); const char* purple(); const char* cyan(); - const char* light_gray(); const char* white(); - const char* light_red(); - const char* dim(); - // formating const char* bold(); + const char* dim(); const char* underline(); - // reset const char* reset(); } -// macros for easy logging of standard message types -#define logDebug StreamLogger(StreamLogger::Debug) -#define logWarning StreamLogger(StreamLogger::Warning) -#define logError StreamLogger(StreamLogger::Error) -#define logFatal(ret) StreamLogger(StreamLogger::Error, ret) -#endif +// global settings +void throwExceptions(bool setting); +void setErrorLog(const std::string& filename); + + +namespace detail { + + void writeToErrorLog(const std::string& message); + void errorExit(int code); + + struct logger + { + template logger& operator << (const T& t) { ss << t; return *this; } + logger& operator << (std::ostream&(*f)(std::ostream&)) { f(ss); return *this; } + std::ostringstream ss; + }; + +} + + +namespace fs { + + struct fatal : public detail::logger + { + int ret; + fatal(int err) : ret(err) {} + ~fatal() { + std::cerr << term::red() << "error: " << term::reset() << this->ss.str() << "\n"; + detail::writeToErrorLog(this->ss.str()); + detail::errorExit(this->ret); + } + }; + + struct error : public detail::logger + { + ~error() { + std::cerr << term::red() << "error: " << term::reset() << this->ss.str() << "\n"; + detail::writeToErrorLog(this->ss.str()); + } + }; + + struct warning : public detail::logger + { + ~warning() { std::cerr << term::yellow() << "warning: " << term::reset() << this->ss.str() << "\n"; } + }; + + struct debug : public detail::logger + { + ~debug() { std::cerr << term::cyan() << "debug: " << term::reset() << this->ss.str() << "\n"; } + }; + +} + +// temporary definitions +#define logWarning fs::warning() +#define logFatal(ret) fs::fatal(ret) + +// old c-style error functions +void ErrorExit(int ecode, const char *fmt, ...); +void ErrorPrintf(int ecode, const char *fmt, ...); +#define ErrorReturn(ret, args) { ErrorPrintf args; return(ret); } +#define ErrorInit(a, b, c) FSinit(); + +// global error +extern int Gerror; + +// error codes +#define NO_ERROR 0 +#define ERROR_NONE NO_ERROR +#define ERROR_NO_FILE -1 +#define ERROR_NOFILE ERROR_NO_FILE +#define ERROR_NO_MEMORY -2 +#define ERROR_NOMEMORY ERROR_NO_MEMORY +#define ERROR_UNSUPPORTED -3 +#define ERROR_BADPARM -4 +#define ERROR_BAD_PARM ERROR_BADPARM +#define ERROR_BADFILE -5 +#define ERROR_BAD_FILE ERROR_BADFILE +#define ERROR_SIZE -6 +#define ERROR_BADLOOP -7 +#define ERROR_OUT_OF_BOUNDS -8 diff --git a/mri_ca_train/mri_ca_train.cpp b/mri_ca_train/mri_ca_train.cpp index 5946de3f76c..95e22abcf71 100644 --- a/mri_ca_train/mri_ca_train.cpp +++ b/mri_ca_train/mri_ca_train.cpp @@ -112,9 +112,19 @@ int DoSym = 0; int n_omp_threads; #endif char *DoneFile = NULL; -int WriteDoneFile(char *DoneFile, int code); char *cmdline, cwd[2000]; + +static void writeDoneFile(char *DoneFile, int errorcode) +{ + if (DoneFile == NULL) return; + FILE *fp = fopen(DoneFile, "w"); + if (fp == NULL) return; + fprintf(fp, "%d\n", errorcode); + fclose(fp); +} + + int main(int argc, char *argv[]) { @@ -162,7 +172,11 @@ main(int argc, char *argv[]) argc -= nargs ; argv += nargs ; } - SetErrorExitDoneFile(DoneFile); + + // catch exceptions in order to write a done file + throwExceptions(true); + try { + printf("\n"); printf("setenv SUBJECTS_DIR %s\n",getenv("SUBJECTS_DIR")); printf("cd %s\n",cwd); @@ -1037,17 +1051,20 @@ main(int argc, char *argv[]) GCAfree(&gca) ; msec = start.milliseconds() ; - seconds = nint((float)msec/1000.0f) ; + seconds = nint((float)msec / 1000.0f) ; minutes = seconds / 60 ; seconds = seconds % 60 ; - printf("classifier array training took %d minutes" - " and %d seconds.\n", minutes, seconds) ; + printf("classifier array training took %d minutes and %d seconds.\n", minutes, seconds) ; - ErrorWriteDoneFile(DoneFile, 0); - printf("mri_ca_train done\n"); + } catch(...) { + writeDoneFile(DoneFile, 1); + printf("mri_ca_train failed\n"); + exit(1); + } - exit(0) ; - return(0) ; + writeDoneFile(DoneFile, 0); + printf("mri_ca_train done\n"); + return 0; } /*---------------------------------------------------------------------- Parameters: diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 626615471d3..43ad13ef7c9 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -48,7 +48,6 @@ add_library(utils STATIC dmatrix.cpp dti.cpp dtk.fs.cpp - error.cpp evschutils.cpp fcd.cpp fftutils.cpp diff --git a/utils/error.cpp b/utils/error.cpp deleted file mode 100644 index c5b78581a8d..00000000000 --- a/utils/error.cpp +++ /dev/null @@ -1,211 +0,0 @@ -/** - * @file error.c - * @brief error handling routines - * - */ -/* - * Original Author: Bruce Fischl - * CVS Revision Info: - * $Author: zkaufman $ - * $Date: 2015/07/28 21:24:19 $ - * $Revision: 1.25 $ - * - * Copyright © 2011 The General Hospital Corporation (Boston, MA) "MGH" - * - * Terms and conditions for use, reproduction, distribution and contribution - * are found in the 'FreeSurfer Software License Agreement' contained - * in the file 'LICENSE' found in the FreeSurfer distribution, and here: - * - * https://surfer.nmr.mgh.harvard.edu/fswiki/FreeSurferSoftwareLicense - * - * Reporting: freesurfer@nmr.mgh.harvard.edu - * - */ - -/*----------------------------------------------------- - INCLUDE FILES --------------------------------------------------------*/ - -#include -#include -#include -#include -#include -#include - -#include "error.h" -#include "fsinit.h" -#include "hips.h" -#include "proto.h" -#include "rgb.h" - -/*----------------------------------------------------- - MACROS AND CONSTANTS --------------------------------------------------------*/ - -#define ERROR_FNAME "error.log" - -/*----------------------------------------------------- - STATIC DATA --------------------------------------------------------*/ - -static char error_fname[100] = ERROR_FNAME; -static int (*error_vprintf)(const char *fmt, va_list args) = vprintf; -static int (*error_vfprintf)(FILE *fp, const char *fmt, va_list args) = vfprintf; -static void (*error_exit)(int ecode) = NULL; -static void rgb_error(char *error_str); -char *ErrorExitDoneFile = NULL; - -/*----------------------------------------------------- - GLOBAL DATA --------------------------------------------------------*/ - -int Gerror = NO_ERROR; - -/*----------------------------------------------------- - GLOBAL FUNCTIONS --------------------------------------------------------*/ - -void SetErrorExitDoneFile(char *DoneFile) -{ - extern char *ErrorExitDoneFile; - ErrorExitDoneFile = DoneFile; -} - -/*----------------------------------------------------- - Parameters: - - Returns value: - - Description -------------------------------------------------------*/ -static void rgb_error(char *error_str) -{ - ErrorPrintf(ERROR_BADPARM, error_str); - return; -} - -/*----------------------------------------------------- - Parameters: - - Returns value: - - Description -------------------------------------------------------*/ -int ErrorInit(char *fname, - int (*vfprint)(FILE *fp, const char *fmt, va_list args), - int (*vprint)(const char *fmt, va_list args)) -{ - FSinit(); - error_exit = exit; - i_seterror(rgb_error); - if (fname) strcpy(error_fname, fname); - if (vfprint) error_vfprintf = vfprint; - if (vprint) error_vprintf = vprint; - - unlink(error_fname); /* start with a fresh log file */ - errno = 0; - - /* probably should be some info into log file like date/user etc... */ - return (NO_ERROR); -} - -/*----------------------------------------------------- - Parameters: - - Returns value: - - Description -------------------------------------------------------*/ -void ErrorExit(int ecode, const char *fmt, ...) -{ - va_list args; - extern char *ErrorExitDoneFile; - - Gerror = ecode; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - fprintf(stderr, "\n"); - fflush(stderr); - fflush(stdout); - if (errno) perror(NULL); - - if (ErrorExitDoneFile != NULL) { - // This creates a text file with the value of 1. This can - // be used to let another process know that this process - // is finshed and exited with an error. Good when submitting - // to cluster - ErrorWriteDoneFile(ErrorExitDoneFile, 1); - } - - if (error_exit) - (*error_exit)(ecode); - else - exit(ecode); -} - -/*! - \fn int ErrorWriteDoneFile(char *DoneFile, int errorcode) - \brief This creates a text file with the contents being - errorcode. This can be used to let another process know that this - process is finshed and exited with or without an error. Good when - submitting to cluster. The basic idea is to use - SetErrorExitDoneFile(char *DoneFile) to set the file for - ErrorExit(). If no error occurs during processing, then - the process should run ErrorWriteDoneFile(DoneFile, 0); - */ -int ErrorWriteDoneFile(char *DoneFile, int errorcode) -{ - FILE *fp; - if (DoneFile == NULL) return (0); - fp = fopen(DoneFile, "w"); - if (fp == NULL) return (1); - fprintf(fp, "%d\n", errorcode); - fclose(fp); - return (0); -} - -/*----------------------------------------------------- - Parameters: - - Returns value: - - Description -------------------------------------------------------*/ -int ErrorPrintf(int ecode, const char *fmt, ...) -{ - va_list args; - FILE *fp; - - Gerror = ecode; - va_start(args, fmt); - (*error_vfprintf)(stderr, fmt, args); - fprintf(stderr, "\n"); - fflush(stderr); - fflush(stdout); - va_end(args); - if (errno) perror(NULL); - - va_start(args, fmt); - fp = fopen(ERROR_FNAME, "a"); - if (fp) { - (*error_vfprintf)(fp, fmt, args); - fprintf(fp, "\n"); - fclose(fp); /* close file to flush changes */ - } - va_end(args); - return (ecode); -} - -/*----------------------------------------------------- - Parameters: - - Returns value: - - Description -------------------------------------------------------*/ -int ErrorSetExitFunc(void (*exit_func)(int ecode)) -{ - error_exit = exit_func; - return (1); -} diff --git a/utils/log.cpp b/utils/log.cpp index 2acd977fd2a..19ff59c8344 100644 --- a/utils/log.cpp +++ b/utils/log.cpp @@ -1,59 +1,146 @@ #include +#include +#include #include +#include +#include #include -#include #include "log.h" -// destructor calls the print routine -StreamLogger::~StreamLogger() { - const char *content = ss.str().c_str(); - switch(status) { - case Warning : fprintf(stderr, "%swarning:%s %s\n", term::yellow(), term::reset(), content); break; - case Error : fprintf(stderr, "%serror:%s %s\n", term::red(), term::reset(), content); break; - case Debug : fprintf(stdout, "%s%s%s\n", term::dim(), content, term::reset()); break; +namespace term { + + // Checks to make sure that stdout and stderr are ttys that accept colors. +#if 0 + static bool termAllowsColor() { + if (!isatty(fileno(stderr))) return false; + if (!isatty(fileno(stdout))) return false; + if (const char* term = getenv("TERM")) { + return 0 == strcmp(term, "cygwin") + || 0 == strcmp(term, "linux") + || 0 == strcmp(term, "rxvt-unicode-256color") + || 0 == strcmp(term, "screen") + || 0 == strcmp(term, "screen-256color") + || 0 == strcmp(term, "tmux-256color") + || 0 == strcmp(term, "xterm") + || 0 == strcmp(term, "xterm-256color") + || 0 == strcmp(term, "xterm-termite") + || 0 == strcmp(term, "xterm-color"); + } + else return false; } +#else + // colors aren't fully tested yet, so let's always return false until confident + static bool termAllowsColor() { return false; } +#endif + + const char* black() { return termAllowsColor() ? "\e[30m" : ""; } + const char* red() { return termAllowsColor() ? "\e[31m" : ""; } + const char* green() { return termAllowsColor() ? "\e[32m" : ""; } + const char* yellow() { return termAllowsColor() ? "\e[33m" : ""; } + const char* blue() { return termAllowsColor() ? "\e[34m" : ""; } + const char* purple() { return termAllowsColor() ? "\e[35m" : ""; } + const char* cyan() { return termAllowsColor() ? "\e[36m" : ""; } + const char* white() { return termAllowsColor() ? "\e[37m" : ""; } + const char* bold() { return termAllowsColor() ? "\e[1m" : ""; } + const char* dim() { return termAllowsColor() ? "\e[2m" : ""; } + const char* underline() { return termAllowsColor() ? "\e[4m" : ""; } + const char* reset() { return termAllowsColor() ? "\e[0m" : ""; } + +} + + +// static logging options +static std::string errorlog = std::string(); +static bool exceptions = false; + +int Gerror = NO_ERROR; + + +/** + Configures the global error log file. All messages sent to `fs::fatal()` and + `fs::error()` are written to this file. +*/ +void setErrorLog(const std::string& filename) +{ + errorlog = filename; +} + + +/** + Configures a global setting to throw exceptions instead of exiting + when `fs::fatal()` and `ErrorExit()` are called. +*/ +void throwExceptions(bool setting) +{ + exceptions = setting; +} + - if (exitout) exit(retcode); +/** + Writes a message the global error log if configured with `setErrorLog(filename)`. + **This is for internal use only** - use `fs::error()` to throw errors. +*/ +void detail::writeToErrorLog(const std::string& message) +{ + if (errorlog.empty()) return; + + std::ofstream file; + file.open(errorlog, std::ios_base::app); + file << message << std::endl; } -// checks to make sure the output is a tty that accepts colors -static bool termAllowsColor() { - if (!isatty(fileno(stderr))) return false; - if (const char* term = getenv("TERM")) { - return 0 == strcmp(term, "cygwin") - || 0 == strcmp(term, "linux") - || 0 == strcmp(term, "rxvt-unicode-256color") - || 0 == strcmp(term, "screen") - || 0 == strcmp(term, "screen-256color") - || 0 == strcmp(term, "tmux-256color") - || 0 == strcmp(term, "xterm") - || 0 == strcmp(term, "xterm-256color") - || 0 == strcmp(term, "xterm-termite") - || 0 == strcmp(term, "xterm-color"); +/** + Exits with a given return code, or throws exceptions if enabled via `throwExceptions(true)`. + **This is for internal use only** - use `fs::fatal(code)` to throw a fatal error. +*/ +void detail::errorExit(int code) +{ + if (exceptions) { + if (code != 0) throw code; + } else { + exit(code); } - else return false; } -namespace term { - // colors - const char* black() { return termAllowsColor() ? "\e[30m" : ""; } - const char* red() { return termAllowsColor() ? "\e[31m" : ""; } - const char* green() { return termAllowsColor() ? "\e[32m" : ""; } - const char* yellow() { return termAllowsColor() ? "\e[33m" : ""; } - const char* blue() { return termAllowsColor() ? "\e[34m" : ""; } - const char* purple() { return termAllowsColor() ? "\e[35m" : ""; } - const char* cyan() { return termAllowsColor() ? "\e[36m" : ""; } - const char* light_gray() { return termAllowsColor() ? "\e[37m" : ""; } - const char* white() { return termAllowsColor() ? "\e[37m" : ""; } - const char* light_red() { return termAllowsColor() ? "\e[91m" : ""; } - const char* dim() { return termAllowsColor() ? "\e[2m" : ""; } - // formating - const char* bold() { return termAllowsColor() ? "\e[1m" : ""; } - const char* underline() { return termAllowsColor() ? "\e[4m" : ""; } - // reset - const char* reset() { return termAllowsColor() ? "\e[0m" : ""; } -} \ No newline at end of file +// a rather ugly but safe way to convert a va_list to a string +#define vargsToString(format, message) \ + va_list vargs; \ + va_start(vargs, format); \ + va_list vaCopy; \ + va_copy(vaCopy, vargs); \ + const int iLen = std::vsnprintf(NULL, 0, format, vaCopy); \ + va_end(vaCopy); \ + std::vector zc(iLen + 1); \ + std::vsnprintf(zc.data(), zc.size(), format, vargs); \ + va_end(vargs); \ + const std::string message = std::string(zc.data(), zc.size()); + + +/** + A c-style error function that wraps the cpp stream logging utilities. Ideally, `fs::error() << message` + should be used instead. +*/ +void ErrorPrintf(int errcode, const char *format, ...) +{ + Gerror = errcode; + if (errno) fs::error() << strerror(errno); + vargsToString(format, message); + fs::error() << message; +} + + +/** + A c-style fatal error that wraps the cpp stream logging utilities. Ideally, `fs::fatal(code) << message` + should be used instead. +*/ +void ErrorExit(int errcode, const char *format, ...) +{ + Gerror = errcode; + if (errno) fs::error() << strerror(errno); + vargsToString(format, message); + fs::fatal(errcode) << message; +}