isdn4k-utils/isdnlog/tools/rate_skip.c

471 lines
13 KiB
C

/*
* $Id: rate_skip.c,v 1.1 2005/02/23 14:33:40 tobiasb Exp $
*
* rate database (Tarifdatenbank) -- skipping of providers
*
* Copyright 2005 Tobias Becker <tobiasb@isdn4linux.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.
*/
/*
* Skipped providers: The user may decide to completely ignore ("skip")
* a subset of providers defined in the rate file. This done by Q:<...>
* lines in rate.conf and/or a skipProv=<...> line in the parameter file.
* Skipping is either unconditional or takes place only if the provider
* is not explicit mentioned by a P:... line in rate.conf.
*
* The internal list of skipped providers is kept simple as it will be
* used only while reading the ratefile. No care is taken about
* overlapping entries and similar things.
*
* If the rate file uses redirs (R:...), disabling all non booked providers by
* the * wildcard entry will probably cause error during rate file loading like
* "rate-de.dat:@1938 Couldn't find provider 33_1 for redir #3 pnum 33_2 (7)".
* Because of this, a second list of entries becomes necessary. If a provider
* is matched by this "white" list, it is not skipped, regardless of any
* entries in the regular "black" list.
*
* Interface:
*
* int add_skipped_provider(char *line, char **msg);
* Parse line as list of skipped providers. The format is explained in
* rate.conf(5). The return value is 0 for proper input and 1 otherwise.
* If msg is not NULL, it will be used for an description in case of error.
* line is modified during parsing.
*
* int is_skipped_provider(int prov, int var, int booked);
* Returns 1 if the provider written as P:prov,var in the rate file is to
* be skipped or 0 otherwise. var may be UNKNOWN (-1). booked states
* whether there is a line P:prov=var in rate.conf or not.
*
* int clear_skipped_provider(void);
* This clears the internal list of skipped providers. Returns the number
* of freed list entries.
*
* int dump_skipped_provider(char *str, int len);
* Write the internal list of skipped providers to str with a maxium of len
* characters. The format of the output is the same as expected for input
* in add_skipped_provider. Returns the number of list entries or -1 in
* case of error.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "rate_skip.h"
#ifndef UNKNOWN /* tools.h is a little too heavy ... */
#define UNKNOWN (-1)
#endif
#define BLACK 0 /* indices for regular and */
#define WHITE 1 /* exceptional list */
#define FLAG_ALL 0x1 /* matches all providers */
#define FLAG_UNCOND 0x2 /* valid for booked providers too */
#define FLAG_PROV0 0x4 /* value for prov[0] present */
#define FLAG_PROV1 0x8 /* value for prov[1] present */
#define FLAG_VAR0 0x10 /* value for var[0] present */
#define FLAG_VAR1 0x20 /* value for var[1] present */
#define FLAG_WHITE 0x40 /* value for entry in white list */
struct skip {
struct skip *next; /* next entry in list or NULL if last */
int flags; /* details about kind of entry */
int prov[2]; /* first [0] and last [1] provider number */
int var[2]; /* same for variant number */
};
static struct skip *list[2] = {NULL, NULL};
/* remove space and comment in string */
static void strip (char *from) {
char *to = from;
while (*from && *from != '#') {
if (!isspace(*from))
*to++ = *from;
++from;
}
*to = '\0';
}
int add_skipped_provider(char *line, char **msg) {
char *next, *p[2], *q;
int l, colour, uncond = 0, all_white = 0;
struct skip entry, *add, *last[2] = {NULL, NULL};
static char errmsg[128];
*msg = errmsg;
**msg = '\0';
strip(line);
/* whole line unconditional? */
if (*line == '!') {
uncond = 1;
line++;
}
/* whole line for white list? */
else if (*line == '~') {
all_white = 1;
line++;
}
while (1) {
next = strchr(line, ';');
if (next)
*next++ = '\0';
/* silently ignore empty ranges */
if (!*line) {
if (next) {
line = next;
continue;
}
else
break;
}
/* select default list */
colour = all_white ? WHITE : BLACK;
/* line points to a NUL-terminated range */
entry.flags = uncond ? FLAG_UNCOND : 0;
/* current entry unconditional? */
l = strlen(line);
if ( *(line + l - 1) == '!' ) {
*(line + --l) = '\0';
entry.flags |= FLAG_UNCOND;
}
/* current entry for white list? */
else if ( *(line + l - 1) == '~' ) {
*(line + --l) = '\0';
colour = WHITE;
}
if (colour == WHITE) {
entry.flags |= FLAG_WHITE;
/* either on white list or uncondional or none of both */
entry.flags &= ~FLAG_UNCOND;
}
/* wildcard entry? */
if (*line == '*') {
if (*(line + 1)) {
snprintf(*msg, sizeof errmsg, "unexpected characters after '*': %s",
line +1);
return 1;
}
else
entry.flags |= FLAG_ALL;
}
/* range entry: <prov>- | -<prov> | <prov>-<prov> | <prov> */
else {
if (*line == '-') {
p[0] = NULL;
p[1] = line+1;
}
else if (*(line + l - 1) == '-') {
*(line + --l) = '\0';
p[0] = line;
p[1] = NULL;
}
else if ( (q = strchr(line,'-')) ) {
*q = '\0';
p[0] = line;
p[1] = q + 1;
}
else {
p[0] = p[1] = line;
}
for (l=0; l < 2; l++) {
if (!p[l])
continue;
q = NULL;
entry.prov[l] = (int) strtol(p[l], &q, 10);
entry.flags |= l ? FLAG_PROV1 : FLAG_PROV0;
if (q && *q == ',') {
p[l] = q + 1;
q = NULL;
entry.var[l] = (int) strtol(p[l], &q, 10);
entry.flags |= l ? FLAG_VAR1 : FLAG_VAR0;
}
if (q && *q) {
snprintf(*msg, sizeof errmsg,
"unexpected characters for prov[,var]: %s", q);
return 1;
}
}
} /* /range entry */
/* entry is complete, save it to list */
add = malloc(sizeof(struct skip));
if (!add) {
snprintf(*msg, sizeof errmsg, "Out of memory!");
return 1;
}
*add = entry;
/* wildcard entries are added on top of list */
if (entry.flags & FLAG_ALL) {
add->next = list[colour];
list[colour] = add;
}
else {
if (!last[colour] && list[colour]) {
last[colour] = list[colour];
while (last[colour]->next)
last[colour] = last[colour]->next;
}
if (last[colour])
last[colour]->next = add;
else
list[colour] = add;
add->next = NULL;
last[colour] = add;
}
if (next)
line = next;
else
break;
} /* /loop over ranges: <range>;<range> */
*msg = NULL;
return 0;
}
static int on_list(struct skip *testlist, int prov, int var, int booked) {
struct skip *test = testlist;
register int match, flags;
while (test) {
/* compare entry with query */
match = 1;
flags = test->flags;
if (!(flags & FLAG_ALL)) {
if (match && flags & FLAG_PROV0 && prov < test->prov[0])
match = 0;
if (match && flags & FLAG_PROV1 && prov > test->prov[1])
match = 0;
if (match && var != UNKNOWN) {
if (flags & FLAG_PROV0 && prov == test->prov[0] &&
flags & FLAG_VAR0 && var < test->var[0])
match = 0;
if (match && flags & FLAG_PROV1 && prov == test->prov[1] &&
flags & FLAG_VAR1 && var > test->var[1])
match = 0;
}
/* for UNKNOWN variant, entry must be valid for whole provider,
* not only for some variant of this provider
*/
if (match && var == UNKNOWN) {
if (flags & FLAG_PROV0 && prov == test->prov[0] && flags & FLAG_VAR0)
match = 0;
if (match && flags & FLAG_PROV1 && prov == test->prov[1] &&
flags & FLAG_VAR1)
match = 0;
}
} /* /no wildcard entry */
if (match && ((test->flags & FLAG_UNCOND) == FLAG_UNCOND) >= booked)
return 1;
test = test->next;
}
return 0;
}
int is_skipped_provider(int prov, int var, int booked) {
/* first look at white list, where there is no booked attribute */
if (on_list(list[WHITE], prov, var, 0))
return 0;
return on_list(list[BLACK], prov, var, booked);
}
static int clear_list(struct skip **alist) {
struct skip *this, *next = *alist;
int n = 0;
while ( (this=next) != NULL ) {
next = this->next;
free(this);
++n;
}
*alist = NULL;
return n;
}
int clear_skipped_provider(void) {
return clear_list(&list[WHITE]) + clear_list(&list[BLACK]);
}
int dump_skipped_provider(char *str, int len) {
int colour = BLACK;
struct skip *entry = list[colour];
char *end = str + len, *p;
int n = 0, v;
char range[64];
if (len < 1)
return -1;
*str = '\0';
if (!entry && colour == BLACK)
entry = list[colour = WHITE];
while (entry && str < end) {
/* build string from range: * | [<prov>[,<var>][-][<prov>[,<var>]] */
p = range;
*p = '\0';
if (entry->flags & FLAG_PROV0) {
p += snprintf(p, 15, "%i", entry->prov[0]);
if (entry->flags & FLAG_VAR0)
p += snprintf(p, 15, ",%i", entry->var[0]);
}
/* write '-' separator when necessary:
* - not both provider numbers present
* - different provider numbers
* (both provider number present and identical and:)
* - both variants present and different
* - exact one variant present
*/
if ((entry->flags & (FLAG_PROV0|FLAG_PROV1)) != (FLAG_PROV0|FLAG_PROV1) ||
entry->prov[0] != entry->prov[1] ||
( (v=(entry->flags & (FLAG_VAR0|FLAG_VAR1))) == (FLAG_VAR0|FLAG_VAR1)
&& entry->var[0] != entry->var[1] ) ||
v == FLAG_VAR0 || v== FLAG_VAR1) {
*p++ = '-';
if (entry->flags & FLAG_PROV1) {
p += snprintf(p, 15, "%i", entry->prov[1]);
if (entry->flags & FLAG_VAR1)
p += snprintf(p, 15, ",%i", entry->var[1]);
}
}
if (entry->flags & FLAG_ALL) {
/* range should be "-" */
range[0] = '*';
range[1] = '\0';
}
str += snprintf(str, end-str, "%s%s%s%s", n ? ";" : "", range,
entry->flags & FLAG_WHITE ? "~" : "",
entry->flags & FLAG_UNCOND ? "!" : "");
++n;
entry = entry->next;
if (!entry && colour == BLACK)
entry = list[colour = WHITE];
}
return (entry) ? -1 : n;
}
#ifdef STANDALONE
/* build it using
gcc -g -Wall -DSTANDALONE -o rate_skip rate_skip.c
*/
int main (int argc, char *argv[]) {
char input[BUFSIZ], *p;
int ret, prov, var, booked;
printf("Testing provider skipping, available commands:\n"
" a<string> -- add string to list of skipped providers\n"
" c -- clear list of skipped providers\n"
" d -- dump list of skipped providers\n"
" i<prov> <var> <booked> -- is <prov> ... on list?\n"
" q -- quit\n"
" s<string> -- strip <string>\n");
do {
printf(".");
fflush(stdout);
fgets(input, BUFSIZ, stdin);
input[BUFSIZ-1] = '\0';
switch (*input) {
case 'a': ret = add_skipped_provider(input+1, &p);
if (ret)
printf("msg = %s\n", p);
printf("ret = %i (%s)\n", ret, ret ? "ERROR" : "OK");
break;
case 'c': ret = clear_skipped_provider();
printf("ret = %i (number of freed entries)\n", ret);
break;
case 'd': ret = dump_skipped_provider(input, BUFSIZ);
printf("out =>%s<\n", input);
printf("ret = %i (%s)\n", ret,
ret >= 0 ? "number dumped entries" : "ERROR");
break;
case 'i': p = input + 1;
prov = var = UNKNOWN;
booked = 0;
prov = (int) strtol(p, &p, 10);
if (p && *p) {
var = (int) strtol(p, &p, 10);
if (p && *p)
booked = (int) strtol(p, NULL, 10);
}
ret = is_skipped_provider(prov, var, booked);
printf("ret = %i (%s) for prov = %i, var = %i, booked = %i\n",
ret, ret ? "YES" : "NO", prov, var, booked);
break;
case 'q':
break;
case 's': strip(input+1);
printf("res =>%s<\n", input+1);
break;
default: printf("Unknown command (q to quit)\n");
break;
} /* /switch */
} while (*input != 'q');
return 0;
}
#endif /* STANDALONE */
/*
* $Log: rate_skip.c,v $
* Revision 1.1 2005/02/23 14:33:40 tobiasb
* New feature: provider skipping.
* Certain providers can be completely ignored (skipped) when loading the
* rate-file. The selection is done by Q: tags in rate.conf or by skipProv=
* in the parameter file. The syntax is explained in the new manual page
* rate.conf(5). Absurd settings for provider skipping may cause trouble.
* The version number will change to 4.70 in a few days after an update
* of the rate-de.dat.
*
*/
/* vim:set ts=2: */