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 
23 static int32_t	 getint(const char **);
24 static int	 copybyte(const char);
25 
26 
27 int
28 main(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 
108 static int32_t
109 getint(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 
126 static int
127 copybyte(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