• srand(0)

    From Michael Sanders@3:633/10 to All on Mon Dec 22 08:48:10 2025
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From James Kuyper@3:633/10 to All on Mon Dec 22 06:44:42 2025
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Mon Dec 22 13:18:19 2025
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From James Kuyper@3:633/10 to All on Mon Dec 22 12:13:37 2025
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if seeded
    with 0. ...

    The details of how the seed affects the random number sequence are
    unspecified by the standard. I personally would consider a pseudo-random
    number generator to be quite defective if there were any seed that
    produced a constant output.

    ... And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    The C standard explains that as follows: "If rand is called before any
    calls to srand have been made, the same sequence shall be generated as
    when srand is first called with a seed value of 1." (7.24.2.2p2).


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Mon Dec 22 18:41:10 2025
    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if seeded
    with 0. ...

    The details of how the seed affects the random number sequence are unspecified by the standard. I personally would consider a pseudo-random number generator to be quite defective if there were any seed that
    produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With sophisticated
    generator polynomials they produce also sequences of maximum period;
    say, for N=31 a non-repeating sequence of length 2^N - 1. The one
    element that is missing from the sequence is the 0 (that reproduces
    itself).

    Technically you pick some bit-values from fixed positions (depending
    on the generator polynomial) of the register and xor the bits to shift
    the result into the register. Here's ad hoc an example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >> 30))
    & 0x1;
    reg <<= 1;
    reg |= new_bit;
    reg &= 0x7fffffff;
    count++;
    } while (reg != init);
    printf ("period: %d\n", count);
    }


    Janis

    [...]


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Mon Dec 22 20:45:38 2025
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if
    seeded with 0. ...

    The details of how the seed affects the random number sequence are unspecified by the standard. I personally would consider a
    pseudo-random number generator to be quite defective if there were
    any seed that produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With sophisticated generator polynomials they produce also sequences of maximum period;
    say, for N=31 a non-repeating sequence of length 2^N - 1. The one
    element that is missing from the sequence is the 0 (that reproduces
    itself).

    Technically you pick some bit-values from fixed positions (depending
    on the generator polynomial) of the register and xor the bits to shift
    the result into the register. Here's ad hoc an example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >>
    30)) & 0x1;
    reg <<= 1;
    reg |= new_bit;
    reg &= 0x7fffffff;
    count++;
    } while (reg != init);
    printf ("period: %d\n", count);
    }


    Janis

    [...]


    Pay attention that C Standard only requires for the same seed to always produces the same sequence. There is no requirement that different
    seeds have to produce different sequences.
    So, for generator in your example, implementation like below would be
    fully legal. Personally, I wouldn't even consider it as particularly
    poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    [O.T.]
    In practice, using LFSR for rand() is not particularly bright idea for different reason: LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. In
    order to get 31 new bits, without predictable repetitions from the
    previous value, you would have to do 31 steps. That's slow! The process
    can be accelerate by generation of several bits at time via look up
    tables, but in order to get decent speed the table has to be rater big
    and using big tables in standard library is bad sportsmanship.

    It seems that overwhelming majority C RTLs use Linear Congruential
    Generators, probably because for Stanadard library compactness of both
    code and data is considered more important than very high speed (not
    that on modern HW LCGs are slow) or superior random properties of
    Mersenne Twisters.
    [/O.T]

    IMHO, decent PRNG with decent thread-safe interface (not POSIX
    imbecile rand_r() ) should have been part of the C Standard
    library at least since C11. But somehow it did not happen until now.



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Kaz Kylheku@3:633/10 to All on Mon Dec 22 19:16:06 2025
    On 2025-12-22, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if seeded
    with 0. ...

    The details of how the seed affects the random number sequence are
    unspecified by the standard. I personally would consider a pseudo-random
    number generator to be quite defective if there were any seed that
    produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers).

    The entire class of LFRS's is not homogeneous in this way; it
    is possible for a Linear Boolean function to generate a nonzero
    out of zero inputs.

    The LFSR page in the Wikipedia references this definition:

    https://en.wikipedia.org/wiki/Linearity#Boolean_functions

    A Boolean linear function of the inputs x1, ... xn is
    a kind of polynomial with coefficients C0, C1, ... CN combined
    with the inputs using AND for multiplication, and XOR for addition:

    f(x1, ... xn) = C0 ^ (C1 & x1) ^ (C2 & x2) ^ .. ^ (Cn ^ xn)

    If c0 is chosen as 1, then we get a 1 output when all the x's are
    zero. (The article uses "b" for the parameters and "a" for the
    coefficients, which I'm not crazy about.)

    Also see the two conditions, given in the description, one of which has
    to be true for Boolean linearity. For condition (2) the remark is given
    that under that condition f(F, F, F, ... F) = T.

    So if we have a "condition 2" linear function, our LSFR avoids
    the behavior.

    I don't recall having paid attention to this exact material in the
    past so it is a "TIL" for me.

    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >> 30))
    & 0x1;

    These could of course be XOR, without it making a difference; the least significant bit in a binary addition is the XOR of the LSB's of the
    inputs.

    reg <<= 1;
    reg |= new_bit;

    So to have that C0 = 1 coefficient in the linear function, we just make
    it "reg |= (new_bit ^ 1);".

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Mon Dec 22 21:16:53 2025
    On Mon, 22 Dec 2025 20:45:38 +0200, Michael S wrote:

    ... (not POSIX imbecile rand_r() ) ...

    Obsoleted in newer POSIX, I see.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Mon Dec 22 22:19:56 2025
    On 2025-12-22 19:45, Michael S wrote:
    [...]
    In practice, using LFSR for rand() is not particularly bright idea for different reason: LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. [...]

    Please note that I've not suggested that.

    I had been merely answering your question concerning Michael's doubts...

    Is it incorrect to use 0 (zero) to seed srand()?
    No, why whould you think so?
    There's number sequence generators that produce 0 sequences if seeded
    with 0.

    Janis


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Mon Dec 22 22:35:35 2025
    On 2025-12-22 20:16, Kaz Kylheku wrote:
    On 2025-12-22, Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    [...]

    I don't recall having paid attention to this exact material in the
    past so it is a "TIL" for me.

    What does "TIL" mean?

    I actually had used PN-generators in the past; used it in a Pascal
    program to make statistical tests, and also in assembler for a DSP
    codec to create reproducible random payload data to compare after
    transmission. For that purpose it was the perfect choice.


    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >> 30))
    & 0x1;

    These could of course be XOR, without it making a difference; the least significant bit in a binary addition is the XOR of the LSB's of the
    inputs.

    Actually the pluses are a remnant of my decades old piece of Pascal
    code that I wrote back then that I now just transcribed. The Pascal
    version I used back then didn't support XOR so I used the addition.
    Being in a boolean context I'd nowadays (in C) have rather written
    purely new_bit = ((reg>>2)^(reg>>4)^(reg>>6)^(reg>>30)) & 0x1;
    but since it's unimportant, as you say, I lazily left it as it was.

    Janis

    [...]


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Mon Dec 22 22:57:27 2025
    On 2025-12-22 19:45, Michael S wrote:
    [...] LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. In
    order to get 31 new bits, without predictable repetitions from the
    previous value, you would have to do 31 steps. That's slow! The process
    can be accelerate by generation of several bits at time via look up
    tables, but in order to get decent speed the table has to be rater big
    and using big tables in standard library is bad sportsmanship.

    Yes. But mind that the speed is also depending on what quality you
    need. For example; I used the PN-generator to create bit-sequences
    (as you also suggest). For another application both, PN-LFSR and
    LCG (that you mention below), were inacceptable; we used a cipher
    to create the random data. (If you compare the speed of creating
    the cipher to a bit-shift-register the latter looks really fast.)


    It seems that overwhelming majority C RTLs use Linear Congruential Generators, probably because for Stanadard library compactness of both
    code and data is considered more important than very high speed (not
    that on modern HW LCGs are slow) or superior random properties of
    Mersenne Twisters.

    For "standard applications" I always used the simple LCGs; simple
    and fast. Or whatever the tools or library provided; which were
    mostly anyway LCGs.

    Janis

    [...]


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From John McCue@3:633/10 to All on Tue Dec 23 00:39:49 2025
    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    --
    [t]csh(1) - "An elegant shell, for a more... civilized age."
    - Paraphrasing Star Wars

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Tue Dec 23 02:17:01 2025
    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random number. Seem
    easier and more portable across Linux & the *BSDs.

    Not to mention a lot stronger, cryptographically.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Ike Naar@3:633/10 to All on Tue Dec 23 06:49:22 2025
    On 2025-12-23, John McCue <jmclnx@gmail.com.invalid> wrote:
    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    srand takes an unsigned argument.

    unsigned s;
    read(fd, &s, sizeof s);

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Tue Dec 23 07:17:05 2025
    On Mon, 22 Dec 2025 06:44:42 -0500, James Kuyper wrote:

    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    Excuse my delayed reply James (net provider was down most of today).

    Well, I guess I did not expect such large differences between
    gcc & musl somehow (cant test with clang just yet). I understand the
    sequence is deterministic & likely still some differences with musl,
    yet I wrongly assumed it seems, the sequences would be the same...

    #include <stdio.h>
    #include <stdlib.h>

    int main(void) {
    srand(0);
    printf("%d\n", rand());
    return 0;
    }

    /*

    $ gcc -O2 -o rnd rnd.c && ./rnd
    1804289383

    $ musl-gcc -static -O2 -o rnd rnd.c && ./rnd
    2049033599

    */

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Tue Dec 23 07:24:34 2025
    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    I see no difference when invoking any of:

    man 3 srand
    or: man 3 posix srand
    or: man posix 3 srand

    What I'm doing wrong here?

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Tue Dec 23 07:25:42 2025
    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Tue Dec 23 08:25:59 2025
    On 23/12/2025 08:17, Michael Sanders wrote:
    On Mon, 22 Dec 2025 06:44:42 -0500, James Kuyper wrote:

    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    Excuse my delayed reply James (net provider was down most of today).

    Well, I guess I did not expect such large differences between
    gcc & musl somehow (cant test with clang just yet). I understand the
    sequence is deterministic & likely still some differences with musl,
    yet I wrongly assumed it seems, the sequences would be the same...

    #include <stdio.h>
    #include <stdlib.h>

    int main(void) {
    srand(0);
    printf("%d\n", rand());
    return 0;
    }

    /*

    $ gcc -O2 -o rnd rnd.c && ./rnd
    1804289383

    $ musl-gcc -static -O2 -o rnd rnd.c && ./rnd
    2049033599

    */


    It is not the compilers that are different, it is the C standard
    libraries that are different. gcc is a compiler, not a library, but you
    are probably using glibc with it by default. musl is a library, not a compiler. There is no reason to suppose that different C standard
    libraries use the same implementation of rand()and srand(), so no reason
    to suppose they would give the same sequences - though each on their own
    will give a deterministic pseudo-random sequence based on their seeds.

    If you swap gcc with clang you will get the same results - it will
    depend on whether you are linking with glibc, musl, or another C library.



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Tue Dec 23 09:59:27 2025
    On 2025-12-23 08:24, Michael Sanders wrote:
    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    Sorry, can't help you here. Maybe someone else can.

    Myself I only access the Unix man pages as they come,
    i.e. using either 'man entry' or 'man section entry'.
    The POSIX information is usually textually integrated
    in the man pages.


    I see no difference when invoking any of:

    man 3 srand

    That's what I'm doing, and I see, for example,

    ...
    HISTORY
    rand()
    srand()
    SVr4, 4.3BSD, C89, POSIX.1-2001.

    rand_r()
    POSIX.1-2001. Obsolete in POSIX.1-2008.
    ...

    Janis

    or: man 3 posix srand
    or: man posix 3 srand

    What I'm doing wrong here?



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Tue Dec 23 11:18:55 2025
    On Mon, 22 Dec 2025 22:57:27 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 19:45, Michael S wrote:
    [...] LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. In
    order to get 31 new bits, without predictable repetitions from the
    previous value, you would have to do 31 steps. That's slow! The
    process can be accelerate by generation of several bits at time via
    look up tables, but in order to get decent speed the table has to
    be rater big and using big tables in standard library is bad
    sportsmanship.

    Yes. But mind that the speed is also depending on what quality you
    need. For example; I used the PN-generator to create bit-sequences
    (as you also suggest). For another application both, PN-LFSR and
    LCG (that you mention below), were inacceptable; we used a cipher
    to create the random data. (If you compare the speed of creating
    the cipher to a bit-shift-register the latter looks really fast.)


    It seems that overwhelming majority C RTLs use Linear Congruential Generators, probably because for Stanadard library compactness of
    both code and data is considered more important than very high
    speed (not that on modern HW LCGs are slow) or superior random
    properties of Mersenne Twisters.

    For "standard applications" I always used the simple LCGs; simple
    and fast. Or whatever the tools or library provided; which were
    mostly anyway LCGs.

    Janis

    [...]


    When I need PRNG then I am typically not deeply concerned about size of
    its internal state. On the other hand, I don't want to care about
    potentially insufficient randomness of the output (not in crypto
    sense). On the 3rd hand, vectors that I generate with PRNG tend to be
    big and I don't like to wait, so I do care somewhat about speed.
    Those 3 factors together plus availability long ago made MT19937-64
    into my personal default PRNG of choice.

    MT19937-64 is available out of the box(*) in C++. But not in C,
    unfortunately.

    At higher theoretical level MT is a generalization of LFSR, but it is
    not obvious when one looks at implementation.

    ---------
    * - hidden behind unnecessary levels of abstraction that just make it
    harder to use, but that's another story.


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Tue Dec 23 10:54:23 2025
    On 2025-12-23 10:18, Michael S wrote:

    When I need PRNG then I am typically not deeply concerned about size of
    its internal state. On the other hand, I don't want to care about
    potentially insufficient randomness of the output (not in crypto
    sense). On the 3rd hand, vectors that I generate with PRNG tend to be
    big and I don't like to wait, so I do care somewhat about speed.
    Those 3 factors together plus availability long ago made MT19937-64
    into my personal default PRNG of choice.

    I've never intensified my knowledge in direction of MT algorithms.


    MT19937-64 is available out of the box(*) in C++. But not in C, unfortunately.

    This is really strange given that the name ("Mersenne Twister") is
    that prominent.

    Looking that up I find at least "C" code for MT19937 in Wikipedia https://de.wikipedia.org/wiki/Mersenne-Twister
    It's based on 32 bit logic it seems; interpreting your "MT19937-64"
    I assume you're looking for a 64 bit based version?


    At higher theoretical level MT is a generalization of LFSR, but it is
    not obvious when one looks at implementation.

    Well, at least there's the 'mod' operations all based on powers of 2
    along with all the binary op's which suggests some (non-arithmetic) bit-register type of algorithm, but the multiplication with 0x9908b0df
    (5 * 513496723) - which I'd suppose be hard to realize as/with LFSR -
    may suggest some other generator type.

    Janis


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Bonita Montero@3:633/10 to All on Tue Dec 23 11:04:07 2025
    Am 22.12.2025 um 09:48 schrieb Michael Sanders:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    Use mt19993_64 ! ;-)


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Bäuerle@3:633/10 to All on Tue Dec 23 11:09:02 2025
    Janis Papanagnou wrote:
    On 2025-12-23 08:24, Michael Sanders wrote:

    [...]
    How do I bring up *posix only* man pages using 3?

    Sorry, can't help you here. Maybe someone else can.

    Manual pages are for the implementation of the operating system.

    The current standard version can be viewed here: <https://pubs.opengroup.org/onlinepubs/9799919799/functions/srand.html>
    This is the older standard version, still containing rand_r(): <https://pubs.opengroup.org/onlinepubs/9699919799/functions/srand.html>

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Tue Dec 23 02:16:39 2025
    Michael Sanders <porkchop@invalid.foo> writes:
    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:
    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    I see no difference when invoking any of:

    man 3 srand
    or: man 3 posix srand
    or: man posix 3 srand

    What I'm doing wrong here?

    On Debian, Ubuntu, and similar systems, you can install the
    "manpages-posix" (section 1) and "manpages-posix-dev" (sections 3
    and 7) packages. You can then view the POSIX man page for srand
    by typing any of:

    man 3posix srand
    man -s 3posix srand
    man srand.3posix

    I'd expect similar packages to be available on (some) other systems.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Tue Dec 23 13:50:31 2025
    On Tue, 23 Dec 2025 10:54:23 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-23 10:18, Michael S wrote:

    When I need PRNG then I am typically not deeply concerned about
    size of its internal state. On the other hand, I don't want to care
    about potentially insufficient randomness of the output (not in
    crypto sense). On the 3rd hand, vectors that I generate with PRNG
    tend to be big and I don't like to wait, so I do care somewhat
    about speed. Those 3 factors together plus availability long ago
    made MT19937-64 into my personal default PRNG of choice.

    I've never intensified my knowledge in direction of MT algorithms.


    MT19937-64 is available out of the box(*) in C++. But not in C, unfortunately.

    This is really strange given that the name ("Mersenne Twister") is
    that prominent.

    Looking that up I find at least "C" code for MT19937 in Wikipedia https://de.wikipedia.org/wiki/Mersenne-Twister
    It's based on 32 bit logic it seems; interpreting your "MT19937-64"
    I assume you're looking for a 64 bit based version?


    "Available out of the box" in this sentence means "part of standard
    library".
    Of course, MT19937-64 is available as 'C' source. But that's one more source+header to copy from project to project. Unlike in C++ where it's
    always here.


    At higher theoretical level MT is a generalization of LFSR, but it
    is not obvious when one looks at implementation.

    Well, at least there's the 'mod' operations all based on powers of 2
    along with all the binary op's which suggests some (non-arithmetic) bit-register type of algorithm, but the multiplication with 0x9908b0df
    (5 * 513496723) - which I'd suppose be hard to realize as/with LFSR -
    may suggest some other generator type.

    Janis




    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Tue Dec 23 14:45:26 2025
    On Tue, 23 Dec 2025 08:25:59 +0100, David Brown wrote:

    It is not the compilers that are different, it is the C standard
    libraries that are different. gcc is a compiler, not a library, but you
    are probably using glibc with it by default. musl is a library, not a compiler. There is no reason to suppose that different C standard
    libraries use the same implementation of rand()and srand(), so no reason
    to suppose they would give the same sequences - though each on their own will give a deterministic pseudo-random sequence based on their seeds.

    If you swap gcc with clang you will get the same results - it will
    depend on whether you are linking with glibc, musl, or another C library.

    Sure enough & thank you David - I appreciate your explanation.

    I see where my thinking was off now. You're 100% correct
    (I'm still learning as you noticed).

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Tue Dec 23 14:47:59 2025
    On Tue, 23 Dec 2025 02:16:39 -0800, Keith Thompson wrote:

    On Debian, Ubuntu, and similar systems, you can install the
    "manpages-posix" (section 1) and "manpages-posix-dev" (sections 3
    and 7) packages. You can then view the POSIX man page for srand
    by typing any of:

    man 3posix srand
    man -s 3posix srand
    man srand.3posix

    I'd expect similar packages to be available on (some) other systems.

    Great! Thanks Keith, you sir are a walking encyclopedia.

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Tue Dec 23 14:49:28 2025
    On Tue, 23 Dec 2025 11:09:02 +0100 (CET), Michael B„uerle wrote:

    Manual pages are for the implementation of the operating system.

    The current standard version can be viewed here: <https://pubs.opengroup.org/onlinepubs/9799919799/functions/srand.html>
    This is the older standard version, still containing rand_r(): <https://pubs.opengroup.org/onlinepubs/9699919799/functions/srand.html>

    Thank you for the pointers, I appreciate your insight.

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Tue Dec 23 14:55:59 2025
    On Tue, 23 Dec 2025 02:17:01 -0000 (UTC), Lawrence D?Oliveiro wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random number. Seem
    easier and more portable across Linux & the *BSDs.

    Not to mention a lot stronger, cryptographically.

    No srand() combined with crypto on my end. Sounds like an invitation
    to get hacked from everything I've ever read about mixing the two.

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Tue Dec 23 16:08:49 2025
    Michael Sanders <porkchop@invalid.foo> writes:
    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    I see no difference when invoking any of:

    man 3 srand
    or: man 3 posix srand
    or: man posix 3 srand

    What I'm doing wrong here?

    You're looking in the wrong place.

    https://pubs.opengroup.org/onlinepubs/9799919799/

    Select <System Interface> in the top left frame,
    select (3) in the subsequent bottom left frame and
    select the interface name in the bottom left frame. The
    manual page will be in the right frame.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/rand.html

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Tue Dec 23 16:13:18 2025
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> writes:
    On 2025-12-23 08:24, Michael Sanders wrote:
    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    Sorry, can't help you here. Maybe someone else can.

    Myself I only access the Unix man pages as they come,
    i.e. using either 'man entry' or 'man section entry'.
    The POSIX information is usually textually integrated
    in the man pages.


    I see no difference when invoking any of:

    man 3 srand

    That's what I'm doing, and I see, for example,

    ...
    HISTORY
    rand()
    srand()
    SVr4, 4.3BSD, C89, POSIX.1-2001.

    Those interfaces were originally documented in the SVID
    (System V Interface Definition). The third edition (1989) states:

    "The function rand() uses a multiplicative congruential random-number
    generator with a period 2^32 that returns successive psuedo-random
    numbers in the range 0 to 32767."

    In the FUTURE DIRECTIONS section, it notes:

    "The algorithms used in rand() and srand() are obsolete and will
    be replaced with algorithms providing better pseudo-random characteristics
    in a future issue".

    There was never a fourth edition.


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From David Brown@3:633/10 to All on Tue Dec 23 19:15:24 2025
    On 23/12/2025 15:45, Michael Sanders wrote:
    On Tue, 23 Dec 2025 08:25:59 +0100, David Brown wrote:

    It is not the compilers that are different, it is the C standard
    libraries that are different. gcc is a compiler, not a library, but you
    are probably using glibc with it by default. musl is a library, not a
    compiler. There is no reason to suppose that different C standard
    libraries use the same implementation of rand()and srand(), so no reason
    to suppose they would give the same sequences - though each on their own
    will give a deterministic pseudo-random sequence based on their seeds.

    If you swap gcc with clang you will get the same results - it will
    depend on whether you are linking with glibc, musl, or another C library.

    Sure enough & thank you David - I appreciate your explanation.

    I see where my thinking was off now. You're 100% correct
    (I'm still learning as you noticed).


    There are people who have been in this group for decades that still have trouble understanding the distinction between a C compiler, a C standard library, and a C implementation (which combines both). There are C
    compilers that have a standard library tightly attached or "built in" in
    the same product, and others which can work with a number of different C standard libraries. There are C standard libraries that only work with
    a single compiler, and others that are much more general - though you
    can't write a complete C standard library purely in fully portable C.
    And C compilers can implement standard library functions in the compiler itself (this is typically done for small functions like memcpy, or maths functions - things that can be significantly more efficient when handled inline by the compiler). There are many nuances involved - but you've
    found a clear way to show a difference between two common C libraries,
    so that's a good start.



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Richard Tobin@3:633/10 to All on Tue Dec 23 19:05:28 2025
    In article <ycz2R.198208$79B9.129561@fx14.iad>,
    Scott Lurndal <slp53@pacbell.net> wrote:

    HISTORY
    rand()
    srand()
    SVr4, 4.3BSD, C89, POSIX.1-2001.

    Those interfaces were originally documented in the SVID
    (System V Interface Definition).

    Not "originally".

    The third edition (1989) states:

    "The function rand() uses a multiplicative congruential random-number
    generator with a period 2^32 that returns successive psuedo-random
    numbers in the range 0 to 32767."

    Unix 5th edition (page dated 1/15/73) says:

    Rand uses a multiplicative congruential random number generator to
    return successive pseudo-random numbers (in r0) in the range from 1
    to 2^15-1.

    (In those days they documented the assembler interface as well.)

    The SVID r3 text is almost identical to 7th edition's (1979).

    In the FUTURE DIRECTIONS section, it notes:

    "The algorithms used in rand() and srand() are obsolete and will
    be replaced with algorithms providing better pseudo-random characteristics
    in a future issue".

    There was never a fourth edition.

    There was, but it was post-Posix. Rather than having improved
    algorithms in [s]rand() it has "see also drand48".

    https://www.sco.com/developers/devspecs/vol1a.pdf

    -- Richard

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From John McCue@3:633/10 to All on Tue Dec 23 20:37:42 2025
    Ike Naar <ike@sdf.org> wrote:
    On 2025-12-23, John McCue <jmclnx@gmail.com.invalid> wrote:
    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    srand takes an unsigned argument.

    unsigned s;
    read(fd, &s, sizeof s);

    I am not quite sure what you are saying about srand(3).

    If you decide to read /dev/urandom, there is no need to
    call srand(3), the OS maintains random data itself. So
    read(2) will just return the random number of the type
    you want based upon the call.

    --
    [t]csh(1) - "An elegant shell, for a more... civilized age."
    - Paraphrasing Star Wars

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Wed Dec 24 00:08:24 2025
    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if
    seeded with 0. ...

    The details of how the seed affects the random number sequence
    are unspecified by the standard. I personally would consider a
    pseudo-random number generator to be quite defective if there
    were any seed that produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With
    sophisticated generator polynomials they produce also sequences of
    maximum period; say, for N=31 a non-repeating sequence of length
    2^N - 1. The one element that is missing from the sequence is the
    0 (that reproduces itself).

    Technically you pick some bit-values from fixed positions
    (depending on the generator polynomial) of the register and xor
    the bits to shift the result into the register. Here's ad hoc an
    example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >>
    30)) & 0x1;
    reg <<= 1;
    reg |= new_bit;
    reg &= 0x7fffffff;
    count++;
    } while (reg != init);
    printf ("period: %d\n", count);
    }


    Janis

    [...]


    Pay attention that C Standard only requires for the same seed to
    always produces the same sequence. There is no requirement that
    different seeds have to produce different sequences.
    So, for generator in your example, implementation like below would
    be fully legal. Personally, I wouldn't even consider it as
    particularly poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    [O.T.]
    In practice, using LFSR for rand() is not particularly bright idea
    for different reason: LFSR is a reasonably good PRNG for a single
    bit, but not when you want to generate a group of 31 pseudo-random
    bits. In order to get 31 new bits, without predictable repetitions
    from the previous value, you would have to do 31 steps. That's
    slow! The process can be accelerate by generation of several bits
    at time via look up tables, but in order to get decent speed the
    table has to be rater big and using big tables in standard library
    is bad sportsmanship.

    It seems that overwhelming majority C RTLs use Linear Congruential Generators, probably because for Stanadard library compactness of
    both code and data is considered more important than very high
    speed (not that on modern HW LCGs are slow) or superior random
    properties of Mersenne Twisters.

    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. O?Neill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.


    I don't know. Testing randomness is complicated matter.
    How can I be sure that L?Ecuyer and Simard?s TestU01 suite
    tests things
    that I personally care about and that it does not test things that are
    of no interest for me? Especially, the latter.

    Also, the TestU01 suit is made for generators with 32-bit output.
    M. O?Neill used ad hoc technique to make it applicable to generators
    with 64-bit output. Is this technique right? Or may be it put 64-bit
    PRNG at unfair disadvantage?

    Besides, I strongly disagree with at least one assertion made by
    O?Neill: "While security-related applications should
    use a secure generator, because we cannot always know the future
    contexts in which our code will be used, it seems wise for all
    applications to avoid generators that make discovering their entire
    internal state completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my
    application easy discovery of complete state of PRNG is not a defect.

    Anyway, even if I am skeptical about her criticism of popular PRNGs, intuitively I agree with the constructive part of the article -
    medium-quality PRNG that feeds medium quality hash function can
    potentially produce very good fast PRNG with rather small internal
    state.

    On related note, I think that even simple counter fed into high quality
    hash function (not cryptographically high quality, far less than that)
    can produce excellent PRNG with even smaller internal state. But not
    very fast one. Although the speed depends on specifics of used
    computer. I can imagine computer that has low-latency Rijndael128
    instruction. On such computer, running counter through 3-4 rounds of
    Rijndael ill produce very good PRNG that is only 2-3 times slower than,
    for example, LCG 128/64.



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From James Kuyper@3:633/10 to All on Tue Dec 23 18:29:58 2025
    On 2025-12-23 06:50, Michael S wrote:
    On Tue, 23 Dec 2025 10:54:23 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-23 10:18, Michael S wrote:
    ...
    MT19937-64 is available out of the box(*) in C++. But not in C,
    unfortunately.

    This is really strange given that the name ("Mersenne Twister") is
    that prominent.

    Looking that up I find at least "C" code for MT19937 in Wikipedia
    https://de.wikipedia.org/wiki/Mersenne-Twister
    It's based on 32 bit logic it seems; interpreting your "MT19937-64"
    I assume you're looking for a 64 bit based version?


    "Available out of the box" in this sentence means "part of standard
    library".

    Citation, please? I can find neither Mersenne nor "MT19937-64" anywhere
    in n5001.pdf, the latest draft version of the C++ standard that I have
    access to, which is dated 2024-12-17.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Tue Dec 23 16:30:16 2025
    James Kuyper <jameskuyper@alumni.caltech.edu> writes:
    On 2025-12-23 06:50, Michael S wrote:
    On Tue, 23 Dec 2025 10:54:23 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-23 10:18, Michael S wrote:
    ...
    MT19937-64 is available out of the box(*) in C++. But not in C,
    unfortunately.

    This is really strange given that the name ("Mersenne Twister") is
    that prominent.

    Looking that up I find at least "C" code for MT19937 in Wikipedia
    https://de.wikipedia.org/wiki/Mersenne-Twister
    It's based on 32 bit logic it seems; interpreting your "MT19937-64"
    I assume you're looking for a 64 bit based version?


    "Available out of the box" in this sentence means "part of standard
    library".

    Citation, please? I can find neither Mersenne nor "MT19937-64" anywhere
    in n5001.pdf, the latest draft version of the C++ standard that I have
    access to, which is dated 2024-12-17.

    N5001 29.5.4.3 [rand.eng.mers] "Class template mersenne_twister_engine".

    N5001 29.5.6 [rand.predef] "Engines and engine adaptors with predefined parameters" defines "mt19937" and "mt19937_64".

    Its description of the algorithm isn't very detailed, but it
    does impose some very specific requirements. For mt19937:
    "Required behavior: The 10000th consecutive invocation of a
    default-constructed object of type mt19937 produces the value
    4123659995.". For mt19937_64: "Required behavior: The 10000th
    consecutive invocation of a default-constructed object of type
    mt19937_64 produces the value 9981545732273789042."

    If we're going to discuss this in any more detail (rather than
    discussing random numbers in general), I suggest comp.lang.c++.

    As already mentioned, there are implementations of mt19937 for C,
    but unlike in C++ they aren't part of the standard library.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Wed Dec 24 02:02:59 2025
    On Wed, 24 Dec 2025 00:08:24 +0200, Michael S wrote:

    Testing randomness is complicated matter.

    Impossible, really, if you define ?random? as ?Nobody can know what comes next?.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From James Kuyper@3:633/10 to All on Tue Dec 23 23:43:19 2025
    On 2025-12-23 21:02, Lawrence D?Oliveiro wrote:
    On Wed, 24 Dec 2025 00:08:24 +0200, Michael S wrote:

    Testing randomness is complicated matter.

    Impossible, really, if you define ?random? as ?Nobody can know what comes next?.

    The quality of pseudo-random number generators can be measured, but you
    need to carefully define what you mean by "quality". The relevant
    measures can be different for different purposes. I've seen a randome
    number generator used in a context where the only relevant criteria was
    that the probability of each number occurring was equal. In that
    particular contest, a function that simply always returned the sequence
    0, 1, 2, ... RAND_MAX, and then started over again at the beginning
    would have been good enough. Most applications have somewhat stronger requirements :-)


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Wed Dec 24 05:34:40 2025
    On Tue, 23 Dec 2025 23:43:19 -0500, James Kuyper wrote:

    On 2025-12-23 21:02, Lawrence D?Oliveiro wrote:

    On Wed, 24 Dec 2025 00:08:24 +0200, Michael S wrote:

    Testing randomness is complicated matter.

    Impossible, really, if you define ?random? as ?Nobody can know what
    comes next?.

    The quality of pseudo-random number generators can be measured, but you
    need to carefully define what you mean by "quality".

    That?s not exactly disagreeing with what I?m saying ...

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Chris M. Thomasson@3:633/10 to All on Tue Dec 23 21:44:04 2025
    On 12/22/2025 12:48 AM, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    Forgive me for C++, but this RNG of mine might be useful for detecting
    the state of a system:

    https://groups.google.com/g/comp.lang.c++/c/7u_rLgQe86k/m/fYU9SnuAFQAJ

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Wed Dec 24 06:16:51 2025
    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC), Michael Sanders wrote:

    Wish there was such a 'device' under Windows...

    You should get one if you install WSL2.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Wed Dec 24 10:51:14 2025
    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Chris M. Thomasson@3:633/10 to All on Wed Dec 24 00:59:16 2025
    On 12/24/2025 12:51 AM, Michael S wrote:
    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom



    CSPRNG, vs an actual TRNG?

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Wed Dec 24 12:12:11 2025
    On Wed, 24 Dec 2025 09:00:50 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Michael S <already5chosen@yahoo.com> wrote:
    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:

    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:


    Also, the TestU01 suit is made for generators with 32-bit output.
    M. O?Neill used ad hoc technique to make it applicable to gener
    ators
    with 64-bit output. Is this technique right? Or may be it put 64-bit
    PRNG at unfair disadvantage?

    My point of view is that generator can be used to generate long
    bistream. Then you can cut the bitstream and get number of
    desired size. Good tests should check that such usage leads
    to reasonable properties. So, fact that one generator produces
    32-bit pieces and other produces 64-bit pieces should be irrelevant
    to the test.


    What you say is correct in few use cases. But there are many uses
    cases (in field of testing of numeric code, probably, most of them)
    in which "less random" LS bits are acceptable.
    Not that I can see why it could be the case for MT19937-64, but it
    could apply to one of two of other 64-bit generators tested by O'Neill.


    Besides, I strongly disagree with at least one assertion made by
    O?Neill: "While security-related applications should
    use a secure generator, because we cannot always know the future
    contexts in which our code will be used, it seems wise for all
    applications to avoid generators that make discovering their entire internal state completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my
    application easy discovery of complete state of PRNG is not a
    defect.

    O?Neill is not a prophet, ignore what she say it you think you
    know better (which is probably the above).

    Anyway, even if I am skeptical about her criticism of popular PRNGs, intuitively I agree with the constructive part of the article - medium-quality PRNG that feeds medium quality hash function can
    potentially produce very good fast PRNG with rather small internal
    state.

    She seem to care very much about having minimal possible state.
    That is may be nice on embeded systems, but in general I would
    happily accept slighty bigger state (say 256 bits). But if
    we can get good properties with very small state, then why not?
    After all looking at state and updating it takes code, so
    small state helps with having fast generator.


    Agreed.

    Concerning Mersenne Twister, she is not the only one to
    criticise it. My personal opinion is that given large
    state and not so simple update Mersenne Twister would
    have to be very very good to justify its use.

    One theoretical advantage of MT19937 is that it has period of astronomic proportions. Which means that one instance of PRNG could be
    de-multiplexed into millions or billions of sub-streams with no
    detectable degradation of the quality of each sub-stream.
    However I fail to see how de-multiplexing into more than ~ one
    thousand of sub-streams can be practical. And for the latter one does
    not need to be astronomical, something like period=2**96 would be
    fully sufficient with many bits to spare.
    So, in theory I agree with the criticism. But in practice I am not
    bothered by the size of MT state.

    But it
    fails some tests, so does not look _better_ than other
    generators.


    It would be interesting to find out what were those tests that failed.
    I wonder, if tests suit can run faster on multicore computer. I don't
    want to wait 5-6 hours just to find out that report does not provide an information that I am looking for.

    On related note, I think that even simple counter fed into high
    quality hash function (not cryptographically high quality, far less
    than that) can produce excellent PRNG with even smaller internal
    state. But not very fast one. Although the speed depends on
    specifics of used computer. I can imagine computer that has
    low-latency Rijndael128 instruction. On such computer, running
    counter through 3-4 rounds of Rijndael ill produce very good PRNG
    that is only 2-3 times slower than, for example, LCG 128/64.

    Maybe.


    May be I'd even test my hypothesis. Eventually. Except that, again, I
    am not thrilled by idea of waiting 6 hours for each result.


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From BGB@3:633/10 to All on Wed Dec 24 05:22:11 2025
    On 12/23/2025 11:54 AM, Waldek Hebisch wrote:
    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if
    seeded with 0. ...

    The details of how the seed affects the random number sequence are
    unspecified by the standard. I personally would consider a
    pseudo-random number generator to be quite defective if there were
    any seed that produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With sophisticated
    generator polynomials they produce also sequences of maximum period;
    say, for N=31 a non-repeating sequence of length 2^N - 1. The one
    element that is missing from the sequence is the 0 (that reproduces
    itself).

    Technically you pick some bit-values from fixed positions (depending
    on the generator polynomial) of the register and xor the bits to shift
    the result into the register. Here's ad hoc an example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    uint32_t init = 0x00000038;
    uint32_t reg = init;
    uint32_t new_bit;
    int count = 0;
    do {
    new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >>
    30)) & 0x1;
    reg <<= 1;
    reg |= new_bit;
    reg &= 0x7fffffff;
    count++;
    } while (reg != init);
    printf ("period: %d\n", count);
    }


    Janis

    [...]


    Pay attention that C Standard only requires for the same seed to always
    produces the same sequence. There is no requirement that different
    seeds have to produce different sequences.
    So, for generator in your example, implementation like below would be
    fully legal. Personally, I wouldn't even consider it as particularly
    poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    [O.T.]
    In practice, using LFSR for rand() is not particularly bright idea for
    different reason: LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. In
    order to get 31 new bits, without predictable repetitions from the
    previous value, you would have to do 31 steps. That's slow! The process
    can be accelerate by generation of several bits at time via look up
    tables, but in order to get decent speed the table has to be rater big
    and using big tables in standard library is bad sportsmanship.

    It seems that overwhelming majority C RTLs use Linear Congruential
    Generators, probably because for Stanadard library compactness of both
    code and data is considered more important than very high speed (not
    that on modern HW LCGs are slow) or superior random properties of
    Mersenne Twisters.

    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. O?Neill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.


    A lot can depend on what one wants as well...

    Fast/Simple:
    seed=seed*65521+17;
    val=(seed>>16)&32767;

    At first glance, this approach seems random enough, but these type of
    RNGs have a type of repeating pattern that can become obvious, say, if
    using them to generate random noise images.



    Or, can also work OK (also fast/simple):
    seed=(seed<<1)^(~(seed>>7));
    val=(seed>>8)&32767;

    Some people seem to really like using lookup tables.

    64-bit multiply can potentially be very slow, and multiply in general
    isn't always cheap, so can make sense to avoid using it if not necessary.

    So, shift-and-XOR is fast, above approach is also trivially extended to
    64 bits.

    Its randomness can be improved somewhat (at the cost of speed), say:
    seed1=(seed1<<1)^(~(seed1>>13));
    seed2=(seed2<<3)^(~(seed2>>19));
    seed1^=seed2>>23;
    seed2^=seed1>>23;
    val=(seed1>>11)^(seed2>>11);
    val=(val^(val>>17))&32767;

    Where seed1 and seed2 are two 64-bit values.

    Not much formal testing here, mostly just sort of approaches that seemed
    to work OK IME.



    Had also noted that there are ways to do checksums that are a lot faster
    and simpler than more widespread algorithms and also seem to still do reasonably well at error detection.

    Say, for example:
    sum1=1; sum2=1;
    for(i=0; i<szWords; i++)
    { sum1+=data[i]; sum2+=sum1; }
    sum1=((uint32_t)sum1)+(sum1>>32);
    sum2=((uint32_t)sum2)+(sum2>>32);
    csum=sum1^sum2;

    Where, sum1/sum2 are 64-bit, and data is interpreted as 32-bit words,
    all unsigned.

    But, yeah...


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Wed Dec 24 15:21:11 2025
    On Wed, 24 Dec 2025 06:16:51 -0000 (UTC), Lawrence D?Oliveiro wrote:

    Wish there was such a 'device' under Windows...

    You should get one if you install WSL2.

    To be fair there is the 'Windows entropy pool' & its
    non-deterministic too but its only available via API.

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Ike Naar@3:633/10 to All on Wed Dec 24 15:22:47 2025
    On 2025-12-23, John McCue <jmclnx@gmail.com.invalid> wrote:
    Ike Naar <ike@sdf.org> wrote:
    On 2025-12-23, John McCue <jmclnx@gmail.com.invalid> wrote:
    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    srand takes an unsigned argument.

    unsigned s;
    read(fd, &s, sizeof s);

    I am not quite sure what you are saying about srand(3).

    If you decide to read /dev/urandom, there is no need to
    call srand(3), the OS maintains random data itself. So
    read(2) will just return the random number of the type
    you want based upon the call.

    Sorry, my misunderstanding. I thought you wanted to use a random
    number (from /dev/urandom instead of time(NULL)) to seed srand().

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Wed Dec 24 15:28:24 2025
    On Wed, 24 Dec 2025 10:51:14 +0200, Michael S wrote:

    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom

    Was referring to the concept of a device in the same idiom of BSD/Linux/Apple...

    Something that is just as easy to use.

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Wed Dec 24 15:41:13 2025
    On Tue, 23 Dec 2025 21:44:04 -0800, Chris M. Thomasson wrote:

    On 12/22/2025 12:48 AM, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);


    Forgive me for C++, but this RNG of mine might be useful for detecting
    the state of a system:

    https://groups.google.com/g/comp.lang.c++/c/7u_rLgQe86k/m/fYU9SnuAFQAJ

    My aims (mostly just learning my around C at this point)
    are *much* more simple. I needed something that is seed-able/deterministic/portable allowing the user a
    shot at replaying a round in a silly game I've been
    working on every now & again:

    int genseed(int seed_in) {
    if (seed_in >= 10000000) return seed_in;
    unsigned long t = (unsigned long)time(NULL);
    unsigned long c = (unsigned long)clock();
    return (int)(((t ^ c) % 80000000UL) + 10000000UL);
    }

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Wed Dec 24 15:44:33 2025
    On Tue, 23 Dec 2025 16:08:49 GMT, Scott Lurndal wrote:

    Michael Sanders <porkchop@invalid.foo> writes:
    On Mon, 22 Dec 2025 13:18:19 +0100, Janis Papanagnou wrote:

    There's number sequence generators that produce 0 sequences if seeded
    with 0. And maybe the comment in 'man 3 rand', "If no seed value is
    provided, the rand() function is automatically seeded with a value
    of 1.", may have fostered his doubt.

    Janis - naive question for you...

    How do I bring up *posix only* man pages using 3?

    I see no difference when invoking any of:

    man 3 srand
    or: man 3 posix srand
    or: man posix 3 srand

    What I'm doing wrong here?

    You're looking in the wrong place.

    https://pubs.opengroup.org/onlinepubs/9799919799/

    Select <System Interface> in the top left frame,
    select (3) in the subsequent bottom left frame and
    select the interface name in the bottom left frame. The
    manual page will be in the right frame.

    https://pubs.opengroup.org/onlinepubs/9799919799/functions/rand.html

    Thank you Scott.

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Wed Dec 24 17:44:52 2025
    On Wed, 24 Dec 2025 15:28:24 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Wed, 24 Dec 2025 10:51:14 +0200, Michael S wrote:

    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7: https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later: https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom


    Was referring to the concept of a device in the same idiom of BSD/Linux/Apple...

    Something that is just as easy to use.


    What is not easy in the functions referred above? You do the same
    couple of steps as on Unix: open device then read few bytes from it.
    Only names are different.



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Wed Dec 24 16:17:03 2025
    On Wed, 24 Dec 2025 17:44:52 +0200, Michael S wrote:

    On Wed, 24 Dec 2025 15:28:24 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Wed, 24 Dec 2025 10:51:14 +0200, Michael S wrote:

    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7:
    https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later:
    https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom


    Was referring to the concept of a device in the same idiom of
    BSD/Linux/Apple...

    Something that is just as easy to use.


    What is not easy in the functions referred above? You do the same
    couple of steps as on Unix: open device then read few bytes from it.
    Only names are different.

    Depends the toolkit no?

    Unix:
    $ head -c 8 /dev/urandom | od -An | tr -d ' '
    4fa2c3d17b9a8f12

    Windows:
    PS C:\Users\Bob>
    $bytes = New-Object byte[] 8 [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes) [Console]::Write($bytes | ForEach-Object { "{0:x2}" -f $_ })

    --
    :wq
    Mike Sanders
    es

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Wed Dec 24 17:53:54 2025
    On 2025-12-24 17:17, Michael Sanders wrote:

    Unix:
    $ head -c 8 /dev/urandom | od -An | tr -d ' '
    4fa2c3d17b9a8f12

    Windows:
    PS C:\Users\Bob>
    $bytes = New-Object byte[] 8 [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
    [Console]::Write($bytes | ForEach-Object { "{0:x2}" -f $_ })

    Amazing! 8-o

    Or rather; frightening! ("The little Shop of Horrors")
    A mixture (best/worst) of all; OO, Functional, and Shell?

    What is that; "Powershell", or something else?
    (I've mostly ignored Windows during the past 20+ years.)

    Janis


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Wed Dec 24 18:04:05 2025
    On 2025-12-24 16:41, Michael Sanders wrote:

    My aims (mostly just learning my around C at this point)
    are *much* more simple. I needed something that is seed-able/deterministic/portable allowing the user a
    shot at replaying a round in a silly game I've been
    working on every now & again:

    int genseed(int seed_in) {
    if (seed_in >= 10000000) return seed_in;
    unsigned long t = (unsigned long)time(NULL);
    unsigned long c = (unsigned long)clock();
    return (int)(((t ^ c) % 80000000UL) + 10000000UL);
    }


    If you need a portable function across different platforms
    you may want to write an own random() function, code based
    on some simple, proven algorithm. Or borrow a piece of code
    from some existing public source code library.

    For "_replaying_ a round in a silly game" across platforms
    (or generally) you should not seed it with time() or other
    random factors (as shown in your code snippet).

    Janis


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Wed Dec 24 17:08:48 2025
    Michael S <already5chosen@yahoo.com> writes:
    On Wed, 24 Dec 2025 15:28:24 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Wed, 24 Dec 2025 10:51:14 +0200, Michael S wrote:

    On Tue, 23 Dec 2025 07:25:42 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Thanks John. Wish there was such a 'device' under Windows...


    There is.
    Windows XP/Vista/7:
    https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom
    Win8 and later:
    https://learn.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom


    Was referring to the concept of a device in the same idiom of
    BSD/Linux/Apple...

    Something that is just as easy to use.


    What is not easy in the functions referred above? You do the same
    couple of steps as on Unix: open device then read few bytes from it.
    Only names are different.

    Even easier in the GCC; one can just generate the rdrand instruction directly for intel targets:

    unsigned int __builtin_ia32_rdrand16_step (unsigned short *);
    unsigned int __builtin_ia32_rdrand32_step (unsigned int *);
    unsigned int __builtin_ia32_rdrand64_step (unsigned long long *);

    ARM64 provides a system register (RNDR) which is accessible at
    all exception levels. The Neoverse implementations provide implementation-defined system registers that convert the
    read of the register into a bus transaction to a device which
    satisfies the random number request (set up by the boot firmware);
    so the characteristics of the hardware generator are specific to
    an implementation of a neoverse core.

    int __builtin_arm_rndr(uint64_t *val);
    int __builtin_arm_rndrrs(uint64_t *val);

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Wed Dec 24 17:27:32 2025
    On Wed, 24 Dec 2025 17:53:54 +0100, Janis Papanagnou wrote:

    On 2025-12-24 17:17, Michael Sanders wrote:

    Unix:
    $ head -c 8 /dev/urandom | od -An | tr -d ' '
    4fa2c3d17b9a8f12

    Windows:
    PS C:\Users\Bob>
    $bytes = New-Object byte[] 8
    [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($bytes)
    [Console]::Write($bytes | ForEach-Object { "{0:x2}" -f $_ })

    Amazing! 8-o

    Or rather; frightening! ("The little Shop of Horrors")
    A mixture (best/worst) of all; OO, Functional, and Shell?

    What is that; "Powershell", or something else?
    (I've mostly ignored Windows during the past 20+ years.)

    Janis

    Yes its Powershell. You nailed it spot on IMO its terrible Janis.
    *Speaking only for myself* it's: thick/crufty/oop/functional/shell
    all mashed together like sludge. Awful stuff.

    And plain old 'cmd.exe' (more or less the Windows equivalent
    of /bin/sh) has evolved oddly too with all its 'syntactical sugar'...

    witness: echo hello world? | | tr 'A-Za-z' 'N-ZA-Mn-za-m'

    vs.: <https://rosettacode.org/wiki/Rot-13#Batch_File>

    I'm breaking free from it =)

    This tool helps: <https://frippery.org/busybox/>

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Wed Dec 24 17:33:23 2025
    On Wed, 24 Dec 2025 17:27:32 -0000 (UTC), Michael Sanders wrote:

    witness: echo hello world? | | tr 'A-Za-z' 'N-ZA-Mn-za-m'

    typo, should be: echo hello world? | tr 'A-Za-z' 'N-ZA-Mn-za-m'

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Wed Dec 24 20:16:22 2025
    On Wed, 24 Dec 2025 17:33:23 -0000 (UTC)
    Michael Sanders <porkchop@invalid.foo> wrote:

    On Wed, 24 Dec 2025 17:27:32 -0000 (UTC), Michael Sanders wrote:

    witness: echo hello world? | | tr 'A-Za-z' 'N-ZA-Mn-za-m'

    typo, should be: echo hello world? | tr 'A-Za-z' 'N-ZA-Mn-za-m'


    I agree that Powershell is too complicated and too "wannabe real
    programming language" which makes it bad shell scripting language.
    Esp. so for quick throwaway scripts.

    However I don't quite understand what you find wrong with cmd.exe.
    Cryptic? May be. But I can not imagine shell scripting language which is
    not cryptic in some way.
    Has few limitations that one would not expect in shell script in 2025?
    Yes.
    set a= 1000000
    set /A ab= %a% * 1000
    1000000000
    set /A ab= %a% * 2000
    2000000000
    set /A ab= %a% * 3000
    -1294967296

    But cmd.exe language certainly is *not* over-complicated. Rather more
    like too primitive.


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Wed Dec 24 19:00:32 2025
    On Wed, 24 Dec 2025 15:21:11 -0000 (UTC), Michael Sanders wrote:

    On Wed, 24 Dec 2025 06:16:51 -0000 (UTC), Lawrence D?Oliveiro wrote:

    Wish there was such a 'device' under Windows...

    You should get one if you install WSL2.

    To be fair there is the 'Windows entropy pool' & its
    non-deterministic too but its only available via API.

    You begin to see why Microsoft is supporting Linux more and more.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Chris M. Thomasson@3:633/10 to All on Wed Dec 24 13:48:19 2025
    On 12/23/2025 2:08 PM, Michael S wrote:
    [...]
    I don't know. Testing randomness is complicated matter.

    [...]

    Fwiw, ent is a nice little program:

    https://www.fourmilab.ch/random/



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Thu Dec 25 02:01:11 2025
    On Wed, 24 Dec 2025 20:16:22 +0200, Michael S wrote:

    I agree that Powershell is too complicated and too "wannabe real
    programming language" which makes it bad shell scripting language.
    Esp. so for quick throwaway scripts.

    However I don't quite understand what you find wrong with cmd.exe.
    Cryptic? May be. But I can not imagine shell scripting language which is
    not cryptic in some way.
    Has few limitations that one would not expect in shell script in 2025?
    Yes.
    set a= 1000000
    set /A ab= %a% * 1000
    1000000000
    set /A ab= %a% * 2000
    2000000000
    set /A ab= %a% * 3000
    -1294967296

    But cmd.exe language certainly is *not* over-complicated. Rather more
    like too primitive.

    Complicated doesn't quite capture the zeitgeist, closer to obtuse,
    or baroque. An example below of a valid block that makes Perl look
    good... Notice for instance the alternate use of set compared to
    your example.

    yours: set [option] var = assignment

    below: set "var = assignment"

    Not to mention some other quirks. But that's just my opinion.

    @echo off

    cd /d "%~dp0"
    nul chcp 65001
    set "_MediaInfo=C:\Program Files\MediaInfo\MediaInfo.exe"

    for /f ^usebackq^delims^= %%G in =;(` "%__AppDir__%where.exe" .:v*.mpg `);= do for /f usebackq^delims^= %%i in =;(`
    call "%_MediaInfo%" --inform^^^=Video^^^;^^^%%Height^^^%% "%%~fG" `);= do rename "%%~fG" "%%~nG-%%~ip%%~xG"

    nul chcp 437

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Thu Dec 25 03:17:41 2025
    On Thu, 25 Dec 2025 02:01:11 -0000 (UTC), Michael Sanders wrote:

    Not to mention some other quirks. But that's just my opinion.

    Nothing specific to either cmd.exe or PowerShell, but the Windows
    command line is fundamentally broken. This is because it derives from
    the CP/M command line model, which in turn was inherited from old-time
    DEC operating systems.

    On these DEC systems, the command line was a simple string buffer. So
    there is this assumption that program invocation is always going to be
    mediated by some kind of ?shell? program, and the concept of one
    program directly invoking another is either nonexistent, or only
    grudgingly tolerated.

    Contrast this with the Unix approach, where the command line is an
    array of separate string ?words?. There is no ?shell? that occupies a privileged place in the system; any program can directly invoke any
    other, without having to worry about properly escaping any special
    characters that might be (mis)interpreted by some ?shell?.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Thu Dec 25 04:30:45 2025
    On Wed, 24 Dec 2025 17:53:54 +0100, Janis Papanagnou wrote:

    [...]

    Ok seed is working for replaying a game. =)
    Pure C, no ncurses etc, built in help too.

    You know this game don't you Janis?
    Simple little project. Screenshot...

    <https://drive.google.com/file/d/1dKSjDzmu0mLy76GWrlZUT9HVK72GiP9d/view>

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From BGB@3:633/10 to All on Wed Dec 24 23:09:25 2025
    On 12/24/2025 5:22 AM, BGB wrote:
    On 12/23/2025 11:54 AM, Waldek Hebisch wrote:
    Michael S <already5chosen@yahoo.com> wrote:
    On Mon, 22 Dec 2025 18:41:10 +0100
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:

    On 2025-12-22 18:13, James Kuyper wrote:
    On 2025-12-22 07:18, Janis Papanagnou wrote:
    On 2025-12-22 12:44, James Kuyper wrote:
    On 2025-12-22 03:48, Michael Sanders wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    No, why whould you think so?

    There's number sequence generators that produce 0 sequences if
    seeded with 0. ...

    The details of how the seed affects the random number sequence are
    unspecified by the standard. I personally would consider a
    pseudo-random number generator to be quite defective if there were
    any seed that produced a constant output.

    I wouldn't have mentioned that if there weren't a whole class of
    such functions that expose exactly that behavior by design. Have
    a look for PN-(Pseudo Noise-)generators and LFSR (Linear Feedback
    Shift Registers). These have been defined to produce random noise
    (bit pattern with good statistical distribution). With sophisticated
    generator polynomials they produce also sequences of maximum period;
    say, for N=31 a non-repeating sequence of length 2^N - 1. The one
    element that is missing from the sequence is the 0 (that reproduces
    itself).

    Technically you pick some bit-values from fixed positions (depending
    on the generator polynomial) of the register and xor the bits to shift >>>> the result into the register. Here's ad hoc an example...

    #include <stdio.h>
    #include <stdint.h>

    int main ()
    {
    ÿÿÿÿÿ uint32_t init = 0x00000038;
    ÿÿÿÿÿ uint32_t reg = init;
    ÿÿÿÿÿ uint32_t new_bit;
    ÿÿÿÿÿ int count = 0;
    ÿÿÿÿÿ do {
    ÿÿÿÿÿÿÿÿÿ new_bit = ((reg >> 2) + (reg >> 4) + (reg >> 6) + (reg >>
    30)) & 0x1;
    ÿÿÿÿÿÿÿÿÿ reg <<= 1;
    ÿÿÿÿÿÿÿÿÿ reg |= new_bit;
    ÿÿÿÿÿÿÿÿÿ reg &= 0x7fffffff;
    ÿÿÿÿÿÿÿÿÿ count++;
    ÿÿÿÿÿ } while (reg != init);
    ÿÿÿÿÿ printf ("period: %d\n", count);
    }


    Janis

    [...]


    Pay attention that C Standard only requires for the same seed to always
    produces the same sequence. There is no requirement that different
    seeds have to produce different sequences.
    So, for generator in your example, implementation like below would be
    fully legal. Personally, I wouldn't even consider it as particularly
    poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    [O.T.]
    In practice, using LFSR for rand() is not particularly bright idea for
    different reason: LFSR is a reasonably good PRNG for a single bit, but
    not when you want to generate a group of 31 pseudo-random bits. In
    order to get 31 new bits, without predictable repetitions from the
    previous value, you would have to do 31 steps. That's slow! The process
    can be accelerate by generation of several bits at time via look up
    tables, but in order to get decent speed the table has to be rater big
    and using big tables in standard library is bad sportsmanship.

    It seems that overwhelming majority C RTLs use Linear Congruential
    Generators, probably because for Stanadard library compactness of both
    code and data is considered more important than very high speed (not
    that on modern HW LCGs are slow) or superior random properties of
    Mersenne Twisters.

    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. O?Neill where she gives a family of algorithms and runs
    several statistical tests against known algorithms.ÿ Mersenne
    Twister does not look good in tests.ÿ If you have enough (128) bits
    LCGs do pass tests.ÿ A bunch of generators with 64-bit state also
    passes tests.ÿ So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries.ÿ Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.


    A lot can depend on what one wants as well...

    Fast/Simple:
    ÿ seed=seed*65521+17;
    ÿ val=(seed>>16)&32767;

    At first glance, this approach seems random enough, but these type of
    RNGs have a type of repeating pattern that can become obvious, say, if
    using them to generate random noise images.


    Turns out more true of the one below than the one above, though both are
    still pretty weak.


    Usually, one very quick/dirty way to test a RNG is to generate an image
    filled with random numbers:
    If it looks like white noise, the RNG works OK;
    If the image has repeating patterns or any other sort of obvious
    structure, the RNG is broken.

    Not a great test, but for many practical use cases may be good enough.

    The above example shows evidence of "structural" features, implying the
    RNG may not be particularly strong.



    Or, can also work OK (also fast/simple):
    ÿ seed=(seed<<1)^(~(seed>>7));
    ÿ val=(seed>>8)&32767;


    Got around to testing, this particular one was kinda broken (breaks down
    into repeating patterns).


    A tweak, say:
    seed=(seed<<1)^(~(seed>>7))^(seed>>21);
    val=(seed>>8)&32767;
    Is much better...

    Also seems to work:
    seed=(seed<<3)^(~(seed>>19))^(seed>>13);
    Etc.



    Seems the simple pattern with one XOR may be too little, and at least
    two XORs may be needed here. But, this approach is still fragile and
    prone to break down into repeating patterns. Also seems to need a mix of
    a prime and non-prime for some reason.


    A lot may depend on on how quickly the random numbers need to be
    generated vs the quality of the needed randomness (and whether or not
    the target machine has a fast integer multiply).

    Where, say, one down-side of more traditional "multiply 64-bit value by constant" RNGs is that performance suffers badly in the absence of fast
    64-bit multiply.

    one other tradeoff being if the target machine is natively 32 or 64 bit.


    In some cases, it may make sense to do a fast RNG with a simpler
    algorithm, but then ever N numbers, reseed the fast RNG with a number generated by a slower but more powerful RNG.

    Where, say, fast RNG may be needed for things like genetic algorithms
    where often the performance of the mutation step depends highly on
    having fast random numbers (with the actual goodness of the RNG being secondary, but a certain minimum is needed to avoid the process breaking down).



    Some people seem to really like using lookup tables.


    I don't really get the point of lookup table driven RNGs.

    A lot of these ones are, say, a table with 256 spots, and an index.
    Each time one generates a random number (usually a byte), it returns the
    value at that location in the table and advances to the next index.

    Sometimes some get clever and use an algorithm to jitter the table index.

    I have mostly seen this strategy used in old game engines.

    These typically fail the image test as by design they give repeating
    patterns.



    64-bit multiply can potentially be very slow, and multiply in general
    isn't always cheap, so can make sense to avoid using it if not necessary.

    So, shift-and-XOR is fast, above approach is also trivially extended to
    64 bits.

    Its randomness can be improved somewhat (at the cost of speed), say:
    ÿ seed1=(seed1<<1)^(~(seed1>>13));
    ÿ seed2=(seed2<<3)^(~(seed2>>19));
    ÿ seed1^=seed2>>23;
    ÿ seed2^=seed1>>23;
    ÿ val=(seed1>>11)^(seed2>>11);
    ÿ val=(val^(val>>17))&32767;

    Where seed1 and seed2 are two 64-bit values.

    Not much formal testing here, mostly just sort of approaches that seemed
    to work OK IME.


    The strategy used above is closer to what I had often used IME.

    If more randomness is needed, the pattern can be extended to 4 seeds and
    with more variability in the shifts.

    Or, can also be trimmed down if it needs to be faster.



    Had also noted that there are ways to do checksums that are a lot faster
    and simpler than more widespread algorithms and also seem to still do reasonably well at error detection.

    Say, for example:
    ÿ sum1=1; sum2=1;
    ÿ for(i=0; i<szWords; i++)
    ÿÿÿ { sum1+=data[i]; sum2+=sum1; }
    ÿ sum1=((uint32_t)sum1)+(sum1>>32);
    ÿ sum2=((uint32_t)sum2)+(sum2>>32);
    ÿ csum=sum1^sum2;

    Where, sum1/sum2 are 64-bit, and data is interpreted as 32-bit words,
    all unsigned.


    A variant running 2 or 4 sets of sums in parallel can also be faster,
    but the single pair of sums demonstrates the basic idea.

    Though, special handling may be needed if the data is not a multiple of
    the working size (if this needs to be handled, one may need special case
    logic to deal with the last N bytes).


    Note here that a single linear sum of all the values, while fast, is
    often unreasonably weak (and will miss many types of errors).

    A second sum of the first sum can greatly increase error detection.

    While, at the same time, being significantly faster than something like
    CRC32 (one may find that for various use-cases, things like CRC32 or
    Adler32 checksums may be unacceptably slow).



    I have yet to find an ideally fast strategy for hashing strings.

    One simple strategy is, say:
    h=0; cs=str;
    while(*cs)h=h*251+(*cs++);
    h=h*251+1;
    v=(h>>8)&4095; //or, whatever is the size of the hash table.

    But, this isn't particularly fast.

    But, then another problem is that strings are usually short and one
    doesn't know the length in advance. Using "strlen()" also being not
    exactly fast either (trying to use "strlen()") may be slower than the
    naive approach.

    one variation of the above is, say:
    cs=str+1; c=*str; h=0;
    while(c) { h=h*251+c; c=*cs++; }
    Or, say:
    cs=str+1; c=*str; h=0;
    while(c) { h=(h<<7)+(c-h); c=*cs++; }
    Or, ...

    But, gains are small, and usually not really an obvious way to speed it
    up when the majority of string lengths are between 3 and 7 characters
    (most strategies being more effective against longer strings).

    while, at least, excluding wonk like:
    c0=str[0]; c1=str[1]; ...
    if(!c0) { return(0); }
    if(!c1) { return(c0); }
    if(!c2) { return((c0*31+c1)&4095); }
    ...
    Then falling back to a generic strategy for 8+ characters.


    ...


    But, yeah...



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From BGB@3:633/10 to All on Wed Dec 24 23:35:55 2025
    On 12/23/2025 8:55 AM, Michael Sanders wrote:
    On Tue, 23 Dec 2025 02:17:01 -0000 (UTC), Lawrence D?Oliveiro wrote:

    On Tue, 23 Dec 2025 00:39:49 -0000 (UTC), John McCue wrote:

    I like to just read /dev/urandom when I need a random number. Seem
    easier and more portable across Linux & the *BSDs.

    Not to mention a lot stronger, cryptographically.

    No srand() combined with crypto on my end. Sounds like an invitation
    to get hacked from everything I've ever read about mixing the two.


    While arguably a typical C library "rand()" isn't that strong, if one
    has a number sequence of output random digits, it might still take an impractical amount of time to brute-force search the entire seed space
    for a 64-bit seed.


    So, for example, if used to encrypt a point-to-point session, it is
    likely whatever session would have ended long before the attacker could brute-force the pattern.

    And, AFAIK, with a typical LCG there is no good way to feed the numbers
    back into the RNG to get back the seed (though, such a strategy is
    possible with some of my shift-and-XOR RNG's, but these are usually more intended to generate numbers faster, as the C library "rand()" is
    sometimes not fast enough...).



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Thu Dec 25 05:41:39 2025
    On Wed, 24 Dec 2025 18:04:05 +0100, Janis Papanagnou wrote:

    On 2025-12-24 16:41, Michael Sanders wrote:

    My aims (mostly just learning my around C at this point)
    are *much* more simple. I needed something that is
    seed-able/deterministic/portable allowing the user a
    shot at replaying a round in a silly game I've been
    working on every now & again:

    int genseed(int seed_in) {
    if (seed_in >= 10000000) return seed_in;
    unsigned long t = (unsigned long)time(NULL);
    unsigned long c = (unsigned long)clock();
    return (int)(((t ^ c) % 80000000UL) + 10000000UL);
    }


    If you need a portable function across different platforms
    you may want to write an own random() function, code based
    on some simple, proven algorithm. Or borrow a piece of code
    from some existing public source code library.

    For "_replaying_ a round in a silly game" across platforms
    (or generally) you should not seed it with time() or other
    random factors (as shown in your code snippet).

    Janis

    I've been thinking about this very issue. There *must* be a
    portable default (hence time at least for now) if the user
    supplies a non-sensical seed or no seed at all & it also
    must be deterministic or the seed idea wont work.

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Thu Dec 25 09:10:16 2025
    On 2025-12-25 05:30, Michael Sanders wrote:

    Ok seed is working for replaying a game. =)
    Pure C, no ncurses etc, built in help too.

    How did you implement the colored squares? Using characters from
    an "extended" character set and ANSI controls for the colors?

    You know this game don't you Janis?
    Simple little project. Screenshot...

    <https://drive.google.com/file/d/1dKSjDzmu0mLy76GWrlZUT9HVK72GiP9d/view>

    Looks nice.[*]

    Only that it looks as if one gets too much information (compared
    to the original Mastermind)! IMO and AFAIK one should *not* get
    the _exact_ place of a wrong digit indicated. (The green hints
    should all be left aligned, and the [optional] red ones all right
    aligned, and the blue ones in between.)

    Janis

    [*] 18 months ago I've written an optically less appealing command
    line variant to refresh my Algol 68 skills. Though I just notice
    that I hadn't finish it yet; I've only implemented playing modes 1
    and 2. ;-)


    Enter a value for #places: 4
    Enter a value for #colors: 6

    Available playing modes:
    1 - computer selects, human guesses
    2 - human selects, computer guesses
    3 - alternate select/guess roles per game
    4 - human selects, human guesses
    5 - computer selects, computer guesses
    0 - leave the game
    Choose the playing mode (1-5): 1

    A secret color combination to guess has been chosen.
    You have to guess it.

    Enter a color combination: 1122
    Turn 1: 1 1 2 2 -
    Enter a color combination: 3345
    Turn 2: 3 3 4 5 - @
    Enter a color combination: 6444
    Turn 3: 6 4 4 4 - @@
    Enter a color combination: 6646
    Turn 4: 6 6 4 6 - @@@@

    You guessed it!


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Thu Dec 25 09:51:14 2025
    On 2025-12-25 06:09, BGB wrote:
    On 12/24/2025 5:22 AM, BGB wrote:

    Some people seem to really like using lookup tables.

    Of course; they can speed up things significantly. And simplify
    the operations for contemporary data sizes (bytes, 64 bit words,
    etc.).


    I don't really get the point of lookup table driven RNGs.

    In e.g. bit-oriented algorithms (e.g. based on LFSR) you can speed
    up processing significantly by processing larger quantities (like
    octets/bytes) and processing/accessing values then byte-wise.[*]

    Someone already mentioned that a bit-wise operating PN-generator
    for random numbers could make use of such a table driven approach.
    (You can extend that principle to larger quantities than bits, e.g.
    to gain from larger processor word lengths, especially it you need
    those larger entities as result in the first place.)

    A lot of these ones are, say, a table with 256 spots, and an index.
    Each time one generates a random number (usually a byte), it returns the value at that location in the table and advances to the next index.

    Sometimes some get clever and use an algorithm to jitter the table index.

    I have mostly seen this strategy used in old game engines.

    These typically fail the image test as by design they give repeating patterns.

    [...]

    Janis

    [*] Here's some old "C" example for a CRC-16 using table-lookup... http://random.gridbug.de/ccitt_crc16.c


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From BGB@3:633/10 to All on Thu Dec 25 03:07:03 2025
    On 12/24/2025 1:00 PM, Lawrence D?Oliveiro wrote:
    On Wed, 24 Dec 2025 15:21:11 -0000 (UTC), Michael Sanders wrote:

    On Wed, 24 Dec 2025 06:16:51 -0000 (UTC), Lawrence D?Oliveiro wrote:

    Wish there was such a 'device' under Windows...

    You should get one if you install WSL2.

    To be fair there is the 'Windows entropy pool' & its
    non-deterministic too but its only available via API.

    You begin to see why Microsoft is supporting Linux more and more.


    Usual strategy IME is usually to save a file or similar with RNG state
    for a big RNG hidden inside somewhere, and then the program loads the
    file, goes through an "entropy mining" step, and then either immediately
    or eventually saves the file again.

    This approach is generally portable.


    In some other types of programs, the RNG state is hidden inside some
    other type of data that tends to be saved and reloaded (such as the
    player state in a 3D engine).

    One entropy-mining process is to use "clock()" or similar and then spin
    in a loop for a certain amount of time effectively building a hash of
    the values returned by clock. The exact timing when the values change
    will tend to carry a certain amount of entropy.


    Say, for example:
    t0=clock();
    t0e=t0+(0.1*CLOCKS_PER_SEC); //usually not too obnoxious.
    t1=t0;
    seed1=1; seed2=1;
    while(t1<t0e)
    { t1=clock(); seed1+=t1; seed2+=seed1; }
    seed1+=((uint32_t)seed1)+(seed1>>32);
    seed2+=((uint32_t)seed2)+(seed2>>32);
    seed=seed1^seed2;

    This can be combined with the saved/restored RNG state.

    This entropy-mining approach seems to work reasonably well despite its limitations (will typically give a unique value each run).

    granted, doesn't work if the system as a whole is sufficiently
    deterministic (doesn't really work on microcontrollers or similar).

    ...



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From BGB@3:633/10 to All on Thu Dec 25 04:24:11 2025
    On 12/25/2025 2:51 AM, Janis Papanagnou wrote:
    On 2025-12-25 06:09, BGB wrote:
    On 12/24/2025 5:22 AM, BGB wrote:

    Some people seem to really like using lookup tables.

    Of course; they can speed up things significantly. And simplify
    the operations for contemporary data sizes (bytes, 64 bit words,
    etc.).


    I don't really get the point of lookup table driven RNGs.

    In e.g. bit-oriented algorithms (e.g. based on LFSR) you can speed
    up processing significantly by processing larger quantities (like octets/bytes) and processing/accessing values then byte-wise.[*]

    Someone already mentioned that a bit-wise operating PN-generator
    for random numbers could make use of such a table driven approach.
    (You can extend that principle to larger quantities than bits, e.g.
    to gain from larger processor word lengths, especially it you need
    those larger entities as result in the first place.)



    I meant, more like this (from Doom):
    <==
    unsigned char rndtable[256] = {
    0, 8, 109, 220, 222, 241, 149, 107, 75, 248, 254, 140, 16, 66 ,
    74, 21, 211, 47, 80, 242, 154, 27, 205, 128, 161, 89, 77, 36 ,
    95, 110, 85, 48, 212, 140, 211, 249, 22, 79, 200, 50, 28, 188 ,
    52, 140, 202, 120, 68, 145, 62, 70, 184, 190, 91, 197, 152, 224 ,
    149, 104, 25, 178, 252, 182, 202, 182, 141, 197, 4, 81, 181, 242 ,
    145, 42, 39, 227, 156, 198, 225, 193, 219, 93, 122, 175, 249, 0 ,
    175, 143, 70, 239, 46, 246, 163, 53, 163, 109, 168, 135, 2, 235 ,
    25, 92, 20, 145, 138, 77, 69, 166, 78, 176, 173, 212, 166, 113 ,
    94, 161, 41, 50, 239, 49, 111, 164, 70, 60, 2, 37, 171, 75 ,
    136, 156, 11, 56, 42, 146, 138, 229, 73, 146, 77, 61, 98, 196 ,
    135, 106, 63, 197, 195, 86, 96, 203, 113, 101, 170, 247, 181, 113 ,
    80, 250, 108, 7, 255, 237, 129, 226, 79, 107, 112, 166, 103, 241 ,
    24, 223, 239, 120, 198, 58, 60, 82, 128, 3, 184, 66, 143, 224 ,
    145, 224, 81, 206, 163, 45, 63, 90, 168, 114, 59, 33, 159, 95 ,
    28, 139, 123, 98, 125, 196, 15, 70, 194, 253, 54, 14, 109, 226 ,
    71, 17, 161, 93, 186, 87, 244, 138, 20, 52, 123, 251, 26, 36 ,
    17, 46, 52, 231, 232, 76, 31, 221, 84, 37, 216, 165, 212, 106 ,
    197, 242, 98, 43, 39, 175, 254, 145, 190, 84, 118, 222, 187, 136 ,
    120, 163, 236, 249
    };

    int rndindex = 0;
    int prndindex = 0;

    int P_Random (void)
    {
    prndindex = (prndindex+1)&0xff;
    return rndtable[prndindex];
    }

    int M_Random (void)
    {
    rndindex = (rndindex+1)&0xff;
    return rndtable[rndindex];
    }

    void M_ClearRandom (void)
    {
    rndindex = prndindex = 0;
    }



    Like, why?...

    There are different/better ways of doing RNG.



    Granted, can't really change this in Doom, because modifying it means
    that demos will desync (or, at least, more than they desync already). Apparently some source-ports (like ZDoom) put significant effort into immitating the behavior of each version of the Doom engine such that the
    demos from the corresponding IWADs work (with magic to try to identify
    the Doom version by pattern-matching the IWAD).

    My port is based on linuxdoom, and doesn't go this far (there was a combination of both algorithmic differences and also Doom to some extent
    being sensitive to the contents of memory from out-of-bounds memory
    accesses).

    Had tried copying a few simple tricks from ZDoom, but were not enough to
    get desync free demo-playback (though, it plays out the same between
    builds for different targets, ...).



    Kind of a similar issue with my ROTT port, except that ROTT is more of a
    mess, and there isn't even consistency between runs, or even necessarily between different runs of the same demo within the demo loop.

    Had sort of got it stabilized, but in the past could seemingly only get consistent desync-free playback when built for 32-bit x86.


    But, then again, did at one point investigate why a feature had broken
    in ROTT, and realized that the code in question was sensitive to (among
    other things) the specific behavior of a signed-integer overflow when promoting to a larger type (it only worked correctly if it first
    overflowed at the narrower size and was then promoted; rather than one
    where it simply did the operation at the wider size with no
    wrap-on-overflow behavior).

    Well, and also a lot of cases where there was a lot of out-of-bounds
    accesses, which I then needed to try to hack around to handle in some
    way "sensible" that was still mostly consistent with the original out-of-bounds access behavior.

    And, some amount of "YOLO" array access, namely accessing one array out
    of bounds to access stuff in another array mostly depending on their
    relative locations in memory (this is not a coding style that I
    particularly consider acceptable, unless maybe the application itself
    directly controls the memory layout, rather than something that is
    mostly in the domain of the C compiler or linker).

    Though, there did still end up being a certain amount of "characteristic behavior" changes resulting from fixing up OOB issues.

    Say, for example, if the player manages to get outside the map, rather
    than the game corrupting memory and crashing; one may find themselves on
    an infinite plane where the map just wraps around (once every 128 blocks).

    But, despite everything, there is still some sort of lingering
    non-determinism in the mix here.

    ...


    Well, at least my Hexen port seemingly has non-desync'ing demos. And my Heretic port seemingly works correctly with the Shareware WAD I have
    (but not with a registered WAD; but the registered WAD has a lot more
    maps vs the Shareware version).

    As with Doom, behavior is mostly consistent across targets. Though, each
    did require modification to work on ARM, since ARM defaults to unsigned
    char and the various versions of the Doom engine had contained code that assumed that 'char' was signed (well, along with a lot of obligatory
    changes in "r_data.c" and similar to deal with 64-bit targets having
    64-bit pointers and similar, etc).

    ...



    Some later games (mostly starting with Quake) had instead avoided desync issues by recording game event messages. Rather than trying to record
    and playback player keyboard inputs and similar.

    Though, in my attempt to port Quake3, I ended up dropping the original
    QVM as it was designed in a way that wasn't really compatible with
    64-bit machines (but, the "game" code was also written in C and so could
    be compiled to native code).

    In my case, one possibility could be (if I want to move away from native
    code) compiling the "game VM" scripts as RISC-V or similar and then
    using an RV64 VM in place of the QVM (well, this or figure out how to
    make the QVM work on a 64-bit host, but this is harder as it also
    assumes sharing structs between the host and VM, which only really works
    if both have the same layout).

    Some fudging was needed with the Quake 1 "progs.dat" VM, but had been a
    little simpler as less was required here (went from absolute addresses
    to hunk-relative addresses for things like strings, etc). Well, because
    again the VM was designed in a way that it was not particularly
    amendable to changing memory layout for some structures based on things
    like pointer size. In this case, there was a relatively smaller cross
    section, so switching to functions to wrap/unwrap strings and similar
    was less of an issue.

    ...



    A lot of these ones are, say, a table with 256 spots, and an index.
    Each time one generates a random number (usually a byte), it returns
    the value at that location in the table and advances to the next index.

    Sometimes some get clever and use an algorithm to jitter the table index.

    I have mostly seen this strategy used in old game engines.

    These typically fail the image test as by design they give repeating
    patterns.

    [...]

    Janis

    [*] Here's some old "C" example for a CRC-16 using table-lookup... http://random.gridbug.de/ccitt_crc16.c


    Not exactly the same thing, but works.

    Even as such though, lookup table driven CRC still isn't particularly
    fast if compared with "linear sums of 32-bit words" or similar.



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Thu Dec 25 19:31:26 2025
    On Thu, 25 Dec 2025 03:07:03 -0600, BGB wrote:

    One entropy-mining process is to use "clock()" or similar and then
    spin in a loop for a certain amount of time effectively building a
    hash of the values returned by clock. The exact timing when the
    values change will tend to carry a certain amount of entropy.

    The turbulence of the air/gas inside disk drives is apparently a good
    source of randomness.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Janis Papanagnou@3:633/10 to All on Thu Dec 25 21:14:00 2025
    On 2025-12-25 20:31, Lawrence D?Oliveiro wrote:

    The turbulence of the air/gas inside disk drives is apparently a good
    source of randomness.

    $ ln /dev/sda /dev/rnd # ;-)

    Too bad that I recently switched to SSDs. :-/

    Janis


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From BGB@3:633/10 to All on Thu Dec 25 15:29:59 2025
    On 12/25/2025 1:31 PM, Lawrence D?Oliveiro wrote:
    On Thu, 25 Dec 2025 03:07:03 -0600, BGB wrote:

    One entropy-mining process is to use "clock()" or similar and then
    spin in a loop for a certain amount of time effectively building a
    hash of the values returned by clock. The exact timing when the
    values change will tend to carry a certain amount of entropy.

    The turbulence of the air/gas inside disk drives is apparently a good
    source of randomness.

    Yeah, but one doesn't easily have access to this information.
    Likewise to access from the low order bits of CPU thermometers or
    similar, etc.

    For some of my targets, there is also no HDD (typically, everything runs
    off of SD cards).


    FWIW, in my own CPU design, there is actually a hardware RNG where
    internal signals are basically gathered up and fed around the bus in a
    special noise channel and used to continuously feed into a hardware RNG
    for which a value can be read with a special CPU instruction.

    But, alas, mainline CPUs lack such a feature.

    On x86, it is also possible to get some level of entropy from mining
    RDTSC, but this is non-portable.




    But, yeah, tested out a few more RNG designs, and ATM:
    seed1 ^= ~(seed2>>47); seed2 ^= ~(seed1>>43); // 4 cycles
    seed1 ^= (seed1<<13); seed2 ^= (seed2>>11); // 4 cycles
    seed1 ^= (seed1>>19); seed2 ^= (seed2<<17); // 4 cycles
    val = ((seed1 ^ seed2) >> 32) & 0x7FFF; // 6 cycles
    Seems to be working pretty OK (decent randomness), and is moderately fast.

    Add cost of +4 cycles for LD (2c penalty), +2 ST
    Est cost: Around 24 clock cycles.


    Though, breaking up the shifts and xors using temporaries could be used
    to micro-optimize it a little more (vs trying to rely on compile-time instruction shuffling).

    Downside as that this particular approach (XOR'ing values with
    themselves and modifying the original variable before the next step),
    creates a lot of dependencies which limits the potential ILP (can't get
    ILP over 2 in this case).

    Where, the interleaved "seed1 = (seed1<<SH1) ^ (seed1>>SH2);" pattern
    allows for slightly higher ILP, but seemingly gets less randomness per
    shift (so the total dependency length ends up similar).

    In the CPU in question, would effectively need 4 seed values to get
    better ILP, say:
    seed1 ^= ~(seed2>>47); seed2 ^= ~(seed3>>43);
    seed3 ^= ~(seed4>>53); seed4 ^= ~(seed1>>41);
    // ~ 4 cycles
    seed1 ^= (seed1<<13); seed2 ^= (seed2>>11);
    seed3 ^= (seed3<< 7); seed4 ^= (seed4>> 5);
    seed1 ^= (seed1>>19); seed2 ^= (seed2<<17);
    seed3 ^= (seed3>>23); seed4 ^= (seed4<<29);
    // ~ 6 cycles
    val = ((seed1 ^ seed2) >> 32) & 0x7FFF; // 6 cycles
    //+ 8 cycles (Global LD/ST)
    Est Cost: Around 24 clock cycles.

    (24 cycle estimate assuming compiler shuffles instructions into a
    roughly optimal order).


    Though, with more operations still likely to be slower, despite the
    higher ILP potential (the higher ILP would merely reduce the slowdown
    from having more operations).

    In this case, ideally one wants to be able to have groups of 6
    non-dependent ALU operations on a 3-wide CPU with 2-cycle ALU latency.
    Though, in this case, achieving peak ILP would actually require ~ 6 or 8
    seed registers. Though, in this case, would require using XG3, for
    RISC-V it would also suffer due to RV64 only having 32 GPRs rather than
    64, so trying to use 8 seeds in parallel would run out of GPRs on RISC-V (don't want to spill, this will hurt a lot worse than the lost ILP).

    Where, if ILP is maximized, one can get to around 0.3 clock cycles per operation.



    But, on the target in question, 2 or 4 seeds is still likely to be considerably faster than, say:
    seed = seed * 0x0000FC4BA2F7ACABULL + 1;

    Which suffers from the "great evil" known as "slow 64-bit multiply"
    (needs 68 cycles to do a 64-bit multiply, similar to performing a divide).

    Where, the cost of the 64-bit integer multiply will stomp all over the
    cycle cost of the former (well, and not like "rand()" tends to be used
    all that heavily in any case).


    Well, contrast to the naive lookup-table approach:
    index=(index+1)&255; //latency = 8 cycles (3 LD, 2 ADD, 2 AND, 1 ST)
    val=table[index]; //latency = 5 cycles (2 ADD, 3 LD).
    So: ~ 13 cycles of latency.

    So, looks simple, but not great in this case (both weak, and latency
    isn't great for its weakness).


    Then again:
    seed = seed * 0x0000FC4BA2F7ACABULL + 1;
    //75 cycles: 3 LD, 1 const, 68 MUL, 2 ADD, 1 ST
    ret = (myseed >> 48) & 0x7FFF; //4 cycles: 2 SHR, 2 AND
    Cost: Around 80 cycles.


    ...



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Paul@3:633/10 to All on Thu Dec 25 23:25:39 2025
    On Thu, 12/25/2025 4:29 PM, BGB wrote:
    On 12/25/2025 1:31 PM, Lawrence D?Oliveiro wrote:
    On Thu, 25 Dec 2025 03:07:03 -0600, BGB wrote:

    One entropy-mining process is to use "clock()" or similar and then
    spin in a loop for a certain amount of time effectively building a
    hash of the values returned by clock. The exact timing when the
    values change will tend to carry a certain amount of entropy.

    The turbulence of the air/gas inside disk drives is apparently a good
    source of randomness.

    Yeah, but one doesn't easily have access to this information.
    Likewise to access from the low order bits of CPU thermometers or similar, etc.

    For some of my targets, there is also no HDD (typically, everything runs off of SD cards).


    FWIW, in my own CPU design, there is actually a hardware RNG where internal signals are basically gathered up and fed around the bus in a special noise channel and used to continuously feed into a hardware RNG for which a value can be read with a special CPU instruction.

    But, alas, mainline CPUs lack such a feature.

    How have you concluded such a thing ?

    My CPU happens to have a random number generator running at 500MB/sec.

    And it works on the same principle as other RNGs. It uses one physical
    process for entropy, and it uses a pseudo random number generator for
    the at-speed part (the 500MB/sec).

    On mine, there are 16 ring oscillators, with one ring oscillator
    having three inverters in a ring. The slowest oscillator has 59 inverters
    in a row. (The number of inverters must be an odd number, in order
    to ensure the oscillators start OK.) A sampling circuit samples all
    sixteen RO and creates a 16 bit number. The 16 bit number is a "seed" to
    the pseudo random number generator.

    Keywords like: "RDRAND, RDSEED"

    https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/white-papers/amd-random-number-generator.pdf

    There are other implementations. Intel has more than one method for doing this, historically.

    https://www.electronicdesign.com/resources/article/21796238/understanding-intels-ivy-bridge-random-number-generator

    The Linux people happen not to like those, but, they exist anyway, chugging away.

    And completely unrelated.

    https://www.2uo.de/myths-about-urandom/

    Paul

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From BGB@3:633/10 to All on Thu Dec 25 23:41:11 2025
    On 12/25/2025 10:25 PM, Paul wrote:
    On Thu, 12/25/2025 4:29 PM, BGB wrote:
    On 12/25/2025 1:31 PM, Lawrence D?Oliveiro wrote:
    On Thu, 25 Dec 2025 03:07:03 -0600, BGB wrote:

    One entropy-mining process is to use "clock()" or similar and then
    spin in a loop for a certain amount of time effectively building a
    hash of the values returned by clock. The exact timing when the
    values change will tend to carry a certain amount of entropy.

    The turbulence of the air/gas inside disk drives is apparently a good
    source of randomness.

    Yeah, but one doesn't easily have access to this information.
    Likewise to access from the low order bits of CPU thermometers or similar, etc.

    For some of my targets, there is also no HDD (typically, everything runs off of SD cards).


    FWIW, in my own CPU design, there is actually a hardware RNG where internal signals are basically gathered up and fed around the bus in a special noise channel and used to continuously feed into a hardware RNG for which a value can be read with a special CPU instruction.

    But, alas, mainline CPUs lack such a feature.

    How have you concluded such a thing ?

    My CPU happens to have a random number generator running at 500MB/sec.

    And it works on the same principle as other RNGs. It uses one physical process for entropy, and it uses a pseudo random number generator for
    the at-speed part (the 500MB/sec).

    On mine, there are 16 ring oscillators, with one ring oscillator
    having three inverters in a ring. The slowest oscillator has 59 inverters
    in a row. (The number of inverters must be an odd number, in order
    to ensure the oscillators start OK.) A sampling circuit samples all
    sixteen RO and creates a 16 bit number. The 16 bit number is a "seed" to
    the pseudo random number generator.

    Keywords like: "RDRAND, RDSEED"

    https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/white-papers/amd-random-number-generator.pdf

    There are other implementations. Intel has more than one method for doing this, historically.

    https://www.electronicdesign.com/resources/article/21796238/understanding-intels-ivy-bridge-random-number-generator

    The Linux people happen not to like those, but, they exist anyway, chugging away.

    And completely unrelated.

    https://www.2uo.de/myths-about-urandom/


    OK.

    I hadn't heard about it; seems my PC's CPU is just barely new enough to support it (apparently with a timing of around 1300 cycles per RDRAND on
    the Zen+).

    Would theoretically give around 2.85 million RNG's per second.

    Though, either way, relying on it wouldn't be particularly portable
    (would need something x86+compiler+CPU specific). Could maybe make sense
    for seeding a PRNG though if target-specific code is OK in this sense.



    Quick check:
    One of the RNG's algos mentioned previously runs at roughly 300 million
    RNGs in one second.

    One of the slightly faster designs pulls off around 800 million RNGs in
    one second.

    ...


    Though, one can maybe divide RNGs into categories:
    Needs to be true random;
    Pseudorandom is OK, but it doesn't matter;
    Needs to be deterministic.
    RNG only provides appearance of randomness,
    but said randomness needs to play out the same for each run.

    It is not "one size fits all" in this case...



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Fri Dec 26 05:42:18 2025
    On Thu, 25 Dec 2025 23:25:39 -0500, Paul wrote:

    My CPU happens to have a random number generator running at
    500MB/sec.

    ...

    The Linux people happen not to like those, but, they exist anyway,
    chugging away.

    Because it?s difficult to see how you can trust them.

    Not without thoroughly mashing them through something like this <https://en.wikipedia.org/wiki/Fortuna_(PRNG)>, anyway.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Paul@3:633/10 to All on Fri Dec 26 01:52:15 2025
    On Fri, 12/26/2025 12:42 AM, Lawrence D?Oliveiro wrote:
    On Thu, 25 Dec 2025 23:25:39 -0500, Paul wrote:

    My CPU happens to have a random number generator running at
    500MB/sec.

    ...

    The Linux people happen not to like those, but, they exist anyway,
    chugging away.

    Because it?s difficult to see how you can trust them.

    Not without thoroughly mashing them through something like this <https://en.wikipedia.org/wiki/Fortuna_(PRNG)>, anyway.


    The claim was, that x86 processors didn't have anything.

    They do have something.

    And it has a source of entropy to do re-seed with.

    Not all the Intel ones have the same arch.

    And they're good enough for a "guess-a-number" game in C.

    If you want a conventional "repeatable-upon-request" sequence,
    then a regular PRNG can be used, and you can just make your
    own if you're bored. As long as it doesn't have zeros sensitivity
    (gets "stuck" in the all zeros state).

    A general rule of thumb, is "quality random numbers take time".
    Based on that, the 500MB/sec output I got from my processor,
    I wouldn't be using that for any serious purpose, without
    further study. I just thought it amusing that such a firehose
    existed on the processor, not that such a rate was safe to use.

    Paul




    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Fri Dec 26 07:56:58 2025
    On Fri, 26 Dec 2025 01:52:15 -0500, Paul wrote:

    On Fri, 12/26/2025 12:42 AM, Lawrence D?Oliveiro wrote:

    On Thu, 25 Dec 2025 23:25:39 -0500, Paul wrote:

    My CPU happens to have a random number generator running at
    500MB/sec.

    ...

    The Linux people happen not to like those, but, they exist anyway,
    chugging away.

    Because it?s difficult to see how you can trust them.

    Not without thoroughly mashing them through something like this
    <https://en.wikipedia.org/wiki/Fortuna_(PRNG)>, anyway.

    The claim was, that x86 processors didn't have anything.

    Not what I was responding to. I said nothing about such a claim, either
    way.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Fri Dec 26 08:08:57 2025
    On Thu, 25 Dec 2025 09:10:16 +0100, Janis Papanagnou wrote:

    How did you implement the colored squares? Using characters from
    an "extended" character set and ANSI controls for the colors?

    Yes exactly. I do have a fallback for older terminal emulators:

    void set_utf(void) {
    const char *env = getenv("MASTERMIND");
    UTF = (env && env[0] == '0' && env[1] == '\0') ? 0 : 1;
    }

    if (UTF) static const char *glyph_utf[] = { ... }; // unicode
    else static const char *glyph_xyz[] = { ... }; // ascii

    Check out <https://jrgraphix.net/r/Unicode/2500-257F>

    Lots of useful glyphs there. Assuming the reader's software can
    render these, here's some tree glyphs that provide a smart visual representation of branching hierarchical taxonomies. For instance,
    if the next number in the sequence is larger than its mother node,
    attach to that node, else climb the tree till the next mother node
    & seek attachment there...

    195, 510, 855, 1380, 25, 170

    195
    ÃÄ510
    ³ ÀÄ855
    ³ ÀÄ1380
    ÀÄ25
    ÀÄ170

    Very handy for sorting things given certain contexts.

    Looks nice.[*]

    Thanks

    Only that it looks as if one gets too much information (compared
    to the original Mastermind)! IMO and AFAIK one should *not* get
    the _exact_ place of a wrong digit indicated. (The green hints
    should all be left aligned, and the [optional] red ones all right
    aligned, and the blue ones in between.)

    Yes it's just a variant. I still need to merge/harmonize
    this new way (popular with some folks here in the U.S.)
    with the older 'proper' way you & I learned it.

    [*] 18 months ago I've written an optically less appealing command
    line variant to refresh my Algol 68 skills. Though I just notice
    that I hadn't finish it yet; I've only implemented playing modes 1
    and 2. ;-)


    Enter a value for #places: 4
    Enter a value for #colors: 6

    Available playing modes:
    1 - computer selects, human guesses
    2 - human selects, computer guesses
    3 - alternate select/guess roles per game
    4 - human selects, human guesses
    5 - computer selects, computer guesses
    0 - leave the game
    Choose the playing mode (1-5): 1

    A secret color combination to guess has been chosen.
    You have to guess it.

    Enter a color combination: 1122
    Turn 1: 1 1 2 2 -
    Enter a color combination: 3345
    Turn 2: 3 3 4 5 - @
    Enter a color combination: 6444
    Turn 3: 6 4 4 4 - @@
    Enter a color combination: 6646
    Turn 4: 6 6 4 6 - @@@@

    You guessed it!

    D*mn! Chuckle, that's 'god-mode' Janis, I could probably
    play 5 colors at say 6, no make that 7 chances, but after
    that, my brain begins to melt. =)

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Fri Dec 26 08:13:38 2025
    On Thu, 25 Dec 2025 03:17:41 -0000 (UTC), Lawrence D?Oliveiro wrote:

    Nothing specific to either cmd.exe or PowerShell, but the Windows
    command line is fundamentally broken. This is because it derives from
    the CP/M command line model, which in turn was inherited from old-time
    DEC operating systems.

    On these DEC systems, the command line was a simple string buffer. So
    there is this assumption that program invocation is always going to be mediated by some kind of ?shell? program, and the concept of one
    program directly invoking another is either nonexistent, or only
    grudgingly tolerated.

    Contrast this with the Unix approach, where the command line is an
    array of separate string ?words?. There is no ?shell? that occupies a privileged place in the system; any program can directly invoke any
    other, without having to worry about properly escaping any special
    characters that might be (mis)interpreted by some ?shell?.

    Bingo - you nailed it in my mind. To be sure, when in Windows you
    have to play by their rules as Michael S. more or less pointed out.

    Given a choice, I'm seeking stiller waters with the odd exception
    here & there. Its all good =)

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Fri Dec 26 08:23:56 2025
    On Wed, 24 Dec 2025 23:35:55 -0600, BGB wrote:

    While arguably a typical C library "rand()" isn't that strong, if one
    has a number sequence of output random digits, it might still take an impractical amount of time to brute-force search the entire seed space
    for a 64-bit seed.

    That is a great point IMO. After reading this I used Gemini to get a guess
    on the number of permutations for A-Z, its reply was:

    'over 403 quintillion millions' of permatations for A-Z...

    Now if we split that list (assuming each line was randomized 26 characters
    A-Z) & used each line exactly *once*, then destroyed it, we might maintain
    some privacy. At least till quantum stuff is in every day use...

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From BGB@3:633/10 to All on Fri Dec 26 04:48:17 2025
    On 12/26/2025 1:56 AM, Lawrence D?Oliveiro wrote:
    On Fri, 26 Dec 2025 01:52:15 -0500, Paul wrote:

    On Fri, 12/26/2025 12:42 AM, Lawrence D?Oliveiro wrote:

    On Thu, 25 Dec 2025 23:25:39 -0500, Paul wrote:

    My CPU happens to have a random number generator running at
    500MB/sec.

    ...

    The Linux people happen not to like those, but, they exist anyway,
    chugging away.

    Because it?s difficult to see how you can trust them.

    Not without thoroughly mashing them through something like this
    <https://en.wikipedia.org/wiki/Fortuna_(PRNG)>, anyway.

    The claim was, that x86 processors didn't have anything.

    Not what I was responding to. I said nothing about such a claim, either
    way.

    In my case, I was not aware of such a feature having been added, but I
    haven't really been keeping up with every new feature being added to x86
    in recent years.




    Some years ago, I got sorta burned on AVX, as it wasn't until comparably recently that I got a CPU that could actually run it (I tend to build
    PCs with parts that are a few generations behind, to keep cost more reasonable). And, it wasn't until very recently (a few months ago) that
    I got something where using AVX didn't make things actively slower (my
    main PC can run AVX, technically, but doing so performs poorly).

    Much after AVX, I sorta lost motivation to keep up on newer additions to
    the ISA; as often it would be a painfully long time before I would
    actually be able to use it (and I still don't have any PC's with CPUs
    made in this decade).


    Well, and then it has started seeming like in a longer term sense, x86
    may be doomed. Granted, its end has been predicted for a long time,
    though its main threat may be in part the end of Moore's law, which in a mostly steady-state it is likely that performance per area and
    performance per watt will become the dominant factors, and conventional
    x86 processors haven't been great on either front here (combined with a
    longer term timeframe making JIT compilation and eventually abandonment
    the more likely endgame).

    Well, and after Moore's law hits its limit, it may backslide slightly as things settle on whichever process node is most economical in a perf/$
    sense.

    Though, this is unlikely to happen quickly (more likely over a period of decades).

    Well, and then there are more near-term things, like the mess of things
    the whole "AI" thing is creating, and MS repeatedly shooting itself in
    the foot (maybe not enough to dethrone themselves, but they are pushing it).

    ...



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From BGB@3:633/10 to All on Fri Dec 26 14:48:45 2025
    On 12/26/2025 2:23 AM, Michael Sanders wrote:
    On Wed, 24 Dec 2025 23:35:55 -0600, BGB wrote:

    While arguably a typical C library "rand()" isn't that strong, if one
    has a number sequence of output random digits, it might still take an
    impractical amount of time to brute-force search the entire seed space
    for a 64-bit seed.

    That is a great point IMO. After reading this I used Gemini to get a guess
    on the number of permutations for A-Z, its reply was:

    'over 403 quintillion millions' of permatations for A-Z...

    Now if we split that list (assuming each line was randomized 26 characters A-Z) & used each line exactly *once*, then destroyed it, we might maintain some privacy. At least till quantum stuff is in every day use...


    Though, to be fair, 26! is larger than 2^64.


    But, yeah, if you have 64 bits of entropy, while it can be brute forced,
    it wont happen very quickly (and most normal attackers aren't going to
    have a supercomputer or similar on-call to crack it faster).


    Contrast, say for something with a 32 seed, it could be possible that it
    could be brute forced within a few seconds or so.



    But, yeah, one of the main use-cases I had often had for large seed
    state RNGs is things like UUID generation, which requires a reasonably
    good RNG with 256 or preferably 512 bits or more of seed state.


    Intermediate is ASLR, where something like a 64 or 128-bit RNG is mostly
    fine. Well, and the effectiveness of ASLR is reduced by a few practical limitations:
    Can usually only do ASLR on page boundaries,
    losing some bits of entropy;
    May need to cluster ASLR into reused sections of the address space,
    as full randomization results in mostly-empty page table pages.

    Say, for example, you start with 48 bits,
    Cut off 12 to 16 bits for page offset,
    Cut off a few HOBs for userland (say 46b for userland space);
    Maybe quantize high order address into 256 or so buckets.
    ...


    Then one finds that effectively they have around 16 bits or so of
    entropy here.


    Well, say, for ASLR allocation, one might have a process like:
    For N tries, with a counter:
    Generate a random number to select a bucket;
    If index is an unused bucket try again (decrementing counter);
    Generate a random address within this bucket;
    Check if the span of pages is free;
    If so, allocate these pages, call it done.
    Else, retry (decrementing counter).
    For N more tries:
    Generate a full address over the allowed range;
    Check if pages are free;
    If so, allocate pages;
    Set up a bucket for this area of address space;
    Done.
    If we get here, likely the whole address space is full...
    Or the user is trying to allocate vast areas of address space,
    and fragmentation has won the day.

    The first step for allocation is to mark pages as reserved, then return
    the base address for the region. then generally the pages are modified
    to have the desired attributes. Typically (at least in my case) pages
    are not immediately assigned a backing location in the page-file, but
    this may happen lazily.

    On first access, if a read, it may be assigned initially to a read-only
    zero page (no dedicated backing RAM); on first write it is given backing
    RAM (and assigned a location in the pagefile). If something is paged
    out, it may detect if the page is all zeroes and potentially revert it
    back to an initial zeroed-page state (freeing the backing page rather
    than writing it to the pagefile). This can be used to reduce wasting RAM
    and pagefile space on bulk zeroed memory (can often be a significant
    chunk of RAM in some cases).

    Though, by itself has a risk of resulting in a situation where the
    pagefile is over-committed (there is more active memory allocated than available pagefile space should all this RAM actually be used). So,
    better to keep track of this and have allocations fail should the total
    number of committed pages would exceed the size of the pagefile
    regardless of whether or not all the pagefile pages are actually being
    used (well, could go the other way; but then there is a bad situation
    where backing storage runs out in the absence of new memory allocations, potentially resulting in processes crashing at random, rather than
    failing earlier by a "mmap()" call or similar returning NULL).



    Well, as can note my memory map (custom OS on a custom ISA, though I am
    also running RISC-V code on this to some extent) looks sorta like (47:32 only):
    0000: Low 4GB region (system, direct-mapped memory)
    0001..3FFF: Shared / Global Alloc region;
    4000..7FFF: Private Alloc (per-process private VAS)
    8000..BFFF: System Reserved (MMU)
    C000..FFFF: Hardware memory map stuff (NOMMU RAM and MMIO, etc)
    ...

    The idea here being that private address space is only visible to the
    process in question, but stuff in the global space may potentially be
    visible between processes (though is not necessarily accessible to all processes; idea is that an ACL based protection scheme would be used
    here rather than by address-space isolation).

    There is a region for direct-mapped RAM currently located in 0000_40000000..0000_7FFFFFFF; this is generally read-only from userland. Addresses in this range have virtual-address translation, but are mapped
    1:1 with backing RAM pages (so its size is naturally more limited).

    A sort of ASLR is used in the direct-mapping space as well, but less
    effective since the space is smaller. In this case, RNG tries to
    generate starting page addresses directly.

    Well, and, say (low 4GB):
    00xxxxxx: Mostly ROM, some SRAM for IRQs, and read-only niche pages.
    There are ROM pages whose sole purpose is to be all zeroes, etc.
    010xxxxx: RAM area, reserved for kernel stacks and similar
    011xxxxx: RAM area, kernel image starts here.
    ...


    Can note about how programs work in my case:
    The executable parts of an EXE or DLL are separate from the
    user-writable parts;
    Currently, the executable parts are held in direct-mapped memory rather
    than pagefile backed memory (stuff tended to break if these parts could
    be paged out);
    Things like writable memory (data/bss) and heap, are instead allocated
    in pagefile backed memory.


    Generally, the mappings for the EXEs and DLLs are shared between all
    instances of that binary; with data/bss being per-process (accessed via
    the GP/GBR register, *1).

    Well, except ELF PIE binaries, which need to load a new instance each
    time (so waste RAM, more so with the large amounts of clutter GCC leaves
    in the ELF binaries; where things like symbol tables, and worse, DWARF metadata, can be well in access of any actual code in the binary...).

    *1: In my ABI, GP/GBR always holds a pointer to a table which can be
    used by each image to reload its own data area. EXE's always have an
    index of 0 (since there is only ever one EXE per process instance) and
    each DLL is assigned an index number into the table at load time. By
    going through a ritual, functions can get back to their own table. This process is handled callee side, with functions deciding whether or not
    to do so based on criteria (may be potentially called from outside the
    current PE image, and which may access global variables and/or are
    non-leaf functions, etc).

    By default, my compiler tends to shuffle functions and variables around
    based on random numbers (different for each run of the compiler), though
    the effectiveness is reduced by also needing to try to pull related
    functions closer together, and commonly used variables closer to the
    start of the data section (commonly used globals may go into ".data"
    rather than ".bss" even if technically uninitialized; mostly to make it
    more efficient to access them with smaller displacements).

    But, the general goal here is to try to make it harder for any potential buffer-overflow shell-code to do anything useful (assuming it gets past
    the stack canaries, etc, which also use RNG values). There is a risk
    here that if a binary goes for long enough without being recompiled,
    that it could become more vulnerable (in this case, even in otherwise vulnerable code, there would be some security merit to periodic
    recompilation to shuffle around functions and generate new canary values).

    Though, had experimented with another mechanism to reduce the
    probability of shell-code, namely tagging function-pointers and
    link-register values to limit tampering (all the link registers and
    function pointers could be tagged with a magic number that is unique for
    each run).

    This sorta works, but is weakened if needing to deal with generic RISC-V
    code (use of AUIPC+ADDI is incompatible with this approach; and kinda
    defeated if any shell-code could simply use an untagged pointer; though
    a partial workaround is to trap if "JALR X0, 0(X1)" is used with an incorrectly tagged pointer, as this almost invariably means a
    return-address had been stomped; but would still be weak against
    stomping other values).

    Though one possibility could be to increase the strength of
    stack-canaries by flagging them with relocs, allowing the program loader
    to itself re-jitter the canary values without needing a recompile.


    Well, and GCC compiled code has an additional weakness here in that GCC doesn't normally use stack canaries (they seemingly do very little to
    give binaries any kind of resistance against buffer overflow exploits).


    Granted, none of these is an infallible defense, but if one piles on
    enough stuff, it becomes statistically improbable that any shell-code
    could make it past the defenses (any would-be attacker would likely need
    to know the RNG states at the moment a given process was created in
    order to craft an effective attack).

    Well, unless the attack is basically just to crash the process, this
    doesn't require knowing any of the RNG state.


    Well, one thing that isn't done in my case is to jitter the kernel load address, which could probably also make sense (always loading the kernel
    at the same address being itself a potential attack surface; say, if exploiting a kernel-mode buffer overflow or a breakdown in the memory-protection schemes, etc).


    ...



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From BGB@3:633/10 to All on Fri Dec 26 15:12:48 2025
    On 12/26/2025 2:48 PM, BGB wrote:


    Though one possibility could be to increase the strength of stack-
    canaries by flagging them with relocs, allowing the program loader to
    itself re-jitter the canary values without needing a recompile.


    Well, and GCC compiled code has an additional weakness here in that GCC doesn't normally use stack canaries (they seemingly do very little to
    give binaries any kind of resistance against buffer overflow exploits).


    Groan... Self clarification:
    GCC does have an option to enable stack canaries, but it is opt-in
    rather than enabled by default (say, in MSVC one would need to opt-out instead).

    What I wrote could be misinterpreted, I meant GCC's default settings do
    little in the case of protecting against stack overflows (and was *not* implying that canaries were ineffective).

    But, alas, now people will probably be misinterpreting what I wrote and
    now I will need to deal with this, grr...




    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Sun Dec 28 05:51:11 2025
    On Thu, 25 Dec 2025 04:30:45 -0000 (UTC), Michael Sanders wrote:

    ... built in help too.

    Help is complete (err... except for man page)

    <https://drive.google.com/file/d/1m_9oOQX4KRrMVYpPobjfTGkC2UFSoiKk/view?usp=sharing>

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Tue Dec 30 06:07:07 2025
    On Fri, 26 Dec 2025 08:08:57 -0000 (UTC), Michael Sanders wrote:

    Yes it's just a variant. I still need to merge/harmonize
    this new way (popular with some folks here in the U.S.)
    with the older 'proper' way you & I learned it.

    Merged & harmonized...

    int main(int argc, char *argv[]) {

    // if app named 'moo' play bulls & cows else mastermind
    const char *p = strrchr(argv[0], '/');
    p = p ? p + 1 : argv[0];
    MOO = (strcmp(p, "moo") == 0) ? 1 : 0;

    int s = play(get_seed(argc, argv));
    fflush(stdout);
    fprintf(stderr, "%d\n", s);

    return 0;

    }

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Tue Dec 30 06:34:35 2025
    On Fri, 26 Dec 2025 08:08:57 -0000 (UTC), Michael Sanders wrote:

    Check out <https://jrgraphix.net/r/Unicode/2500-257F>

    Lots of useful glyphs there. Assuming the reader's software can
    render these, here's some tree glyphs that provide a smart visual representation of branching hierarchical taxonomies.

    Another possibility is sixel graphics
    <https://www.arewesixelyet.com/>. Seems to be enjoying a mini-revival
    at the moment.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Tue Dec 30 14:05:19 2025
    On Tue, 30 Dec 2025 06:34:35 -0000 (UTC), Lawrence D?Oliveiro wrote:

    Another possibility is sixel graphics
    <https://www.arewesixelyet.com/>. Seems to be enjoying a mini-revival
    at the moment.

    Hey neato-burrito! Many thanks for the pointer Lawrence,
    appreciate that =)

    I was sort of dimly of aware of sixel somehow or another,
    but you've rattled me out of hibernation with this.

    Think I'll to try to cook up something I can play around with.

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Tue Dec 30 18:42:30 2025
    Michael Sanders <porkchop@invalid.foo> writes:
    On Fri, 26 Dec 2025 08:08:57 -0000 (UTC), Michael Sanders wrote:

    Yes it's just a variant. I still need to merge/harmonize
    this new way (popular with some folks here in the U.S.)
    with the older 'proper' way you & I learned it.

    Merged & harmonized...

    int main(int argc, char *argv[]) {

    // if app named 'moo' play bulls & cows else mastermind
    const char *p = strrchr(argv[0], '/');

    What if 'argv[0]' is NULL (and argc == 0)?


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Wed Dec 31 02:01:55 2025
    On Tue, 30 Dec 2025 18:42:30 GMT, Scott Lurndal wrote:

    What if 'argv[0]' is NULL (and argc == 0)?

    Well, seems we have to make a choice, ISO vs. POSIX:

    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argc?1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    int main(int argc, char *argv[]) {

    // if app named 'moo' play bulls & cows else mastermind
    const char *p = (argc > 0 && argv && argv[0]) ? argv[0] : "";
    const char *s = strrchr(p, '/');
    p = s ? s + 1 : p;
    MOO = (strcmp(p, "moo") == 0) ? 1 : 0;

    ...

    return 0;

    }

    *POSIX.1-2017 (and later)*

    POSIX execve() specification:

    The argument argv is an array of character pointers
    to null-terminated strings.

    The application shall ensure that argv[0] points to a filename
    string that is associated with the process being started.

    int main(int argc, char *argv[]) {

    // if app named 'moo' play bulls & cows else mastermind
    const char *p = strrchr(argv[0], '/');
    p = p ? p + 1 : argv[0];
    MOO = (strcmp(p, "moo") == 0) ? 1 : 0;

    ...

    return 0;

    }


    What say you?

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Wed Dec 31 03:10:52 2025
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:

    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argc?1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldn?t be relying on it.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Wed Dec 31 03:28:03 2025
    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC), Lawrence D?Oliveiro wrote:

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldn?t be relying on it.

    Some windows snippets:

    int main(int argc, char **argv); // subsystem (text-mode)

    int wmain(int argc, wchar_t **argv); // gui

    And to muddy the waters further (what I tend to use)...

    #include <windows.h>
    #include <stdio.h>

    int main(void) {

    LPSTR cmd = GetCommandLineA();

    printf("Raw command line:\n%s\n", cmd);

    return 0;
    }

    app.exe foo "bar baz" 123
    "app.exe" foo "bar baz" 123

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Tue Dec 30 19:35:12 2025
    Michael Sanders <porkchop@invalid.foo> writes:
    On Tue, 30 Dec 2025 18:42:30 GMT, Scott Lurndal wrote:
    What if 'argv[0]' is NULL (and argc == 0)?

    Well, seems we have to make a choice, ISO vs. POSIX:

    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    [...]

    *POSIX.1-2017 (and later)*

    POSIX execve() specification:

    The argument argv is an array of character pointers
    to null-terminated strings.

    The application shall ensure that argv[0] points to a filename
    string that is associated with the process being started.

    [...]

    What say you?

    It happens that I recently spent some time looking into this.

    As you say, POSIX requires argc >= 1, but ISO C only guarantees
    argc >= 0.

    If argc == 0, a program that assumes argv[0] is non-null
    can run into serious problems if that assumption is invalid.
    In particular, a program called "pkexec" would try to traverse
    arguments starting with argv[1], which logically doesn't
    exist if argc==0. Due to the way program arguments are laid
    out in memory, argv[1] is also envp[0]. Frivolity ensued.
    See <https://nvd.nist.gov/vuln/detail/cve-2021-4034>.

    The Linux kernel updated execve to ensure that the invoked program
    has argc>=1. It was patched in early 2022. NetBSD still has this vulnerability.

    Summary: Some systems guarantee that argc>=1 and argv[0] points to
    a valid string, but software that's intended to be portable should
    tolerate argc==0 and argv[0]==NULL.

    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Tue Dec 30 19:37:25 2025
    Lawrence D?Oliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:
    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argc?1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldn?t be relying on it.

    That's not clear. Linux (since 2022) guarantees argc>=1. I don't
    know whether Windows makes a similar guarantee, but it's entirely
    plausible that it could.

    But not all systems guarantee argc>=1, so any portable code shouldn't
    make assumptions beyond what's guaranteed by ISO C.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Wed Dec 31 04:51:11 2025
    On Tue, 30 Dec 2025 19:35:12 -0800, Keith Thompson wrote:

    Summary: Some systems guarantee that argc>=1 and argv[0] points to
    a valid string, but software that's intended to be portable should
    tolerate argc==0 and argv[0]==NULL.

    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.

    Thanks Keith so I'll roll with ISO:

    int main(int argc, char *argv[]) {

    // ISO: if app named 'moo' play bulls & cows else mastermind
    const char *p = (argc > 0 && argv && argv[0]) ? argv[0] : "";
    const char *m = strrchr(p, '/');
    MOO = strcmp(m ? m + 1 : p, "moo") == 0;

    int s = get_seed(argc, argv); // returns default seed if not supplied

    fflush(stdout);
    fprintf(stderr, "%d\n", play(s)); // print seed to stderr

    return 0;
    }

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Wed Dec 31 09:37:08 2025
    On Wed, 31 Dec 2025 03:28:03 -0000 (UTC), Michael Sanders wrote:

    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC), Lawrence D?Oliveiro wrote:

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldn?t be relying on it.

    Some windows snippets:

    Are there any standards for how C argc/argv are supposed to behave on
    Windows?

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Wed Dec 31 15:15:02 2025
    On Tue, 30 Dec 2025 19:35:12 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Michael Sanders <porkchop@invalid.foo> writes:
    On Tue, 30 Dec 2025 18:42:30 GMT, Scott Lurndal wrote:
    What if 'argv[0]' is NULL (and argc == 0)?

    Well, seems we have to make a choice, ISO vs. POSIX:

    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    [...]

    *POSIX.1-2017 (and later)*

    POSIX execve() specification:

    The argument argv is an array of character pointers
    to null-terminated strings.

    The application shall ensure that argv[0] points to a filename
    string that is associated with the process being started.

    [...]

    What say you?

    It happens that I recently spent some time looking into this.

    As you say, POSIX requires argc >= 1, but ISO C only guarantees
    argc >= 0.

    If argc == 0, a program that assumes argv[0] is non-null
    can run into serious problems if that assumption is invalid.
    In particular, a program called "pkexec" would try to traverse
    arguments starting with argv[1], which logically doesn't
    exist if argc==0. Due to the way program arguments are laid
    out in memory, argv[1] is also envp[0]. Frivolity ensued.
    See <https://nvd.nist.gov/vuln/detail/cve-2021-4034>.

    The Linux kernel updated execve to ensure that the invoked program
    has argc>=1. It was patched in early 2022. NetBSD still has this vulnerability.

    Summary: Some systems guarantee that argc>=1 and argv[0] points to
    a valid string, but software that's intended to be portable should
    tolerate argc==0 and argv[0]==NULL.

    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.


    If you are interested in behavior on non-POSIX systems, primarily
    Windows, but possibly others as well (e.g. VMS) then using exec() in
    caller sounds like a bad idea. It just not how these systems work and
    not how people write programs on them.
    Even when exec() *appears* to works in some environments (like
    msys2) it likely emulated by spawn() followed by exit().

    I'd implement caller with spawn(). I suppose that even on POSIX it is
    more idiomatic.


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Wed Dec 31 15:29:09 2025
    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:

    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:

    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0] through argv[argc?1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldn?t be relying on it.

    How did you come to this conclusion?
    Keith's test appears to show the opposite - he was not able to convince
    the Windows system to call application with empty argv list.
    Of course, he tried only one way out of many, but knowing how native
    Windows system call works, it appears extremely likely that on Windows
    argc < 1 is impossible.

    https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-pr ocessthreadsapi-createprocessa


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Wed Dec 31 17:24:29 2025
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    Lawrence D?Oliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:
    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argc?1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldn?t be relying on it.

    That's not clear. Linux (since 2022) guarantees argc>=1.

    Does it? That seems to be up to the shell, since the exec()
    manual pages on the latest Fedora Core release don't indicate
    that argv[0] must be initialized or that argc be greater than zero.



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Paul@3:633/10 to All on Wed Dec 31 12:30:02 2025
    On Tue, 12/30/2025 10:37 PM, Keith Thompson wrote:
    Lawrence D?Oliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:
    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argc?1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldn?t be relying on it.

    That's not clear. Linux (since 2022) guarantees argc>=1. I don't
    know whether Windows makes a similar guarantee, but it's entirely
    plausible that it could.

    But not all systems guarantee argc>=1, so any portable code shouldn't
    make assumptions beyond what's guaranteed by ISO C.


    The Windows guarantee is with MSVC, according to this answer.

    ********** CoPilot Question **********

    Does the Windows operating system guarantee that argc >= 1 for an EXE program ?

    argv[0] usually contains the program absolute path.

    ********** CoPilot Answer ************

    Here?s the clear, standards?grounded, Windows?specific answer you?re looking for ? with citations from the search results.

    # **Short Answer**
    **Yes. On Windows, when using the Microsoft C runtime (MSVCRT), argc
    is always guaranteed to be ò 1**, and ** argv[0] always exists**.
    However, **Windows itself does *not* guarantee what argv[0] contains** -
    only that the C runtime *treats* it as the program name.

    ---

    # **What Windows Guarantees vs. What the C Runtime Guarantees**

    ### **Microsoft C Runtime Guarantee**
    Microsoft?s documentation explicitly states:

    - *?Since the program name is considered an argument, the value of argc is at least one.?*

    So if your EXE is built with MSVC and uses the standard main or wmain , you can rely on:

    - argc >= 1
    - argv[0] is a valid null?terminated string
    - argv[argc] == NULL

    ### **MSVC Startup Code Behavior**
    MSVC startup code parses the command line and
    **always inserts the program name as argv[0] ** (quoted if needed).

    ---

    # **What Windows *Does Not* Guarantee**
    Windows itself does **not** enforce that the first token of the command line
    is the executable name. This is a convention, not a rule:

    - * By convention the module name ... is the first argument ... Note that this is
    only a convention and is not enforced in any way. *

    This means:

    - A caller can technically pass *anything* as the first token.
    - A process created via
  • From bart@3:633/10 to All on Wed Dec 31 18:42:45 2025
    On 31/12/2025 17:30, Paul wrote:
    On Tue, 12/30/2025 10:37 PM, Keith Thompson wrote:
    Lawrence D?Oliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:
    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members argv[0]
    through argv[argc?1] inclusive shall contain pointers to strings
    which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldn?t be relying on it.

    That's not clear. Linux (since 2022) guarantees argc>=1. I don't
    know whether Windows makes a similar guarantee, but it's entirely
    plausible that it could.

    But not all systems guarantee argc>=1, so any portable code shouldn't
    make assumptions beyond what's guaranteed by ISO C.


    The Windows guarantee is with MSVC, according to this answer.

    ********** CoPilot Question **********

    Does the Windows operating system guarantee that argc >= 1 for an EXE program ?

    argv[0] usually contains the program absolute path.

    ********** CoPilot Answer ************

    Here?s the clear, standards?grounded, Windows?specific answer you?re looking for ? with citations from the search results.

    # **Short Answer**
    **Yes. On Windows, when using the Microsoft C runtime (MSVCRT), argc
    is always guaranteed to be ò 1**, and ** argv[0] always exists**. However, **Windows itself does *not* guarantee what argv[0] contains** -
    only that the C runtime *treats* it as the program name.

    ---

    # **What Windows Guarantees vs. What the C Runtime Guarantees**

    ### **Microsoft C Runtime Guarantee**
    Microsoft?s documentation explicitly states:

    - *?Since the program name is considered an argument, the value of argc is at least one.?*

    So if your EXE is built with MSVC and uses the standard main or wmain , you can rely on:

    - argc >= 1
    - argv[0] is a valid null?terminated string
    - argv[argc] == NULL

    ### **MSVC Startup Code Behavior**
    MSVC startup code parses the command line and
    **always inserts the program name as argv[0] ** (quoted if needed).

    ---

    # **What Windows *Does Not* Guarantee**
    Windows itself does **not** enforce that the first token of the command line is the executable name. This is a convention, not a rule:

    - * By convention the module name ... is the first argument ... Note that this is
    only a convention and is not enforced in any way. *

    This means:

    - A caller can technically pass *anything* as the first token.
    - A process created via CreateProcess can have an arbitrary lpCommandLine .
    - argv[0] may not be the absolute path.
    - It may not even be a valid path at all.

    This is why portable code should not assume argv[0] is the executable path.

    ---

    # **Cross?Platform Note**
    The C standard does **not** guarantee that argv[0] contains the program name ? only that it exists.
    This is echoed in the search results:

    - * In short: No... this is more of a common convention than a strict rule. *

    ---

    # **Final Summary**
    | Question | Answer |
    |------------------------------------------------------|------------------------------------------|
    | **Does Windows guarantee argc >= 1 ?** | **Yes ? if using MSVC?s C runtime.** |
    | **Is argv[0] guaranteed to exist?** | **Yes ? with MSVC.** |
    | **Is argv[0] guaranteed to be the absolute path?** | **No.** |
    | **Does Windows enforce any of this?** | **No ? it?s the C runtime, not the OS.** |

    If you need the *actual* executable path, use:

    - GetModuleFileName(NULL, ...) (Windows API)

    ---

    https://learn.microsoft.com/en-us/cpp/c-language/argument-description?view=msvc-170&utm_source=copilot.com

    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments?view=msvc-170&utm_source=copilot.com

    https://github-wiki-see.page/m/johnstevenson/winbox-args/wiki/How-Windows-parses-the-command-line?utm_source=copilot.com

    https://www.codegenes.net/blog/is-argv-0-name-of-executable-an-accepted-standard-or-just-a-common-convention/?utm_source=copilot.com

    When argv[0] Isn?t the Executable Name

    4.1 Invocation via exec Functions
    4.2 Symbolic Links
    4.3 Shell Scripts and Aliases
    4.4 Debuggers, Emulators, and Special Environments

    ********** End CoPilot Answer ************

    So is that a Yes or No?

    My C compiler calls __getmainargs() in msvcrt.dll to get argc/argv.

    __getmainargs() is also imported by programs compiled with Tiny C, and
    also with gcc 14.x from winlibs.com. (I assume it is actually called for
    the same purpose.)

    The specs for __getmainargs() say that the returned argc value is always
    = 1.

    (I doubt whether msvcrt.dll, which is present because so many programs
    rely on it, is what is used by MSVC-compiled appls, but you'd have to
    look inside such an app to check. EXEs inside \windows\system tend to
    import DLLs with names like "api-ms-win...".)

    In any case, it is easy enough to do a check on argc's value in your applications. (And on Windows, if it is 0 and you really need the path,
    you can get it with GetModuleFileNameA().)

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Paul@3:633/10 to All on Wed Dec 31 15:07:02 2025
    On Wed, 12/31/2025 1:42 PM, bart wrote:

    So is that a Yes or No?

    My C compiler calls __getmainargs() in msvcrt.dll to get argc/argv.

    __getmainargs() is also imported by programs compiled with Tiny C, and also with gcc 14.x from winlibs.com. (I assume it is actually called for the same purpose.)

    The specs for __getmainargs() say that the returned argc value is always >= 1.

    (I doubt whether msvcrt.dll, which is present because so many programs rely on it, is what is used by MSVC-compiled appls, but you'd have to look inside such an app to check. EXEs inside \windows\system tend to import DLLs with names like "api-ms-win...".)

    In any case, it is easy enough to do a check on argc's value in your applications. (And on Windows, if it is 0 and you really need the path, you can get it with GetModuleFileNameA().)

    The answer says "with MSVC". And your usage of msvcrt.dll likely applies
    to that statement.

    If you wrote your own version of a runtime msvcrt.dll then the answer could be no or maybe.

    I think if you're sufficiently clever, you can break that. But for
    the most part, lots of regular/lazy programming efforts will be guaranteeing
    a good result. I've used the argv[0] to determine the role of a program (encoder or decoder mode), and I'm just a very bad hobby programmer.

    Windows is pretty careless with metadata. In twenty years of watching Windows, I watched a program lose its name, and also one which had no parent. I thought when a program lost a parent that System() would own it. But when I checked, not even System was the parent. The parent was nothing. And Windows seemingly does not kill items malformed in that way. (The root cause may have been a RAM error, but I cannot be sure.)

    If you look in Task Manager in W10/W11, you cannot see Memory Compressor.
    If you use tasklist /V in a Terminal, then you can see it. That is just so you can contrast that with the behavior of Task Manager. And if you use Sysinternals (bought by Microsoft) Process Explorer utility, if you check Properties on Memory Compressor (even as administrator), no metadata is visible and the status says something about a necessary device is not responding.
    As if someone launched that in a particular way so it would be orphaned on purpose.

    But this is Windows for you. That's how you do things there.

    Paul


    Paul

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Wed Dec 31 22:18:30 2025
    On Wed, 31 Dec 2025 18:42:45 +0000
    bart <bc@freeuk.com> wrote:

    On 31/12/2025 17:30, Paul wrote:
    On Tue, 12/30/2025 10:37 PM, Keith Thompson wrote:
    Lawrence D?Oliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 02:01:55 -0000 (UTC), Michael Sanders wrote:
    *ISO C (C17 / C23)*:

    C17, 5.1.2.2.1 "Program startup"

    The value of argc shall be nonnegative.

    argv[argc] shall be a null pointer.

    If the value of argc is greater than zero, the array members
    argv[0] through argv[argc?1] inclusive shall contain pointer
    s to
    strings which are given implementation-defined values.

    ...

    What say you?

    Clearly on Windows, there are no guarantees about argc contains,
    so you shouldn?t be relying on it.

    That's not clear. Linux (since 2022) guarantees argc>=1. I don't
    know whether Windows makes a similar guarantee, but it's entirely
    plausible that it could.

    But not all systems guarantee argc>=1, so any portable code
    shouldn't make assumptions beyond what's guaranteed by ISO C.


    The Windows guarantee is with MSVC, according to this answer.

    ********** CoPilot Question **********

    Does the Windows operating system guarantee that argc >= 1 for an
    EXE program ?

    argv[0] usually contains the program absolute path.

    ********** CoPilot Answer ************

    Here?s the clear, standards?grounded, Windows?s
    pecific answer
    you?re looking for ? with citations from the search res
    ults.

    # **Short Answer**
    **Yes. On Windows, when using the Microsoft C runtime (MSVCRT), argc
    is always guaranteed to be ò 1**, and ** argv[0] always
    exists**. However, **Windows itself does *not* guarantee what
    argv[0] contains** - only that the C runtime *treats* it as the
    program name.

    ---

    # **What Windows Guarantees vs. What the C Runtime Guarantees**

    ### **Microsoft C Runtime Guarantee**
    Microsoft?s documentation explicitly states:

    - *?Since the program name is considered an argument, the value
    of
    argc is at least one.?*

    So if your EXE is built with MSVC and uses the standard main or
    wmain , you can rely on:

    - argc >= 1
    - argv[0] is a valid null?terminated string
    - argv[argc] == NULL

    ### **MSVC Startup Code Behavior**
    MSVC startup code parses the command line and
    **always inserts the program name as argv[0] ** (quoted if
    needed).

    ---

    # **What Windows *Does Not* Guarantee**
    Windows itself does **not** enforce that the first token of the
    command line is the executable name. This is a convention, not a
    rule:

    - * By convention the module name ... is the first argument ...
    Note that this is only a convention and is not enforced in any way.
    *

    This means:

    - A caller can technically pass *anything* as the first token.
    - A process created via CreateProcess can have an arbitrary
    lpCommandLine .
    - argv[0] may not be the absolute path.
    - It may not even be a valid path at all.

    This is why portable code should not assume argv[0] is the
    executable path.

    ---

    # **Cross?Platform Note**
    The C standard does **not** guarantee that argv[0] contains the
    program name ? only that it exists. This is echoed in the search
    results:

    - * In short: No... this is more of a common convention than a
    strict rule. *

    ---

    # **Final Summary**
    | Question | Answer
    | |------------------------------------------------------|---------------
    ---------------------------|
    | **Does Windows guarantee argc >= 1 ?** | **Yes ?
    ? if
    using MSVC?s C runtime.** | | **Is argv[0] guaranteed to
    exist?** | **Yes ? with MSVC.**
    | | **Is argv[0] guaranteed to be the absolute path?** | **No.**
    | | **Does Windows enforce any of
    this?** | **No ? it?s the C runtime, not
    the OS.** |

    If you need the *actual* executable path, use:

    - GetModuleFileName(NULL, ...) (Windows API)

    ---

    https://learn.microsoft.com/en-us/cpp/c-language/argument-description?v
    iew=msvc-170&utm_source=copilot.com

    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line
    -arguments?view=msvc-170&utm_source=copilot.com

    https://github-wiki-see.page/m/johnstevenson/winbox-args/wiki/How-Windo
    ws-parses-the-command-line?utm_source=copilot.com

    https://www.codegenes.net/blog/is-argv-0-name-of-executable-an-accepted
    -standard-or-just-a-common-convention/?utm_source=copilot.com

    When argv[0] Isn?t the Executable Name

    4.1 Invocation via exec Functions
    4.2 Symbolic Links
    4.3 Shell Scripts and Aliases
    4.4 Debuggers, Emulators, and Special Environments

    ********** End CoPilot Answer ************

    So is that a Yes or No?

    My C compiler calls __getmainargs() in msvcrt.dll to get argc/argv.

    __getmainargs() is also imported by programs compiled with Tiny C,
    and also with gcc 14.x from winlibs.com. (I assume it is actually
    called for the same purpose.)

    The specs for __getmainargs() say that the returned argc value is
    always
    = 1.

    (I doubt whether msvcrt.dll, which is present because so many
    programs rely on it, is what is used by MSVC-compiled appls, but
    you'd have to look inside such an app to check. EXEs inside
    \windows\system tend to import DLLs with names like "api-ms-win...".)

    In any case, it is easy enough to do a check on argc's value in your applications. (And on Windows, if it is 0 and you really need the
    path, you can get it with GetModuleFileNameA().)


    I experimented a little with CreateProcess() in caller (parent) and GetCommandLine() in callee (child). It seems that [under Windows] it is impossible to pass empty command line to child process.
    It makes no difference whether lpCommandLine argument of CreateProcess()
    call is NULL or "". In both cases the OS fills command line of the child
    with copy of lpApplicationName parameter.
    What *is* possible is passing of command line parameter that contains
    one or several white spaces. How compiler/start up code interprets such
    command line depends on specific compiler/start up code.
    If, like in your case, start up code is borrowed from Microsoft, then
    such command line is interpreted as (argc = 1; argv[0] = ""; argv[1] =
    NULL). I would guess that any start up code that is based on CommandLineToArgvW() syscall will deliver the same result.
    Of course, there are other possible choices as well. As mentioned up
    thread, as far as C Standard is concerned, argc=0 is also legal. But
    not using Microsoft's utilities sounds like plenty of extra work for non-obvious gain.


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Wed Dec 31 20:51:41 2025
    On Wed, 31 Dec 2025 15:15:02 +0200, Michael S wrote:

    ... using exec() in caller sounds like a bad idea. It just not how
    these systems work and not how people write programs on them.

    I don?t think I?ve used exec() on its own without fork() much, but it
    is quite common to do fork() without exec(). That?s another thing that
    I suspect non-*nix systems don?t handle very well.

    I'd implement caller with spawn(). I suppose that even on POSIX it
    is more idiomatic.

    Do you mean posix_spawn(3)?
    <https://manpages.debian.org/posix_spawn(3)>

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Wed Dec 31 20:52:59 2025
    On Wed, 31 Dec 2025 15:29:09 +0200, Michael S wrote:

    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:

    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldn?t be relying on it.

    How did you come to this conclusion?

    The fact that the C spec says so. Is there any standard on Windows for
    how different C compilers are supposed to handle argc/argv?

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Wed Dec 31 20:55:59 2025
    On Wed, 31 Dec 2025 18:42:45 +0000, bart wrote:

    In any case, it is easy enough to do a check on argc's value in your applications. (And on Windows, if it is 0 and you really need the
    path, you can get it with GetModuleFileNameA().)

    Remember that, on *nix systems, the contents of argv are arbitrary and caller-specified. And none of them need bear any relation to the
    actual filename of the invoked executable.

    In fact, it is quite common for utilities to behave differently based
    on the name, as passed in argv[0], by which they are invoked.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From bart@3:633/10 to All on Wed Dec 31 22:57:55 2025
    On 31/12/2025 20:55, Lawrence D?Oliveiro wrote:
    On Wed, 31 Dec 2025 18:42:45 +0000, bart wrote:

    In any case, it is easy enough to do a check on argc's value in your
    applications. (And on Windows, if it is 0 and you really need the
    path, you can get it with GetModuleFileNameA().)

    Remember that, on *nix systems, the contents of argv are arbitrary and caller-specified. And none of them need bear any relation to the
    actual filename of the invoked executable.

    In fact, it is quite common for utilities to behave differently based
    on the name, as passed in argv[0], by which they are invoked.

    Yes, I do that all the time (especially from my other languages that
    also make use of msvcrt.dll).

    But, there is a difference between argv[0] and GetModuleFileName().

    The latter returns the full path of the executable (which also then
    allows you to pick up associated files in the same folder).

    argv[0] merely returns what was typed on the command line to invoke the application.

    So if someone types:

    C:\abc> prog

    it may run a prog.exe found in, say, c:\programs\myapp, and return the
    full path as "c:\programs\myapp\prog.exe".

    args[0] will give you only "prog"; good luck with that!

    I found out a few years ago that this useful funcion doesn't exist on Unix-like systems. You have to do it a more complicated way that may or
    may not work.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Wed Dec 31 15:00:24 2025
    Michael S <already5chosen@yahoo.com> writes:
    On Tue, 30 Dec 2025 19:35:12 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    [...]
    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.

    If you are interested in behavior on non-POSIX systems, primarily
    Windows, but possibly others as well (e.g. VMS) then using exec() in
    caller sounds like a bad idea. It just not how these systems work and
    not how people write programs on them.
    Even when exec() *appears* to works in some environments (like
    msys2) it likely emulated by spawn() followed by exit().

    I'd implement caller with spawn(). I suppose that even on POSIX it is
    more idiomatic.

    If I were going to look into the behavior on Windows, I'd probably
    want to use Windows native features. (I tried my test on Cygwin,
    and the callee wasn't invoked.)

    Apparently the Windows way to invoke a program is CreateProcessA().
    But it takes the command line as a single string. There might not
    be a Windows-native way to exercise the kind of control over argc
    and argv provided by POSIX execve().

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Wed Dec 31 15:03:34 2025
    Michael S <already5chosen@yahoo.com> writes:
    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:
    [...]
    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldn?t be relying on it.

    How did you come to this conclusion?
    Keith's test appears to show the opposite - he was not able to convince
    the Windows system to call application with empty argv list.
    Of course, he tried only one way out of many, but knowing how native
    Windows system call works, it appears extremely likely that on Windows
    argc < 1 is impossible.

    To be clear, my test didn't show anything about Windows. I ran the test
    under Cygwin, not under native Windows.

    [...]

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Wed Dec 31 15:14:27 2025
    Lawrence D?Oliveiro <ldo@nz.invalid> writes:
    On Wed, 31 Dec 2025 15:29:09 +0200, Michael S wrote:
    On Wed, 31 Dec 2025 03:10:52 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:
    Clearly on Windows, there are no guarantees about argc contains, so
    you shouldn?t be relying on it.

    How did you come to this conclusion?

    The fact that the C spec says so.

    You may well be correct, but I don't know how you reached that
    conclusion.

    Older Linux kernels make it possible to invoke a program with argc==0
    and argv[0]==NULL. Newer Linux kernels have a modified execve() that guarantees argc==1 and argv[0]!=NULL. NetBSD still permits argc==0.

    So some Unix-like systems (try to) guarantee argc>0, and some do not.

    It's entirely possible that Windows goes beyond the ISO C
    requirements and explicitly or implicitly guarantees argc>0.
    It's also entirely possible that it doesn't. Do you have any
    concrete information one way or the other

    Is there any standard on Windows for
    how different C compilers are supposed to handle argc/argv?

    That's a good question, and I don't know the answer.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Wed Dec 31 15:17:22 2025
    scott@slp53.sl.home (Scott Lurndal) writes:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    [...]
    That's not clear. Linux (since 2022) guarantees argc>=1.

    Does it? That seems to be up to the shell, since the exec()
    manual pages on the latest Fedora Core release don't indicate
    that argv[0] must be initialized or that argc be greater than zero.

    As I understand it, all the exec() functions go through the execve()
    system call. In 2022, the Linux kernel execve() implementation was
    modified to guarantee that argc>0 and argv[0]!=NULL in the invoked
    program. A shell presumably can't bypass that.

    commit dcd46d897adb70d63e025f175a00a89797d31a43
    Author: Kees Cook <kees@kernel.org>
    Date: 2022-01-31 16:09:47 -0800

    exec: Force single empty string when argv is empty

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Wed Dec 31 15:29:15 2025
    Paul <nospam@needed.invalid> writes:
    [...]
    # **Cross?Platform Note**
    The C standard does **not** guarantee that argv[0] contains the
    program name ? only that it exists.
    [...]

    That isn't quite correct, or is at least misleading. ISO C guarantees
    that argv[0] exists, but not that it points to a string. On some
    systems, it can contain be a null pointer.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From highcrew@3:633/10 to All on Thu Jan 1 00:31:32 2026
    Hi,

    On 1/1/26 12:29 AM, Keith Thompson wrote:
    Paul <nospam@needed.invalid> writes:
    That isn't quite correct, or is at least misleading. ISO C guarantees
    that argv[0] exists, but not that it points to a string. On some
    systems, it can contain be a null pointer.

    I heard of this before.

    Is it just theoretical, or do we have actual systems where
    argv[0]==NULL? I never saw it happen in any modern operating system.

    --
    High Crew

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Thu Jan 1 01:45:48 2026
    On Wed, 31 Dec 2025 15:00:24 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:
    On Tue, 30 Dec 2025 19:35:12 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    [...]
    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.

    If you are interested in behavior on non-POSIX systems, primarily
    Windows, but possibly others as well (e.g. VMS) then using exec()
    in caller sounds like a bad idea. It just not how these systems
    work and not how people write programs on them.
    Even when exec() *appears* to works in some environments (like
    msys2) it likely emulated by spawn() followed by exit().

    I'd implement caller with spawn(). I suppose that even on POSIX it
    is more idiomatic.

    If I were going to look into the behavior on Windows, I'd probably
    want to use Windows native features. (I tried my test on Cygwin,
    and the callee wasn't invoked.)


    That's likely because under Windows callee is named callee.exe.
    I didn't try on cygwin, but that was the reason of failure under msys2.
    Also, I am not sure if slash in the name is allowed. May be, backslash
    is required.

    Apparently the Windows way to invoke a program is CreateProcessA().
    But it takes the command line as a single string. There might not
    be a Windows-native way to exercise the kind of control over argc
    and argv provided by POSIX execve().


    Yes, some tricky combinations easily possible under POSIX are hard to
    achieve under Windows. The result could depend on exact implementation
    of parsing of command line in C start up code. May be, some combinations
    are even impossible, although right now I can not think out an
    example.

    But that was not my point. My point was that analog of exec() family
    does not exist in Windows (or VMS). Even if it appears to exist that
    just means that your environment somehow fooling you. The PID of child
    is not the same as parent's.
    So, if you want cross-platform test, it makes more sense to use spawn()
    family. It does not look like your test will be any more complicated
    with spawn() and as a free benefit you can print some info of
    interest in the parent process after after spawn() call.

    However I was wrong in my suggestion. I didn't realize that spawn
    functions under Windows and their POSIX equivalents have different names
    and different order of arguments.
    So, I take my suggestion back.







    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Wed Dec 31 16:00:45 2025
    bart <bc@freeuk.com> writes:
    [...]
    argv[0] merely returns what was typed on the command line to invoke the application.

    So if someone types:

    C:\abc> prog

    it may run a prog.exe found in, say, c:\programs\myapp, and return the full path as
    "c:\programs\myapp\prog.exe".

    args[0] will give you only "prog"; good luck with that!
    [...]

    That's typically how it works, but it's not guaranteed.

    If a program is invoked from a shell on a Unix-like system, the
    shell will use something like fork() and one of the exec() functions,
    and will typically (perhaps always?) arrange for the new process's
    argv[0] to point to a copy of the program name given on the shell
    command line. (Programs that behave differently depending on the
    string pointed to by argv[0] are typically invoked via symbolic
    links, so for example vi and view might be names for the same
    executable.)

    But there are a number of other ways to invoke programs. In the
    example I posted, a C program calls execve() directly, and sets up
    argc and argv in a way that a shell would never do.

    For safety, any program should safely handle being invoked
    with unusual arguments -- especially any program with extra
    privileges. For simplicity, it could simply abort if argc==0
    and/or argv[0]==NULL.

    Similar considerations probably apply on Windows, though it seems that
    Windows tries to guarantee argc>1 and argv[0]!=NULL.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Wed Dec 31 16:05:19 2025
    highcrew <high.crew3868@fastmail.com> writes:
    On 1/1/26 12:29 AM, Keith Thompson wrote:
    Paul <nospam@needed.invalid> writes:
    That isn't quite correct, or is at least misleading. ISO C guarantees
    that argv[0] exists, but not that it points to a string. On some
    systems, it can contain be a null pointer.

    I heard of this before.

    Is it just theoretical, or do we have actual systems where
    argv[0]==NULL? I never saw it happen in any modern operating system.

    Some systems (try to) guarantee that argv[0] points to a string, which
    may or may not be the name of the program. But there are ways, on some systems, to invoke a program with argc==0 and argv[0]==NULL.

    For details see here (I've recently made some updates).

    https://github.com/Keith-S-Thompson/argv0

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Wed Dec 31 16:34:44 2025
    Michael S <already5chosen@yahoo.com> writes:
    On Wed, 31 Dec 2025 15:00:24 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    [...]
    If I were going to look into the behavior on Windows, I'd probably
    want to use Windows native features. (I tried my test on Cygwin,
    and the callee wasn't invoked.)

    That's likely because under Windows callee is named callee.exe.
    I didn't try on cygwin, but that was the reason of failure under msys2.
    Also, I am not sure if slash in the name is allowed. May be, backslash
    is required.

    No, that's definitely not it. Cygwin emulates a Unix-like system
    on top of Windows. It creates executables with a ".exe" suffix,
    but plays some tricks so that "foo.exe" also looks like "foo".
    Also, Cygwin uses "/" as a directory separator. When I modified
    the caller program to set argv[0], it worked correctly.

    I've updated the caller to show the result of execve() (which
    normally doesn't return). It returns -1 to indicate an error and
    sets errno to 7 (E2BIG, Argument list too long) -- which is a bit
    odd, but there's no E2SMALL code.

    [...]

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Thu Jan 1 01:03:16 2026
    On Wed, 31 Dec 2025 22:57:55 +0000, bart wrote:

    But, there is a difference between argv[0] and GetModuleFileName().

    So the latter cannot be used as a simple substitute for the former, as
    you might have led us to believe.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Mike Terry@3:633/10 to All on Thu Jan 1 02:01:29 2026
    On 31/12/2025 23:00, Keith Thompson wrote:
    Michael S <already5chosen@yahoo.com> writes:
    On Tue, 30 Dec 2025 19:35:12 -0800
    Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:
    [...]
    For more information, see
    <https://github.com/Keith-S-Thompson/argv0>.

    If you are interested in behavior on non-POSIX systems, primarily
    Windows, but possibly others as well (e.g. VMS) then using exec() in
    caller sounds like a bad idea. It just not how these systems work and
    not how people write programs on them.
    Even when exec() *appears* to works in some environments (like
    msys2) it likely emulated by spawn() followed by exit().

    I'd implement caller with spawn(). I suppose that even on POSIX it is
    more idiomatic.

    If I were going to look into the behavior on Windows, I'd probably
    want to use Windows native features. (I tried my test on Cygwin,
    and the callee wasn't invoked.)

    Apparently the Windows way to invoke a program is CreateProcessA().
    But it takes the command line as a single string. There might not
    be a Windows-native way to exercise the kind of control over argc
    and argv provided by POSIX execve().

    On Windows there is a command-line provided to applications via GetCommandLine() api. This is a
    single zero-terminated string. There is no mention of the possibility of GetCommandLine returning
    NULL.

    Windows (and in general non-*nix OS's) does not have a concept of processes having a list of strings
    as their basic start up mechanism. [Windows processes have an environment strings block, similar to
    *nix and used in the same way, but the "command line" is just a string...]

    Windows views parsing of the command-line string to be in the application domain, I guess. The MSVC
    compiler provides console-mode applications with the usual (argc, argv) but the MSVC application
    start-up code does this rather than the OS. For GUI-mode apps, MSVC provides a different interface
    to the user entry point. (Of course, if the app wants the command-line it can call the Windows api
    to get it, and if it wants command-line tokens it can parse it itself, or call CommandLineToArgv()
    api which is part of the shell api and replicates MSVC startup behaviour...)


    Mike.


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Thu Jan 1 02:29:59 2026
    On Thu, 1 Jan 2026 02:01:29 +0000, Mike Terry wrote:

    On Windows there is a command-line provided to applications via GetCommandLine() api. This is a single zero-terminated string.

    This idea comes from CP/M, which in turn copied it from the old DEC
    operating systems. This is a very primitive model, which never
    envisioned the more advanced kinds of things a Unix-style command line
    makes possible.

    Windows views parsing of the command-line string to be in the
    application domain, I guess.

    Consider a basic thing like quoting words on the command line, to
    allow the passing of arguments with spaces and other odd characters in
    them: with a raw command string, both ends have to agree on the same conventions for doing such things. Getting this wrong leads to great
    sadness.

    On *nix systems, there is no need for such conventions: the argv array
    will be passed literally from invoking program to invoking one, with
    no need for special quoting at all.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Thu Jan 1 07:23:06 2026
    On Wed, 31 Dec 2025 16:34:44 -0800, Keith Thompson wrote:

    [...]

    It creates executables with a ".exe" suffix,
    but plays some tricks so that "foo.exe" also looks like "foo".

    Small aside... Minus the suffix, path in order of search
    is (if i recall correctly):

    1. an associated 'opener' if any (can be defined by associating
    suffix .foo with notepad.exe or whatever, also inherits the
    calling app's icon)

    2. in memory 'alias' via script/batch (set var=foo)

    3. current folder

    4. system path

    but... this also depends on %COMSPEC% ($SHELL in Windows) typically:

    powershell.exe
    cmd.exe
    command.exe
    user defined (like perl.exe for .pl or "busybox sh" for .sh)

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Thu Jan 1 07:32:34 2026
    On Wed, 31 Dec 2025 09:37:08 -0000 (UTC), Lawrence D?Oliveiro wrote:

    Are there any standards for how C argc/argv are supposed to behave on Windows?

    Good question, some more ways to open things (that I know of),
    see 2nd example for 'sort of' argc/argv...

    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <shellapi.h>

    int open_app(const wchar_t *exe_or_path) {
    HINSTANCE r = ShellExecuteW(NULL, L"open", exe_or_path, NULL, NULL, SW_SHOWNORMAL);
    return ((INT_PTR)r > 32) ? 0 : -1;
    }

    open_app(L"notepad.exe");
    open_app(L"C:\\Windows\\System32\\calc.exe");

    or...

    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <wchar.h>

    int launch_app(const wchar_t *cmdline) {
    STARTUPINFOW si;
    PROCESS_INFORMATION pi;

    ZeroMemory(&si, sizeof si);
    ZeroMemory(&pi, sizeof pi);
    si.cb = sizeof si;

    /* CreateProcess *may modify the buffer */
    wchar_t buf[1024];
    wcsncpy(buf, cmdline, 1023);
    buf[1023] = L'\0';

    if (!CreateProcessW(
    NULL, // application name (NULL = parse from cmdline)
    buf, // command line (MUTABLE) <--
    NULL, NULL, // process/thread security
    FALSE, // inherit handles
    0, // creation flags
    NULL, // environment
    NULL, // working directory
    &si,
    &pi))
    return -1;

    /* fire-and-forget */
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    return 0;
    }

    launch_app(L"notepad.exe C:\\temp\\notes.txt");
    launch_app(L\"\"C:\\Program Files\\VideoLAN\\VLC\\vlc.exe\" --fullscreen video.mp4\");

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From bart@3:633/10 to All on Thu Jan 1 14:05:29 2026
    On 01/01/2026 01:03, Lawrence D?Oliveiro wrote:
    On Wed, 31 Dec 2025 22:57:55 +0000, bart wrote:

    But, there is a difference between argv[0] and GetModuleFileName().

    So the latter cannot be used as a simple substitute for the former, as
    you might have led us to believe.


    It depends on your needs. If you need to know exactly what was typed,
    then you use GetCommandLine and extract the first part of it.

    Or you just use __getmainargs. Or argv[0] if available.

    However, I was responding to this:

    LD'O:
    In fact, it is quite common for utilities to behave differently based
    on the name, as passed in argv[0], by which they are invoked.

    And previously I'd said this:

    (And on Windows, if it is 0 and you really need the path, you can get
    it with GetModuleFileNameA().)

    At that point I'd forgotten that GetModuleFileName gives you more
    information than argv[0], so you'd to extract it. But you'd have to do
    that anyway to clean up the input: somebody may have typed c:\abc\.\.\.\.\.\ProG

    So I wasn't misleading anybody.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Thu Jan 1 19:02:49 2026
    On Thu, 1 Jan 2026 07:32:34 -0000 (UTC), Michael Sanders wrote:

    On Wed, 31 Dec 2025 09:37:08 -0000 (UTC), Lawrence D?Oliveiro wrote:

    Are there any standards for how C argc/argv are supposed to behave
    on Windows?

    Good question, some more ways to open things (that I know of), see
    2nd example for 'sort of' argc/argv...

    [examples omitted]

    All those are at the sending end. But what would C code see at the
    receiving end?

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Thu Jan 1 19:03:47 2026
    On Thu, 1 Jan 2026 14:05:29 +0000, bart wrote:

    On 01/01/2026 01:03, Lawrence D?Oliveiro wrote:

    On Wed, 31 Dec 2025 22:57:55 +0000, bart wrote:

    But, there is a difference between argv[0] and
    GetModuleFileName().

    So the latter cannot be used as a simple substitute for the former,
    as you might have led us to believe.

    It depends on your needs.

    You neglected to mention that when offering the substitute before
    though, didn?t you?

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Thu Jan 1 19:20:08 2026
    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC), Lawrence D?Oliveiro wrote:

    All those are at the sending end. But what would C code see at the
    receiving end?

    Opp, you're right. My bad (stuck function pointer h*ll at the moment...)

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Thu Jan 1 21:53:20 2026
    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:

    On Thu, 1 Jan 2026 07:32:34 -0000 (UTC), Michael Sanders wrote:

    On Wed, 31 Dec 2025 09:37:08 -0000 (UTC), Lawrence D?Oliveiro w
    rote:

    Are there any standards for how C argc/argv are supposed to behave
    on Windows?

    Good question, some more ways to open things (that I know of), see
    2nd example for 'sort of' argc/argv...

    [examples omitted]

    All those are at the sending end. But what would C code see at the
    receiving end?

    The first three cases look very simple.

    1.
    argc=1
    argv[0]=notepad.exe
    argv[1]=NULL

    2.
    argc=1
    argv[0]=C:\Windows\System32\calc.exe
    argv[1]=NULL

    3.
    argc=2
    argv[0]=notepad.exe
    argv[1]=C:\temp\notes.txt
    argv[2]=NULL


    4th case does not look like a legal C code.
    It probably was screwed either by poster's news reader or by my
    news reader.




    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From bart@3:633/10 to All on Thu Jan 1 20:28:52 2026
    On 01/01/2026 19:03, Lawrence D?Oliveiro wrote:
    On Thu, 1 Jan 2026 14:05:29 +0000, bart wrote:

    On 01/01/2026 01:03, Lawrence D?Oliveiro wrote:

    On Wed, 31 Dec 2025 22:57:55 +0000, bart wrote:

    But, there is a difference between argv[0] and
    GetModuleFileName().

    So the latter cannot be used as a simple substitute for the former,
    as you might have led us to believe.

    It depends on your needs.

    You neglected to mention that when offering the substitute before
    though, didn?t you?


    FFS, why are you always looking for some argument?

    Have the last word if you like. If it makes you happier, I will admit I
    was deliberately misleading and totally wrong.

    But about what, I don't know! FWIW, I first mentioned GetModuleFileName
    as a possibility when argv[0] was not available.

    So I now withdraw that suggestion. That means that if argv[0] isn't
    available, then you're fucked, since there are apparently no workarounds
    that would be 100% equivalent to what argv[0] would have provided.



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Thu Jan 1 23:50:00 2026
    On Thu, 1 Jan 2026 21:53:20 +0200, Michael S wrote:

    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:

    All those are at the sending end. But what would C code see at the
    receiving end?

    The first three cases look very simple.

    Is there some spec in Windows which describes how that works?

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Fri Jan 2 14:32:07 2026
    On Thu, 1 Jan 2026 23:50:00 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:

    On Thu, 1 Jan 2026 21:53:20 +0200, Michael S wrote:

    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:

    All those are at the sending end. But what would C code see at the
    receiving end?

    The first three cases look very simple.

    Is there some spec in Windows which describes how that works?

    There is a spec that describes how that works in Microsoft's
    implementation. That implementation is available free of charge to
    other Windows compilers.
    If vendor of Windows 'C' compiler decided to implement different
    algorithm then nobody can stop him.

    Through the years you were told so, by different people, and shown
    the spec may be 100 times. But being the trolll you are, you continue
    to ask.

    Still, for the benefit of more sincere readers and also for myself, in
    order to have both pieces in one place: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-co mmandlinetoargvw https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arg uments


    More interesting and meaningful question is how to do the reverse.
    I.e. how to convert an argv[] array into flat form in a way that
    guarantees that CommandLineToArgvW() parses it back into original form?
    Is it even possible in general case or there exist limitations
    (ignoring, for sake of brevity, 2*15-1 size limit)?

    Microsoft certainly has reverse conversion implemented, e.g. here: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/spawnv-ws pawnv

    But I am not aware of command line serialization part available as a
    library call in isolation from process creation part.

    I binged around and googled around, but all I was able to find was the
    name of the function that performs the work: __acrt_pack_wide_command_line_and_environment

    I was not able to find the source code of the function.

    [O.T.]
    I am sure that 15, 10 or even 5 years ago Google would give me link to
    the source in a second. Or, may be, 5 years ago Google already
    wouldn't, but Bing still would.
    But today both search engines are hopelessly crippled with AI and do not
    appear to actually search the web. Instead, the try to guess the
    answer I likely want to hear.
    [/O.T.]












    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Fri Jan 2 16:18:39 2026
    On Fri, 2 Jan 2026 14:32:07 +0200
    Michael S <already5chosen@yahoo.com> wrote:


    More interesting and meaningful question is how to do the reverse.
    I.e. how to convert an argv[] array into flat form in a way that
    guarantees that CommandLineToArgvW() parses it back into original
    form? Is it even possible in general case or there exist limitations (ignoring, for sake of brevity, 2*15-1 size limit)?

    Microsoft certainly has reverse conversion implemented, e.g. here: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/spawnv-wspawnv


    Experimenting with _spawnv() shows that Microsoft made no effort in the direction of invertible serialization/de-serialization of argv[] lists.
    That is, as long as there are no double quotes, everything works as
    expected. But when there are double quotes in the original argv[] then
    more often than not they can't be passed exactly.

    An exact result of de-serialization in presence of unusually placed
    " characters depends on the version of the library.
    Very old versions, like those before UCRT or relatively new
    versions of UCRT, like one in VS2022, do not produce an exact
    inversion, but otherwise their results are reasonable. The version of
    the library supplied with UCRT variant of msys2 tools is much less
    reasonable. I'd rather characterize it as buggy.
    Frankly, I don't understand how it is possible. It leads me to believe
    that parsing routines in C RTL do not use system-provided parser and
    instead do parsing by themselves.




    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Fri Jan 2 20:46:47 2026
    On Fri, 2 Jan 2026 14:32:07 +0200, Michael S wrote:

    Through the years you were told so, by different people, and shown
    the spec may be 100 times. But being the trolll you are, you
    continue to ask.

    Still, for the benefit of more sincere readers and also for myself,
    in order to have both pieces in one place: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments

    That second one begins with these words in large letters:

    ?MICROSOFT SPECIFIC?

    (Remember I was asking for a spec applicable to Windows generally.)

    Who?s trolling now?
    Who?s trolling now?

    (To the tune of ?Who?s sorry now?? etc)

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Fri Jan 2 20:52:59 2026
    On Fri, 2 Jan 2026 16:18:39 +0200, Michael S wrote:

    Experimenting with _spawnv() shows that Microsoft made no effort in the direction of invertible serialization/de-serialization of argv[] lists.
    That is, as long as there are no double quotes, everything works as
    expected. But when there are double quotes in the original argv[] then
    more often than not they can't be passed exactly.

    And every now and then, this kind of fudging can progress beyond
    regular inflammation sadness into the full-fledged tumour of a Windows
    security hole <https://web.archive.org/web/20240711101329/https://list.waikato.ac.nz/archives/list/wlug@list.waikato.ac.nz/message/NVHWSZP7QF7QKYJO2UCHGZC4ADWSNNS7/>.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Mike Terry@3:633/10 to All on Sat Jan 3 04:08:40 2026
    On 02/01/2026 12:32, Michael S wrote:
    On Thu, 1 Jan 2026 23:50:00 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:

    On Thu, 1 Jan 2026 21:53:20 +0200, Michael S wrote:

    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:

    All those are at the sending end. But what would C code see at the
    receiving end?

    The first three cases look very simple.

    Is there some spec in Windows which describes how that works?

    There is a spec that describes how that works in Microsoft's
    implementation. That implementation is available free of charge to
    other Windows compilers.
    If vendor of Windows 'C' compiler decided to implement different
    algorithm then nobody can stop him.

    Through the years you were told so, by different people, and shown
    the spec may be 100 times. But being the trolll you are, you continue
    to ask.

    Still, for the benefit of more sincere readers and also for myself, in
    order to have both pieces in one place: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments


    In the long distant past I investigated how MSVC converts a command-line to its argc/argv input.
    There was an internal routine in the CRT startup code that did pretty much what we would expect, and
    I reversed engineered that for my code (or did I just copy the code? surely the former!). The MSVC
    code did not call CommandLineToArgvW in those days, but reading the description of that api it all
    sounds very familiar - the state flags for controlling "quoted/unquoted" text, even vs odd numbers
    of backslashes and all that.

    I didn't find it difficult to create command-line strings to call C programs, given what I wanted
    those programs to see as argv[n] with n>0. I think it was just a case of quoting all arguments,
    then applying quoting rules as docuemented for CommandLineToArgvW to handle nested quotes/backslashes.

    But I can see a sticky problem - the MSVC parsing for argv[0] was completely separate from thr main
    loop handling other arguments. The logic was considerably simplified, assuming that argv[0] was the
    path for the module being invoked. Since that is expected to be a valid file system path, the logic
    did not handle nested quotes etc.. I think the logic was just:

    - if 1st char is a DQUOTE, copy chars for argv[0] up to next DQUOTE or null terminator.
    (enclosing DQUOTE chars are not included)
    - else copy chars for argv[0] up to next whitespace or null terminator.
    (all chars are included, I think including DQUOTE should it occur)

    Given this, it would not be possible to create certain argv[0] strings containing quotes etc., and I
    understand that the likes of execve() allow that possibility. So I don't know what should happen
    for this case. E.g. I don't see there is a command-line that gives argv[0] the string "\" ". This
    was never a problem for me in practice.

    There would always be at least an argv[0] with this logic, so MSVC ensures argc>0 and argv[0] !=
    NULL. (Of course, MSVC is not "Windows". Various posters in this thread seem to be asking "what
    does /Windows/ do regarding argc/argv?" as though the OS is responsible for setting them.)


    More interesting and meaningful question is how to do the reverse.
    I.e. how to convert an argv[] array into flat form in a way that
    guarantees that CommandLineToArgvW() parses it back into original form?
    Is it even possible in general case or there exist limitations
    (ignoring, for sake of brevity, 2*15-1 size limit)?

    Yes, programmers need this if they need to create a process to invoke some utility program which
    will see particular argv parameters. Users are used to typing in command-lines as a string, e.g. at
    a console, so I suppose they don't normally need to think about the argv[] parsing; they can just
    build the required command-line and use that. (But it's a problem in the general case.)

    The argc/argv problem seemed easy enough in practice if we only need to handle the "real" arguments
    argv[n] with n>0. (Involving CMD.EXE introduced much worse complications, as you might imagine.
    But generally I always thought that MS wasn't really interested in /documenting/ how programmers
    should do things like this, in the same way they never bothered explaining exactly how CMD
    processing worked. Probably because it was forever changing!... Put another way, for many years
    they were really more focussed on admins clicking buttons in some GUI!)


    Microsoft certainly has reverse conversion implemented, e.g. here: https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/spawnv-wspawnv

    But I am not aware of command line serialization part available as a
    library call in isolation from process creation part.

    I binged around and googled around, but all I was able to find was the
    name of the function that performs the work: __acrt_pack_wide_command_line_and_environment

    I was not able to find the source code of the function.

    [O.T.]
    I am sure that 15, 10 or even 5 years ago Google would give me link to
    the source in a second. Or, may be, 5 years ago Google already
    wouldn't, but Bing still would.
    But today both search engines are hopelessly crippled with AI and do not appear to actually search the web. Instead, the try to guess the
    answer I likely want to hear.
    [/O.T.]

    I hear you on that! Also [O.T.] I used to have access to most of Windows source code as part of
    some Windows source code sharing program, so could have found answers to this sort of question with
    minimal effort. (My employer met the criteria based on number of Windows servers/desktops deployed.
    There were various hoops you had to jump through with contracts being regularly (re)signed by
    company board level directors and the likes. Wonder if they still do that... Anyway I'm long
    retired now.)


    Mike.


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Lawrence D?Oliveiro@3:633/10 to All on Sat Jan 3 04:39:39 2026
    On Sat, 3 Jan 2026 04:08:40 +0000, Mike Terry wrote:

    The argc/argv problem seemed easy enough in practice if we only need
    to handle the "real" arguments argv[n] with n>0. (Involving CMD.EXE introduced much worse complications, as you might imagine. But
    generally I always thought that MS wasn't really interested in
    /documenting/ how programmers should do things like this, in the
    same way they never bothered explaining exactly how CMD processing
    worked. Probably because it was forever changing!... Put another
    way, for many years they were really more focussed on admins
    clicking buttons in some GUI!)

    After years -- decades -- of conditioning its users to be allergic to
    the command line, now suddenly the rise of Linux has made command
    lines cool again. Leaving Microsoft in an awkward position ...

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Sat Jan 3 14:24:12 2026
    On Sat, 3 Jan 2026 04:39:39 -0000 (UTC), Lawrence D?Oliveiro wrote:

    After years -- decades -- of conditioning its users to be allergic to
    the command line, now suddenly the rise of Linux has made command
    lines cool again. Leaving Microsoft in an awkward position ...

    And telemetry... compulsory updates... ads all over the place...
    (even paid editions). I'd rather have a quite conversation with
    my OS[1] instead of an argument. Microsoft has gotten /very/
    predacious the last few years at least from my point of view.

    [1] CLI is good for that

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Sat Jan 3 20:38:42 2026
    On Sat, 3 Jan 2026 04:08:40 +0000
    Mike Terry <news.dead.person.stones@darjeeling.plus.com> wrote:

    On 02/01/2026 12:32, Michael S wrote:
    On Thu, 1 Jan 2026 23:50:00 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:

    On Thu, 1 Jan 2026 21:53:20 +0200, Michael S wrote:

    On Thu, 1 Jan 2026 19:02:49 -0000 (UTC)
    Lawrence D?Oliveiro <ldo@nz.invalid> wrote:

    All those are at the sending end. But what would C code see at
    the receiving end?

    The first three cases look very simple.

    Is there some spec in Windows which describes how that works?

    There is a spec that describes how that works in Microsoft's implementation. That implementation is available free of charge to
    other Windows compilers.
    If vendor of Windows 'C' compiler decided to implement different
    algorithm then nobody can stop him.

    Through the years you were told so, by different people, and shown
    the spec may be 100 times. But being the trolll you are, you
    continue to ask.

    Still, for the benefit of more sincere readers and also for myself,
    in order to have both pieces in one place: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-commandlinetoargvw
    https://learn.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments


    In the long distant past I investigated how MSVC converts a
    command-line to its argc/argv input. There was an internal routine in
    the CRT startup code that did pretty much what we would expect, and I reversed engineered that for my code (or did I just copy the code?
    surely the former!). The MSVC code did not call CommandLineToArgvW
    in those days, but reading the description of that api it all sounds
    very familiar - the state flags for controlling "quoted/unquoted"
    text, even vs odd numbers of backslashes and all that.

    I didn't find it difficult to create command-line strings to call C
    programs, given what I wanted those programs to see as argv[n] with
    0. I think it was just a case of quoting all arguments, then
    applying quoting rules as docuemented for CommandLineToArgvW to
    handle nested quotes/backslashes.

    But I can see a sticky problem - the MSVC parsing for argv[0] was
    completely separate from thr main loop handling other arguments. The
    logic was considerably simplified, assuming that argv[0] was the path
    for the module being invoked. Since that is expected to be a valid
    file system path, the logic did not handle nested quotes etc.. I
    think the logic was just:

    - if 1st char is a DQUOTE, copy chars for argv[0] up to next DQUOTE
    or null terminator. (enclosing DQUOTE chars are not included)
    - else copy chars for argv[0] up to next whitespace or null
    terminator. (all chars are included, I think including DQUOTE should
    it occur)

    Given this, it would not be possible to create certain argv[0]
    strings containing quotes etc., and I understand that the likes of
    execve() allow that possibility. So I don't know what should happen
    for this case. E.g. I don't see there is a command-line that gives
    argv[0] the string "\" ". This was never a problem for me in
    practice.

    There would always be at least an argv[0] with this logic, so MSVC
    ensures argc>0 and argv[0] != NULL. (Of course, MSVC is not
    "Windows". Various posters in this thread seem to be asking "what
    does /Windows/ do regarding argc/argv?" as though the OS is
    responsible for setting them.)


    More interesting and meaningful question is how to do the reverse.
    I.e. how to convert an argv[] array into flat form in a way that
    guarantees that CommandLineToArgvW() parses it back into original
    form? Is it even possible in general case or there exist limitations (ignoring, for sake of brevity, 2*15-1 size limit)?

    Yes, programmers need this if they need to create a process to invoke
    some utility program which will see particular argv parameters.
    Users are used to typing in command-lines as a string, e.g. at a
    console, so I suppose they don't normally need to think about the
    argv[] parsing; they can just build the required command-line and use
    that. (But it's a problem in the general case.)


    I hoped that Microsoft's implementation of _spawnv() at least
    makes an effort at reverse transformation. But test shows that it
    does not. _spawnl() appears to do something very dumb - just concatenate
    all argv[] strings inserting one space character between parts.
    So, it has troubles not just with double quotes but even with spaces
    within arguments.


    The argc/argv problem seemed easy enough in practice if we only need
    to handle the "real" arguments argv[n] with n>0. (Involving CMD.EXE introduced much worse complications, as you might imagine. But
    generally I always thought that MS wasn't really interested in
    /documenting/ how programmers should do things like this, in the same
    way they never bothered explaining exactly how CMD processing worked.
    Probably because it was forever changing!... Put another way, for
    many years they were really more focussed on admins clicking buttons
    in some GUI!)


    To be fair, powershell exists for many more years than WinNt family was existing before emergence of powershell.
    The problem with it, at least IMHO, is a second system syndrome.
    command.com and to significant extent cmd.exe as well, seen as
    programming language was too weak and too primitive. With Powershell
    they went too far in the opposite direction - its rather powerfull, as programming language, but nott as simple at simple tasks as one expects
    from OS shell.
    So, considering that fewer and fewer people with programmer's attitude
    are employed as admins, it's no wonder that most of them are incapable
    to write powershell scripts by themselves. At best, they can cat&past.








    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Geoff@3:633/10 to All on Mon Jan 5 20:00:26 2026
    On Wed, 31 Dec 2025 15:14:27 -0800, Keith Thompson <Keith.S.Thompson+u@gmail.com> wrote:

    It's entirely possible that Windows goes beyond the ISO C
    requirements and explicitly or implicitly guarantees argc>0.
    It's also entirely possible that it doesn't. Do you have any
    concrete information one way or the other

    https://learn.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-170

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Wed Jan 7 07:39:08 2026
    John McCue <jmclnx@gmail.com.invalid> writes:

    Michael Sanders <porkchop@invalid.foo> wrote:

    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Wed Jan 7 07:46:40 2026
    Michael S <already5chosen@yahoo.com> writes:

    [regarding rand() and srand()]

    Pay attention that C Standard only requires for the same seed to always produces the same sequence. There is no requirement that different
    seeds have to produce different sequences.
    So, for generator in your example, implementation like below would be
    fully legal. Personally, I wouldn't even consider it as particularly
    poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    It seems better to do, for example,

    void srand(unsigned seed ) { init = seed - !seed;}


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Wed Jan 7 07:50:09 2026
    antispam@fricas.org (Waldek Hebisch) writes:

    [...]
    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. O?Neill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.

    Interesting paper. Thank you.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Wed Jan 7 18:14:14 2026
    On Wed, 07 Jan 2026 07:46:40 -0800
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:

    [regarding rand() and srand()]

    Pay attention that C Standard only requires for the same seed to
    always produces the same sequence. There is no requirement that
    different seeds have to produce different sequences.
    So, for generator in your example, implementation like below would
    be fully legal. Personally, I wouldn't even consider it as
    particularly poor quality:

    void srand(unsigned seed ) { init = seed | 1;}

    It seems better to do, for example,

    void srand(unsigned seed ) { init = seed - !seed;}


    Yes, it is better.


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Wed Jan 7 08:41:25 2026
    Michael S <already5chosen@yahoo.com> writes:

    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:
    [...]
    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. O?Neill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.

    I don't know. Testing randomness is complicated matter.
    How can I be sure that L'Ecuyer and Simard's TestU01 suite tests
    things that I personally care about and that it does not test
    things that are of no interest for me? Especially, the latter.

    Do you think any of the tests in the TestU01 suite are actually counter-indicated? As long as you don't think any TestU01 test
    makes things worse, there is no reason not to use all of them.
    You are always free to disregard tests you don't care about.

    Also, the TestU01 suit is made for generators with 32-bit output.
    M. O'Neill used ad hoc technique to make it applicable to
    generators with 64-bit output. Is this technique right? Or may
    be it put 64-bit PRNG at unfair disadvantage?

    As long as the same mapping is applied to all 64-bit PRNGs under
    consideration I don't see a problem. The point of the test is to
    compare PRNGs, not to compare test methods. If someone thinks a
    different set of tests is called for they are free to run them.

    Besides, I strongly disagree with at least one assertion made by
    O'Neill: "While security-related applications should use a secure
    generator, because we cannot always know the future contexts in
    which our code will be used, it seems wise for all applications to
    avoid generators that make discovering their entire internal state
    completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my
    application easy discovery of complete state of PRNG is not a
    defect.

    You and she are talking about different things. You are talking
    about choosing a PRNG to be used only by yourself. She is talking
    about choosing a PRNG to be made available to other people without
    knowing who they are or what their needs are. In the second case
    it's reasonable to raise the bar for the set of criteria that need
    to be met.

    Anyway, even if I am skeptical about her criticism of popular PRNGs, intuitively I agree with the constructive part of the article - medium-quality PRNG that feeds medium quality hash function can
    potentially produce very good fast PRNG with rather small internal
    state.

    After looking at one of the example PCG generators, I would
    describe it as a medium-quality PRNG that feeds a low-quality
    hash. The particular combination I looked at produced good
    results, but it isn't clear which combinations of PRNG and
    hash would do likewise.

    On related note, I think that even simple counter fed into high
    quality hash function (not cryptographically high quality, far
    less than that) can produce excellent PRNG with even smaller
    internal state. But not very fast one. Although the speed
    depends on specifics of used computer. I can imagine computer
    that has low-latency Rijndael128 instruction. On such computer,
    running counter through 3-4 rounds of Rijndael ill produce very
    good PRNG that is only 2-3 times slower than, for example, LCG
    128/64.

    I think the point of her paper where she talks about determining
    how much internal state is needed is to measure the efficacy of
    the PRNG, not to try to reduce the amount of state needed. Based
    on my own experience with various PRNGs I think it's a mistake to
    try to minimize the amount of internal state needed. My own rule
    of thumb is to allow at least a factor of four: for example, a
    PRNG with a 32-bit output should have at least 128 bits of state.
    My latest favorite has 256 bits of state to produce 32-bit
    outputs (and so might also do well to produce 64-bit outputs, but
    I haven't tested that).

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Wed Jan 7 13:54:21 2026
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    John McCue <jmclnx@gmail.com.invalid> writes:
    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.

    And neither is the non-conforming rand() on OpenBSD.

    The rand(1) man page on OpenBSD 7.8 says:

    Standards insist that this interface return deterministic
    results. Unsafe usage is very common, so OpenBSD changed the
    subsystem to return non-deterministic results by default.

    To satisfy portable code, srand() may be called to initialize
    the subsystem. In OpenBSD the seed variable is ignored,
    and strong random number results will be provided from
    arc4random(3). In other systems, the seed variable primes a
    simplistic deterministic algorithm.

    It does provide an srand_deterministic() function that behaves the way
    srand() is supposed to.

    And a program that calls rand() produces a link-time warning, even
    though OpenBSD's rand() *doesn't* return deterministic values.

    ld: warning: rand_test.c(rand_test.o:(main)): warning: rand() may return deterministic values, is that what you want?

    (In a similarly questionable decision, OpenBSD's printf triggers a
    SIGABRT signal if the format string uses "%n".)

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael S@3:633/10 to All on Thu Jan 8 01:06:01 2026
    On Wed, 07 Jan 2026 08:41:25 -0800
    Tim Rentsch <tr.17687@z991.linuxsc.com> wrote:

    Michael S <already5chosen@yahoo.com> writes:

    On Tue, 23 Dec 2025 17:54:05 -0000 (UTC)
    antispam@fricas.org (Waldek Hebisch) wrote:
    [...]
    There is a paper "PCG: A Family of Simple Fast Space-Efficient
    Statistically Good Algorithms for Random Number Generation"
    by M. O?Neill where she gives a family of algorithms and runs
    several statistical tests against known algorithms. Mersenne
    Twister does not look good in tests. If you have enough (128) bits
    LCGs do pass tests. A bunch of generators with 64-bit state also
    passes tests. So the only reason to prefer Mersenne Twister is
    that it is implemented in available libraries. Otherwise it is
    not so good, have large state and needs more execution time
    than alternatives.

    I don't know. Testing randomness is complicated matter.
    How can I be sure that L'Ecuyer and Simard's TestU01 suite tests
    things that I personally care about and that it does not test
    things that are of no interest for me? Especially, the latter.

    Do you think any of the tests in the TestU01 suite are actually counter-indicated? As long as you don't think any TestU01 test
    makes things worse, there is no reason not to use all of them.
    You are always free to disregard tests you don't care about.


    Except that it's difficult psychologically.
    The batteries of test gains position of of authority in your mind.
    Well, may be, you specifically are resistant, but I am not. Nor is
    Melissa O'Nail, it seems.

    To illustrate my point, I will tell you the story about myself.
    Sort of confession.
    If you had read the rest of this thread (or paid attention to
    finer details in the article of O'Nail) then you already know that
    mt19937 consistently fails in scomp_LinearComp() subtest of Crush and
    BigCrush batteries of Test01. As reported, I "fixed" it by skipping
    every 19936th word of mt19937 output. This particular "fix" is benign.
    I am sure that it does not make output of generator less random. The
    only impact is a bit of slowness, because of the need to manage yet
    another modulo counter.
    What I did not tell so far is that I tried another "fix". I added
    leakage during mt state update. It means that periodically I
    forced two LS bits of newly generated state word to be '01', without
    affecting the word that goes to tampering and then to output.
    And according to Test01 it worked! Flew through all batteries!
    Luckily, I was sufficiently self-conscious to understand that I don't understand nearly enough about algebra of Galois fields to predict all consequences of my modification. But that's me. I know few people that
    are less aware of their limitations.

    Also, the TestU01 suit is made for generators with 32-bit output.
    M. O'Neill used ad hoc technique to make it applicable to
    generators with 64-bit output. Is this technique right? Or may
    be it put 64-bit PRNG at unfair disadvantage?

    As long as the same mapping is applied to all 64-bit PRNGs under consideration I don't see a problem. The point of the test is to
    compare PRNGs, not to compare test methods. If someone thinks a
    different set of tests is called for they are free to run them.


    But Melissa, following advice of L'Ecuyer, and me, following advice of
    Melissa, tested 64-bit generators three times - LSW then MSW, only LSW
    and only MSW, thus putting them under trice more serious scrutiny than
    32-bit counterparts.

    Besides, I strongly disagree with at least one assertion made by
    O'Neill: "While security-related applications should use a secure generator, because we cannot always know the future contexts in
    which our code will be used, it seems wise for all applications to
    avoid generators that make discovering their entire internal state completely trivial."
    No, I know exactly what I am doing/ I know exactly that for my
    application easy discovery of complete state of PRNG is not a
    defect.

    You and she are talking about different things. You are talking
    about choosing a PRNG to be used only by yourself. She is talking
    about choosing a PRNG to be made available to other people without
    knowing who they are or what their needs are. In the second case
    it's reasonable to raise the bar for the set of criteria that need
    to be met.


    No, this part of her article is a mistake, plain and simple.
    He even sort of admitted it couple of years later in her blogg.
    In reality, we either need secure PRNG or do not need secure PRNG.
    There is no middle ground of "more or less secure insecure PRNGs".
    PRNGs she advocates are insecure until proven otherwise by crypto
    analysts.

    Anyway, even if I am skeptical about her criticism of popular PRNGs, intuitively I agree with the constructive part of the article - medium-quality PRNG that feeds medium quality hash function can
    potentially produce very good fast PRNG with rather small internal
    state.

    After looking at one of the example PCG generators, I would
    describe it as a medium-quality PRNG that feeds a low-quality
    hash. The particular combination I looked at produced good
    results, but it isn't clear which combinations of PRNG and
    hash would do likewise.


    I tend to like CRC32C for hashing 64 bits into 32 bits; for no reason
    apart from that this primitive is available and fast on modern Intel
    and AMD CPUs. ARM64 CPUs as well, I think, although less than 100% sure.

    On related note, I think that even simple counter fed into high
    quality hash function (not cryptographically high quality, far
    less than that) can produce excellent PRNG with even smaller
    internal state. But not very fast one. Although the speed
    depends on specifics of used computer. I can imagine computer
    that has low-latency Rijndael128 instruction. On such computer,
    running counter through 3-4 rounds of Rijndael ill produce very
    good PRNG that is only 2-3 times slower than, for example, LCG
    128/64.

    I think the point of her paper where she talks about determining
    how much internal state is needed is to measure the efficacy of
    the PRNG, not to try to reduce the amount of state needed. Based
    on my own experience with various PRNGs I think it's a mistake to
    try to minimize the amount of internal state needed. My own rule
    of thumb is to allow at least a factor of four: for example, a
    PRNG with a 32-bit output should have at least 128 bits of state.
    My latest favorite has 256 bits of state to produce 32-bit
    outputs (and so might also do well to produce 64-bit outputs, but
    I haven't tested that).

    One important point that I seem to figure out recently is that the only practical way to produce both solid and very fast PRNG that adheres to
    standard language APIs with 32-bit and to somewhat smaller extent 64-bit output, is to use buffering. I.e. most of the time generator simply
    reads pre-calculated word from the buffer and only ones per N
    iterations runs an actual PRNG algorithm, probably in a loop, often
    in SIMD. In order for this approach to be effective, buffer can't be particularly small. 32 bytes (256 bits) appear to be an absolute
    minimum. The buffer and counter that manages buffering, are parts of the generator state. That alone sets a practical minimal limit on the size
    of generator and diminishes significance of the difference between
    PRNGs with "algorithmic" state of 64 bits, 128 bits or even 256 bits.

    The observation certainly applies to PCGs or to anything else that
    utilizes LCG for its state update primitive.

    Now, if one does no look for ultimate speed then said above does not
    apply.



    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Thu Jan 8 02:57:08 2026
    On Mon, 22 Dec 2025 08:48:10 -0000 (UTC), Michael Sanders wrote:

    [...]

    This puppy is done (at least I'm happy with it).

    Main thing I wanted to accomplish was solid keyboard
    handling in the REPL (so I can apply the knowledge
    gained to another project & I believe I'm pretty close.)

    As it stands the game now has:

    - 3 games in a single binary (mastermind, moo, bagels)

    - man page

    - make file

    - sparse comments...

    Zipped sources [8.3KB] for a few days below:

    <https://drive.google.com/file/d/1PAzCj-Mfx06y2oHz9LQTxa74Aerd2pYe/view?usp=sharing>

    Thanks for the help everyone, I'm gone fishin' for awhile =)

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Thu Jan 8 15:34:04 2026
    On Wed, 07 Jan 2026 13:54:21 -0800, Keith Thompson wrote:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    John McCue <jmclnx@gmail.com.invalid> writes:
    Michael Sanders <porkchop@invalid.foo> wrote:
    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.

    And neither is the non-conforming rand() on OpenBSD.

    The rand(1) man page on OpenBSD 7.8 says:

    Standards insist that this interface return deterministic
    results. Unsafe usage is very common, so OpenBSD changed the
    subsystem to return non-deterministic results by default.

    To satisfy portable code, srand() may be called to initialize
    the subsystem. In OpenBSD the seed variable is ignored,
    and strong random number results will be provided from
    arc4random(3). In other systems, the seed variable primes a
    simplistic deterministic algorithm.

    It does provide an srand_deterministic() function that behaves the way srand() is supposed to.

    So then clang would use:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But I don't know (yet) that gcc does as well under OpenBSD.

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Thu Jan 8 09:26:18 2026
    antispam@fricas.org (Waldek Hebisch) writes:

    Michael S <already5chosen@yahoo.com> wrote:
    [...]
    Anyway, even if I am skeptical about her criticism of popular PRNGs,
    intuitively I agree with the constructive part of the article -
    medium-quality PRNG that feeds medium quality hash function can
    potentially produce very good fast PRNG with rather small internal
    state.

    She seem to care very much about having minimal possible state.
    That is may be nice on embeded systems, but in general I would
    happily accept slighty bigger state (say 256 bits). But if
    we can get good properties with very small state, then why not?
    [...]

    That depends on whether one thinks the tests done to measure
    quality are sufficient to determine all the axes of "good
    properties". I'm not inclined to think that they do. The
    set of tests done in the TestU01 suite are quite good IMO,
    but I don't think they measure all the properties of interest.
    I prefer to err on the side of caution.

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Thu Jan 8 14:44:27 2026
    Michael Sanders <porkchop@invalid.foo> writes:
    On Wed, 07 Jan 2026 13:54:21 -0800, Keith Thompson wrote:
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    [...]
    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.

    And neither is the non-conforming rand() on OpenBSD.

    The rand(1) man page on OpenBSD 7.8 says:

    Standards insist that this interface return deterministic
    results. Unsafe usage is very common, so OpenBSD changed the
    subsystem to return non-deterministic results by default.

    To satisfy portable code, srand() may be called to initialize
    the subsystem. In OpenBSD the seed variable is ignored,
    and strong random number results will be provided from
    arc4random(3). In other systems, the seed variable primes a
    simplistic deterministic algorithm.

    It does provide an srand_deterministic() function that behaves the way
    srand() is supposed to.

    So then clang would use:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But I don't know (yet) that gcc does as well under OpenBSD.

    I don't know what you mean when you say that clang "would use"
    that code.

    I'm not aware that either clang or gcc uses random numbers
    internally. I don't know why they would.

    You could certainly write the above code and compile it with either
    gcc or clang (or any other C compiler on OpenBSD). I've confirmed
    that gcc on OpenBSD does predefine the symbol __OpenBSD__. There
    should be no relevant difference between gcc and clang; random
    number generation is implemented in the library, not in the compiler.

    If your point is that a programmer using either gcc or clang could
    use the above code to get the required deterministic behavior
    for rand(), I agree. (Though it shouldn't be necessary; IMHO the
    OpenBSD folks made a very bad decision.)

    Relatedly, the NetBSD implementation of rand() is conforming, but
    of very low quality. The low-order bit alternates between 0 and
    1 on successive rand() calls, the two low-order bits repeat with
    a cycle of 4, and so on. Larry Jones wrote about it here in 2010:

    The even/odd problem was caused at Berkeley by a well meaning
    but clueless individual who increased the range of the generator
    (which originally matched the sample implementation) by returning
    the *entire* internal state rather than just the high-order
    bits of it. BSD was very popular, so that defective generator
    got around a lot, unfortunately.

    And I've just discovered that the OpenBSD rand() returns alternating
    odd and even results after a call to srand_determinstic().

    It's disturbing that this has never been fixed.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Fri Jan 9 06:06:57 2026
    On Thu, 08 Jan 2026 14:44:27 -0800, Keith Thompson wrote:

    Michael Sanders <porkchop@invalid.foo> writes:
    On Wed, 07 Jan 2026 13:54:21 -0800, Keith Thompson wrote:
    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:
    [...]
    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.

    And neither is the non-conforming rand() on OpenBSD.

    The rand(1) man page on OpenBSD 7.8 says:

    Standards insist that this interface return deterministic
    results. Unsafe usage is very common, so OpenBSD changed the
    subsystem to return non-deterministic results by default.

    To satisfy portable code, srand() may be called to initialize
    the subsystem. In OpenBSD the seed variable is ignored,
    and strong random number results will be provided from
    arc4random(3). In other systems, the seed variable primes a
    simplistic deterministic algorithm.

    It does provide an srand_deterministic() function that behaves the way
    srand() is supposed to.

    So then clang would use:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But I don't know (yet) that gcc does as well under OpenBSD.

    I don't know what you mean when you say that clang "would use"
    that code.

    I'm not aware that either clang or gcc uses random numbers
    internally. I don't know why they would.

    Well, I meant the macro itself is (I'm guessing) probably defined
    by clang since its the default compiler.

    You could certainly write the above code and compile it with either
    gcc or clang (or any other C compiler on OpenBSD). I've confirmed
    that gcc on OpenBSD does predefine the symbol __OpenBSD__. There
    should be no relevant difference between gcc and clang; random
    number generation is implemented in the library, not in the compiler.

    This is the info I'm, wondering about: both clang & gcc predefine
    the symbol.

    If your point is that a programmer using either gcc or clang could
    use the above code to get the required deterministic behavior
    for rand(), I agree. (Though it shouldn't be necessary; IMHO the
    OpenBSD folks made a very bad decision.)

    Relatedly, the NetBSD implementation of rand() is conforming, but
    of very low quality. The low-order bit alternates between 0 and
    1 on successive rand() calls, the two low-order bits repeat with
    a cycle of 4, and so on. Larry Jones wrote about it here in 2010:

    The even/odd problem was caused at Berkeley by a well meaning
    but clueless individual who increased the range of the generator
    (which originally matched the sample implementation) by returning
    the *entire* internal state rather than just the high-order
    bits of it. BSD was very popular, so that defective generator
    got around a lot, unfortunately.

    And I've just discovered that the OpenBSD rand() returns alternating
    odd and even results after a call to srand_determinstic().

    It's disturbing that this has never been fixed.

    Yikes! Thanks Keith. This is sort of odd for OpenBSD.


    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Thu Jan 8 22:46:42 2026
    Michael Sanders <porkchop@invalid.foo> writes:
    On Thu, 08 Jan 2026 14:44:27 -0800, Keith Thompson wrote:
    Michael Sanders <porkchop@invalid.foo> writes:
    [...]
    So then clang would use:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But I don't know (yet) that gcc does as well under OpenBSD.

    I don't know what you mean when you say that clang "would use"
    that code.

    I'm not aware that either clang or gcc uses random numbers
    internally. I don't know why they would.

    Well, I meant the macro itself is (I'm guessing) probably defined
    by clang since its the default compiler.

    You mean the macro __OpenBSD__? Yes, that and other similar macros
    are predefined by the compiler, which is configured for each OS.
    gcc on OpenBSD also predefines it. (I don't know whether it's
    predefined by the preprocessor directly or by some header that's
    included implicitly. That doesn't really matter.) Compilers on
    other platforms will not predefine __OpenBSD__.

    But your original statement implied that clang would *use* that
    particular piece of code, which didn't make much sense. Were you
    just asking about how the __OpenBSD__ macro is defined, without
    reference to srand?

    [...]

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Tim Rentsch@3:633/10 to All on Fri Jan 9 00:36:21 2026
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:

    Tim Rentsch <tr.17687@z991.linuxsc.com> writes:

    John McCue <jmclnx@gmail.com.invalid> writes:

    Michael Sanders <porkchop@invalid.foo> wrote:

    Is it incorrect to use 0 (zero) to seed srand()?

    int seed = (argc >= 2 && strlen(argv[1]) == 9)
    ? atoi(argv[1])
    : (int)(time(NULL) % 900000000 + 100000000);

    srand(seed);

    I like to just read /dev/urandom when I need a random
    number. Seem easier and more portable across Linux &
    the *BSDs.

    int s;
    read(fd, &s, sizeof(int));

    Apples and oranges. Many applications that use random numbers
    need a stream of numbers that is deterministic and reproducible,
    which /dev/urandom is not.

    And neither is the non-conforming rand() on OpenBSD.

    The rand(1) man page on OpenBSD 7.8 says:

    Standards insist that this interface return deterministic
    results. Unsafe usage is very common, so OpenBSD changed the
    subsystem to return non-deterministic results by default.

    To satisfy portable code, srand() may be called to initialize
    the subsystem. In OpenBSD the seed variable is ignored,
    and strong random number results will be provided from
    arc4random(3). In other systems, the seed variable primes a
    simplistic deterministic algorithm.

    Apparently the OpenBSD folks have seen fit to remove the only
    desirable property that ISO C actually specifies for the standard
    library random number generator. Bravo!

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Michael Sanders@3:633/10 to All on Fri Jan 9 22:38:59 2026
    On Thu, 08 Jan 2026 22:46:42 -0800, Keith Thompson wrote:

    But your original statement implied that clang would *use* that
    particular piece of code, which didn't make much sense. Were you
    just asking about how the __OpenBSD__ macro is defined, without
    reference to srand?

    Well, under OpenBSD I plan on using:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But what I was asking is whether or not gcc would recognize
    the __OpenBSD__ macro (why wouldn't I'm assuming) since clang
    is the default compiler.

    But also about srand()... you've got me really wondering why
    OpenBSD would deviate from the standard as they have. I get
    that the those folks disagree because its deterministic, but
    its the accepted standard to be deterministic with srand().

    Only speaking for myself here, rather than srand_deterministic()
    and srand() (that's not deterministic under OpenBSD) it
    would've made more sense to've implemented srand_non_deterministic()
    and left srand() alone. That design decision on their part only
    muddies the waters in my thinking. Live & learn =)

    --
    :wq
    Mike Sanders

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Scott Lurndal@3:633/10 to All on Fri Jan 9 23:27:15 2026
    Michael Sanders <porkchop@invalid.foo> writes:
    On Thu, 08 Jan 2026 22:46:42 -0800, Keith Thompson wrote:

    But your original statement implied that clang would *use* that
    particular piece of code, which didn't make much sense. Were you
    just asking about how the __OpenBSD__ macro is defined, without
    reference to srand?

    Well, under OpenBSD I plan on using:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But what I was asking is whether or not gcc would recognize
    the __OpenBSD__ macro (why wouldn't I'm assuming) since clang
    is the default compiler.

    $ gcc -dM -E - < /dev/null

    will show all the preprocessor macros predefined by the compiler.

    There 397 predefined macros on my Fedora gcc 14 installation.

    Note that other macros may be defined in header files.



    But also about srand()... you've got me really wondering why
    OpenBSD would deviate from the standard as they have. I get
    that the those folks disagree because its deterministic, but
    its the accepted standard to be deterministic with srand().

    I expect they were primary concerned with the security
    implications of a deterministic algorithm.


    Only speaking for myself here, rather than srand_deterministic()
    and srand() (that's not deterministic under OpenBSD) it
    would've made more sense to've implemented srand_non_deterministic()
    and left srand() alone. That design decision on their part only
    muddies the waters in my thinking. Live & learn =)

    I'm sure they wanted the change to apply by default to existing
    applications (many of them likely distributed with various BSD
    releases).


    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Keith Thompson@3:633/10 to All on Fri Jan 9 17:09:47 2026
    Michael Sanders <porkchop@invalid.foo> writes:
    On Thu, 08 Jan 2026 22:46:42 -0800, Keith Thompson wrote:
    But your original statement implied that clang would *use* that
    particular piece of code, which didn't make much sense. Were you
    just asking about how the __OpenBSD__ macro is defined, without
    reference to srand?

    Well, under OpenBSD I plan on using:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    But what I was asking is whether or not gcc would recognize
    the __OpenBSD__ macro (why wouldn't I'm assuming) since clang
    is the default compiler.

    OK.

    Do you understand that your original question was unclear?

    You said that "clang would use" the quoted 5-line code snippet,
    and asked whether "gcc does as well". It's not clang or gcc that
    would use that code. It would be used by a programmer writing code
    to be compiled with clang or gcc.

    I understand now what you meant. I'd like to be sure that you
    understand the problem with the question as you originally wrote it.

    I have clang 19.1.7 and gcc 13.2.0 installed on OpenBSD 7.8, and
    both predefine the macro __OpenBSD__.

    But also about srand()... you've got me really wondering why
    OpenBSD would deviate from the standard as they have. I get
    that the those folks disagree because its deterministic, but
    its the accepted standard to be deterministic with srand().

    Only speaking for myself here, rather than srand_deterministic()
    and srand() (that's not deterministic under OpenBSD) it
    would've made more sense to've implemented srand_non_deterministic()
    and left srand() alone. That design decision on their part only
    muddies the waters in my thinking. Live & learn =)

    I don't know why they made that decision. It was clearly deliberate.
    I agree that adding an srand_non_deterministic() function would
    have been better.

    --
    Keith Thompson (The_Other_Keith) Keith.S.Thompson+u@gmail.com
    void Void(void) { Void(); } /* The recursive call of the void */

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)
  • From Kaz Kylheku@3:633/10 to All on Sat Jan 10 19:44:01 2026
    On 2026-01-09, Michael Sanders <porkchop@invalid.foo> wrote:
    On Thu, 08 Jan 2026 22:46:42 -0800, Keith Thompson wrote:

    But your original statement implied that clang would *use* that
    particular piece of code, which didn't make much sense. Were you
    just asking about how the __OpenBSD__ macro is defined, without
    reference to srand?

    Well, under OpenBSD I plan on using:

    #ifdef __OpenBSD__
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    This is is better

    // In some common configuration header:

    #ifdef __OpenBSD__
    #define HAVE_SRAND_DETERMINISTIC 1
    #define HAVE_... /* other such macros */
    #endif

    (Or the configuration can be generated by scripts which detect features
    in environment.)

    Then in the code:

    #if HAVE_SRAND_DETERMINISTIC
    srand_deterministic(seed);
    #else
    srand(seed);
    #endif

    If a platform other than __OpenBSD__ comes along which has
    srand_deterministic you just make sure HAVE_SRAND_DETERMINISTIC 1 is
    turned on; you don't have to edit the code where that is used.

    This idea is seen in the configuration of GNU programs and such.

    There is a "GDB Internals" document which discusses it in a section
    called "Clean Design"

    Partial quote:

    New #ifdef?s which test for specific compilers or manufacturers or
    operating systems are unacceptable. All #ifdef?s should test for
    features. The information about which configurations contain which
    features should be segregated into the configuration files. Experience
    has proven far too often that a feature unique to one particular system
    often creeps into other systems; and that a conditional based on some
    predefined macro for your current system will become worthless over
    time, as new versions of your system come out that behave differently
    with regard to this feature.

    [ ... more discussion ... ]

    https://www.sourceware.org/gdb/5/onlinedocs/gdbint.pdf

    I think the GNU Coding Standards document may have had a similar
    discussion; I don't see it in the current version though.

    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca

    --- PyGate Linux v1.5.2
    * Origin: Dragon's Lair, PyGate NNTP<>Fido Gate (3:633/10)