/* ********************************************************************* This source file is a part of the standard library of Scol For the latest info, see http://www.scolring.org Copyright (c) 2013 Stephane Bisaro aka Iri. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt ********************************************************************* */ /* * Standard functions for errors and customized log files * See http://redmine.scolring.org/projects/tutorials/wiki/Scol_usage * for more informations */ /*! \file error.pkg * \author Scol team * \version 0.2 * \warning This API requires sprintf, currently in Syspack Scol library. * \warning To use it with the Console library (lib/console), this API requires * too the lib/console/console.pkg package. * \copyright GNU Lesser General Public License 2.0 or later * \brief Scol Standard Library - Error API * * \details This API provides an high level method to easily include a customized error manager * * You can : * - define your masks yourself * - enabled / disabled this manager * - define your code numbers and messages error * - write - or not - in the Scol console * - use a hard file log or a temporary file log * - and more things yet * * It is NOT recommanded to use the internal STD_ERROR structure to any application. * Use this API instead. * **/ /*! \struct STD_ERROR * * \brief Opaque internal structure. You should not call it directly, use * API instead ! * **/ struct STD_ERROR = [ StdErrMask : I, /*!< current mask. If this mask and the message mask are OK, the message will be written */ StdErrMaskLast : I, /*!< last mask set, to allow an 'undo' */ StdErrPeriod : I, /*!< not used */ StdErrEnabled : I, /*!< object is enabled (1) or disabled (0) */ StdErrTmp : I, /*!< 1 for a temporary log file else 0 (regular file) */ StdErrTmpFile : FileTemp, /*!< if Temporary file asked (1) */ StdErrFilename : W, /*!< Physical file of Logfile object */ StdErrLast : [I S], /*!< Last message printed (number and string) */ StdErrFoo : I, /*!< Write (1) or no (0) the message in the Scol console (_fooXXX functions) */ StdErrConsole : I, /*!< Show (1) or hide (0) the console */ StdErrTypes : [[I S] r1]/*!< list of customized errors (code error and string) */ ] mkSTD_ERROR;; /*! \var StdErrList * List of all managers created. No use directly **/ typeof StdErrList = [STD_ERROR r1];; /* set the cursor to the end of the file */ fun std_errsettemptoend (err)= _FILESeekTemp err.StdErrTmpFile 0 SEEK_END;; /* fun [S] W */ fun std_errcheckfilename (filename)= // _getmodifypack if std_szCheckLastChar filename "/" then filename else strcat filename "/";; _getmodifypack filename;; /*! \brief Open a new manager * By default, the manager is ENABLED, the console message and the console are disabled. * Other parameters are at nil * * \ingroup std_error * * Prototype: fun [I] STD_ERROR * * \param I : a mask * * \return STD_ERROR : the new manager object **/ fun std_errOpen (mask)= let mkSTD_ERROR [nil nil nil nil nil nil nil nil nil nil nil] -> s in ( set s.StdErrMask = mask; set s.StdErrEnabled = 1; // enabled by default set s.StdErrFoo = 0; // console message disabled by default set s.StdErrConsole = 0; // hide the console by default set s.StdErrTmp = 0; // hard log file set StdErrList = s :: StdErrList; s );; /* create a new temporary log file and write into a first string. * The cursor is set to the end */ fun std_errnewtemp (err, str)= if nil == set err.StdErrTmpFile = _FILEOpenTemp _channel then nil else ( _FILEWriteTemp err.StdErrTmpFile str; std_errsettemptoend err; err );; /*! \brief Open a new log file * The current date is written. * * \ingroup std_error * * Prototype: fun [STD_ERROR S] STD_ERROR * * \param STD_ERROR : a manager object * \param S : a filename for the log file if the TMPFILE is disabled * if TMPFILE is enabled, this argument is ignored and could be nil. * * \remark if any, the current file is overwritten. * * \return STD_ERROR : the same manager object or nil if error **/ fun std_errOpenLog (err, filename)= if err == nil then nil else let gmtime time -> [sec mn h d m y _ _] in let sprintf "Date : %i %i, %i - %ih %imn %isec\n" [y m d h mn sec] -> str in ( if 1 == err.StdErrTmp then std_errnewtemp err str else if filename == nil then nil else ( // set filename = _getmodifypack if std_szCheckLastChar filename "/" then filename else strcat filename "/"; let std_errcheckfilename filename -> w in if !_createpack str w then ( set err.StdErrFilename = w; err ) else nil ) );; // _createpack sprintf "Date : %i %i, %i% - %ih %imn %isec\n" [y m d h mn sec] filename; // _openlog filename err.StdErrMask period; /*! \brief Open a new log file. The filename is the current date and time * with a prefix : my_folder/the_prefix_2013_12_14-17_25_21.log * The current date is written. * The TMPFILE must be disabled. * * \ingroup std_error * * Prototype: fun [STD_ERROR S] STD_ERROR * * \param STD_ERROR : a manager object * \param S : a filename for the log file such as "my_folder/the_prefix" * * Note : if any, the current file is overwritten. * * \return STD_ERROR : the same manager object or nil if error (manager * is nil or TMPFILE enabled) **/ fun std_errOpenLogNow (err, name)= if ((err == nil) || (err.StdErrTmp)) then nil else ( // set name = if name == nil then "Logs/noname" else if std_szCheckLastChar name "/" then name else strcat name "/"; set name = if name == nil then "Logs/noname" else let strfindi ".log" name 0 -> pos in if pos == nil then name else substr name 0 pos; let gmtime time -> [sec mn h d m y _ _] in let sprintf "Date : %i %i, %i - %ih %imn %isec\n" [y m d h mn sec] -> msg in let sprintf "%s_%i_%i_%i-%i_%i_%i.log" [name y m d h mn sec] -> filename in std_errOpenLog err filename );; /*! \brief Close a manager. If TMPFILE is enabled, the temporary file is * closed and its content is lost. * * \ingroup std_error * * Prototype: fun [STD_ERROR] I * * \param STD_ERROR : a manager object * \param S : a filename for the log file * * \return I : 0 if success, 1 if the manager is already closed (or not created), * 2 if the temporary file can not be closed correctly **/ fun std_errClose (err)= if err == nil then 1 else let 0 -> r in ( if err.StdErrTmp then if !_FILECloseTemp err.StdErrTmpFile then set r = r+2 else 0 else 0; set StdErrList = std_lRemoveElt StdErrList err; set err = nil; r );; /*! \brief Open a new manager in copying the configuration of another one. * * * \ingroup std_error * * Prototype: fun [STD_ERROR S] STD_ERROR * * \param STD_ERROR : a manager object * \param S : a filename for the log file or nil if a temporary file is prefered * * Note : if any, the current file is overwritten. * * \return STD_ERROR : the new manager object or nil if error **/ fun std_errCopy (err, filename)= if err == nil then nil else if ((!err.StdErrTmp) && (filename == nil)) then nil else let mkSTD_ERROR [nil nil nil nil nil nil nil nil nil nil nil] -> s in ( set s.StdErrMask = err.StdErrMask; set s.StdErrMaskLast = err.StdErrMaskLast; set s.StdErrPeriod = err.StdErrPeriod; set s.StdErrEnabled = err.StdErrEnabled; set s.StdErrTmp = err.StdErrTmp; set s.StdErrFoo = err.StdErrFoo; set s.StdErrTypes = err.StdErrTypes; // set filename = _getmodifypack if std_szCheckLastChar filename "/" then filename else strcat filename "/"; let std_errcheckfilename filename -> w in let gmtime time -> [sec mn h d m y _ _] in let sprintf "Date : %i %i, %i% - %ih %imn %isec\n" [y m d h mn sec] -> str in if 1 == err.StdErrTmp then std_errnewtemp s str else if !_createpack str w then ( set s.StdErrFilename = w; s ) else nil );; /*! \brief Returns the current mask of a manager * * \ingroup std_error * * Prototype: fun [STD_ERROR] I * * \param STD_ERROR : a manager object * * \return I : the current mask or nil if error **/ fun std_errGetMask (err)= err.StdErrMask;; /*! \brief Set the current mask of a manager * * \ingroup std_error * * Prototype: fun [STD_ERROR I] STD_ERROR * * \param STD_ERROR : a manager object * \param I : the new mask to set * * \return STD_ERROR : the current manager or nil if error **/ fun std_errSetMask (err, mask)= set err.StdErrMaskLast = err.StdErrMask; set err.StdErrMask = mask; err;; /*! \brief Perform an 'undo' to the mask of a manager * * \ingroup std_error * * Prototype: fun [STD_ERROR] I * * \param STD_ERROR : a manager object * * \return STD_ERROR : the same manager or nil if error **/ fun std_errUndoMask (err)= if err == nil then nil else let err.StdErrMask -> mask in ( set err.StdErrMask = err.StdErrMaskLast; set err.StdErrMaskLast = mask; err );; /*! \brief Get the period. Period is unused, so this function is unused too ! * * \ingroup std_error * * Prototype: fun [STD_ERROR] I * * \param STD_ERROR : a manager object * * \return I : the manager period or nil if error **/ fun std_errGetPeriod (err)= err.StdErrPeriod;; /*! \brief Returns the path of the log file of a manager * * \ingroup std_error * * Prototype: fun [STD_ERROR] S * * \param STD_ERROR : a manager object * * \return S : the path or nil if error (by example, a temporary file is used) **/ fun std_errGetFilename (err)= _PtoScol _WtoP err.StdErrFilename;; /*! \brief Returns the current state of a manager * * \ingroup std_error * * Prototype: fun [STD_ERROR] I * * \param STD_ERROR : a manager object * * \return I : 1 if this manager is enabled, 0 if disabled or nil if error **/ fun std_errGetEnabled (err)= err.StdErrEnabled;; /*! \brief Set the current state of a manager * * \ingroup std_error * * Prototype: fun [STD_ERROR I] STD_ERROR * * \param STD_ERROR : a manager object * \param I : the new state : 1 -> enabled, 0 to disable. Another value is ignored. * * \return STD_ERROR : the same manager or nil if error (manager is nil or bad value) **/ fun std_errSetEnabled (err, state)= if ((state) || (state == 0)) then ( set err.StdErrEnabled = state; err ) else nil;; /*! \brief Returns if, for a manager, the message are displayed in the * Scol console. * * \ingroup std_error * * Prototype: fun [STD_ERROR] I * * \param STD_ERROR : a manager object * * \return I : 1, yes, 0, no or nil if error **/ fun std_errGetFoo (err)= err.StdErrFoo;; /*! \brief Set if error message are displayed in the Scol console * * \ingroup std_error * * Prototype: fun [STD_ERROR I] STD_ERROR * * \param STD_ERROR : a manager object * \param I : the new state : 1 -> enabled, 0 to disable. Another value is ignored. * * \return STD_ERROR : the same manager or nil if error (manager is nil or bad value) **/ fun std_errSetFoo (err, state)= if ((state) || (state == 0)) then ( set err.StdErrFoo = state; err ) else nil;; /*! \brief Returns the last written message * * \ingroup std_error * * Prototype: fun [STD_ERROR] [I S] * * \param STD_ERROR : a manager object * * \return [I S] : a tuple with the last code number and the last string * or nil if error **/ fun std_errGetLast (err)= err.StdErrLast;; /*! \brief Returns the content of the log file (for a manager) * This can be a hard file or a temporary file. * * \ingroup std_error * * Prototype: fun [STD_ERROR] S * * \param STD_ERROR : a manager object * * \return S : the content or nil if error **/ fun std_errGetContent (err)= if err == nil then nil else if err.StdErrTmp then let ( _FILESeekTemp err.StdErrTmpFile SEEK_SET 0; _FILEReadTemp err.StdErrTmpFile _FILESizeTemp err.StdErrTmpFile ) -> str in ( std_errsettemptoend err; str ) else _getpack _WtoP err.StdErrFilename;; /*! \brief A conivience function. * Save the content of the temporary file in a hard file * * \ingroup std_error * * Prototype: fun [STD_ERROR W] I * * \param STD_ERROR : a manager object * \param W : a write reference path name to save the current datas * * \return I : 0 if success or nil or another value not null if error **/ fun std_errSaveTemp (err, filename)= if ((err != nil) || (err.StdErrTmp)) then let ( _FILESeekTemp err.StdErrTmpFile SEEK_SET 0; _FILEReadTemp err.StdErrTmpFile _FILESizeTemp err.StdErrTmpFile ) -> str in _createpack str _getmodifypack filename else nil;; /*! \brief Returns the list of defined eror types for a manager * * \ingroup std_error * * Prototype: fun [STD_ERROR] [[I S] r1] * * \param STD_ERROR : a manager object * * \return [[I S] r1] : the list or nil if error **/ fun std_errGetTypes (err)= if err == nil then nil else err.StdErrTypes;; /*! \brief Returns the string error message for a given number code error * (for a manager) * * \ingroup std_error * * Prototype: fun [STD_ERROR I] S * * \param STD_ERROR : a manager object * \param I : an error code number * * \return S : this error message or nil if error **/ fun std_errGetTypeFromNumber (err, number)= switch err.StdErrTypes number;; fun std_errgettypefromstring (list, str)= if list == nil then nil else let hd list -> [i s] in if !strcmpi s str then i else std_errgettypefromstring tl list str;; /*! \brief Returns the number code error from a given string error message * (for a manager) * * \ingroup std_error * * Prototype: fun [STD_ERROR S] I * * \param STD_ERRO] : a manager object * \param S : a string error (case-insensitive) * * \return I : the error number or nil if error **/ fun std_errGetTypeFromString (err, str)= std_errgettypefromstring err.StdErrTypes str;; /*! \brief Add a new predefined error in a manager * * \ingroup std_error * * Prototype: fun [STD_ERROR I S] STD_ERROR * * \param STD_ERROR : a manager object * \param I : a new number error * \param S : the appropriate message string * * \return STD_ERROR : the same manager or nil if error (manager is nil or number already existing) **/ fun std_errAddType (err, num, str)= if nil == std_errGetTypeFromNumber err num then ( set str = if str == nil then "" else str; set err.StdErrTypes = [num str] :: err.StdErrTypes; err ) else nil;; /*! \brief Reset all predefined error in a manager * * \ingroup std_error * * Prototype: fun [STD_ERROR] STD_ERROR * * \param STD_ERROR : a manager object * * \return STD_ERROR : the same manager **/ fun std_errClearTypes (err)= set err.StdErrTypes = nil; err;; fun std_errremovetype (tuple, e)= let tuple -> [n _] in n == e;; /*! \brief Remove a predefined error in a manager * * \ingroup std_error * * Prototype: fun [STD_ERROR I] STD_ERROR * * \param STD_ERROR : a manager object * \param I : a number error to remove * * \return STD_ERROR : the same manager or nil if error **/ fun std_errRemoveType (err, num)= set err.StdErrTypes = std_lRemoveFunc err.StdErrTypes @std_errremovetype num; err;; /*! \brief Returns if a temporary file is used (for a manager) * * \ingroup std_error * * Prototype: fun [STD_ERROR] I * * \param STD_ERROR : a manager object * * \return I : yes (1) or no (0) or nil if error **/ fun std_errGetIsTemp (err)= err.StdErrTmp;; /*! \brief Set if a manager uses (or not) a temporary file. * If the old and the new value are the same, no change is done. * If the new value is 1 and the old value was 0, a new temporary file is * created and the content of the old 'hard' file is copied in the new * temporary file. The old 'hard' file is unchanged and no new message * will be written. * If the new file is 0 and the 'hard' file name is not nil, the file is * created and the content of the old temporary file is copied. This last * one is closed (and destroyed). * All other configuration or an invalid argument will return a nil value. * * \ingroup std_error * * Prototype: fun [STD_ERROR I] STD_ERROR * * \param STD_ERROR : a manager object * \param I : 1 to use a temporary file, else 0 * \param S : a filename (should be nil if the temporay file will be used) * * \return STD_ERROR : the same manager or nil if error **/ fun std_errSetIsTemp (err, isTmp, filename)= if isTmp == err.StdErrTmp then // no change to do err else if ((isTmp) && (!err.StdErrTmp)) then ( set err.StdErrTmp = isTmp; std_errnewtemp err _getpack _WtoP err.StdErrFilename ) else if ((!isTmp) && (filename != nil)) then ( // set filename = _getmodifypack if std_szCheckLastChar filename "/" then filename else strcat filename "/"; let std_errcheckfilename filename -> w in let ( _FILESeekTemp err.StdErrTmpFile SEEK_SET 0; _FILEReadTemp err.StdErrTmpFile _FILESizeTemp err.StdErrTmpFile ) -> str in if !_createpack str w then ( set err.StdErrFilename = w; _FILECloseTemp err.StdErrTmpFile; err ) else nil ) else nil;; /*! \brief Returns if the Scol console is shown (for this manager) * Only if this API manages the Scol console, if another code show * or hide it, this return can be wrong. * * \ingroup std_error * * Prototype: fun [STD_ERROR] I * * \param STD_ERROR : a manager object * * \return I : show (1) or hide (0) or nil if error **/ fun std_errGetConsole (err)= err.StdErrConsole;; /*! \brief Set the state of the Scol console * if another code show or hide it, the effect can be different. * * \ingroup std_error * * Prototype: fun [STD_ERROR I] STD_ERROR * * \param STD_ERROR : a manager object * \param I : 1 to show the Scol console, 0 to hide it. * * \return STD_ERROR : the same manager **/ fun std_errSetConsole (err, state)= if (state == err.StdErrConsole) then err else ( if (set err.StdErrConsole = state) then _showconsole else _hideconsole; err );; fun std_errprint (err, num, str, mask)= if err == nil then 1 else if !err.StdErrEnabled then 2 else if ((err.StdErrMask & mask)) then 3 else let gmtime time -> [sec mn h d m y _ _] in let sprintf "Date : %i %i, %i - %ih %imn %isec :\n" [y m d h mn sec] -> d in //let strcatn d :: (itoa num) :: " " :: str :: "\n" :: nil -> s in let sprintf "%s %i %s\n" [d num str] -> s in (_fooS s; if err.StdErrFoo then _fooS s else nil; if err.StdErrTmp then if !_FILEWriteTemp err.StdErrTmpFile s then ( std_errsettemptoend err; set err.StdErrLast = [num str]; 0 ) else 4 else if !_appendpack s err.StdErrFilename then ( set err.StdErrLast = [num str]; 0 ) else 5 );; /*! \brief Send a new string message error to a manager. * According the manager settings, the string can be written in the Scol * console, in the application log file if the client set it, in the * customized log file, ... * * \ingroup std_error * * Prototype: fun [STD_ERROR S I] I * * \param STD_ERROR : a manager object * \param S : the string message error * \param I : a mask for this message. It is tested with the mask of the * manager to determine wether the message should be written. If the test * is positive, the message is immediatly written. * Thus, the logical 'mask_of_manager & mask_of_this_message' must be not null * to write the message. * * \return I : 0 if success, 1 if the manager is nil, 2 if the manager * is disabled, 3 if the test is negative, 4 if the message can not be * written in the temporary file, 5 if the message cannot be written in * the 'hard' file. **/ fun std_errString (err, str, mask)= std_errprint err nil str mask;; /*! \brief Send a predefined error to a manager. * According the manager settings, the string can be written in the Scol * console, in the application log file if the client set it, in the * customized log file, ... * * \ingroup std_error * * Prototype: fun [STD_ERROR I I] I * * \param STD_ERROR : a manager object * \param I : a predefined number error * \param I : a mask for this message. It is tested with the mask of the * manager to determine wether the message should be written. If the test * is positive, the message is immediatly written. * Thus, the logical 'mask_of_manager & mask_of_this_message' must be at not null * to write the message. * * \return I : 0 if success, 1 if the manager is nil, 2 if the manager * is disabled, 3 if the test is negative, 4 if the message can not be * written in the temporary file, 5 if the message cannot be written in * the 'hard' file or 6 if the number is not a predefined error. **/ fun std_errNumber (err, num, mask)= _fooId num; let std_errGetTypeFromNumber err num -> str in if (str == nil) then 6 else std_errprint err num str mask;; /*! \brief Return the number of opened managers * * \ingroup std_error * * Prototype: fun [ ] I * * \return I : the number **/ fun std_errObjNumbers ()= sizelist StdErrList;; /*! \brief Returns the last created(opened) manager * * \ingroup std_error * * Prototype: fun [] STD_ERROR * * \return STD_ERROR : this last (can be nil if no manager open) **/ fun std_errObjLast ()= hd StdErrList;; fun std_errgetobjenabled (list, a)= if list == nil then nil else let hd list -> err in if a == err.StdErrEnabled then err :: std_errgetobjenabled tl list a else std_errgetobjenabled tl list a;; fun std_errgetobjenabled2 (a)= /* not used */ let StdErrList -> list in let nil -> out in ( while list != nil do let hd list -> err in ( if a == err.StdErrEnabled then set out = err :: out else nil; set list = tl list ); out );; /*! \brief Returns a list of all manager enabled * * \ingroup std_error * * Prototype: fun [] [STD_ERROR r1] * * \return [STD_ERROR r1] : this list (can be nil if no manager enabled) **/ fun std_errGetObjEnabled ()= std_errgetobjenabled StdErrList 1;; /*! \brief Returns a list of all manager disabled * * \ingroup std_error * * Prototype: fun [] [STD_ERROR r1] * * \return [STD_ERROR r1] : this list (can be nil if no manager disabled) **/ fun std_errGetObjDisabled ()= std_errgetobjenabled StdErrList 0;; proto console_print = fun [CONSOLE S] I;; /*! \brief Same thing than std_errString. * In addition, you can print the string message error in a EasyConsole * (see in lib/console directory) * * \ingroup std_error * * Prototype: fun [STD_ERROR CONSOLE S I] I * * \param STD_ERROR : a manager object * \param CONSOLE : an EasyConsole already created. * \param S : the string message error * \param I : a mask for this message. It is tested with the mask of the * manager to determine wether the message should be written. If the test * is positive, the message is immediatly written. * Thus, the logical 'mask_of_manager & mask_of_this_message' must be at not null * to write the message. * \remark : The mask is always ignored to writing in the EsayConsole. * * \return I : 0 if success, 1 if the manager is nil, 2 if the manager * is disabled, 3 if the test is negative, 4 if the message can not be * written in the temporary file, 5 if the message cannot be written in * the 'hard' file. **/ fun std_errStringAndInConsole (err, csl, str, mask)= let gmtime time -> [sec mn h d m y _ _] in let sprintf "Date : %i %i, %i% - %ih %imn %isec :\n" [y m d h mn sec] -> d in let strcatn d :: str :: "\n" :: nil -> s in ( console_print csl s; std_errprint err nil str mask );; /*! \brief Same thing than std_errNumber. * In addition, you can print the message error in a EasyConsole * (see in lib/console directory) * * \ingroup std_error * * Prototype: fun [STD_ERROR CONSOLE I I] I * * \param STD_ERROR : a manager object * \param CONSOLE : an EasyConsole object * \param I : a predefined number error * \param I : a mask for this message. It is tested with the mask of the * manager to determine wether the message should be written. If the test * is positive, the message is immediatly written. * Thus, the logical 'mask_of_manager & mask_of_this_message' must be at not null * to write the message. * \remark : The mask is always ignored to writing in the EsayConsole. * * \return I : 0 if success, 1 if the manager is nil, 2 if the manager * is disabled, 3 if the test is negative, 4 if the message can not be * written in the temporary file, 5 if the message cannot be written in * the 'hard' file or 6 if the number is not a predefined error. **/ fun std_errNumberAndInConsole (err, csl, num, mask)= let std_errGetTypeFromNumber err num -> str in let gmtime time -> [sec mn h d m y _ _] in let sprintf "Date : %i %i, %i% - %ih %imn %isec :\n" [y m d h mn sec] -> d in let strcatn d :: (itoa num) :: " " :: str :: "\n" :: nil -> s in ( console_print csl s; std_errprint err num str mask );;