/* $Id: opt_time.c,v 1.2 2005/01/22 18:47:16 tobiasb Exp $
 *
 * ISDN accounting for isdn4linux. (Report-module)
 * Time options parsing.  (Seperated from isdnrep.c 1.96)
 *
 * Copyright 1995 .. 2000 by Andreas Kool (akool@isdn4linux.de)
 *                     and Stefan Luethje (luethje@sl-gw.lake.de)
 *
 * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Copyright 1994, Steven Grimm <koreth@hyperion.com>.
 *
 *
 * $Log: opt_time.c,v $
 * Revision 1.2  2005/01/22 18:47:16  tobiasb
 * New: "isdnrep -t y" for calls of yesterday.
 * Added some more verifications in the processing of isdnrep's  -t option.
 *
 * Revision 1.1  2003/10/29 17:41:35  tobiasb
 * isdnlog-4.67:
 *  - Enhancements for isdnrep:
 *    - New option -r for recomputing the connection fees with the rates
 *      from the current (and for a different or the cheapest provider).
 *    - Revised output format of summaries at end of report.
 *    - New format parameters %j, %v, and %V.
 *    - 2 new input formats for -t option.
 *  - Fix for dualmode workaround 0x100 to ensure that incoming calls
 *    will not become outgoing calls if a CALL_PROCEEDING message with
 *    an B channel confirmation is sent by a terminal prior to CONNECT.
 *  - Fixed and enhanced t: Tag handling in pp_rate.
 *  - Fixed typo in interface description of tools/rate.c
 *  - Fixed typo in tools/isdnrate.man, found by Paul Slootman.
 *  - Minor update to sample isdn.conf files:
 *    - Default isdnrep format shows numbers with 16 chars (+ & 15 digits).
 *    - New isdnrep format (-FNIO) without display of transfered bytes.
 *    - EUR as currency in Austria, may clash with outdated rate-at.dat.
 *      The number left of the currency symbol is nowadays insignificant.
 *  - Changes checked in earlier but after step to isdnlog-4.66:
 *    - New option for isdnrate: `-rvNN' requires a vbn starting with NN.
 *    - Do not compute the zone with empty strings (areacodes) as input.
 *    - New ratefile tags r: und t: which need an enhanced pp_rate.
 *      For a tag description see rate-files(5).
 *    - Some new and a few updated international cellphone destinations.
 *
 * NOTE: If there any questions, problems, or problems regarding isdnlog,
 *    feel free to join the isdn4linux mailinglist, see
 *    https://www.isdn4linux.de/mailman/listinfo/isdn4linux for details,
 *    or send a mail in English or German to <tobiasb@isdn4linux.de>.
 *
 */


#define _OPT_TIME_C_

/* allow standalone binary for testing */
/* build with 
   gcc -g -DTEST_OPT_TIME -o opt_time opt_time.c 
 */
#ifdef TEST_OPT_TIME
#include <stdio.h>
#include <string.h>
#include <time.h>
#define UNKNOWN (-1)
#define min(x,y) ((x) < (y) ? (x) : (y))
char *Strncpy(char *dest, const char *src, int len);
#else

#include "isdnrep.h"

#endif

#define END_TIME    1
#define S_YESTERDAY "y" /* character(s) for the yesterday time specification */
			
/*****************************************************************************/

static time_t get_month(char *String, int TimeStatus);
static time_t get_time(char *String, int TimeStatus);
static time_t get_gertime(char *s, int TimeStatus);
static time_t get_ytime(time_t *base, int back, int TimeStatus);
static time_t get_itime(char *s, int TimeStatus);

