changeset 131:5c7109183c8c

shorten new program name to themwi-update-outrt
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 06 Oct 2022 21:19:59 -0800
parents 44dc809ffec0
children aa278d75d757
files .hgignore utils/Makefile utils/themwi-update-out-routes.c utils/themwi-update-outrt.c
diffstat 4 files changed, 476 insertions(+), 476 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Thu Oct 06 20:56:14 2022 -0800
+++ b/.hgignore	Thu Oct 06 21:19:59 2022 -0800
@@ -19,4 +19,4 @@
 ^utils/themwi-dump-numdb$
 ^utils/themwi-short-dial$
 ^utils/themwi-update-numdb$
-^utils/themwi-update-out-routes$
+^utils/themwi-update-outrt$
--- a/utils/Makefile	Thu Oct 06 20:56:14 2022 -0800
+++ b/utils/Makefile	Thu Oct 06 21:19:59 2022 -0800
@@ -2,7 +2,7 @@
 CFLAGS=	-O2
 PROGS=	sip-out-test sip-rx-test sip-udp-dump themwi-check-own \
 	themwi-dump-numdb themwi-short-dial themwi-update-numdb \
-	themwi-update-out-routes
+	themwi-update-outrt
 LIBNUMDB=../libnumdb/libnumdb.a
 LIBSIP=	../libsip/libsip.a
 LIBUTIL=../libutil/libutil.a
@@ -31,7 +31,7 @@
 themwi-update-numdb:	themwi-update-numdb.o ${LIBUTIL}
 	${CC} ${CFLAGS} -o $@ $@.o ${LIBUTIL}
 
-themwi-update-out-routes:	themwi-update-out-routes.o ${LIBUTIL}
+themwi-update-outrt:	themwi-update-outrt.o ${LIBUTIL}
 	${CC} ${CFLAGS} -o $@ $@.o ${LIBUTIL}
 
 install:
