/* $Id: isdnrep.c,v 1.104 2007/01/05 04:23:58 tobiasb Exp $
 *
 * ISDN accounting for isdn4linux. (Report-module)
 *
 * 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.
 *
 * functions htoi() and url_unescape():
 * Copyright 1994, Steven Grimm <koreth@hyperion.com>.
 *
 *
 * $Log: isdnrep.c,v $
 * Revision 1.104  2007/01/05 04:23:58  tobiasb
 * Made isdnrep and isdnrate buildable under cygwin. See ChangeLog for details.
 *
 * Revision 1.103  2006/07/03 15:51:27  keil
 * - HZ is not used anymore, this value is defined as duration in
 *   1/100 seconds independ from system HZ value
 *
 * Revision 1.102  2004/12/16 21:30:50  tobiasb
 * New option -U: default source number for outgoing calls.
 *
 * Revision 1.101  2004/07/25 14:21:13  tobiasb
 * New isdnrep option -m [*|/]number.  It multiplies or divide the cost of
 * each call by the given number.  `-m/1.16' for example displays the costs
 * without the German `Umsatzsteuer'.
 *
 * Revision 1.100  2004/07/24 17:58:06  tobiasb
 * New isdnrep options: `-L:' controls the displayed call summaries in the
 * report footer.  `-x' displays only call selected or not deselected by
 * hour or type of day -- may be useful in conjunction with `-r'.
 *
 * Activated new configuration file entry `REPOPTIONS' for isdnrep default
 * options.  This options can be disabled by `-c' on the command line.
 *
 * Revision 1.99  2004/02/25 12:09:08  paul
 * There was no bounds checking on unknownzones, which is only useds
 * if DEBUG is defined. This caused a SIGSEGV with many unknown numbers
 * (which were all the same, BTW...)  Now only do unknownzones if DEBUG
 * is defined.
 *
 * Revision 1.98  2003/11/14 20:29:29  tobiasb
 * Removed SIGSEGV in isdnrep that occurred while fetching zone names for outgoing calls
 * from the current ratefile if the matching zone did not contain a name after the zone
 * number.  This error was introduced with isdnrep-4.65 or rev. 1.96 of isdnrep.c.
 * Uwe Furchheim discovered the error and provided sufficient details on the mailinglist
 * rates4linux-users, see http://sourceforge.net/mailarchive/forum.php?forum_id=4262
 *
 * Revision 1.97  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>.
 *
 * Revision 1.96  2003/07/25 22:18:03  tobiasb
 * isdnlog-4.65:
 *  - New values for isdnlog option -2x / dual=x with enable certain
 *    workarounds for correct logging in dualmode in case of prior
 *    errors.  See `man isdnlog' and isdnlog/processor.c for details.
 *  - New isdnlog option -U2 / ignoreCOLP=2 for displaying ignored
 *    COLP information.
 *  - Improved handling of incomplete D-channel frames.
 *  - Increased length of number aliases shown immediately by isdnlog.
 *    Now 127 instead of 32 chars are possible. (Patch by Jochen Erwied.)
 *  - The zone number for an outgoing call as defined in the rate-file
 *    is written to the logfile again and used by isdnrep
 *  - Improved zone summary of isdnrep.  Now the real zone numbers as
 *    defined in the rate-file are shown.  The zone number is taken
 *    from the logfile as mentioned before or computed from the current
 *    rate-file.  Missmatches are indicated with the chars ~,+ and *,
 *    isdnrep -v ... explains the meanings.
 *  - Fixed provider summary of isdnrep. Calls should no longer be
 *    treated wrongly as done via the default (preselected) provider.
 *  - Fixed the -pmx command line option of isdnrep, where x is the xth
 *    defined [MSN].
 *  - `make install' restarts isdnlog after installing the data files.
 *  - A new version number generates new binaries.
 *  - `make clean' removes isdnlog/isdnlog/ilp.o when called with ILP=1.
 *
 * Revision 1.95  2001/03/21 10:24:01  paul
 * Previous patch for correctly deleting entries messed up the printing
 * of a specific date (or range), now hopefully fixed. Please test!
 * Also replaced usage of tmpnam() with more secure mkstemp().
 * Reindented some lines, tabstop of 2 is really strange.
 *
 * Revision 1.94  2001/02/08 14:04:53  paul
 * Fix bug where deleting up to 31/12/99 caused ALL entries to be deleted;
 * now the entries being deleted are output (as usual with isdnrep), and
 * the rest are preserved in the log.
 * Also cleaned up error messages to be a bit more consistent.
 *
 * Revision 1.93  2000/08/17 21:34:44  akool
 * isdnlog-4.40
 *  - README: explain possibility to open the "outfile=" in Append-Mode with "+"
 *  - Fixed 2 typos in isdnlog/tools/zone/de - many thanks to
 *      Tobias Becker <tobias@talypso.de>
 *  - detect interface (via IIOCNETGPN) _before_ setting CHARGEINT/HUPTIMEOUT
 *  - isdnlog/isdnlog/processor.c ... fixed wrong init of IIOCNETGPNavailable
 *  - isdnlog/isdnrep/isdnrep.c ... new option -S summary
 *  - isdnlog/isdnrep/rep_main.c
 *  - isdnlog/isdnrep/isdnrep.1.in
 *  - isdnlog/tools/NEWS
 *  - isdnlog/tools/cdb/debian ... (NEW dir) copyright and such from orig
 *  - new "rate-de.dat" from sourceforge (hi and welcome: Who is "roro"?)
 *
 * Revision 1.92  2000/06/22 16:08:24  keil
 * parameter in (...) are allways converted into int
 * newer gcc give an error using char here
 *
 * Revision 1.91  2000/05/27 14:55:30  akool
 * isdnlog-4.25
 *  - isdnlog/isdnrep/isdnrep.c ... bugfix for wrong providers and duration
 *                                  by Hans Klein on d.a.c.i
 *
 *  - isdnlog/tools/rate-at.c ... 1046 Neu, 1002 ab 1.6., 1024
 *  - isdnlog/rate-at.dat ... 1046 Neu, 1002 ab 1.6., 1024
 *  - new rates 01078:3U and 01024:Super_24
 *
 * Revision 1.90  2000/03/06 07:03:20  akool
 * isdnlog-4.15
 *   - isdnlog/tools/tools.h ... moved one_call, sum_calls to isdnrep.h
 *     ==> DO A 'make clean' PLEASE
 *   - isdnlog/tools/telnum.c ... fixed a small typo
 *   - isdnlog/isdnrep/rep_main.c ... incl. dest.h
 *   - isdnlog/isdnrep/isdnrep.c ... fixed %l, %L
 *   - isdnlog/isdnrep/isdnrep.h ... struct one_call, sum_calls are now here
 *
 *   Support for Norway added. Many thanks to Tore Ferner <torfer@pvv.org>
 *     - isdnlog/rate-no.dat  ... NEW
 *     - isdnlog/holiday-no.dat  ... NEW
 *     - isdnlog/samples/isdn.conf.no ... NEW
 *     - isdnlog/samples/rate.conf.no ... NEW
 *
 * Revision 1.89  2000/02/28 19:53:55  akool
 * isdnlog-4.14
 *   - Patch from Roland Rosenfeld <roland@spinnaker.de> fix for isdnrep
 *   - isdnlog/tools/rate.c ... epnum
 *   - isdnlog/tools/rate-at.c ... new rates
 *   - isdnlog/rate-at.dat
 *   - isdnlog/tools/rate-files.man ... %.3f
 *   - doc/Configure.help ... unknown cc
 *   - isdnlog/configure.in ... unknown cc
 *   - isdnlog/.Config.in ... unknown cc
 *   - isdnlog/Makefile.in ... unknown cc
 *   - isdnlog/tools/dest/Makefile.in ... LANG => DEST_LANG
 *   - isdnlog/samples/rate.conf.pl ... NEW
 *   - isdnlog/samples/isdn.conf.pl ... NEW
 *   - isdnlog/rate-pl.dat ... NEW
 *   - isdnlog/tools/isdnrate.c ... fixed -P pid_dir, restarts on HUP now
 *   - isdnlog/tools/isdnrate.man ... SIGHUP documented
 *
 * Revision 1.88  2000/02/07 20:32:41  akool
 * isdnlog-4.09
 *   - NEW: 01078:3U and 010050:Drillisch foreign countries
 *   - isdnlog/isdnrep/isdnrep.c ... moved hist, provider ok again
 *   - isdnlog/isdnrep/CHANGES.isdnrep ... NEW (old changes)
 *   - isdnlog/isdnlog/isdnlog.8.in ... addded signals
 *   - isdnlog/README ... upd. core (SIGSEGV), files
 *
 * Revision 1.87  2000/01/16 12:36:58  akool
 * isdnlog-4.03
 *  - Patch from Gerrit Pape <pape@innominate.de>
 *    fixes html-output if "-t" option of isdnrep is omitted
 *  - Patch from Roland Rosenfeld <roland@spinnaker.de>
 *    fixes "%p" in ILABEL and OLABEL
 *
 * Revision 1.86  1999/12/31 13:57:18  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
 *
 *  for older changes please look at CHANGES
 */


#define _REP_FUNC_C_

#include <sys/param.h>
#include <dirent.h>
#include <search.h>
/* included by libisdn.h: #include <linux/limits.h> */
#include <string.h>

#include "dest.h"
#include "isdnrep.h"
#include "../../vbox/src/libvbox.h"
#include "libisdn.h"

#define SET_TIME    1
#define GET_TIME    2
#define GET_DATE    4
#define GET_DATE2   8
#define GET_YEAR   16

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

#define GET_OUT     0
#define GET_IN      1

#define GET_BYTES   0
#define GET_BPS     2

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

#define C_BEGIN_FMT '%'

#define FMT_FMT 1
#define FMT_STR 2

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

#define C_VBOX  'v'
#define C_FAX   'f'

#define F_VBOX  1
#define F_FAX   2

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

#define F_BEGIN 1
#define F_END   2

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

#define DEF_FMT "  %X %D %15.15H %T %-15.15F %7u %U %I %O"
#define WWW_FMT "%X %D %17.17H %T %-17.17F %-20.20l SI: %S %9u %U %I %O"

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

#define STR_FAX "Fax: "

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

#define H_ENV_VAR "QUERY_STRING"

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

#define F_1ST_LINE       1
#define F_BODY_HEADER    2
#define F_BODY_HEADERL   4
#define F_BODY_LINE      8
#define F_BODY_BOTTOM1  16
#define F_BODY_BOTTOM2  32
#define F_COUNT_ONLY    64
#define F_TEXT_LINE    128
#define F_TABLE_LINE   256

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

#define H_BG_COLOR     "#FFFFFF"
#define H_TABLE_COLOR1 "#CCCCFF"
#define H_TABLE_COLOR2 "#FFCCCC"
#define H_TABLE_COLOR3 "#CCFFCC"
#define H_TABLE_COLOR4 "#FFFFCC"
#define H_TABLE_COLOR5 "#CCFFFF"

#define H_FORM_ON      "<FORM METHOD=\"put\" ACTION=\"%s\">"
#define H_FORM_OFF     "</FORM>"

#define H_1ST_LINE     "<CENTER><FONT size=+1><B>%s</B></FONT><P>\n"
#define H_TEXT_LINE    "<CENTER><B>%s</B><P>\n"
#define H_BODY_LINE    "<TR>%s</TR>\n"
#define H_BODY_HEADER1 "<TABLE width=%g%% bgcolor=%s border=0 cellspacing=0 cellpadding=0>\n"
#define H_BODY_HEADER2 "<COL width=%d*>\n"
//#define H_BODY_HEADER2 "<COLGROUP span=1 width=%d*>\n"
#define H_BODY_HEADER3 "<TH colspan=%d>%s</TH>\n"
#define H_BODY_BOTTOM1 "<TD align=left colspan=%d>%s</TD>%s\n"
#define H_BODY_BOTTOM2 "</TABLE><P>\n"
#define H_TABLE_LINE   "<TR><TD align=left colspan=%d><TT>%s</TT></TD></TR>\n"
#define H_LINE "<TR><TD colspan=%d><HR size=%d noshade width=100%%></TD></TR>\n"

#define H_LEFT         "<TD align=left><TT>%s</TT></TD>"
#define H_CENTER       "<TD align=center><TT>%s</TT></TD>"
#define H_RIGHT        "<TD align=right><TT>%s</TT></TD>"
#define H_LINK         "<A HREF=\"%s?-M+%c%d%s\">%s</A>"
#define H_LINK_DAY     "<A HREF=\"%s?%s\">%s</A>&nbsp;"
#define H_FORM_DAY     "%s<input name=\"%s\" maxlength=%d value=\"\" size=%d><INPUT TYPE=\"submit\" NAME=\"submit\" VALUE=\"go\">"

#define H_EMPTY        "&nbsp;"

#define H_APP_ZYXEL2 "application/x-zyxel2"
#define H_APP_ZYXEL3 "application/x-zyxel3"
#define H_APP_ZYXEL4 "application/x-zyxel4"
#define H_APP_ULAW   "application/x-ulaw"
#define H_APP_TEXT   "text/plain"
#define H_APP_FAX3   "application/x-faxg3"

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

#define	UNKNOWNZONE  MAXZONES

/*****************************************************************************/
/* flags for zones_src.flags (FZN = flag zone name) */
#define FZN_KNOWN   0x01  /* zone_names[i] is a valid zone name, not ?? */
#define FZN_UNSURE  0x02  /* zone_names[i] may be incorrect */
#define FZN_MPROV   0x04  /* zone i is used with more than one provider */
#define FZN_MNAME   0x08  /* found different names for zone i */

/*****************************************************************************/
/* bits in sel_sums[] for the footer summaries */
#define SUM_IN      0x01
#define SUM_OUT     0x02
#define SUM_INOUT   0x04
#define SUM_ZONE    0x08
#define SUM_PROV    0x10
#define SUM_MSN     0x20
#define SUM_ALL     0x3f
#define SUM__CHARS  "ioczpm"  /* corresponding chars for option value */

/*****************************************************************************/
/* indices for sel_sums, days, and hours option */
#define INCLUDE     0
#define EXCLUDE     1
#define RESULT      2

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

typedef struct {
	int   type;
	char *string;
	char  s_type;
	char *range;
} prt_fmt;

typedef struct {
	int    version;
	int    compression;
	int    used;
	time_t time;
	char   type;
	char  *name;
} file_list;

typedef struct {
	int flags;      /* see FZN_ constants */
  int first_prov; /* for detection of FZN_MPROV */
} zones_src_t;
			
/*****************************************************************************/

