/*
 * log.c
 * Logging that supports both client and server.
 *
 * Copyright (C) Leni Mayo 2001-2002
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License, version 2, as
 *	published by the Free Software Foundation.
 *	
 *	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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <string.h> /* for strcat   */
#include <stdarg.h> /* for va_args  */
#include <syslog.h>
#include <errno.h>
#include "common.h"
#include "log.h"

static LogContext context;
static LogLevel   loglevel;
static int        fUsingSyslog;
static FILE       *fpLogFile;

void logInitialise(const char *argv0, LogContext x, int debuglevel, const char *pszLogName, LogOpenMode mode)
{
	int facility = LOG_DAEMON;
	const char *pszFileName = NULL;

	context = x;

	switch (debuglevel)
	{
		case 0:  loglevel = eLogLevelNone;      break;
		case 1:  loglevel = eLogLevelDefault;   break;
		case 2:  loglevel = eLogLevelRdf;       break;
		case 3:  loglevel = eLogLevelClient;    break;
		case 4:  loglevel = eLogLevelDeveloper; break;
		case 5:  loglevel = eLogLevelProfiling; break;
		default: loglevel = eLogLevelDefault;   break;
	}

	if (loglevel != eLogLevelNone && pszLogName)
	{
		/* use these short variable names so as to fit on one 80 col line
		*/
		const char *p = pszLogName;
		int f = LOG_DAEMON;  /* the facility - see man 3 syslog  */
		int m = TRUE;        /* TRUE iff there was a match       */

	   	     if (strcmp(p, "LOG_DAEMON") == 0) { f = LOG_DAEMON; m = TRUE; }
		else if (strcmp(p, "LOG_LOCAL0") == 0) { f = LOG_LOCAL0; m = TRUE; }
		else if (strcmp(p, "LOG_LOCAL1") == 0) { f = LOG_LOCAL1; m = TRUE; }
		else if (strcmp(p, "LOG_LOCAL2") == 0) { f = LOG_LOCAL2; m = TRUE; }
		else if (strcmp(p, "LOG_LOCAL3") == 0) { f = LOG_LOCAL3; m = TRUE; }
		else if (strcmp(p, "LOG_LOCAL4") == 0) { f = LOG_LOCAL4; m = TRUE; }
		else if (strcmp(p, "LOG_LOCAL5") == 0) { f = LOG_LOCAL5; m = TRUE; }
		else if (strcmp(p, "LOG_LOCAL6") == 0) { f = LOG_LOCAL6; m = TRUE; }
		else if (strcmp(p, "LOG_LOCAL7") == 0) { f = LOG_LOCAL7; m = TRUE; }
		else m = FALSE;

		facility = f;
		fUsingSyslog = m;
	}

	if (loglevel != eLogLevelNone && !fUsingSyslog)
	{
		if (pszLogName && strlen(pszLogName) != 0)
		{
			pszFileName = pszLogName;
		}
		else
		{
			pszFileName = NULL;
		}
	}

	if (loglevel != eLogLevelNone && fUsingSyslog)
	{
		/*
		 * facility == LOG_DAEMON (default)
		 * priority == LOG_WARNING
		 * option   == LOG_CONS means log to the console if syslog failed
		 * 
		 *
		 * The logging is so verbose that you can't really mix the
		 * syslog output with anything else, so prefixing
		 * the output with argv0 is a waste of space and buggers up the 
		 * nice formatting.
		 *
		 * openlog(argv0, LOG_CONS, facility);
		 */

		openlog("", LOG_CONS, facility);
	}

	if (loglevel != eLogLevelNone && pszFileName)
	{
		char *pszMode;

		if (mode == eLogOpenModeAppend)
			pszMode = "a";
		else if (mode == eLogOpenModeTruncate)
			pszMode = "w+";
		else
			assert(NOTHERE);

		fpLogFile = fopen(pszFileName, pszMode);

		if (fpLogFile == NULL)
		{
			fprintf(stderr, "Unable to open logfile \"%s\" - %s", pszFileName, strerror(errno));
		}
	}
}

void logCleanup()
{
	if (fUsingSyslog)
	{
		closelog();
	}

	if (fpLogFile != NULL)
	{
		fclose(fpLogFile);
	}
}

void printLog(LogLevel level, const char *fmt, ...)
{
	va_list ap;
	char buf[4096];

	if ( (
		  level == eLogLevelRdf ||
	      level == eLogLevelClient ||
	      level == eLogLevelDeveloper ||
	      level == eLogLevelProfiling
		  ) &&
	    level > loglevel)
	{
		return;
	}

	va_start(ap, fmt); 
	vsnprintf(buf, sizeof(buf), fmt, ap);

	if (fUsingSyslog)
	{
		/*
		 * syslog() turns newlines into spaces so a buf containing newlines
		 * quickly becomes unreadable.  Instead, we make multiple calls
		 * to syslog().
		 */
		char *p, *start;

		start = buf;

		while (start != NULL)
		{
			p = strchr(start, '\n');

			if (p != NULL)
			{
				*p = '\0';
			}

			if (strlen(start) > 0)
			{
				syslog(LOG_WARNING, start);
			}

			if (p == NULL)
			{
				start = NULL;
			}
			else
			{
				start = p + 1;
			}
		}
	}
	else
	{
		FILE *fp = NULL;

		if (fpLogFile != NULL)
		{
			fp = fpLogFile;
		}
		else if (context != eLogContextDaemon)
		{
			switch(level)
			{
				case eLogLevelDefault:       fp = stdout; break;
				case eLogLevelClient:        fp = stdout; break;
				case eLogLevelDeveloper:     fp = stdout; break;
				case eLogLevelRdf:           fp = stdout; break;
				case eLogLevelProfiling:     fp = stdout; break;
				case eLogLevelInternalError: fp = stderr; break;
				case eLogLevelError:         fp = stderr; break;
				default:                    
					assert(NOTHERE);
			}
		}

		if (fp != NULL)
		{
			vfprintf(fp, fmt, ap);
			fflush(fp);
		}
	}
}

LogLevel logLevel()
{
	return loglevel;
}
