[comp.lang.c] popen

anigbogu@loria.crin.fr (Julian ANIGBOGU) (02/23/90)

Can some kind soul point me to where I can get a PC implementation of
the Unix C popen() /* for handle to a pipe */. I manipulate huge
rasterfiles(from a scanner) on a Sun and I adopted the policy of
compressing all files to save disk space. I open them with
popen("uncompress -c filename","r") so that they remain compressed on
disk. 

 I now need to port the programs to DOS and to my chagrin my regular
compiler doesn't implement a pipe open.

 Suggestions in any form would be appreciated.

Julian


-- 
				---------------------------------------
e-mail:	anigbogu@loria.crin.fr 	| All opinions expressed here are      |
				|  naturally mine. However ...         |
				----------------------------------------

CMH117@psuvm.psu.edu (Charles Hannum) (03/03/90)

popen() cannot be implemented under DOS.

dbinette@van-bc.UUCP (Dave Binette) (03/03/90)

In article <1503@loria.crin.fr> anigbogu@loria.crin.fr (Julian Anigbogu) writes:
>Can some kind soul point me to where I can get a PC implementation of
>the Unix C popen() /* for handle to a pipe */. I manipulate huge
>rasterfiles(from a scanner) on a Sun and I adopted the policy of
>compressing all files to save disk space. I open them with
>popen("uncompress -c filename","r") so that they remain compressed on
>disk. 


Following this tirade on the inadequacies of DOS is a code fragment that
should help.

To start with... here is an excerpt from a help utility for C


Defines:
  Modes used by the spawn function.

  P_WAIT     child runs separately,
             parent waits until exit.
  P_NOWAIT   child and parent run
             concurrently. (not implemented)
  P_OVERLAY  child replaces parent so that
             parent no longer exists.

Defined in process.h

The critical issue here is P_NOWAIT.
Because under "DOS" processes are sequential there will be no "pipe"
(as we know it in *IX) to waylay disk usage that you are trying to avoid.

{normally *IX uses a fixed size pipe to transfer data from concurrent procs,
 this allows large amounts of data to be passed without consuming disk space}

Under "DOS" the system will actually create a temporary file somewhere. 
When the "parent" process terminates the "child" process is started with its 
stdin reading from the temporary file.

With that horrible scenario in mind.... here is an implimentation
of the popen/pclose commands.
hardly perfect but it might do the trick for READING OUTPUT from
a program.















#ifndef M_XENIX

/* ----------------------------------------------------------------    */
/* Compatability                            */
/* ----------------------------------------------------------------    */

char Popentemp[MAXARGLINE+1];

FILE * popen(char *s, char * fmode)
{
    char    * tpath;
    char    cmd[MAXARGLINE+1];
    FILE    * fp;

    if((tpath = getenv("TMP")) == (char *)0)
        if((tpath = getenv("TEMP")) == (char *)0)
            tpath = "";

    strcpy(Popentemp, tpath);
    strcat(Popentemp, mktemp("\\$PICK_XXXXXX"));
    strcpy(cmd, s);
    strcat(cmd, " > ");
    strcat(cmd, Popentemp);
    system(cmd);
    fp = fopen(Popentemp, fmode);
    return(fp);
}

/* ----------------------------------------------------------------    */

int pclose(FILE * fp)
{
    int r = -1;

    if(fp)
        if(fclose(fp) == 0);
            r = unlink(Popentemp);

    return(r);
}

#endif

anigbogu@loria.crin.fr (Julian ANIGBOGU) (03/03/90)

In article <90061.202350CMH117@psuvm.psu.edu> CMH117@psuvm.psu.edu (Charles Hannum) writes:
>
>popen() cannot be implemented under DOS.
         ^^^^^^
WRONG!! Take a look at Ian Stewartson's implementation of Shell (sh)
for MSDOS. It contains a working version of both popen() and pclose().
After I posted my question, I downloaded his implementation from
comp.sources.misc and found the response there. It's really neat. You
can use it freelly as long as you acknowledge the source by keeping
the copyright notice in there.

