FreeCalypso > hg > themwi-nanp
view utils/themwi-update-numdb.c @ 3:5bf2648e5413
themwi-update-numdb compiles in the new model
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Wed, 13 Dec 2023 02:01:24 +0000 |
parents | 1773886ef54e |
children |
line wrap: on
line source
/* * 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 <themwi/nanp/number_db_v2.h> #include <themwi/nanp/number_utils.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(const 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(const 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(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(struct owned_number_rec *rec, const 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(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(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(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(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(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(void) { char *cp, *np; void (*handler)(char *); 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(const void *p1v, const void *p2v) { const uint16_t *p1 = p1v, *p2 = p2v; 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(const void *p1v, const void *p2v) { const uint16_t *p1 = p1v, *p2 = p2v; if (*p1 < *p2) return(-1); if (*p1 > *p2) return(1); return(0); } static void owned_num_check_dup(void) { const 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(void) { const 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(const struct owned_number_rec *src) { const 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(const struct owned_number_rec *src) { const 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(void) { const 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(void) { 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); } int main(int argc, 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); }