worms.c revision 1.29
1/*	$OpenBSD: worms.c,v 1.29 2018/08/06 06:27:32 mestre 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
237	if (pledge("stdio tty", NULL) == -1)
238		err(1, "pledge");
239
240	curs_set(0);
241	CO = COLS;
242	LI = LINES;
243	last = CO - 1;
244	bottom = LI - 1;
245	if (!(ip = reallocarray(NULL, LI, CO * sizeof(short))) ||
246	    !(ref = calloc(LI, sizeof(short *)))) {
247		endwin();
248		nomem();
249	}
250	for (n = 0; n < LI; ++n) {
251		ref[n] = ip;
252		ip += CO;
253	}
254	for (ip = ref[0], n = LI * CO; --n >= 0;)
255		*ip++ = 0;
256	for (n = number, w = &worm[0]; --n >= 0; w++) {
257		w->orientation = w->head = 0;
258		if (!(ip = calloc(length, sizeof(short)))) {
259			endwin();
260			nomem();
261		}
262		w->xpos = ip;
263		for (x = length; --x >= 0;)
264			*ip++ = -1;
265		if (!(ip = calloc(length, sizeof(short)))) {
266			endwin();
267			nomem();
268		}
269		w->ypos = ip;
270		for (y = length; --y >= 0;)
271			*ip++ = -1;
272	}
273
274	(void)signal(SIGHUP, onsig);
275	(void)signal(SIGINT, onsig);
276	(void)signal(SIGQUIT, onsig);
277	(void)signal(SIGSTOP, onsig);
278	(void)signal(SIGTSTP, onsig);
279	(void)signal(SIGTERM, onsig);
280
281	if (field) {
282		const char *p = field;
283
284		for (y = LI; --y >= 0;) {
285			for (x = CO; --x >= 0;) {
286				addch(*p++);
287				if (!*p)
288					p = field;
289			}
290			refresh();
291		}
292	}
293	for (;;) {
294		refresh();
295		if (sig_caught) {
296			endwin();
297			return 0;
298		}
299		nanosleep(&sleeptime, NULL);
300		for (n = 0, w = &worm[0]; n < number; n++, w++) {
301			if ((x = w->xpos[h = w->head]) < 0) {
302				mvaddch(y = w->ypos[h] = bottom,
303				    x = w->xpos[h] = 0,
304				    flavor[n % sizeof(flavor)]);
305				ref[y][x]++;
306			}
307			else
308				y = w->ypos[h];
309			if (++h == length)
310				h = 0;
311			if (w->xpos[w->head = h] >= 0) {
312				int x1, y1;
313
314				x1 = w->xpos[h];
315				y1 = w->ypos[h];
316				if (--ref[y1][x1] == 0)
317					mvaddch(y1, x1, trail);
318			}
319
320			if (x == 0) {
321				if (y == 0)
322					op = &upleft[w->orientation];
323				else if (y == bottom)
324					op = &lowleft[w->orientation];
325				else
326					op = &left[w->orientation];
327			} else if (x == last) {
328				if (y == 0)
329					op = &upright[w->orientation];
330				else if (y == bottom)
331					op = &lowright[w->orientation];
332				else
333					op = &right[w->orientation];
334			} else {
335				if (y == 0)
336					op = &upper[w->orientation];
337				else if (y == bottom)
338					op = &lower[w->orientation];
339				else
340					op = &normal[w->orientation];
341			}
342
343			switch (op->nopts) {
344			case 0:
345				endwin();
346				return(1);
347			case 1:
348				w->orientation = op->opts[0];
349				break;
350			default:
351				w->orientation =
352				    op->opts[arc4random_uniform(op->nopts)];
353			}
354			mvaddch(y += yinc[w->orientation],
355			    x += xinc[w->orientation],
356			    flavor[n % sizeof(flavor)]);
357			ref[w->ypos[h] = y][w->xpos[h] = x]++;
358		}
359	}
360}
361
362void
363onsig(int signo)
364{
365	sig_caught = 1;
366}
367
368void
369nomem(void)
370{
371	errx(1, "not enough memory.");
372}
373