Discussion:
popen() and r+
(too old to reply)
M***@dastardlyhq.com
2023-01-09 09:43:37 UTC
Permalink
Has anyone ever used popen() with the r+ option on MacOS? Writing to the file
descriptor works fine (the output goes to the printer queue) but any attempt to
read the response from CUPS from the file descriptor simply hangs. eg:

if ((pfile = popen("lp","r+")))
{
... write data to printer ...
fgets(str,sizeof(str),pfile); <- hangs here
}

It also hangs if I try while(!feof(pfile)) or read(fileno(pfile),....)

lp is producing output and I can redirect to a file , eg popen("lp > myfile",..)
I assume this is some kind of buffer flush issue but I can't find anything on
google about it.

Thanks for any help.
Kaz Kylheku
2023-01-09 11:20:09 UTC
Permalink
Post by M***@dastardlyhq.com
Has anyone ever used popen() with the r+ option on MacOS? Writing to the file
Coprocesses are tricky; it's easy to get into a deadlock if you just
read and write with one thread, without multiplexing the I/O using
poll or select.
Post by M***@dastardlyhq.com
descriptor works fine (the output goes to the printer queue) but any attempt to
if ((pfile = popen("lp","r+")))
{
... write data to printer ...
Did you fflush(pfile) here? Maybe all the data didn't go.
I think popen returns streams that are in fully buffered mode,
so output is only flushed when a buffer fills, the stream is
closed or fflush is called.

You would think that a simple half-duplex situation like this would
avoid deadlock, since lp should read your entire request until the end,
and only then produce a response.

However, if some trailing part of your request is sitting in a buffer,
such that lp is waiting for that last bit before replying, then you have
a deadlock.

(Note that ISO C itself requires a flush operation when switching from
output to input on a bidirectional stream, as a matter of well-defined
behavior. When switching from input to output, a file positioning
operation is required (regardless of whether the stream supports
positioning; seeking zero bytes from the current position with
fseek should do the trick). I think the main reason for this is that
some library implementations share one buffer between input
and output.)
Post by M***@dastardlyhq.com
fgets(str,sizeof(str),pfile); <- hangs here
}
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
M***@dastardlyhq.com
2023-01-09 11:30:22 UTC
Permalink
On Mon, 9 Jan 2023 11:20:09 -0000 (UTC)
Post by Kaz Kylheku
Post by M***@dastardlyhq.com
Has anyone ever used popen() with the r+ option on MacOS? Writing to the file
Coprocesses are tricky; it's easy to get into a deadlock if you just
read and write with one thread, without multiplexing the I/O using
poll or select.
Post by M***@dastardlyhq.com
descriptor works fine (the output goes to the printer queue) but any attempt
to
Post by M***@dastardlyhq.com
if ((pfile = popen("lp","r+")))
{
... write data to printer ...
Did you fflush(pfile) here? Maybe all the data didn't go.
No I didn't. Just tried it - didn't make any difference sadly.
Post by Kaz Kylheku
You would think that a simple half-duplex situation like this would
avoid deadlock, since lp should read your entire request until the end,
and only then produce a response.
However, if some trailing part of your request is sitting in a buffer,
such that lp is waiting for that last bit before replying, then you have
a deadlock.
Oddly if popened in write only mode the output from lp is written to stdout
without any delay and if stdout is redirected to a file, eg:

popen("lp > myfile","w")

that happily writes to the file.
Post by Kaz Kylheku
(Note that ISO C itself requires a flush operation when switching from
output to input on a bidirectional stream, as a matter of well-defined
behavior. When switching from input to output, a file positioning
operation is required (regardless of whether the stream supports
positioning; seeking zero bytes from the current position with
fseek should do the trick). I think the main reason for this is that
Unfortunately not:

if (fseek(pfile,0,SEEK_SET) == -1) perror("fseek()");

results in

fseek(): Illegal seek

Perhaps the popen() implementation on MacOS/BSD is just broken. I'll have to
try it on Linux when I get a chance later.
Nicolas George
2023-01-09 12:01:35 UTC
Permalink
Post by Kaz Kylheku
However, if some trailing part of your request is sitting in a buffer,
such that lp is waiting for that last bit before replying, then you have
a deadlock.
I would guess it is not enough: lp is probably waiting for EOF, and there is
no way to close only half of a popen stream.
M***@dastardlyhq.com
2023-01-09 15:56:26 UTC
Permalink
On 09 Jan 2023 12:01:35 GMT
Post by Nicolas George
Post by Kaz Kylheku
However, if some trailing part of your request is sitting in a buffer,
such that lp is waiting for that last bit before replying, then you have
a deadlock.
I would guess it is not enough: lp is probably waiting for EOF, and there is
no way to close only half of a popen stream.
Surely if you're going to implement r+ functionality you'd make sure you
flushed the write buffer before the close so this issue doesn't occur?
Nicolas George
2023-01-09 16:09:18 UTC
Permalink
Post by M***@dastardlyhq.com
Post by Nicolas George
I would guess it is not enough: lp is probably waiting for EOF, and there is
no way to close only half of a popen stream.
Surely if you're going to implement r+ functionality you'd make sure you
flushed the write buffer before the close so this issue doesn't occur?
The issue is not about an unflushed buffer, it is about a stream that is
still open and therefore could receive more data. Consider this:

f = popen("some_command", "r+");
fprintf(f, "Hello.\n");
fflush(f);
fgets(line, f);

How does some_command know you are done talking after "Hello."?
M***@dastardlyhq.com
2023-01-09 16:59:30 UTC
Permalink
On 09 Jan 2023 16:09:18 GMT
Post by Nicolas George
Post by M***@dastardlyhq.com
Post by Nicolas George
I would guess it is not enough: lp is probably waiting for EOF, and there is
no way to close only half of a popen stream.
Surely if you're going to implement r+ functionality you'd make sure you
flushed the write buffer before the close so this issue doesn't occur?
The issue is not about an unflushed buffer, it is about a stream that is
f = popen("some_command", "r+");
fprintf(f, "Hello.\n");
fflush(f);
fgets(line, f);
How does some_command know you are done talking after "Hello."?
Fair point. So clearly a 2 way popen isn't going to work for utilities that
don't respond until they get an EOF on their input. Unless there's some way to
spoof that.
Kenny McCormack
2023-01-09 18:31:22 UTC
Permalink
Post by Nicolas George
Post by M***@dastardlyhq.com
Post by Nicolas George
I would guess it is not enough: lp is probably waiting for EOF, and there is
no way to close only half of a popen stream.
Surely if you're going to implement r+ functionality you'd make sure you
flushed the write buffer before the close so this issue doesn't occur?
The issue is not about an unflushed buffer, it is about a stream that is
f = popen("some_command", "r+");
fprintf(f, "Hello.\n");
fflush(f);
fgets(line, f);
How does some_command know you are done talking after "Hello."?
Right. The classic example of this is "sort". sort cannot possibly
generate any output until it is certain that no more input is coming.

I assume "lp" behaves likewise. It looks to me that the best solution is
as given by OP: redirect output to a file (in the shell command passed to
popen()), then parse that file after doing pclose().
--
If Jeb is Charlie Brown kicking a football-pulled-away, Mitt is a '50s
housewife with a black eye who insists to her friends the roast wasn't
dry.
Scott Lurndal
2023-01-09 18:44:08 UTC
Permalink
Post by Kenny McCormack
Post by Nicolas George
Post by M***@dastardlyhq.com
Post by Nicolas George
I would guess it is not enough: lp is probably waiting for EOF, and there is
no way to close only half of a popen stream.
Surely if you're going to implement r+ functionality you'd make sure you
flushed the write buffer before the close so this issue doesn't occur?
The issue is not about an unflushed buffer, it is about a stream that is
f = popen("some_command", "r+");
fprintf(f, "Hello.\n");
fflush(f);
fgets(line, f);
How does some_command know you are done talking after "Hello."?
Right. The classic example of this is "sort". sort cannot possibly
generate any output until it is certain that no more input is coming.
I assume "lp" behaves likewise. It looks to me that the best solution is
as given by OP: redirect output to a file (in the shell command passed to
popen()), then parse that file after doing pclose().
Or bite the bullet and use fork/exec and set up stdin and stdout
for the child appropriately with two stdio streams, one for read
and one for write.
Kenny McCormack
2023-01-09 21:13:49 UTC
Permalink
In article <YfZuL.38332$***@fx33.iad>,
Scott Lurndal <***@pacbell.net> wrote:
...
Post by Scott Lurndal
Post by Kenny McCormack
I assume "lp" behaves likewise. It looks to me that the best solution is
as given by OP: redirect output to a file (in the shell command passed to
popen()), then parse that file after doing pclose().
Or bite the bullet and use fork/exec and set up stdin and stdout
for the child appropriately with two stdio streams, one for read
and one for write.
Of course you can always do that. I took it as a parameter of the problem
that the goal was to stay inside the context of popen().
--
The difference between communism and capitalism?
In capitalism, man exploits man. In communism, it's the other way around.

- Daniel Bell, The End of Ideology (1960) -
M***@dastardlyhq.com
2023-01-10 09:54:20 UTC
Permalink
On Mon, 09 Jan 2023 18:44:08 GMT
Post by Scott Lurndal
Post by Kenny McCormack
Post by Nicolas George
Post by M***@dastardlyhq.com
Post by Nicolas George
I would guess it is not enough: lp is probably waiting for EOF, and there
is
Post by Kenny McCormack
Post by Nicolas George
Post by M***@dastardlyhq.com
Post by Nicolas George
no way to close only half of a popen stream.
Surely if you're going to implement r+ functionality you'd make sure you
flushed the write buffer before the close so this issue doesn't occur?
The issue is not about an unflushed buffer, it is about a stream that is
f = popen("some_command", "r+");
fprintf(f, "Hello.\n");
fflush(f);
fgets(line, f);
How does some_command know you are done talking after "Hello."?
Right. The classic example of this is "sort". sort cannot possibly
generate any output until it is certain that no more input is coming.
I assume "lp" behaves likewise. It looks to me that the best solution is
as given by OP: redirect output to a file (in the shell command passed to
popen()), then parse that file after doing pclose().
Or bite the bullet and use fork/exec and set up stdin and stdout
for the child appropriately with two stdio streams, one for read
and one for write.
Urgh. Yes I could but thats what one wants to avoid by using popen(). Its such
a faff to setup plus all the error checking whereas popen() is 1 line of code.
Scott Lurndal
2023-01-10 14:37:17 UTC
Permalink
Post by M***@dastardlyhq.com
On Mon, 09 Jan 2023 18:44:08 GMT
Post by Scott Lurndal
Post by Kenny McCormack
Post by Nicolas George
Post by M***@dastardlyhq.com
Post by Nicolas George
I would guess it is not enough: lp is probably waiting for EOF, and there
is
Post by Kenny McCormack
Post by Nicolas George
Post by M***@dastardlyhq.com
Post by Nicolas George
no way to close only half of a popen stream.
Surely if you're going to implement r+ functionality you'd make sure you
flushed the write buffer before the close so this issue doesn't occur?
The issue is not about an unflushed buffer, it is about a stream that is
f = popen("some_command", "r+");
fprintf(f, "Hello.\n");
fflush(f);
fgets(line, f);
How does some_command know you are done talking after "Hello."?
Right. The classic example of this is "sort". sort cannot possibly
generate any output until it is certain that no more input is coming.
I assume "lp" behaves likewise. It looks to me that the best solution is
as given by OP: redirect output to a file (in the shell command passed to
popen()), then parse that file after doing pclose().
Or bite the bullet and use fork/exec and set up stdin and stdout
for the child appropriately with two stdio streams, one for read
and one for write.
Urgh. Yes I could but thats what one wants to avoid by using popen(). Its such
a faff to setup plus all the error checking whereas popen() is 1 line of code.
Does OSX have a printer API that allows one to submit a job (rather than
using system(3) or popen(3))?
M***@dastardlyhq.com
2023-01-10 16:01:14 UTC
Permalink
On Tue, 10 Jan 2023 14:37:17 GMT
Post by Scott Lurndal
Post by M***@dastardlyhq.com
Post by Scott Lurndal
Or bite the bullet and use fork/exec and set up stdin and stdout
for the child appropriately with two stdio streams, one for read
and one for write.
Urgh. Yes I could but thats what one wants to avoid by using popen(). Its
such
Post by M***@dastardlyhq.com
a faff to setup plus all the error checking whereas popen() is 1 line of code.
Does OSX have a printer API that allows one to submit a job (rather than
using system(3) or popen(3))?
Yes, there's the standard CUPS C API which Linux also shares, but its a bit long
winded. However the main reasons for not using it are if the box still uses
the old style lp system CUPS is no use plus the headers and library might not
be installed either so the program won't even compile.
Kaz Kylheku
2023-01-11 10:40:06 UTC
Permalink
Post by Nicolas George
Post by Kaz Kylheku
However, if some trailing part of your request is sitting in a buffer,
such that lp is waiting for that last bit before replying, then you have
a deadlock.
I would guess it is not enough: lp is probably waiting for EOF, and there is
no way to close only half of a popen stream.
That's a great observation.

Now the "r+" mode is not portable to begin with.

The POSIX model of standard I/O streams is such that a stream has one
underlying descriptor, accessible via fileno.

Suppose an implementation implements a bidirectional popen using only
one descriptor, such that the descriptor returned by fileno is all there
is; that object handles both data transfer directions.

Such an implementation is quite possibly using the socketpair function
to creae the bidirectional descriptor.

And so if socketpair is being used, then it should be possible to use
shutdown on the descriptor to do a half-close.

As in:

// write data to lp ...

// send everything to socket
fflush(pipe_stream);

// close in writing direction
if (shutdown(fileno(pipe_stream), SHUT_WR) != 0)
{
// didn't work: not a socket?
}

It's worth a try.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
M***@dastardlyhq.com
2023-01-11 16:54:54 UTC
Permalink
On Wed, 11 Jan 2023 10:40:06 -0000 (UTC)
Post by Kaz Kylheku
Post by Nicolas George
Post by Kaz Kylheku
However, if some trailing part of your request is sitting in a buffer,
such that lp is waiting for that last bit before replying, then you have
a deadlock.
I would guess it is not enough: lp is probably waiting for EOF, and there is
no way to close only half of a popen stream.
That's a great observation.
Now the "r+" mode is not portable to begin with.
The POSIX model of standard I/O streams is such that a stream has one
underlying descriptor, accessible via fileno.
Suppose an implementation implements a bidirectional popen using only
one descriptor, such that the descriptor returned by fileno is all there
is; that object handles both data transfer directions.
Such an implementation is quite possibly using the socketpair function
to creae the bidirectional descriptor.
And so if socketpair is being used, then it should be possible to use
shutdown on the descriptor to do a half-close.
// write data to lp ...
// send everything to socket
fflush(pipe_stream);
// close in writing direction
if (shutdown(fileno(pipe_stream), SHUT_WR) != 0)
{
// didn't work: not a socket?
}
It's worth a try.
Tried, didn't work. feof() was always true and fgets() returned garbage. :(
Scott Lurndal
2023-01-09 16:19:18 UTC
Permalink
Post by M***@dastardlyhq.com
Has anyone ever used popen() with the r+ option on MacOS? Writing to the file
descriptor works fine (the output goes to the printer queue) but any attempt to
if ((pfile = popen("lp","r+")))
{
... write data to printer ...
fgets(str,sizeof(str),pfile); <- hangs here
}
It also hangs if I try while(!feof(pfile)) or read(fileno(pfile),....)
lp is producing output and I can redirect to a file , eg popen("lp > myfile",..)
I assume this is some kind of buffer flush issue but I can't find anything on
google about it.
The 'p' in "popen" stands for "pipe". Pipes in unix are unidirectional.

From https://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html


"Since open files are shared, a mode r command can be used as
an input filter and a mode w command as an output filter."

and
"The behavior of popen() is specified for values of mode of r and w."

You can't do what you want to do with popen.
M***@dastardlyhq.com
2023-01-09 16:57:15 UTC
Permalink
On Mon, 09 Jan 2023 16:19:18 GMT
Post by M***@dastardlyhq.com
Post by M***@dastardlyhq.com
Has anyone ever used popen() with the r+ option on MacOS? Writing to the file
descriptor works fine (the output goes to the printer queue) but any attempt
to
Post by M***@dastardlyhq.com
if ((pfile = popen("lp","r+")))
{
... write data to printer ...
fgets(str,sizeof(str),pfile); <- hangs here
}
It also hangs if I try while(!feof(pfile)) or read(fileno(pfile),....)
lp is producing output and I can redirect to a file , eg popen("lp >
myfile",..)
Post by M***@dastardlyhq.com
I assume this is some kind of buffer flush issue but I can't find anything on
google about it.
The 'p' in "popen" stands for "pipe". Pipes in unix are unidirectional.
From https://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html
"Since open files are shared, a mode r command can be used as
an input filter and a mode w command as an output filter."
and
"The behavior of popen() is specified for values of mode of r and w."
You can't do what you want to do with popen.
From the MacOS popen() man page:

reading or writing, not both. Because popen() is now implemented using a
bidirectional pipe, the mode argument may request a bidirectional data
flow. The mode argument is a pointer to a null-terminated string which
must be `r' for reading, `w' for writing, or `r+' for reading and
writing.

HTH.
Kenny McCormack
2023-01-10 02:20:15 UTC
Permalink
In article <tphh1a$q5j$***@gioia.aioe.org>, <***@dastardlyhq.com> wrote:
...
Post by M***@dastardlyhq.com
Post by Scott Lurndal
"The behavior of popen() is specified for values of mode of r and w."
You can't do what you want to do with popen.
reading or writing, not both. Because popen() is now implemented using a
bidirectional pipe, the mode argument may request a bidirectional data
flow. The mode argument is a pointer to a null-terminated string which
must be `r' for reading, `w' for writing, or `r+' for reading and
writing.
HTH.
When answering questions like this, it is important to be clear as to
whether you are answering from a "POSIX standards" POV vs. from the OP's
actual use case situation. The POSIX standards answer is, as stated above,
you can't do that (and now go home and bother us no more). However, OP
stated that he was using a Mac and that Mac documentation explicitly allows
this (with, of course, the caveat that it is an "extension").

Note, BTW, that I don't currently have access to a Mac to check this, but I
am assuming that the reports posted so far in this thread are accurate.

Interestingly, the Linux documentation that just checked makes it clear
that only "r" and "w" are valid. Even though, Linux certainly has
bidirectional pipes available (and, in fact, I think it is common knowledge
that pipe is implemented with socketpair), so you'd think Linux would have
a bidirectional version of popen() available, but apparently such is not
the case.
--
Watching ConservaLoons playing with statistics and facts is like watching a
newborn play with a computer. Endlessly amusing, but totally unproductive.
M***@dastardlyhq.com
2023-01-10 09:57:19 UTC
Permalink
On Tue, 10 Jan 2023 02:20:15 -0000 (UTC)
Post by Kenny McCormack
....
Post by M***@dastardlyhq.com
Post by Scott Lurndal
"The behavior of popen() is specified for values of mode of r and w."
You can't do what you want to do with popen.
reading or writing, not both. Because popen() is now implemented using a
bidirectional pipe, the mode argument may request a bidirectional data
flow. The mode argument is a pointer to a null-terminated string which
must be `r' for reading, `w' for writing, or `r+' for reading and
writing.
HTH.
When answering questions like this, it is important to be clear as to
whether you are answering from a "POSIX standards" POV vs. from the OP's
actual use case situation. The POSIX standards answer is, as stated above,
you can't do that (and now go home and bother us no more). However, OP
stated that he was using a Mac and that Mac documentation explicitly allows
this (with, of course, the caveat that it is an "extension").
Note, BTW, that I don't currently have access to a Mac to check this, but I
am assuming that the reports posted so far in this thread are accurate.
Interestingly, the Linux documentation that just checked makes it clear
that only "r" and "w" are valid. Even though, Linux certainly has
Yes, I didn't realise that when I started writing the code on MacOS.
Post by Kenny McCormack
bidirectional pipes available (and, in fact, I think it is common knowledge
that pipe is implemented with socketpair), so you'd think Linux would have
a bidirectional version of popen() available, but apparently such is not
the case.
Perhaps they realised it can't work properly in the current API of popen()
as there's no way to close one half of the pipe without some modification
to pclose().

Anyone know if r+ is a MacOS thing or it was brought over from FreeBSD?
Kaz Kylheku
2023-01-11 10:45:32 UTC
Permalink
Post by M***@dastardlyhq.com
On Mon, 09 Jan 2023 16:19:18 GMT
Post by Scott Lurndal
You can't do what you want to do with popen.
reading or writing, not both. Because popen() is now implemented using a
bidirectional pipe, the mode argument may request a bidirectional data
flow. The mode argument is a pointer to a null-terminated string which
must be `r' for reading, `w' for writing, or `r+' for reading and
writing.
And, so, continuing in the same vein as my previous posting: we have had
bidirectional pipes in Unix for decades. The socketpair function creates
two descriptors and places them into an array, just like pipe. But
they are a pair of connected sockets: writing to one makes data appear
in the other and vice versa.

So there are two possibilities:

1. MacOS invented a new kind of bidirectional pipe that is other than a
socket pair.

2. The bidirectional pipe is actually a socket pair.

If (2) is the case, it would be amazing if, after flushing all the
written data to lp, you couldn't pull out the descriptor using fileno,
and then half-close it using shutdown(fd, SHUT_WR).
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Geoff Clare
2023-01-11 13:43:53 UTC
Permalink
Post by Kaz Kylheku
And, so, continuing in the same vein as my previous posting: we have had
bidirectional pipes in Unix for decades. The socketpair function creates
two descriptors and places them into an array, just like pipe. But
they are a pair of connected sockets: writing to one makes data appear
in the other and vice versa.
1. MacOS invented a new kind of bidirectional pipe that is other than a
socket pair.
I'm fairly sure it's a true bidirectional pipe, not a socket pair.
However, such things have existed since SVR4 (where they were
STREAMS-based). I think in Solaris they are no longer STREAMS-based
but are still bidirectional.

MacOS got them from FreeBSD, which has had them since 2.2.1 according
to their historical man pages:

www.freebsd.org/cgi/man.cgi?query=pipe&sektion=2&manpath=FreeBSD+2.2.1-RELEASE
Post by Kaz Kylheku
2. The bidirectional pipe is actually a socket pair.
This would not conform to POSIX, as fstat() would show S_ISSOCK(st_mode)
true instead of S_ISFIFO(st_mode).
--
Geoff Clare <***@gclare.org.uk>
Nicolas George
2023-01-11 14:58:28 UTC
Permalink
Geoff Clare , dans le message
Post by Geoff Clare
I'm fairly sure it's a true bidirectional pipe, not a socket pair.
Note that pipes, unidirectional or bidirectional, are as much pairs than
socket pairs. There is little difference between a stream socket pair and a
bidirectional pipe: maybe socket pairs have a few extra features like
half-close through shutdown() or ancillary data, or maybe they are the same
thing internally.
Kaz Kylheku
2023-01-12 05:23:42 UTC
Permalink
Post by Geoff Clare
Post by Kaz Kylheku
2. The bidirectional pipe is actually a socket pair.
This would not conform to POSIX, as fstat() would show S_ISSOCK(st_mode)
true instead of S_ISFIFO(st_mode).
Sure, but the behavior "r+" in popen is unspecified.
--
TXR Programming Language: http://nongnu.org/txr
Cygnal: Cygwin Native Application Library: http://kylheku.com/cygnal
Mastodon: @***@mstdn.ca
Nicolas George
2023-01-09 21:55:39 UTC
Permalink
Post by Scott Lurndal
Post by M***@dastardlyhq.com
Has anyone ever used popen() with the r+ option on MacOS?
^^^^^^
Post by Scott Lurndal
The 'p' in "popen" stands for "pipe". Pipes in unix are unidirectional.
From https://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html
"Since open files are shared, a mode r command can be used as
an input filter and a mode w command as an output filter."
and
"The behavior of popen() is specified for values of mode of r and w."
You can't do what you want to do with popen.
Unfortunately, they can:
https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/popen.3.html

“or `r+' for reading and writ-ing.”

Blame on Apple for adding this extension without documenting how to
half-close the stream. They could have returned two FILE*, or documented the
use of shutdown(fileno(p)).

Lucky guess: try write(fileno(p), "", 0).
M***@dastardlyhq.com
2023-01-10 09:52:18 UTC
Permalink
On 09 Jan 2023 21:55:39 GMT
Post by Nicolas George
Post by Scott Lurndal
Post by M***@dastardlyhq.com
Has anyone ever used popen() with the r+ option on MacOS?
^^^^^^
Post by Scott Lurndal
The 'p' in "popen" stands for "pipe". Pipes in unix are unidirectional.
From https://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html
"Since open files are shared, a mode r command can be used as
an input filter and a mode w command as an output filter."
and
"The behavior of popen() is specified for values of mode of r and w."
You can't do what you want to do with popen.
https://developer.apple.com/library/archive/documentation/System/Conceptual/Man
Pages_iPhoneOS/man3/popen.3.html
“or `r+' for reading and writ-ing.”
Blame on Apple for adding this extension without documenting how to
half-close the stream. They could have returned two FILE*, or documented the
use of shutdown(fileno(p)).
Lucky guess: try write(fileno(p), "", 0).
Good idea, didn't work though. It does look like Apples implementation is
broken, or more politely - incomplete. There seems to be no way to close one
half of the pipe to force the child process to complete its stdout.
Geoff Clare
2023-01-10 13:23:11 UTC
Permalink
Post by M***@dastardlyhq.com
It does look like Apples implementation is
broken, or more politely - incomplete. There seems to be no way to close one
half of the pipe to force the child process to complete its stdout.
If popen(..., "r+") is only usable with commands that don't wait for
EOF, then execute a command that doesn't wait for EOF.

You were trying to execute "lp". You could instead execute something
like:

sed '/^=EnD=oF=InPuT=$/{d;q;}' | lp

and write that sentinel string (plus a newline) to the stream after
the data to be printed. To reduce the risk of the sentinel string
occurring in the data to be printed, you could include some rarely-used
control characters.
--
Geoff Clare <***@gclare.org.uk>
M***@dastardlyhq.com
2023-01-10 15:56:29 UTC
Permalink
On Tue, 10 Jan 2023 13:23:11 +0000
Post by Geoff Clare
Post by M***@dastardlyhq.com
It does look like Apples implementation is
broken, or more politely - incomplete. There seems to be no way to close one
half of the pipe to force the child process to complete its stdout.
If popen(..., "r+") is only usable with commands that don't wait for
EOF, then execute a command that doesn't wait for EOF.
You were trying to execute "lp". You could instead execute something
sed '/^=EnD=oF=InPuT=$/{d;q;}' | lp
and write that sentinel string (plus a newline) to the stream after
the data to be printed. To reduce the risk of the sentinel string
occurring in the data to be printed, you could include some rarely-used
control characters.
Sounds like more trouble than its worth and rather flakey.
Loading...