Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/netbsd-10]: src/games/worms Pull up following revision(s) (requested by ...



details:   https://anonhg.NetBSD.org/src/rev/821e1f93e0ae
branches:  netbsd-10
changeset: 374474:821e1f93e0ae
user:      martin <martin%NetBSD.org@localhost>
date:      Tue Apr 25 16:12:05 2023 +0000

description:
Pull up following revision(s) (requested by kre in ticket #148):

        games/worms/worms.c: revision 1.24
        games/worms/worms.c: revision 1.25
        games/worms/worms.c: revision 1.26
        games/worms/worms.c: revision 1.27
        games/worms/worms.c: revision 1.28
        games/worms/worms.c: revision 1.29
        games/worms/worms.6: revision 1.17

Garbage collect "char *mp" - it used to point at memory malloc()'d
to hold the termcap entry, back when worms used termcap rather than
curses, and termcap was termcap, rather that a terminfo wannabe.

This should have been removed when worms was converted to curses in
1999, but wasn't, so worms has been doing a meaningless malloc(1024)
and never using the result, ever since.

While here, since the line needed changing anyway to remove mp,
change a malloc() of a product into calloc() so it can deal with
any possible (admittedly extremely unlikely here) integer overflows.

NFCI

80 column police.

If this code were just a few chars over the limit, it might get
let off with a warning, but 214 in an 80 zone is way beyond that.
Six months loss of coding licence, and a 214000 character fine.

NFCI.

(I verified the the code was unchanged by joining the resulting
lines back into one again, and then diff'ing that line against
the original - it is identical).

When worms default delay was changed from 0, to 20ms (Oct 2020)
it lost the ability to run flat out (no sleeps) - which while not
very useful for actually observing the behaviour of various Nematoda,
can be useful when 'worms' is being used to generate cpu heat - it
turns out to be a simple tool to make all of the app itself, the
xterm it runs in (when running under X) and the X server, all more
or less continuously busy - a trivial perpetual CPU load generator.

Changing that was not a simple matter of just allowing -d 0..1000
rather than -d 1..1000 which had always been the limits on -d, as
previously, simply by excluding 0, common error checking wasn't
essential.  -djunk would return 0 which was invalid, and so an error
- - that it was invalid because 0 < 1, rather than because "junk" is not
a number wasn't material.

Now we need some slightly more elaborate error checking code for the
- -d value, and if we're going to do that, may as well do the same for
the -l and -n options, which also take numeric values.  That is, it
will no longer be possible to say:

        worms -n "5 worms"

Just the number is allowed (but -d now allows a "ms" or "us" suffix).

While here, place a reasonable upper limit (depending upon the
screen size, and worm length) upon the number of worms, so they
have somewhere to go, rather than just on top of each other.

exit(1) from the errx() rather than 0, in the case that curses
initscr() fails (doesn't seem right that it should appear as a
successful run of worms, if it never managed to start).

A couple more minor 80 column violations are handled, without
further penalty, in this update (one simply vanishes, the other
is wrapped).

Note that this sounds like a lot, but it is really all just minor
internal bookkeeping updates - the only real advertised user visible
change is the ability to use -d0 which just restores that which was
the default (though it could never be set via the option) until Oct 2020
- - so really all that's happening is replacing minor functionality lost
then, while leaving the change of the default delay that that change
made, untouched.

No man page update required (that never excluded -d0).

Correct some unfortunate behaviour in extreme cases pointed out by RVP.
(Very long worms in a smallish window could result in the max number of
worms being 0...)

While here (also suggested by RVP) seed the random number generator,
also add a -S option to set the seed (note: while this is documented
in the updated man page, it does not appear in the usage message in
case of an error ... not likely to be used often enough to include there).

Also some minor improvements suggested by RVP:
        delete the prototype for main()
        exit curses mode before abort() (which should not happen, but...)
        no need to return (->exit) after abort() as modern abort() can
                never return.

In addition, check for extraneous (ignored) (non-option) args.

Check for absurdly small or big screens (the worm placement algorithm
doesn't work well for lines of columns < 3, and the abort() mentioned
above actually happens if one of those is == 1).

More flavours of worms added.

Some minor man page wording improvements.

Typo in an error message (pointed out by RVP; Thanks)

Another err message typo!   (found this one myself!!)

diffstat:

 games/worms/worms.6 |   39 +++++++++---
 games/worms/worms.c |  159 ++++++++++++++++++++++++++++++++++++++++++---------
 2 files changed, 159 insertions(+), 39 deletions(-)

diffs (truncated from 337 to 300 lines):

diff -r a4d907f561d6 -r 821e1f93e0ae games/worms/worms.6
--- a/games/worms/worms.6       Tue Apr 25 15:58:37 2023 +0000
+++ b/games/worms/worms.6       Tue Apr 25 16:12:05 2023 +0000
@@ -1,4 +1,4 @@
-.\"    $NetBSD: worms.6,v 1.16 2020/10/14 07:32:53 nia Exp $
+.\"    $NetBSD: worms.6,v 1.16.6.1 2023/04/25 16:12:05 martin Exp $
 .\"
 .\" Copyright (c) 1989, 1993
 .\"    The Regents of the University of California.  All rights reserved.
@@ -29,7 +29,7 @@
 .\"
 .\"    @(#)worms.6     8.1 (Berkeley) 5/31/93
 .\"
-.Dd October 14, 2020
+.Dd April 17, 2023
 .Dt WORMS 6
 .Os
 .Sh NAME
@@ -41,6 +41,7 @@
 .Op Fl d Ar delay
 .Op Fl l Ar length
 .Op Fl n Ar number
+.Op Fl S Ar seed
 .Sh DESCRIPTION
 .Nm
 is a
@@ -49,11 +50,11 @@ version of the DEC-2136 program
 .Dq worms .
 .Pp
 The options are as follows:
-.Bl -tag -width XlXlengthXX
+.Bl -tag -width Fl
 .It Fl d Ar delay
-Specifies
-.Ar delay
-as a delay, in milliseconds, between each update.
+Specifies a
+.Ar delay ,
+in milliseconds, between each update.
 This is useful for fast terminals.
 Reasonable values are around 20-200;
 the default is 20.
@@ -62,13 +63,31 @@ Makes a
 .Dq field
 for the worm(s) to eat.
 .It Fl l Ar length
-Specifies
+Specifies the
 .Ar length
-as a length for each worm; the default is 16.
+of each worm; the default is 16, the minimum is 2.
 .It Fl n Ar number
-Specifies
+Specifies the
 .Ar number
-as the number of worms; the default is 3.
+of worms; the default is 3.
+There must be at least one.
+.It Fl S Ar seed
+Provide an integer
+.Ar seed
+for the random number generator.
+Specifying zero (0, the default) causes a random seed to be used.
 .It Fl t
 Makes each worm leave a trail behind it.
 .El
+.Pp
+The maximum
+.Ar length ,
+and
+.Ar number ,
+of worms depends upon the screen size, though the
+.Ar length
+can never exceed 1024.
+If the screen is particularly small, even the defaults
+for those may be too large.
+Screens with less than 3 rows or columns cannot be handled,
+nor can ones with insufficient total space.
diff -r a4d907f561d6 -r 821e1f93e0ae games/worms/worms.c
--- a/games/worms/worms.c       Tue Apr 25 15:58:37 2023 +0000
+++ b/games/worms/worms.c       Tue Apr 25 16:12:05 2023 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: worms.c,v 1.23 2020/10/14 07:32:53 nia Exp $   */
+/*     $NetBSD: worms.c,v 1.23.6.1 2023/04/25 16:12:05 martin Exp $    */
 
 /*
  * Copyright (c) 1980, 1993
@@ -39,7 +39,7 @@
 #if 0
 static char sccsid[] = "@(#)worms.c    8.1 (Berkeley) 5/31/93";
 #else
-__RCSID("$NetBSD: worms.c,v 1.23 2020/10/14 07:32:53 nia Exp $");
+__RCSID("$NetBSD: worms.c,v 1.23.6.1 2023/04/25 16:12:05 martin Exp $");
 #endif
 #endif /* not lint */
 
@@ -62,11 +62,15 @@ static char sccsid[] = "@(#)worms.c 8.1 
  */
 #include <sys/types.h>
 
+#include <ctype.h>
 #include <curses.h>
 #include <err.h>
+#include <limits.h>
 #include <signal.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <strings.h>
 #include <unistd.h>
 
 static const struct options {
@@ -165,7 +169,8 @@ static const struct options {
 
 
 static const char      flavor[] = {
-       'O', '*', '#', '$', '%', '0', '@', '~'
+       'O', '*', '#', '$', '%', '0', '@', '~',
+       '+', 'w', ':', '^', '_', '&', 'x', 'o'
 };
 static const short     xinc[] = {
        1,  1,  1,  0, -1, -1, -1,  0
@@ -179,7 +184,6 @@ static struct       worm {
 
 static volatile sig_atomic_t sig_caught = 0;
 
-int     main(int, char **);
 static void nomem(void) __dead;
 static void onsig(int);
 
@@ -191,55 +195,137 @@ main(int argc, char *argv[])
        const struct options *op;
        short *ip;
        int CO, LI, last, bottom, ch, length, number, trail;
+       unsigned int seed;
        short **ref;
        const char *field;
-       char *mp;
+       char *ep;
        unsigned int delay = 20000;
+       unsigned long ul;
+       bool argerror = false;
 
-       mp = NULL;
        length = 16;
        number = 3;
        trail = ' ';
        field = NULL;
-       while ((ch = getopt(argc, argv, "d:fl:n:t")) != -1)
+       seed = 0;
+       while ((ch = getopt(argc, argv, "d:fl:n:S:t")) != -1) {
                switch(ch) {
                case 'd':
-                       if ((delay = (unsigned int)strtoul(optarg, NULL, 10)) < 1 || delay > 1000)
-                               errx(1, "invalid delay (1-1000)");
-                       delay *= 1000;  /* ms -> us */
-                       break;
+                       ul = strtoul(optarg, &ep, 10);
+                       if (ep != optarg) {
+                               while (isspace(*(unsigned char *)ep))
+                                       ep++;
+                       }
+                       if (ep == optarg ||
+                           (*ep != '\0' &&
+                               ( ep[1] == '\0' ? (*ep != 'm' && *ep != 'u') :
+                               ( strcasecmp(ep, "ms") != 0 &&
+                                 strcasecmp(ep, "us") != 0 )) )) {
+                                   errx(1, "-d: invalid delay (%s)", optarg);
+                       }
+                       /*
+                        * if ul >= INT_MAX/1000 we don't need the *1000,
+                        * as even without that it will exceed the limit
+                        * just below and be treated as an error.
+                        * (This does assume >=32 bit int, but so does POSIX)
+                        */
+                       if (*ep != 'u' && ul < INT_MAX / 1000)
+                               ul *= 1000;  /* ms -> us */
+                       if (ul > 1000*1000) {
+                               errx(1,
+                                  "-d: delay (%s) out of range [0 - 1000]",
+                                  optarg);
+                       }
+                       delay = (unsigned int)ul;
+                       continue;
                case 'f':
                        field = "WORM";
-                       break;
+                       continue;
                case 'l':
-                       if ((length = atoi(optarg)) < 2 || length > 1024) {
-                               errx(1, "invalid length (%d - %d).",
-                                    2, 1024);
+                       ul = strtoul(optarg, &ep, 10);
+                       if (ep == optarg || *ep != '\0' ||
+                           ul < 2 || ul > 1024) {
+                               errx(1, "-l: invalid length (%s) [%d - %d].",
+                                    optarg, 2, 1024);
                        }
-                       break;
+                       length = (int)ul;
+                       continue;
                case 'n':
-                       if ((number = atoi(optarg)) < 1) {
-                               errx(1, "invalid number of worms.");
+                       ul = strtoul(optarg, &ep, 10);
+                       if (ep == optarg || *ep != '\0' ||
+                           ul < 1 || ul > INT_MAX / 10 ) {
+                               errx(1, "-n: invalid number of worms (%s).",
+                                   optarg);
                        }
-                       break;
+                       /* upper bound is further limited later */
+                       number = (int)ul;
+                       continue;
+               case 'S':
+                       ul = strtoul(optarg, &ep, 0);
+                       if (ep == optarg || *ep != '\0' ||
+                           ul > UINT_MAX ) {
+                               errx(1, "-S: invalid seed (%s).", optarg);
+                       }
+                       seed = (unsigned int)ul;
+                       continue;
                case 't':
                        trail = '.';
-                       break;
+                       continue;
                case '?':
                default:
-                       (void)fprintf(stderr,
-                           "usage: worms [-ft] [-d delay] [-l length] [-n number]\n");
-                       exit(1);
+                       argerror = true;
+                       break;
                }
+               break;
+       }
 
-       if (!(worm = malloc((size_t)number *
-           sizeof(struct worm))) || !(mp = malloc((size_t)1024)))
-               nomem();
+       if (argerror || argc > optind)
+               errx(1,
+                   "Usage: worms [-ft] [-d delay] [-l length] [-n number]");
+               /* -S omitted deliberately, not useful often enough */
+
        if (!initscr())
-               errx(0, "couldn't initialize screen");
+               errx(1, "couldn't initialize screen");
        curs_set(0);
        CO = COLS;
        LI = LINES;
+
+       if (CO == 0 || LI == 0) {
+               endwin();
+               errx(1, "screen must be a rectangle, not (%dx%d)", CO, LI);
+       }
+       if (CO >= INT_MAX / LI) {
+               endwin();
+               errx(1, "screen (%dx%d) too large for worms", CO, LI);
+       }
+
+       /* now known that LI*CO cannot overflow an int => also not a long */
+
+       if (LI < 3 || CO < 3 || LI * CO < 40) {
+               /*
+                * The placement algorithm is too weak for dimensions < 3.
+                * Need at least 40 spaces so we can have (n > 1) worms
+                * of a reasonable length, and still leave empty space.
+                */
+               endwin();
+               errx(1, "screen (%dx%d) too small for worms", CO, LI);
+       }
+
+       ul = (unsigned long)CO * LI;
+       if ((unsigned long)length > ul / 20) {
+               endwin();
+               errx(1, "-l: worms too long (%d) for screen; max: %lu",
+                   length, ul / 20);
+       }
+
+       ul /= (length * 3);     /* no more than 33% arena occupancy */
+       if ((unsigned long)(unsigned)number > ul) {
+               endwin();
+               errx(1, "-n: too many worms (%d) max: %lu", number, ul);
+       }
+       if (!(worm = calloc((size_t)number, sizeof(struct worm))))
+               nomem();
+
        last = CO - 1;
        bottom = LI - 1;
        if (!(ip = malloc((size_t)(LI * CO * sizeof(short)))))
@@ -284,6 +370,7 @@ main(int argc, char *argv[])
                        refresh();
                }
        }
+       srandom(seed ? seed : arc4random());
        for (;;) {
                refresh();
                if (sig_caught) {
@@ -316,12 +403,24 @@ main(int argc, char *argv[])



Home | Main Index | Thread Index | Old Index