/*****************************************************************************/
/* parse time option, a return value of 0 means failure. */
int get_term (char *String, time_t *Begin, time_t *End,int delentries)
{
	time_t now;
	time_t  Date[2];
	int Cnt;
	char DateStr[2][256] = {"",""};
	int idate = 0;
	char sep[3] = { '-', 0, 0 };
	int sep_l = 1;
	char *p;

	time(&now);

	if (tolower(String[0])=='i' || strstr(String,"--")) {
		String += tolower(String[0])=='i';
		sep[1] = sep[0];
		sep_l++;
		idate++;
	}	

	if ( (p=strstr(String, sep)) )
	{
		if (String != p)                                    /* ...<sep> */ 
			Strncpy(DateStr[0], String, min(p-String+1,256));
		if (*(p+sep_l))                                     /* <sep>... */
			Strncpy(DateStr[1], p+sep_l, 256);
	}
	else
	{
		Strncpy(DateStr[0], String, 256);
		Strncpy(DateStr[1], String, 256);
	}

	for (Cnt = 0; Cnt < 2; Cnt++)
	{
		int DateLen = strlen(DateStr[Cnt]);

		if (strspn(DateStr[Cnt], S_YESTERDAY) == DateLen)
			Date[Cnt] = get_ytime(&now, DateLen, delentries?0:Cnt);
		else if ( strspn(DateStr[Cnt], idate ? "01234567890T:-" : "01234567890/.")
				     != DateLen )
			return 0;		
		else if (idate)
			Date[Cnt] = get_itime(DateStr[Cnt],delentries?0:Cnt);
		else if ( (p=strchr(DateStr[Cnt],'.')) && strchr(p+1,'.') )
			Date[Cnt] = get_gertime(DateStr[Cnt],delentries?0:Cnt);
		else if (strchr(DateStr[Cnt],'/'))
			Date[Cnt] = get_month(DateStr[Cnt],delentries?0:Cnt);
		else
			Date[Cnt] = get_time(DateStr[Cnt],delentries?0:Cnt);
	}

	*Begin = DateStr[0][0] == '\0' ? 0 : Date[0];
	*End = DateStr[1][0] == '\0' || !Date[1] ? now : Date[1];

	if (*Begin > *End)
		return 0;

	return 1;
}

/*****************************************************************************/
/* parse a date like [DD/][M]M/[[YY]YY] */
static time_t get_month(char *String, int TimeStatus)
{
	time_t now;
	int Cnt = 0;
	struct tm *TimeStruct;
	int Args[3];
	int ArgCnt;


	time(&now);
	if ( !(TimeStruct = localtime(&now)) )
		return 0;
	TimeStruct->tm_sec = 0;
	TimeStruct->tm_min = 0;
	TimeStruct->tm_hour= 0;
	TimeStruct->tm_mday= 1;
	TimeStruct->tm_isdst= UNKNOWN;

	ArgCnt = sscanf(String,"%d/%d/%d",&(Args[0]),&(Args[1]),&(Args[2]));

	switch (ArgCnt)
	{
		case 3:
			TimeStruct->tm_mday = Args[0];
			Cnt++;
		case 2:
			/* if (Args[Cnt+1] > 99) */
			if (Args[Cnt+1] >= 1900)
				TimeStruct->tm_year = ((Args[Cnt+1] / 100) - 19) * 100 + (Args[Cnt+1]%100);
			else
				if (Args[Cnt+1] < 70)
					TimeStruct->tm_year = Args[Cnt+1] + 100;
				else
					TimeStruct->tm_year = Args[Cnt+1];
		case 1:
			TimeStruct->tm_mon = Args[Cnt];
			break;
		default:
			return 0;
	}

	if (TimeStatus == END_TIME)
	{
		if (ArgCnt == 3)	/* Wenn Tag angegeben ist */
		{
			TimeStruct->tm_mday++;
			TimeStruct->tm_mon--;
		}
	}
	else
		TimeStruct->tm_mon--;

	return mktime(TimeStruct);
}

