xref: /openbsd/games/bcd/bcd.c (revision f6aab3d8)
1 /*	$OpenBSD: bcd.c,v 1.26 2018/01/23 07:06:55 otto Exp $	*/
2 /*	$NetBSD: bcd.c,v 1.6 1995/04/24 12:22:23 cgd Exp $	*/
3 
4 /*
5  * Copyright (c) 1989, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Steve Hayman of the Indiana University Computer Science Dept.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 /*
37  * bcd --
38  *
39  * Read one line of standard input and produce something that looks like a
40  * punch card.  An attempt to reimplement /usr/games/bcd.  All I looked at
41  * was the man page.
42  *
43  * I couldn't find a BCD table handy so I wrote a shell script to deduce what
44  * the patterns were that the old bcd was using for each possible 8-bit
45  * character.  These are the results -- the low order 12 bits represent the
46  * holes.  (A 1 bit is a hole.)  These may be wrong, but they match the old
47  * program!
48  *
49  * Steve Hayman
50  * sahayman@iuvax.cs.indiana.edu
51  * 1989 11 30
52  *
53  *
54  * I found an error in the table. The same error is found in the SunOS 4.1.1
55  * version of bcd. It has apparently been around a long time. The error caused
56  * 'Q' and 'R' to have the same punch code. I only noticed the error due to
57  * someone pointing it out to me when the program was used to print a cover
58  * for an APA!  The table was wrong in 4 places. The other error was masked
59  * by the fact that the input is converted to upper case before lookup.
60  *
61  * Dyane Bruce
62  * db@diana.ocunix.on.ca
63  * Nov 5, 1993
64  */
65 
66 #include <err.h>
67 #include <ctype.h>
68 #include <stdio.h>
69 #include <string.h>
70 #include <unistd.h>
71 
72 u_short holes[256] = {
73     0x0,	 0x0,	  0x0,	   0x0,	    0x0,     0x0,     0x0,     0x0,
74     0x0,	 0x0,	  0x0,	   0x0,	    0x0,     0x0,     0x0,     0x0,
75     0x0,	 0x0,	  0x0,	   0x0,	    0x0,     0x0,     0x0,     0x0,
76     0x0,	 0x0,	  0x0,	   0x0,	    0x0,     0x0,     0x0,     0x0,
77     0x0,	 0x206,	  0x20a,   0x042,   0x442,   0x222,   0x800,   0x406,
78     0x812,	 0x412,	  0x422,   0xa00,   0x242,   0x400,   0x842,   0x300,
79     0x200,	 0x100,	  0x080,   0x040,   0x020,   0x010,   0x008,   0x004,
80     0x002,	 0x001,	  0x012,   0x40a,   0x80a,   0x212,   0x00a,   0x006,
81     0x022,	 0x900,	  0x880,   0x840,   0x820,   0x810,   0x808,   0x804,
82     0x802,	 0x801,	  0x500,   0x480,   0x440,   0x420,   0x410,   0x408,
83     0x404,	 0x402,	  0x401,   0x280,   0x240,   0x220,   0x210,   0x208,
84     0x204,	 0x202,	  0x201,   0x082,   0x806,   0x822,   0x600,   0x282,
85     0x022,	 0x900,	  0x880,   0x840,   0x820,   0x810,   0x808,   0x804,
86     0x802,	 0x801,	  0x500,   0x480,   0x440,   0x420,   0x410,   0x408,
87     0x404,	 0x402,	  0x401,   0x280,   0x240,   0x220,   0x210,   0x208,
88     0x204,	 0x202,	  0x201,   0x082,   0x806,   0x822,   0x600,   0x282,
89     0x0,	 0x0,	  0x0,	   0x0,	    0x0,     0x0,     0x0,     0x0,
90     0x0,	 0x0,	  0x0,	   0x0,	    0x0,     0x0,     0x0,     0x0,
91     0x0,	 0x0,	  0x0,	   0x0,	    0x0,     0x0,     0x0,     0x0,
92     0x0,	 0x0,	  0x0,	   0x0,	    0x0,     0x0,     0x0,     0x0,
93     0x0,	 0x206,	  0x20a,   0x042,   0x442,   0x222,   0x800,   0x406,
94     0x812,	 0x412,	  0x422,   0xa00,   0x242,   0x400,   0x842,   0x300,
95     0x200,	 0x100,	  0x080,   0x040,   0x020,   0x010,   0x008,   0x004,
96     0x002,	 0x001,	  0x012,   0x40a,   0x80a,   0x212,   0x00a,   0x006,
97     0x022,	 0x900,	  0x880,   0x840,   0x820,   0x810,   0x808,   0x804,
98     0x802,	 0x801,	  0x500,   0x480,   0x440,   0x420,   0x410,   0x408,
99     0x404,	 0x402,	  0x401,   0x280,   0x240,   0x220,   0x210,   0x208,
100     0x204,	 0x202,	  0x201,   0x082,   0x806,   0x822,   0x600,   0x282,
101     0x022,	 0x900,	  0x880,   0x840,   0x820,   0x810,   0x808,   0x804,
102     0x802,	 0x801,	  0x500,   0x480,   0x440,   0x420,   0x410,   0x408,
103     0x404,	 0x402,	  0x401,   0x280,   0x240,   0x220,   0x210,   0x208,
104     0x204,	 0x202,	  0x201,   0x082,   0x806,   0x822,   0x600,   0x282,
105 };
106 
107 /*
108  * i'th bit of w.
109  */
110 #define	bit(w,i)	((w)&(1<<(i)))
111 
112 void	printonecard(char *, size_t);
113 void	printcard(char *);
114 int	decode(char *buf);
115 
116 int	columns	= 48;
117 
118 int
119 main(int argc, char *argv[])
120 {
121 	char cardline[1024];
122 	extern char *__progname;
123 	int dflag = 0;
124 	int ch;
125 
126 	if (pledge("stdio", NULL) == -1)
127 		err(1, "pledge");
128 
129 	while ((ch = getopt(argc, argv, "dl")) != -1) {
130 		switch (ch) {
131 		case 'd':
132 			dflag = 1;
133 			break;
134 		case 'l':
135 			columns = 80;
136 			break;
137 		default:
138 			fprintf(stderr, "usage: %s [-l] [string ...]\n",
139 			    __progname);
140 			fprintf(stderr, "usage: %s -d [-l]\n", __progname);
141 			return 1;
142 		}
143 	}
144 	argc -= optind;
145 	argv += optind;
146 
147 	if (dflag) {
148 		while (decode(cardline) == 0) {
149 			printf("%s\n", cardline);
150 		}
151 		return 0;
152 	}
153 
154 
155 	/*
156 	 * The original bcd prompts with a "%" when reading from stdin,
157 	 * but this seems kind of silly.  So this one doesn't.
158 	 */
159 	if (argc > 0) {
160 		while (argc--) {
161 			printcard(*argv);
162 			argv++;
163 		}
164 	} else {
165 		while (fgets(cardline, sizeof(cardline), stdin))
166 			printcard(cardline);
167 	}
168 	return 0;
169 }
170 
171 void
172 printcard(char *str)
173 {
174 	size_t len = strlen(str);
175 
176 	while (len > 0) {
177 		size_t amt = len > columns ? columns : len;
178 		printonecard(str, amt);
179 		str += amt;
180 		len -= amt;
181 	}
182 }
183 
184 void
185 printonecard(char *str, size_t len)
186 {
187 	static const char rowchars[] = "   123456789";
188 	int	i, row;
189 	char	*p, *end;
190 
191 	end = str + len;
192 
193 	/* make string upper case. */
194 	for (p = str; p < end; ++p)
195 		*p = toupper((unsigned char)*p);
196 
197 	/* top of card */
198 	putchar(' ');
199 	for (i = 1; i <= columns; ++i)
200 		putchar('_');
201 	putchar('\n');
202 
203 	/*
204 	 * line of text.  Leave a blank if the character doesn't have
205 	 * a hole pattern.
206 	 */
207 	p = str;
208 	putchar('/');
209 	for (i = 1; p < end; i++, p++)
210 		if (holes[(unsigned char)*p])
211 			putchar(*p);
212 		else
213 			putchar(' ');
214 	while (i++ <= columns)
215 		putchar(' ');
216 	putchar('|');
217 	putchar('\n');
218 
219 	/*
220 	 * 12 rows of potential holes; output a ']', which looks kind of
221 	 * like a hole, if the appropriate bit is set in the holes[] table.
222 	 * The original bcd output a '[', a backspace, five control A's,
223 	 * and then a ']'.  This seems a little excessive.
224 	 */
225 	for (row = 0; row <= 11; ++row) {
226 		putchar('|');
227 		for (i = 0, p = str; p < end; i++, p++) {
228 			if (bit(holes[(unsigned char)*p], 11 - row))
229 				putchar(']');
230 			else
231 				putchar(rowchars[row]);
232 		}
233 		while (i++ < columns)
234 			putchar(rowchars[row]);
235 		putchar('|');
236 		putchar('\n');
237 	}
238 
239 	/* bottom of card */
240 	putchar('|');
241 	for (i = 1; i <= columns; i++)
242 		putchar('_');
243 	putchar('|');
244 	putchar('\n');
245 }
246 
247 #define LINES 12
248 
249 int
250 decode(char *buf)
251 {
252 	int col, i;
253 	char lines[LINES][1024];
254 	char tmp[1024];
255 
256 	/* top of card; if missing signal no more input */
257 	if (fgets(tmp, sizeof(tmp), stdin) == NULL)
258 		return 1;
259 	/* text line, ignored */
260 	if (fgets(tmp, sizeof(tmp), stdin) == NULL)
261 		return -1;
262 	/* twelve lines of data */
263 	for (i = 0; i < LINES; i++)
264 		if (fgets(lines[i], sizeof(lines[i]), stdin) == NULL)
265 			return -1;
266 	/* bottom of card */
267 	if (fgets(tmp, sizeof(tmp), stdin) == NULL)
268 		return -1;
269 
270 	for (i = 0; i < LINES; i++) {
271 		if (strlen(lines[i]) < columns + 2)
272 			return -1;
273 		if (lines[i][0] != '|' || lines[i][columns + 1] != '|')
274 			return -1;
275 		memmove(&lines[i][0], &lines[i][1], columns);
276 		lines[i][columns] = 0;
277 	}
278 	for (col = 0; col < columns; col++) {
279 		unsigned int val = 0;
280 		for (i = 0; i < LINES; i++)
281 			if (lines[i][col] == ']')
282 				val |= 1 << (11 - i);
283 		buf[col] = ' ';
284 		for (i = 0; i < 256; i++)
285 			if (holes[i] == val && holes[i]) {
286 				buf[col] = i;
287 				break;
288 			}
289 	}
290 	buf[col] = 0;
291 	for (col = columns - 1; col >= 0; col--) {
292 		if (buf[col] == ' ')
293 			buf[col] = '\0';
294 		else
295 			break;
296 	}
297 	return 0;
298 }
299