1/*	$OpenBSD: worms.c,v 1.30 2021/10/23 11:22:49 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	/* set default delay based on terminal baud rate */
184	if (tcgetattr(STDOUT_FILENO, &term) == 0 &&
185	    (speed = cfgetospeed(&term)) > B9600)
186		delay = (speed / B9600) - 1;
187
188	length = 16;
189	number = 3;
190	trail = ' ';
191	field = NULL;
192	while ((ch = getopt(argc, argv, "d:fhl:n:t")) != -1)
193		switch(ch) {
194		case 'd':
195			delay = (time_t)strtonum(optarg, 0, 1000, &errstr);
196			if (errstr)
197				errx(1, "delay (0-1000) is %s: %s", errstr,
198				    optarg);
199			break;
200		case 'f':
201			field = "WORM";
202			break;
203		case 'l':
204			length = strtonum(optarg, 2, 1024, &errstr);
205			if (errstr)
206				errx(1, "length (2-1024) is %s: %s", errstr,
207				    optarg);
208			break;
209		case 'n':
210			number = strtonum(optarg, 1, 100, &errstr);
211			if (errstr)
212				errx(1, "number of worms (1-100) is %s: %s",
213				    errstr, optarg);
214			break;
215		case 't':
216			trail = '.';
217			break;
218		case 'h':
219		default:
220			(void)fprintf(stderr, "usage: %s [-ft] [-d delay] "
221			    "[-l length] [-n number]\n", getprogname());
222			return 1;
223		}
224
225	/* Convert delay from ms -> ns */
226	sleeptime.tv_sec = 0;
227	sleeptime.tv_nsec = delay * 500000;
228	timespecadd(&sleeptime, &sleeptime, &sleeptime);
229
230	if (!(worm = calloc(number, sizeof(struct worm))))
231		nomem();
232	initscr();
233
234	if (pledge("stdio tty", NULL) == -1)
235		err(1, "pledge");
236
237	curs_set(0);
238	CO = COLS;
239	LI = LINES;
240	last = CO - 1;
241	bottom = LI - 1;
242	if (!(ip = reallocarray(NULL, LI, CO * sizeof(short))) ||
243	    !(ref = calloc(LI, sizeof(short *)))) {
244		endwin();
245		nomem();
246	}
247	for (n = 0; n < LI; ++n) {
248		ref[n] = ip;
249		ip += CO;
250	}
251	for (ip = ref[0], n = LI * CO; --n >= 0;)
252		*ip++ = 0;
253	for (n = number, w = &worm[0]; --n >= 0; w++) {
254		w->orientation = w->head = 0;
255		if (!(ip = calloc(length, sizeof(short)))) {
256			endwin();
257			nomem();
258		}
259		w->xpos = ip;
260		for (x = length; --x >= 0;)
261			*ip++ = -1;
262		if (!(ip = calloc(length, sizeof(short)))) {
263			endwin();
264			nomem();
265		}
266		w->ypos = ip;
267		for (y = length; --y >= 0;)
268			*ip++ = -1;
269	}
270
271	(void)signal(SIGHUP, onsig);
272	(void)signal(SIGINT, onsig);
273	(void)signal(SIGQUIT, onsig);
274	(void)signal(SIGSTOP, onsig);
275	(void)signal(SIGTSTP, onsig);
276	(void)signal(SIGTERM, onsig);
277
278	if (field) {
279		const char *p = field;
280
281		for (y = LI; --y >= 0;) {
282			for (x = CO; --x >= 0;) {
283				addch(*p++);
284				if (!*p)
285					p = field;
286			}
287			refresh();
288		}
289	}
290	for (;;) {
291		refresh();
292		if (sig_caught) {
293			endwin();
294			return 0;
295		}
296		nanosleep(&sleeptime, NULL);
297		for (n = 0, w = &worm[0]; n < number; n++, w++) {
298			if ((x = w->xpos[h = w->head]) < 0) {
299				mvaddch(y = w->ypos[h] = bottom,
300				    x = w->xpos[h] = 0,
301				    flavor[n % sizeof(flavor)]);
302				ref[y][x]++;
303			}
304			else
305				y = w->ypos[h];
306			if (++h == length)
307				h = 0;
308			if (w->xpos[w->head = h] >= 0) {
309				int x1, y1;
310
311				x1 = w->xpos[h];
312				y1 = w->ypos[h];
313				if (--ref[y1][x1] == 0)
314					mvaddch(y1, x1, trail);
315			}
316
317			if (x == 0) {
318				if (y == 0)
319					op = &upleft[w->orientation];
320				else if (y == bottom)
321					op = &lowleft[w->orientation];
322				else
323					op = &left[w->orientation];
324			} else if (x == last) {
325				if (y == 0)
326					op = &upright[w->orientation];
327				else if (y == bottom)
328					op = &lowright[w->orientation];
329				else
330					op = &right[w->orientation];
331			} else {
332				if (y == 0)
333					op = &upper[w->orientation];
334				else if (y == bottom)
335					op = &lower[w->orientation];
336				else
337					op = &normal[w->orientation];
338			}
339
340			switch (op->nopts) {
341			case 0:
342				endwin();
343				return(1);
344			case 1:
345				w->orientation = op->opts[0];
346				break;
347			default:
348				w->orientation =
349				    op->opts[arc4random_uniform(op->nopts)];
350			}
351			mvaddch(y += yinc[w->orientation],
352			    x += xinc[w->orientation],
353			    flavor[n % sizeof(flavor)]);
354			ref[w->ypos[h] = y][w->xpos[h] = x]++;
355		}
356	}
357}
358
359void
360onsig(int signo)
361{
362	sig_caught = 1;
363}
364
365void
366nomem(void)
367{
368	errx(1, "not enough memory.");
369}
370