550 lines
14 KiB
C
550 lines
14 KiB
C
/* $Id: holiday.c,v 1.18 2000/01/12 23:22:53 akool Exp $
|
||
*
|
||
* Feiertagsberechnung
|
||
*
|
||
* Copyright 1999 by Michael Reinelt (reinelt@eunet.at)
|
||
*
|
||
* 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.
|
||
*
|
||
* $Log: holiday.c,v $
|
||
* Revision 1.18 2000/01/12 23:22:53 akool
|
||
* - isdnlog/tools/holiday.c ... returns ERVERYDAY for '*'
|
||
* - FAQ/configure{,.in} ... test '==' => '='
|
||
* - isdnlog/tools/dest/configure{,.in} ... test '==' => '='
|
||
* - isdnlog/tools/dest/Makefile.in ... test '==' => '='
|
||
* - isdnlog/tools/zone/configure{,.in} ... test '==' => '='
|
||
*
|
||
* - isdnlog/tools/rate-at.c ... P:1069
|
||
* - isdnlog/rate-at.dat ... P:1069
|
||
* - isdnlog/country-de.dat ... _DEMF
|
||
*
|
||
* - many new rates
|
||
* - more EURACOM sequences decoded
|
||
*
|
||
* Revision 1.17 1999/12/31 13:57:19 akool
|
||
* isdnlog-4.00 (Millenium-Edition)
|
||
* - Oracle support added by Jan Bolt (Jan.Bolt@t-online.de)
|
||
* - resolved *any* warnings against rate-de.dat
|
||
* - Many new rates
|
||
* - CREDITS file added
|
||
*
|
||
* Revision 1.16 1999/09/09 11:21:05 akool
|
||
* isdnlog-3.49
|
||
*
|
||
* Revision 1.15 1999/07/15 16:41:49 akool
|
||
* small enhancement's and fixes
|
||
*
|
||
* Revision 1.14 1999/06/22 19:41:19 akool
|
||
* zone-1.1 fixes
|
||
*
|
||
* Revision 1.13 1999/06/15 20:05:04 akool
|
||
* isdnlog Version 3.33
|
||
* - big step in using the new zone files
|
||
* - *This*is*not*a*production*ready*isdnlog*!!
|
||
* - Maybe the last release before the I4L meeting in Nuernberg
|
||
*
|
||
* Revision 1.12 1999/05/22 10:19:21 akool
|
||
* isdnlog Version 3.29
|
||
*
|
||
* - processing of "sonderrufnummern" much more faster
|
||
* - detection for sonderrufnummern of other provider's implemented
|
||
* (like 01929:FreeNet)
|
||
* - Patch from Oliver Lauer <Oliver.Lauer@coburg.baynet.de>
|
||
* - Patch from Markus Schoepflin <schoepflin@ginit.de>
|
||
* - easter computing corrected
|
||
* - rate-de.dat 1.02-Germany [22-May-1999 11:37:33] (from rate-CVS)
|
||
* - countries-de.dat 1.02-Germany [22-May-1999 11:37:47] (from rate-CVS)
|
||
* - new option "-B" added (see README)
|
||
* (using "isdnlog -B16 ..." isdnlog now works in the Netherlands!)
|
||
*
|
||
* Revision 1.11 1999/05/09 18:24:18 akool
|
||
* isdnlog Version 3.25
|
||
*
|
||
* - README: isdnconf: new features explained
|
||
* - rate-de.dat: many new rates from the I4L-Tarifdatenbank-Crew
|
||
* - added the ability to directly enter a country-name into "rate-xx.dat"
|
||
*
|
||
* Revision 1.10 1999/04/29 19:03:37 akool
|
||
* isdnlog Version 3.22
|
||
*
|
||
* - T-Online corrected
|
||
* - more online rates for rate-at.dat (Thanks to Leopold Toetsch <lt@toetsch.at>)
|
||
*
|
||
* Revision 1.9 1999/04/26 22:12:14 akool
|
||
* isdnlog Version 3.21
|
||
*
|
||
* - CVS headers added to the asn* files
|
||
* - repaired the "4.CI" message directly on CONNECT
|
||
* - HANGUP message extended (CI's and EH's shown)
|
||
* - reactivated the OVERLOAD message
|
||
* - rate-at.dat extended
|
||
* - fixes from Michael Reinelt
|
||
*
|
||
* Revision 1.8 1999/04/20 20:31:58 akool
|
||
* isdnlog Version 3.19
|
||
* patches from Michael Reinelt
|
||
*
|
||
* Revision 1.7 1999/04/19 19:25:07 akool
|
||
* isdnlog Version 3.18
|
||
*
|
||
* - countries-at.dat added
|
||
* - spelling corrections in "countries-de.dat" and "countries-us.dat"
|
||
* - LCR-function of isdnconf now accepts a duration (isdnconf -c .,duration)
|
||
* - "rate-at.dat" and "rate-de.dat" extended/fixed
|
||
* - holiday.c and rate.c fixed (many thanks to reinelt@eunet.at)
|
||
*
|
||
* Revision 1.6 1999/04/16 14:39:58 akool
|
||
* isdnlog Version 3.16
|
||
*
|
||
* - more syntax checks for "rate-xx.dat"
|
||
* - isdnrep fixed
|
||
*
|
||
* Revision 1.5 1999/04/14 13:17:15 akool
|
||
* isdnlog Version 3.14
|
||
*
|
||
* - "make install" now install's "rate-xx.dat", "rate.conf" and "ausland.dat"
|
||
* - "holiday-xx.dat" Version 1.1
|
||
* - many rate fixes (Thanks again to Michael Reinelt <reinelt@eunet.at>)
|
||
*
|
||
* Revision 1.4 1999/04/10 16:36:31 akool
|
||
* isdnlog Version 3.13
|
||
*
|
||
* WARNING: This is pre-ALPHA-dont-ever-use-Code!
|
||
* "tarif.dat" (aka "rate-xx.dat"): the next generation!
|
||
*
|
||
* You have to do the following to test this version:
|
||
* cp /usr/src/isdn4k-utils/isdnlog/holiday-de.dat /etc/isdn
|
||
* cp /usr/src/isdn4k-utils/isdnlog/rate-de.dat /usr/lib/isdn
|
||
* cp /usr/src/isdn4k-utils/isdnlog/samples/rate.conf.de /etc/isdn/rate.conf
|
||
*
|
||
* After that, add the following entries to your "/etc/isdn/isdn.conf" or
|
||
* "/etc/isdn/callerid.conf" file:
|
||
*
|
||
* [ISDNLOG]
|
||
* SPECIALNUMBERS = /usr/lib/isdn/sonderrufnummern.dat
|
||
* HOLIDAYS = /usr/lib/isdn/holiday-de.dat
|
||
* RATEFILE = /usr/lib/isdn/rate-de.dat
|
||
* RATECONF = /etc/isdn/rate.conf
|
||
*
|
||
* Please replace any "de" with your country code ("at", "ch", "nl")
|
||
*
|
||
* Good luck (Andreas Kool and Michael Reinelt)
|
||
*
|
||
* Revision 1.3 1999/03/24 19:38:53 akool
|
||
* - isdnlog Version 3.10
|
||
* - moved "sondernnummern.c" from isdnlog/ to tools/
|
||
* - "holiday.c" and "rate.c" integrated
|
||
* - NetCologne rates from Oliver Flimm <flimm@ph-cip.uni-koeln.de>
|
||
* - corrected UUnet and T-Online rates
|
||
*
|
||
* Revision 1.2 1999/03/16 17:38:03 akool
|
||
* - isdnlog Version 3.07
|
||
* - Michael Reinelt's patch as of 16Mar99 06:58:58
|
||
* - fix a fix from yesterday with sondernummern
|
||
* - ignore "" COLP/CLIP messages
|
||
* - dont show a LCR-Hint, if same price
|
||
*
|
||
* Revision 1.1 1999/03/14 12:16:23 akool
|
||
* - isdnlog Version 3.04
|
||
* - general cleanup
|
||
* - new layout for "rate-xx.dat" and "holiday-xx.dat" files from
|
||
* Michael Reinelt <reinelt@eunet.at>
|
||
* unused by now - it's a work-in-progress !
|
||
* - bugfix for Wolfgang Siefert <siefert@wiwi.uni-frankfurt.de>
|
||
* The Agfeo AS 40 (Software release 2.1b) uses AOC_AMOUNT, not AOC_UNITS
|
||
* - bugfix for Ralf G. R. Bergs <rabe@RWTH-Aachen.DE> - 0800/xxx numbers
|
||
* are free of charge ;-)
|
||
* - tarif.dat V 1.08 - new mobil-rates DTAG
|
||
*
|
||
*/
|
||
|
||
/*
|
||
* Schnittstelle:
|
||
*
|
||
* int initHoliday(char *path, char **msg)
|
||
* initialisiert die Feiertagsberechnung, liest die Feiertagsdatei
|
||
* und gibt die Anzahl der Feiertage zur<75>ck, im Fehlerfall -1
|
||
*
|
||
* void exitHoliday()
|
||
* deinitialisiert die Feiertagsberechnung
|
||
*
|
||
* int isDay(struct tm *tm, bitfield mask, char **name)
|
||
* pr<70>ft, ob <tm> ein Tag aus <mask> ist, und liefert
|
||
* eine Beschreibung in <*name>
|
||
*/
|
||
|
||
#define _HOLIDAY_C_
|
||
|
||
#ifdef STANDALONE
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <ctype.h>
|
||
#include <stdarg.h>
|
||
#include <time.h>
|
||
#include <unistd.h>
|
||
#include <errno.h>
|
||
#ifndef __GLIBC__
|
||
extern const char *basename (const char *name);
|
||
#endif
|
||
#else
|
||
#include "isdnlog.h"
|
||
#include "tools.h"
|
||
#endif
|
||
|
||
#include "holiday.h"
|
||
|
||
#define SOMEDAY (1<<MONDAY | 1<<TUESDAY | 1<<WEDNESDAY | 1<<THURSDAY | 1<<FRIDAY | 1<<SATURDAY | 1<<SUNDAY)
|
||
#define LENGTH 120 /* max length of lines in data file */
|
||
#define COUNT(array) sizeof(array)/sizeof(array[0])
|
||
|
||
typedef unsigned int julian;
|
||
|
||
typedef struct {
|
||
int day;
|
||
int month;
|
||
char *name;
|
||
} HOLIDATE;
|
||
|
||
static char *defaultWeekday[] = { "", /* not used */
|
||
"Everyday",
|
||
"Workday",
|
||
"Weekend",
|
||
"Monday",
|
||
"Tuesday",
|
||
"Wednesday",
|
||
"Thursday",
|
||
"Friday",
|
||
"Saturday",
|
||
"Sunday",
|
||
"Holiday" };
|
||
|
||
static int line = 0;
|
||
static char *Weekday[COUNT(defaultWeekday)] = { NULL, };
|
||
static int nHoliday = 0;
|
||
static HOLIDATE *Holiday = NULL;
|
||
|
||
static void warning (char *file, char *fmt, ...)
|
||
{
|
||
va_list ap;
|
||
char msg[BUFSIZ];
|
||
|
||
va_start (ap, fmt);
|
||
vsnprintf (msg, BUFSIZ, fmt, ap);
|
||
va_end (ap);
|
||
#ifdef STANDALONE
|
||
fprintf(stderr, "WARNING: %s line %3d: %s\n", basename(file), line, msg);
|
||
#else
|
||
print_msg(PRT_NORMAL, "WARNING: %s line %3d: %s\n", basename(file), line, msg);
|
||
#endif
|
||
}
|
||
|
||
/* easter & julian calculations by Guenther Brunthaler (gbr001@yahoo.com) */
|
||
|
||
static julian date2julian(int y, int m, int d)
|
||
{
|
||
if (m<3) {m+=9; y--;} else m-=3;
|
||
return (146097*(y/100))/4+(1461*(y%100))/4+(153*m+2)/5+d;
|
||
}
|
||
|
||
#if 0 /* not used by now */
|
||
static void julian2date(julian jd, int *yp, int *mp, int *dp)
|
||
{
|
||
julian j,c,y,m,d;
|
||
|
||
j=jd*4-1;
|
||
c=(j/146097)*100;
|
||
d=(j%146097)/4;
|
||
y=(4*d+3)/1461;
|
||
d=(((4*d+3)%1461)+4)/4;
|
||
m=(5*d-3)/153;
|
||
d=(((5*d-3)%153)+5)/5;
|
||
|
||
if (m>9) {m-=9; y++;} else m+=3;
|
||
|
||
*yp=c+y;
|
||
*mp=m;
|
||
*dp=d;
|
||
}
|
||
#endif
|
||
|
||
static julian getEaster(int year)
|
||
{
|
||
int g, c, x, z, d, e, n, m;
|
||
|
||
g=year%19+1;
|
||
c=year/100+1;
|
||
x=3*c/4-12;
|
||
z=(8*c+5)/25-5;
|
||
d=5*year/4-x-10;
|
||
e=(11*g+20+z-x)%30;
|
||
if ((e==25 && g>11) || e==24) e++;
|
||
n=44-e;
|
||
if (n<21) n+=30;
|
||
n+=7-(d+n)%7;
|
||
if (n>31) {n-=31; m=4;} else m=3;
|
||
return date2julian(year,m,n);
|
||
}
|
||
|
||
static char *strip (char *s)
|
||
{
|
||
char *p;
|
||
|
||
while (isblank(*s)) s++;
|
||
for (p=s; *p; p++)
|
||
if (*p=='#' || *p=='\n') {
|
||
*p='\0';
|
||
break;
|
||
}
|
||
for (p--; p>s && isblank(*p); p--)
|
||
*p='\0';
|
||
return s;
|
||
}
|
||
|
||
void exitHoliday(void)
|
||
{
|
||
int i;
|
||
|
||
for (i=0; i<COUNT(Weekday); i++) {
|
||
if (Weekday[i]) {
|
||
free (Weekday[i]);
|
||
Weekday[i]=NULL;
|
||
}
|
||
}
|
||
for (i=0; i<nHoliday; i++) {
|
||
if (Holiday[i].name) free (Holiday[i].name);
|
||
}
|
||
if (Holiday) free (Holiday);
|
||
nHoliday=0;
|
||
Holiday=NULL;
|
||
}
|
||
|
||
int initHoliday(char *path, char **msg)
|
||
{
|
||
FILE *stream;
|
||
int i,m,d;
|
||
char *s, *date, *name;
|
||
char buffer[LENGTH];
|
||
char version[LENGTH]="<unknown>";
|
||
static char message[LENGTH];
|
||
|
||
if (msg)
|
||
*(*msg=message)='\0';
|
||
|
||
exitHoliday();
|
||
|
||
for (i=0; i<COUNT(Weekday); i++)
|
||
Weekday[i]=strdup(defaultWeekday[i]);
|
||
|
||
if (!path || !*path) {
|
||
if (msg) snprintf (message, LENGTH, "Warning: no holiday database specified!");
|
||
return 0;
|
||
}
|
||
|
||
if ((stream=fopen(path,"r"))==NULL) {
|
||
if (msg) snprintf (message, LENGTH, "Error: could not load holidays from %s: %s",
|
||
path, strerror(errno));
|
||
return -1;
|
||
}
|
||
|
||
line=0;
|
||
while ((s=fgets(buffer,LENGTH,stream))!=NULL) {
|
||
line++;
|
||
if (*(s=strip(s))=='\0')
|
||
continue;
|
||
if (s[1]!=':') {
|
||
warning (path, "expected ':', got '%s'!", s+1);;
|
||
continue;
|
||
}
|
||
switch (*s) {
|
||
case 'W':
|
||
s+=2; while (isblank(*s)) s++;
|
||
if (isdigit(*s)) {
|
||
d=strtol(s,&s,10);
|
||
if (d<1 || d>7) {
|
||
warning(path, "invalid weekday %d", d);
|
||
continue;
|
||
}
|
||
d+=MONDAY-1;
|
||
} else if (*s=='W') {
|
||
d=WORKDAY;
|
||
s++;
|
||
} else if (*s=='E') {
|
||
d=WEEKEND;
|
||
s++;
|
||
} else if (*s=='H') {
|
||
d=HOLIDAY;
|
||
s++;
|
||
} else if (*s=='*') {
|
||
d=EVERYDAY;
|
||
s++;
|
||
} else {
|
||
warning(path, "invalid weekday %c", *s);
|
||
continue;
|
||
}
|
||
if (!isblank(*s)) {
|
||
warning ("expected whitespace, got '%s'!", s);;
|
||
continue;
|
||
}
|
||
if (*(name=strip(s))=='\0') {
|
||
warning(path, "empty weekday %d", d);
|
||
continue;
|
||
}
|
||
if (Weekday[d]) free(Weekday[d]);
|
||
Weekday[d]=strdup(name);
|
||
break;
|
||
|
||
case 'D':
|
||
name=s+2;
|
||
if ((date=strsep(&name," \t"))==NULL) {
|
||
warning(path, "Syntax error");
|
||
continue;
|
||
}
|
||
if (strncmp(date,"easter",6)==0) {
|
||
m=-1;
|
||
d=atoi(date+6);
|
||
} else {
|
||
d=atof(strsep(&date,"."));
|
||
m=atof(strsep(&date,"."));
|
||
}
|
||
|
||
Holiday=(HOLIDATE*)realloc(Holiday,(nHoliday+1)*sizeof(HOLIDATE));
|
||
Holiday[nHoliday].day=d;
|
||
Holiday[nHoliday].month=m;
|
||
Holiday[nHoliday].name=strdup(strip(name));
|
||
nHoliday++;
|
||
break;
|
||
|
||
case 'V': /* V:xxx Version der Datenbank */
|
||
s+=2; while(isblank(*s)) s++;
|
||
strcpy(version, s);
|
||
break;
|
||
|
||
default:
|
||
warning(path, "Unknown tag '%c'", *s);
|
||
}
|
||
}
|
||
fclose(stream);
|
||
|
||
if (msg) snprintf (message, LENGTH, "Holiday Version %s loaded [%d entries from %s]",
|
||
version, nHoliday, path);
|
||
|
||
return nHoliday;
|
||
}
|
||
|
||
static int isHoliday(struct tm *tm, char **name)
|
||
{
|
||
int i;
|
||
julian easter, day;
|
||
|
||
day=date2julian(tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday);
|
||
easter=getEaster(tm->tm_year+1900);
|
||
|
||
for (i=0; i<nHoliday; i++) {
|
||
if ((Holiday[i].month==-1 && Holiday[i].day==day-easter) ||
|
||
(Holiday[i].month==tm->tm_mon+1 && Holiday[i].day==tm->tm_mday)) {
|
||
if(name) *name=Holiday[i].name;
|
||
return 1;
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
static char *staticString (char *fmt, ...)
|
||
{
|
||
va_list ap;
|
||
char buffer[BUFSIZ];
|
||
int i;
|
||
|
||
static char **Table=NULL;
|
||
static int Size=0;
|
||
|
||
va_start (ap, fmt);
|
||
vsnprintf (buffer, BUFSIZ, fmt, ap);
|
||
va_end (ap);
|
||
|
||
for (i=0; i<Size; i++)
|
||
if (strcmp (buffer, Table[i])==0)
|
||
return Table[i];
|
||
|
||
Size++;
|
||
Table=realloc(Table, Size*sizeof(char*));
|
||
Table[Size-1]=strdup(buffer);
|
||
|
||
return Table[Size-1];
|
||
}
|
||
|
||
int isDay(struct tm *tm, bitfield mask, char **name)
|
||
{
|
||
char *holiname;
|
||
|
||
julian day=(date2julian(tm->tm_year+1900,tm->tm_mon+1,tm->tm_mday)-6)%7+MONDAY;
|
||
|
||
if ((mask & (1<<HOLIDAY)) && isHoliday(tm, &holiname)) {
|
||
if (name) *name=staticString("%s (%s)", Weekday[HOLIDAY], holiname);
|
||
return HOLIDAY;
|
||
}
|
||
|
||
if ((mask & (1<<WEEKEND)) && (day==SATURDAY || day==SUNDAY)) {
|
||
if (name) *name=staticString("%s (%s)", Weekday[WEEKEND], Weekday[day]);
|
||
return WEEKEND;
|
||
}
|
||
|
||
if ((mask & (1<<WORKDAY)) && day!=SATURDAY && day!=SUNDAY && !isHoliday(tm, NULL)) {
|
||
if (name) *name=staticString("%s (%s)", Weekday[WORKDAY], Weekday[day]);
|
||
return WORKDAY;
|
||
}
|
||
|
||
if (mask & (1<<day)) {
|
||
if (name) *name=staticString("%s", Weekday[day]);
|
||
return day;
|
||
}
|
||
|
||
if (mask & (1<<EVERYDAY)) {
|
||
if (name) *name=staticString("%s", Weekday[EVERYDAY]);
|
||
return EVERYDAY;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
#ifdef HOLITEST
|
||
void main (int argc, char *argv[])
|
||
{
|
||
int i, d;
|
||
char *msg, *name;
|
||
struct tm tm;
|
||
|
||
|
||
initHoliday("../holiday-de.dat", &msg);
|
||
printf ("%s\n", msg);
|
||
|
||
for (i=1; i < argc; i++) {
|
||
tm.tm_mday=atoi(strsep(argv+i,"."));
|
||
tm.tm_mon=atoi(strsep(argv+i,"."))-1;
|
||
tm.tm_year=atoi(strsep(argv+i,"."))-1900;
|
||
|
||
d=isDay(&tm,1<<HOLIDAY,&name);
|
||
printf ("%02d.%02d.%04d\t%2d = %s\n", tm.tm_mday,tm.tm_mon+1,tm.tm_year+1900,d,d?name:"no Holiday");
|
||
d=isDay(&tm,1<<WEEKEND,&name);
|
||
printf ("%02d.%02d.%04d\t%2d = %s\n", tm.tm_mday,tm.tm_mon+1,tm.tm_year+1900,d,d?name:"no Weekend");
|
||
d=isDay(&tm,1<<WORKDAY,&name);
|
||
printf ("%02d.%02d.%04d\t%2d = %s\n", tm.tm_mday,tm.tm_mon+1,tm.tm_year+1900,d,d?name:"no Workday");
|
||
d=isDay(&tm,SOMEDAY,&name);
|
||
printf ("%02d.%02d.%04d\t%2d = %s\n", tm.tm_mday,tm.tm_mon+1,tm.tm_year+1900,d,d?name:"no Day (Uh?)");
|
||
}
|
||
}
|
||
#endif
|