changeset 8:34bbb0585cab

libutil: import from previous fc-pcsc-tools version
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 14 Mar 2021 05:42:37 +0000
parents b25d4dfe5798
children c9ef9e91dd8e
files libutil/Makefile libutil/alpha_decode.c libutil/alpha_fromfile.c libutil/alpha_valid.c libutil/decimal_str.c libutil/filesearch.c libutil/gsm7_decode.c libutil/gsm7_encode.c libutil/gsm7_encode_table.c libutil/gsm7_pack.c libutil/gsm7_unpack.c libutil/hexdigits.c libutil/hexread.c libutil/hexstr.c libutil/iccid_luhn.c libutil/nibbles2asc.c libutil/number_decode.c libutil/number_encode.c libutil/pinentry.c libutil/plmncodes.c libutil/plmnlist.c libutil/revnibbles.c libutil/shorthand.c
diffstat 23 files changed, 1224 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/Makefile	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,17 @@
+CC=	gcc
+CFLAGS=	-O2
+OBJS=	alpha_decode.o alpha_fromfile.o alpha_valid.o decimal_str.o \
+	filesearch.o gsm7_decode.o gsm7_encode.o gsm7_encode_table.o \
+	gsm7_pack.o gsm7_unpack.o hexdigits.o hexread.o hexstr.o iccid_luhn.o \
+	nibbles2asc.o number_decode.o number_encode.o pinentry.o plmncodes.o \
+	plmnlist.o revnibbles.o shorthand.o
+LIB=	libutil.a
+
+all:	${LIB}
+
+${LIB}:	${OBJS}
+	ar rcu $@ ${OBJS}
+	ranlib $@
+
+clean:
+	rm -f *.[oa] errs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/alpha_decode.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,38 @@
+/*
+ * This module contains functions for decoding and displaying alpha fields
+ * that exist in various SIM files.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+
+static void
+print_alpha_field_hex(data, nbytes, outf)
+	u_char *data;
+	unsigned nbytes;
+	FILE *outf;
+{
+	u_char *dp, *endp;
+
+	fputs("HEX ", outf);
+	dp = data;
+	endp = data + nbytes;
+	while (dp < endp)
+		fprintf(outf, "%02X", *dp++);
+}
+
+void
+print_alpha_field(data, nbytes, outf)
+	u_char *data;
+	unsigned nbytes;
+	FILE *outf;
+{
+	if (!nbytes) {
+		fputs("\"\"", outf);
+		return;
+	}
+	if (data[0] & 0x80)
+		print_alpha_field_hex(data, nbytes, outf);
+	else
+		print_gsm7_string_to_file(data, nbytes, outf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/alpha_fromfile.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,107 @@
+/*
+ * This module implements functions for parsing alpha tag strings
+ * from input data files, to be used by commands like pb-update
+ * and smsp-restore.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+
+extern u_char gsm7_encode_table[256];
+
+char *
+alpha_from_file_qstring(cp, record, maxlen, filename_for_errs, lineno_for_errs)
+	char *cp, *filename_for_errs;
+	u_char *record;
+	unsigned maxlen;
+{
+	unsigned acclen, nadd;
+	int c;
+
+	for (acclen = 0; ; ) {
+		if (*cp == '\0') {
+unterm_qstring:		fprintf(stderr,
+				"%s line %d: unterminated quoted string\n",
+				filename_for_errs, lineno_for_errs);
+			return(0);
+		}
+		if (*cp == '"')
+			break;
+		c = *cp++;
+		if (c == '\\') {
+			if (*cp == '\0')
+				goto unterm_qstring;
+			c = *cp++;
+			if (c >= '0' && c <= '7' && isxdigit(*cp)) {
+				c = ((c - '0') << 4) | decode_hex_digit(*cp++);
+				goto bypass_encoding;
+			}
+			switch (c) {
+			case 'n':
+				c = '\n';
+				goto bypass_encoding;
+			case 'r':
+				c = '\r';
+				goto bypass_encoding;
+			case 'e':
+				c = 0x1B;
+				goto bypass_encoding;
+			case '"':
+			case '\\':
+				break;
+			default:
+				fprintf(stderr,
+				"%s line %d: non-understood backslash escape\n",
+					filename_for_errs, lineno_for_errs);
+				return(0);
+			}
+		}
+		c = gsm7_encode_table[c];
+		if (c == 0xFF) {
+			fprintf(stderr,
+	"%s line %d: character in quoted string cannot be encoded in GSM7\n",
+				filename_for_errs, lineno_for_errs);
+			return(0);
+		}
+bypass_encoding:
+		if (c & 0x80)
+			nadd = 2;
+		else
+			nadd = 1;
+		if (acclen + nadd > maxlen) {
+			fprintf(stderr,
+		"%s line %d: alpha tag string is longer than SIM limit\n",
+				filename_for_errs, lineno_for_errs);
+			return(0);
+		}
+		if (c & 0x80)
+			record[acclen++] = 0x1B;
+		record[acclen++] = c & 0x7F;
+	}
+	return(cp + 1);
+}
+
+char *
+alpha_from_file_hex(cp, record, maxlen, filename_for_errs, lineno_for_errs)
+	char *cp, *filename_for_errs;
+	u_char *record;
+	unsigned maxlen;
+{
+	unsigned acclen;
+
+	for (acclen = 0; ; ) {
+		if (!isxdigit(cp[0]) || !isxdigit(cp[1]))
+			break;
+		if (acclen >= maxlen) {
+			fprintf(stderr,
+		"%s line %d: alpha tag string is longer than SIM limit\n",
+				filename_for_errs, lineno_for_errs);
+			return(0);
+		}
+		record[acclen++] = (decode_hex_digit(cp[0]) << 4) |
+				    decode_hex_digit(cp[1]);
+		cp += 2;
+	}
+	return(cp);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/alpha_valid.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,152 @@
+/*
+ * This module contains functions for validating alpha fields
+ * that exist in various SIM files.
+ */
+
+#include <sys/types.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static
+validate_classic_gsm(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	u_char *dp;
+	unsigned n;
+
+	dp = data;
+	for (n = 0; n < nbytes; n++) {
+		if (*dp == 0xFF)
+			break;
+		if (*dp & 0x80)
+			return(-1);
+		dp++;
+	}
+	if (textlenp)
+		*textlenp = n;
+	for (; n < nbytes; n++)
+		if (*dp++ != 0xFF)
+			return(-1);
+	return(0);
+}
+
+static
+validate_ucs2_80(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	u_char *dp, *endp;
+
+	if (nbytes < 3)
+		return(-1);
+	dp = data + 1;
+	endp = data + nbytes;
+	while (dp < endp) {
+		if (dp + 1 == endp) {
+			if (*dp != 0xFF)
+				return(-1);
+			if (textlenp)
+				*textlenp = dp - data;
+			return(0);
+		}
+		if (dp[0] == 0xFF && dp[1] == 0xFF)
+			break;
+		dp += 2;
+	}
+	if (textlenp)
+		*textlenp = dp - data;
+	while (dp < endp)
+		if (*dp++ != 0xFF)
+			return(-1);
+	return(0);
+}
+
+static
+validate_ucs2_81(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	u_char *dp, *endp;
+	unsigned textlen;
+
+	if (nbytes < 4)
+		return(-1);
+	if (!data[1])
+		return(-1);
+	textlen = data[1] + 3;
+	if (textlen > nbytes)
+		return(-1);
+	if (textlenp)
+		*textlenp = textlen;
+	dp = data + textlen;
+	endp = data + nbytes;
+	while (dp < endp)
+		if (*dp++ != 0xFF)
+			return(-1);
+	return(0);
+}
+
+static
+validate_ucs2_82(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	u_char *dp, *endp;
+	unsigned textlen;
+
+	if (nbytes < 5)
+		return(-1);
+	if (!data[1])
+		return(-1);
+	textlen = data[1] + 4;
+	if (textlen > nbytes)
+		return(-1);
+	if (textlenp)
+		*textlenp = textlen;
+	dp = data + textlen;
+	endp = data + nbytes;
+	while (dp < endp)
+		if (*dp++ != 0xFF)
+			return(-1);
+	return(0);
+}
+
+static
+validate_empty(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	u_char *dp;
+	unsigned n;
+
+	dp = data;
+	for (n = 0; n < nbytes; n++)
+		if (*dp++ != 0xFF)
+			return(-1);
+	if (textlenp)
+		*textlenp = 0;
+	return(0);
+}
+
+validate_alpha_field(data, nbytes, textlenp)
+	u_char *data;
+	unsigned nbytes, *textlenp;
+{
+	if (data[0] < 0x80)
+		return validate_classic_gsm(data, nbytes, textlenp);
+	switch (data[0]) {
+	case 0x80:
+		return validate_ucs2_80(data, nbytes, textlenp);
+	case 0x81:
+		return validate_ucs2_81(data, nbytes, textlenp);
+	case 0x82:
+		return validate_ucs2_82(data, nbytes, textlenp);
+	case 0xFF:
+		return validate_empty(data, nbytes, textlenp);
+	default:
+		return -1;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/decimal_str.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,41 @@
+/*
+ * This module implements some functions for initial parsing of decimal
+ * string arguments, intended for implementation of commands like
+ * write-iccid and write-imsi.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+
+parse_decimal_string_arg(arg, dest, maxdigits)
+	char *arg;
+	u_char *dest;
+	unsigned maxdigits;
+{
+	unsigned n, ndig;
+
+	if (!*arg) {
+		fprintf(stderr,
+			"error: empty argument given for decimal string\n");
+		return(-1);
+	}
+	for (n = 0; *arg; ) {
+		if (!isdigit(*arg)) {
+			fprintf(stderr,
+			"error: non-digit char in decimal string argument\n");
+			return(-1);
+		}
+		if (n >= maxdigits) {
+			fprintf(stderr,
+			"error: decimal string exceeds limit of %u digits\n",
+				maxdigits);
+			return(-1);
+		}
+		dest[n++] = *arg++ - '0';
+	}
+	ndig = n;
+	while (n < maxdigits)
+		dest[n++] = 0xF;
+	return ndig;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/filesearch.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,27 @@
+/*
+ * This module implements the function that searches for files
+ * in a dedicated directory for SIM programming scripts.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+static char script_install_dir[] = "/opt/freecalypso/sim-scripts";
+
+FILE *
+open_script_input_file(req_filename)
+	char *req_filename;
+{
+	char pathbuf[256];
+	FILE *f;
+
+	if (!index(req_filename, '/') && strlen(req_filename) < 128) {
+		sprintf(pathbuf, "%s/%s", script_install_dir, req_filename);
+		f = fopen(pathbuf, "r");
+		if (f)
+			return f;
+	}
+	f = fopen(req_filename, "r");
+	return f;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/gsm7_decode.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,88 @@
+/*
+ * This module contains functions for decoding GSM7 strings
+ * that exist in various SIM files.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+
+static char gsm7_decode_table[128] = {
+	'@', 0,   '$',  0,   0,   0,    0,   0,
+	0,   0,   '\n', 0,   0,   '\r', 0,   0,
+	0,   '_', 0,    0,   0,   0,    0,   0,
+	0,   0,   0,    0,   0,   0,    0,   0,
+	' ', '!', '"',  '#', 0,   '%',  '&', 0x27,
+	'(', ')', '*',  '+', ',', '-',  '.', '/',
+	'0', '1', '2',  '3', '4', '5',  '6', '7',
+	'8', '9', ':',  ';', '<', '=',  '>', '?',
+	0,   'A', 'B',  'C', 'D', 'E',  'F', 'G',
+	'H', 'I', 'J',  'K', 'L', 'M',  'N', 'O',
+	'P', 'Q', 'R',  'S', 'T', 'U',  'V', 'W',
+	'X', 'Y', 'Z',  0,   0,   0,    0,   0,
+	0,   'a', 'b',  'c', 'd', 'e',  'f', 'g',
+	'h', 'i', 'j',  'k', 'l', 'm',  'n', 'o',
+	'p', 'q', 'r',  's', 't', 'u',  'v', 'w',
+	'x', 'y', 'z',  0,   0,   0,    0,   0
+};
+
+static char gsm7ext_decode_table[128] = {
+	0,   0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0,
+	0,   0, 0, 0, '^', 0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0,
+	0,   0, 0, 0, 0,   0, 0, 0, '{', '}', 0, 0, 0,   0,   0,   '\\',
+	0,   0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, '[', '~', ']', 0,
+	'|', 0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0,
+	0,   0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0,
+	0,   0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0,
+	0,   0, 0, 0, 0,   0, 0, 0, 0,   0,   0, 0, 0,   0,   0,   0
+};
+
+void
+print_gsm7_string_to_file(data, nbytes, outf)
+	u_char *data;
+	unsigned nbytes;
+	FILE *outf;
+{
+	u_char *dp, *endp;
+	int b, c;
+
+	dp = data;
+	endp = data + nbytes;
+	putc('"', outf);
+	while (dp < endp) {
+		b = *dp++;
+		if (b == 0x1B) {
+			if (dp >= endp || *dp == 0x1B || *dp == '\n' ||
+			    *dp == '\r') {
+				putc('\\', outf);
+				putc('e', outf);
+				continue;
+			}
+			b = *dp++;
+			c = gsm7ext_decode_table[b];
+			if (!c) {
+				fprintf(outf, "\\e\\%02X", b);
+				continue;
+			}
+		} else {
+			c = gsm7_decode_table[b];
+			if (!c) {
+				fprintf(outf, "\\%02X", b);
+				continue;
+			}
+		}
+		if (c == '\n') {
+			putc('\\', outf);
+			putc('n', outf);
+			continue;
+		}
+		if (c == '\r') {
+			putc('\\', outf);
+			putc('r', outf);
+			continue;
+		}
+		if (c == '"' || c == '\\')
+			putc('\\', outf);
+		putc(c, outf);
+	}
+	putc('"', outf);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/gsm7_encode.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,76 @@
+/*
+ * This module implements functions for parsing quoted string
+ * arguments intended for GSM7 string encoding, and actually
+ * encoding them into GSM7 binary strings.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+
+extern u_char gsm7_encode_table[256];
+
+qstring_arg_to_gsm7(arg, record, maxlen)
+	char *arg;
+	u_char *record;
+	unsigned maxlen;
+{
+	unsigned acclen, nadd;
+	char *cp;
+	int c;
+
+	cp = arg;
+	for (acclen = 0; *cp; ) {
+		c = *cp++;
+		if (c == '\\') {
+			if (*cp == '\0') {
+				fprintf(stderr,
+					"error: dangling backslash escape\n");
+				return(-1);
+			}
+			c = *cp++;
+			if (c >= '0' && c <= '7' && isxdigit(*cp)) {
+				c = ((c - '0') << 4) | decode_hex_digit(*cp++);
+				goto bypass_encoding;
+			}
+			switch (c) {
+			case 'n':
+				c = '\n';
+				goto bypass_encoding;
+			case 'r':
+				c = '\r';
+				goto bypass_encoding;
+			case 'e':
+				c = 0x1B;
+				goto bypass_encoding;
+			case '"':
+			case '\\':
+				break;
+			default:
+				fprintf(stderr,
+				"error: non-understood backslash escape\n");
+				return(-1);
+			}
+		}
+		c = gsm7_encode_table[c];
+		if (c == 0xFF) {
+			fprintf(stderr,
+	"error: character in alpha tag string cannot be encoded in GSM7\n");
+			return(-1);
+		}
+bypass_encoding:
+		if (c & 0x80)
+			nadd = 2;
+		else
+			nadd = 1;
+		if (acclen + nadd > maxlen) {
+			fprintf(stderr,
+			"error: alpha tag string is longer than SIM limit\n");
+			return(-1);
+		}
+		if (c & 0x80)
+			record[acclen++] = 0x1B;
+		record[acclen++] = c & 0x7F;
+	}
+	return(acclen);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/gsm7_encode_table.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,43 @@
+/*
+ * This library module contains the table for encoding from ISO 8859-1
+ * into the GSM 7-bit default alphabet (03.38 or 23.038).  High bit set
+ * in the output indicates escape encoding, used for ASCII characters
+ * [\]^ and {|}~.  0xFF indicates invalid chars.
+ */
+
+#include <sys/types.h>
+
+u_char gsm7_encode_table[256] = {
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,	/* 0x00 */
+	0xFF, 0xFF, '\n', 0xFF, 0xFF, '\r', 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,	/* 0x10 */
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	' ',  '!',  '"',  '#',  0x02, '%',  '&',  0x27,	/* 0x20 */
+	'(',  ')',  '*',  '+',  ',',  '-',  '.',  '/',
+	'0',  '1',  '2',  '3',  '4',  '5',  '6',  '7',	/* 0x30 */
+	'8',  '9',  ':',  ';',  '<',  '=',  '>',  '?',
+	0x00, 'A',  'B',  'C',  'D',  'E',  'F',  'G',	/* 0x40 */
+	'H',  'I',  'J',  'K',  'L',  'M',  'N',  'O',
+	'P',  'Q',  'R',  'S',  'T',  'U',  'V',  'W',	/* 0x50 */
+	'X',  'Y',  'Z',  0xBC, 0xAF, 0xBE, 0x94, 0x11,
+	0xFF, 'a',  'b',  'c',  'd',  'e',  'f',  'g',	/* 0x60 */
+	'h',  'i',  'j',  'k',  'l',  'm',  'n',  'o',
+	'p',  'q',  'r',  's',  't',  'u',  'v',  'w',	/* 0x70 */
+	'x',  'y',  'z',  0xA8, 0xC0, 0xA9, 0xBD, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,	/* 0x80 */
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,	/* 0x90 */
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x40, 0xFF, 0x01, 0x24, 0x03, 0xFF, 0x5F,	/* 0xA0 */
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,	/* 0xB0 */
+	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60,
+	0xFF, 0xFF, 0xFF, 0xFF, 0x5B, 0x0E, 0x1C, 0x09,	/* 0xC0 */
+	0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0x5C, 0xFF,	/* 0xD0 */
+	0x0B, 0xFF, 0xFF, 0xFF, 0x5E, 0xFF, 0xFF, 0x1E,
+	0x7F, 0xFF, 0xFF, 0xFF, 0x7B, 0x0F, 0x1D, 0xFF,	/* 0xE0 */
+	0x04, 0x05, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF,
+	0xFF, 0x7D, 0x08, 0xFF, 0xFF, 0xFF, 0x7C, 0xFF,	/* 0xF0 */
+	0x0C, 0x06, 0xFF, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/gsm7_pack.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,22 @@
+/*
+ * This library module implements the function for packing septets into octets.
+ */
+
+#include <sys/types.h>
+
+gsm7_pack(inbuf, outbuf, noctets)
+	u_char *inbuf, *outbuf;
+	unsigned noctets;
+{
+	u_char *ip = inbuf, *op = outbuf;
+	unsigned n, c;
+
+	for (n = 0; n < noctets; n++) {
+		c = n % 7;
+		*op++ = ((ip[1] << 7) | ip[0]) >> c;
+		if (c == 6)
+			ip += 2;
+		else
+			ip += 1;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/gsm7_unpack.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,22 @@
+/*
+ * This library module implements unpacking of GSM 7-bit data
+ * from packed octets.
+ */
+
+#include <sys/types.h>
+
+static u_char shift[8] = {0, 7, 6, 5, 4, 3, 2, 1};
+
+gsm7_unpack(inbuf, outbuf, nseptets)
+	u_char *inbuf, *outbuf;
+	unsigned nseptets;
+{
+	u_char *inp = inbuf, *outp = outbuf;
+	unsigned n;
+
+	for (n = 0; n < nseptets; n++) {
+		*outp++ = (((inp[1] << 8) | inp[0]) >> shift[n&7]) & 0x7F;
+		if (n & 7)
+			inp++;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/hexdigits.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,23 @@
+/*
+ * This module contains elementary functions for working with hex digits.
+ */
+
+decode_hex_digit(c)
+{
+	if (c >= '0' && c <= '9')
+		return(c - '0');
+	if (c >= 'A' && c <= 'F')
+		return(c - 'A' + 10);
+	if (c >= 'a' && c <= 'f')
+		return(c - 'a' + 10);
+	return(-1);
+}
+
+encode_hex_digit(d)
+	unsigned d;
+{
+	if (d <= 9)
+		return(d + '0');
+	else
+		return(d - 10 + 'A');
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/hexread.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,63 @@
+/*
+ * This module contains the function for reading hex files,
+ * to be used in the implementation of manual write commands.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+extern FILE *open_script_input_file();
+
+read_hex_data_file(filename, databuf, maxlen)
+	char *filename;
+	u_char *databuf;
+	unsigned maxlen;
+{
+	FILE *inf;
+	unsigned count;
+	int c, c2;
+
+	inf = open_script_input_file(filename);
+	if (!inf) {
+		perror(filename);
+		return(-1);
+	}
+	for (count = 0; ; ) {
+		do
+			c = getc(inf);
+		while (isspace(c));
+		if (c < 0)
+			break;
+		if (c == '#') {
+			do
+				c = getc(inf);
+			while (c >= 0 && c != '\n');
+			continue;
+		}
+		if (!isxdigit(c)) {
+inv_input:		fprintf(stderr, "%s: invalid hex file input\n",
+				filename);
+			fclose(inf);
+			return(-1);
+		}
+		c2 = getc(inf);
+		if (!isxdigit(c2))
+			goto inv_input;
+		if (count >= maxlen) {
+			fprintf(stderr, "%s: hex input data is too long\n",
+				filename);
+			fclose(inf);
+			return(-1);
+		}
+		databuf[count++] = (decode_hex_digit(c) << 4) |
+				   decode_hex_digit(c2);
+	}
+	fclose(inf);
+	if (!count) {
+		fprintf(stderr, "%s: no hex data input found\n", filename);
+		return(-1);
+	}
+	return(count);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/hexstr.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,38 @@
+/*
+ * This module contains the function for decoding hex strings.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+
+decode_hex_data_from_string(arg, databuf, minlen, maxlen)
+	char *arg;
+	u_char *databuf;
+	unsigned minlen, maxlen;
+{
+	unsigned count;
+
+	for (count = 0; ; count++) {
+		while (isspace(*arg))
+			arg++;
+		if (!*arg)
+			break;
+		if (!isxdigit(arg[0]) || !isxdigit(arg[1])) {
+			fprintf(stderr, "error: invalid hex string input\n");
+			return(-1);
+		}
+		if (count >= maxlen) {
+			fprintf(stderr, "error: hex string is too long\n");
+			return(-1);
+		}
+		databuf[count] = (decode_hex_digit(arg[0]) << 4) |
+				 decode_hex_digit(arg[1]);
+		arg += 2;
+	}
+	if (count < minlen) {
+		fprintf(stderr, "error: hex string is too short\n");
+		return(-1);
+	}
+	return(count);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/iccid_luhn.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,27 @@
+/*
+ * This module implements a function for computing the Luhn check digit
+ * for ICCIDs that follow the 18+1 convention.
+ */
+
+#include <sys/types.h>
+
+compute_iccid_luhn(digits)
+	u_char *digits;
+{
+	int i, dig, sum;
+
+	sum = 0;
+	for (i = 0; i < 18; i++) {
+		dig = digits[i];
+		if (i & 1) {
+			dig *= 2;
+			if (dig > 9)
+				dig -= 9;
+		}
+		sum += dig;
+	}
+	dig = sum % 10;
+	if (dig)
+		dig = 10 - dig;
+	return dig;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/nibbles2asc.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,19 @@
+/*
+ * This module implements a function for turning a nibble array
+ * into printable ASCII.
+ */
+
+#include <sys/types.h>
+
+void
+nibbles_to_ascii(nib, len, out)
+	u_char *nib;
+	unsigned len;
+	char *out;
+{
+	unsigned n;
+
+	for (n = 0; n < len; n++)
+		*out++ = encode_hex_digit(*nib++);
+	*out = '\0';
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/number_decode.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,58 @@
+/*
+ * This module implements functions for decoding phone numbers.
+ */
+
+#include <sys/types.h>
+
+static char gsm_address_digits[16] =
+	{'0','1','2','3','4','5','6','7','8','9','*','#','a','b','c','?'};
+
+decode_phone_number(data, nbytes, out)
+	u_char *data;
+	unsigned nbytes;
+	char *out;
+{
+	u_char *dp, *endp;
+	int c;
+
+	dp = data;
+	endp = data + nbytes;
+	while (dp < endp) {
+		c = *dp & 0xF;
+		if (c == 0xF)
+			return(-1);
+		*out++ = gsm_address_digits[c];
+		c = *dp >> 4;
+		if (c == 0xF) {
+			if (dp + 1 == endp)
+				break;
+			else
+				return(-1);
+		}
+		*out++ = gsm_address_digits[c];
+		dp++;
+	}
+	*out = '\0';
+	return(0);
+}
+
+decode_address_digits(inbuf, outbuf, ndigits)
+	u_char *inbuf;
+	char *outbuf;
+	unsigned ndigits;
+{
+	u_char *inp = inbuf;
+	char *outp = outbuf;
+	unsigned n = 0, b;
+
+	while (n < ndigits) {
+		b = *inp++;
+		*outp++ = gsm_address_digits[b & 0xF];
+		n++;
+		if (n >= ndigits)
+			break;
+		*outp++ = gsm_address_digits[b >> 4];
+		n++;
+	}
+	*outp = '\0';
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/number_encode.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,104 @@
+/*
+ * This module implements functions for encoding phone numbers.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+digit_char_to_gsm(ch)
+{
+	switch (ch) {
+	case '0':
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+	case '8':
+	case '9':
+		return (ch - '0');
+	case '*':
+		return 0xA;
+	case '#':
+		return 0xB;
+	case 'a':
+	case 'b':
+	case 'c':
+		return (ch - 'a' + 0xC);
+	case 'A':
+	case 'B':
+	case 'C':
+		return (ch - 'A' + 0xC);
+	}
+	return (-1);
+}
+
+void
+pack_digit_bytes(digits, dest, num_digit_bytes)
+	u_char *digits, *dest;
+	unsigned num_digit_bytes;
+{
+	u_char *sp, *dp;
+	unsigned n;
+
+	sp = digits;
+	dp = dest;
+	for (n = 0; n < num_digit_bytes; n++) {
+		*dp++ = sp[0] | (sp[1] << 4);
+		sp += 2;
+	}
+}
+
+encode_phone_number_arg(arg, fixp, mode)
+	char *arg;
+	u_char *fixp;
+{
+	u_char digits[20];
+	unsigned ndigits, num_digit_bytes;
+	char *cp, *endp;
+	int c;
+
+	cp = arg;
+	if (*cp == '+') {
+		fixp[1] = 0x91;
+		cp++;
+	} else
+		fixp[1] = 0x81;
+	if (digit_char_to_gsm(*cp) < 0) {
+inv_arg:	fprintf(stderr, "error: invalid phone number argument\n");
+		return(-1);
+	}
+	for (ndigits = 0; ; ndigits++) {
+		c = digit_char_to_gsm(*cp);
+		if (c < 0)
+			break;
+		cp++;
+		if (ndigits >= 20) {
+			fprintf(stderr, "error: too many number digits\n");
+			return(-1);
+		}
+		digits[ndigits] = c;
+	}
+	if (mode)
+		fixp[0] = ndigits;
+	if (ndigits & 1)
+		digits[ndigits++] = 0xF;
+	num_digit_bytes = ndigits >> 1;
+	if (!mode)
+		fixp[0] = num_digit_bytes + 1;
+	pack_digit_bytes(digits, fixp + 2, num_digit_bytes);
+	if (*cp == ',') {
+		cp++;
+		if (!isdigit(*cp))
+			goto inv_arg;
+		fixp[1] = strtoul(cp, &endp, 0);
+		if (*endp)
+			goto inv_arg;
+	} else if (*cp)
+		goto inv_arg;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/pinentry.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,28 @@
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+
+encode_pin_entry(arg, dest)
+	char *arg;
+	u_char *dest;
+{
+	unsigned n;
+
+	n = 0;
+	while (*arg) {
+		if (!isdigit(*arg)) {
+			fprintf(stderr,
+			"error: PIN argument contains a non-digit character\n");
+			return(-1);
+		}
+		if (n >= 8) {
+			fprintf(stderr, "error: PIN argument is too long\n");
+			return(-1);
+		}
+		*dest++ = *arg++;
+		n++;
+	}
+	for (; n < 8; n++)
+		*dest++ = 0xFF;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/plmncodes.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,58 @@
+/*
+ * This module implements some functions for working with MCC-MNC PLMN codes.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+
+decode_plmn_3bytes(bin, asc, space_pad)
+	u_char *bin;
+	char *asc;
+{
+	asc[0] = encode_hex_digit(bin[0] & 0xF);
+	asc[1] = encode_hex_digit(bin[0] >> 4);
+	asc[2] = encode_hex_digit(bin[1] & 0xF);
+	asc[3] = '-';
+	asc[4] = encode_hex_digit(bin[2] & 0xF);
+	asc[5] = encode_hex_digit(bin[2] >> 4);
+	asc[6] = encode_hex_digit(bin[1] >> 4);
+	asc[7] = '\0';
+	if (asc[6] == 'F') {
+		if (space_pad)
+			asc[6] = ' ';
+		else
+			asc[6] = '\0';
+	}
+}
+
+encode_plmn_3bytes(asc, bin)
+	char *asc;
+	u_char *bin;
+{
+	u_char mcc[3], mnc[3];
+
+	if (!isxdigit(asc[0]) || !isxdigit(asc[1]) || !isxdigit(asc[2]))
+		return(-1);
+	mcc[0] = decode_hex_digit(asc[0]);
+	mcc[1] = decode_hex_digit(asc[1]);
+	mcc[2] = decode_hex_digit(asc[2]);
+	asc += 3;
+	if (*asc == '-')
+		asc++;
+	if (!isxdigit(asc[0]) || !isxdigit(asc[1]))
+		return(-1);
+	mnc[0] = decode_hex_digit(asc[0]);
+	mnc[1] = decode_hex_digit(asc[1]);
+	asc += 2;
+	if (*asc == '\0')
+		mnc[2] = 0xF;
+	else if (isxdigit(asc[0]) && asc[1] == '\0')
+		mnc[2] = decode_hex_digit(*asc);
+	else
+		return(-1);
+	bin[0] = (mcc[1] << 4) | mcc[0];
+	bin[1] = (mnc[2] << 4) | mcc[2];
+	bin[2] = (mnc[1] << 4) | mnc[0];
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/plmnlist.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,69 @@
+/*
+ * This module implements a function for reading PLMN lists from files.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+extern FILE *open_script_input_file();
+
+read_plmn_list_from_file(filename, buf, ef_len)
+	char *filename;
+	u_char *buf;
+	unsigned ef_len;
+{
+	FILE *inf;
+	int lineno, rc;
+	char linebuf[1024], *cp, *np;
+	u_char *dp, *endp;
+
+	inf = open_script_input_file(filename);
+	if (!inf) {
+		perror(filename);
+		return(-1);
+	}
+	dp = buf;
+	endp = buf + ef_len;
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) {
+		if (!index(linebuf, '\n')) {
+			fprintf(stderr,
+				"%s line %d: too long or missing newline\n",
+				filename, lineno);
+			fclose(inf);
+			return(-1);
+		}
+		for (cp = linebuf; ; ) {
+			while (isspace(*cp))
+				cp++;
+			if (*cp == '\0' || *cp == '#')
+				break;
+			for (np = cp; *cp && !isspace(*cp); cp++)
+				;
+			if (*cp)
+				*cp++ = '\0';
+			if (dp >= endp) {
+				fprintf(stderr,
+			"%s line %d: number of PLMN codes exceeds EF size\n",
+					filename, lineno);
+				fclose(inf);
+				return(-1);
+			}
+			rc = encode_plmn_3bytes(np, dp);
+			if (rc < 0) {
+				fprintf(stderr, "%s line %d: invalid MCC-MNC\n",
+					filename, lineno);
+				fclose(inf);
+				return(-1);
+			}
+			dp += 3;
+		}
+	}
+	fclose(inf);
+	while (dp < endp)
+		*dp++ = 0xFF;
+	return(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/revnibbles.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,40 @@
+/*
+ * This module implements some reversed-nibbles parsing functions.
+ */
+
+#include <sys/types.h>
+
+decode_reversed_nibbles(bytes, nbytes, dest)
+	u_char *bytes;
+	unsigned nbytes;
+	char *dest;
+{
+	u_char *sp;
+	char *dp;
+	unsigned n, c;
+
+	sp = bytes;
+	dp = dest;
+	for (n = 0; n < nbytes; n++) {
+		c = *sp & 0xF;
+		*dp++ = encode_hex_digit(c);
+		c = *sp >> 4;
+		*dp++ = encode_hex_digit(c);
+		sp++;
+	}
+}
+
+pack_reversed_nibbles(nibbles, bytes, nbytes)
+	u_char *nibbles, *bytes;
+	unsigned nbytes;
+{
+	u_char *sp, *dp;
+	unsigned n;
+
+	sp = nibbles;
+	dp = bytes;
+	for (n = 0; n < nbytes; n++) {
+		*dp++ = sp[0] | (sp[1] << 4);
+		sp += 2;
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libutil/shorthand.c	Sun Mar 14 05:42:37 2021 +0000
@@ -0,0 +1,64 @@
+/*
+ * This module implements the function for parsing shorthand decimal strings.
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+
+parse_decimal_shorthand(arg, dest, maxdigits)
+	char *arg;
+	u_char *dest;
+	unsigned maxdigits;
+{
+	unsigned n, ntail;
+
+	if (!*arg) {
+		fprintf(stderr,
+			"error: empty argument given for decimal string\n");
+		return(-1);
+	}
+	if (!isdigit(*arg)) {
+		fprintf(stderr,
+		"error: decimal string argument begins with a non-digit\n");
+		return(-1);
+	}
+	for (n = 0; isdigit(*arg); ) {
+		if (n >= maxdigits) {
+toolong:		fprintf(stderr,
+			"error: decimal string exceeds limit of %u digits\n",
+				maxdigits);
+			return(-1);
+		}
+		dest[n++] = *arg++ - '0';
+	}
+	if (!*arg) {
+		if (n != maxdigits) {
+			fprintf(stderr,
+				"error: %u digits required, %u digits given\n",
+				maxdigits, n);
+			return(-1);
+		}
+		return(0);
+	}
+	if (*arg++ != '-') {
+malformed:	fprintf(stderr,
+			"error: malformed shorthand decimal string argument\n");
+		return(-1);
+	}
+	ntail = strlen(arg);
+	if (n + ntail >= maxdigits)
+		goto toolong;
+	while (n < maxdigits - ntail)
+		dest[n++] = 0;
+	if (!isdigit(*arg))
+		goto malformed;
+	while (*arg) {
+		if (!isdigit(*arg))
+			goto malformed;
+		dest[n++] = *arg++ - '0';
+	}
+	return(0);
+}