• Re: C90 fpeek

    From Scott Lurndal@21:1/5 to Keith Thompson on Fri Jan 24 14:05:58 2025
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    "Paul Edwards" <mutazilah@gmail.com> writes:
    [...]
    With the benefit of hindsight, is there any reason why fpeek
    couldn't have been added to C90, with implementations
    being allowed with just a macro that returns some sort of
    "unsupported"?

    If fpeek (or similar) makes sense, can someone suggest an
    appropriate interface?
    [...]

    It would help to know what "fpeek" is supposed to do. There no such
    function in any edition of the C standard or in any implementation
    that I'm aware of.

    And indeed, giving the default buffering in stdio, the concept of
    peek with respect to a serial port doesn't make a whole lot of
    sense. Note that 'getc()'/'ungetc()' is effectively a peek
    operation.

    I noticed linux has an 'peekfd' command which looks like
    an interesting debug tool, with the caveat:

    BUGS
    Probably lots. Don't be surprised if the process you are monitoring dies.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From James Kuyper@21:1/5 to Keith Thompson on Fri Jan 24 09:24:35 2025
    On 1/24/25 00:13, Keith Thompson wrote:
    "Paul Edwards" <mutazilah@gmail.com> writes:
    [...]
    With the benefit of hindsight, is there any reason why fpeek
    couldn't have been added to C90, with implementations
    being allowed with just a macro that returns some sort of
    "unsupported"?

    If fpeek (or similar) makes sense, can someone suggest an
    appropriate interface?
    [...]

    It would help to know what "fpeek" is supposed to do. There no such
    function in any edition of the C standard or in any implementation
    that I'm aware of.

    A google search uncovered a stackoverflow question for which the answer was:

    int fpeek(FILE *stream)
    {
    int c;

    c = fgetc(stream);
    ungetc(c, stream);

    return c;
    }

    I don't see any reason why such a function is needed in the standard
    library. However, if it were added, since fgetc() and ungetc() are
    mandatory for hosted implementations, I also see no reason to allow for
    it to be unsupported.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Michael S@21:1/5 to James Kuyper on Fri Jan 24 16:49:54 2025
    On Fri, 24 Jan 2025 09:24:35 -0500
    James Kuyper <jameskuyper@alumni.caltech.edu> wrote:

    On 1/24/25 00:13, Keith Thompson wrote:
    "Paul Edwards" <mutazilah@gmail.com> writes:
    [...]
    With the benefit of hindsight, is there any reason why fpeek
    couldn't have been added to C90, with implementations
    being allowed with just a macro that returns some sort of
    "unsupported"?

    If fpeek (or similar) makes sense, can someone suggest an
    appropriate interface?
    [...]

    It would help to know what "fpeek" is supposed to do. There no such function in any edition of the C standard or in any implementation
    that I'm aware of.

    A google search uncovered a stackoverflow question for which the
    answer was:

    int fpeek(FILE *stream)
    {
    int c;

    c = fgetc(stream);
    ungetc(c, stream);

    return c;
    }

    I don't see any reason why such a function is needed in the standard
    library. However, if it were added, since fgetc() and ungetc() are
    mandatory for hosted implementations, I also see no reason to allow
    for it to be unsupported.

    It can be supported, but not useful for OP's purouses without ability
    to set file to O_NONBLOCK. Which, I would think, is outside of C
    standard.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Scott Lurndal on Fri Jan 24 19:48:59 2025
    On 2025-01-24, Scott Lurndal <scott@slp53.sl.home> wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    "Paul Edwards" <mutazilah@gmail.com> writes:
    [...]
    With the benefit of hindsight, is there any reason why fpeek
    couldn't have been added to C90, with implementations
    being allowed with just a macro that returns some sort of
    "unsupported"?

    If fpeek (or similar) makes sense, can someone suggest an
    appropriate interface?
    [...]

    It would help to know what "fpeek" is supposed to do. There no such >>function in any edition of the C standard or in any implementation
    that I'm aware of.

    And indeed, giving the default buffering in stdio, the concept of
    peek with respect to a serial port doesn't make a whole lot of
    sense.

    It absolutely does; have you never done a poll() or select() on a tty
    file descriptor?

    The argument could be made to have a poll-like function in C,
    that works with FILE * streams.

    I could use such a thing in POSIX programs. Working with stdio streams
    while doing multiplexing of real-time I/O though them onto a single
    thread is a bit ugly.

    Note that 'getc()'/'ungetc()' is effectively a peek
    operation.

    Nope, because getc will block when there is no data.

    Unless you non-portably arranged otherwise. E.g. on POSIX
    we can get the fileno(stream), and use fcntl to set up O_NONBLOCK.
    Then get(stream) returns EOF, with errno set to EWOULDBLOCK
    and we whave to clearerr(stream), then poll the fd, and so it goes.

    Been there done that. Went back there, done that again,
    and then several more times, like a raging masochist.

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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Paul Edwards on Fri Jan 24 19:38:10 2025
    On 2025-01-24, Paul Edwards <mutazilah@gmail.com> wrote:
    "Keith Thompson" <Keith.S.Thompson+u@gmail.com> wrote in message news:87plkc6bgm.fsf@nosuchdomain.example.com...
    "Paul Edwards" <mutazilah@gmail.com> writes:
    [...]
    With the benefit of hindsight, is there any reason why fpeek
    couldn't have been added to C90, with implementations
    being allowed with just a macro that returns some sort of
    "unsupported"?

    If fpeek (or similar) makes sense, can someone suggest an
    appropriate interface?
    [...]

    It would help to know what "fpeek" is supposed to do. There no such
    function in any edition of the C standard or in any implementation
    that I'm aware of.

    fpeek would tell you whether there are any characters available
    to be read, on a bidirectional data stream, opened with r+b or
    whatever.

    This functionality went into POSIX, which can be regarded
    as a larger version of the C language with a lot more operating
    system functionality, and lower portability (POSIX programs
    require a POSIX C implementation, not just a C implementation).

    C came from Unix, and was accompanied by a library of functions
    like malloc, printf, open, read, write.

    When a standardization effort was launched, two groups were formed:
    one of the language and one for the OS.

    The language one took the closer-to-the-language things like
    printf and malloc and fopen.

    The OS group took the system level things like open, read, write.

    The fpeek function you're looking for can be writen in POSIX
    as a combination of fileno (to obtain the integer file descriptor
    from a FILE * stream) and a polling function like poll or select,
    executed with a zero timeout.

    Moreover, care has to be taken not to perform this test on
    a stdio stream which itself has unread bytes in its won buffer;
    it only makes sense when all bytes in the stream's buffer
    have been removed.

    The proper technique to use FILE * streams together with select/poll
    based multiplexing is to either use unbuffered I/O, or else
    non-blocking I/O.

    In the PipeWatch program, I demonstrate the use of both in the
    same event loop:

    https://www.kylheku.com/cgit/pw/tree/pw.c

    The program simultaneously reads interactive TTY input, and
    updates a full-screen display, while also processing bulk data from
    standard input. In case standard input is a pipe, it is set to
    nonblocking mode (look for O_NONBLOCK). For the TTY, rather than
    nonblocking mode, we set the standard I/O stream to unbuffered via
    setvbuf.

    When you put a file descriptor into nonblocking mode, then whenever the standard I/O stream above it runs out of bytes, and the descriptor
    doesn't have any, the I/O stream experiences an error, which it
    translates to (for instance) an EOF return from getc(stream). errno
    indicates EWOULDBLOCK. At that point you know you can use the poll
    function to monitor the descriptor for the availability data. You have
    remember to clearerr(stream) to remove the error state from the stdio
    stream before retrying the input operation when the descriptor
    indicates that it has data.

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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Paul Edwards on Fri Jan 24 19:18:15 2025
    On 2025-01-24, Paul Edwards <mutazilah@gmail.com> wrote:
    I am able to open COM1: with C90 fopen and do a zmodem
    file transfer on the stream.

    So long as it is an error-free environment, I can switch between
    reading and writing so long as I do the C90-required fseek. I
    simply fseek by 0 from SEEK_CUR.

    However, if there is line noise, it is unpredictable when there
    will be a NAK coming down the line.

    So what I would like to do is fseek of 0, then fpeek(stream),
    and if it says there is data available, I read it. On streams where
    peeking is not available, it does an appropriate return, and I
    rely on it being an error-free environment (as now, ie I'm no
    worse off).

    C stdio lets you read one character and then put it back
    into the stream with ungetc.

    There is no non-blocking I/O.

    There is no way to detect whether an input stream has
    buffered data that may be read immediately without waiting.

    Beyond the buffer, there is no way to detect whether data has arrived
    from the outside environment into the host system, such that a
    subsequent operation that needs to ask the host for data in order to
    replenish the stream buffer will not block.

    With the benefit of hindsight, is there any reason why fpeek
    couldn't have been added to C90, with implementations
    being allowed with just a macro that returns some sort of
    "unsupported"?

    For C90, part of the answer would be that the committe
    was in codifying-existing-practice mode, and not so much
    in invent-new-cruft mode.

    There was no fpeek out in the wild to standardize.

    ANSI C saw itself as standardizing the portable parts of
    the C language as it came from Unix. Operating system
    stuff was left to the POSIX group.

    POSIX provides a way to access the underlying file descriptor
    of a stream: via int fileno(FILE *).

    There are ways of detecting unread data, but they are device
    specific. It's a bit of a mess.

    If fpeek (or similar) makes sense, can someone suggest an
    appropriate interface?

    Before sending a NAK I probably want to do an fdiscard
    of the input stream too. But again with no guarantees that
    the data will be discarded, and my protocol needs to allow
    for that.

    What does discard mean? Without any knowledge of how much data is
    buffered, in what places, and precise control over what is discarded,
    it's just a hand-wavy operation.

    Discarding an unknown amount of buffered input is counterproductive in
    the face of a streaming or sliding window type protocol!

    You're likely discarding a good packet (or portion thereof) that is
    coming on the heels of the bad one you are NAKing.

    The receiver shoold read everything and make the best out of every byte.

    What you need for a proper Zmodem implementation isn't "fpeek" but a
    good measure of concurrency or some facsimile thereof. The sender has to
    keep transmitting data nonstop, while receiving and responding to NAKs.

    In the POSIX world, you could do this in a single thread with the select
    or poll functions. These functions take multiple open file descriptors
    as inputs and return an indication whether the desired conditions are
    true, like data being avaialble in input descriptors, or output
    descriptors having space for more data. These functions can block while
    none of the conditions are satisifed.

    The problem can be solved with threads also (which C now has as of C11).

    You may be able to open two FILE * descriptors on the serial port,
    one for reading and one for writing. Have a dedicated thread which
    reads the responses, looking for NAKs, and a decidated thread
    for sending. The two can coordinate their actitivies. The sender
    can have a queue of what to send next, and NAK processing can
    push a retransmit item that queue.

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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Scott Lurndal@21:1/5 to Kaz Kylheku on Fri Jan 24 22:08:11 2025
    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    On 2025-01-24, Scott Lurndal <scott@slp53.sl.home> wrote:
    Keith Thompson <Keith.S.Thompson+u@gmail.com> writes:
    "Paul Edwards" <mutazilah@gmail.com> writes:
    [...]
    With the benefit of hindsight, is there any reason why fpeek
    couldn't have been added to C90, with implementations
    being allowed with just a macro that returns some sort of
    "unsupported"?

    If fpeek (or similar) makes sense, can someone suggest an
    appropriate interface?
    [...]

    It would help to know what "fpeek" is supposed to do. There no such >>>function in any edition of the C standard or in any implementation
    that I'm aware of.

    And indeed, giving the default buffering in stdio, the concept of
    peek with respect to a serial port doesn't make a whole lot of
    sense.

    It absolutely does; have you never done a poll() or select() on a tty
    file descriptor?

    Hundreds of times over the last 35 years. Never on a buffered stdio
    stream for which poll is basically useless. And always with O_NONBLOCK set
    on the file descriptor (from open, not fopen+fileno()), usually with
    the underlying tty or pty set to so-called 'raw' mode.

    I don't recall ever using stdio streams myself for any real world
    problem - for terminal input I generally use libreadline or libedit;
    for serial port input/output, open + tcsetattr + fcntl (usually with
    O_NOCTTY) + read or write.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Scott Lurndal on Sat Jan 25 00:50:14 2025
    On 2025-01-24, Scott Lurndal <scott@slp53.sl.home> wrote:
    Kaz Kylheku <643-408-1753@kylheku.com> writes:
    It absolutely does; have you never done a poll() or select() on a tty
    file descriptor?

    Hundreds of times over the last 35 years. Never on a buffered stdio
    stream for which poll is basically useless.

    It totally works, if done right.

    And always with O_NONBLOCK set
    on the file descriptor (from open, not fopen+fileno()), usually with
    the underlying tty or pty set to so-called 'raw' mode.

    You can fdopen that that, fread or getchar until EOF + errno ==
    EWOULDBLOCK, then poll the fileno. Clear the error state with
    clearerr and off you go: you can read from the stream to refill
    its buffer and get bytes.

    It can be very useful!

    In any case, if you ever find yourself building a little buffering layer
    over a file descriptor, it's something to think about: could the
    requirements just be satisfied with old stdio?

    Just because the requirements for situations like timed out reads, or multiplexing multiple buffered streams with one thread --- that does not
    pull stdio off the table!

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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Kaz Kylheku@21:1/5 to Chris M. Thomasson on Sat Jan 25 00:52:14 2025
    On 2025-01-24, Chris M. Thomasson <chris.m.thomasson.1@gmail.com> wrote:
    On 1/24/2025 11:48 AM, Kaz Kylheku wrote:
    Unless you non-portably arranged otherwise. E.g. on POSIX
    we can get the fileno(stream), and use fcntl to set up O_NONBLOCK.
    Then get(stream) returns EOF, with errno set to EWOULDBLOCK
    and we whave to clearerr(stream), then poll the fd, and so it goes.

    Been there done that. Went back there, done that again,
    and then several more times, like a raging masochist.

    Why not use AIO?

    In conjunction with stdio buffering? It doesn't seem possible; you have
    to go through alternative functions like aio_read, which stdio doesn't interface with.

    A stdio implementation that uses aio_read and friends under the hood
    might be interesting.

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

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Paul Edwards on Sat Jan 25 23:55:58 2025
    On Sat, 25 Jan 2025 10:58:32 +1100, Paul Edwards wrote:

    You could argue that if I'm willing to add ANSI X3.64,
    why not also add C23 and POSIX and ...

    I don't have a good answer to that, other than I'm trying to keep
    movement away from C90 to a minimum.

    Let me suggest a more reasonable baseline for code that is to be minimally relevant to this century: C99 + POSIX.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Chris M. Thomasson on Sat Jan 25 23:53:07 2025
    On Fri, 24 Jan 2025 14:14:26 -0800, Chris M. Thomasson wrote:

    Why not use AIO?

    Or, for higher performance (and Linux-specific), how about io_uring.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Lawrence D'Oliveiro@21:1/5 to Michael S on Sat Jan 25 23:54:29 2025
    On Fri, 24 Jan 2025 16:49:54 +0200, Michael S wrote:

    ... not useful for OP's purouses without ability to
    set file to O_NONBLOCK. Which, I would think, is outside of C standard.

    Every week or two, it seems, another example appears of how boring
    standard C is, without the help of a POSIX layer underneath.

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)