static int show_msn(one_call *cur_call);
static int add_sum_calls(sum_calls *s1, sum_calls *s2);
static int print_sum_calls(sum_calls *s, int computed);
static int add_one_call(sum_calls *s1, one_call *s2, double units);
static int clear_sum(sum_calls *s1);
static char *print_currency(double money, int computed);
static void strich(int type);
static int n_match(char *Pattern, char* Number, char* version);
static int set_caller_infos(one_call *cur_call, char *string, time_t from);
static int set_alias(one_call *cur_call, int *nx, char *myname);
static int print_header(int lday);
static int print_entries(one_call *cur_call, double unit, int *nx, char *myname);
static int print_bottom(double unit, char *start, char *stop);
static char *get_time_value(time_t t, int *day, int flag);
static char **string_to_array(char *string);
static prt_fmt** get_format(const char *format);
static char *set_byte_string(int flag, double Bytes);
static int print_line(int status, one_call *cur_call, int computed, char *overlap);
static int print_line2(int status, const char *, ...);
static int print_line3(const char *fmt, ...);
static int append_string(char **string, prt_fmt *fmt_ptr, char* value);
static char *html_conv(char *string);
static int get_format_size(void);
static int set_col_size(void);
static char *overlap_string(char *s1, char *s2);
static char* fill_spaces(char *string);
static void free_format(prt_fmt** ptr);
static int html_bottom(char *progname, char *start, char *stop);
static int html_header(void);
static char *print_diff_date(char *start, char *stop);
static int get_file_list(void);
static int set_dir_entries(char *directory, int (*set_fct)(const char *, const char *));
static int set_vbox_entry(const char *path, const char *file);
static int set_mgetty_entry(const char *path, const char *file);
static int set_element_list(file_list* elem);
static int Compare_files(const void *e1, const void *e2);
static file_list *get_file_to_call(time_t filetime, char type);
static char *get_links(time_t filetime, char type);
static char *append_fax(char **string, char *file, char type, int version);
static int time_in_interval(time_t t1, time_t t2, char type);
static char *nam2html(char *file);
static char *get_a_day(time_t t, int d_diff, int m_diff, int flag);
static char *get_time_string(time_t begin, time_t end, int d_diff, int m_diff);
static char *get_default_html_params(void);
static char *create_vbox_file(char *file, int *compression);
static int htoi(char *s);
static char **get_http_args(char *str, int *index);
static char *url_unescape(char *str);
static int app_fmt_string(char *target, int targetlen, char *fmt, int condition, char *value);
static int find_format_length(char *string);
static int check_day_hour(bitfield *days, bitfield *hours, const time_t check);

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

static int      invertnumbers = 0;
static int      unknowns = 0;
static UNKNOWNS unknown[MAXUNKNOWN];
static int      zones[MAXZONES + 1];
static int      zones_usage[MAXZONES + 1];
static char *   zones_names[MAXZONES + 1];
static double   zones_dm[MAXZONES + 1];
static double   zones_dur[MAXZONES + 1];
static zones_src_t
                zones_src[MAXZONES +1];
static char**   ShowMSN = NULL;
static int*     colsize = NULL;
static double   h_percent = 100.0;
static char*    h_table_color = H_TABLE_COLOR1;
static file_list **file_root = NULL;
static int      file_root_size = 0;
static int      file_root_member = 0;
static char    *_myname;
static time_t   _begintime;
static int      read_path = 0;

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

static char msg1[]    = "isdnrep: can't open %s (%s)\n";
static char wrongdate2[]   = "isdnrep: wrong date for delete: %s\n";
static char nomemory[]   = "isdnrep: out of memory!\n";

static char htmlconv[][2][10] = {
	{">", "&gt;"},
	{"<", "&lt;"},
	{" ", H_EMPTY},
	{"" , ""},
};

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

static int Tarif96 = 0;
static int Tarif962 = 0;

static sum_calls day_sum;
static sum_calls day_com_sum;
static sum_calls all_sum;
static sum_calls all_com_sum;

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

static double *msn_sum;
static int    *usage_sum;
static double *dur_sum;

static int    usage_provider[MAXPROVIDER];
static int    provider_failed[MAXPROVIDER];
static double duration_provider[MAXPROVIDER];
static double pay_provider[MAXPROVIDER];
#if DEBUG
#include <assert.h>
static char   unknownzones[4096];
#endif

#undef MAXPROVIDER
#define MAXPROVIDER getNProvider()


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

void info(int chan, int reason, int state, char *msg)
{
  /* DUMMY - dont needed here! */
} /* info */

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

int send_html_request(char *myname, char *option)
{
	char file[PATH_MAX];
	char commandline[PATH_MAX];
	char *filetype = NULL;
	char *command  = NULL;
	char *vboxfile = NULL;
	int  compression;


	if (*option == C_VBOX)
	{
		sprintf(file,"%s%c%s",vboxpath,C_SLASH,option+2);

		if (option[1] == '1')
		{
			if (vboxcommand1)
				command = vboxcommand1;
			else
				filetype = H_APP_ZYXEL4;
		}
		else
		if (option[1] == '2')
		{
			if (vboxcommand2)
				command = vboxcommand2;
			else
			{
				vboxfile = strcpy(file,create_vbox_file(file,&compression));

				switch(compression)
				{
					case 2 : filetype = H_APP_ZYXEL2;
					         break;
					case 3 : filetype = H_APP_ZYXEL3;
					         break;
					case 4 : filetype = H_APP_ZYXEL4;
					         break;
					case 6 : filetype = H_APP_ULAW;
					         break;
					default: printf( "Content-Type: %s\n\n",H_APP_TEXT);
				           printf( "%s: unsupported compression type of vbox file :`%d'\n",myname,compression);
                                           return(UNKNOWN);
					         break;
				}
			}
		}
		else
		{
			printf( "Content-Type: %s\n\n",H_APP_TEXT);
			printf( "%s: unsupported version of vbox `%c'\n",myname,option[0]);
                        return(UNKNOWN);
		}
	}
	else
	if (*option == C_FAX)
	{
		sprintf(file,"%s%c%s",mgettypath,C_SLASH,option+2);

		if (option[1] == '3')
		{
			if (mgettycommand)
				command = mgettycommand;
			else
				filetype = H_APP_FAX3;
		}
		else
		{
			printf( "Content-Type: %s\n\n",H_APP_TEXT);
			printf( "%s:unsupported version of fax `%c%c'\n",myname,option[0],option[1]);
                        return(UNKNOWN);
		}
	}
	else
	{
		printf( "Content-Type: %s\n\n",H_APP_TEXT);
		printf( "%s:invalid option string `%c%c'\n",myname,option[0],option[1]);
                return(UNKNOWN);
	}

	if (command == NULL)
	{
		char precmd[SHORT_STRING_SIZE];

		sprintf(precmd, "echo \"Content-Type: %s\"",filetype);
		system(precmd);
		sprintf(precmd, "echo");
		system(precmd);
	//	printf( "Content-Type: %s\n\n",filetype);
	}

	sprintf(commandline,"%s %s",command?command:"cat",file);
	system(commandline);

	if (vboxfile)
	{
		if (unlink(vboxfile))
		{
			print_msg(PRT_ERR, "isdnrep: can't delete file `%s': %s!\n",file, strerror(errno));
                        return(UNKNOWN);
		}
	}

	return 0;
}

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

int read_logfile(char *myname)
{
  auto	   double     einheit = 0.23;
  auto	   int	      nx[2];
  auto	   time_t     now, from = 0;
  auto 	   struct tm *tm;
  auto     int        lday = UNKNOWN;
  auto	   char	      start[20] = "", stop[20];
  auto     FILE      *fi, *ftmp = NULL;
  auto     char       string[BUFSIZ], s[BUFSIZ];
  one_call            cur_call;


  initHoliday(holifile, NULL);
  initDest(destfile, NULL);
  initRate(rateconf, ratefile, zonefile, NULL);

  interns0 = 3; /* Fixme: */

  msn_sum = calloc(mymsns + 1, sizeof(double));
  usage_sum = calloc(mymsns + 1, sizeof(int));
  dur_sum = calloc(mymsns + 1, sizeof(double));
#if DEBUG
  *unknownzones = 0;
#endif

	_myname = myname;
	_begintime = begintime;

	if (html & H_PRINT_HEADER)
		html_header();

	if (lineformat == NULL)
	{
		if (html)
			lineformat = WWW_FMT;
		else
			lineformat = DEF_FMT;
	}

	if (get_format(lineformat) == NULL)
                return(UNKNOWN);

	/* following two lines must be after get_format()! */
	if (html)
		get_file_list();

  clear_sum(&day_sum);
  clear_sum(&day_com_sum);
  clear_sum(&all_sum);
  clear_sum(&all_com_sum);

	if (knowns == 0 || strcmp(known[knowns-1]->who,S_UNKNOWN))
	{
		if ((known = (KNOWN**) realloc(known, sizeof(KNOWN *) * (knowns+1))) == NULL)
    {
      print_msg(PRT_ERR, nomemory);
      return(UNKNOWN);
    }

		if ((known[knowns] = (KNOWN*) calloc(1,sizeof(KNOWN))) == NULL)
    {
      print_msg(PRT_ERR, nomemory);
      return(UNKNOWN);
    }

    known[knowns]->who = S_UNKNOWN;
    known[knowns++]->num = "0000";
	}

	if (recalc.mode && !prep_recalc())
		return UNKNOWN; /* error in -r Option, msg by prep_recalc to stdout */ 

  if (delentries)
  {
    if(begintime)
    {
      print_msg(PRT_ERR, wrongdate2, timestring);
      return(UNKNOWN);
    }
    else
    {
      if ((ftmp = tmpfile()) == NULL)
      {
        print_msg(PRT_ERR, msg1, "tmpfile", strerror(errno));
        return(UNKNOWN);
      }
    }
  }

  if (!timearea) { /* from darf nicht gesetzt werden, wenn alle Telefonate angezeigt werden sollen */
		/* get time of start of today (midnight) */
    time(&now);
    	/* aktuelle Zeit wird gesetzt */
    tm = localtime(&now);
    	/* Zeit von 1970 nach Struktur */
    tm->tm_sec = tm->tm_min = tm->tm_hour = 0;
    from = mktime(tm);
    	/* Struktur nach Zeit von 1970 */
    	/* from hat den aktuellen Tag 0.00 Uhr */
  } /* if */

  if ((fi = fopen(logfile, "r")) == (FILE *)NULL){
  	print_msg(PRT_ERR, msg1, logfile, strerror(errno));
        return(UNKNOWN);
  }

  memset(zones_usage, 0, sizeof(zones_usage));
	memset(zones_names, 0, sizeof(zones_names));
	memset(zones_src, 0, sizeof(zones_src)); /* start with zone names unknown */

  while (fgets(s, BUFSIZ, fi)) {
    strcpy(string,s);

		if (*s == '#')
			continue;

		/* set_caller_infos() returns UNKNOWN also if outside time range */
		/* but if we are deleting, then the line must be copied to tempfile */
		/* so no direct "continue;" in that case */

		if (set_caller_infos(&cur_call,s,from) == UNKNOWN) {
		  if (!delentries)
			  continue;
			if (cur_call.t >= endtime) {
				/* past end: stop delete, start copying */
				fputs(string,ftmp);
				continue;
			}
		}

		/* TODO (tobiasb|2004-02): right place for setting begintime?
		   either verify whether delentries is working or disable/remove it */

		if (!print_failed && cur_call.duration == 0)
			continue;

		if (!begintime)
			begintime = cur_call.t;

		if (phonenumberonly)
			if (!show_msn(&cur_call))
				continue;

		if (incomingonly && cur_call.dir == DIALOUT)
			continue;

		if (outgoingonly && cur_call.dir == DIALIN)
			continue;

		if (check_day_hour(days, hours, cur_call.t))
			continue;

		get_time_value(cur_call.t,&day,SET_TIME);

		/* Andreas, warum ist diese Abfrage hier drin, warum soll das nicht moeglich
		   sein? */
		if (cur_call.duration > 172800.0) /* laenger als 2 Tage? Das KANN nicht sein! */
			cur_call.duration = 0;

		set_alias(&cur_call,nx,myname);

		if (day != lday) {
			if (header)
				print_header(lday);

			lday = day;

			if (!*start)
			{
				sprintf(start, "%s %s", get_time_value(0,NULL,GET_DATE),
				                        get_time_value(0,NULL,GET_YEAR));
				sprintf(stop, "%s %s", get_time_value(0,NULL,GET_DATE),
				                       get_time_value(0,NULL,GET_YEAR));
			}
			else
				sprintf(stop, "%s %s", get_time_value(0,NULL,GET_DATE),
				                       get_time_value(0,NULL,GET_YEAR));
		} /* if */

		if (!cur_call.currency_factor)
		                einheit = 1.0;
			else
				einheit = cur_call.currency_factor;

		print_entries(&cur_call,einheit,nx,myname);

	} /* while */

	fclose(fi);

	if (delentries) /* Erzeugt neue verkuerzte Datei */
    lday = UNKNOWN;

  if (lday == UNKNOWN && html)
	{
		if (begintime == 0)
			begintime = time(NULL);

		get_time_value(begintime,&lday,SET_TIME);
		sprintf(start, "%s %s", get_time_value(0,NULL,GET_DATE),
		                        get_time_value(0,NULL,GET_YEAR));

		if (endtime) {
		get_time_value(endtime,&lday,SET_TIME);
		sprintf(stop, "%s %s", get_time_value(0,NULL,GET_DATE),
		                       get_time_value(0,NULL,GET_YEAR));
		}
		else {
		  strcpy(stop, start);
		}

		print_line2(F_1ST_LINE,"I S D N  Connection Report");
		print_line2(F_TEXT_LINE,"");
		print_line2(F_TEXT_LINE,"no calls from %s to %s", start, stop);
		print_line2(F_TEXT_LINE,"");
                lday = UNKNOWN;
	}

  if (lday != UNKNOWN && header)
		print_bottom(einheit, start, stop);

	if (delentries) /* Erzeugt neue verkuerzte Datei */
	{
		rewind(ftmp);

		if ((fi = fopen(logfile, "w")) == (FILE *)NULL)
		{
			print_msg(PRT_ERR, msg1, logfile, strerror(errno));
      return(UNKNOWN);
		}

		while (fgets(s, BUFSIZ, ftmp))
			fputs(s,fi);

		fclose(fi);
		fclose(ftmp);
	}

	if (html & H_PRINT_HEADER)
		html_bottom(myname,start,stop);

	return 0;
} /* read_logfile */

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

