Discussion:
C90 fpeek
Add Reply
Paul Edwards
2025-01-24 03:08:44 UTC
Reply
Permalink
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).

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?

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.

Thanks. Paul.



P.S. zpg.zip (z/PDOS-generic) from https://pdos.org is now
self-hosting - gccmvs (gcc 3.2.3) can rebuild itself, byte-exact.
It runs under Hercules/380 (herc32.zip or herc64.zip) and
microemacs is provided too. This is for an IBM mainframe and
it uses EBCDIC (including a FAT32 file system in EBCDIC).
I'm starting preparations to dial a BBS (using Virtualbox to
link COM1 to an IP address) to talk to the world.
Keith Thompson
2025-01-24 05:13:29 UTC
Reply
Permalink
"Paul Edwards" <***@gmail.com> writes:
[...]
Post by Paul Edwards
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.
--
Keith Thompson (The_Other_Keith) Keith.S.Thompson+***@gmail.com
void Void(void) { Void(); } /* The recursive call of the void */
Paul Edwards
2025-01-24 05:41:39 UTC
Reply
Permalink
Post by Keith Thompson
[...]
Post by Paul Edwards
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.

For an ordinary disk file, it wouldn't necessarily do anything,
and just return with "unknown" or whatever, for the application
to decide what to do.

But for a serial port/modem connected to a BBS, the infrastructure
would likely already know if there are characters sitting at the COM
port (or some corresponding buffer), so it would be in a position to
confirm/deny the presence of pending characters to be read.

It also occurred to me that games could use this, fpeek of stdin to
see if a key has been pressed.

Apologies for not making this clearer in the original post.

BFN. Paul.
Kaz Kylheku
2025-01-24 19:38:10 UTC
Reply
Permalink
Post by Paul Edwards
Post by Keith Thompson
[...]
Post by Paul Edwards
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: @***@mstdn.ca
Paul Edwards
2025-01-24 21:15:20 UTC
Reply
Permalink
Post by Kaz Kylheku
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.
That's my point though - I'm after a solution from the language
side. I'm not interested in just one OS (POSIX).

Someone mentioned threads in C23 - but I assume that is a
massive overhead - if possible at all in an environment like
MSDOS.

fpeek() I can envisage happening in MSDOS. An appropriate
C library could query the UART to see if there is a character
pending. Or perhaps a fairly slightly modified MSDOS could
do that. Or a serial driver could independently read from the
UART and construct a buffer.

Note that having an fpeek would allow me to cope with
buffer overruns on an error-free link that lacks flow control.
However, it alone won't cope with line noise that garbles
a ZNAK response, as both ends would be blocked on
reading in that case.

