306 lines
9.7 KiB
C++
306 lines
9.7 KiB
C++
/***************************************************************************
|
|
* TWAIN Data Source Manager version 2.1
|
|
* Manages image acquisition data sources used by a machine.
|
|
* Copyright © 2007 TWAIN Working Group:
|
|
* Adobe Systems Incorporated,AnyDoc Software Inc., Eastman Kodak Company,
|
|
* Fujitsu Computer Products of America, JFL Peripheral Solutions Inc.,
|
|
* Ricoh Corporation, and Xerox Corporation.
|
|
* All rights reserved.
|
|
*
|
|
* This library 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.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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 library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Contact the TWAIN Working Group by emailing the Technical Subcommittee at
|
|
* twainwg@twain.org or mailing us at 13090 Hwy 9, Suite 3, Boulder Creek, CA 95006.
|
|
*
|
|
***************************************************************************/
|
|
|
|
/**
|
|
* @file log.cpp
|
|
* Log messages.
|
|
* Provide logging for the messages to and from the Data Source Manager.
|
|
* @author TWAIN Working Group
|
|
* @date March 2007
|
|
*/
|
|
|
|
#include "dsm.h"
|
|
|
|
|
|
|
|
/**
|
|
* Enviroment varible of path to where to write the LogFile name.
|
|
* @see CTwnDsmLog
|
|
*/
|
|
#define kLOGENV "TWAINDSM_LOG"
|
|
|
|
/**
|
|
* Enviroment varible of the fopen logmode to use (if you need to
|
|
* grow the log). The default behavior is to wipe it clean each time
|
|
* we start up...
|
|
* @see CTwnDsmLog
|
|
*/
|
|
#define kLOGMODEENV "TWAINDSM_LOGMODE"
|
|
|
|
/**
|
|
* Maximum message length we can handle...
|
|
* @see CTwnDsmLog
|
|
*/
|
|
#define TWNDSM_MAX_MSG 1024
|
|
|
|
|
|
|
|
/**
|
|
* Our implementation class where we hide our attributes...
|
|
*/
|
|
class CTwnDsmLogImpl
|
|
{
|
|
public:
|
|
/// Make sure we're squeaky clean...
|
|
CTwnDsmLogImpl()
|
|
{
|
|
memset(&pod,0,sizeof(pod));
|
|
}
|
|
|
|
public:
|
|
// If you add a class in future, (and I can't imagine why you
|
|
// would) declare it here and not in the pod, or the memset
|
|
// we do in the constructor will ruin your day...
|
|
|
|
/**
|
|
* We use a pod system because it help prevents us from
|
|
* making dumb initialization mistakes...
|
|
*/
|
|
struct _pod
|
|
{
|
|
FILE *m_plog; /**< where we'll dump information. */
|
|
char *m_message; /**< buffer for our messages. */
|
|
char m_logpath[FILENAME_MAX]; /**< where we put the file. */
|
|
char m_logmode[16]; /**< how we fopen the file. */
|
|
int m_nIndent; /**< how far to indent the log message */
|
|
} pod; /**< Pieces of data for CTwnDsmAppsImpl*/
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
* The constructor for our class. This is where we see if we have a
|
|
* file in the TWAINDSM_LOG environment variable. If so, then we'll
|
|
* log stuff. If not, then we'll log nothing. TWAINDSM_LOGMODE
|
|
* selects how we open the file. The default value is "w+", which
|
|
* means it's wiped out each time a new session is started. Setting
|
|
* this environmental to "a+" will cause the log information to be
|
|
* appended to an existing file (a new one will still be created if
|
|
* needed...
|
|
*/
|
|
CTwnDsmLog::CTwnDsmLog()
|
|
{
|
|
// Init stuff...
|
|
m_ptwndsmlogimpl = new CTwnDsmLogImpl;
|
|
|
|
// see if a logfile is to be used
|
|
SGETENV(m_ptwndsmlogimpl->pod.m_logpath,NCHARS(m_ptwndsmlogimpl->pod.m_logpath),kLOGENV);
|
|
|
|
// If we have a path, then get our mode...
|
|
if (m_ptwndsmlogimpl->pod.m_logpath[0])
|
|
{
|
|
SGETENV(m_ptwndsmlogimpl->pod.m_logmode,NCHARS(m_ptwndsmlogimpl->pod.m_logmode),kLOGMODEENV);
|
|
if (!m_ptwndsmlogimpl->pod.m_logmode[0])
|
|
{
|
|
// The default is to wipe the log clean...
|
|
SSTRCPY(m_ptwndsmlogimpl->pod.m_logmode,sizeof(m_ptwndsmlogimpl->pod.m_logmode),"w");
|
|
}
|
|
|
|
// Only bother to allocate a buffer if logging is on...
|
|
m_ptwndsmlogimpl->pod.m_message = (char*)calloc(TWNDSM_MAX_MSG,1);
|
|
if (!m_ptwndsmlogimpl->pod.m_message)
|
|
{
|
|
kPANIC("Unable to allocate a buffer for logging...");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* The destructor for our class. Make sure the log is closed,
|
|
* free the buffer and destroy our implementation class...
|
|
*/
|
|
CTwnDsmLog::~CTwnDsmLog()
|
|
{
|
|
if (m_ptwndsmlogimpl)
|
|
{
|
|
if (m_ptwndsmlogimpl->pod.m_plog)
|
|
{
|
|
fclose(m_ptwndsmlogimpl->pod.m_plog);
|
|
}
|
|
if (m_ptwndsmlogimpl->pod.m_message)
|
|
{
|
|
free(m_ptwndsmlogimpl->pod.m_message);
|
|
}
|
|
delete m_ptwndsmlogimpl;
|
|
m_ptwndsmlogimpl = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Logging function.
|
|
*
|
|
* We provide a timestamp from hours to milliseconds, which can be
|
|
* used to help with performance, and to detect large, unexpected
|
|
* idle times. The filename and line number in the source code is
|
|
* provided. GetLastError or errno may offer a hint about a problem
|
|
* with a system call, but be careful, since it's not cleared and so
|
|
* it may report a message that has nothing to do with the current
|
|
* calls, or anything going on in the DSM. The id of the thread that
|
|
* called us is useful for finding problems with unsafe use, or use
|
|
* that crosses thread boundaries in a bad way (like on Windows, when
|
|
* one has to stay in the same thread as the HWND if the DAT_NULL
|
|
* messages are going to work)...
|
|
*/
|
|
void CTwnDsmLog::Log(const int _doassert,
|
|
const char* const _file,
|
|
const int _line,
|
|
const char* const _format,
|
|
...)
|
|
{
|
|
// We've nothing to do, so bail...
|
|
if (0 == m_ptwndsmlogimpl->pod.m_logpath[0])
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Okay, now use the stack...
|
|
UINT nError = 0;
|
|
UINT nChars = 0;
|
|
char *message = NULL;
|
|
const char *file = NULL;
|
|
|
|
// Grab the system error, this can be really useful...
|
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|
nError = GetLastError();
|
|
if (nError == 0)
|
|
{
|
|
// Yeah, yeah...this is dumb, but I like a clean prefast log... :)
|
|
nError = 0;
|
|
}
|
|
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
|
|
nError = errno;
|
|
#else
|
|
#error Sorry, we do not recognize this system...
|
|
#endif
|
|
|
|
// If we have no log yet, try to get one...
|
|
if (0 == m_ptwndsmlogimpl->pod.m_plog)
|
|
{
|
|
FOPEN(m_ptwndsmlogimpl->pod.m_plog,m_ptwndsmlogimpl->pod.m_logpath,m_ptwndsmlogimpl->pod.m_logmode);
|
|
if (0 == m_ptwndsmlogimpl->pod.m_plog)
|
|
{
|
|
fprintf(stderr,"DSM: Error - logging has been disabled because logfile could not be opened: file=<%s>, mode=<%s>, errno=%d\r\n",m_ptwndsmlogimpl->pod.m_logpath,m_ptwndsmlogimpl->pod.m_logmode,errno);
|
|
m_ptwndsmlogimpl->pod.m_logpath[0] = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
// Trim the filename down to just the filename, no path...
|
|
file = 0;
|
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|
// Only look for this on Windows...
|
|
file = strrchr(_file,'\\');
|
|
#endif
|
|
if (!file)
|
|
{
|
|
// If we didn't find a backslash, try a forward slash...
|
|
file = strrchr(_file,'/');
|
|
}
|
|
if (file)
|
|
{
|
|
// skip the slash...
|
|
file = &file[1];
|
|
}
|
|
else
|
|
{
|
|
// Couldn't find any slashes...
|
|
file = (char*)_file;
|
|
}
|
|
|
|
// Build the message header...
|
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|
SYSTEMTIME st;
|
|
GetLocalTime(&st);
|
|
nChars = SNPRINTF(m_ptwndsmlogimpl->pod.m_message,
|
|
TWNDSM_MAX_MSG,
|
|
#if (TWNDSM_CMP_VERSION >= 1400)
|
|
TWNDSM_MAX_MSG,
|
|
#endif
|
|
"[%02d%02d%02d%03d %-8s %4d %5u %p] %.*s",
|
|
(int)st.wHour, (int)st.wMinute, (int)st.wSecond,(int)st.wMilliseconds,
|
|
file, (int)_line,
|
|
nError,
|
|
(void*)(UINT_PTR)GETTHREADID(),
|
|
m_ptwndsmlogimpl->pod.m_nIndent*2, " ");
|
|
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
|
|
timeval tv;
|
|
tm tm;
|
|
gettimeofday(&tv,NULL);
|
|
tzset();
|
|
localtime_r(&tv.tv_sec,&tm);
|
|
nChars = SNPRINTF(m_ptwndsmlogimpl->pod.m_message,
|
|
TWNDSM_MAX_MSG,
|
|
"[%02d%02d%02d%03d %-8s %4d %5d %p] %.*s",
|
|
tm.tm_hour,tm.tm_min,tm.tm_sec,(int)(tv.tv_usec / 1000),
|
|
file,_line,
|
|
nError,
|
|
(void*)GETTHREADID(),
|
|
m_ptwndsmlogimpl->pod.m_nIndent*2, " ");
|
|
|
|
#else
|
|
#error Sorry, we do not recognize this system...
|
|
#endif
|
|
|
|
// This is the room remaining in the buffer, with room for a null...
|
|
nChars = (TWNDSM_MAX_MSG - nChars) - 1;
|
|
message = &m_ptwndsmlogimpl->pod.m_message[strlen(m_ptwndsmlogimpl->pod.m_message)];
|
|
|
|
// Finally, tack on the user portion of the message...
|
|
va_list valist;
|
|
va_start(valist,_format);
|
|
#if (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP) && (TWNDSM_CMP_VERSION >= 1400)
|
|
_vsnprintf_s(message,nChars,nChars,_format,valist);
|
|
#elif (TWNDSM_CMP == TWNDSM_CMP_VISUALCPP)
|
|
_vsnprintf(message,nChars,_format,valist);
|
|
#elif (TWNDSM_CMP == TWNDSM_CMP_GNUGPP)
|
|
vsnprintf(message,nChars,_format,valist);
|
|
#else
|
|
#error Sorry, we do not recognize this system...
|
|
#endif
|
|
va_end(valist);
|
|
|
|
// Write the message...
|
|
fprintf(m_ptwndsmlogimpl->pod.m_plog,"%s\r\n",m_ptwndsmlogimpl->pod.m_message);
|
|
fflush(m_ptwndsmlogimpl->pod.m_plog);
|
|
|
|
// Do the assert, if asked for...
|
|
if (_doassert)
|
|
{
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
void CTwnDsmLog::Indent(int nChange)
|
|
{
|
|
m_ptwndsmlogimpl->pod.m_nIndent += nChange;
|
|
}
|