static int print_bottom(double unit, char *start, char *stop)
{
  auto     char       string[BUFSIZ], sx[BUFSIZ];
  register int	      i, j, k;
  register char      *p = NULL;
  sum_calls           tmp_sum;
  auto     double     s = 0.0, s2 = 0.0;
  auto	   int	      s1 = 0;
	auto     int        showIO = 1;   /* display inbyte and outbyte */

	if (timearea && summary < 2) {
		strich(1);
		print_sum_calls(&day_sum,0);

		if (day_com_sum.eh)
		{
			print_sum_calls(&day_com_sum,1);

			clear_sum(&tmp_sum);
			add_sum_calls(&tmp_sum,&day_sum);
			add_sum_calls(&tmp_sum,&day_com_sum);
			strich(1);
			print_sum_calls(&tmp_sum,0);
		}
		else
			printf("\n");
	} /* if */

	add_sum_calls(&all_sum,&day_sum);
	add_sum_calls(&all_com_sum,&day_com_sum);

	strich(2);
	print_sum_calls(&all_sum,0);

	if (bill)
          return(0);

	if (all_com_sum.eh)
	{
		print_sum_calls(&all_com_sum,1);

		clear_sum(&tmp_sum);
		add_sum_calls(&tmp_sum,&all_sum);
		add_sum_calls(&tmp_sum,&all_com_sum);
		strich(2);
		print_sum_calls(&tmp_sum,0);
	}
	else
		printf("\n");

	print_line2(F_BODY_BOTTOM2,"");

	i = 0;
	if (recalc.mode && recalc.unknown)
	{
		print_line2(F_TEXT_LINE, "Recomputed %d of %d qualified call(s)",
		            (recalc.count-recalc.unknown), recalc.count);
		i++;
	}
	if (tolower(recalc.mode)=='b' &&
	    recalc.cheaper != recalc.count- recalc.unknown)
	{
		print_line2(F_TEXT_LINE, "Found cheaper provider for %d of %d call(s)",
		            recalc.cheaper, (recalc.count-recalc.unknown));
		i++;
	}
	if (i)
		print_line2(F_TEXT_LINE, "");
	
	/* foreign caller summary (out and in)  ------------------------------- */
	/* strategy for width of summary lines:
	 * call entry format (-F, -s) with %I or %O ==> summary also
	 * call entry format longer than 80 chars ==> summary also
	 * summary header out is 72 chars log, header in 74 chars */

	if ( !(strstr(lineformat, "%I") || strstr(lineformat, "%O")) )
		showIO = 0;
	if ( set_col_size() > 80 )
	{
		if (showIO)
			get_format("%-25.25s %4d call(s) %10.10s  %12s %-12s %-12s");
		else
			get_format("%-36.36s %4d call(s) %10.10s  %12s");
	}
	else
	{
		if (showIO)
			get_format("%-14.14s %4d call(s) %10.10s  %12s %-12s %-12s");
		else
			get_format("%-36.36s %4d call(s) %10.10s  %12s");
	}
			
	for (j = 0; j < 2; j++)
	{
		if ( (j == DIALOUT && !incomingonly && sel_sums[RESULT] & SUM_OUT) ||
		     (j == DIALIN  && !outgoingonly && sel_sums[RESULT] & SUM_IN ) )
		{
			sprintf(string, "%s Summary for %s", (j == DIALOUT) ? "Outgoing calls (calling:)" : "Incoming calls (called by:)",
			              print_diff_date(start,stop));

			h_percent = 80.0;
			h_table_color = H_TABLE_COLOR2;
			print_line2(F_BODY_HEADER,"");
			print_line2(F_BODY_HEADERL,"%s",string);
			strich(1);

			for (i = 0 ; i < knowns; i++) {
				if (known[i]->usage[j]) {
					print_line3(NULL,
					          /*!numbers?*/known[i]->who/*:known[i]->num*/,
					          known[i]->usage[j],
					          double2clock(known[i]->dur[j]),
					          j==DIALOUT?print_currency(known[i]->pay,0):
					          fill_spaces(print_currency(known[i]->pay,0)),
					          set_byte_string(GET_IN|GET_BYTES,known[i]->ibytes[j]),
					          set_byte_string(GET_OUT|GET_BYTES,known[i]->obytes[j]));
					          /* the last two arguments may be excessive */
				} /* if */
			} /* for */

			print_line2(F_BODY_BOTTOM2,"");
		} /* /if show out|in summary */
	}

	if (sel_sums[RESULT] & SUM_INOUT) {
		/* print mixed summary about foreign numbers */
		sprintf(string, "Calls (incoming & outgoing) Summary for %s",
				              print_diff_date(start,stop));

		h_percent = 80.0;
		h_table_color = H_TABLE_COLOR2;
		print_line2(F_BODY_HEADER,"");
		print_line2(F_BODY_HEADERL,"%s",string);
		strich(1);

		for (i = 0 ; i < knowns; i++) {
			if (known[i]->usage[DIALIN]||known[i]->usage[DIALOUT]) {
				print_line3(NULL,
				          /*!numbers?*/known[i]->who/*:known[i]->num*/,
				          known[i]->usage[DIALIN] + known[i]->usage[DIALOUT],
				          double2clock(known[i]->dur[DIALIN] + known[i]->dur[DIALOUT]),
				          known[i]->usage[DIALOUT]?print_currency(known[i]->pay,0):
				          fill_spaces(print_currency(known[i]->pay,0)),
				          set_byte_string(GET_IN|GET_BYTES,
				              known[i]->ibytes[DIALIN] + known[i]->ibytes[DIALOUT]),
				          set_byte_string(GET_OUT|GET_BYTES,
				              known[i]->obytes[DIALIN] + known[i]->obytes[DIALOUT]) );
				          /* the last two arguments may be excessive */
			} /* if */
		} /* for */

		print_line2(F_BODY_BOTTOM2,"");
	} /* /if show foreign in&out summary */


	if (!incomingonly && sel_sums[RESULT] & SUM_ZONE)
	{
		/* zone summary (outgoing)  ----------------------------------------- */
		int ztypes = 0;
		/* prev.:   60.0 */
		h_percent = 70.0;
		h_table_color = H_TABLE_COLOR3;
		/* prev.:  "%-21.21s %4d call(s) %10.10s  %12s" */
		get_format("%-34.34s %4d call(s) %10.10s  %12s");
		print_line2(F_BODY_HEADER,"");
		/* Fixme: zones are provider-specific
		   we are summing up zones for all provides here */
		print_line2(F_BODY_HEADERL,"Outgoing calls ordered by Zone");
		strich(1);

		for (i = 0; i < MAXZONES /* + 1 */; i++)
			if (zones_usage[i]) {
				auto     char  s[BUFSIZ];
				char zinfo=' ';
				int *zflags=&(zones_src[i].flags);

				if (*zflags & FZN_MNAME) {
					zinfo = '*';
					ztypes |= FZN_MNAME;
				}	
				else if (*zflags & FZN_MPROV) {
					zinfo = '+';
					ztypes |= FZN_MPROV;
				}	
				else if (*zflags & FZN_UNSURE) {
					zinfo = '~';
					ztypes |= FZN_UNSURE;
				}	

				sprintf(s, "Zone %3d:%c%s", i, zinfo, zones_names[i]);

				print_line3(NULL, s, zones_usage[i],
				double2clock(zones_dur[i]),
				print_currency(zones_dm[i], 0));
			} /* if */

#if DEBUG
		if (zones_usage[UNKNOWNZONE])
			printf( "(%s)\n", unknownzones);
#endif

		if (verbose && ztypes) {	/* explain symbol in front of zone number */
			if (ztypes & FZN_UNSURE)
				print_line2(F_TABLE_LINE,"         ~ = Zone name not known for sure");
			if (ztypes & FZN_MPROV)
				print_line2(F_TABLE_LINE,"         + = Zone summed over several providers");
			if (ztypes & FZN_MNAME)
				print_line2(F_TABLE_LINE,"         * = Zone name differs with provider");
		}

		print_line2(F_BODY_BOTTOM2,"");
	} /* /if show zone summary */

	if (!incomingonly && sel_sums[RESULT] & SUM_PROV)
	{
		/* provider summary (outgoing)  ------------------------------------- */
		/* prev.:   60.0 */
		h_percent = 80.0;
		h_table_color = H_TABLE_COLOR4;
		/* prev.:  "%-8.8s %05s %-25.25s %4d call(s) %10.10s  %12s %s" */
		get_format("%-8.8s %8.8s %-24.24s %4d call(s) %10.10s  %13.13s  %-13s");
		/*          "Prov" vbn   provstr  no          durat.   cost     ..avail */
    /*                                                80 chars >|           */
		print_line2(F_BODY_HEADER,"");
		print_line2(F_BODY_HEADERL,"Outgoing calls ordered by Provider");
		strich(1);

		for (i = 0; i < MAXPROVIDER; i++) {
			prefix2provider(i, string);
		  if (usage_provider[i]) {
				if (duration_provider[i])
					sprintf(sx, "%5.1f%% avail.",
					        100.0 * (usage_provider[i] - provider_failed[i]) / usage_provider[i]);
				else
					*sx = 0;
				p = getProvider(i);
				if (!p || p[strlen(p) - 1] == '?') /* UNKNOWN Provider */
					p = "UNKNOWN";

				print_line3(NULL, "Provider", string, p,
				            usage_provider[i],
				            double2clock(duration_provider[i]),
				            print_currency(pay_provider[i], 0), sx);
			} /* if */

		} /* for */

		print_line2(F_BODY_BOTTOM2,"");

	} /* /if show provider summary */

	if (!incomingonly && sel_sums[RESULT] & SUM_MSN)
	{
		/* MSN summary (outgoing)  ------------------------------------------ */
		h_percent = 60.0;
		h_table_color = H_TABLE_COLOR5;
		get_format("%-30.30s %4d call(s) %10.10s  %12s");
		print_line2(F_BODY_HEADER,"");
		print_line2(F_BODY_HEADERL,"Outgoing calls ordered by MSN");
		strich(1);

		for (k = 0; k <= mymsns; k++) {
			if (usage_sum[k]) { /* there have been calls with this MSN */

				print_line3(NULL, ((k == mymsns) ? S_UNKNOWN : known[k]->who),
				  usage_sum[k],
				  double2clock(dur_sum[k]),
				  print_currency(msn_sum[k], 0));

				s += msn_sum[k];
				s1 += usage_sum[k];
				s2 += dur_sum[k];
			} /* if */
 		} /* for */
	} /* /if show MSN summary */

#if 0
  if (s) {
		strich(2);
		print_line3(NULL, "TOTAL", s1, double2clock(s2), print_currency(s, 0));
  } /* if */
#endif

	print_line2(F_BODY_BOTTOM2,"");

	if (seeunknowns && unknowns) {
		printf("\n\nUnknown caller(s)\n");
		strich(3);

		for (i = 0; i < unknowns; i++) {
#if 0
			printf("%s %-14s ", unknown[i].called ? "called by" : "  calling", unknown[i].num);
#else
                  if ((unknown[i].cause != 1) &&  /* Unallocated (unassigned) number */
                      (unknown[i].cause != 3) &&  /* No route to destination */
                      (unknown[i].cause != 28)) { /* Invalid number format (address incomplete) */

                       printf("%s ", unknown[i].called ? "Called by" : "  Calling");

                       printf("??? %s\n\t\t\t ", unknown[i].num);
                  } /* if */
#endif
			for (k = 0; k < unknown[i].connects; k++) {
				strcpy(string, ctime(&unknown[i].connect[k]));

				if ((p = strchr(string, '\n')))
					*p = 0;

				*(string + 19) = 0;

				if (k && (k + 1) % 2) {
					printf("\n\t\t\t ");
				} /* if */

				printf("%s%s", k & 1 ? ", " : "", string + 4);
			} /* for */

			printf("\n");
		} /* for */
	} /* if */

	return 0;
}

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

static int print_line3(const char *fmt, ...)
{
	char *string = NULL;
	char  tmpstr[BUFSIZ*3];
	auto  va_list ap;
	prt_fmt** fmtstring;


	if (fmt == NULL)
		fmtstring = get_format(NULL);
	else
		fmtstring = get_format(fmt);

	if (fmtstring == NULL)
                return(UNKNOWN);

	va_start(ap, fmt);

	while(*fmtstring != NULL)
	{
		if ((*fmtstring)->type == FMT_FMT)
		{
			switch((*fmtstring)->s_type)
			{
				case 's' : append_string(&string,*fmtstring,va_arg(ap,char*));
				           break;
				case 'c' : sprintf(tmpstr,"%c",va_arg(ap,int));
				           append_string(&string,*fmtstring,tmpstr);
				           break;
				case 'd' : sprintf(tmpstr,"%d",va_arg(ap,int));
				           append_string(&string,*fmtstring,tmpstr);
				           break;
				case 'f' : sprintf(tmpstr,"%f",va_arg(ap,double));
				           append_string(&string,*fmtstring,tmpstr);
				           break;
				default  : print_msg(PRT_ERR, "isdnrep: internal Error: unknown format `%c'!\n",(*fmtstring)->s_type);
				           break;
			}
		}
		else
		if ((*fmtstring)->type == FMT_STR)
		{
			append_string(&string,NULL,(*fmtstring)->string);
		}
		else
			print_msg(PRT_ERR, "isdnrep: internal Error: unknown format type `%d'!\n",(*fmtstring)->type);

		fmtstring++;
	}

	va_end(ap);

	print_line2(F_BODY_LINE,"%s",string);
	free(string);
	return 0;
}

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

static int print_line2(int status, const char *fmt, ...)
{
	char string[BUFSIZ*3];
	auto va_list ap;


	va_start(ap, fmt);
	vsnprintf(string, BUFSIZ*3, fmt, ap);
	va_end(ap);

	if (!html)
		status = 0;

	switch (status)
	{
		case F_COUNT_ONLY  : break;
		case F_1ST_LINE    : printf(H_1ST_LINE,string);
		                     break;
		case F_TEXT_LINE    : printf(H_TEXT_LINE,string);
		                     break;
		case F_BODY_BOTTOM1:
		case F_BODY_LINE   : printf(H_BODY_LINE,string);
		                     break;
		case F_BODY_HEADER : printf(H_BODY_HEADER1,h_percent,h_table_color);
		                     set_col_size();
		                     break;
		case F_BODY_HEADERL: printf(H_BODY_HEADER3,get_format_size(),string);
		                     break;
		case F_BODY_BOTTOM2: printf(H_BODY_BOTTOM2);
		                     break;
		case F_TABLE_LINE:   printf(H_TABLE_LINE, get_format_size(), html_conv(string));
		                     break;

		default            : printf("%s\n",string);
		                     break;
	}

	return 0;
}

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