/*****************************************************************************/
/* parse a date like [MM]DD[hhmm[[CC]YY][.ss]] */
static time_t get_time(char *String, int TimeStatus)
{
	time_t now;
	int Len = 0;
	int Year;
	char *Ptr;
	struct tm *TimeStruct;


	time(&now);
	if ( !(TimeStruct = localtime(&now)) )
		return 0;
	TimeStruct->tm_sec = 0;
	TimeStruct->tm_min = 0;
	TimeStruct->tm_hour= 0;
	TimeStruct->tm_isdst= UNKNOWN;

	switch (strlen(String))
	{
		case 0:
						return 0;
		case 1:
		case 2:
						TimeStruct->tm_mday = atoi(String);
						break;
		default:
						Len = strlen(String);

			if ((Ptr = strchr(String,'.')) != NULL)
			{
				TimeStruct->tm_sec = atoi(Ptr+1);
				Len -= strlen(Ptr);
			}

			if (Len % 2)
				return 0;

			if (sscanf(String,"%2d%2d%2d%2d%d",
					&(TimeStruct->tm_mon),
					&(TimeStruct->tm_mday),
					&(TimeStruct->tm_hour),
					&(TimeStruct->tm_min),
					&Year		) 		> 4) {
				/* if (Year > 99) */
	if (Year >= 1900)
					TimeStruct->tm_year = ((Year / 100) - 19) * 100 + (Year%100);
				else {
		if (Year < 70)
			TimeStruct->tm_year = Year + 100;
		else
			TimeStruct->tm_year = Year;
				}
			}
			TimeStruct->tm_mon--;
			break;
	}

	if (TimeStatus == END_TIME) {
		if (TimeStruct->tm_sec == 0 &&
				TimeStruct->tm_min == 0 &&
				TimeStruct->tm_hour== 0   )
			TimeStruct->tm_mday++;
		else {
			if (TimeStruct->tm_sec == 0 &&
				TimeStruct->tm_min == 0   )
				TimeStruct->tm_hour++;
			else {
				if (TimeStruct->tm_sec == 0   )
					TimeStruct->tm_min++;
				else
					TimeStruct->tm_sec++;
			}
		}
	}
	return mktime(TimeStruct);
}

/*****************************************************************************/
/* parse a date like [D]D.[M]M.[[CC][Y]Y] */
static time_t get_gertime(char *s, int TimeStatus)
{
	/* convert to CCYY-MM-DD and give it to get_itime */
	char t[10+1], *sm, *sy;
	int day, month, year;
	if (strlen(s)>10 || strlen(s)<4)
		return 0;
	sm = strchr(s,'.') + 1;
	sy = strchr(sm,'.') + 1;

	if ( *sy )
		year = atoi(sy);                     /* (century and) year from input */
	else {                                 /* get current year */
		time_t now;
		struct tm *tm;
		time(&now);
		if ( !(tm = localtime(&now)) )
			return 0;
		year = tm->tm_year+1900;
	}

	month = atoi(sm);
	day = atoi(s);
	if (year>9999 || month>12 || day>31)
		return 0;

	/* expansion of two digit year value takes place in get_itime */
	sprintf(t, "%02d-%02d-%02d", year, month, day);

	return get_itime(t, TimeStatus);
}

/*****************************************************************************/
/* parse a date like y[y[y...]] where each y(esterday) goes back one day. */
static time_t get_ytime(time_t *base, int back, int TimeStatus) {
	struct tm *tm;
	time_t yesterday = *base - 24*60*60 * back;
	char t[10+1];

	tm = localtime(&yesterday);	
	if (tm == NULL || tm->tm_year > 9999 - 1900 || tm->tm_mon > 12 - 1
	    || tm->tm_mday > 31)
		return 0;

	sprintf(t, "%04d-%02d-%02d", 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday);
	return get_itime(t, TimeStatus);
}

