Files
taskwarrior-2.x/src/ui/log.cpp
2010-09-09 20:49:17 -04:00

218 lines
5.8 KiB
C++

////////////////////////////////////////////////////////////////////////////////
// task - a command line task list manager.
//
// Copyright 2006 - 2010, Paul Beckingham.
// All rights reserved.
//
// 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.
//
// 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.,
// 51 Franklin Street, Fifth Floor,
// Boston, MA
// 02110-1301
// USA
//
////////////////////////////////////////////////////////////////////////////////
#include <sys/file.h>
#include <sys/stat.h>
#include <pwd.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <string>
////////////////////////////////////////////////////////////////////////////////
// Global data.
static std::string gLogDir = ".";
static std::string gLogName = "default";
static bool gEnabled = true;
static const unsigned int maxMessageLength = 128;
static bool bDuplicates = false;
static std::string gPrior = "none";
static int gRepetitionCount = 0;
////////////////////////////////////////////////////////////////////////////////
void logSetDirectory (const std::string& dir)
{
gLogDir = dir;
}
////////////////////////////////////////////////////////////////////////////////
void logSetName (const std::string& name)
{
gLogName = name;
}
////////////////////////////////////////////////////////////////////////////////
void logEnable (const bool value)
{
gEnabled = value;
}
////////////////////////////////////////////////////////////////////////////////
void logDuplicates (const bool value)
{
bDuplicates = value;
}
////////////////////////////////////////////////////////////////////////////////
void getTimeStamp (std::string& ts)
{
// Get time info.
time_t now;
time (&now);
struct tm* t = localtime (&now);
// Generate timestamp.
char timestamp[20];
sprintf (timestamp, "%04d-%02d-%02d %02d:%02d:%02d",
t->tm_year + 1900,
t->tm_mon + 1,
t->tm_mday,
t->tm_hour,
t->tm_min,
t->tm_sec);
ts = timestamp;
}
////////////////////////////////////////////////////////////////////////////////
// #Version: 1.0
// #Date: 17-Oct-2004
// #Fields: date time x-pid x-message
// 2004-10-18 00:12:00 12345 "Message"
void logWrite (const std::string& message)
{
if (!gEnabled) return;
if (!bDuplicates)
{
if (message == gPrior)
{
++gRepetitionCount;
return;
}
else
gPrior = message;
}
// Get time info.
time_t now;
time (&now);
struct tm* t = localtime (&now);
// Sanitize 'message'.
std::string sanitized = message;
std::string::size_type bad;
while ((bad = sanitized.find ("\"")) != std::string::npos)
sanitized.replace (bad, 1, "'");
if (sanitized.length () > maxMessageLength)
sanitized = sanitized.substr (0, maxMessageLength) + "...";
// Generate timestamp.
char timestamp[20];
sprintf (timestamp, "%04d-%02d-%02d %02d:%02d:%02d",
t->tm_year + 1900,
t->tm_mon + 1,
t->tm_mday,
t->tm_hour,
t->tm_min,
t->tm_sec);
// Determine file name.
char file[_POSIX_PATH_MAX];
sprintf (file, "%s/%s.%04d%02d%02d.log",
gLogDir.c_str (),
gLogName.c_str (),
t->tm_year + 1900,
t->tm_mon + 1,
t->tm_mday);
// Determine whether file exists.
bool exists = access (file, F_OK) != -1 ? true : false;
// Create file if necessary, with header.
FILE* log = fopen (file, "a");
if (log)
{
// Lock file.
#if defined (__SVR4) && defined (__sun)
#else
int fn = fileno (log);
if (! flock (fn, LOCK_EX))
{
#endif
// Create the header, if the file did not exist.
if (! exists)
{
fprintf (log, "# File: %s.%04d%02d%02d.log\n",
gLogName.c_str (),
t->tm_year + 1900,
t->tm_mon + 1,
t->tm_mday);
fprintf (log, "# Fields: date time x-pid x-message\n");
}
// Optionally write a repetition message.
if (gRepetitionCount)
{
fprintf (log, "%s %d \"(Repeated %d times)\"\n",
timestamp,
(int) getpid (),
gRepetitionCount);
gRepetitionCount = 0;
}
// Write entry.
fprintf (log, "%s %d \"%s\"\n",
timestamp,
(int) getpid (),
sanitized.c_str ());
#if defined (__SVR4) && defined (__sun)
#else
}
#endif
// close file.
fclose (log);
if (! exists)
chmod (file, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
}
}
////////////////////////////////////////////////////////////////////////////////
void logWrite (const char* message, ...)
{
if (!gEnabled) return;
// Crude and mostly ineffective check for buffer overrun.
if (::strlen (message) >= 65536)
throw std::string ("Data exceeds 65,536 bytes. Break data into smaller chunks.");
char buffer[65536];
va_list args;
va_start (args, message);
vsnprintf (buffer, 65536, message, args);
va_end (args);
logWrite (std::string (buffer));
}
////////////////////////////////////////////////////////////////////////////////