static int print_line(int status, one_call *cur_call, int computed, char *overlap)
{
	char *string = NULL;
	char  help[32];
	prt_fmt **fmtstring = get_format(NULL);
	int dir;
	int i = 0;
	int free_col;
	int last_free_col = UNKNOWN;


	if (colsize == NULL || status == F_COUNT_ONLY)
	{
		free(colsize);

		if ((colsize = (int*) calloc(get_format_size()+1,sizeof(int))) == NULL)
		{
			print_msg(PRT_ERR, nomemory);
                        return(UNKNOWN);
		}
	}

	while (*fmtstring != NULL)
	{
		free_col = 0;

		if ((*fmtstring)->type == FMT_FMT)
		{
			switch((*fmtstring)->s_type)
			{
				/* time: */
				case 'X': if (status == F_BODY_LINE)
				            colsize[i] = append_string(&string,*fmtstring, get_time_value(0,NULL,GET_TIME));
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				            	colsize[i] = append_string(&string,*fmtstring, fill_spaces(get_time_value(0,NULL,GET_TIME)));
				          }
				          break;
				/* date (normal format with year): */
				case 'x': if (status == F_BODY_LINE)
				            colsize[i] = append_string(&string,*fmtstring, get_time_value(0,NULL,GET_DATE2));
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				            	colsize[i] = append_string(&string,*fmtstring, fill_spaces(get_time_value(0,NULL,GET_DATE2)));
				          }
				          break;
				/* date (without year): */
				case 'y': if (status == F_BODY_LINE)
				            colsize[i] = append_string(&string,*fmtstring, get_time_value(0,NULL,GET_DATE));
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				            	colsize[i] = append_string(&string,*fmtstring, fill_spaces(get_time_value(0,NULL,GET_DATE)));
				          }
				          break;
				/* year: */
				case 'Y': if (status == F_BODY_LINE)
				            colsize[i] = append_string(&string,*fmtstring, get_time_value(0,NULL,GET_YEAR));
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				            	colsize[i] = append_string(&string,*fmtstring, fill_spaces(get_time_value(0,NULL,GET_YEAR)));
				          }
				          break;
				/* duration: */
				case 'D': if (status == F_BODY_LINE)
				            colsize[i] = append_string(&string,*fmtstring, double2clock(cur_call->duration));
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				            	colsize[i] = append_string(&string,*fmtstring, fill_spaces(double2clock(cur_call->duration)));
				          }
				          break;
				/* Home (if possible the name): */
				/* Benoetigt Range! */
				case 'H': if (status == F_BODY_LINE)
				          {
				          	dir = cur_call->dir?CALLED:CALLING;
				            if (!numbers)
				          	{
				          		colsize[i] = append_string(&string,*fmtstring,
				           	             cur_call->who[dir][0]?cur_call->who[dir]:cur_call->num[dir]);
				          		break;
				          	}
				          }
				/* Home (number): */
				/* Benoetigt Range! */
				case 'h': if (status == F_BODY_LINE)
				            colsize[i] = append_string(&string,*fmtstring,
				                        cur_call->num[cur_call->dir?CALLED:CALLING]);
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				          		colsize[i] = append_string(&string,*fmtstring, "");
				          }
				          break;
				/* The other (if possible the name): */
				/* Benoetigt Range! */
			 	case 'F': if (status == F_BODY_LINE)
				          {
				            dir = cur_call->dir?CALLING:CALLED;
				          	if (!numbers)
				          	{
				          		colsize[i] = append_string(&string,*fmtstring,
				           	             cur_call->who[dir][0]?cur_call->who[dir]:cur_call->num[dir]);
				          		break;
				          	}
				          }
				/* The other (number): */
				/* Benoetigt Range! */
				case 'f': if (status == F_BODY_LINE)
				            colsize[i] = append_string(&string,*fmtstring,
				                        cur_call->num[cur_call->dir?CALLING:CALLED]);
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				          		colsize[i] = append_string(&string,*fmtstring, "");
				          }
				          break;
				/* The home location: */
				/* Benoetigt Range! */
				case 'L': if (status == F_BODY_LINE)
				          {
				            dir = cur_call->dir?CALLED:CALLING;
		          		colsize[i] = append_string(&string,*fmtstring, cur_call->sarea[dir]);
				          }
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				          		colsize[i] = append_string(&string,*fmtstring, "");
				          }
				          break;
				/* The remote location: */
				/* Benoetigt Range! */
				case 'l': if (status == F_BODY_LINE)
				          {
				            dir = cur_call->dir?CALLING:CALLED;
			          		colsize[i] = append_string(&string,*fmtstring,  cur_call->sarea[dir]);
				          }
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				          		colsize[i] = append_string(&string,*fmtstring, "");
				          }
				          break;
				/* The "To"-sign (from home to other) (-> or <-): */
				case 'T': if (status == F_BODY_LINE)
				            colsize[i] = append_string(&string,*fmtstring, cur_call->dir?"<-":"->");
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				          		colsize[i] = append_string(&string,*fmtstring, "  ");
				          }
				          break;
				/* The "To"-sign (from other to home) (-> or <-): */
				case 't': if (status == F_BODY_LINE)
				            colsize[i] = append_string(&string,*fmtstring, cur_call->dir?"->":"<-");
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				          		colsize[i] = append_string(&string,*fmtstring, "  ");
				          }
				          break;
				/* The units (if exists): */
				/* Benoetigt Range! */
				case 'u': if (cur_call->eh > 0)
				          {
				          	sprintf(help,"%d EH",cur_call->eh);
				          	colsize[i] = append_string(&string,*fmtstring, help);
				          }
				          else
				          	colsize[i] = append_string(&string,*fmtstring, "");
				          break;
				/* The money or/and a message: */
				case 'U': if (cur_call->duration || cur_call->eh > 0 || cur_call->pay > 0)
				          {
				          	if (cur_call->dir)
				          		colsize[i] = append_string(&string,NULL,"            ");
				          	else
				          		colsize[i] = append_string(&string,*fmtstring,print_currency(cur_call->pay,computed));
				          }
				          else
				          if ((status == F_BODY_LINE) &&
				              (cur_call->cause != UNKNOWN) &&
				              (cur_call->cause != 0x10) &&  /* Normal call clearing */
				              (cur_call->cause != 0x1f))    /* Normal, unspecified */
				          	colsize[i] = append_string(&string,*fmtstring,qmsg(TYPE_CAUSE, VERSION_EDSS1, cur_call->cause));
				          else
				          	colsize[i] = append_string(&string,NULL,"            ");
				          break;
				/* In-Bytes: */
				/* Benoetigt Range! */
				case 'I': colsize[i] = append_string(&string,*fmtstring,set_byte_string(GET_IN|GET_BYTES,(double)cur_call->ibytes));
				          break;
				/* Out-Bytes: */
				/* Benoetigt Range! */
				case 'O': colsize[i] = append_string(&string,*fmtstring,set_byte_string(GET_OUT|GET_BYTES,(double)cur_call->obytes));
				          break;
				/* In-Bytes per second: */
				/* Benoetigt Range! */
				case 'P': colsize[i] = append_string(&string,*fmtstring,set_byte_string(GET_IN|GET_BPS,cur_call->duration?cur_call->ibytes/(double)cur_call->duration:0.0));
				          break;
				/* Out-Bytes per second: */
				/* Benoetigt Range! */
				case 'p': colsize[i] = append_string(&string,*fmtstring,set_byte_string(GET_OUT|GET_BPS,cur_call->duration?cur_call->obytes/(double)cur_call->duration:0.0));
				          break;
				/* SI: */
				case 'S': if (status == F_BODY_LINE)
				          	colsize[i] = append_string(&string,*fmtstring,int2str(cur_call->si,2));
				          else
				          	colsize[i] = append_string(&string,*fmtstring,"  ");
				          break;

				/* provider name (from ratefile): */
				/* should have range */
			 	case 'j': if (status == F_BODY_LINE)
				          {
				          	/* if (!numbers) */ /* there is no numeric alternative */
				          	{
				          		register char *p;
				          		p = (cur_call->provider >= 0) ? getProvider(cur_call->provider) : "";
				          		if (cur_call->dir == DIALIN)
				           			p = "";
				          		colsize[i] = append_string(&string,*fmtstring, p);
				          		break;
				          	}
				          }
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				          		colsize[i] = append_string(&string,*fmtstring, "");
				          }
				          break;

				/* v: vbn aka carrier selection prefix (B: tag in ratefile) */
				case 'v': if (status == F_BODY_LINE)
				          {
				          	char help2[16];
				          	if (cur_call->dir == DIALIN)
				          		help[0] = '\0';
				          	else
											prefix2provider(cur_call->provider, help);
				          	snprintf(help2, 16, "%-8s", help);
				            colsize[i] = append_string(&string,*fmtstring, help2);
				          }
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				          		colsize[i] = append_string(&string,*fmtstring,
				          		                           "        ");
				          }
				          break;

				/* V: vbn and provider variant, e. g. "01012_3" */
				case 'V': if (status == F_BODY_LINE)
				          {
				          	char help2[16];
				          	if (cur_call->dir == DIALIN)
				          		help[0] = '\0';
				          	else
											prefix2provider_variant(cur_call->provider, help);
				          	snprintf(help2, 16, "%-10s", help);
				            colsize[i] = append_string(&string,*fmtstring, help2);
				          }
				          else
				          {
				          	free_col = 1;
				          	if (!html || status == F_COUNT_ONLY)
				          		colsize[i] = append_string(&string,*fmtstring,
				          		                           "          ");
				          }
				          break;

				/* Link for answering machine! */
				case 'C': if (html)
				          {
				          	if (status == F_BODY_LINE)
				          		colsize[i] = append_string(&string,*fmtstring,get_links(cur_call->t,C_VBOX));
				          	else
				          		colsize[i] = append_string(&string,*fmtstring,"     ");
				          }
				          else
				          	print_msg(PRT_ERR, "isdnrep: unknown format %%C!\n");
				          break;
				/* Link for fax! */
				case 'G': if (html)
				          {
				          	if (status == F_BODY_LINE)
				          		colsize[i] = append_string(&string,*fmtstring,get_links(cur_call->t,C_FAX));
				          	else
				          		colsize[i] = append_string(&string,*fmtstring,"        ");
				          }
				          else
				          	print_msg(PRT_ERR, "isdnrep: unknown format %%G!\n");
				          break;
				/* there are dummy entries */
				case 'c':
				case 'd':
				case 's': if (status != F_BODY_LINE)
				          	free_col = 1;

				          colsize[i] = append_string(&string,*fmtstring, " ");
				          break;
        default : print_msg(PRT_ERR, "isdnrep: internal Error: unknown format `%c'!\n",(*fmtstring)->s_type);
				          break;
			}
		}
		else
		if ((*fmtstring)->type == FMT_STR)
		{
			if (!html || status == F_COUNT_ONLY || status == F_BODY_LINE || last_free_col != i-1)
				colsize[i] = append_string(&string,NULL,(*fmtstring)->string);
			else
				free_col = 1;
		}
		else
			print_msg(PRT_ERR, "isdnrep: internal Error: unknown format type `%d'!\n",(*fmtstring)->type);

		if (html && status == F_BODY_BOTTOM1 && free_col && last_free_col == i-1)
			last_free_col = i;

		fmtstring++;
		i++;
	}

	if (last_free_col != UNKNOWN)
	{
		char *help2 = NULL;


		if ((help2 = (char*) calloc(strlen(H_BODY_BOTTOM1)+(string?strlen(string):0)+strlen(overlap)+1,sizeof(char))) == NULL)
		{
			print_msg(PRT_ERR, nomemory);
			return(UNKNOWN);
		}

		sprintf(help2,H_BODY_BOTTOM1,last_free_col+1,overlap,string?string:"");

		free(string);
		string = help2;
		overlap = NULL;
	}

	colsize[i] = UNKNOWN;

	if (status == F_COUNT_ONLY)
		return (string) ? strlen(string) : 0;  /* do not call strlen(0) */
	else
		print_line2(status,"%s",overlap_string(string,overlap));

	free(string);

	return 0;
}

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

static void bprint(one_call *call)
{
  register char  *p = call->num[CALLED];
  auto	   char	  target[BUFSIZ], s[BUFSIZ];
  auto	   TELNUM number;

  if (call->duration) {
    if (!memcmp(call->num[CALLED], mycountry, strlen(mycountry))) { /* eigenes Land */
      p += strlen(mycountry);
      sprintf(target, "0%s", p);
    }
    else
      sprintf(target, "%s", p);

    printf( "%s %s %-16s  ",
      get_time_value(0,NULL, GET_TIME),
      double2clock(call->duration),
      target);

    if (call->duration) {
      printf( "%s %-15s",
      print_currency(call->pay * 100.0 / 116.0, 0), getProvider(call->provider));

      strcpy(s, call->num[CALLED]);

      number.nprovider=call->provider;
      normalizeNumber(s, &number, TN_NO_PROVIDER);
      printf( "%s\n", formatNumber("%A", &number));
    }
    else
      printf( "%*s** %s\n", 30, "", qmsg(TYPE_CAUSE, VERSION_EDSS1, call->cause));
  } /* if */
} /* bprint */

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

static int append_string(char **string, prt_fmt *fmt_ptr, char* value)
{
	char  tmpstr[BUFSIZ*3];
	char  tmpfmt2[20];
	char *tmpfmt;
	char *htmlfmt;
	int   condition = html && (*value == ' ' || fmt_ptr == NULL || (fmt_ptr->s_type!='C' && fmt_ptr->s_type!='G'));


	if (fmt_ptr != NULL)
		sprintf(tmpfmt2,"%%%ss",fmt_ptr->range);
	else
		strcpy(tmpfmt2,"%s");

	if (html)
	{
		switch (fmt_ptr==NULL?'\0':fmt_ptr->range[0])
		{
			case '-' : htmlfmt = H_LEFT;
			           break;
			case '\0': htmlfmt = H_CENTER;
			           break;
			default  : htmlfmt = H_RIGHT;
			           break;
		}

		if (!strncmp(STR_FAX,value,strlen(STR_FAX)))
			htmlfmt = H_LEFT;

		if ((tmpfmt = (char*) alloca(sizeof(char)*(strlen(htmlfmt)+strlen(tmpfmt2)+1))) == NULL)
		{
			print_msg(PRT_ERR, nomemory);
			return(UNKNOWN);
		}

		sprintf(tmpfmt,htmlfmt,tmpfmt2);
	}
	else
		tmpfmt = tmpfmt2;

	app_fmt_string(tmpstr,BUFSIZ*3-1,tmpfmt,condition,value);

	if (*string == NULL)
		*string = (char*) calloc(strlen(tmpstr)+1,sizeof(char));
	else
		*string = (char*) realloc(*string,sizeof(char)*(strlen(*string)+strlen(tmpstr)+1));

	if (*string == NULL)
	{
		print_msg(PRT_ERR, nomemory);
		return(UNKNOWN);
	}

	strcat(*string,tmpstr);

	return strlen(tmpstr);
}

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

static char *html_conv(char *string)
{
	static char  RetCode[BUFSIZ];
	char *ptr = RetCode;
	int   i;
	int empty = 1;

	if (string == NULL)
		return NULL;

	do
	{
		for(i = 0; htmlconv[i][0][0] != '\0'; i++)
		{
			if (htmlconv[i][0][0] == *string)
			{
				strcpy(ptr,htmlconv[i][1]);
				while (*ptr != '\0') ptr++;
				break;
			}
		}

		if (htmlconv[i][0][0] == '\0')
			*ptr++ = *string;

		if (*string != '\0' && !isspace(*string))
			empty = 0;
	}
	while(*string++ != '\0');

	if (empty)
		return H_EMPTY;

	return RetCode;
}

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

static char *set_byte_string(int flag, double Bytes)
{
	static char string[4][20];
	static int num = 0;
	int  factor = 1;
	char prefix = ' ';


	num = (num+1)%4;

	if (!Bytes)
	{
		if (flag & GET_BPS)
			strcpy(string[num],"              ");
		else
			strcpy(string[num],"            ");
	}
	else
	{
		if (Bytes >= 9999999999.0)
		{
			factor = 1073741824;
			prefix = 'G';
		}
		else
		if (Bytes >= 9999999)
		{
			factor = 1048576;
			prefix = 'M';
		}
		else
		if (Bytes >= 9999)
		{
			factor = 1024;
			prefix = 'k';
		}

		sprintf(string[num],"%c=%s %cB%s",flag&GET_IN?'I':'O',double2str(Bytes/factor,7,2,0),prefix,flag&GET_BPS?"/s":"");
	}

	return string[num];
}

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

static int set_col_size(void)
{
	one_call *tmp_call;
	int size = 0;
	int i = 0;

	if ((tmp_call = (one_call*) calloc(1,sizeof(one_call))) == NULL)
	{
		print_msg(PRT_ERR, nomemory);
                return(UNKNOWN);
	}

	print_line(F_COUNT_ONLY,tmp_call,0,NULL);

        while(colsize[i] != UNKNOWN)
		if (html)
			printf(H_BODY_HEADER2,colsize[i++]);
		else
			size += colsize[i++];

	free(tmp_call);
	return size;
}

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

static int get_format_size(void)
{
	int i = 0;
	prt_fmt** fmt = get_format(NULL);

	if (fmt == NULL)
		return 0;

	while(*fmt++ != NULL) i++;

	return i;
}

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

static char *overlap_string(char *s1, char *s2)
{
	int i = 0;

	if (s1 == NULL || s2 == NULL)
		return s1;

	while(s1[i] != '\0' && s2[i] != '\0')
	{
		if (isspace(s1[i]))
			s1[i] = s2[i];

		i++;
	}

	return s1;
}

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

static char* fill_spaces(char *string)
{
	static char RetCode[256];
	char *Ptr = RetCode;

	while (*string != '\0')
	{
		*Ptr++ = ' ';
		string++;
	}

	*Ptr = '\0';

	return RetCode;
}

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

static void free_format(prt_fmt** ptr)
{
	prt_fmt** ptr2 = ptr;


	if (ptr == NULL)
		return;

	while(*ptr != NULL)
	{
		free((*ptr)->string);
		free((*ptr)->range);
		free((*ptr++));
	}

	free(ptr2);
	free(colsize);
	colsize = NULL;
}

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

