FreeCalypso > hg > themwi-nanp
diff utils/themwi-update-numdb.c @ 2:1773886ef54e
themwi-update-numdb: old source as starting point
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Wed, 13 Dec 2023 01:23:47 +0000 |
parents | |
children | 5bf2648e5413 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/utils/themwi-update-numdb.c Wed Dec 13 01:23:47 2023 +0000 @@ -0,0 +1,573 @@ +/* + * This program reads (parses) ThemWi config file /var/gsm/number-db2, + * generates the compiled binary form of this database, and then makes + * it live via atomic rename. + */ + +#include <ctype.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include "../include/number_db_v2.h" + +#define MAX_OWNED_NUMBERS 1000 +#define MAX_SHORT_NUMBERS 1000 + +static struct owned_number_rec owned_number_buf[MAX_OWNED_NUMBERS]; +static struct short_number_rec short_number_buf[MAX_SHORT_NUMBERS]; +static unsigned owned_number_count, short_number_count; + +static char *system_dir; +static FILE *inf; +static int lineno; +static char linebuf[256]; +static int prefix_set, prefix_allows_abbrev; +static uint16_t prefix_buf[2]; + +static void +enter_owned_number(rec) + struct owned_number_rec *rec; +{ + if (owned_number_count >= MAX_OWNED_NUMBERS) { + fprintf(stderr, "error: MAX_OWNED_NUMBERS exceeded\n"); + exit(1); + } + bcopy(rec, owned_number_buf + owned_number_count, + sizeof(struct owned_number_rec)); + owned_number_count++; +} + +static void +enter_short_number(rec) + struct short_number_rec *rec; +{ + if (short_number_count >= MAX_SHORT_NUMBERS) { + fprintf(stderr, "error: MAX_SHORT_NUMBERS exceeded\n"); + exit(1); + } + bcopy(rec, short_number_buf + short_number_count, + sizeof(struct short_number_rec)); + short_number_count++; +} + +static void +handle_prefix_line(cp) + char *cp; +{ + char *np, prefix[7]; + + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + if (grok_number_string(np, 1) != 6) { + fprintf(stderr, + "number-db2 line %d: prefix requires 6-digit argument\n", + lineno); + exit(1); + } + dehyphen_number_string(np, prefix); + if (!is_nanp_valid_prefix(prefix)) { + fprintf(stderr, + "number-db2 line %d: prefix violates NANP rules\n", + lineno); + exit(1); + } + prefix_buf[0] = digits3_to_uint16(prefix); + prefix_buf[1] = digits3_to_uint16(prefix + 3); + prefix_set = 1; + while (isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') { + prefix_allows_abbrev = 0; + return; + } + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + if (!strcmp(np, "allow-abbrev")) { + prefix_allows_abbrev = 1; + return; + } + fprintf(stderr, + "number-db2 line %d: non-understood notation \"%s\" after prefix\n", + lineno, np); + exit(1); +} + +static int +parse_extra_number(rec, numstr) + struct owned_number_rec *rec; + char *numstr; +{ + char buf[11]; + int rc; + + rc = grok_number_string(numstr, 1); + switch (rc) { + case 4: + if (!prefix_set) + return(-1); + dehyphen_number_string(numstr, buf); + rec->remap[0] = prefix_buf[0]; + rec->remap[1] = prefix_buf[1]; + rec->remap[2] = digits4_to_uint16(buf); + return(0); + case 10: + dehyphen_number_string(numstr, buf); + rec->remap[0] = digits3_to_uint16(buf); + rec->remap[1] = digits3_to_uint16(buf + 3); + rec->remap[2] = digits4_to_uint16(buf + 6); + return(0); + default: + return(-1); + } +} + +static void +handle_number_attr(rec, tail) + struct owned_number_rec *rec; + char *tail; +{ + char *cp, *np; + int rc; + + for (cp = tail; ; ) { + while (isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') + return; + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + if (!strcmp(np, "sms")) + rec->number_flags |= NUMBER_FLAG_SMSPROV; + else if (!strcmp(np, "e911")) + rec->number_flags |= NUMBER_FLAG_E911PROV; + else if (!strcmp(np, "gsm-sub")) + rec->usage = NUMBER_USAGE_TYPE_GSM_SUB; + else if (!strcmp(np, "map-to")) { + rec->usage = NUMBER_USAGE_TYPE_ALIAS; + while (isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') { + fprintf(stderr, + "number-db2 line %d: map-to requires an argument\n", + lineno); + exit(1); + } + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + rc = parse_extra_number(rec, np); + if (rc < 0) { + fprintf(stderr, + "number-db2 line %d: map-to argument is invalid\n", + lineno); + exit(1); + } + } else if (!strcmp(np, "e911-via")) { + if (rec->usage != NUMBER_USAGE_TYPE_GSM_SUB) { + fprintf(stderr, + "number-db2 line %d: invalid usage of e911-via\n", + lineno); + exit(1); + } + rec->usage |= NUMBER_USAGE_FLAG_E911_VIA; + while (isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') { + fprintf(stderr, + "number-db2 line %d: e911-via requires an argument\n", + lineno); + exit(1); + } + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + rc = parse_extra_number(rec, np); + if (rc < 0) { + fprintf(stderr, + "number-db2 line %d: e911-via argument is invalid\n", + lineno); + exit(1); + } + } else { + fprintf(stderr, + "number-db2 line %d: invalid attribute \"%s\"\n", + lineno, np); + exit(1); + } + } +} + +static void +handle_suffix_line(cp) + char *cp; +{ + char *np; + uint16_t suffix; + struct owned_number_rec own_rec; + struct short_number_rec short_rec; + + if (!prefix_set) { + fprintf(stderr, + "number-db2 line %d: suffix not valid without preceding prefix\n", + lineno); + exit(1); + } + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + if (grok_number_string(np, 0) != 4) { + fprintf(stderr, + "number-db2 line %d: suffix requires 4-digit argument\n", + lineno); + exit(1); + } + suffix = digits4_to_uint16(np); + bzero(&own_rec, sizeof own_rec); + own_rec.number[0] = prefix_buf[0]; + own_rec.number[1] = prefix_buf[1]; + own_rec.number[2] = suffix; + handle_number_attr(&own_rec, cp); + enter_owned_number(&own_rec); + if (!prefix_allows_abbrev) + return; + if ((own_rec.usage & NUMBER_USAGE_MASK) != NUMBER_USAGE_TYPE_GSM_SUB) + return; + bzero(&short_rec, sizeof short_rec); + short_rec.short_num = suffix; + short_rec.short_num_type = SHORT_NUM_TYPE_ABBREV; + short_rec.fullnum_flags = own_rec.number_flags; + short_rec.fullnum_prefix[0] = prefix_buf[0]; + short_rec.fullnum_prefix[1] = prefix_buf[1]; + enter_short_number(&short_rec); +} + +static void +handle_full10_line(cp) + char *cp; +{ + char *np, full10[11]; + struct owned_number_rec own_rec; + + prefix_set = 0; /* cancel any previous prefix line */ + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + if (grok_number_string(np, 1) != 10) { + fprintf(stderr, + "number-db2 line %d: full10 requires 10-digit argument\n", + lineno); + exit(1); + } + dehyphen_number_string(np, full10); + if (!is_nanp_valid_prefix(full10)) { + fprintf(stderr, + "number-db2 line %d: number violates NANP rules\n", + lineno); + exit(1); + } + bzero(&own_rec, sizeof own_rec); + own_rec.number[0] = digits3_to_uint16(full10); + own_rec.number[1] = digits3_to_uint16(full10 + 3); + own_rec.number[2] = digits4_to_uint16(full10 + 6); + handle_number_attr(&own_rec, cp); + enter_owned_number(&own_rec); +} + +static void +handle_itn_line(cp) + char *cp; +{ + char *np; + struct short_number_rec short_rec; + + prefix_set = 0; /* cancel any previous prefix line */ + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + if (grok_number_string(np, 0) != 4) { + fprintf(stderr, + "number-db2 line %d: itn requires 4-digit argument\n", + lineno); + exit(1); + } + bzero(&short_rec, sizeof short_rec); + short_rec.short_num = digits4_to_uint16(np); + short_rec.short_num_type = SHORT_NUM_TYPE_ITN; + enter_short_number(&short_rec); +} + +static void +handle_test_sink_line(cp) + char *cp; +{ + char *np; + struct short_number_rec short_rec; + + prefix_set = 0; /* cancel any previous prefix line */ + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + if (grok_number_string(np, 0) != 4) { + fprintf(stderr, + "number-db2 line %d: test-sink requires 4-digit argument\n", + lineno); + exit(1); + } + bzero(&short_rec, sizeof short_rec); + short_rec.short_num = digits4_to_uint16(np); + short_rec.short_num_type = SHORT_NUM_TYPE_TEST_SINK; + enter_short_number(&short_rec); +} + +static void +process_line() +{ + char *cp, *np; + void (*handler)(); + + if (!index(linebuf, '\n')) { + fprintf(stderr, + "number-db2 line %d: too long or missing newline\n", + lineno); + exit(1); + } + for (cp = linebuf; isspace(*cp); cp++) + ; + if (*cp == '\0' || *cp == '#') + return; + for (np = cp; *cp && !isspace(*cp); cp++) + ; + if (*cp) + *cp++ = '\0'; + if (!strcmp(np, "prefix")) + handler = handle_prefix_line; + else if (!strcmp(np, "suffix")) + handler = handle_suffix_line; + else if (!strcmp(np, "full10")) + handler = handle_full10_line; + else if (!strcmp(np, "itn")) + handler = handle_itn_line; + else if (!strcmp(np, "test-sink")) + handler = handle_test_sink_line; + else { + fprintf(stderr, + "number-db2 line %d: non-understood keyword \"%s\"\n", + lineno, np); + exit(1); + } + while (isspace(*cp)) + cp++; + if (*cp == '\0' || *cp == '#') { + fprintf(stderr, + "number-db2 line %d: missing argument after \"%s\" keyword\n", + lineno, np); + exit(1); + } + handler(cp); +} + +static int +compare_owned_num(p1, p2) + uint16_t *p1, *p2; +{ + if (p1[0] < p2[0]) + return(-1); + if (p1[0] > p2[0]) + return(1); + if (p1[1] < p2[1]) + return(-1); + if (p1[1] > p2[1]) + return(1); + if (p1[2] < p2[2]) + return(-1); + if (p1[2] > p2[2]) + return(1); + return(0); +} + +static int +compare_short_num(p1, p2) + uint16_t *p1, *p2; +{ + if (*p1 < *p2) + return(-1); + if (*p1 > *p2) + return(1); + return(0); +} + +static void +owned_num_check_dup() +{ + struct owned_number_rec *p, *endp; + + endp = owned_number_buf + owned_number_count - 1; + for (p = owned_number_buf; p < endp; p++) { + if (p[0].number[0] == p[1].number[0] && + p[0].number[1] == p[1].number[1] && + p[0].number[2] == p[1].number[2]) { + fprintf(stderr, + "error: NANP number %03u-%03u-%04u appears more than once\n", + p[0].number[0], p[0].number[1], p[0].number[2]); + exit(1); + } + } +} + +static void +short_num_check_dup() +{ + struct short_number_rec *p, *endp; + + endp = short_number_buf + short_number_count - 1; + for (p = short_number_buf; p < endp; p++) { + if (p[0].short_num == p[1].short_num) { + fprintf(stderr, + "error: short number %04u appears more than once\n", + (unsigned) p->short_num); + exit(1); + } + } +} + +static void +check_secondnum_mapto(src) + struct owned_number_rec *src; +{ + struct owned_number_rec *dest; + + dest = bsearch(src->remap, owned_number_buf, owned_number_count, + sizeof(struct owned_number_rec), compare_owned_num); + if (!dest) { + fprintf(stderr, + "error: NANP %03u-%03u-%04u map-to target is not in the database\n", + src->number[0], src->number[1], src->number[2]); + exit(1); + } + if ((dest->usage & NUMBER_USAGE_MASK) != NUMBER_USAGE_TYPE_GSM_SUB) { + fprintf(stderr, + "error: NANP %03u-%03u-%04u map-to target is not a gsm-sub number\n", + src->number[0], src->number[1], src->number[2]); + exit(1); + } +} + +static void +check_secondnum_e911via(src) + struct owned_number_rec *src; +{ + struct owned_number_rec *dest; + + dest = bsearch(src->remap, owned_number_buf, owned_number_count, + sizeof(struct owned_number_rec), compare_owned_num); + if (!dest) { + fprintf(stderr, + "error: NANP %03u-%03u-%04u e911-via target is not in the database\n", + src->number[0], src->number[1], src->number[2]); + exit(1); + } + if (!(dest->number_flags & NUMBER_FLAG_E911PROV)) { + fprintf(stderr, + "error: NANP %03u-%03u-%04u e911-via target is not an E911 number\n", + src->number[0], src->number[1], src->number[2]); + exit(1); + } +} + +static void +check_secondary_numbers() +{ + struct owned_number_rec *p, *endp; + + endp = owned_number_buf + owned_number_count; + for (p = owned_number_buf; p < endp; p++) { + if ((p->usage & NUMBER_USAGE_MASK) == NUMBER_USAGE_TYPE_ALIAS) + check_secondnum_mapto(p); + if (p->usage & NUMBER_USAGE_FLAG_E911_VIA) + check_secondnum_e911via(p); + } +} + +static void +emit_output() +{ + FILE *outf; + struct numdb_file_hdr hdr; + + outf = fopen("number-db2.newbin", "w"); + if (!outf) { + perror("creating number-db2.newbin"); + exit(1); + } + hdr.owned_number_count = owned_number_count; + hdr.short_number_count = short_number_count; + if (fwrite(&hdr, sizeof hdr, 1, outf) != 1) { +write_err: fprintf(stderr, "error writing to new binary file\n"); + exit(1); + } + if (fwrite(owned_number_buf, sizeof(owned_number_buf[0]), + owned_number_count, outf) != owned_number_count) + goto write_err; + if (fwrite(short_number_buf, sizeof(short_number_buf[0]), + short_number_count, outf) != short_number_count) + goto write_err; + fclose(outf); +} + +main(argc, argv) + char **argv; +{ + if (argc > 2) { + fprintf(stderr, "usage: %s [directory]\n", argv[0]); + exit(1); + } + if (argv[1]) + system_dir = argv[1]; + else + system_dir = "/var/gsm"; + if (chdir(system_dir) < 0) { + perror(system_dir); + exit(1); + } + inf = fopen("number-db2", "r"); + if (!inf) { + perror("opening number-db2"); + exit(1); + } + for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) + process_line(); + fclose(inf); + if (owned_number_count >= 2) { + qsort(owned_number_buf, owned_number_count, + sizeof(owned_number_buf[0]), compare_owned_num); + owned_num_check_dup(); + } + check_secondary_numbers(); + if (short_number_count >= 2) { + qsort(short_number_buf, short_number_count, + sizeof(short_number_buf[0]), compare_short_num); + short_num_check_dup(); + } + emit_output(); + /* make it live */ + if (rename("number-db2.newbin", "number-db2.bin") < 0) { + perror("rename"); + exit(1); + } + exit(0); +}