But - and this is unusual - I'm happy to live with that. I'm not
after a perfect solution, I'm after a simple solution that in this
case assumes an error-free connection (ie I'm using an MNP4
modem or it's really TCP/IP or whatever).

However, I am still mulling over whether I could handle a
garbled ZNAK, by having the sender repeatedly send XON,
going back to do an fpeek, until either the ZNAK is completed,
or the receiver realizes that the ZNAK was likely lost, and
sends another.

Another unusual thing - I'm willing to change the zmodem protocol.
The work that I am doing is all with the benefit of hindsight, and
I'm (sort of) not trying to run existing applications. Even if I am, I
for example forked gcc 3.2.3 and removed all the POSIX stuff
to create a C90 application (ie a lot of work).

If I can't have an fpeek as a C90 extension - so be it - I just say
that you can't have buffer overruns - it must be error free, and
speed limited so that the receiver isn't overwhelmed. Otherwise
both systems need to be rebooted or whatever in order to
continue working.

Another unusual thing - this is to create a simple "starter system".
It's not meant to be the last word in file transfers or anything else.
But I do expect to have the tools required to build "the world's
greatest OS", so I provide a C compiler, and I do want to be
able to collaborate electronically instead of having to send a
USB stick or floppy disk in the post. If that means the stars
need to be aligned for a zmodem transfer to go through - so be it -
the alternative isn't a modern 2025 system - the alternative is
carrier pigeons (literally in fact - I do envisage a Fidonet or UUCP
system running without the internet, and pigeons being exchanged
at schools - possibly schools in Bhutan - see the reference in
Sneakernet wikipedia) - but I'm not there yet).

Another option I have is that on the first ZNAK - or any line noise
in fact - I switch to requiring ACKs.

Note that I can't really properly verbalize what I want to do. I see
the C90 standard and I like it. I'm tempted to say "for its simplicity",
but without much of a reference point, I don't know that that is
correct. E.g. I think it is odd that sin() etc were added, as they are
not required for base functionality (writing a C compiler). So I might
be looking at a stripped down C90 - while still adding extensions.
I don't want timing in my applications. I don't want multithreading.
It just "seems wrong" (rightly or wrongly). The C90 people
certainly didn't add such things - perhaps they had the same vague
"feelings", or perhaps they explained why in the ANSI Rationale
which I haven't yet read.

But an fpeek which is allowed to fail, is something that doesn't
make me uncomfortable right at the moment - rightly or wrongly.

Back to mulling zmodem extensions - if the ZNAK may not
have gotten through, I don't want to send another because
timing issues (especially communicating with Mars) may mean
I am NAKing a good packet. I may want to switch to ACKing
the last good packet. Or perhaps non-blocking ACKing.

BFN. Paul.
Scott Lurndal
2025-01-24 14:05:58 UTC
Reply
Permalink
Post by Keith Thompson
[...]
Post by Paul Edwards
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.
Kaz Kylheku
2025-01-24 19:48:59 UTC
Reply
Permalink
Post by Scott Lurndal
Post by Keith Thompson
[...]
Post by Paul Edwards
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.
Post by Scott Lurndal
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: @***@mstdn.ca
Scott Lurndal
2025-01-24 22:08:11 UTC
Reply
Permalink
Post by Kaz Kylheku
Post by Scott Lurndal
Post by Keith Thompson
[...]
Post by Paul Edwards
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.
Paul Edwards
2025-01-24 23:58:32 UTC
Reply
Permalink
Post by Scott Lurndal
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.
And this is the difference I guess.

I am not trying to solve a particular real world problem that
an employer is paying for.

I am trying to DEFINE a world based on nothing but C90.
You could possibly call it a "limited world" rather than a
"real world". I'm not expecting commercial success with
this position.

However, I do recognize that the C90 committee (plus
existing practice) were not perfect in the late 1980s. So
with the benefit of hindsight, and without time pressure,
I'm willing to revisit C90.

In order to get a fullscreen editor I didn't see any good
option but to add ANSI X3.64 and an EBCDIC equivalent.

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.

I've mentioned before I need an ESC_STR and likely
other control characters to extend C90 (on top of
ANSI X3.64) to avoid having to hardcode hex values
in my applications.

I'm happy to remove sin() etc.

And now I'm looking at an fpeek().

And that "looks neat" to me. I realize I can't mathematically
prove that it is neat (and that irks me), but that's the world
that I want to create (not particularly expecting anyone to
join me in that world - not expecting to get any money from
it - but I want it to exist anyway - a "roughly C90" world).

Note that z/PDOS-generic is a great start (in my opinion) -
if the mainframe can operate in a C90 world, most other
environments should be able to as well.

BFN. Paul.
Lawrence D'Oliveiro
2025-01-25 23:55:58 UTC
Reply
Permalink
Post by Paul Edwards
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.
Paul Edwards
2025-01-27 05:57:20 UTC
Reply
Permalink
Post by Lawrence D'Oliveiro
Post by Paul Edwards
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.
I am not attempting to satisfy your definition of
"minimally relevant to this century".

I'm attempting to construct that world of C90 that you called
boring in another message.

The only real difference is that I don't have an expectation that
the C89/C90 committees were faultless (or had the ability to
be faultless), or that existing practice was faultless - or had the
ability to be faultless - or had the time to be faultless - or had
the hindsight required to be faultless - so I am expecting some
minimal movement away from C90 for the things that weren't
incorporated at the time, but probably would have if there had
been some more data available.

I'm not claiming to have all the data available now, but I do have
some data available now - an entire toolchain and OS in standard
C90.

It is the editor, and file transfer, which I can't do in standard C90.

It's pretty close though, and I think it is reasonable to make some
slight adjustments to C90 based on the shortfall.

BFN. Paul.

Kaz Kylheku
2025-01-25 00:50:14 UTC
Reply
Permalink
Post by Scott Lurndal
Post by Kaz Kylheku
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.
Post by Scott Lurndal
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: @***@mstdn.ca
Chris M. Thomasson
2025-01-24 22:14:26 UTC
Reply
Permalink
Post by Kaz Kylheku
Post by Scott Lurndal
Post by Keith Thompson
[...]
Post by Paul Edwards
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.
Post by Scott Lurndal
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.
Why not use AIO?
Kaz Kylheku
2025-01-25 00:52:14 UTC
Reply
Permalink
Post by Chris M. Thomasson
Post by Kaz Kylheku
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: @***@mstdn.ca
Chris M. Thomasson
2025-01-25 00:54:42 UTC
Reply
Permalink
Post by Kaz Kylheku
Post by Chris M. Thomasson
Post by Kaz Kylheku
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.
Touche!
Post by Kaz Kylheku
A stdio implementation that uses aio_read and friends under the hood
might be interesting.
I think it just might be interesting. Thanks.
Lawrence D'Oliveiro
2025-01-25 23:53:07 UTC
Reply
Permalink
Post by Chris M. Thomasson
Why not use AIO?
Or, for higher performance (and Linux-specific), how about io_uring.
James Kuyper
2025-01-24 14:24:35 UTC
Reply
Permalink
Post by Keith Thompson
[...]
Post by Paul Edwards
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.
Michael S
2025-01-24 14:49:54 UTC
Reply
Permalink
On Fri, 24 Jan 2025 09:24:35 -0500
Post by James Kuyper
Post by Keith Thompson
[...]
Post by Paul Edwards
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.
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.
Lawrence D'Oliveiro
2025-01-25 23:54:29 UTC
Reply
Permalink
... 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.
Kaz Kylheku
2025-01-24 19:18:15 UTC
Reply
Permalink
Post by Paul Edwards
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.
Post by Paul Edwards
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.
Post by Paul Edwards
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: @***@mstdn.ca
Loading...