[comp.sys.amiga.tech] Using PIP:

mcr@julie.UUCP (Michael Richardson) (12/07/89)

  When I finally got annoyed at the silliness of command line
passing, program launching (with exit codes) and the pr_CLI structure
in general [this was at least 6 months ago] I began to build an
alternate launching scheme, much more similar to the way the
workbench startup message works. I based it on Leo Shwab's wblaunch
code. (I'm not sure if Peter, Leo or myself that had this thought
first. I think we all came up with it independently...)

  In order deal with who closes file descriptors, etc.. I wound
up passing a bitmap to the sub-process telling it which file handles
it can close. [Actually, I can also pass the `ToolWindow' field,
and although I haven't done it yet, I always put 0 in the place
where the Workbench message's `number of arguments' is so I could
in fact be launched from Workbench...] I'd like this field eventually
to be $ffffffff. (I can pass any number of FileHandles though.
They are installed in the Manx `_devtab' structure.)

  The point: I want a Popen call that works properly, and for which
each end has it's own filehandle. PIP: seemed to be the thing, except
that I can't seem to get it work. [My popen call takes an argv/argv
rather than a string. My `RogueStart' (there is a book named Rogue
Star by Frederik Pohl) shell is too simple at present to bother
feeding it through that.]

  I tried a simple test program:

 #include <stdio.h>
 #include <fcntl.h>
 #include <exec/memory.h>
 #include <libraries/dos.h>
 #include <libraries/dosextens.h>
 #include "bcpl.h"

 extern void *AllocMem();
 extern struct FileHandle *Open();
 extern FILE *fdopen();
 extern void *CreatePort();
 extern void *GetMsg();

 main()
 {
   struct FileHandle *pin,*pout,*ret,*temp;
   int i;
   char buf[100],buf2[100];

   if((pin=AllocMem(sizeof(struct FileHandle),MEMF_PUBLIC|MEMF_CLEAR))==NULL) {
     return(NULL);
   }
   if((pout=Open("PIP:4096",2000L))==NULL) {
     FreeMem(pin,sizeof(struct FileHandle));
     return(NULL);
   }
   temp=(struct FileHandle*)BTOC(pout);
   (*pin)=(*temp);
   pin=(struct FileHandle*)CTOB(pin);

   printf("Created two handles: %08x and %08x\n",pin,pout);
   printf("Please type a line: ");
   fgets(buf,100,stdin);
   printf("Got %s\n",buf);

   i=Write(pin,buf,strlen(buf));
   printf("Wrote string (%d)\n",i);

   i=Read(pout,buf2,100);
   buf2[i]='\0';
   printf("Read (%d) %s\n",i,buf2);

   Close(pin);
   i=Read(pout,buf2,100);
   buf2[i]='\0';
   printf("Read (%d) %s\n",i,buf2);
   Close(pout);

 }

  While this seemed to work --- it didn't always work twice, it seemed
that I could crash it by allocating pout, and opening pin rather than
the reverse. I can understand this.
  It also seemed that the order in which I closed them was significant.
I haven't run this program in awhile (I've been procrastinating
this posting for at least a couple of months... But today I'm
home sick, and don't feel like bashing any of my other projects...)
so I'm sorry if I am a bit vague --- but there is no way that I can
guarantee that the two ends of the pipe will be closed by the two
processes in the right order. Processes are quite asynchronous
in nature...
  I pretty sure that it is the order in which I close them
that is causing the guru --- sometimes when I run a little test it
crashes. Sometimes not. If I run two of them sequencially or
concurrently, I get the same results...

FILE * popenv(ac,av,mode,pp)
int ac;
char *av[];
char *mode;
struct MsgPort **pp;
{
  struct FileHandle *in,*out,*err;
  struct FileHandle *pin,*pout,*ret,*temp;
  struct MsgPort *p;
  struct RogueStartup *r;
  FILE   *rf;
  int code;
  long mask;
  int i;

  in=RogueInput();
  out=RogueOutput();
  err=RogueError();

  if(mode[0]!='r' && mode[0]!='w') return(NULL);