/*****************************************************************************/
/* parse a date like CCYY[-]MM[-]DD["T"]hh[:]mm[:]ss */
static time_t get_itime(char *s, int TimeStatus)
{
	time_t now, offset=0;
	struct tm *t;
	char *p;
	char a[5];
	int i[6];
	int n;

	time(&now);
	if ( !(t = localtime(&now)) )
		return 0;
	t->tm_isdst = UNKNOWN;
	
	p = s;
	if (strlen(s)<4)                       /* at least CCYY or YY-MM */
		return 0;
	
	p = s;
	if ( !( isdigit(p[0]) && isdigit(p[1]) ) )
		return 0;
	if (p[2] == '-') {                     /* YY- */
		Strncpy(a, p, 2+1);
		i[0] = atoi(a);
		i[0] += ((t->tm_year+1900)/100)*100; /* add current century */
		p += 2;
	} 
	else {                                 /* CCYY */
		if ( !( isdigit(p[2]) && isdigit(p[3]) ) )
			return 0;
		Strncpy(a, p, 4+1);
		i[0] = atoi(a);
		p += 4;
	}

	for (n=1; n<6; n++)                    /*  MM DD hh mm ss */
	{                                      /* 1  2  3  4  5   */
		if (*p == ( n<3 ? '-' : (n<4 ? 'T' : ':') ) )
			p++;
		if (! *p)
			break;
		if ( !( isdigit(p[0]) && isdigit(p[1]) ) )
			return 0;
		Strncpy(a, p, 2+1);
		i[n] = atoi(a);
		p += 2;
	}
 
	t->tm_year = i[0] - 1900;

	if (n==2 && TimeStatus==END_TIME && i[1] == 12)
		n = 1;                             /* --2003-12 => --2003 */

	if (n==1 && TimeStatus==END_TIME)    /* --2003 => --2003-12-31T23:59:59 */
	{
		TimeStatus = 0;                    /* 1) => --2003-01-01T00:00:00 */
		t->tm_year++;                      /* 2) => --2004-01-01T00:00:00 */
		offset = -1;                       /* 3) => --2003-12-31T23:59:59 */
	}

	t->tm_mon  = (n>1) ? i[1]-1 : 0;

	if (n==2 && TimeStatus==END_TIME)    /* --2000-02 => --2000-02-29T23:59:59 */
	{
		TimeStatus = 0;                    /* 1) => --2000-02-01T00:00:00 */
		t->tm_mon++;                       /* 2) => --2000-03-01T00:00:00 */
		offset = -1;                       /* 3) => --2000-02-29T23:59:59 */
	}

	t->tm_mday = (n>2) ? i[2] : 1;

	t->tm_hour = (n>3) ? i[3] : ( (TimeStatus==END_TIME) ? 23 : 0);
	t->tm_min  = (n>4) ? i[4] : ( (TimeStatus==END_TIME) ? 59 : 0);
	t->tm_sec  = (n>5) ? i[5] : ( (TimeStatus==END_TIME) ? 59 : 0);

	return mktime(t)+offset;
}	

/*****************************************************************************/

#ifdef TEST_OPT_TIME

/* taken from ../../lib/libtools.c */
char *Strncpy(char *dest, const char *src, int len)
{
	int l = strlen(src);
	if (l > len - 1)
		l = len - 1;
	strncpy(dest,src,l);
	dest[l] = '\0';
	return dest;
}

#define STIME(z) a=localtime(&z); strftime(s, BUFSIZ, "%Y-%m-%d %H:%M:%S", a);

int main(int argc, char *argv[], char *envp[])
{
	int delentries = 0, res;
	time_t begintime, endtime, now;
	struct tm *a;
	char s[BUFSIZ];

	time(&now);
	STIME(now)
	printf("Testing get_term (parses isdnrep's -t option)\n"
	       "Enter . to quit, d to switch delentries.\n"
	       "Any other input will be parsed as option value\n"
	       "Current time: %s\n", s);
	while(1)
	{
		fgets(s, BUFSIZ, stdin);
		s[strlen(s)-1]=0;

		if (s[0] == '.' && s[1] == 0)
			break;
		else if (s[0] == 'd' && s[1] == 0) {
			delentries = !delentries;
			continue;
		}
		res = get_term (s, &begintime, &endtime, delentries);
		if (res) {
			STIME(begintime)
			printf("OK: %s -- ", s);
			STIME(endtime)
			printf("%s  (delentries=%d)\n", s, delentries);
		}
		else
			printf("ERR\n");
	}
	return 0;
}
#undef STIME
#endif

/*****************************************************************************/

/* vim:set ts=2: */

/*****************************************************************************/