--- a/utils/themwi-update-out-routes.c	Thu Oct 06 20:56:14 2022 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,473 +0,0 @@
-/*
- * This program reads (parses) ThemWi config file /var/gsm/out-routes,
- * generates the compiled binary form of this database, and then makes
- * it live via atomic rename.
- */
-
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <unistd.h>
-#include "../include/out_routes.h"
-
-#define	MAX_DEST_ENTRIES	16
-#define	MAX_INN_ENTRIES		64
-#define	MAX_SPC_NUM_ENTRIES	64
-
-static struct sip_out_dest dest_records[MAX_DEST_ENTRIES];
-static char *dest_names[MAX_DEST_ENTRIES];
-static struct inn_route inn_records[MAX_INN_ENTRIES];
-static struct special_num_route special_num_records[MAX_SPC_NUM_ENTRIES];
-static unsigned dest_rec_count, inn_rec_count, special_num_count;
-
-static char *system_dir;
-static FILE *inf;
-static int lineno;
-static char linebuf[256];
-
-static int
-find_dest_by_name(sought_name)
-	char *sought_name;
-{
-	unsigned n;
-
-	for (n = 0; n < dest_rec_count; n++)
-		if (!strcmp(dest_names[n], sought_name))
-			return n;
-	return -1;
-}
-
-static int
-find_dest_by_number(target_num)
-	char *target_num;
-{
-	unsigned inn_index;
-	struct inn_route *rec;
-	char *pp, *tp;
-
-	for (inn_index = 0; inn_index < inn_rec_count; inn_index++) {
-		rec = inn_records + inn_index;
-		pp = rec->prefix;
-		tp = target_num;
-		while (*pp && *pp == *tp) {
-			pp++;
-			tp++;
-		}
-		if (*pp)
-			continue;
-		return rec->sip_dest_id;
-	}
-	return -1;
-}
-
-static void
-handle_dest_line(cp)
-	char *cp;
-{
-	char *name, *name_copy, *domain, *ip_str, *port_str;
-	struct sip_out_dest *rec;
-	unsigned portnum;
-	int rc;
-
-	for (name = cp; *cp && !isspace(*cp); cp++)
-		;
-	if (*cp)
-		*cp++ = '\0';
-	while (isspace(*cp))
-		cp++;
-	if (*cp == '\0' || *cp == '#') {
-inv_syntax:	fprintf(stderr, "out-routes line %d: invalid syntax for dest\n",
-			lineno);
-		exit(1);
-	}
-	for (domain = cp; *cp && !isspace(*cp); cp++)
-		;
-	if (*cp)
-		*cp++ = '\0';
-	while (isspace(*cp))
-		cp++;
-	if (*cp == '\0' || *cp == '#')
-		goto inv_syntax;
-	for (ip_str = cp; *cp && !isspace(*cp); cp++)
-		;
-	if (*cp)
-		*cp++ = '\0';
-	while (isspace(*cp))
-		cp++;
-	if (*cp == '\0' || *cp == '#')
-		port_str = 0;
-	else {
-		for (port_str = cp; *cp && !isspace(*cp); cp++)
-			;
-		if (*cp)
-			*cp++ = '\0';
-		while (isspace(*cp))
-			cp++;
-		if (*cp != '\0' && *cp != '#')
-			goto inv_syntax;
-	}
-	rc = find_dest_by_name(name);
-	if (rc >= 0) {
-		fprintf(stderr,
-		"out-routes line %d: duplicate destination name \"%s\"\n",
-			lineno, name);
-		exit(1);
-	}
-	if (dest_rec_count >= MAX_DEST_ENTRIES) {
-		fprintf(stderr,
-			"out-routes line %d: MAX_DEST_ENTRIES exceeded\n",
-			lineno);
-		exit(1);
-	}
-	name_copy = strdup(name);
-	if (!name_copy) {
-		perror("strdup");
-		exit(1);
-	}
-	dest_names[dest_rec_count] = name_copy;
-	rec = dest_records + dest_rec_count;
-	if (strlen(domain) > MAX_SIP_DEST_DOMAIN) {
-		fprintf(stderr,
-			"out-routes line %d: dest domain string is too long\n",
-			lineno);
-		exit(1);
-	}
-	strcpy(rec->domain, domain);
-	rec->sin.sin_family = AF_INET;
-	rec->sin.sin_addr.s_addr = inet_addr(ip_str);
-	if (rec->sin.sin_addr.s_addr == INADDR_NONE) {
-		fprintf(stderr,
-			"out-routes line %d: dest IP address is invalid\n",
-			lineno);
-		exit(1);
-	}
-	if (port_str) {
-		portnum = strtoul(port_str, &cp, 10);
-		if (*cp)
-			goto inv_syntax;
-	} else
-		portnum = 5060;
-	rec->sin.sin_port = htons(portnum);
-	dest_rec_count++;
-}
-
-static void
-handle_inn_route(cp)
-	char *cp;
-{
-	char *prefix, *dest_name;
-	struct inn_route *rec;
-	int rc, dest_id;
-
-	for (prefix = cp; *cp && !isspace(*cp); cp++)
-		;
-	if (*cp)
-		*cp++ = '\0';
-	while (isspace(*cp))
-		cp++;
-	if (*cp == '\0' || *cp == '#') {
-inv_syntax:	fprintf(stderr,
-			"out-routes line %d: invalid syntax for inn-route\n",
-			lineno);
-		exit(1);
-	}
-	for (dest_name = cp; *cp && !isspace(*cp); cp++)
-		;
-	if (*cp)
-		*cp++ = '\0';
-	while (isspace(*cp))
-		cp++;
-	if (*cp != '\0' && *cp != '#')
-		goto inv_syntax;
-	rc = grok_number_string(prefix, 1);
-	if (rc < 1)
-		goto inv_syntax;
-	if (rc > MAX_INN_PREFIX) {
-		fprintf(stderr,
-			"out-routes line %d: inn-route prefix is too long\n",
-			lineno);
-		exit(1);
-	}
-	dest_id = find_dest_by_name(dest_name);
-	if (dest_id < 0) {
-		fprintf(stderr,
-		"out-routes line %d: SIP destination \"%s\" not defined\n",
-			lineno, dest_name);
-		exit(1);
-	}
-	if (inn_rec_count >= MAX_INN_ENTRIES) {
-		fprintf(stderr,
-			"out-routes line %d: MAX_INN_ENTRIES exceeded\n",
-			lineno);
-		exit(1);
-	}
-	rec = inn_records + inn_rec_count;
-	dehyphen_number_string(prefix, rec->prefix);
-	rec->sip_dest_id = dest_id;
-	inn_rec_count++;
-}
-
-static void
-preen_special_num_code(num_code)
-	char *num_code;
-{
-	char *cp;
-	int c;
-	unsigned n;
-
-	n = 0;
-	for (cp = num_code; *cp; ) {
-		c = *cp++;
-		if (is_valid_ext_digit(c))
-			n++;
-		else {
-			fprintf(stderr,
-		"out-routes line %d: special-num string \"%s\" is invalid\n",
-				lineno, num_code);
-			exit(1);
-		}
-	}
-	if (n > MAX_SPECIAL_NUM) {
-		fprintf(stderr,
-		"out-routes line %d: special-num string \"%s\" is too long\n",
-			lineno, num_code);
-		exit(1);
-	}
-}
-
-static void
-handle_special_num_map_to(num_code, cp)
-	char *num_code, *cp;
-{
-	struct special_num_route *rec;
-	char *tgt_num_src;
-	int rc, dest_id;
-
-	while (isspace(*cp))
-		cp++;
-	if (*cp++ != '+') {
-inv_syntax:	fprintf(stderr,
-		"out-routes line %d: invalid syntax for special-num map-to\n",
-			lineno);
-		exit(1);
-	}
-	if (!isdigit(*cp))
-		goto inv_syntax;
-	for (tgt_num_src = cp; *cp && !isspace(*cp); cp++)
-		;
-	if (*cp)
-		*cp++ = '\0';
-	while (isspace(*cp))
-		cp++;
-	if (*cp != '\0' && *cp != '#')
-		goto inv_syntax;
-	rc = grok_number_string(tgt_num_src, 1);
-	if (rc < 1)
-		goto inv_syntax;
-	if (rc > MAX_E164_NUMBER) {
-		fprintf(stderr,
-		"out-routes line %d: map-to number is too long for E.164\n",
-			lineno);
-		exit(1);
-	}
-	rec = special_num_records + special_num_count;
-	strcpy(rec->special_num, num_code);
-	rec->sip_user[0] = '+';
-	dehyphen_number_string(tgt_num_src, rec->sip_user+1);
-	dest_id = find_dest_by_number(rec->sip_user+1);
-	if (dest_id < 0) {
-		fprintf(stderr,
-			"out-routes line %d: no inn-route for map-to number\n",
-			lineno);
-		exit(1);
-	}
-	rec->sip_dest_id = dest_id;
-	special_num_count++;
-}
-
-static void
-handle_special_num_route_to(num_code, cp)
-	char *num_code, *cp;
-{
-	struct special_num_route *rec;
-	char *dest_name;
-	int dest_id;
-
-	while (isspace(*cp))
-		cp++;
-	if (*cp == '\0' || *cp == '#') {
-inv_syntax:	fprintf(stderr,
-		"out-routes line %d: invalid syntax for special-num route-to\n",
-			lineno);
-		exit(1);
-	}
-	for (dest_name = cp; *cp && !isspace(*cp); cp++)
-		;
-	if (*cp)
-		*cp++ = '\0';
-	while (isspace(*cp))
-		cp++;
-	if (*cp != '\0' && *cp != '#')
-		goto inv_syntax;
-	dest_id = find_dest_by_name(dest_name);
-	if (dest_id < 0) {
-		fprintf(stderr,
-		"out-routes line %d: SIP destination \"%s\" not defined\n",
-			lineno, dest_name);
-		exit(1);
-	}
-	rec = special_num_records + special_num_count;
-	strcpy(rec->special_num, num_code);
-	strcpy(rec->sip_user, num_code);
-	rec->sip_dest_id = dest_id;
-	special_num_count++;
-}
-
-static void
-handle_special_num(cp)
-	char *cp;
-{
-	char *num_code, *handling_kw;
-
-	for (num_code = cp; *cp && !isspace(*cp); cp++)
-		;
-	if (*cp)
-		*cp++ = '\0';
-	preen_special_num_code(num_code);
-	if (special_num_count >= MAX_SPC_NUM_ENTRIES) {
-		fprintf(stderr,
-			"out-routes line %d: MAX_SPC_NUM_ENTRIES exceeded\n",
-			lineno);
-		exit(1);
-	}
-	while (isspace(*cp))
-		cp++;
-	if (*cp == '\0' || *cp == '#') {
-inv_syntax:	fprintf(stderr,
-			"out-routes line %d: invalid syntax for special-num\n",
-			lineno);
-		exit(1);
-	}
-	for (handling_kw = cp; *cp && !isspace(*cp); cp++)
-		;
-	if (*cp)
-		*cp++ = '\0';
-	if (!strcmp(handling_kw, "map-to"))
-		handle_special_num_map_to(num_code, cp);
-	else if (!strcmp(handling_kw, "route-to"))
-		handle_special_num_route_to(num_code, cp);
-	else
-		goto inv_syntax;
-}
-
-static void
-process_line()
-{
-	char *cp, *np;
-	void (*handler)();
-
-	if (!index(linebuf, '\n')) {
-		fprintf(stderr,
-			"out-routes 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, "dest"))
-		handler = handle_dest_line;
-	else if (!strcmp(np, "inn-route"))
-		handler = handle_inn_route;
-	else if (!strcmp(np, "special-num"))
-		handler = handle_special_num;
-	else {
-		fprintf(stderr,
-			"out-routes line %d: non-understood keyword \"%s\"\n",
-			lineno, np);
-		exit(1);
-	}
-	while (isspace(*cp))
-		cp++;
-	if (*cp == '\0' || *cp == '#') {
-		fprintf(stderr,
-		"out-routes line %d: missing argument after \"%s\" keyword\n",
-			lineno, np);
-		exit(1);
-	}
-	handler(cp);
-}
-
-static void
-emit_output()
-{
-	FILE *outf;
-	struct out_routes_header hdr;
-
-	outf = fopen("out-routes.newbin", "w");
-	if (!outf) {
-		perror("creating out-routes.newbin");
-		exit(1);
-	}
-	hdr.num_dest = dest_rec_count;
-	hdr.num_inn = inn_rec_count;
-	hdr.num_special = special_num_count;
-	if (fwrite(&hdr, sizeof hdr, 1, outf) != 1) {
-write_err:	fprintf(stderr, "error writing to new binary file\n");
-		exit(1);
-	}
-	if (fwrite(dest_records, sizeof(dest_records[0]), dest_rec_count, outf)
-	    != dest_rec_count)
-		goto write_err;
-	if (fwrite(inn_records, sizeof(inn_records[0]), inn_rec_count, outf)
-	    != inn_rec_count)
-		goto write_err;
-	if (fwrite(special_num_records, sizeof(special_num_records[0]),
-		   special_num_count, outf) != special_num_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("out-routes", "r");
-	if (!inf) {
-		perror("opening out-routes");
-		exit(1);
-	}
-	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++)
-		process_line();
-	fclose(inf);
-	emit_output();
-	/* make it live */
-	if (rename("out-routes.newbin", "out-routes.bin") < 0) {
-		perror("rename");
-		exit(1);
-	}
-	exit(0);
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils/themwi-update-outrt.c	Thu Oct 06 21:19:59 2022 -0800
@@ -0,0 +1,473 @@
+/*
+ * This program reads (parses) ThemWi config file /var/gsm/out-routes,
+ * generates the compiled binary form of this database, and then makes
+ * it live via atomic rename.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#include "../include/out_routes.h"
+
+#define	MAX_DEST_ENTRIES	16
+#define	MAX_INN_ENTRIES		64
+#define	MAX_SPC_NUM_ENTRIES	64
+
+static struct sip_out_dest dest_records[MAX_DEST_ENTRIES];
+static char *dest_names[MAX_DEST_ENTRIES];
+static struct inn_route inn_records[MAX_INN_ENTRIES];
+static struct special_num_route special_num_records[MAX_SPC_NUM_ENTRIES];
+static unsigned dest_rec_count, inn_rec_count, special_num_count;
+
+static char *system_dir;
+static FILE *inf;
+static int lineno;
+static char linebuf[256];
+
+static int
+find_dest_by_name(sought_name)
+	char *sought_name;
+{
+	unsigned n;
+
+	for (n = 0; n < dest_rec_count; n++)
+		if (!strcmp(dest_names[n], sought_name))
+			return n;
+	return -1;
+}
+
+static int
+find_dest_by_number(target_num)
+	char *target_num;
+{
+	unsigned inn_index;
+	struct inn_route *rec;
+	char *pp, *tp;
+
+	for (inn_index = 0; inn_index < inn_rec_count; inn_index++) {
+		rec = inn_records + inn_index;
+		pp = rec->prefix;
+		tp = target_num;
+		while (*pp && *pp == *tp) {
+			pp++;
+			tp++;
+		}
+		if (*pp)
+			continue;
+		return rec->sip_dest_id;
+	}
+	return -1;
+}
+
+static void
+handle_dest_line(cp)
+	char *cp;
+{
+	char *name, *name_copy, *domain, *ip_str, *port_str;
+	struct sip_out_dest *rec;
+	unsigned portnum;
+	int rc;
+
+	for (name = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#') {
+inv_syntax:	fprintf(stderr, "out-routes line %d: invalid syntax for dest\n",
+			lineno);
+		exit(1);
+	}
+	for (domain = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#')
+		goto inv_syntax;
+	for (ip_str = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#')
+		port_str = 0;
+	else {
+		for (port_str = cp; *cp && !isspace(*cp); cp++)
+			;
+		if (*cp)
+			*cp++ = '\0';
+		while (isspace(*cp))
+			cp++;
+		if (*cp != '\0' && *cp != '#')
+			goto inv_syntax;
+	}
+	rc = find_dest_by_name(name);
+	if (rc >= 0) {
+		fprintf(stderr,
+		"out-routes line %d: duplicate destination name \"%s\"\n",
+			lineno, name);
+		exit(1);
+	}
+	if (dest_rec_count >= MAX_DEST_ENTRIES) {
+		fprintf(stderr,
+			"out-routes line %d: MAX_DEST_ENTRIES exceeded\n",
+			lineno);
+		exit(1);
+	}
+	name_copy = strdup(name);
+	if (!name_copy) {
+		perror("strdup");
+		exit(1);
+	}
+	dest_names[dest_rec_count] = name_copy;
+	rec = dest_records + dest_rec_count;
+	if (strlen(domain) > MAX_SIP_DEST_DOMAIN) {
+		fprintf(stderr,
+			"out-routes line %d: dest domain string is too long\n",
+			lineno);
+		exit(1);
+	}
+	strcpy(rec->domain, domain);
+	rec->sin.sin_family = AF_INET;
+	rec->sin.sin_addr.s_addr = inet_addr(ip_str);
+	if (rec->sin.sin_addr.s_addr == INADDR_NONE) {
+		fprintf(stderr,
+			"out-routes line %d: dest IP address is invalid\n",
+			lineno);
+		exit(1);
+	}
+	if (port_str) {
+		portnum = strtoul(port_str, &cp, 10);
+		if (*cp)
+			goto inv_syntax;
+	} else
+		portnum = 5060;
+	rec->sin.sin_port = htons(portnum);
+	dest_rec_count++;
+}
+
+static void
+handle_inn_route(cp)
+	char *cp;
+{
+	char *prefix, *dest_name;
+	struct inn_route *rec;
+	int rc, dest_id;
+
+	for (prefix = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#') {
+inv_syntax:	fprintf(stderr,
+			"out-routes line %d: invalid syntax for inn-route\n",
+			lineno);
+		exit(1);
+	}
+	for (dest_name = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp != '\0' && *cp != '#')
+		goto inv_syntax;
+	rc = grok_number_string(prefix, 1);
+	if (rc < 1)
+		goto inv_syntax;
+	if (rc > MAX_INN_PREFIX) {
+		fprintf(stderr,
+			"out-routes line %d: inn-route prefix is too long\n",
+			lineno);
+		exit(1);
+	}
+	dest_id = find_dest_by_name(dest_name);
+	if (dest_id < 0) {
+		fprintf(stderr,
+		"out-routes line %d: SIP destination \"%s\" not defined\n",
+			lineno, dest_name);
+		exit(1);
+	}
+	if (inn_rec_count >= MAX_INN_ENTRIES) {
+		fprintf(stderr,
+			"out-routes line %d: MAX_INN_ENTRIES exceeded\n",
+			lineno);
+		exit(1);
+	}
+	rec = inn_records + inn_rec_count;
+	dehyphen_number_string(prefix, rec->prefix);
+	rec->sip_dest_id = dest_id;
+	inn_rec_count++;
+}
+
+static void
+preen_special_num_code(num_code)
+	char *num_code;
+{
+	char *cp;
+	int c;
+	unsigned n;
+
+	n = 0;
+	for (cp = num_code; *cp; ) {
+		c = *cp++;
+		if (is_valid_ext_digit(c))
+			n++;
+		else {
+			fprintf(stderr,
+		"out-routes line %d: special-num string \"%s\" is invalid\n",
+				lineno, num_code);
+			exit(1);
+		}
+	}
+	if (n > MAX_SPECIAL_NUM) {
+		fprintf(stderr,
+		"out-routes line %d: special-num string \"%s\" is too long\n",
+			lineno, num_code);
+		exit(1);
+	}
+}
+
+static void
+handle_special_num_map_to(num_code, cp)
+	char *num_code, *cp;
+{
+	struct special_num_route *rec;
+	char *tgt_num_src;
+	int rc, dest_id;
+
+	while (isspace(*cp))
+		cp++;
+	if (*cp++ != '+') {
+inv_syntax:	fprintf(stderr,
+		"out-routes line %d: invalid syntax for special-num map-to\n",
+			lineno);
+		exit(1);
+	}
+	if (!isdigit(*cp))
+		goto inv_syntax;
+	for (tgt_num_src = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp != '\0' && *cp != '#')
+		goto inv_syntax;
+	rc = grok_number_string(tgt_num_src, 1);
+	if (rc < 1)
+		goto inv_syntax;
+	if (rc > MAX_E164_NUMBER) {
+		fprintf(stderr,
+		"out-routes line %d: map-to number is too long for E.164\n",
+			lineno);
+		exit(1);
+	}
+	rec = special_num_records + special_num_count;
+	strcpy(rec->special_num, num_code);
+	rec->sip_user[0] = '+';
+	dehyphen_number_string(tgt_num_src, rec->sip_user+1);
+	dest_id = find_dest_by_number(rec->sip_user+1);
+	if (dest_id < 0) {
+		fprintf(stderr,
+			"out-routes line %d: no inn-route for map-to number\n",
+			lineno);
+		exit(1);
+	}
+	rec->sip_dest_id = dest_id;
+	special_num_count++;
+}
+
+static void
+handle_special_num_route_to(num_code, cp)
+	char *num_code, *cp;
+{
+	struct special_num_route *rec;
+	char *dest_name;
+	int dest_id;
+
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#') {
+inv_syntax:	fprintf(stderr,
+		"out-routes line %d: invalid syntax for special-num route-to\n",
+			lineno);
+		exit(1);
+	}
+	for (dest_name = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	while (isspace(*cp))
+		cp++;
+	if (*cp != '\0' && *cp != '#')
+		goto inv_syntax;
+	dest_id = find_dest_by_name(dest_name);
+	if (dest_id < 0) {
+		fprintf(stderr,
+		"out-routes line %d: SIP destination \"%s\" not defined\n",
+			lineno, dest_name);
+		exit(1);
+	}
+	rec = special_num_records + special_num_count;
+	strcpy(rec->special_num, num_code);
+	strcpy(rec->sip_user, num_code);
+	rec->sip_dest_id = dest_id;
+	special_num_count++;
+}
+
+static void
+handle_special_num(cp)
+	char *cp;
+{
+	char *num_code, *handling_kw;
+
+	for (num_code = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	preen_special_num_code(num_code);
+	if (special_num_count >= MAX_SPC_NUM_ENTRIES) {
+		fprintf(stderr,
+			"out-routes line %d: MAX_SPC_NUM_ENTRIES exceeded\n",
+			lineno);
+		exit(1);
+	}
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#') {
+inv_syntax:	fprintf(stderr,
+			"out-routes line %d: invalid syntax for special-num\n",
+			lineno);
+		exit(1);
+	}
+	for (handling_kw = cp; *cp && !isspace(*cp); cp++)
+		;
+	if (*cp)
+		*cp++ = '\0';
+	if (!strcmp(handling_kw, "map-to"))
+		handle_special_num_map_to(num_code, cp);
+	else if (!strcmp(handling_kw, "route-to"))
+		handle_special_num_route_to(num_code, cp);
+	else
+		goto inv_syntax;
+}
+
+static void
+process_line()
+{
+	char *cp, *np;
+	void (*handler)();
+
+	if (!index(linebuf, '\n')) {
+		fprintf(stderr,
+			"out-routes 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, "dest"))
+		handler = handle_dest_line;
+	else if (!strcmp(np, "inn-route"))
+		handler = handle_inn_route;
+	else if (!strcmp(np, "special-num"))
+		handler = handle_special_num;
+	else {
+		fprintf(stderr,
+			"out-routes line %d: non-understood keyword \"%s\"\n",
+			lineno, np);
+		exit(1);
+	}
+	while (isspace(*cp))
+		cp++;
+	if (*cp == '\0' || *cp == '#') {
+		fprintf(stderr,
+		"out-routes line %d: missing argument after \"%s\" keyword\n",
+			lineno, np);
+		exit(1);
+	}
+	handler(cp);
+}
+
+static void
+emit_output()
+{
+	FILE *outf;
+	struct out_routes_header hdr;
+
+	outf = fopen("out-routes.newbin", "w");
+	if (!outf) {
+		perror("creating out-routes.newbin");
+		exit(1);
+	}
+	hdr.num_dest = dest_rec_count;
+	hdr.num_inn = inn_rec_count;
+	hdr.num_special = special_num_count;
+	if (fwrite(&hdr, sizeof hdr, 1, outf) != 1) {
+write_err:	fprintf(stderr, "error writing to new binary file\n");
+		exit(1);
+	}
+	if (fwrite(dest_records, sizeof(dest_records[0]), dest_rec_count, outf)
+	    != dest_rec_count)
+		goto write_err;
+	if (fwrite(inn_records, sizeof(inn_records[0]), inn_rec_count, outf)
+	    != inn_rec_count)
+		goto write_err;
+	if (fwrite(special_num_records, sizeof(special_num_records[0]),
+		   special_num_count, outf) != special_num_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("out-routes", "r");
+	if (!inf) {
+		perror("opening out-routes");
+		exit(1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++)
+		process_line();
+	fclose(inf);
+	emit_output();
+	/* make it live */
+	if (rename("out-routes.newbin", "out-routes.bin") < 0) {
+		perror("rename");
+		exit(1);
+	}
+	exit(0);
+}