static prt_fmt** get_format(const char *format)
{
	static prt_fmt **RetCode = NULL;
	prt_fmt *fmt = NULL;
	char    *Ptr = NULL;
	char    *string = NULL;
	char    *start = NULL;
	char    *End = NULL;
	char     Range[20] = "";
	int      num;
	char     Type;


	if (format == NULL)
		return RetCode;

	free_format(RetCode);
	RetCode = NULL;

	if ((End    = (char*) alloca(sizeof(char)*(strlen(format)+1))) == NULL ||
	    (string = (char*) alloca(sizeof(char)*(strlen(format)+1))) == NULL   )
	{
		print_msg(PRT_ERR, nomemory);
		return NULL;
	}

	Ptr = start = strcpy(string,format);

	do
	{
		if (*Ptr == C_BEGIN_FMT)
		{
			if (Ptr[1] != C_BEGIN_FMT)
			{
				if (Ptr != start)
				{
					if ((fmt = (prt_fmt*) calloc(1,sizeof(prt_fmt))) == NULL)
					{
						print_msg(PRT_ERR, nomemory);
						delete_element(&RetCode,0);
						return NULL;
					}

					*Ptr = '\0';
					fmt->string= strdup(start);
					fmt->type  = FMT_STR;
					append_element(&RetCode,fmt);
				}

				*Range = *End = '\0';
				if ((num = sscanf(Ptr+1,"%[^a-zA-Z]%c%[^\n]",Range,&Type,End)) > 1 ||
				    (num = sscanf(Ptr+1,"%c%[^\n]",&Type,End))                 > 0   )
				{
					if (!isalpha(Type))
    				print_msg(PRT_ERR, "isdnrep: warning: invalid token in format type `%c'!\n",Type);

					if ((fmt = (prt_fmt*) calloc(1,sizeof(prt_fmt))) == NULL)
					{
						print_msg(PRT_ERR, nomemory);
						delete_element(&RetCode,0);
						return NULL;
					}

					switch (Type)
					{
						case 'C': read_path |= F_VBOX;
						          break;
						case 'G': read_path |= F_FAX;
						          break;
						default : break;
					}

					fmt->s_type= Type;
					fmt->range = strdup(Range);
					fmt->type  = FMT_FMT;

					append_element(&RetCode,fmt);
					*Range = '\0';

					if (*End != '\0')
						Ptr = start = strcpy(string,End);
					else
						Ptr = start = "";
				}
				else
				{
    			print_msg(PRT_ERR, "isdnrep: error: invalid token in format string `%s'!\n",format);
    			return NULL;
    		}
			}
			else
			{
				memmove(Ptr,Ptr+1,strlen(Ptr));
				Ptr++;
			}
		}
		else
			Ptr++;
	}
	while(*Ptr != '\0');

	if (Ptr != start)
	{
		if ((fmt = (prt_fmt*) calloc(1,sizeof(prt_fmt))) == NULL)
		{
			print_msg(PRT_ERR, nomemory);
			delete_element(&RetCode,0);
			return NULL;
		}

		fmt->string= strdup(start);
		fmt->type  = FMT_STR;
		append_element(&RetCode,fmt);
	}

	return RetCode;
}

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

static int print_entries(one_call *cur_call, double unit, int *nx, char *myname)
{
  register int i, zone, computed = 0;


  if (1 /* cur_call->dir == DIALOUT */) {

    zone = (cur_call->zone >= 0) ? cur_call->zone : UNKNOWNZONE;

		  zones[zone] += cur_call->eh;
    zones_dm[zone] += cur_call->pay;

		  if (cur_call->duration > 0)
		    zones_dur[zone] += cur_call->duration;

    zones_usage[zone] += 1;

#if DEBUG
    if (zone == UNKNOWNZONE) {
      assert(strlen(unknownzones) + 4 < sizeof(unknownzones));
      if (*unknownzones)
        strcat(unknownzones, ", ");

			assert(strlen(unknownzones) + strlen(cur_call->num[1]) < sizeof(unknownzones));
      strcat(unknownzones, cur_call->num[1]);
                } /* if */
#endif

    add_one_call(computed ? &day_com_sum : &day_sum, cur_call, unit);

		if (cur_call->dir) {
      if (nx[CALLING] == UNKNOWN) {
	known[knowns - 1]->usage[DIALIN]++;
	known[knowns - 1]->ibytes[DIALIN] += cur_call->ibytes;
	known[knowns - 1]->obytes[DIALIN] += cur_call->obytes;

				if (cur_call->duration > 0)
					known[knowns-1]->dur[DIALIN] += cur_call->duration;
			}
      else {
				known[nx[CALLING]]->usage[cur_call->dir]++;
				known[nx[CALLING]]->dur[cur_call->dir] += cur_call->duration;
				known[nx[CALLING]]->ibytes[DIALIN] += cur_call->ibytes;
				known[nx[CALLING]]->obytes[DIALIN] += cur_call->obytes;
      } /* else */
		}
		else {
      usage_provider[cur_call->provider]++;

      if ((cur_call->cause == 0x22) || /* No circuit/channel available */
          (cur_call->cause == 0x2a))   /* Switching equipment congestion */
        provider_failed[cur_call->provider]++;

      duration_provider[cur_call->provider] += cur_call->duration;
      pay_provider[cur_call->provider] += cur_call->pay;

      if (nx[CALLED] == UNKNOWN) {
				known[knowns-1]->eh += cur_call->eh;
      	known[knowns-1]->pay += cur_call->pay;
				known[knowns-1]->ibytes[DIALOUT] += cur_call->ibytes;
				known[knowns-1]->obytes[DIALOUT] += cur_call->obytes;
				known[knowns-1]->usage[DIALOUT]++;

				if (cur_call->duration > 0)
					known[knowns-1]->dur[DIALOUT] += cur_call->duration;
			}
			else {
				known[nx[CALLED]]->eh += cur_call->eh;
      	known[nx[CALLED]]->pay += cur_call->pay;
				known[nx[CALLED]]->usage[cur_call->dir]++;
				known[nx[CALLED]]->dur[cur_call->dir] += cur_call->duration;
				known[nx[CALLED]]->ibytes[DIALOUT] += cur_call->ibytes;
				known[nx[CALLED]]->obytes[DIALOUT] += cur_call->obytes;
			} /* if */
		} /* else */
	}
  else
    add_one_call(computed ? &day_com_sum : &day_sum, cur_call, unit);

      	 	      		if (cur_call->dir == DIALOUT) {
    int first_found = UNKNOWN;

                                  for (i = 0; i < mymsns; i++) {
                                    if (!n_match(known[i]->num, cur_call->num[0], cur_call->version)) {
                                      /* Ermitteln der ersten passenden MSN (lt. README) */
        if (first_found == UNKNOWN)
                                        first_found = i;
          /* exakte Uebereinstimmung inkl. SI */
                                      if (known[i]->si == cur_call->si) {
          msn_sum[i] += cur_call->pay;
                                      usage_sum[i]++;
                                      dur_sum[i] += cur_call->duration;
                                      break;
                                    } /* if */
                                    } /* if */
                                  } /* for */

                                  if (i == mymsns) {
                                    /* keine exakte Uebereinstimmung, aber ohne SI */
      if (first_found != UNKNOWN)
                                      i = first_found;
      msn_sum[i] += cur_call->pay;
                                    usage_sum[i]++;
                                    dur_sum[i] += cur_call->duration;
                                  } /* if */

      	 	      		} /* if */

  if (bill) {
    if (cur_call->dir == DIALOUT)
    bprint(cur_call);
  }
  else if(!summary)
    print_line(F_BODY_LINE,cur_call,computed,NULL);

  return(0);
} /* print_entries */

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

static int print_header(int lday)
{
  sum_calls tmp_sum;
	time_t now;


	if (lday == UNKNOWN) {   /* first day, print overall header, no day sum */
		time(&now);

		if (bill)
			print_line2(F_1ST_LINE, "Ihre Verbindungen im einzelnen  -  %s", ctime(&now));
		else
			print_line2(F_1ST_LINE,"I S D N  Connection Report  -  %s", ctime(&now));

		if (recalc.mode)
		{
			char s[80];
			char *prov, *vbn;
			if (recalc.mode == '-')		  
				snprintf(s, 80, "Connection fees recomputed");
			else if (tolower(recalc.mode) == 'b')
				snprintf(s, 80, "Connection fees recomputed using the cheapest "
                 "of all %sproviders", (recalc.mode=='b')?"booked ":"");
			else
			{
				prov = getProvider(recalc.prefix);
				vbn = getProviderVBN(recalc.prefix);
				if (strstr(prov, vbn))   /* don't show vbn twice */
					snprintf(s, 80, "Connection fees recomputed for provider %s",
					         prov);
				else
					snprintf(s, 80, "Connection fees recomputed for provider %s %s",
				         vbn, prov);
			}
			print_line2(F_TEXT_LINE, "%s", s);
		}

		/* TODO: consistent terminology. fees vs. costs (tobiasb|200407) */
		if (modcost.mode)
			print_line2(F_TEXT_LINE, "All displayed costs have been %s by %s.",
			            modcost.mode == 2 ? "divided" : "multiplied", modcost.numstr);
		
	}
	else
	{
		if (summary >= 2)
			return 0;
		strich(1);
		print_sum_calls(&day_sum,0);

		if (day_com_sum.eh)
		{
			print_sum_calls(&day_com_sum,1);

			clear_sum(&tmp_sum);
			add_sum_calls(&tmp_sum,&day_sum);
			add_sum_calls(&tmp_sum,&day_com_sum);
			strich(1);
			print_sum_calls(&tmp_sum,0);
		}
		else
			printf("\n\n");

		add_sum_calls(&all_sum,&day_sum);
		clear_sum(&day_sum);

		add_sum_calls(&all_com_sum,&day_com_sum);
		clear_sum(&day_com_sum);

		print_line2(F_BODY_BOTTOM2,"");
	} /* if */

	h_percent = 100.0;
	h_table_color = H_TABLE_COLOR1;
	print_line2(F_BODY_HEADER,"");
	if (summary < 2) 
		print_line2(F_BODY_HEADERL,"%s %s", get_time_value(0,NULL,GET_DATE),
	                                      get_time_value(0,NULL,GET_YEAR));
	else   /* show timespam "from .. to" for options -SS */
	{
		char s[80];
		s[0]=0;
		strcat(s, get_time_value(0, NULL, GET_DATE));
		strcat(s, " ");
		strcat(s, get_time_value(0, NULL, GET_YEAR));
		strcat(s, " .. ");
		get_time_value(endtime, &lday, SET_TIME);
		strcat(s, get_time_value(0, NULL, GET_DATE));
		strcat(s, " ");
		strcat(s, get_time_value(0, NULL, GET_YEAR));
		print_line2(F_BODY_HEADERL, "%s", s);
	}
		
	return 0;
}

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

static char *get_time_value(time_t t, int *day, int flag)
{
	static char time_string[4][18] = {"","",""};
        static int  oldday = UNKNOWN;
        static int  oldyear = UNKNOWN;
	struct tm *tm;


	if (flag & SET_TIME)
	{
  	tm = localtime(&t);

		/*sprintf(time_string[0],"%d:%d:%d",tm->tm_hour,tm->tm_min,tm->tm_sec);*/
		strftime(time_string[0],17,"%X",tm);

		if (oldday != tm->tm_yday || oldyear != tm->tm_year)
		{
	 		*day = tm->tm_mday;

			Tarif96 = tm->tm_year > 95;

		/* Ab Juli '96 gibt's noch mal eine Senkung in den Zonen 3 und 4
		   genaue Preise sind jedoch noch nicht bekannt. FIX-ME */

			Tarif962 = (Tarif96 && (tm->tm_mon > 5)) || (tm->tm_year > 96);

			strftime(time_string[1],17,"%a %b %d",tm);
			strftime(time_string[2],17,"%x",tm);
			oldday  = tm->tm_yday;

			if (oldyear != tm->tm_year)
			{
				sprintf(time_string[3],"%d",1900+tm->tm_year);
				oldyear = tm->tm_year;
			}
		}
	}

	if (flag & GET_TIME)
		return time_string[0];
	else
	if (flag & GET_DATE)
		return time_string[1];
	else
	if (flag & GET_DATE2)
		return time_string[2];
	else
	if (flag & GET_YEAR)
		return time_string[3];

	return NULL;

}

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

static int set_alias(one_call *cur_call, int *nx, char *myname)
{
	auto int n, cc, i, j;
	auto int hit;


	for (n = CALLING; n <= CALLED; n++) {
		nx[n] = UNKNOWN;
		hit = 0;

		if (!*(cur_call->num[n])) {
//			if (!numbers)
			{
				cur_call->num[n][0] = C_UNKNOWN;
				cur_call->num[n][1] = '\0';
			}
			/* Wenn keine Nummer, dann Name "UNKNOWN" */

			hit++;
		} else {
			/* In der folg. Schleife werden Nummern durch Namen ersetzt */
			cc = 0;

			for (j = 0; ((j < 2) && !cc); j++) {
				for (i = 0; i < knowns; i++) {

					if (cur_call->version[0] != '\0')
					{
						if (!strcmp(cur_call->version,LOG_VERSION_2) ||
						    !strcmp(cur_call->version,LOG_VERSION_3) ||
							  !strcmp(cur_call->version,LOG_VERSION_4) ||
						    !strcmp(cur_call->version,LOG_VERSION) )
							cc = ((known[i]->si == cur_call->si) || j) &&
							     !n_match(known[i]->num, cur_call->num[n], cur_call->version);
					}
					else
					{
						if (*cur_call->num[n] != '0') {

							/* Alte Syntax der "isdn.log" : Ohne vorlaufene "0" */
							cc = ((known[i]->si == cur_call->si) || j) &&
							     !match(known[i]->num+1, cur_call->num[n], 0);

							if (!cc) {
								/* Ganz alte Syntax der "isdn.log" : Ohne Vorwahl */
								cc = ((known[i]->si == cur_call->si) || j) &&
							     !n_match(known[i]->num, cur_call->num[n], LOG_VERSION_1);
							} /* if */
						}
					}

					if (cc) {

						Strncpy(cur_call->who[n], known[i]->who,RETSIZE);

 						nx[n] = i;
						hit++;
						break;
					} /* if */
				} /* for */
			} /* for */

			/* In der naechsten Schleife werden die unbekannten Nummern
			   registriert */
			if (!hit && seeunknowns) {
				for (i = 0; i < unknowns; i++)
					if (!strcmp(unknown[i].num, cur_call->num[n])) {
						hit++;
						break;
					} /* if */

				strcpy(unknown[i].num, cur_call->num[n]);
				strcpy(unknown[i].mynum, cur_call->num[1 - n]);
				unknown[i].si1 = cur_call->si1;
				unknown[i].called = !n;
				unknown[i].connect[unknown[i].connects] = cur_call->t;
				unknown[i].cause = cur_call->cause;

				/* ACHTUNG: MAXCONNECTS und MAXUNKNOWN sollten unbedingt groesser sein ! */
				if (unknown[i].connects + 1 < MAXCONNECTS)
					unknown[i].connects++;
				else
					print_msg(PRT_ERR, "%s: WARNING: too many unknown connections from %s\n", myname, unknown[i].num);

				if (!hit) {
					if (unknowns < MAXUNKNOWN)
						unknowns++;
					else
						print_msg(PRT_ERR, "%s: WARNING: too many unknown numbers\n", myname);
				} /* if */
			} /* if */
		} /* else */
	} /* for */

	return 0;
}

