Re: popen <pan.2006.08.17.17.01.34.648798@cablespeed.com> <1155835419.657053.153580@b28g2000cwb.googlegroups.com>



At Thu, 17 Aug 2006 18:46:18 -0400 "Amadeus W. M." <amadeus84@xxxxxxxxxxxxxx> wrote:


On Thu, 17 Aug 2006 21:08:47 +0200, Robert Heller wrote:

At Thu, 17 Aug 2006 14:11:07 -0400 "Amadeus W. M." <amadeus84@xxxxxxxxxxxxxx> wrote:


On Thu, 17 Aug 2006 10:23:39 -0700, Lew Pitcher wrote:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


Amadeus W. M. wrote:
I created a little program (myprog) which acts like a filter:

cat data | myprog > output_data

I want to build a GUI for it. The particular graphical libraries
are irrelevant here. The user must create the data by clicking
a couple of times in a drawing area. Then, using popen/pclose,
I want to pass the data from memory to myprog, and read and
display the output data. The popen man page says I can open
a stream to myprog either for writing, or for reading, but not
both.

Short of saving the user data to a temporary file, and then
have myprog read it from there, and have the gui read the ouput
from myprog, is there any other way of doing what I want?

Of course there is. Just, not with popen()

popen() is just a wrapper for fork() and exec(), so rewrite your code
to use fork()/exec() instead of popen()


fork() and exec() would be fine, but even so, the problem
of passing data to and from the forked-off process remains,
doesn't it?

Add in socketpair():

Rough 'psuedo code':

int sockets[2];
int status;

if (socketpair(AF_UNIX,SOCK_STREAM,0,sockets) < 0) {
int err = errno;
perror("socketpair");
exit(err);
}

pid = fork();
if (pid < 0) {
int err = errno;
perror("fork");
exit(err);
} else if (pid == 0) {
close(0); /* stdin */
close(1); /* stdout */
dup(sockets[0]); /* sockets[0] connects to stdin of child */
dup(sockets[1]); /* sockets[1] connects to stdout of child */
if (execl("myprog","myprog",...) < 0) {
int err = errno;
perror("execl");
exit(err);
}
} else {
/* Write to sockets[0], read from sockets[1] */
close(sockets[0]);
close(sockets[1]);
waitpid(pid,&status,0);
}

For a proper GUI, you'd include the guts of the else clause as part of
the event loop and/or include code to check the X11 event queue or some
such. And the error handling would be better (pop up an error dialog
and proably not just call exit()).


I was thinking that this whole thing would be part of a callback
of some "Filter" button. Is this what you mean?

Sort of. The callback would do everything upto the last else close
(all of which will happen virtually instantly). There would then be
some kind of event handling of the I/O to/from the sockets (or pipes)
that would be handled by the event loop (if you were using Tcl/Tk, you
could bind the sockets to a Tcl_Channel and use Tcl's 'fileevent'
command -- I have a chunk of SWIG code that does exactly that, if you
are interested). The contents of the the last else clause (/* Write to
sockets[0], read from sockets[1] */...) will take some time (unless the
data source in tiny). The GUI would presumably display a 'busy' cursor
or something (eg flying sheets of paper, grain going through a grinder,
or whatever silliness to indicating data is going though your filter).
You don't want to be 'stuck' in the callback for the whole time the I/O
is in progress or the GUI will 'freeze' and appear 'dead'. This is
misleading -- you want the GUI to keep being responsive, even if it is
refusing all input ("I'm working, go away. I'm not dead.").


Also, any preference for sockets vs. pipes?

No. Just that socketpair() is real convient. Sockets are
bidirectional, so there is no need to worry about which end of which
one you connect to stdin and stdout.


Thanks!







--
Robert Heller -- 978-544-6933
Deepwoods Software -- Linux Installation and Administration
http://www.deepsoft.com/ -- Web Hosting, with CGI and Database
heller@xxxxxxxxxxxx -- Contract Programming: C/C++, Tcl/Tk

.



Relevant Pages