  if((pin=AllocMem(sizeof(struct FileHandle),MEMF_PUBLIC|MEMF_CLEAR))==NULL) {
    return(NULL);
  }
  if((pout=Open("PIP:4096",2000L))==NULL) {
    FreeMem(pin,sizeof(struct FileHandle));
    return(NULL);
  }
  temp=(struct FileHandle*)BTOC(pout);
  (*pin)=(*temp);
  pin=(struct FileHandle*)CTOB(pin);

  if(mode[0]='r') {
    out=pin;
    ret=pout;
    mask=(1L << 1);
  }
  else {
    in=pout;
    ret=pin;
    mask=(1L << 0);
  }

  /* Now set up the _devtab structure. */
  for(i=0; i<_numdev; i++) {
    if(_devtab[i].fd==NULL) {
      break;
    }
  }

  if(i==_numdev) {
    Close(pin);
    Close(pout);
    return(NULL);
  }
  _devtab[i].fd=(long)ret;
  _devtab[i].mode=(mode[0]=='r' ? O_RDONLY : O_WRONLY);

  if((rf=(FILE *)fdopen(i,mode))==NULL) {
    _devtab[i].fd=NULL;
    Close(pin);
    Close(pout);
    return(NULL);
  }

  if((p=(struct MsgPort *)CreatePort(NULL,NULL))==NULL) {
    if(ret==pin) Close(pout);
    else Close(pin);
    fclose(rf);
    return(NULL);
  }
  if((r=RogueLaunch(p,av[0],av,ac,NULL,0,NULL,10000,in,out,err,mask))==NULL) {
/* RogueLaunch(rport, name, argv, argc, env, pri, win, stack, in, out, err) --
 *                                               launch a program.
 *
 * RogueLaunch builds a startup message, loads a program, and launches it.
 *
 * Arguments:
 *      rport -- reply port to receive startup message back when the program
 *               finishes.
 *      name  -- File name for the program to load.
 *      argv  -- list of filenames to pass to loaded program.
 *      argc  -- length of argument list.
 *      env   -- list of environment variables (local)
 *      pri   -- priority of the new process.
 *      win   -- ToolWindow for the new process, or NULL.
 *      stack -- Stack size for the new process.
 *      in    -- Where to find Standard input \ Only taking
 *      out   -- Where to find Standard output > three is
 *      err   -- Where to find Standard error / arbitrary.
 *      mask  -- bitmap of file descriptors to close.
 *
 * argv[0] should be the same as the program. pri should normally be 0.
 * stack should normally be at least 4K.
 */


    if(ret==pin) Close(pout);
    else Close(pin);
    fclose(rf);
    DeletePort(p);
    return(NULL);
  }
  *pp=p;

  return(rf);

}

int pclose(f,pp)
FILE *f;
struct MsgPort **pp;
{
  struct MsgPort *p;
  struct RogueStart *rs;
  int code;

  if(f==NULL||pp==NULL) return(-1);

  p=*pp;

  if(p==NULL) return(-2);

  WaitPort(p);
  rs=GetMsg(p);
  if(rs) {
    code=RogueFreeStartup(rs);
  }
  fclose(f);     /* Close our end of the pipe */

  return(code);
}

  I also somewhat suspect the fclose() call, but I believe I
have tried converting everything to FileHandle level...


  BTW: Programs compiled with the Rogue Startup code can continue to run
from CLI, and if I ever make the WorkBench modification from the workbench
as well.
  Many parts of the uucp system that this is being posted with runs