static void repair(one_call *cur_call)
{
  RATE Rate, lcrRate;
  TELNUM srcnum, destnum;
	int have_z;    /* Zone Rate.z from getZone valid or not */
	int best_zone; /* =Rate.z if valid or =Rate.zone if Rate.z invalid */
  int is_defsrc = 0; /* default source number used or not */

  if (cur_call->dir == DIALOUT && !*cur_call->num[CALLING] && defsrc.mode > 0) {
    Strncpy(cur_call->num[CALLING], defsrc.number, NUMSIZE);
    ++is_defsrc;
  }
  if (*cur_call->num[CALLING]) {
    normalizeNumber(cur_call->num[CALLING],&srcnum,TN_ALL);
    strcpy(cur_call->sarea[CALLING], srcnum.sarea);
  }
  else
    *cur_call->sarea[CALLING] = 0;
  if (*cur_call->num[CALLED]) {
    destnum.nprovider = cur_call->provider;
    Strncpy(destnum.provider,getProvider(cur_call->provider), TN_MAX_PROVIDER_LEN);
    normalizeNumber(cur_call->num[CALLED], &destnum, TN_NO_PROVIDER);
    strcpy(cur_call->sarea[CALLED], destnum.sarea);
  }
  else
    *cur_call->sarea[CALLED] = 0;

  if ((cur_call->dir == DIALOUT) &&
      (cur_call->duration > 0) &&
      *cur_call->num[CALLED]
     ) {


    call[0].connect = cur_call->t;
    call[0].disconnect = cur_call->t + cur_call->duration;
    call[0].intern[CALLED] = strlen(cur_call->num[CALLED]) < interns0;
    call[0].provider = cur_call->provider;
    call[0].aoce = cur_call->eh;
    call[0].dialin = 0;
    strcpy(call[0].num[CALLED], cur_call->num[CALLED]);
    strcpy(call[0].onum[CALLED], cur_call->num[CALLED]);

    call[0].sondernummer[CALLED] = destnum.ncountry==0;

    clearRate(&Rate);
    Rate.src[0] = srcnum.country;
    Rate.src[1] = srcnum.area;
    Rate.src[2] = "";
    Rate.dst[0] = destnum.country;
    Rate.dst[1] = destnum.area;
    Rate.dst[2] = destnum.msn;
    Rate.start = cur_call->t;
    Rate.now = call[0].disconnect;
    Rate.prefix = cur_call->provider;

		if (recalc.mode) {
			recalc.count++;
			if (recalc.mode == 'p' || recalc.mode == 'v')
				Rate.prefix = recalc.prefix;
			if (!getRate(&Rate,0)) {
				if ( (tolower(recalc.mode) == 'b') &&  /* find new provider with lcr */
				     !getPrsel(cur_call->num[CALLED], &Rate.prefix, NULL, NULL) &&
				     getLeastCost (&Rate, &lcrRate, recalc.mode=='b', Rate.prefix)
				     != UNKNOWN &&
				     lcrRate.Charge < Rate.Charge && 
				     !strcmp(lcrRate.dst[1], Rate.dst[1]) /* no new special number */
           ) {
					recalc.cheaper++;
					cur_call->pay = lcrRate.Charge;
					cur_call->provider = lcrRate.prefix;
				}
				else {  /* lcr failed (error or no cheaper provider) or not requested */
					cur_call->pay = Rate.Charge;
					cur_call->provider = Rate.prefix;
				}
			}
			else {  /* fall back to logged provider for zone name etc */
				recalc.unknown++;
				Rate.prefix = cur_call->provider;
				Rate._zone = Rate._area = Rate.zone = UNKNOWN;
			}
		}

    if (!getRate(&Rate,0)) {
#if DEBUG
			fprintf(stderr,"Rate.zone=%i Rate.z=%i Rate.domestic=%i call.[0]so..r[CALLED]=%i\n", Rate.zone, Rate.z, Rate.domestic, call[0].sondernummer[CALLED]);
#endif
			have_z = (Rate.domestic && !call[0].sondernummer[CALLED] && Rate.z>0);
			best_zone = have_z ? Rate.z : Rate.zone;
      if(strcmp(cur_call->version, LOG_VERSION)) {
				/* LOG_VERSION is "3.2" since 1999-01-24 or isdnlog-3.33, earlier
				 * calls are recalculated.  At least for Germany this makes nowadays
				 * (2003-02) no sense, since rates _have_ changed and the Euro was
				 * introduced on 2002-01-01. */	
        cur_call->pay = Rate.Charge; /* Fixme: is that ok, propably rates have changed */
	      cur_call->zone = (best_zone < MAXZONES) ? best_zone : UNKNOWN;
			}
			else {
				/* A patched isdnlog-4.64 or later versions may have written the zone
				 * in the last field of the log entry.  In this case, keep this zone.
				 * isdnlog-3.32 and earlier did so too, but with an older LOG_VERSION
				 * which triggered recalculation above.
				 * As the zone summation is carried out over all providers, using
				 * the zone number from the rate-file (Rate.zone) seems a little bit
				 * closer to the desired result as the internal zone index (Rate._zone)
				 */
				if (cur_call->zone == UNKNOWN)
					cur_call->zone = (best_zone < MAXZONES) ? best_zone : UNKNOWN;
			}
			/* Next difficulty: We need a zone name for the zone summation. These
			 * names may differ between different providers.  Even with a recorded
			 * zone from the logfile, we need a name.
			 * As before duplicated strings (strdup) are not released 
			 * TODO: Use the zone names from the preselected provider with
			 * priority instead of the current use of the first `best' name. */
			if (cur_call->zone != UNKNOWN) {	/* valid index for zones_-arrays */
				int *zflags=&(zones_src[cur_call->zone].flags);

				if ( cur_call->provider>0 && zones_src[cur_call->zone].first_prov>0
				     && zones_src[cur_call->zone].first_prov != cur_call->provider)
					*zflags |= FZN_MPROV; /* zone is used at different providers */ 

				if (!zones_names[cur_call->zone]) { /* no zone name yet */
					/* get a name in any circumstands */
					if (Rate.Zone) {
						zones_names[cur_call->zone] = strdup(Rate.Zone);
						*zflags |= FZN_KNOWN;
						if (Rate.zone!=cur_call->zone && !have_z && Rate.z!=cur_call->zone)
							/* zone number from logfile does not match zone from rate file */
							*zflags |= FZN_UNSURE;
					}
					else {
						zones_names[cur_call->zone] = strdup("??"); /* old behaviour */
					}
					zones_src[cur_call->zone].first_prov = cur_call->provider;
				}
				else { /* got already a zone name */
					if ( !(*zflags & FZN_KNOWN) && Rate.Zone ) {
						/* get a name better than ?? */
						zones_names[cur_call->zone] = strdup(Rate.Zone);
						*zflags |= FZN_KNOWN;
						if (Rate.zone!=cur_call->zone && !have_z && Rate.z!=cur_call->zone)
							*zflags |= FZN_UNSURE;
					}
					else if ( *zflags & FZN_UNSURE && Rate.Zone &&
					          ( Rate.zone==cur_call->zone ||
                      (have_z && Rate.z==cur_call->zone) ) ) {
						/* get a name without zone number difference between log and rate */
						zones_names[cur_call->zone] = strdup(Rate.Zone);
						zones_src[cur_call->zone].flags &= ~FZN_UNSURE;
					}
					else {
						/* zone name not replaced,  Rate.Zone could be NULL */
						if ( (*zflags & (FZN_KNOWN|FZN_UNSURE|FZN_MNAME|FZN_MPROV)) == (FZN_KNOWN|FZN_MPROV)
								 && (!Rate.Zone||strcmp(zones_names[cur_call->zone], Rate.Zone)) )
							*zflags |= FZN_MNAME; /* diff. names at diff. providers */
					} 
				} /* if zone name first time */
			} /* if valid zones_.. index */
    } /* if getRate sucessful */
  } /* if DIALOUT && duration > && num[CALLED] */
  if (is_defsrc && defsrc.mode == 2) /* do not display default source number */
    *cur_call->num[CALLING] = 0;
} /* repair */

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

static int set_caller_infos(one_call *cur_call, char *string, time_t from)
{
	int      rc;
  register int    i = 0, adapt = 0;
  auto     char **array;
  auto     double dur1 = 0.0, dur2 = 0.0;


	array = string_to_array(string);

	if (array[5] == NULL)
    return(UNKNOWN);

	cur_call->t = atol(array[5]);

	rc = 0;
	if (delentries && cur_call->t < endtime)
		rc = UNKNOWN; /* return this, but first process the record */
	else if (timearea && (cur_call->t < begintime || cur_call->t > endtime))
    return(UNKNOWN);
	else
		if (cur_call->t < from)
      return(UNKNOWN);

	cur_call->eh = 0;
  cur_call->dir = UNKNOWN;
  cur_call->cause = UNKNOWN;
	cur_call->ibytes = cur_call->obytes = 0L;
  cur_call->pay    = 0.0;
	cur_call->version[0] = '\0';
	cur_call->si = cur_call->si1 = 0;
	cur_call->dir = DIALOUT;
	cur_call->who[0][0] = '\0';
	cur_call->who[1][0] = '\0';
  cur_call->provider = UNDEFINED;
  cur_call->zone = UNDEFINED;

	for (i = 1; array[i] != NULL; i++)
	{
		switch (i)
		{
			case  0 : cur_call->t = atom(array[i]);
			          break;
			case  1 : strcpy(cur_call->num[0], Kill_Blanks(array[i]));
			          break;
			case  2 : strcpy(cur_call->num[1], Kill_Blanks(array[i]));
								/* Korrektur der falschen Eintraege aus den ersten Januar-Tagen 1998 */
								if (!memcmp(cur_call->num[1], "+491019", 7)) {
									cur_call->provider = 19;
									memmove(cur_call->num[1] + 3, cur_call->num[1] + 8, strlen(cur_call->num[1]) - 7);
									adapt++;
								}
								else if (!memcmp(cur_call->num[1], "+491033", 7)) {
									cur_call->provider = 33;
									memmove(cur_call->num[1] + 3, cur_call->num[1] + 8, strlen(cur_call->num[1]) - 7);
									adapt++;
								}
								else if (!memcmp(cur_call->num[1], "+491070", 7)) {
									cur_call->provider = 70;
									memmove(cur_call->num[1] + 3, cur_call->num[1] + 8, strlen(cur_call->num[1]) - 7);
									adapt++;
								} /* else */
								if (adapt)
									strcpy(cur_call->version, LOG_VERSION_4);

								break;
			case  3 : dur1 = cur_call->duration = strtod(array[i],NULL);
								if (dur1 < 0)  /* wrong entry on some incoming voice calls */
									dur1 = cur_call->duration = 0;
			          break;
			case  4 : dur2 = cur_call->duration = strtod(array[i],NULL)/100;
			          break;
			case  5 : /*cur_call->t = atol(array[i]);*/
			          break;
			case  6 : cur_call->eh = atoi(array[i]);
			          break;
			case  7 : cur_call->dir = (*array[i] == 'I') ? DIALIN : DIALOUT;
			          break;
			case  8 : cur_call->cause = atoi(array[i]);
			          break;
			case  9 : cur_call->ibytes = atol(array[i]);
			          break;
			case  10: cur_call->obytes = atol(array[i]);
			          break;
			case  11: if (!adapt)
			            strcpy(cur_call->version,array[i]);
			          break;
			case  12: cur_call->si = atoi(array[i]);
			          break;
			case  13: cur_call->si1 = atoi(array[i]);
			          break;
			case  14: cur_call->currency_factor = atof(array[i]);
			          break;
			case  15: strncpy(cur_call->currency, array[i], 3);
			          break;
      case  16: cur_call->pay = atof(array[i]);
								/* Korrektur der falschen Eintr„ge vor dem 16-Jan-99 */
								if (cur_call->pay == -1.0)
									cur_call->pay = 0.0;
			          break;
			case  17: if (!adapt) {
			      	    cur_call->provider = atoi(array[i]);
									/* Korrektur der falschen Eintrage bis zum 16-Jan-99 */
									if (cur_call->provider <= UNKNOWN)
										cur_call->provider = preselect;
									/* -lt- provider-# may change during time */
									cur_call->provider = pnum2prefix(cur_call->provider,cur_call->t);
									/* provider from logfile unknown in ratefile or rateconf
									 * --> default to preselected provider */
									if (cur_call->provider == UNKNOWN)
										cur_call->provider = pnum2prefix(preselect, cur_call->t);
								} /* if */
								break;

			/* Seit 21-Jan-99 steht die Zone im Logfile */
			case  18: cur_call->zone = atoi(array[i]);
			      	  break;

			default : print_msg(PRT_ERR, "isdnrep: unknown element found `%s'!\n",array[i]);
			          break;
		}
	}

	if (i < 3)
		return(UNKNOWN);

  /* alle Eintraege vor dem 21-Jan-99 ergaenzen:
       - zone fehlte
       - pay war falsch
  */

  {
#if DEBUG
    auto char yy[BUFSIZ];
#endif

    if (abs((int)dur1 - (int)dur2) > 1) {
#if DEBUG
      sprintf(yy, "\nWARNING: Wrong duration! dur1=%f, dur2=%f, durdiff=%f\n",
        dur1, dur2, dur1 - dur2);
      printf( yy);
#endif
      cur_call->duration = dur1;
    }

  }

	/* repair() recalculates the connection cost for entries with LOG_VERSION
   * older than the current and sets cur_call->zone for outgoing calls with
   * recorded one of -1 (=UNKNWON).  The necessary information is taken from
   * the current rate-file.  The -r and -U options are also handled there.
   */
  repair(cur_call);

	if (cur_call->pay != 0.0 && modcost.mode)
	{
		if (modcost.mode == 1)
			cur_call->pay *= modcost.number;
		else if (modcost.mode == 2)
			cur_call->pay /= modcost.number;
	}

  return(rc);
}


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

static char **string_to_array(char *string)
{
	static char *array[30];
	auto   char *ptr;
	auto   int   i = 0;

	memset(array, 0, sizeof(array));
	if ((ptr = strrchr(string,C_DELIM)) != NULL)
		*ptr = '\0';

	array[0] = string;

	while((string = strchr(string,C_DELIM)) != NULL)
	{
		*string++ = '\0';
		array[++i] = string;
	}

	array[++i] = NULL;
	return array;
}

/*****************************************************************************/
/* called for -p ... command line options */ 
int set_msnlist(char *String)
{
  int Cnt;
  int Value = 1;
  char *Ptr = String;


  if (ShowMSN)
    return 0;

  while((Ptr = strchr(Ptr,',')) != NULL)
  {
    Ptr++;
    Value++;
  }

  ShowMSN = (char**) calloc(Value+1,sizeof(char*));
  for(Cnt = 0; Cnt < Value; Cnt++)
    ShowMSN[Cnt] = (char*) calloc(NUMSIZE,sizeof(char));

  if (!ShowMSN)
  {
    print_msg(PRT_ERR, nomemory);
    exit(1);
  }

  if (*String == 'n')
  {
    ++String;
    invertnumbers++;
  }

  Cnt = 0;

  while(String)
  {

    if (*String == 'm')
    {
      Value = atoi(++String);

      if (Value > 0 && Value <= mymsns)
      {
        strcpy(ShowMSN[Cnt],known[Value-1]->num);
        Cnt++;
      }
      else
      {
        print_msg(PRT_ERR, "isdnrep: invalid MSN %d!\n", Cnt);
      }
    }
    else
      sscanf(String,"%[^,],",ShowMSN[Cnt++]);

    String = strchr(String,',');
    if (String) String++;
  }

  return 0;
}

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

