worms.c revision 1.17
1/*	$OpenBSD: worms.c,v 1.17 2004/11/29 08:52:29 jsg Exp $	*/
2
3/*
4 * Copyright (c) 1980, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#ifndef lint
33static char copyright[] =
34"@(#) Copyright (c) 1980, 1993\n\
35	The Regents of the University of California.  All rights reserved.\n";
36#endif /* not lint */
37
38#ifndef lint
39#if 0
40static char sccsid[] = "@(#)worms.c	8.1 (Berkeley) 5/31/93";
41#else
42static char rcsid[] = "$OpenBSD: worms.c,v 1.17 2004/11/29 08:52:29 jsg Exp $";
43#endif
44#endif /* not lint */
45
46/*
47 *
48 *	 @@@	    @@@	   @@@@@@@@@@	  @@@@@@@@@@@	 @@@@@@@@@@@@
49 *	 @@@	    @@@	  @@@@@@@@@@@@	  @@@@@@@@@@@@	 @@@@@@@@@@@@@
50 *	 @@@	    @@@	 @@@@	   @@@@	  @@@@		 @@@@ @@@  @@@@
51 *	 @@@   @@   @@@	 @@@	    @@@	  @@@		 @@@  @@@   @@@
52 *	 @@@  @@@@  @@@	 @@@	    @@@	  @@@		 @@@  @@@   @@@
53 *	 @@@@ @@@@ @@@@	 @@@	    @@@	  @@@		 @@@  @@@   @@@
54 *	  @@@@@@@@@@@@	 @@@@	   @@@@	  @@@		 @@@  @@@   @@@
55 *	   @@@@	 @@@@	  @@@@@@@@@@@@	  @@@		 @@@  @@@   @@@
56 *	    @@	  @@	   @@@@@@@@@@	  @@@		 @@@  @@@   @@@
57 *
58 *				 Eric P. Scott
59 *			  Caltech High Energy Physics
60 *				 October, 1980
61 *
62 */
63#include <sys/types.h>
64
65#include <curses.h>
66#include <err.h>
67#include <signal.h>
68#include <stdio.h>
69#include <stdlib.h>
70#include <termios.h>
71#include <unistd.h>
72
73static const struct options {
74	int nopts;
75	int opts[3];
76}
77	normal[8] = {
78	{ 3, { 7, 0, 1 } },
79	{ 3, { 0, 1, 2 } },
80	{ 3, { 1, 2, 3 } },
81	{ 3, { 2, 3, 4 } },
82	{ 3, { 3, 4, 5 } },
83	{ 3, { 4, 5, 6 } },
84	{ 3, { 5, 6, 7 } },
85	{ 3, { 6, 7, 0 } }
86},	upper[8] = {
87	{ 1, { 1, 0, 0 } },
88	{ 2, { 1, 2, 0 } },
89	{ 0, { 0, 0, 0 } },
90	{ 0, { 0, 0, 0 } },
91	{ 0, { 0, 0, 0 } },
92	{ 2, { 4, 5, 0 } },
93	{ 1, { 5, 0, 0 } },
94	{ 2, { 1, 5, 0 } }
95},
96	left[8] = {
97	{ 0, { 0, 0, 0 } },
98	{ 0, { 0, 0, 0 } },
99	{ 0, { 0, 0, 0 } },
100	{ 2, { 2, 3, 0 } },
101	{ 1, { 3, 0, 0 } },
102	{ 2, { 3, 7, 0 } },
103	{ 1, { 7, 0, 0 } },
104	{ 2, { 7, 0, 0 } }
105},
106	right[8] = {
107	{ 1, { 7, 0, 0 } },
108	{ 2, { 3, 7, 0 } },
109	{ 1, { 3, 0, 0 } },
110	{ 2, { 3, 4, 0 } },
111	{ 0, { 0, 0, 0 } },
112	{ 0, { 0, 0, 0 } },
113	{ 0, { 0, 0, 0 } },
114	{ 2, { 6, 7, 0 } }
115},
116	lower[8] = {
117	{ 0, { 0, 0, 0 } },
118	{ 2, { 0, 1, 0 } },
119	{ 1, { 1, 0, 0 } },
120	{ 2, { 1, 5, 0 } },
121	{ 1, { 5, 0, 0 } },
122	{ 2, { 5, 6, 0 } },
123	{ 0, { 0, 0, 0 } },
124	{ 0, { 0, 0, 0 } }
125},
126	upleft[8] = {
127	{ 0, { 0, 0, 0 } },
128	{ 0, { 0, 0, 0 } },
129	{ 0, { 0, 0, 0 } },
130	{ 0, { 0, 0, 0 } },
131	{ 0, { 0, 0, 0 } },
132	{ 1, { 3, 0, 0 } },
133	{ 2, { 1, 3, 0 } },
134	{ 1, { 1, 0, 0 } }
135},
136	upright[8] = {
137	{ 2, { 3, 5, 0 } },
138	{ 1, { 3, 0, 0 } },
139	{ 0, { 0, 0, 0 } },
140	{ 0, { 0, 0, 0 } },
141	{ 0, { 0, 0, 0 } },
142	{ 0, { 0, 0, 0 } },
143	{ 0, { 0, 0, 0 } },
144	{ 1, { 5, 0, 0 } }
145},
146	lowleft[8] = {
147	{ 3, { 7, 0, 1 } },
148	{ 0, { 0, 0, 0 } },
149	{ 0, { 0, 0, 0 } },
150	{ 1, { 1, 0, 0 } },
151	{ 2, { 1, 7, 0 } },
152	{ 1, { 7, 0, 0 } },
153	{ 0, { 0, 0, 0 } },
154	{ 0, { 0, 0, 0 } }
155},
156	lowright[8] = {
157	{ 0, { 0, 0, 0 } },
158	{ 1, { 7, 0, 0 } },
159	{ 2, { 5, 7, 0 } },
160	{ 1, { 5, 0, 0 } },
161	{ 0, { 0, 0, 0 } },
162	{ 0, { 0, 0, 0 } },
163	{ 0, { 0, 0, 0 } },
164	{ 0, { 0, 0, 0 } }
165};
166
167static const char	flavor[] = {
168	'O', '*', '#', '$', '%', '0', '@', '~'
169};
170static const short	xinc[] = {
171	1,  1,  1,  0, -1, -1, -1,  0
172}, yinc[] = {
173	-1,  0,  1,  1,  1,  0, -1, -1
174};
175static struct	worm {
176	int orientation, head;
177	short *xpos, *ypos;
178} *worm;
179
180volatile sig_atomic_t sig_caught = 0;
181
182void	 nomem(void);
183void	 onsig(int);
184
185int
186main(int argc, char *argv[])
187{
188	int x, y, h, n;
189	struct worm *w;
190	const struct options *op;
191	short *ip;
192	int CO, LI, last, bottom, ch, length, number, trail;
193	short **ref;
194	const char *field;
195	struct termios term;
196	speed_t speed;
197	u_int delay = 0;
198
199	/* set default delay based on terminal baud rate */
200	if (tcgetattr(STDOUT_FILENO, &term) == 0 &&
201	    (speed = cfgetospeed(&term)) > B9600)
202		delay = (speed / B9600) - 1;
203
204	length = 16;
205	number = 3;
206	trail = ' ';
207	field = NULL;
208	while ((ch = getopt(argc, argv, "d:fhl:n:t")) != -1)
209		switch(ch) {
210		case 'd':
211			if ((delay = (u_int)strtoul(optarg, (char **)NULL, 10)) < 1
212			    || delay > 1000)
213				errx(1, "invalid delay (1-1000)");
214			delay *= 1000;  /* ms -> us */
215			break;
216		case 'f':
217			field = "WORM";
218			break;
219		case 'l':
220			if ((length = atoi(optarg)) < 2 || length > 1024)
221				errx(1, "invalid length (%d - %d).", 2, 1024);
222			break;
223		case 'n':
224			if ((number = atoi(optarg)) < 1 || number > 100)
225				errx(1, "invalid number of worms (%d - %d).",
226				    1, 100);
227			break;
228		case 't':
229			trail = '.';
230			break;
231		case '?': case 'h':
232		default:
233			(void)fprintf(stderr,
234			    "usage: worms [-ft] [-d delay] [-l length] [-n number]\n");
235			exit(1);
236		}
237
238	srandomdev();
239	if (!(worm = malloc((size_t)number * sizeof(struct worm))))
240		nomem();
241	initscr();
242	curs_set(0);
243	CO = COLS;
244	LI = LINES;
245	last = CO - 1;
246	bottom = LI - 1;
247	if (!(ip = malloc((size_t)(LI * CO * sizeof(short)))) ||
248	    !(ref = malloc((size_t)(LI * sizeof(short *))))) {
249		endwin();
250		nomem();
251	}
252	for (n = 0; n < LI; ++n) {
253		ref[n] = ip;
254		ip += CO;
255	}
256	for (ip = ref[0], n = LI * CO; --n >= 0;)
257		*ip++ = 0;
258	for (n = number, w = &worm[0]; --n >= 0; w++) {
259		w->orientation = w->head = 0;
260		if (!(ip = malloc((size_t)(length * sizeof(short))))) {
261			endwin();
262			nomem();
263		}
264		w->xpos = ip;
265		for (x = length; --x >= 0;)
266			*ip++ = -1;
267		if (!(ip = malloc((size_t)(length * sizeof(short))))) {
268			endwin();
269			nomem();
270		}
271		w->ypos = ip;
272		for (y = length; --y >= 0;)
273			*ip++ = -1;
274	}
275
276	(void)signal(SIGHUP, onsig);
277	(void)signal(SIGINT, onsig);
278	(void)signal(SIGQUIT, onsig);
279	(void)signal(SIGSTOP, onsig);
280	(void)signal(SIGTSTP, onsig);
281	(void)signal(SIGTERM, onsig);
282
283	if (field) {
284		const char *p = field;
285
286		for (y = LI; --y >= 0;) {
287			for (x = CO; --x >= 0;) {
288				addch(*p++);
289				if (!*p)
290					p = field;
291			}
292			refresh();
293		}
294	}
295	for (;;) {
296		refresh();
297		if (sig_caught) {
298			endwin();
299			exit(0);
300		}
301		if (delay) usleep(delay);
302		for (n = 0, w = &worm[0]; n < number; n++, w++) {
303			if ((x = w->xpos[h = w->head]) < 0) {
304				mvaddch(y = w->ypos[h] = bottom,
305				    x = w->xpos[h] = 0,
306				    flavor[n % sizeof(flavor)]);
307				ref[y][x]++;
308			}
309			else
310				y = w->ypos[h];
311			if (++h == length)
312				h = 0;
313			if (w->xpos[w->head = h] >= 0) {
314				int x1, y1;
315
316				x1 = w->xpos[h];
317				y1 = w->ypos[h];
318				if (--ref[y1][x1] == 0)
319					mvaddch(y1, x1, trail);
320			}
321			op = &(!x ? (!y ? upleft : (y == bottom ? lowleft : left)) : (x == last ? (!y ? upright : (y == bottom ? lowright : right)) : (!y ? upper : (y == bottom ? lower : normal))))[w->orientation];
322			switch (op->nopts) {
323			case 0:
324				endwin();
325				return(1);
326			case 1:
327				w->orientation = op->opts[0];
328				break;
329			default:
330				w->orientation =
331				    op->opts[(int)random() % op->nopts];
332			}
333			mvaddch(y += yinc[w->orientation],
334			    x += xinc[w->orientation],
335			    flavor[n % sizeof(flavor)]);
336			ref[w->ypos[h] = y][w->xpos[h] = x]++;
337		}
338	}
339}
340
341void
342onsig(int signo)
343{
344	sig_caught = 1;
345}
346
347void
348nomem(void)
349{
350	errx(1, "not enough memory.");
351}
352