Home | History | Annotate | Line # | Download | only in worms
worms.c revision 1.26
      1 /*	$NetBSD: worms.c,v 1.26 2023/04/15 15:21:56 kre 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 #include <sys/cdefs.h>
     33 #ifndef lint
     34 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\
     35  The Regents of the University of California.  All rights reserved.");
     36 #endif /* not lint */
     37 
     38 #ifndef lint
     39 #if 0
     40 static char sccsid[] = "@(#)worms.c	8.1 (Berkeley) 5/31/93";
     41 #else
     42 __RCSID("$NetBSD: worms.c,v 1.26 2023/04/15 15:21:56 kre 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 <ctype.h>
     66 #include <curses.h>
     67 #include <err.h>
     68 #include <limits.h>
     69 #include <signal.h>
     70 #include <stdio.h>
     71 #include <stdlib.h>
     72 #include <strings.h>
     73 #include <unistd.h>
     74 
     75 static const struct options {
     76 	int nopts;
     77 	int opts[3];
     78 }
     79 	normal[8] = {
     80 	{ 3, { 7, 0, 1 } },
     81 	{ 3, { 0, 1, 2 } },
     82 	{ 3, { 1, 2, 3 } },
     83 	{ 3, { 2, 3, 4 } },
     84 	{ 3, { 3, 4, 5 } },
     85 	{ 3, { 4, 5, 6 } },
     86 	{ 3, { 5, 6, 7 } },
     87 	{ 3, { 6, 7, 0 } }
     88 },	upper[8] = {
     89 	{ 1, { 1, 0, 0 } },
     90 	{ 2, { 1, 2, 0 } },
     91 	{ 0, { 0, 0, 0 } },
     92 	{ 0, { 0, 0, 0 } },
     93 	{ 0, { 0, 0, 0 } },
     94 	{ 2, { 4, 5, 0 } },
     95 	{ 1, { 5, 0, 0 } },
     96 	{ 2, { 1, 5, 0 } }
     97 },
     98 	left[8] = {
     99 	{ 0, { 0, 0, 0 } },
    100 	{ 0, { 0, 0, 0 } },
    101 	{ 0, { 0, 0, 0 } },
    102 	{ 2, { 2, 3, 0 } },
    103 	{ 1, { 3, 0, 0 } },
    104 	{ 2, { 3, 7, 0 } },
    105 	{ 1, { 7, 0, 0 } },
    106 	{ 2, { 7, 0, 0 } }
    107 },
    108 	right[8] = {
    109 	{ 1, { 7, 0, 0 } },
    110 	{ 2, { 3, 7, 0 } },
    111 	{ 1, { 3, 0, 0 } },
    112 	{ 2, { 3, 4, 0 } },
    113 	{ 0, { 0, 0, 0 } },
    114 	{ 0, { 0, 0, 0 } },
    115 	{ 0, { 0, 0, 0 } },
    116 	{ 2, { 6, 7, 0 } }
    117 },
    118 	lower[8] = {
    119 	{ 0, { 0, 0, 0 } },
    120 	{ 2, { 0, 1, 0 } },
    121 	{ 1, { 1, 0, 0 } },
    122 	{ 2, { 1, 5, 0 } },
    123 	{ 1, { 5, 0, 0 } },
    124 	{ 2, { 5, 6, 0 } },
    125 	{ 0, { 0, 0, 0 } },
    126 	{ 0, { 0, 0, 0 } }
    127 },
    128 	upleft[8] = {
    129 	{ 0, { 0, 0, 0 } },
    130 	{ 0, { 0, 0, 0 } },
    131 	{ 0, { 0, 0, 0 } },
    132 	{ 0, { 0, 0, 0 } },
    133 	{ 0, { 0, 0, 0 } },
    134 	{ 1, { 3, 0, 0 } },
    135 	{ 2, { 1, 3, 0 } },
    136 	{ 1, { 1, 0, 0 } }
    137 },
    138 	upright[8] = {
    139 	{ 2, { 3, 5, 0 } },
    140 	{ 1, { 3, 0, 0 } },
    141 	{ 0, { 0, 0, 0 } },
    142 	{ 0, { 0, 0, 0 } },
    143 	{ 0, { 0, 0, 0 } },
    144 	{ 0, { 0, 0, 0 } },
    145 	{ 0, { 0, 0, 0 } },
    146 	{ 1, { 5, 0, 0 } }
    147 },
    148 	lowleft[8] = {
    149 	{ 3, { 7, 0, 1 } },
    150 	{ 0, { 0, 0, 0 } },
    151 	{ 0, { 0, 0, 0 } },
    152 	{ 1, { 1, 0, 0 } },
    153 	{ 2, { 1, 7, 0 } },
    154 	{ 1, { 7, 0, 0 } },
    155 	{ 0, { 0, 0, 0 } },
    156 	{ 0, { 0, 0, 0 } }
    157 },
    158 	lowright[8] = {
    159 	{ 0, { 0, 0, 0 } },
    160 	{ 1, { 7, 0, 0 } },
    161 	{ 2, { 5, 7, 0 } },
    162 	{ 1, { 5, 0, 0 } },
    163 	{ 0, { 0, 0, 0 } },
    164 	{ 0, { 0, 0, 0 } },
    165 	{ 0, { 0, 0, 0 } },
    166 	{ 0, { 0, 0, 0 } }
    167 };
    168 
    169 
    170 static const char	flavor[] = {
    171 	'O', '*', '#', '$', '%', '0', '@', '~'
    172 };
    173 static const short	xinc[] = {
    174 	1,  1,  1,  0, -1, -1, -1,  0
    175 }, yinc[] = {
    176 	-1,  0,  1,  1,  1,  0, -1, -1
    177 };
    178 static struct	worm {
    179 	int orientation, head;
    180 	short *xpos, *ypos;
    181 } *worm;
    182 
    183 static volatile sig_atomic_t sig_caught = 0;
    184 
    185 int	 main(int, char **);
    186 static void nomem(void) __dead;
    187 static void onsig(int);
    188 
    189 int
    190 main(int argc, char *argv[])
    191 {
    192 	int x, y, h, n;
    193 	struct worm *w;
    194 	const struct options *op;
    195 	short *ip;
    196 	int CO, LI, last, bottom, ch, length, number, trail;
    197 	short **ref;
    198 	const char *field;
    199 	char *ep;
    200 	unsigned int delay = 20000;
    201 	unsigned long ul;
    202 
    203 	length = 16;
    204 	number = 3;
    205 	trail = ' ';
    206 	field = NULL;
    207 	while ((ch = getopt(argc, argv, "d:fl:n:t")) != -1)
    208 		switch(ch) {
    209 		case 'd':
    210 			ul = strtoul(optarg, &ep, 10);
    211 			if (ep != optarg) {
    212 				while (isspace(*(unsigned char *)ep))
    213 					ep++;
    214 			}
    215 			if (ep == optarg ||
    216 			    (*ep != '\0' &&
    217 				( ep[1] == '\0' ? (*ep != 'm' && *ep != 'u') :
    218 				( strcasecmp(ep, "ms") != 0 &&
    219 				  strcasecmp(ep, "us") != 0 )) )) {
    220 				    errx(1, "-d: invalid delay (%s)", optarg);
    221 			}
    222 			/*
    223 			 * if ul >= INT_MAX/1000 we don't need the *1000,
    224 			 * as even without that it will exceed the limit
    225 			 * just below and be treated as an error.
    226 			 * (This does assume >=32 bit int, but so does POSIX)
    227 			 */
    228 			if (*ep != 'u' && ul < INT_MAX / 1000)
    229 				ul *= 1000;  /* ms -> us */
    230 			if (ul > 1000*1000) {
    231 				errx(1,
    232 				   "-d: delay (%s) out of rannge [0 - 1000]",
    233 				   optarg);
    234 			}
    235 			delay = (unsigned int)ul;
    236 			break;
    237 		case 'f':
    238 			field = "WORM";
    239 			break;
    240 		case 'l':
    241 			ul = strtoul(optarg, &ep, 10);
    242 			if (ep == optarg || *ep != '\0' ||
    243 			    ul < 2 || ul > 1024) {
    244 				errx(1, "-l: invalid length (%s) [%d - %d].",
    245 				     optarg, 2, 1024);
    246 			}
    247 			length = (int)ul;
    248 			break;
    249 		case 'n':
    250 			ul = strtoul(optarg, &ep, 10);
    251 			if (ep == optarg || *ep != '\0' ||
    252 			    ul < 1 || ul > INT_MAX / 10 ) {
    253 				errx(1, "-n: invalid number of worms (%s).",
    254 				    optarg);
    255 			}
    256 			/* upper bound is further limited later */
    257 			number = (int)ul;
    258 			break;
    259 		case 't':
    260 			trail = '.';
    261 			break;
    262 		case '?':
    263 		default:
    264 			(void)fprintf(stderr,
    265 			    "usage: worms [-ft] [-d delay] [-l length]"
    266 			    " [-n number]\n");
    267 			exit(1);
    268 		}
    269 
    270 	if (!initscr())
    271 		errx(1, "couldn't initialize screen");
    272 	curs_set(0);
    273 	CO = COLS;
    274 	LI = LINES;
    275 
    276 	if (CO > 4*length || LI < 4*length) {
    277 		ul  = (unsigned long)LI / 2;
    278 		ul *= (unsigned long)CO / length;
    279 	} else {
    280 		ul  = (unsigned long)CO / 2;
    281 		ul *= (unsigned long)LI / length;
    282 	}
    283 
    284 	if ((unsigned long)(unsigned)number > ul) {
    285 		endwin();
    286 		errx(1, "-n: too many worms (%d) max: %lu", number, ul);
    287 	}
    288 	if (!(worm = calloc((size_t)number, sizeof(struct worm))))
    289 		nomem();
    290 
    291 	last = CO - 1;
    292 	bottom = LI - 1;
    293 	if (!(ip = malloc((size_t)(LI * CO * sizeof(short)))))
    294 		nomem();
    295 	if (!(ref = malloc((size_t)(LI * sizeof(short *)))))
    296 		nomem();
    297 	for (n = 0; n < LI; ++n) {
    298 		ref[n] = ip;
    299 		ip += CO;
    300 	}
    301 	for (ip = ref[0], n = LI * CO; --n >= 0;)
    302 		*ip++ = 0;
    303 	for (n = number, w = &worm[0]; --n >= 0; w++) {
    304 		w->orientation = w->head = 0;
    305 		if (!(ip = malloc((size_t)(length * sizeof(short)))))
    306 			nomem();
    307 		w->xpos = ip;
    308 		for (x = length; --x >= 0;)
    309 			*ip++ = -1;
    310 		if (!(ip = malloc((size_t)(length * sizeof(short)))))
    311 			nomem();
    312 		w->ypos = ip;
    313 		for (y = length; --y >= 0;)
    314 			*ip++ = -1;
    315 	}
    316 
    317 	(void)signal(SIGHUP, onsig);
    318 	(void)signal(SIGINT, onsig);
    319 	(void)signal(SIGQUIT, onsig);
    320 	(void)signal(SIGTSTP, onsig);
    321 	(void)signal(SIGTERM, onsig);
    322 
    323 	if (field) {
    324 		const char *p = field;
    325 
    326 		for (y = LI; --y >= 0;) {
    327 			for (x = CO; --x >= 0;) {
    328 				addch(*p++);
    329 				if (!*p)
    330 					p = field;
    331 			}
    332 			refresh();
    333 		}
    334 	}
    335 	for (;;) {
    336 		refresh();
    337 		if (sig_caught) {
    338 			endwin();
    339 			exit(0);
    340 		}
    341 		if (delay) {
    342 			if (delay % 1000000 != 0)
    343 				usleep(delay % 1000000);
    344 			if (delay >= 1000000)
    345 				sleep(delay / 1000000);
    346 		}
    347 		for (n = 0, w = &worm[0]; n < number; n++, w++) {
    348 			if ((x = w->xpos[h = w->head]) < 0) {
    349 				mvaddch(y = w->ypos[h] = bottom,
    350 					x = w->xpos[h] = 0,
    351 					flavor[n % sizeof(flavor)]);
    352 				ref[y][x]++;
    353 			}
    354 			else
    355 				y = w->ypos[h];
    356 			if (++h == length)
    357 				h = 0;
    358 			if (w->xpos[w->head = h] >= 0) {
    359 				int x1, y1;
    360 
    361 				x1 = w->xpos[h];
    362 				y1 = w->ypos[h];
    363 				if (--ref[y1][x1] == 0) {
    364 					mvaddch(y1, x1, trail);
    365 				}
    366 			}
    367 
    368 			op = &(!x
    369 				? (!y
    370 				    ? upleft
    371 				    : (y == bottom ? lowleft : left))
    372 				: (x == last
    373 				    ? (!y ? upright
    374 					  : (y == bottom ? lowright : right))
    375 				    : (!y ? upper
    376 					  : (y == bottom ? lower : normal)))
    377 			      )[w->orientation];
    378 
    379 			switch (op->nopts) {
    380 			case 0:
    381 				refresh();
    382 				abort();
    383 				return(1);
    384 			case 1:
    385 				w->orientation = op->opts[0];
    386 				break;
    387 			default:
    388 				w->orientation =
    389 				    op->opts[(int)random() % op->nopts];
    390 			}
    391 			mvaddch(y += yinc[w->orientation],
    392 				x += xinc[w->orientation],
    393 				flavor[n % sizeof(flavor)]);
    394 			ref[w->ypos[h] = y][w->xpos[h] = x]++;
    395 		}
    396 	}
    397 }
    398 
    399 static void
    400 onsig(int signo __unused)
    401 {
    402 	sig_caught = 1;
    403 }
    404 
    405 /* This is never called before curses is initialised */
    406 static void
    407 nomem(void)
    408 {
    409 	endwin();
    410 	errx(1, "not enough memory.");
    411 }
    412