static int show_msn(one_call *cur_call)
{
  int Cnt;

  for(Cnt = 0; ShowMSN[Cnt] != NULL; Cnt++)
    if (!num_match(ShowMSN[Cnt], cur_call->num[0]) ||
        !num_match(ShowMSN[Cnt]   , cur_call->num[1]))
      return !invertnumbers;
    else
    if (!strcmp(ShowMSN[Cnt],"0"))
      return !invertnumbers;

  return invertnumbers;
}

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

static char *print_currency(double money, int computed)
{
  static char RetCode[256];


	sprintf(RetCode,"%s %s%c", double2str(money, 8, 4, 0), currency, computed ? '*' : ' ');
  return RetCode;
}

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

static int print_sum_calls(sum_calls *s, int computed)
{
  static char String[256];
  one_call *tmp_call;
  int RetCode;


  if (bill)
    printf( "%*s%s\n", 36, "", print_currency(s->pay, 0));
  else {
	if ((tmp_call = (one_call*) calloc(1,sizeof(one_call))) == NULL)
	{
		print_msg(PRT_ERR, nomemory);
                return(UNKNOWN);
	}

	tmp_call->eh = s->eh;
        tmp_call->pay    = s->pay;
	tmp_call->obytes = s->obytes;
	tmp_call->ibytes = s->ibytes;

  sprintf(String,"%3d IN=%s, %3d OUT=%s, %3d failed",
    s->in,
    double2clock(s->din),
    s->out,
    double2clock(s->dout),
    s->err);

  RetCode = print_line(F_BODY_BOTTOM1,tmp_call,computed,String);
	free(tmp_call);

  } /* else */

  return RetCode;
}

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

static int add_one_call(sum_calls *s1, one_call *s2, double units)
{
  if (s2->dir == DIALIN)
  {
    s1->in++;
    s1->din += s2->duration > 0?s2->duration:0;
  }
  else
  {
    s1->out++;
    s1->dout   += s2->duration > 0?s2->duration:0;
    s1->eh     += s2->eh;
    s1->pay    += s2->pay;
  }

  s1->ibytes += s2->ibytes;
  s1->obytes += s2->obytes;
  return 0;
}

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

static int clear_sum(sum_calls *s1)
{
  s1->in     = 0;
  s1->out    = 0;
  s1->eh     = 0;
  s1->err    = 0;
  s1->din    = 0;
  s1->dout   = 0;
  s1->pay    = 0;
  s1->ibytes = 0L;
  s1->obytes = 0L;

  return 0;
}

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

static int add_sum_calls(sum_calls *s1, sum_calls *s2)
{
  s1->in     += s2->in;
  s1->out    += s2->out;
  s1->eh     += s2->eh;
  s1->err    += s2->err;
  s1->din    += s2->din;
  s1->dout   += s2->dout;
  s1->pay    += s2->pay;
  s1->ibytes += s2->ibytes;
  s1->obytes += s2->obytes;
  return 0;
}

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

static void strich(int type)
{
	if (html)
	{
		switch (type) {
			case 3 :
			case 1 : printf(H_LINE,get_format_size(),1);
			         break;
			case 2 : printf(H_LINE,get_format_size(),3);
			         break;
                        default: print_msg(PRT_ERR, "isdnrep: internal error: invalid line flag!\n");
			         break;
		} /* switch */
  }
  else
	{
		char *string;
		int size = set_col_size();

		if ((string = (char*) calloc(size+1,sizeof(char))) == NULL)
    {
      print_msg(PRT_ERR, nomemory);
      return;
    }

		while (size>0)
			string[--size] = type==2?'=':'-';

		printf("%s\n",string);

		free(string);
  }
} /* strich */

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

static int n_match(char *Pattern, char* Number, char* version)
{
        int RetCode = UNKNOWN;
	char s[SHORT_STRING_SIZE];

        if (!strcmp(version,LOG_VERSION_3) || !strcmp(version,LOG_VERSION_4) || !strcmp(version,LOG_VERSION))
	{
		RetCode = num_match(Pattern,Number);
	}
	else
	if (!strcmp(version,LOG_VERSION_2))
	{
		strcpy(s,expand_number(Number));
		RetCode = num_match(Pattern,s);
	}
	else
	if (!strcmp(version,LOG_VERSION_1))
	{
		if ((RetCode = match(Pattern, Number,0)) != 0            &&
		    !strncmp(Pattern,areaprefix,strlen(areaprefix))  )
		{
			sprintf(s,"*%s%s",myarea/*+strlen(areaprefix)*/,Pattern);
			RetCode = match(s,Number,0);
		}
	}
	else
		print_msg(PRT_ERR, "isdnrep: unknown version of logfile entries!\n");

	return RetCode;
}

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

static int html_header(void)
{
	printf("Content-Type: text/html\n\n");
	printf("<HTML>\n");
	printf("<BODY bgcolor=%s>\n",H_BG_COLOR);

	return 0;
}

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

static int html_bottom(char *_progname, char *start, char *stop)
{
	int value;
	int value2;
	char *progname = strdup(_progname);
	char *ptr      = strrchr(progname,'.');


	if (ptr)
		*ptr = '\0';

/*
	printf(H_FORM_ON,_myname);
	printf(H_FORM_DAY,"Date:","-t",40,10);
	printf(H_FORM_OFF);
*/

	if (!incomingonly)
	{
		value = incomingonly;
		value2 = outgoingonly;
		incomingonly++;
		outgoingonly = 0;
		if ((ptr = get_time_string(_begintime,endtime,0,0)) != NULL)
			printf(H_LINK_DAY,_myname,ptr,"incoming only");
		incomingonly = value;
		outgoingonly = value2;
	}

	if (!outgoingonly)
	{
		value = incomingonly;
		value2 = outgoingonly;
		incomingonly = 0;
		outgoingonly++;
		if ((ptr = get_time_string(_begintime,endtime,0,0)) != NULL)
			printf(H_LINK_DAY,_myname,ptr,"outgoing only");
		incomingonly = value;
		outgoingonly = value2;
	}

	if (outgoingonly || incomingonly)
	{
		value = incomingonly;
		value2 = outgoingonly;
		incomingonly = outgoingonly = 0;
		if ((ptr = get_time_string(_begintime,endtime,0,0)) != NULL)
			printf(H_LINK_DAY,_myname,ptr,"all calls");
		incomingonly = value;
		outgoingonly = value2;
	}

	value = print_failed;
	print_failed = !print_failed;
	if ((ptr = get_time_string(_begintime,endtime,0,0)) != NULL)
		printf(H_LINK_DAY,_myname,ptr,value?"print_failed off":"print_failed on");
	print_failed = value;

	if ((ptr = get_time_string(_begintime,endtime,0,-1)) != NULL)
		printf(H_LINK_DAY,_myname,ptr,"previous month");

	if ((ptr = get_time_string(_begintime,endtime,-1,0)) != NULL)
		printf(H_LINK_DAY,_myname,ptr,"previous day");

	if ((ptr = get_time_string(_begintime,endtime,1,0)) != NULL)
		printf(H_LINK_DAY,_myname,ptr,"next day");

	if ((ptr = get_time_string(_begintime,endtime,0,1)) != NULL)
		printf(H_LINK_DAY,_myname,ptr,"next month");

	printf("\n<BR><H6>%s %s  %s</H6>\n",progname,VERSION,__DATE__);
	printf("\n</BODY>\n");
	printf("<HEAD><TITLE>%s %s\n",progname,print_diff_date(start,stop));
	printf("</TITLE>\n");
	printf("</HTML>\n");

	free(progname);
	return 0;
}

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

static char *print_diff_date(char *start, char *stop)
{
	static char RetCode[64];

	if (strcmp(start,stop))
		sprintf(RetCode,"%s .. %s",start,stop);
	else
		sprintf(RetCode,"%s",start);

	return RetCode;
}

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

static int get_file_list(void)
{
	if (read_path & F_VBOX)
		set_dir_entries(vboxpath,set_vbox_entry);

	if (read_path & F_FAX)
		set_dir_entries(mgettypath,set_mgetty_entry);

	qsort(file_root,file_root_member,sizeof(file_list*),Compare_files);
	return 0;
}

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

static int set_dir_entries(char *directory, int (*set_fct)(const char *, const char *))
{
	struct dirent *eptr;
	DIR *dptr;

	if (directory != NULL)
	{
		if ((dptr = opendir(directory)) != NULL)
		{
			while ((eptr = readdir(dptr)) != NULL)
			{
				if (eptr->d_name[0] != '.')
				{
					if (set_fct(directory,eptr->d_name))
                                                return(UNKNOWN);
				}
			}

			closedir(dptr);
		}
		else
		{
			print_msg(PRT_ERR, "isdnrep: can't open directory `%s': %s!\n", directory, strerror(errno));
                        return(UNKNOWN);
		}
	}

	return 0;
}

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

static int set_vbox_entry(const char *path, const char *file)
{
	struct tm tm;
	file_list *lptr = NULL;
	char string[PATH_MAX];
	int cnt;
	FILE *fp;
	vaheader_t ptr;



	sprintf(string,"%s%c%s",path,C_SLASH,file);

	if ((fp = fopen(string,"r")) == NULL)
	{
		print_msg(PRT_ERR, msg1, string, strerror(errno));
                return(UNKNOWN);
	}

	fread(&ptr,sizeof(vaheader_t),1,fp);
	fclose(fp);

	if (strncmp(ptr.magic,"VBOX",4))
	{
		/* Version 0.x and 1.x of vbox! */

		if ((cnt = sscanf(file,"%2d%2d%2d%2d%2d%2d",
	 		            &(tm.tm_year),
	 		            &(tm.tm_mon),
	 		            &(tm.tm_mday),
	 		            &(tm.tm_hour),
	 		            &(tm.tm_min),
	 		            &(tm.tm_sec))) != 6)
		{
			print_msg(PRT_ERR, "isdnrep: invalid file name `%s'!\n",file);
                        return(UNKNOWN);
		}

		if ((lptr = (file_list*) calloc(1,sizeof(file_list))) == NULL)
		{
			print_msg(PRT_ERR, nomemory);
                        return(UNKNOWN);
		}

	 	if (tm.tm_mday > 31)
	 	{
	 		int help = tm.tm_mday;
	 		tm.tm_mday = tm.tm_year;
	 		tm.tm_year = help;
	 	}

		tm.tm_mon--;
                tm.tm_isdst = UNKNOWN;

		lptr->name = strdup(file);
	 	lptr->time = mktime(&tm);
		lptr->type = C_VBOX;
		lptr->used = 0;
		lptr->version = 1;
		lptr->compression = 0;
	}
	else
	{
		/* Version 2.x of vbox! */

		if ((lptr = (file_list*) calloc(1,sizeof(file_list))) == NULL)
		{
			print_msg(PRT_ERR, nomemory);
                        return(UNKNOWN);
		}

		lptr->name = strdup(file);
	 	lptr->time = ntohl(ptr.time);
		lptr->type = C_VBOX;
		lptr->used = 0;
		lptr->version = 2;
		lptr->compression = ntohl(ptr.compression);
	}
/*
	else
	{
		print_msg(PRT_ERR, "Version %d of vbox is not implemented yet!\n",vboxversion);
		print_msg(PRT_ERR, "Invalid version %d of vbox!\n",vboxversion);
                return(UNKNOWN);
	}
*/

	return set_element_list(lptr);
}

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

static int set_mgetty_entry(const char *path, const char *file)
{
	file_list *lptr = NULL;
	char string[PATH_MAX];

	sprintf(string,"%s%c%s",path,C_SLASH,file);

	if (access(string,R_OK))
	{
		print_msg(PRT_ERR, msg1, string, strerror(errno));
                return(UNKNOWN);
	}

	if ((lptr = (file_list*) calloc(1,sizeof(file_list))) == NULL)
	{
		print_msg(PRT_ERR, nomemory);
                return(UNKNOWN);
	}

	lptr->name = strdup(file);

	strcpy(string,"0x3");
	strncpy(string+3,file+2,7);
	lptr->time = strtol(string,NIL,0);

	lptr->type = C_FAX;
	lptr->used = 0;
	lptr->version = 3;
	lptr->compression = 0;

	return set_element_list(lptr);
}

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

static int set_element_list(file_list* elem)
{
	if (file_root_size == file_root_member)
	{
		file_root_size += 10;
		if ((file_root = (file_list**) realloc(file_root,sizeof(file_list*)*file_root_size)) == NULL)
		{
			print_msg(PRT_ERR, nomemory);
                        return(UNKNOWN);
		}
	}

	file_root[file_root_member++] = elem;

	return 0;
}

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

static int Compare_files(const void *e1, const void *e2)
{
	if ((*(file_list**) e1)->time > (*(file_list**) e2)->time)
		return 1;
	else
	if ((*(file_list**) e1)->time < (*(file_list**) e2)->time)
                return(UNKNOWN);

	return 0;
}

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

static file_list *get_file_to_call(time_t filetime, char type)
{
	static file_list **ptr = NULL;
	static int         cnt;
	static time_t      _filetime;
	int interval;

	if (filetime == 0)
	{
		if (ptr != NULL)
		{
			ptr++;
			cnt--;
		}
	}
	else
	{
		ptr = file_root;
		cnt = file_root_member;
		_filetime = filetime;
	}

	while (cnt > 0 && (interval = time_in_interval((*ptr)->time,_filetime,type)) < 1)
	{
		if (interval == 0 && (*ptr)->used == 0 && (*ptr)->type == type)
		{
			(*ptr)->used = 1;
			return (*ptr);
		}

		ptr++;
		cnt--;
	}

	return NULL;
}

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

static int time_in_interval(time_t t1, time_t t2, char type)
{
	int begin;
	int end;

	switch (type)
	{
		case C_VBOX : begin = 0;
		              end   = 10;
		              break;
		case C_FAX  : begin = -2;
		              end   = 1;
		              break;
		default     : begin = 0;
		              end   = 0;
		              break;
	}

	if ((int) t1 < ((int) t2) + begin)
                return(-1);

	if ((int) t1 > ((int) t2) + end)
                return(1);

	return 0;
}

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

static char *get_links(time_t filetime, char type)
{
	static char *string[5] = {NULL,NULL,NULL,NULL,NULL};
        static int   cnt = UNKNOWN;
	file_list   *ptr;


	cnt = (cnt+1) % 5;
	free(string[cnt]);
	string[cnt] = NULL;

	if (type == C_VBOX)
	{
		if ((ptr = get_file_to_call(filetime,type)) != NULL)
		{
			if ((string[cnt] = (char*) calloc(SHORT_STRING_SIZE,sizeof(char))) == NULL)
			{
				print_msg(PRT_ERR, nomemory);
				return NULL;
			}

			sprintf(string[cnt],H_LINK,_myname,type,ptr->version,nam2html(ptr->name),"phone");
		}
	}
	else
	if (type == C_FAX)
	{
		while((ptr = get_file_to_call(filetime,type)) != NULL)
		{
			append_fax(&(string[cnt]),ptr->name,type,ptr->version);
			filetime = 0;
		}
	}
	else
	{
		print_msg(PRT_ERR, "isdnrep: internal error: invalid type %d of file!\n", (int)type);
		return NULL;
	}

	return string[cnt]?string[cnt]:"        ";
}

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

