worms.c revision 1.28
1/*	$OpenBSD: worms.c,v 1.28 2016/03/05 07:47:15 tb 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/*
33 *
34 *	 @@@	    @@@	   @@@@@@@@@@	  @@@@@@@@@@@	 @@@@@@@@@@@@
35 *	 @@@	    @@@	  @@@@@@@@@@@@	  @@@@@@@@@@@@	 @@@@@@@@@@@@@
36 *	 @@@	    @@@	 @@@@	   @@@@	  @@@@		 @@@@ @@@  @@@@
37 *	 @@@   @@   @@@	 @@@	    @@@	  @@@		 @@@  @@@   @@@
38 *	 @@@  @@@@  @@@	 @@@	    @@@	  @@@		 @@@  @@@   @@@
39 *	 @@@@ @@@@ @@@@	 @@@	    @@@	  @@@		 @@@  @@@   @@@
40 *	  @@@@@@@@@@@@	 @@@@	   @@@@	  @@@		 @@@  @@@   @@@
41 *	   @@@@	 @@@@	  @@@@@@@@@@@@	  @@@		 @@@  @@@   @@@
42 *	    @@	  @@	   @@@@@@@@@@	  @@@		 @@@  @@@   @@@
43 *
44 *				 Eric P. Scott
45 *			  Caltech High Energy Physics
46 *				 October, 1980
47 *
48 */
49#include <curses.h>
50#include <err.h>
51#include <signal.h>
52#include <stdlib.h>
53#include <termios.h>
54#include <unistd.h>
55
56static const struct options {
57	int nopts;
58	int opts[3];
59}
60	normal[8] = {
61	{ 3, { 7, 0, 1 } },
62	{ 3, { 0, 1, 2 } },
63	{ 3, { 1, 2, 3 } },
64	{ 3, { 2, 3, 4 } },
65	{ 3, { 3, 4, 5 } },
66	{ 3, { 4, 5, 6 } },
67	{ 3, { 5, 6, 7 } },
68	{ 3, { 6, 7, 0 } }
69},	upper[8] = {
70	{ 1, { 1, 0, 0 } },
71	{ 2, { 1, 2, 0 } },
72	{ 0, { 0, 0, 0 } },
73	{ 0, { 0, 0, 0 } },
74	{ 0, { 0, 0, 0 } },
75	{ 2, { 4, 5, 0 } },
76	{ 1, { 5, 0, 0 } },
77	{ 2, { 1, 5, 0 } }
78},
79	left[8] = {
80	{ 0, { 0, 0, 0 } },
81	{ 0, { 0, 0, 0 } },
82	{ 0, { 0, 0, 0 } },
83	{ 2, { 2, 3, 0 } },
84	{ 1, { 3, 0, 0 } },
85	{ 2, { 3, 7, 0 } },
86	{ 1, { 7, 0, 0 } },
87	{ 2, { 7, 0, 0 } }
88},
89	right[8] = {
90	{ 1, { 7, 0, 0 } },
91	{ 2, { 3, 7, 0 } },
92	{ 1, { 3, 0, 0 } },
93	{ 2, { 3, 4, 0 } },
94	{ 0, { 0, 0, 0 } },
95	{ 0, { 0, 0, 0 } },
96	{ 0, { 0, 0, 0 } },
97	{ 2, { 6, 7, 0 } }
98},
99	lower[8] = {
100	{ 0, { 0, 0, 0 } },
101	{ 2, { 0, 1, 0 } },
102	{ 1, { 1, 0, 0 } },
103	{ 2, { 1, 5, 0 } },
104	{ 1, { 5, 0, 0 } },
105	{ 2, { 5, 6, 0 } },
106	{ 0, { 0, 0, 0 } },
107	{ 0, { 0, 0, 0 } }
108},
109	upleft[8] = {
110	{ 0, { 0, 0, 0 } },
111	{ 0, { 0, 0, 0 } },
112	{ 0, { 0, 0, 0 } },
113	{ 0, { 0, 0, 0 } },
114	{ 0, { 0, 0, 0 } },
115	{ 1, { 3, 0, 0 } },
116	{ 2, { 1, 3, 0 } },
117	{ 1, { 1, 0, 0 } }
118},
119	upright[8] = {
120	{ 2, { 3, 5, 0 } },
121	{ 1, { 3, 0, 0 } },
122	{ 0, { 0, 0, 0 } },
123	{ 0, { 0, 0, 0 } },
124	{ 0, { 0, 0, 0 } },
125	{ 0, { 0, 0, 0 } },
126	{ 0, { 0, 0, 0 } },
127	{ 1, { 5, 0, 0 } }
128},
129	lowleft[8] = {
130	{ 3, { 7, 0, 1 } },
131	{ 0, { 0, 0, 0 } },
132	{ 0, { 0, 0, 0 } },
133	{ 1, { 1, 0, 0 } },
134	{ 2, { 1, 7, 0 } },
135	{ 1, { 7, 0, 0 } },
136	{ 0, { 0, 0, 0 } },
137	{ 0, { 0, 0, 0 } }
138},
139	lowright[8] = {
140	{ 0, { 0, 0, 0 } },
141	{ 1, { 7, 0, 0 } },
142	{ 2, { 5, 7, 0 } },
143	{ 1, { 5, 0, 0 } },
144	{ 0, { 0, 0, 0 } },
145	{ 0, { 0, 0, 0 } },
146	{ 0, { 0, 0, 0 } },
147	{ 0, { 0, 0, 0 } }
148};
149
150static const char	flavor[] = {
151	'O', '*', '#', '$', '%', '0', '@', '~'
152};
153static const short	xinc[] = {
154	1,  1,  1,  0, -1, -1, -1,  0
155}, yinc[] = {
156	-1,  0,  1,  1,  1,  0, -1, -1
157};
158static struct	worm {
159	int orientation, head;
160	short *xpos, *ypos;
161} *worm;
162
163volatile sig_atomic_t sig_caught = 0;
164
165void	 nomem(void);
166void	 onsig(int);
167
168int
169main(int argc, char *argv[])
170{
171	int x, y, h, n;
172	struct worm *w;
173	const struct options *op;
174	short *ip;
175	int CO, LI, last, bottom, ch, length, number, trail;
176	short **ref;
177	const char *field, *errstr;
178	struct timespec sleeptime;
179	struct termios term;
180	speed_t speed;
181	time_t delay = 0;
182
183	if (pledge("stdio rpath tty", NULL) == -1)
184		err(1, "pledge");
185
186	/* set default delay based on terminal baud rate */
187	if (tcgetattr(STDOUT_FILENO, &term) == 0 &&
188	    (speed = cfgetospeed(&term)) > B9600)
189		delay = (speed / B9600) - 1;
190
191	length = 16;
192	number = 3;
193	trail = ' ';
194	field = NULL;
195	while ((ch = getopt(argc, argv, "d:fhl:n:t")) != -1)
196		switch(ch) {
197		case 'd':
198			delay = (time_t)strtonum(optarg, 0, 1000, &errstr);
199			if (errstr)
200				errx(1, "delay (0-1000) is %s: %s", errstr,
201				    optarg);
202			break;
203		case 'f':
204			field = "WORM";
205			break;
206		case 'l':
207			length = strtonum(optarg, 2, 1024, &errstr);
208			if (errstr)
209				errx(1, "length (2-1024) is %s: %s", errstr,
210				    optarg);
211			break;
212		case 'n':
213			number = strtonum(optarg, 1, 100, &errstr);
214			if (errstr)
215				errx(1, "number of worms (1-100) is %s: %s",
216				    errstr, optarg);
217			break;
218		case 't':
219			trail = '.';
220			break;
221		case 'h':
222		default:
223			(void)fprintf(stderr, "usage: %s [-ft] [-d delay] "
224			    "[-l length] [-n number]\n", getprogname());
225			return 1;
226		}
227
228	/* Convert delay from ms -> ns */
229	sleeptime.tv_sec = 0;
230	sleeptime.tv_nsec = delay * 500000;
231	timespecadd(&sleeptime, &sleeptime, &sleeptime);
232
233	if (!(worm = calloc(number, sizeof(struct worm))))
234		nomem();
235	initscr();
236	curs_set(0);
237	CO = COLS;
238	LI = LINES;
239	last = CO - 1;
240	bottom = LI - 1;
241	if (!(ip = reallocarray(NULL, LI, CO * sizeof(short))) ||
242	    !(ref = calloc(LI, sizeof(short *)))) {
243		endwin();
244		nomem();
245	}
246	for (n = 0; n < LI; ++n) {
247		ref[n] = ip;
248		ip += CO;
249	}
250	for (ip = ref[0], n = LI * CO; --n >= 0;)
251		*ip++ = 0;
252	for (n = number, w = &worm[0]; --n >= 0; w++) {
253		w->orientation = w->head = 0;
254		if (!(ip = calloc(length, sizeof(short)))) {
255			endwin();
256			nomem();
257		}
258		w->xpos = ip;
259		for (x = length; --x >= 0;)
260			*ip++ = -1;
261		if (!(ip = calloc(length, sizeof(short)))) {
262			endwin();
263			nomem();
264		}
265		w->ypos = ip;
266		for (y = length; --y >= 0;)
267			*ip++ = -1;
268	}
269
270	(void)signal(SIGHUP, onsig);
271	(void)signal(SIGINT, onsig);
272	(void)signal(SIGQUIT, onsig);
273	(void)signal(SIGSTOP, onsig);
274	(void)signal(SIGTSTP, onsig);
275	(void)signal(SIGTERM, onsig);
276
277	if (field) {
278		const char *p = field;
279
280		for (y = LI; --y >= 0;) {
281			for (x = CO; --x >= 0;) {
282				addch(*p++);
283				if (!*p)
284					p = field;
285			}
286			refresh();
287		}
288	}
289	for (;;) {
290		refresh();
291		if (sig_caught) {
292			endwin();
293			return 0;
294		}
295		nanosleep(&sleeptime, NULL);
296		for (n = 0, w = &worm[0]; n < number; n++, w++) {
297			if ((x = w->xpos[h = w->head]) < 0) {
298				mvaddch(y = w->ypos[h] = bottom,
299				    x = w->xpos[h] = 0,
300				    flavor[n % sizeof(flavor)]);
301				ref[y][x]++;
302			}
303			else
304				y = w->ypos[h];
305			if (++h == length)
306				h = 0;
307			if (w->xpos[w->head = h] >= 0) {
308				int x1, y1;
309
310				x1 = w->xpos[h];
311				y1 = w->ypos[h];
312				if (--ref[y1][x1] == 0)
313					mvaddch(y1, x1, trail);
314			}
315
316			if (x == 0) {
317				if (y == 0)
318					op = &upleft[w->orientation];
319				else if (y == bottom)
320					op = &lowleft[w->orientation];
321				else
322					op = &left[w->orientation];
323			} else if (x == last) {
324				if (y == 0)
325					op = &upright[w->orientation];
326				else if (y == bottom)
327					op = &lowright[w->orientation];
328				else
329					op = &right[w->orientation];
330			} else {
331				if (y == 0)
332					op = &upper[w->orientation];
333				else if (y == bottom)
334					op = &lower[w->orientation];
335				else
336					op = &normal[w->orientation];
337			}
338
339			switch (op->nopts) {
340			case 0:
341				endwin();
342				return(1);
343			case 1:
344				w->orientation = op->opts[0];
345				break;
346			default:
347				w->orientation =
348				    op->opts[arc4random_uniform(op->nopts)];
349			}
350			mvaddch(y += yinc[w->orientation],
351			    x += xinc[w->orientation],
352			    flavor[n % sizeof(flavor)]);
353			ref[w->ypos[h] = y][w->xpos[h] = x]++;
354		}
355	}
356}
357
358void
359onsig(int signo)
360{
361	sig_caught = 1;
362}
363
364void
365nomem(void)
366{
367	errx(1, "not enough memory.");
368}
369