Thanks Ian. BTW, that's the kind of code that should be making it to
Usenet. VIVE FREEWARE!!

Couldn't resist! The debate on Shareware has lasted long enough.
Flames to /dev/null !

Julian

-- 
				---------------------------------------
e-mail:	anigbogu@loria.crin.fr 	| All opinions expressed here are      |
				|  naturally mine. However ...         |
				----------------------------------------

steve@thelake.mn.org (Steve Yelvington) (03/04/90)

[In article <90061.202350CMH117@psuvm.psu.edu>,
     Charles Hannum <CMH117@psuvm.psu.edu> writes ... ]

> popen() cannot be implemented under DOS.

Man can't fly, either. At least not without building an aircraft.

The functionality of popen/pclose can be duplicated by using temporary
files. The calling program need not know the ugly details. For the limited
application mentioned by the original poster -- uncompressing a file and
reading the output -- it's even easier. It need not work internally like
Un*x popen to have the desired results.

I'm writing this on an Atari ST running TOS, which essentially is an
MS-DOS workalike for the Motorola 68000. When I store it, postnews will
pipe it to rnews, which will (among other things) pipe it to uux. So
you're looking at evidence that it can be done in a single-tasking
environment.

Surely it can be done under MS-DOS, too.

cs4g6ag@maccs.dcss.mcmaster.ca (Stephen M. Dunn) (03/04/90)

In article <1503@loria.crin.fr> anigbogu@loria.crin.fr (Julian Anigbogu) writes:
$Can some kind soul point me to where I can get a PC implementation of
$the Unix C popen() /* for handle to a pipe */. I manipulate huge
$rasterfiles(from a scanner) on a Sun and I adopted the policy of
$compressing all files to save disk space. I open them with
$popen("uncompress -c filename","r") so that they remain compressed on
$disk. 
$ I now need to port the programs to DOS and to my chagrin my regular
$compiler doesn't implement a pipe open.

   This isn't a problem with your compiler ... it's just that Unix is a
multi-tasking operating system which can run one program and have its output
piped directly into another program, while DOS is single-tasking and
cannot do this.  DOS's idea of a pipe is as follows:

-run program 1 with its stdout redirected to some file
-run program 2 with its stdin redirected from that file
-delete the temporary file

and the only part of DOS that has any idea of how to do a pipe is the command
interpreter in any case.

   You'll have to implement it yourself by doing the same sort of
thing yourself:  write a function popen that does:

-come up with a temporary file name (there should be a call in your
 library to do this; it's a standard DOS function in DOS 3 and up.
-spawn or exec or system your uncompress utility with redirection of stdout
 (how you handle this depends on which call you use)
-fopen the temporary file and return that handle

and a corresponding close that does:

-close the file
-delete the temporary file

   And, of course, this will take up disk space while your program is
running.
-- 
Stephen M. Dunn                               cs4g6ag@maccs.dcss.mcmaster.ca
          <std_disclaimer.h> = "\nI'm only an undergraduate!!!\n";
****************************************************************************
               I Think I'm Going Bald - Caress of Steel, Rush

darcy@druid.uucp (D'Arcy J.M. Cain) (03/04/90)

In article <1503@loria.crin.fr> anigbogu@loria.crin.fr (Julian Anigbogu) writes:
>Can some kind soul point me to where I can get a PC implementation of
>the Unix C popen() /* for handle to a pipe */. I manipulate huge
>rasterfiles(from a scanner) on a Sun and I adopted the policy of
>compressing all files to save disk space. I open them with
>popen("uncompress -c filename","r") so that they remain compressed on
>disk. 
>
> I now need to port the programs to DOS and to my chagrin my regular
>compiler doesn't implement a pipe open.
>
With good reason.  DOS is a single tasking system so you can't have a
program running without putting the current one on hold first.  The best
way to do what you want is to have the uncompression itself as a subroutine
that returns a specified number of bytes and just call it instead of a
read function.  This is the way I did it and it works just fine.  My function
returns one byte at a time although you could easily make the number of
bytes to return a parameter.

This is not really a C question so followups directed to
comp.sys.ibm.pc.programmer

-- 
D'Arcy J.M. Cain (darcy@druid)     |   Thank goodness we don't get all 
D'Arcy Cain Consulting             |   the government we pay for.
West Hill, Ontario, Canada         |
(416) 281-6094                     |

dowding@linc.cis.upenn.edu (John Dowding) (03/04/90)

In article <25F00B67.17161@maccs.dcss.mcmaster.ca> cs4g6ag@maccs.dcss.mcmaster.ca (Stephen M. Dunn) writes:
>
>   And, of course, this will take up disk space while your program is
>running.

If you write write the temp file to a ram disk, won't this give you
nearly the same effect as the UNIX pipe?

John Dowding
dowding@linc.cis.upenn.edu

CMH117@psuvm.psu.edu (Charles Hannum) (03/05/90)

In article <21231@netnews.upenn.edu>, dowding@linc.cis.upenn.edu (John Dowding)
says:
>
>If you write write the temp file to a ram disk, won't this give you
>nearly the same effect as the UNIX pipe?

Close, but *ix pipes are fixed in size.  (The size probably varies from system
to system; I don't know what it is.)  When the pipe is full, the process that
is writing to it is suspended; when it is empty, the process reading from it is
suspended.  This allows a virtually infinite amount of data to pass over the
pipe.  (Just think about "ls -R | find ..." from the root!!)  Writing to a RAM
disk (or *any* disk) under DOS limits the amount of information that can pass
over the pipe.

A side note:  A variant of *ix piping is implemented in OS/2.


Virtually,
- Charles Martin Hannum II       "Klein bottle for sale ... inquire within."
    (That's Charles to you!)     "To life immortal!"
  cmh117@psuvm.{bitnet,psu.edu}  "No noozzzz izzz netzzzsnoozzzzz..."
  c9h@psuecl.{bitnet,psu.edu}    "Mem'ry, all alone in the moonlight ..."

zmact61@doc.ic.ac.uk (D Spinellis) (03/05/90)

In article <90061.202350CMH117@psuvm.psu.edu> CMH117@psuvm.psu.edu (Charles Hannum) writes:
>
>popen() cannot be implemented under DOS.

/*
 * popen, pclose functions for MS DOS 
 *
 * (C) Copyright 1988, 1990 Diomidis Spinellis. All rights reserved. 
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by Diomidis Spinellis.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 */

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <process.h>

/*
 * Possible actions on an popened file
 */
enum action {
	delete, 			/* Used for "r". Delete the tmp file */
	execute				/* Used for "w". Execute the command. */
};

/*
 * Linked list of things to do at the end of the program execution.
 */
static struct todo {
	FILE *f;			/* File we are working on (to fclose) */
	const char *name;		/* Name of the file (to unlink) */
	const char *command;		/* Command to execute */
	enum action what;		/* What to do (execute or delete) */
	struct todo *next;		/* Next structure */
} *todolist;


/* Clean up function */
static int close_pipes(void);

/*
 * Add a file f running the command command on file name to the list
 * of actions to be done at the end.  The action is specified in what.
 * Return -1 on failure, 0 if ok.
 */
static int
add(FILE *f, const char *command, const char *name, enum action what)
{
	struct todo    *p;

	if ((p = (struct todo *) malloc(sizeof(struct todo))) == NULL)
		return -1;
	p->f = f;
	p->command = command;
	p->name = name;
	p->what = what;
	p->next = todolist;
	todolist = p;
	return 0;
}

FILE *
popen(const char *command, const char *t)
{
	char buff[256];
	char *name;
	FILE *f;
	static init = 0;

	if (!init)
		if (onexit(close_pipes) == NULL)
			return NULL;
		else
			init++;

	if ((name = tempnam(getenv("TMP"), "pp")) == NULL)
		return NULL;

	switch (*t) {
	case 'r':
		sprintf(buff, "%s >%s", command, name);
		if (system(buff) || (f = fopen(name, "r")) == NULL) {
			free(name);
			return NULL;
		}
		if (add(f, command, name, delete)) {
			(void)fclose(f);
			(void)unlink(name);
			free(name);
			return NULL;
		}
		return f;
	case 'w':
		if ((f = fopen(name, "w")) == NULL) {
			free(name);
			return NULL;
		}
		if (add(f, command, name, execute)) {
			(void)fclose(f);
			(void)unlink(name);
			free(name);
			return NULL;
		}
		return f;
	default:
		free(name);
		return NULL;
	}
}

int
pclose(FILE *f)
{
	struct todo *p, **prev;
	char buff[256];
	const char *name;
	int status;

	for (p = todolist, prev = &todolist; p; prev = &(p->next), p = p->next)
		if (p->f == f) {
			*prev = p->next;
			name = p->name;
			switch (p->what) {
			case delete:
				free(p);
				if (fclose(f) == EOF) {
					(void)unlink(name);
					status = EOF;
				} else if (unlink(name) < 0)
					status = EOF;
				else
					status = 0;
				free(name);
				return status;
			case execute:
				(void)sprintf(buff, "%s <%s", p->command, p->name);
				free(p);
				if (system(buff)) {
					(void)unlink(name);
					status = EOF;
				} else if (fclose(f) == EOF) {
					(void)unlink(name);
					status = EOF;
				} else if (unlink(name) < 0)
					status = EOF;
				else
					status = 0;
				free(name);
				return status;
			default:
				return EOF;
			}
		}
	return EOF;
}

/*
 * Clean up at the end.  Called by the onexit handler.
 */
static int
close_pipes(void)
{
	struct todo    *p;

	for (p = todolist; p; p = p->next)
		(void)pclose(p->f);
	return 0;
}
--
Diomidis Spinellis                  Internet:                 dds@cc.ic.ac.uk
Department of Computing             UUCP:                    ...!ukc!iccc!dds
Imperial College                    JANET:                    dds@uk.ac.ic.cc
London SW7 2BZ                      #include "/dev/tty"

roy@comcon.UUCP (Roy M. Silvernail) (03/05/90)

In article <21231@netnews.upenn.edu>, dowding@linc.cis.upenn.edu (John Dowding) writes:
| In article <25F00B67.17161@maccs.dcss.mcmaster.ca> cs4g6ag@maccs.dcss.mcmaster.ca (Stephen M. Dunn) writes:
| >
| >   And, of course, this will take up disk space while your program is
| >running.
| 
| If you write write the temp file to a ram disk, won't this give you
| nearly the same effect as the UNIX pipe?
| 
| John Dowding
| dowding@linc.cis.upenn.edu

Nearly the same, as long as the temp file doesn't outrun the bounds of
your ramdisk. 

I can't tell from experience, 'cause my wimpy litle machine only has
640k, which leaves precious little room for a ramdisk.


-- 
_R_o_y _M_. _S_i_l_v_e_r_n_a_i_l  | UUCP: uunet!comcon!roy  |  "Every race must arrive at this
#include <opinions.h>;#define opinions MINE  |   point in its history"
SnailMail: P.O. Box 210856, Anchorage,       |   ........Mr. Slippery
Alaska, 99521-0856, U.S.A., Earth, etc.      |  <Ono-Sendai: the right choice!>

anigbogu@loria.crin.fr (Julian ANIGBOGU) (03/06/90)

Hello Guys. Given the enormous number of responses I got to my
question on the above subject, I guess it would be in order to give a
summary of the solutions proposed. At the same time I'll like to thank
the following chaps who jumped onto the rescue-waggon. I responded
individually to some but they might have bounced: 

Len Reed, Raymond Chen, Richard Brittain, Chris Larsen, Mr.
HappyWOWface, PeterG, Kaled Keithley, Doug McDonald, Steve Yelvington
etc. Please, don't feel slighted if your name didn't show up!!

Granted that DOS is not multi-tasking OS, one can still fake pipes by
first spawning the program (In my case 'uncompress') that is being
piped , turn the IO parameter from "r" to "w", writing the output to a
TMP file, (which in effect equivalent to writing to STDOUT (if not
already in use) and redirecting into a file which is a longer process
[IMHO]), reopening the file in "r" mode and passing the handle back
the program that spawn 'uncompress'. This seems neat enough for what
I'm doing as the main program is completely unaware of the "dirty
tricks" being played.

I first found my salvation in Ian Stewartson's implementation of the
Unix (sh) ie. after I sent out my question. From some of the responses
I got I'm apparently not the only one confronted with such problems.
What I failed to add in my origal posting was the absence of time
constraint. My goal was having as many compressed rasterfiles as I
could pack onto the hard-disk and uncompressing them as I need them
without the need to explicitely do that in a batch file and calling
the main program recursively. I guessed that if I could get away with
'popen' on Unix it wouldn't cause any harm faking that on DOS.

Julian


-- 
				---------------------------------------
e-mail:	anigbogu@loria.crin.fr 	| All opinions expressed here are      |
				|  naturally mine. However ...         |
				----------------------------------------

wcf@psuhcx.psu.edu (Bill Fenner) (03/07/90)

In article <1669@gould.doc.ic.ac.uk> dds@cc.ic.ac.uk (Diomidis Spinellis) writes:
|In article <90061.202350CMH117@psuvm.psu.edu> CMH117@psuvm.psu.edu (Charles Hannum) writes:
|>popen() cannot be implemented under DOS.
|
|/*
| * popen, pclose functions for MS DOS 

popen() cannot be *properly* implemeted under MSDOS.
-- 
Bill Fenner                   wcf@hcx.psu.edu             ..!psuvax1!psuhcx!wcf
sysop@hogbbs.fidonet.org (1:129/87 - 814/238-9633)     ..!lll-winken!/

CMH117@psuvm.psu.edu (Charles Hannum) (03/07/90)

In article <2149@psuhcx.psu.edu>, wcf@psuhcx.psu.edu (Bill Fenner) says:
>
>In article <1669@gould.doc.ic.ac.uk> dds@cc.ic.ac.uk (Diomidis Spinellis)
>writes:
>|In article <90061.202350CMH117@psuvm.psu.edu> CMH117@psuvm.psu.edu (Charles
>Hannum) writes:
>|>popen() cannot be implemented under DOS.
>|
>|/*
>| * popen, pclose functions for MS DOS
>
>popen() cannot be *properly* implemeted under MSDOS.


Another point (besides the inherent limitation on the amount of data passed
over the pipe) that no one has mentioned:

   *  If the program exits abnormally FOR ANY REASON the temporary file(s)
      will not be erased.

Everyone is willing to offer their own kludgy implementation, to try to show
off, rather than admitting that DOS just doesn't fit pipes.  [B-)]


Virtually,
- Charles Martin Hannum II       "Klein bottle for sale ... inquire within."
    (That's Charles to you!)     "To life immortal!"
  cmh117@psuvm.{bitnet,psu.edu}  "No noozzzz izzz netzzzsnoozzzzz..."
  c9h@psuecl.{bitnet,psu.edu}    "Mem'ry, all alone in the moonlight ..."

nigel@cmsfl@labtam.oz (Nigel Harwood) (07/01/90)

As a general question, how much use to people make of the popen()
function in programs where failures must be detected ?

I am working on a program where I would like to use
popen() but can see no way of detecting when the command exits
with a bad status.

At least not easily anyway.

Popen() will return a NULL pointer if the creation of the 
environment to run the command under fail but not if the command
fails.

So I have instead resorted to using system() and two temporary files
which means I am using one temporary file I wouldn't need if popen()
would tell me the exit code.

Your thoughts ?
-- 
<<<<<<<<<<<<<<<<<<<<<<<<<  Nigel Harwood  >>>>>>>>>>>>>>>>>>>>>>>>>>>
<< Post:  Coles Myer Ltd, PO Box 2000 Tooronga 3146, Australia     >>
<< Phone: +61 3 829 6090  E-mail: nigel@cnw01.storesys.coles.oz.au >>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

bakke@plains.UUCP (Jeffrey P. Bakke) (07/02/90)

In article <855@cmsfl> nigel@cmsfl@labtam.oz (Nigel Harwood) writes:
> As a general question, how much use to people make of the popen()
> function in programs where failures must be detected ?
> 
> I am working on a program where I would like to use
> popen() but can see no way of detecting when the command exits
> with a bad status.
> 
> At least not easily anyway.
> 
> Popen() will return a NULL pointer if the creation of the 
> environment to run the command under fail but not if the command
> fails.
> 

I was stuck in a similar situation before and I couldn't figure any
simple way around it.  I talked to the local system admin who suggested 
the solution which I currently use.  

I make a function called 'CallSystem' which is sent the program and
or args to execute.  I then fork the process (fork()) to obtain a
child process, I then create a pipe from the parent to the child and then
once that is complete, I have the child perform an execl - variant function
based on my needs.  when you execl the child is completely replaced by the
program you call so that the pipe from the parent to the child, is actually
a pipe from the program you want to run and the parent.  And of course, 
using this method you can monitor the execution and termination status
of the child function as you would normally.  It takes some messing 
around and reading of the man pages but it does work and work well once
you know what you're doing.


Jeffrey P. Bakke     | Internet: bakke@plains.NoDak.edu | "Life... don't talk
2550 15th Str S #23B | UUCP    : ...!uunet!plains!bakke |  to me about life..."
Fargo, ND  58105     | BITNET  : bakke@plains.bitnet    |    - Marvin the PA

leo@ehviea.ine.philips.nl (Leo de Wit) (07/02/90)

In article <855@cmsfl> nigel@cmsfl@labtam.oz (Nigel Harwood) writes:
|As a general question, how much use to people make of the popen()
|function in programs where failures must be detected ?
|
|I am working on a program where I would like to use
|popen() but can see no way of detecting when the command exits
|with a bad status.
|
|At least not easily anyway.

From the popen() manual page:

     A stream opened by popen should be closed by pclose, which
     waits for the associated process to terminate and returns
     the exit status of the command.
 
I guess this says it all ...

    Leo.

cpcahil@virtech.uucp (Conor P. Cahill) (07/02/90)

In article <855@cmsfl> nigel@cmsfl@labtam.oz (Nigel Harwood) writes:
>I am working on a program where I would like to use
>popen() but can see no way of detecting when the command exits
>with a bad status.

RTFM: pclose() returns the exit status of the command.

-- 
Conor P. Cahill            (703)430-9247        Virtual Technologies, Inc.,
uunet!virtech!cpcahil                           46030 Manekin Plaza, Suite 160
                                                Sterling, VA 22170 

hannum@vivaldi.psu.edu (Charles Hannum) (07/06/90)

Quoting (without permission) from the popen/pclose man page on this system:

     A stream opened by popen should be closed by pclose, which
     waits for the associated process to terminate and returns
     the exit status of the command.

The reader can extrapolate the rest.

(I'm assuming you're using a real Unix popen() and pclose() ... ?  If you're
using a cheap MS-LOSS half-ass-look-alike, then this probably won't work.  In
fact, I'm almost positive it won't in that case.)
--
 
Virtually,
Charles Martin Hannum		 "Those who say a thing cannot be done should
Please send mail to:		  under no circumstances stand in the way of
hannum@schubert.psu.edu		  he who is doing it." - a misquote