1/*	$OpenBSD: binedit.c,v 1.1 2016/07/30 10:56:13 schwarze Exp $ */
2/*
3 * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17#include <ctype.h>
18#include <endian.h>
19#include <err.h>
20#include <stdint.h>
21#include <stdio.h>
22
23static int32_t	 getint(const char **);
24static int	 copybyte(const char);
25
26
27int
28main(int argc, char *argv[])
29{
30	const char	*cmd;	/* Command string from the command line. */
31	int32_t		 pos;	/* Characters read so far. */
32	int32_t		 dest;	/* Number of characters to be read. */
33	int32_t		 val;	/* Value to be written. */
34	int32_t		 i;	/* Auxiliary for reading and writing. */
35
36	if (argc != 2)
37		errx(1, "usage: binedit command_string");
38	cmd = argv[1];
39	dest = pos = val = 0;
40	while (*cmd != '\0') {
41		switch (*cmd++) {
42		case 'a':  /* Advance to destination. */
43			while (pos < dest) {
44				pos++;
45				if (copybyte('a') == EOF)
46					errx(1, "a: EOF");
47			}
48			break;
49		case 'c':  /* Copy. */
50			i = getint(&cmd);
51			pos += i;
52			while (i--)
53				if (copybyte('c') == EOF)
54					errx(1, "c: EOF");
55			break;
56		case 'd':  /* Set destination. */
57			dest = val;
58			break;
59		case 'f':  /* Finish. */
60			if (*cmd != '\0')
61				errx(1, "%s: not the last command", cmd - 1);
62			while (copybyte('f') != EOF)
63				continue;
64			break;
65		case 'i':  /* Increment. */
66			i = getint(&cmd);
67			if (i == 0)
68				i = 1;
69			val += i;
70			break;
71		case 'r':  /* Read. */
72			pos += sizeof(i);
73			if (fread(&i, sizeof(i), 1, stdin) != 1) {
74				if (ferror(stdin))
75					err(1, "r: fread");
76				else
77					errx(1, "r: EOF");
78			}
79			val = be32toh(i);
80			break;
81		case 's':  /* Skip. */
82			i = getint(&cmd);
83			pos += i;
84			while (i--) {
85				if (getchar() == EOF) {
86					if (ferror(stdin))
87						err(1, "s: getchar");
88					else
89						errx(1, "s: EOF");
90				}
91			}
92			break;
93		case 'w':  /* Write one integer. */
94			if (*cmd == '-' || *cmd == '+' ||
95			    isdigit((unsigned char)*cmd))
96				val = getint(&cmd);
97			i = htobe32(val);
98			if (fwrite(&i, sizeof(i), 1, stdout) != 1)
99				err(1, "w: fwrite");
100			break;
101		default:
102			errx(1, "%c: invalid command", cmd[-1]);
103		}
104	}
105	return 0;
106}
107
108static int32_t
109getint(const char **cmd)
110{
111	int32_t	 res;
112	int	 minus;
113
114	res = 0;
115	minus = 0;
116	if (**cmd == '-') {
117		minus = 1;
118		(*cmd)++;
119	} else if (**cmd == '+')
120		(*cmd)++;
121	while(isdigit((unsigned char)**cmd))
122		res = res * 10 + *(*cmd)++ - '0';
123	return minus ? -res : res;
124}
125
126static int
127copybyte(const char cmd)
128{
129	int	 ch;
130
131	if ((ch = getchar()) == EOF) {
132		if (ferror(stdin))
133			err(1, "%c: getchar", cmd);
134	} else if (putchar(ch) == EOF)
135		err(1, "%c: putchar", cmd);
136	return ch;
137}
138