via RogueLaunch. Specifially I've rewritten uux/uuxqt/unbatchfc/rmail.
Rmail has also be modified a large amount -- it adds Received:, collapses
From_ [yuck], and adds a Message-Id: if there isn't one. I called
it smail because I'll eventually add `smart-host' to it. It also
calls uux with the `-a' option (however, I haven't figured out
what uux should really do with it in terms of the X. file.).

  [Lots more to say -- but mail me. An AmigaNet (not to me
confused with FidoNet despite that we run software that seems
to be compatible :-)) /AmigaUUCP mailing list has existed for
about year as amiga-mailer@doe.carleton.ca and amiga-mailer@fts1.UUCP
(doe doesn't like .UUCP addresses that much...). The list is fairly
dead right now, due in part because much of the activity has moved
to an echo called AMIGASTAND.]

  The rewrite of the rest [and the posting to Bob Page] awaits pipes
so that I can get rid of some of the temporary files. I don't like
temporary files.







--

  :!mcr!:
  Michael Richardson
                    Amiga
             v--------+
 HOME: mcr@julie.UUCP | SCHOOL: mcr@doe.carleton.ca
 Fido: 1:163/109.10<--+ WORK: michael@fts1.UUCP

peter@sugar.hackercorp.com (Peter da Silva) (12/07/89)

In article <1610.AA1610@julie> mcr@julie.UUCP (Michael Richardson) writes:
>   When I finally got annoyed at the silliness of command line
> passing, program launching (with exit codes) and the pr_CLI structure
> in general [this was at least 6 months ago] I began to build an
> alternate launching scheme, much more similar to the way the
> workbench startup message works.

Good for you.

> I based it on Leo Shwab's wblaunch
> code.

Leo's? Not mine? What's Leo's look like?

>   In order deal with who closes file descriptors, etc.. I wound
> up passing a bitmap to the sub-process telling it which file handles
> it can close.

Where in the Startup message?

> [Actually, I can also pass the `ToolWindow' field,
> and although I haven't done it yet, I always put 0 in the place
> where the Workbench message's `number of arguments' is so I could
> in fact be launched from Workbench...]

What's the point of this? Workbench will pass variable numbers of arguments,
and usually at least one (dirlock & name of the program itself).

