From a175bb1f1a8d9a84a6f0fde64eb458daa6115c3b Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Thu, 18 Jul 2013 15:29:16 +0000 Subject: [PATCH] od: add od clone --- od/od.c | 556 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 556 insertions(+) create mode 100644 od/od.c diff --git a/od/od.c b/od/od.c new file mode 100644 index 0000000..0c789cd --- /dev/null +++ b/od/od.c @@ -0,0 +1,556 @@ +/* + * od.c + * + * od clone for PDP10 files with 9-bit bytes + */ +#include +#include +#include +#include +#include +#include +#include /* getopt() */ +#include "pdp10-extint.h" +#include "pdp10-inttypes.h" +#include "pdp10-stdio.h" + +#define VERSION "pdp10-tools od version 0.1, built " __DATE__ " " __TIME__ "\n" + +struct options { + /* user options: */ + char address_radix; /* -A/--address-radix= */ + unsigned long skip_bytes; /* -j/--skip-bytes= */ + unsigned long read_bytes; /* -N/--read-bytes= */ + char output_type; /* 'c', 'd', 'o', 'u', 'x'; 'a' and 'f' are NYI */ + unsigned char output_z; /* type has trailing 'z' */ + unsigned int bytes_per_datum; /* 1, 2, or 4 */ + unsigned long width; /* -w/--with= */ + unsigned char version; /* -V */ + /* compiled options: */ + char numfmt[8]; /* "%0*{,l,ll}[doux]\0" */ + int chars_per_datum; + unsigned int datums_per_line; +}; + +struct input { + unsigned long read_bytes; + char **files; + unsigned int nrfiles; + PDP10_FILE *pdp10fp; +}; + +static void input_init(struct input *input, unsigned long read_bytes, char **files, unsigned int nrfiles) +{ + input->read_bytes = read_bytes; + input->files = files; + input->nrfiles = nrfiles; + input->pdp10fp = NULL; +} + +static void input_fini(struct input *input) +{ + if (input->pdp10fp) + pdp10_fclose(input->pdp10fp); +} + +static int input_fgetc_raw(struct input *input, const char *progname) +{ + int ch; + const char *filename; + + for (;;) { + if (input->pdp10fp) { + ch = pdp10_fgetc(input->pdp10fp); + if (ch != EOF) + return ch; + pdp10_fclose(input->pdp10fp); + input->pdp10fp = NULL; + } + + if (input->nrfiles == 0) + return EOF; + + filename = input->files[0]; + ++input->files; + --input->nrfiles; + + /* XXX: call pdp10_fdopen() if filename is "-" */ + if (strcmp(filename, "-") == 0) + filename = "/dev/stdin"; + input->pdp10fp = pdp10_fopen(filename, "rb"); + if (!input->pdp10fp) { + fprintf(stderr, "%s: %s: failed to open: %s\n", progname, filename, strerror(errno)); + input->nrfiles = 0; + return EOF; + } + } +} + +static int input_fgetc_limited(struct input *input, const char *progname) +{ + int ch; + + if (input->read_bytes == 0) + return EOF; + + ch = input_fgetc_raw(input, progname); + if (ch == EOF) + return ch; + + if (input->read_bytes != -1UL) + --input->read_bytes; + + return ch; +} + +static int od1(const char *progname, struct options *options, struct input *input) +{ + pdp10_uint36_t offset; + const unsigned int bytes_per_line = options->bytes_per_datum * options->datums_per_line; + pdp10_uint9_t line_bytes[bytes_per_line]; + unsigned int nrbytes, b; + int ch; + pdp10_uint36_t datum; + + offset = 0; + + for (; options->skip_bytes > 0; ++offset, --options->skip_bytes) { + if (input_fgetc_raw(input, progname) == EOF) { + fprintf(stderr, "%s: cannot skip past end of combined input\n", progname); + return 1; + } + } + + for (;;) { + + switch (options->address_radix) { + case 'o': + printf("%011" PDP10_PRIo36, offset); + break; + case 'd': + printf("%010" PDP10_PRIu36, offset); + break; + case 'x': + printf("%09" PDP10_PRIx36, offset); + break; + } + + for (nrbytes = 0; nrbytes < bytes_per_line; ++nrbytes) { + ch = input_fgetc_limited(input, progname); + if (ch == EOF) + break; + line_bytes[nrbytes] = ch; + } + for (b = nrbytes; b < bytes_per_line; ++b) + line_bytes[b] = 0; + + if (nrbytes == 0) { + printf("\n"); + break; + } + + for (b = 0; b < nrbytes; b += options->bytes_per_datum) { + if (options->output_type == 'c') { + if ((unsigned char)line_bytes[b] == line_bytes[b] + && isprint(line_bytes[b])) { + printf(" %c", line_bytes[b]); + continue; + } + switch (line_bytes[b]) { + case '\0': + printf(" \\0"); + break; + case '\t': + printf(" \\t"); + break; + case '\r': + printf(" \\r"); + break; + case '\n': + printf(" \\n"); + break; + case '\f': + printf(" \\f"); + break; + case '\e': + printf(" \\e"); + break; + default: + printf(" %03o", line_bytes[b]); + break; + } + continue; + } + switch (options->bytes_per_datum) { + default: + case 1: + datum = line_bytes[b]; + if (options->output_type == 'd') { + const pdp10_uint36_t PDP10_UINT9_SBIT = ~(PDP10_UINT9_MAX >> 1) & PDP10_UINT9_MAX; + datum = ((datum & PDP10_UINT9_MAX) ^ PDP10_UINT9_SBIT) - PDP10_UINT9_SBIT; + } + break; + case 2: + { + struct pdp10_ext_uint18 ext18; + ext18.nonet[0] = line_bytes[b + 0]; + ext18.nonet[1] = line_bytes[b + 1]; + datum = pdp10_uint18_from_ext(&ext18); + if (options->output_type == 'd') { + const pdp10_uint36_t PDP10_UINT18_SBIT = ~(PDP10_UINT18_MAX >> 1) & PDP10_UINT18_MAX; + datum = ((datum & PDP10_UINT18_MAX) ^ PDP10_UINT18_SBIT) - PDP10_UINT18_SBIT; + } + break; + } + case 4: + { + struct pdp10_ext_uint36 ext36; + ext36.nonet[0] = line_bytes[b + 0]; + ext36.nonet[1] = line_bytes[b + 1]; + ext36.nonet[2] = line_bytes[b + 2]; + ext36.nonet[3] = line_bytes[b + 3]; + datum = pdp10_uint36_from_ext(&ext36); + if (options->output_type == 'd') { + const pdp10_uint36_t PDP10_UINT36_SBIT = ~(PDP10_UINT36_MAX >> 1) & PDP10_UINT36_MAX; + datum = ((datum & PDP10_UINT36_MAX) ^ PDP10_UINT36_SBIT) - PDP10_UINT36_SBIT; + } + break; + } + } + printf(" "); + printf(options->numfmt, options->chars_per_datum, datum); + } + + while (b < bytes_per_line) { + printf("%*s", options->chars_per_datum + 1, ""); + b += options->bytes_per_datum; + } + + if (options->output_z) { + printf(" >"); + for (b = 0; b < nrbytes; ++b) { + if ((unsigned char)line_bytes[b] == line_bytes[b] + && isprint(line_bytes[b])) + printf("%c", line_bytes[b]); + else + printf("."); + } + printf("<"); + } + printf("\n"); + + offset += nrbytes; + } + + return 0; +} + +static int od(const char *progname, struct options *options, char **files, int nrfiles) +{ + struct input input; + char fake_file[2]; + char *fake_files[1]; + int i; + int status; + + /* getopt() doesn't diagnose invalid short options, so we'll do that here */ + for (i = 0; i < nrfiles; ++i) { + if (files[i][0] == '-' && files[i][1] != '\0') { + fprintf(stderr, "%s: invalid option '%s'\n", progname, files[i]); + return 1; + } + } + + if (options->version) + printf(VERSION); + + if (nrfiles <= 0) { + fake_file[0] = '-'; + fake_file[1] = '\0'; + fake_files[0] = fake_file; + files = fake_files; + nrfiles = 1; + } + + input_init(&input, options->read_bytes, files, nrfiles); + status = od1(progname, options, &input); + input_fini(&input); + return status; +} + +/* + * Command-line interface. + */ + +static void usage(const char *progname) + +{ + fprintf(stderr, + "Usage: %s [-V] [-bcdDiloOsSxX] [-t [bcdoux][1248][z]] [-A RADIX] [-j BYTES] [-N BYTES] [-w [BYTES]] [files..]\n", + progname); +} + +static int parse_radix(const char *progname, struct options *options, const char *string) +{ + switch (string[0]) { + case 'd': + case 'o': + case 'x': + case 'n': + if (string[1] == '\0') { + options->address_radix = string[0]; + return 0; + } + /*FALLTHROUGH*/ + default: + fprintf(stderr, "%s: invalid radix '%s'\n", progname, string); + return -1; + } +} + +static int parse_bytes(const char *progname, unsigned long *dst, const char *string) +{ + unsigned long val; + char *end; + + errno = 0; + val = strtoul(string, &end, 0); + if ((val == 0 || val == ULONG_MAX) && errno != 0) { + fprintf(stderr, "%s: invalid number '%s': %s\n", progname, string, strerror(errno)); + return -1; + } + switch (*end) { + case 'b': + val *= 512; + ++end; + break; + case 'K': + val *= 1024; + ++end; + break; + case 'M': + val *= 1024 * 1024; + ++end; + break; + case 'G': + val *= 1024 * 1024 * 1024; + ++end; + break; + } + if (*end != '\0') { + fprintf(stderr, "%s: invalid multiplier in '%s'\n", progname, string); + return -1; + } + *dst = val; + return 0; +} + +static int parse_type(const char *progname, struct options *options, const char *string) +{ + const char *s; + + s = string; + switch (*s) { + case 'c': + case 'd': + case 'o': + case 'u': + case 'x': + options->output_type = *s; + break; + default: + fprintf(stderr, "%s: invalid type letter '%c' in type specifier '%s'\n", progname, *s, string); + return -1; + } + + if (*s == 'c') { + ++s; + options->bytes_per_datum = 1; + } else { + ++s; + switch (*s) { + case '1': + options->bytes_per_datum = 1; + ++s; + break; + case '2': + options->bytes_per_datum = 2; + ++s; + break; + case '4': + options->bytes_per_datum = 4; + ++s; + break; + case 'z': + case '\0': + options->bytes_per_datum = 4; + break; + default: + fprintf(stderr, "%s: invalid type size '%c' in type specifier '%s'\n", progname, *s, string); + return -1; + } + } + + if (*s == 'z') { + options->output_z = 1; + ++s; + } + + if (*s != '\0') { + fprintf(stderr, "%s: invalid suffix '%c' in type specifier '%s'\n", progname, *s, string); + return -1; + } + + return 0; +} + +static int compile_options(const char *progname, struct options *options) +{ + pdp10_uint36_t maxval; + char tmpbuf[16]; + + options->datums_per_line = options->width / options->bytes_per_datum; + if (options->datums_per_line * options->bytes_per_datum != options->width) { + fprintf(stderr, "%s: line width %lu is not a multiple of the input datum size %u\n", + progname, options->width, options->bytes_per_datum); + return -1; + } + + switch (options->output_type) { + case 'd': + sprintf(options->numfmt, "%% *%s", PDP10_PRId36); + break; + case 'u': + sprintf(options->numfmt, "%% *%s", PDP10_PRIu36); + break; + case 'o': + sprintf(options->numfmt, "%%0*%s", PDP10_PRIu36); + break; + case 'x': + sprintf(options->numfmt, "%%0*%s", PDP10_PRIx36); + break; + default: + options->chars_per_datum = 3; /* for -c */ + return 0; + } + + maxval = PDP10_UINT36_MAX; + if (options->bytes_per_datum < 4) + maxval >>= 18; + if (options->bytes_per_datum < 2) + maxval >>= 9; + + sprintf(tmpbuf, options->numfmt, 1, maxval); + options->chars_per_datum = strlen(tmpbuf); + + return 0; +} + +int main(int argc, char **argv) +{ + struct options options; + + memset(&options, 0, sizeof options); + options.address_radix = 'o'; + options.read_bytes = -1UL; + options.output_type = 'o'; + options.bytes_per_datum = 2; + options.width = 16; + + for (;;) { + int ch; + + ch = getopt(argc, argv, "VbcdDiloOsSxXA:j:N:t:w::"); + switch (ch) { + case 'V': + options.version = 1; + continue; + case 'b': /* == -t o1 */ + options.output_type = 'o'; + options.output_z = 0; + options.bytes_per_datum = 1; + break; + case 'c': /* == -t c */ + options.output_type = 'c'; + options.output_z = 0; + options.bytes_per_datum = 1; + continue; + case 'd': /* == -t u2 */ + options.output_type = 'u'; + options.output_z = 0; + options.bytes_per_datum = 2; + continue; + case 'D': /* == -t u4 */ + options.output_type = 'u'; + options.output_z = 0; + options.bytes_per_datum = 4; + continue; + case 'o': /* == -t o2 */ + options.output_type = 'o'; + options.output_z = 0; + options.bytes_per_datum = 2; + continue; + case 'O': /* == -t o4 */ + options.output_type = 'o'; + options.output_z = 0; + options.bytes_per_datum = 4; + continue; + case 's': /* == -t d2 */ + options.output_type = 'd'; + options.output_z = 0; + options.bytes_per_datum = 2; + continue; + case 'S': /* == -t d4 */ + case 'i': /* == -t d */ + case 'l': /* == -t d */ + options.output_type = 'd'; + options.output_z = 0; + options.bytes_per_datum = 4; + continue; + case 'x': /* == -t x2 */ + options.output_type = 'x'; + options.output_z = 0; + options.bytes_per_datum = 2; + continue; + case 'X': /* == -t x4 */ + options.output_type = 'x'; + options.output_z = 0; + options.bytes_per_datum = 4; + continue; + case 't': + if (parse_type(argv[0], &options, optarg) < 0) + goto do_usage; + continue; + case 'A': + if (parse_radix(argv[0], &options, optarg) < 0) + goto do_usage; + continue; + case 'j': + if (parse_bytes(argv[0], &options.skip_bytes, optarg) < 0) + goto do_usage; + continue; + case 'N': + if (parse_bytes(argv[0], &options.read_bytes, optarg) < 0) + goto do_usage; + continue; + case 'w': + if (!optarg) + options.width = 32; + else if (parse_bytes(argv[0], &options.width, optarg) < 0) + goto do_usage; + continue; + case -1: + break; + default: + do_usage: + usage(argv[0]); + return 1; + } + break; + } + + if (compile_options(argv[0], &options) < 0) + return 1; + + return od(argv[0], &options, &argv[optind], argc - optind); +}