static char *append_fax(char **string, char *file, char type, int version)
{
	static int cnt = 0;
	char help[BUFSIZ];
	char help2[20];

	if (*string == NULL)
		cnt = 1;
	else
		cnt++;

	sprintf(help2,"&lt;%d&gt;",cnt);
	sprintf(help,H_LINK,_myname,type,version,nam2html(file),help2);

	if (*string == NULL)
		*string = strdup(STR_FAX);

	if ((*string = (char*) realloc(*string,sizeof(char)*(strlen(*string)+strlen(help)+2))) == NULL)
	{
		print_msg(PRT_ERR, nomemory);
		return NULL;
	}

	strcat(*string,help);
	strcat(*string," ");

	return *string;
}

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

static char *nam2html(char *file)
{
	static char RetCode[SHORT_STRING_SIZE];
	char *ptr = RetCode;

	while (*file != '\0')
	{
		switch (*file)
		{
			case '+': strcpy(ptr,"%2b");
			          ptr += 3;
			          file++;
			          break;
			default : *ptr++ = *file++;
			          break;
		}
	}

	*ptr = '\0';

	return RetCode;
}

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

static char *get_a_day(time_t t, int d_diff, int m_diff, int flag)
{
	static char string[16];
	struct tm *tm;
	time_t cur_time;
	char   flag2 = 0;


	if (t == 0)
	{
		if (flag & F_END)
			return NULL;

		time(&t);
		flag2 = 1;
	}

	if (flag & F_END)
		t++;

	tm = localtime(&t);

	tm->tm_mday += d_diff;
	tm->tm_mon  += m_diff;
        tm->tm_isdst = UNKNOWN;

	if (flag2 == 1)
	{
		tm->tm_hour  = 0;
		tm->tm_min   = 0;
		tm->tm_sec   = 0;
	}

	t = mktime(tm);

	if (flag & F_END)
		t-=2;

	time(&cur_time);

	if (cur_time < t)
	{
		if (!(flag & F_END))
			return NULL;
	/*
		if (flag & F_END)
			t = cur_time;
		else
			return NULL;
	*/
	}

	tm = localtime(&t);
	sprintf(string,"%02d%02d%02d%02d%04d.%02d",tm->tm_mon+1,tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_year+1900,tm->tm_sec);

	return string;
}

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

static char *get_time_string(time_t begin, time_t end, int d_diff, int m_diff)
{
	static char string[256];
	char *ptr = NULL;


	strcpy(string,get_default_html_params());

	if ((ptr = get_a_day(begin,d_diff,m_diff,F_BEGIN)) != NULL)
	{
		strcat(string,"+-t");
		strcat(string,ptr);

		if ((ptr = get_a_day(end,d_diff,m_diff,F_END)) != NULL)
		{
			strcat(string,"-");
			strcat(string,ptr);
		}

		return string;
	}

	return NULL;
}

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

static char *get_default_html_params(void)
{
	static char string[50];

	sprintf(string,"-w%d%s%s%s",
	                        html-1,
	/*                         ^^---sehr gefaehrlich, da eine UND-Verknuepfung!!! */
	                        print_failed?"+-E":"",
	                        outgoingonly?"+-o":"",
	                        incomingonly?"+-i":"");
	return string;
}

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

static char *create_vbox_file(char *file, int *compression)
{
	int fdin, fdout, len;
	char string[BUFSIZ];
	char *fileout = NULL;
	vaheader_t header;

	if ((fdin = open(file,O_RDONLY)) == -1)
	{
		print_msg(PRT_ERR, msg1, file, strerror(errno));
		return NULL;
	}

	if (read(fdin,&header,sizeof(vaheader_t)) == sizeof(vaheader_t))
	{
		if (compression != NULL)
			*compression = ntohl(header.compression);

		fileout = strdup("/tmp/isdnrepXXXXXX");
		if( (fdout = mkstemp(fileout)) < 0 )
		{
			print_msg(PRT_ERR, msg1, fileout, strerror(errno));
			close(fdin);
			free(fileout);
			return NULL;
		}

		while((len = read(fdin,string,BUFSIZ)) > 0)
		{
			if (write(fdout,string,len) != len)
			{
				print_msg(PRT_ERR, "isdnrep: can't write to file `%s': %s!\n", fileout, strerror(errno));
				close(fdout);
				close(fdin);
				unlink(fileout);
				free(fileout);
				return NULL;
			}
		}

		close(fdout);

		if (len < 0)
		{
			print_msg(PRT_ERR, "isdnrep: can't read from file `%s': %s!\n", file, strerror(errno));
			close(fdin);
			unlink(fileout);
			free(fileout);
			return NULL;
		}
	}

	close(fdin);
	return fileout;
}

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

static int htoi(char *s)
{
	int	value;
	char c;

	c = s[0];
	if (isupper(c))
		c = tolower(c);
	value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;

	c = s[1];
	if (isupper(c))
		c = tolower(c);
	value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;

	return (value);
}

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

static char *url_unescape(char *str)
{
	char *RetCode = str;
	char *dest = str;

	while (str[0])
	{
		if (str[0] == '+')
			dest[0] = ' ';
		else if (str[0] == '%' && isxdigit(str[1]) && isxdigit(str[2]))
		{
			dest[0] = (unsigned char) htoi(str + 1);
			str += 2;
		}
		else
			dest[0] = str[0];

		str++;
		dest++;
	}

	dest[0] = '\0';

	return RetCode;
}

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

int new_args(int *nargc, char ***nargv)
{
	int index = *nargc;
	int index2 = 1;
	char **h_args;
	char *h_env;

	if ((h_env = getenv(H_ENV_VAR)) == NULL)
		return 1;

	if ((h_args = get_http_args(h_env,&index)) == NULL)
		return 2;

	while (index2 < *nargc)
		h_args[index++] = (*nargv)[index2++];

	h_args[0] = (*nargv)[0];
	h_args[index] = NULL;

	*nargc = index;
	*nargv = h_args;

unlink("/tmp/iii");
{
FILE *fp = fopen("/tmp/iii","w");
for(index2=0;index2 < index;index2++)
{
fputs(h_args[index2],fp);
fputs("*\n",fp);
}
fclose(fp);
}
	return 0;
}

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

static char **get_http_args(char *str, int *index)
{
	char **RetCode = NULL;

	if (index == NULL)
		return NULL;

	if (*index < 0)
		*index = 0;

	str = url_unescape(strdup(str));

	if (str == NULL)
		return NULL;

	if (str == '\0')
	{
		free(str);
		return NULL;
	}

	if ((RetCode = (char**) calloc(20+(*index),sizeof(char*))) == NULL)
	{
		print_msg(PRT_ERR, nomemory);
		return NULL;
	}

	*index = 1;
	RetCode[0] = NULL;

	RetCode[(*index)++] = str;

	while(*str != '\0')
	{
		if (*str == ' ' || *str == '=')
		{
			*str++ = '\0';

			if (*str != ' ' && *str != '=' &&  *str != '\0')
				RetCode[(*index)++] = str;
		}
		else
			str++;
	}

	RetCode[*index] = NULL;

	return RetCode;
}

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

static int find_format_length(char *string)
{
	int len;
	char type;
	char* dest;

	if (string == NULL)
                return(UNKNOWN);

	while (*string != '\0')
	{
		if (*string++ == '%')
		{
			if (*string == '%')
			{
				string++;
				continue;
			}

			while(index("0123456789-",*string)) string++;

			if (*string == '\0')
                                return(UNKNOWN);

			if (*string == '.')
			{
				if (sscanf(++string,"%d%c",&len,&type) == 2 && type == 's' && len >= 0)
				{
					if ((dest = index(--string,'s')) == NULL)
                                                return(UNKNOWN);

					memmove(string,dest,strlen(dest)+1);

					return len;
				}

				if (*string == 's')
					return 0;

                                return(UNKNOWN);
			}
			else if (*string == 's')
                                return(UNKNOWN);

		}
	}

        return(UNKNOWN);
}

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

static int app_fmt_string(char *target, int targetlen, char *fmt, int condition, char *value)
{
	char tmpfmt[BUFSIZ];
	char tmpvalue[BUFSIZ];
	int len;

	strcpy(tmpfmt,fmt);
	strcpy(tmpvalue,value);
	len = find_format_length(tmpfmt);

        if (len != UNKNOWN && len > 0 && strlen(tmpvalue) > len)
		tmpvalue[len] = '\0';

	value = condition?html_conv(tmpvalue):tmpvalue;

	return snprintf(target,targetlen,tmpfmt,value);
}

/*****************************************************************************/
/* returns 0 if check is allowed by -x option, 1 if not, 2 in case of error  */
static int check_day_hour(bitfield *days, bitfield *hours, const time_t check)
{
	struct tm *tp, t;

	if (!days[INCLUDE] && !days[EXCLUDE] && !hours[INCLUDE] && !hours[EXCLUDE])
		return 0;

	if ( !(tp = localtime(&check)) )
		return 2;
	t = *tp;  /* save result from further time.h functioncalls */

	if ( hours[INCLUDE] && !(hours[INCLUDE] & (1 << t.tm_hour)) )
		return 1;  /* hour is not included */
	if ( hours[EXCLUDE] && hours[EXCLUDE] & (1 << t.tm_hour) )
		return 1;  /* hour is excluded */
	if ( days[INCLUDE] && !isDay(&t, days[INCLUDE], 0) )
		return 1;  /* day is not included */
	if ( days[EXCLUDE] && isDay(&t, days[EXCLUDE], 0) )
		return 1;  /* day is excluded */

	return 0;	
} /* /check_day_hour() */

/*****************************************************************************/
/* find provider for recalculation of connection fees (-r Option) */
int prep_recalc (void)
{
	int pnum;
	char *variant;
	/* eror messages should work in HTML mode (text/html) too */
	if (recalc.mode == '-' || /* just recalc, no different provider */
		  recalc.mode == 'b' || /* do lcr with booked providers */
			recalc.mode == 'B' )  /* do lcr with all providers */
		return 1;

	if (recalc.mode == 'p')
	{
		if (strchr(recalc.input, '_'))     /* -rp21_0 */
		{
			recalc.prefix = pnum2prefix_variant(recalc.input, 0);
		}
		else                               /* -rp21 */
		{
			pnum = atoi(recalc.input);
			recalc.prefix = pnum2prefix(pnum, 0);
		}
	}
	else if (recalc.mode == 'v')
	{
		if ( (variant = strchr(recalc.input, '_')) )
		{                                   /* -rv01021_0 */
			int p;
			char *vbn;
			char s[16];
			variant++;
			for (p=0; p<getNProvider(); p++)
			{                                 /* something like vbn2prefix_variant */
				vbn = getProviderVBN(p);
				if (strncmp(vbn, recalc.input, strlen(vbn)))
					continue;
				pnum = prefix2pnum(p);
				snprintf(s, 12, "%d_%s", pnum, variant );
				if ( (recalc.prefix = pnum2prefix_variant(s, 0)) != UNKNOWN )
					break;
			}
		}
		else                               /* -rv01021 */
		{
			int l;
			recalc.prefix = vbn2prefix(recalc.input, &l);
		}
	}
	else
	{
		printf("Unknown provider selection mode in option -r: %c.\n", recalc.mode);
		return 0;
	}
	if (recalc.prefix == UNKNOWN)
	{
		printf("No provider found in ratefile for %s %s.\n",
		       (recalc.mode=='p') ? "Pnum" : "VBN", recalc.input);
		return 0;
	}
	return 1;
}

/*****************************************************************************/
/* parse contents of select summaries option,
 * with NULL as input, all summaries are enabled as default */
int select_summaries(int *list, char *input)
{
	int   bit=1;
	char *s=SUM__CHARS;
	if (input)
		while (*s)
		{
			if ( strchr(input, *s) )
				list[INCLUDE] |= bit;
			if ( strchr(input, toupper(*s)) )
				list[EXCLUDE] |= bit;
			bit <<= 1;
			s++;
		}
	list[RESULT] = list[INCLUDE] ? list[INCLUDE] : SUM_ALL;
	list[RESULT] &= ~list[EXCLUDE];
	return list[RESULT];
}

/*****************************************************************************/
/* parse contents of select day/hour option,
 * result is NULL for correct option or pointer to first failed char */
char *select_day_hour(bitfield *days, bitfield *hours, char *input)
{
	char *s=input, *t;
	int   inex;
	char  a, b, c;
	int   i, j, k;

	if (!s || !*s)
		return NULL;	
	s--;  /* char before [dDhH] */
	do
	{
		/* first char selects day/hour include/EXCLUDE */
		inex = islower(*++s) ? INCLUDE : EXCLUDE;
		if (tolower(*s) == 'd')
		{
			do
			{
				t = ++s;  /* move behind 'd', 'D', or ',' */
				if (isdigit(*s))
				{
					a = b = *s++;
					if (*s == '-')
					{
						s++;
						b = *s++;
					}
					if (a < '1' || b < '1' || a > '7' || b > '7')
						return t;
					if (a <= b)
						for (c=a; c <= b; c++)  /* e.g. 1-1 or 1-5 */
							days[inex] |= 1 << (MONDAY + (c - '1'));
					else
					{
						for (c=a; c <= '7'; c++)  /* e.g. 6-1 -> 6-7, 1-1 */
							days[inex] |= 1 << (MONDAY + (c - '1'));
						for (c='1'; c <= b; c++)
							days[inex] |= 1 << (MONDAY + (c - '1'));
					}
				}
				else
				{
					if (*s == 'W')
						days[inex] |= 1 << WORKDAY;
					else if (*s == 'E')
						days[inex] |= 1 << WEEKEND;
					else if (*s == 'H')
						days[inex] |= 1 << HOLIDAY;
					else if (*s == '*')
						days[inex] |= 1 << EVERYDAY;
					else
						return s;
					s++;
				}
			}
			while (*s == ',');
		}
		else if (tolower(*s) == 'h')
		{
			do
			{
				t = ++s;  /* move behind 'h', 'H', or ',' */
				if (*s == '*')
				{
					i = 0; j = 24;
					s++;
				}
				else
				{
					errno = 0;
					i = j = (int) strtol(s, &s, 10);
					if (errno)
						return s;
					if (*s == '-')
					{
						s++;
						errno = 0;
						j = (int) strtol(s, &s, 10);
						if (errno)
							return s;
					}
					i = (i==24) ? 0 : i;
					j = (j==24) ? 0 : j;
					j = (i==j) ? j+1 : j;  /* e.g. 17-17 (wrong) -> 17-18 */
					j = (j==24) ? 0 : j;
					if (i < 0 || j < 0 || i > 23 || j > 23)
						return t;  /* wrong values in range */
				}
				if (i <= j)
					for (k=i; k < j; k++)  /* e.g. 09-18 */
						hours[inex] |= 1 << k;
				else
				{
					for (k=i; k < 24; k++)  /* e.g. 18-09 -> 18-00, 00-18 */
						hours[inex] |= 1 << k;
					for (k=0; k < j; k++)
						hours[inex] |= 1 << k;
				}
			}
			while (*s == ',');
		}
		else
			return s;
	}
	while (*s == ':');
	if (*s)
		return s;
#if DEBUG
	fprintf(stderr, "xopt: d= %04X, D= %04X, h= %07X, H= %07X\n",
	        (int) days[INCLUDE], (int) days[EXCLUDE],
	        (int) hours[INCLUDE], (int) hours[EXCLUDE]);
#endif
	return NULL;
} /* /select_day_hour() */
/* vim:set ts=2: */
/*****************************************************************************/