> I'd like this field eventually
> to be $ffffffff. (I can pass any number of FileHandles though.
> They are installed in the Manx `_devtab' structure.)

More details, please.

>    if((pin=AllocMem(sizeof(struct FileHandle),MEMF_PUBLIC|MEMF_CLEAR))==NULL) {
>      return(NULL);
>    }
>    if((pout=Open("PIP:4096",2000L))==NULL) {
>      FreeMem(pin,sizeof(struct FileHandle));
>      return(NULL);
>    }
>    temp=(struct FileHandle*)BTOC(pout);
>    (*pin)=(*temp);
>    pin=(struct FileHandle*)CTOB(pin);

You're copying the file handles? Interesting. I wouldn't have expected that to
work... I don't think there's any guarantee of that.

(too bad there's no DupFH call. Commodore? Are you there Commodore? How about
 for 1.4?)

>   BTW: Programs compiled with the Rogue Startup code can continue to run
> from CLI, and if I ever make the WorkBench modification from the workbench
> as well.

Sounds good. Where's the code?

>   The rewrite of the rest [and the posting to Bob Page] awaits pipes
> so that I can get rid of some of the temporary files. I don't like
> temporary files.

Why don't you do this?

	sprintf(PipeName, "%s:rogue%dpipe%d", PipeDevice, ProcessId, Sequence++);
	pin = Open(PipeName, MODE_NEWFILE);
	pout = Open(PipeName, MODE_OLDFILE);

This way you won't have to depend on anyone having PIP:, and won't be playing
unportable games...
-- 
Peter "Have you hugged your wolf today" da Silva <peter@sugar.hackercorp.com>
`-_-'
 'U` "Really, a video game is nothing more than a Skinner box."
       -- Peter Merel <pete@basser.oz>

mcr@julie.UUCP (Michael Richardson) (12/16/89)

>In article <1610.AA1610@julie> mcr@julie.UUCP (Michael Richardson) writes:
>> alternate launching scheme, much more similar to the way the
>> workbench startup message works.
>
>Good for you.
>
>> I based it on Leo Shwab's wblaunch
>> code.
>
>Leo's? Not mine? What's Leo's look like?

  I honestly don't remember. Rest assured that I refered to some code
that you posted at some point too.

>Where in the Startup message?

  /* fast:exp/src/include/roguestar.h */

 struct RogueStartup {
   struct Message rs_Message;
   struct MsgPort *rs_Process;
   BPTR rs_Segment;
   LONG rs_dummy;             /* ALWAYS 0 -- would be NumArgs in WBStartup */
   char *rs_ToolWindow;       /* Usually NULL */
   int  rs_argc;
   char **rs_argv;
   int  rs_fhc;               /* Number of file handles to install */
   long *rs_fhv;
   int  rs_exitcode;
   BPTR rs_CurrentDir;
   char **rs_environ;         /* Arguments of environment space */
   int  rs_envcount;          /* Number of variables */
   char *rs_envspace;         /* Buffer containing variables */
   int  rs_envsize;           /* Size of buffer */
   long rs_closebits[2];      /* Close this filehandle (major kludge) */
 };

  Hmm... Guess I put in 64 bits for FileHandles...

  From boot:install/aztecc/include/workbench/startup.h:

   struct WBStartup {
     struct Message sm_Message;
     struct MsgPort * sm_Process;
     BPTR sm_Segment;
     LONG sm_NumArgs;
     char * sm_ToolWindow;
     struct WBArg * sm_ArgList;
   };

>What's the point of this? Workbench will pass variable numbers of arguments,
>and usually at least one (dirlock & name of the program itself).

  I don't want the locks. I pass the full file names. A possible modification
would use exec List instead of an array to hold the arguments.
  Note: the RogueLaunch program allocates memory for the argv and envp,
and copies. The child does own them. They are cleaned up by the cleanup
process. In order to provide the ability to invoke an synchronous task
and later decide to make it asynchronous (just don't close your window!)
I'll eventually turn this into rogue.library, and have it CreateProc()
an `init' process that will actually wait on the subprocesses, deallocate
storage and return error codes when you `RogueWait()' (or don't wait)
[who was it that was working on Edition 7 under AmigaDOS --- Doug Merrit
no? How is it going?]

>More details, please.

  fast:exp/src/rogue/_main.c:

  /* Copyright (C) 1986,1987 by Manx Software Systems, Inc. */

 [  What I'm to do about that line, I'm not sure yet. ]

  [blayh blah blah]

  pp = _FindTask(0L);
  if(alen) {
    _cli_parse(pp, alen, aptr);
    argc=_argc;
    argv=_argv;
    Enable_Abort = 1;
    _devtab[0].mode |= O_STDIO;             /* shouldn't close if CLI */
    _devtab[1].mode |= O_STDIO;
    goto normsetup;
  }
  else {
    int i;
    char *window;
    long wind;
    struct FileHandle *fhp;

    _WaitPort(&pp->pr_MsgPort);
    RogueStartMsg = (struct RogueStartup *)GetMsg(&pp->pr_MsgPort);
    if(RogueStartMsg->rs_dummy==0) {
      argv=RogueStartMsg->rs_argv;
      argc=RogueStartMsg->rs_argc;
      environ=RogueStartMsg->rs_environ;
      pp->pr_CurrentDir=RogueStartMsg->rs_CurrentDir;
      CurrentDir(RogueStartMsg->rs_CurrentDir);

      if(window=RogueStartMsg->rs_ToolWindow) {
        if(wind = _Open(window, MODE_OLDFILE)) {
          fhp = (struct FileHandle *) (wind << 2);
          pp->pr_ConsoleTask = (APTR) fhp->fh_Type;
          pp->pr_CIS = (BPTR)wind;
          pp->pr_COS = (BPTR)_Open("*", MODE_OLDFILE);
          goto normsetup;
        }
      }
      else if(RogueStartMsg->rs_fhc) {
        for(i=0; i<RogueStartMsg->rs_fhc; i++) {
          _devtab[i].fd=RogueStartMsg->rs_fhv[i];
          if(i<32) {
            if((RogueStartMsg->rs_closebits[0]&(1L << i))==0) {
              _devtab[i].mode |= O_STDIO;   /* shouldn't close if CLI */
            }
          }
        }
        pp->pr_CIS=_devtab[0].fd;
        pp->pr_COS=_devtab[1].fd;
      }
      else {
       normsetup:
        _devtab[0].fd = _Input();
        if(_devtab[1].fd = _Output())
                _devtab[2].fd = _Open("*", MODE_OLDFILE);
      }
    }
    else {
      /* We'd do the normal Workbench thing here */
      /* I suspect that this branch crashes. I don't run workbench and
         draw icons often enough to have every tried it.*/
      _exit(254);
    }
  }
  _exit(main(argc, argv));  /* so much for `void main()' :-) */
}

