• getchar implementation without GNUishms

    From anthk@anthk@openbsd.home to comp.lang.awk on Tue Mar 25 09:51:54 2025
    From Newsgroup: comp.lang.awk

    Hello the guy from https://luxferre.top and gopher://hoi.st
    has pretty interesting stuff, such as

    git://git.luxferre.top/awk-gold-collection.git

    (Run
    git clone --recursive git://git.luxferre.top/awk-gold-collection.git
    to get them all)

    The issue is 'subleq.awk' uses read from GNU Bash I'd guess; thus,
    is not portable to sh/ksh. The flags are missing:

    function getchar(c, cmd) { # POSIX-compatible getchar emulation with sh read
    (cmd="c='';IFS= read -r -n 1 -d $'\\0' c;printf '%u' \"'$c\"") | getline c
    close(cmd)
    return int(c)
    }

    There's another one implemented at tgl.awk, some 'universal' library
    with mission functions for POSIX awk's. Not so universal,
    as it falls in the same traps: it depends on 'od' from
    GNU coreutils, with flags equallly missing:

    function getchar(c) {
    if(!TGL_GCH_CMD) TGL_GCH_CMD = "od -tu1 -w1 -N1 -An -v" # first time usage
    TGL_GCH_CMD | getline c
    close(TGL_GCH_CMD)
    return int(c)
    }


    Then I tried to it under C, both with a leading space and a
    newline and with just the char:

    int main()
    {
    int c;
    if ((c = getchar()) != EOF) {
    printf(" %d\n", c);
    /* printf("%d", c); */
    }
    }

    Then I edited both commands to be piped into getline to use
    my custom C program, but I had no luck.
    Subleq programs with no input work fine.
    Once I try a Forth implemented in subleq, no input
    is parsed right, as Forth doesn't eval a simple
    "2 2 + .s" instruction.

    subleq.fth and forth:

    https://github.com/howerj/subleq/

    The one in C works fine:

    ./subleq sublec.dec sublec.fth

    2 3 + .s
    5 ok

    Not the case with awk (OpenBSD, one true awk), mawk and gawk:

    awk -f subleq.awk ./subleq/subleq.dec ./sublec.fth

    sublec.fth has extra Forth words, thus it can be slower to
    parse under subleq.awk. But the core subleq.dec has a
    minimal implementation enough to do basic arithmetic.
    It works under the C implementation of subleq, again,
    but not under subleq.awk (outside GNU oses).

    Could it be possible to implement a true portable getchar?



    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.awk on Tue Mar 25 14:10:49 2025
    From Newsgroup: comp.lang.awk

    On 25.03.2025 10:51, anthk wrote:
    Hello the guy from https://luxferre.top and gopher://hoi.st
    has pretty interesting stuff, such as

    [ shell specific or other tries to emulate some getchar function ]

    Could it be possible to implement a true portable getchar?

    Those who think that getchar is a useful function may implement that
    natively in Awk. (It avoids external dependencies and all the issues
    that the posted/quoted code has made obvious.)

    Janis

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Arti F. Idiot@addr@is.invalid to comp.lang.awk on Tue Mar 25 22:16:32 2025
    From Newsgroup: comp.lang.awk

    On 3/25/25 7:10 AM, Janis Papanagnou wrote:
    On 25.03.2025 10:51, anthk wrote:
    Hello the guy from https://luxferre.top and gopher://hoi.st
    has pretty interesting stuff, such as

    [ shell specific or other tries to emulate some getchar function ]

    Could it be possible to implement a true portable getchar?

    Those who think that getchar is a useful function may implement that
    natively in Awk. (It avoids external dependencies and all the issues
    that the posted/quoted code has made obvious.)


    I know you can iterate over input strings a character at a time in AWK
    but I don't think you can read a single character from stdin without
    also providing a newline via ENTER, which is perhaps what the OP was
    actually wanting to do?

    I kind of like making CLI interactive stuff and like not having to
    press ENTER after entering single character menu choices. Tried a
    few options using bash and lua before landing on stty(1) and dd(1),
    both POSIX tools:

    --
    # getchar_posix.awk - read exactly one char from stdin and return it
    # without waiting for ENTER to be pressed.
    #

    BEGIN {
    printf "enter a char: "
    Char = getchar()
    printf "\n you entered: %s\n", Char
    }

    function getchar( ,Cmd, Chr) {
    # put TTY in "raw" mode..
    system ("stty -icanon")
    #
    # read Chr via dd(1)..
    Cmd = "dd bs=1 count=1 2>/dev/null"
    Cmd | getline Chr
    close (Cmd)
    #
    # put TTY in "normal" mode..
    system ("stty icanon")
    #
    return Chr
    }
    --

    Saving the TTY state via 'stty -g' beforehand would probably be a good
    addition so if things go sideways the TTY isn't a mess.

    -A
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From gazelle@gazelle@shell.xmission.com (Kenny McCormack) to comp.lang.awk on Wed Mar 26 05:08:56 2025
    From Newsgroup: comp.lang.awk

    In article <vrvv30$spj$1@nnrp.usenet.blueworldhosting.com>,
    Arti F. Idiot <addr@is.invalid> wrote:
    On 3/25/25 7:10 AM, Janis Papanagnou wrote:
    On 25.03.2025 10:51, anthk wrote:
    Hello the guy from https://luxferre.top and gopher://hoi.st
    has pretty interesting stuff, such as

    [ shell specific or other tries to emulate some getchar function ]

    Could it be possible to implement a true portable getchar?

    Those who think that getchar is a useful function may implement that
    natively in Awk. (It avoids external dependencies and all the issues
    that the posted/quoted code has made obvious.)


    I know you can iterate over input strings a character at a time in AWK
    but I don't think you can read a single character from stdin without
    also providing a newline via ENTER, which is perhaps what the OP was
    actually wanting to do?

    This works for me:

    --- Cut Here ---
    @load "call_any"
    BEGIN {
    system("stty raw -echo");
    s = " "
    n = call_any("iisi","read",0,s,1)
    system("stty sane");
    print "s =","|"s"|","n =",n
    }
    --- Cut Here ---

    Note: I could probably change the system() calls to use call_any() as well.

    It looks like tcgetattr(3) and tcsetattr(3) are the currently accepted ways
    of doing this. I've used those calls in my C programs, so would have to
    adapt that to work with call_any().
    --
    Liberals live in a fantasy world where (street) criminals are good people.

    Conservatives live in a fantasy world where businessmen are good people.
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Janis Papanagnou@janis_papanagnou+ng@hotmail.com to comp.lang.awk on Wed Mar 26 06:27:52 2025
    From Newsgroup: comp.lang.awk

    On 26.03.2025 05:16, Arti F. Idiot wrote:
    On 3/25/25 7:10 AM, Janis Papanagnou wrote:
    On 25.03.2025 10:51, anthk wrote:
    Hello the guy from https://luxferre.top and gopher://hoi.st
    has pretty interesting stuff, such as

    [ shell specific or other tries to emulate some getchar function ]

    Could it be possible to implement a true portable getchar?

    Those who think that getchar is a useful function may implement that
    natively in Awk. (It avoids external dependencies and all the issues
    that the posted/quoted code has made obvious.)

    I know you can iterate over input strings a character at a time in AWK
    but I don't think you can read a single character from stdin without
    also providing a newline via ENTER, which is perhaps what the OP was
    actually wanting to do?

    I don't know. - When I read 'getchar' I associated a character oriented function like awk's 'getline'. That would mean not reading from stdin
    with some I/O buffered return-terminated input but just processing the
    data as Awk would read its input from stdin or using 'getline' would.
    You need to maintain some state, though; here the actual read in line
    which acts like a buffer in buffered OS reads.

    What I associated was actually something like

    function getchar ()
    {
    if (_pos >= _len) {
    do {
    if ((getline _line) <=0)
    return ""
    }
    while (!(_len = length (_line)))
    _pos = 0
    }
    return substr (_line, ++_pos, 1)
    }

    used in contexts like

    BEGIN {
    # RS = "$^"
    while (c = getchar())
    print ">" c "<"
    }

    where the commented RS assignment could be activated in case you wanted
    to also read the newline characters, or, without it, to just read in
    the payload data of a line (or record). The necessary state information
    is stored in those variables that are named with a leading underscore.

    As said it's native Awk code without those dependencies on OS, on tools,
    or on specific non-standard flags of tools.

    (But as indicated in my previous response, I don't have any need for a
    function like that. Maybe others do, don't know.)

    Janis

    [...]

    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From gazelle@gazelle@shell.xmission.com (Kenny McCormack) to comp.lang.awk on Wed Mar 26 05:48:06 2025
    From Newsgroup: comp.lang.awk

    In article <vs0258$ncqh$1@news.xmission.com>,
    Kenny McCormack <gazelle@shell.xmission.com> wrote:
    ...
    This also works for me:

    --- Cut Here ---
    BEGIN {
    cmd = "exec bash -c 'while :;do read -rsn1 < /dev/tty;echo $REPLY;done'"
    while (cmd |& getline)
    print "Result:",$0
    }
    --- Cut Here ---
    --
    The randomly chosen signature file that would have appeared here is more than 4 lines long. As such, it violates one or more Usenet RFCs. In order to remain in compliance with said RFCs, the actual sig can be found at the following URL:
    http://user.xmission.com/~gazelle/Sigs/Voltaire
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From gazelle@gazelle@shell.xmission.com (Kenny McCormack) to comp.lang.awk on Wed Mar 26 06:07:19 2025
    From Newsgroup: comp.lang.awk

    In article <vs038p$12n0d$1@dont-email.me>,
    Janis Papanagnou <janis_papanagnou+ng@hotmail.com> wrote:
    ...
    I don't know. - When I read 'getchar' I associated a character oriented >function like awk's 'getline'. That would mean not reading from stdin
    with some I/O buffered return-terminated input but just processing the
    data as Awk would read its input from stdin or using 'getline' would.
    You need to maintain some state, though; here the actual read in line
    which acts like a buffer in buffered OS reads.

    It is an interesting question.

    In strict C terms, getchar() is well-known, and well-defined and is pretty
    much as you describe it. But in an AWK context, that sort of function
    doesn't seem like it would ever be useful, so we should probably assume
    that what most of the posters on the thread are talking about is something
    more akin to getch() - which is what I assumed in my two posted solutions.

    Now, in old languages, like Turbo C and Turbo Pascal, there were generally
    two function with similar names): getchar() - which behaved as Janis
    suggests and is there primarily for standards conformance and getch() -
    which function as as I describe above. Note that in a Unix/Linux context, doing single char input requires messing with TTY modes (which is ugly), whereas in DOS/Windows, it comes pretty much "for free".
    --
    Nov 4, 2008 - the day when everything went
    from being Clinton's fault to being Obama's fault.
    --- Synchronet 3.20c-Linux NewsLink 1.2
  • From Kaz Kylheku@643-408-1753@kylheku.com to comp.lang.awk on Wed Mar 26 06:10:21 2025
    From Newsgroup: comp.lang.awk

    On 2025-03-26, Kenny McCormack <gazelle@shell.xmission.com> wrote:
    that what most of the posters on the thread are talking about is something more akin to getch() - which is what I assumed in my two posted solutions.

    #include <cornholio.h>
    --
    TXR Programming Language: http://nongnu.org/txr
    Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
    Mastodon: @Kazinator@mstdn.ca
    --- Synchronet 3.20c-Linux NewsLink 1.2