>    if((pin=AllocMem(sizeof(struct FileHandle),MEMF_PUBLIC|MEMF_CLEAR))==NULL) {
>      return(NULL);
>    }
>    if((pout=Open("PIP:4096",2000L))==NULL) {
                              ^^^^^

  man:util/ConMan.doc:
                                 ConMan V1.3
                   Copyright (c) 1987, 1988 by William S. Hawes
  ....

   ConMan is also a pipe-handler responding to the name PIP:NNNN, where
   NNNN is the "capacity" of the pipe.  It must be opened using a private
   packet called ACTION_DOUBLE (value 2000L), and the filehandle you get
   from a call to Open("PIP:",2000) counts for two and so can be cloned
   by your program.  After allocating a filehandle structure (using the
   AmigaDOS memory conventions), just copy the filehandle from Open()
   into the new structure --- and don't forget to Close() both of them!
   My WShell command shell uses the ConMan PIP: device as its pipe handler.

  Does this follow better now?

>(too bad there's no DupFH call. Commodore? Are you there Commodore? How about
> for 1.4?)

  YES. PLEASE.
  I believe that Colin Plumb had some other things to peeves having to
do with getting path names from FileHandles. I think they deserve to be
repeated...

 (Colin: remember that `message' you wrote me concerning CRCs? All 22k of it?
I posted it as crcinfo.zoo on 1:163/109 [aka atronx.UUCP, soon to be amiga.ottawa.on.ca].
The download count is quite respectable...)

>Sounds good. Where's the code?

  Coming. I want a working popen() call and I have to write the environment
variable stuff...

>Why don't you do this?
>
>        sprintf(PipeName, "%s:rogue%dpipe%d", PipeDevice, ProcessId, Sequence++);
>        pin = Open(PipeName, MODE_NEWFILE);
>        pout = Open(PipeName, MODE_OLDFILE);

  My last resort. Pipe: doesn't like.
  I've spent hours running dnet both here to school, and between myself and
other people, and I've _never_ gotten the scli server to work the DPIPE:,
or Matt's original pipe, or the 1.3 pipe...

  It took me three days to run Markus Wandel's exec disassembly because I
couldn't get the pipes to work. They just don't like me.




--

  :!mcr!:
  Michael Richardson
                    Amiga
             v--------+
 HOME: mcr@julie.UUCP | SCHOOL: mcr@doe.carleton.ca
 Fido: 1:163/109.10<--+ WORK: michael@fts1.UUCP

peter@sugar.hackercorp.com (Peter da Silva) (12/17/89)

In article <1718.AA1718@julie> mcr@julie.UUCP (Michael Richardson) writes:
>   /* fast:exp/src/include/roguestar.h */

>  struct RogueStartup {
>    struct Message rs_Message;
>    struct MsgPort *rs_Process;
>    BPTR rs_Segment;
>    LONG rs_dummy;             /* ALWAYS 0 -- would be NumArgs in WBStartup */
	Probably should be OK.

>    char *rs_ToolWindow;       /* Usually NULL */
	I'd leave in sm_ArgList as well.

>    int  rs_argc;
>    char **rs_argv;
	So far so good.

>    int  rs_fhc;               /* Number of file handles to install */
>    long *rs_fhv;
	Not sure about this part. UNIXY, to be sure. But is it useful to
	get that far ahead of AmigaDOS?

>    int  rs_exitcode;
	Good.

>    BPTR rs_CurrentDir;
	Good. But I'd put this in sm_ArgList[0]. And put in the real program
	name in there too. Keep compatability with WB stuff.

>    char **rs_environ;         /* Arguments of environment space */
>    int  rs_envcount;          /* Number of variables */
	Not a good idea. You want to co-operate with AmigaDOS, and that means
	using env:.

>    char *rs_envspace;         /* Buffer containing variables */
>    int  rs_envsize;           /* Size of buffer */
>    long rs_closebits[2];      /* Close this filehandle (major kludge) */
	Why is this a kludge? It fits in well with rs_fhc/rs_fhv.

	Of course, you should probably not do this unless you know you
	have a Rogue program.

>   I don't want the locks. I pass the full file names. A possible modification
> would use exec List instead of an array to hold the arguments.

There's a point to both. It'd be cooler to make RogueStartup a full superset
of WBStartup. Using some magic token to distinguish them.

>    ConMan is also a pipe-handler responding to the name PIP:NNNN...

Please don't make it depend on CONMAN, anyway.

> >        sprintf(PipeName, "%s:rogue%dpipe%d", PipeDevice, ProcessId, Sequence++);
> >        pin = Open(PipeName, MODE_NEWFILE);
> >        pout = Open(PipeName, MODE_OLDFILE);

>   My last resort. Pipe: doesn't like.

I can't parse this statement. PIPE: doesn't like what?

>   I've spent hours running dnet both here to school, and between myself and
> other people, and I've _never_ gotten the scli server to work the DPIPE:,
> or Matt's original pipe, or the 1.3 pipe...

You can make "PipeDevice" "PIP:" if you know you need that. I don't use
ConMan (irrational prejudice... it just feels icky to REPLACE con:), so I
could use P:, PIPE:, whatever...

What order are you opening the pipes in?
-- 
Peter "Have you hugged your wolf today" da Silva <peter@sugar.hackercorp.com>
`-_-'
 'U`  "I haven't lost my mind, it's backed up on tape somewhere"

tell@oscar.cs.unc.edu (Stephen Tell) (12/17/89)

In article <4778@sugar.hackercorp.com> peter@sugar.hackercorp.com (Peter da Silva) writes:
>In article <1718.AA1718@julie> mcr@julie.UUCP (Michael Richardson) writes:
>>   /* fast:exp/src/include/roguestar.h */

>>    char **rs_environ;         /* Arguments of environment space */
>>    int  rs_envcount;          /* Number of variables */
>	Not a good idea. You want to co-operate with AmigaDOS, and that means
>	using env:.
But env: is deficient in that its "global" and therefore you can't have
more than one environment.  Consider a 2-user system (using AUX: or dnet:)
or a multi-user BBS; each user may need his own environment.  I'm sure there
are other more mundane examples.

Now if we were all to use env:<process-id>/variable, and use links where only
a small part of the environment (or none at all) changed between a parent
and its child...   (What? no links? aren't they "in there" too?)

>There's a point to both. It'd be cooler to make RogueStartup a full superset
>of WBStartup. Using some magic token to distinguish them.
In general, this looks like a good idea.  CBM seems more likely to adopt it
as a standard somday (1.5?) if it fits well with existing stuff.

>Peter "Have you hugged your wolf today" da Silva <peter@sugar.hackercorp.com>
>`-_-'
> 'U`  "I haven't lost my mind, it's backed up on tape somewhere"

--------------------------------------------------------------------
Steve Tell					tell@wsmail.cs.unc.edu
CS Grad Student, UNC Chapel Hill.

peter@sugar.hackercorp.com (Peter da Silva) (12/17/89)

In article <11223@thorin.cs.unc.edu> tell@oscar.cs.unc.edu (Stephen Tell) writes:
> In article <4778@sugar.hackercorp.com> peter@sugar.hackercorp.com (Peter da Silva) writes:
> >In article <1718.AA1718@julie> mcr@julie.UUCP (Michael Richardson) writes:
> >>   /* fast:exp/src/include/roguestar.h */

> >>    char **rs_environ;         /* Arguments of environment space */
> >>    int  rs_envcount;          /* Number of variables */
> >	Not a good idea. You want to co-operate with AmigaDOS, and that means
> >	using env:.

> But env: is deficient in that its "global" and therefore you can't have
> more than one environment.

I understand this. However you should retain compatibility with the system
as it exists. Similarly, your programs should use AmigaDOS keyword argument
handling rather than UNIX -option style, and they should work properly under
the Workbench and CLI as well as RogueStartup.

If Getenv returns a different value depending on whether the program is
started from RogueStartup and CLI that's a bad thing.

> >There's a point to both. It'd be cooler to make RogueStartup a full superset
> >of WBStartup. Using some magic token to distinguish them.

> In general, this looks like a good idea.  CBM seems more likely to adopt it
> as a standard somday (1.5?) if it fits well with existing stuff.

Also people are more likely to use it today.
-- 
Peter "Have you hugged your wolf today" da Silva <peter@sugar.hackercorp.com>
`-_-'
 'U`  "I haven't lost my mind, it's backed up on tape somewhere"

ricks@eagle.iscs.com (Rick Schaeffer 99) (12/18/89)

I've been following the discussion on using pipes with interest.  Recently
I hacked this small routine which uses the ARP 1.3 AsyncRun() function and
AmigaDos 1.3 pipes to provide a Unix-like popen() function.  It seems to
work fine on anything I've tried (including standard AmigaDos programs like
type and list that were written in BCPL).  It probably has some bugs but,
since it's short and *does* seem to work I thought I'd post it.  Comments,
improvements, and whatnot welcome.  It was compiled with Lattice 5.04 but
shouldn't be too tough to make with Aztec.

/*
** popen.c
** Written by Rick Schaeffer (ricks@isc-br.isc-br.com)
NAME
     popen, pclose - initiate I/O to/from a process

SYNOPSIS
     #include <stdio.h>

     FILE *popen(command, type)
     char *command, *type;

     pclose(stream)
     FILE *stream;

DESCRIPTION
     The arguments to popen are pointers to null-terminated
     strings containing respectively a command line and an
     I/O mode, either "r" for reading or "w" for writing.  It
     creates a pipe between the calling process and the command
     to be executed.  The value returned is a stream pointer that
     can be used (as appropriate) to write to the standard input
     of the command or read from its standard output.

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

     Because stdio files are shared, a type "r" command may be
     used as an input filter, and a type "w" as an output filter.

DIAGNOSTICS
     Popen returns a null pointer if files or processes cannot be
     created.

     Pclose returns -1 if stream is not associated with a
     `popened' command.

*/

#include <stdio.h>
#include <string.h>
#include <proto/dos.h>
#include <proto/exec.h>
#include <arp/arpbase.h>
#include <ios1.h>

struct ArpBase						*ArpBase;
static struct Process				*thistask;
static struct ProcessControlBlock	pcb;

struct pstruct {
	FILE	*fptr;
	struct ZombieMsg	exitmsg;
	};

struct pstruct poarray[6];

FILE *popen(cmd,mode)
char	*cmd;
char	*mode;
{
	char 		*parms;
	static char tempname[] = "pipe:pXXX.XXX";
	char 		*pname,redir[20],*mktemp();
	short		i;
	int			pmode;
	struct pstruct	*poptr;
	BPTR		pfd;

	if (thistask == NULL)
		thistask = (struct Process *) FindTask(NULL);
	if (ArpBase == NULL)
		ArpBase = (struct ArpBase *) OpenLibrary(ArpName,ArpVersion);
	if (ArpBase == NULL) {
		fprintf(stderr,"Arp Open Failed\n");
		return(NULL);
		}
	poptr = NULL;
	for (i=0; i<6; i++) {
		if (poarray[i].fptr == NULL) {
			poptr = &poarray[i];
			break;
			}
		}
	if (poptr == NULL) {
		fprintf(stderr,"popen: Unable to find an open pipe\n");
		return(NULL);
		}
	if (strcmp(mode,"r") == 0)
		pmode = MODE_NEWFILE;
	else if (strcmp(mode,"w") == 0)
		pmode = MODE_OLDFILE;
	else {
		fprintf(stderr,"popen: Mode must be 'r' or 'w'\n");
		return(NULL);
		}
	tempname[5] = 'a' + i;
	strcpy(redir,tempname);
	pname = mktemp(redir);				/* set up a pipe: file name */

	setmem(&pcb, sizeof(struct ProcessControlBlock), 0);
	/* Now get the child's stack and priority set up */
	if (thistask->pr_CLI) {
		struct CommandLineInterface		*cli;
		cli = (struct CommandLineInterface *) BADDR(thistask->pr_CLI);
		pcb.pcb_StackSize = cli->cli_DefaultStack << 2;
		}
	else
		pcb.pcb_StackSize = thistask->pr_StackSize;
	pcb.pcb_Pri = thistask->pr_Task.tc_Node.ln_Pri;
	/* Open the side of the pipe for the child */
	pfd = Open(pname,pmode);
	if (pfd == 0) {
		fprintf(stderr,"popen: Unable to open pipe file\n");
		return(NULL);
		}
	if (pmode == MODE_NEWFILE)
		pcb.pcb_Output = pfd;
	else
		pcb.pcb_Input = pfd;

	/* Locate the break between command and parameters */
	parms = strpbrk(cmd," \t");
	if (parms) {
		*parms++ = 0;
		parms = stpblk(parms);
		if (parms && *parms == 0)
			parms = NULL;
		}
	/* Create a port for the child's exit message */
	poptr->exitmsg.zm_ExecMessage.mn_ReplyPort = CreatePort(NULL,0);
	if (poptr->exitmsg.zm_ExecMessage.mn_ReplyPort == 0) {
		fprintf(stderr,"popen: Couldn't create message port\n");
		return(NULL);
		}
	pcb.pcb_LastGasp = &poptr->exitmsg;

	if (ASyncRun(cmd,parms,&pcb) < 0) {
		fprintf(stderr,"popen: AsyncRun failed\n");
		DeletePort(poptr->exitmsg.zm_ExecMessage.mn_ReplyPort);
		return(NULL);
		}
	/* Now open our side of the pipe */
	poptr->fptr = fopen(pname,mode);
	if (poptr->fptr == NULL) {
		fprintf(stderr,"popen: Unable to open pipe file %s\n",pname);
		DeletePort(poptr->exitmsg.zm_ExecMessage.mn_ReplyPort);
		return(NULL);
		}
	return(poptr->fptr);
}

pclose(fptr)
FILE	*fptr;
{
	short		i;

	for (i=0; i<6; i++)
		if (poarray[i].fptr == fptr)
			break;
	if (i > 5) {
		fprintf(stderr,"popen: DISASTER...couldn't find file pointer in pclose\n");
		exit(1);
		}
	fclose(fptr);
	WaitPort(poarray[i].exitmsg.zm_ExecMessage.mn_ReplyPort);
	poarray[i].fptr = NULL;
	DeletePort(poarray[i].exitmsg.zm_ExecMessage.mn_ReplyPort);
	return(poarray[i].exitmsg.zm_ReturnCode);
}

char *mktemp(template)
char *template;
{
	register char *cp;
	register unsigned long val;

	cp = template;
	cp += strlen(cp);
	for (val = (unsigned long) FindTask(0L) ; ; )
		if (*--cp == 'X') {
			*cp = val%10 + '0';
			val /= 10;
		} else if (*cp != '.')
			break;

	if (*++cp != 0) {
		*cp = 'A';
		while (access(template, 0) == 0) {
			if (*cp == 'Z') {
				*template = 0;
				break;
			}
			++*cp;
		}
	} else {
		if (access(template, 0) == 0)
			*template = 0;
	}
	return template;
}
--
Rick Schaeffer          UUCP:  uunet!isc-br.isc-br.com!ricks
ISC-Bunker Ramo                ricks@isc-br.isc-br.com
Box TAF-C8              Phone: (509)927-5114
Spokane, WA  99220

kim@uts.amdahl.com (Kim DeVaughn) (12/20/89)

In article <4780@sugar.hackercorp.com>, peter@sugar.hackercorp.com (Peter da Silva) writes:
> In article <11223@thorin.cs.unc.edu> tell@oscar.cs.unc.edu (Stephen Tell) writes:
> > In article <4778@sugar.hackercorp.com> peter@sugar.hackercorp.com (Peter da Silva) writes:
> > >In article <1718.AA1718@julie> mcr@julie.UUCP (Michael Richardson) writes:
> > >>   /* fast:exp/src/include/roguestar.h */
> 
> > >>    char **rs_environ;         /* Arguments of environment space */
> > >>    int  rs_envcount;          /* Number of variables */
> > >	Not a good idea. You want to co-operate with AmigaDOS, and that means
> > >	using env:.
> 
> > But env: is deficient in that its "global" and therefore you can't have
> > more than one environment.
> 
> I understand this. However you should retain compatibility with the system
> as it exists. Similarly, your programs should use AmigaDOS keyword argument
> handling rather than UNIX -option style, and they should work properly under
> the Workbench and CLI as well as RogueStartup.

If you opt for compatibility as Peter suggests, could you please make it
settable.  Alot of people prefer -option style specifications.

Similarly with a solution to the env: problem.

/kim

-- 
UUCP:  kim@amdahl.amdahl.com
  or:  {sun,decwrl,hplabs,pyramid,uunet,oliveb,ames}!amdahl!kim
DDD:   408-746-8462
USPS:  Amdahl Corp.  M/S 249,  1250 E. Arques Av,  Sunnyvale, CA 94086
BIX:   kdevaughn     GEnie:   K.DEVAUGHN     CIS:   76535,25