summaryrefslogtreecommitdiff
path: root/usr/src/lib/libast/common/sfio/sfpopen.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libast/common/sfio/sfpopen.c')
-rw-r--r--usr/src/lib/libast/common/sfio/sfpopen.c293
1 files changed, 293 insertions, 0 deletions
diff --git a/usr/src/lib/libast/common/sfio/sfpopen.c b/usr/src/lib/libast/common/sfio/sfpopen.c
new file mode 100644
index 0000000000..2ee4b50cd1
--- /dev/null
+++ b/usr/src/lib/libast/common/sfio/sfpopen.c
@@ -0,0 +1,293 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1985-2007 AT&T Knowledge Ventures *
+* and is licensed under the *
+* Common Public License, Version 1.0 *
+* by AT&T Knowledge Ventures *
+* *
+* A copy of the License is available at *
+* http://www.opensource.org/licenses/cpl1.0.txt *
+* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* Glenn Fowler <gsf@research.att.com> *
+* David Korn <dgk@research.att.com> *
+* Phong Vo <kpv@research.att.com> *
+* *
+***********************************************************************/
+#include "sfhdr.h"
+
+/* Create a coprocess.
+** Written by Kiem-Phong Vo.
+*/
+
+#if _PACKAGE_ast
+#include <proc.h>
+#else
+
+#define EXIT_NOTFOUND 127
+
+#define READ 0
+#define WRITE 1
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+static char Meta[1<<CHAR_BIT], **Path;
+
+/* execute command directly if possible; else use the shell */
+#if __STD_C
+static void execute(const char* argcmd)
+#else
+static void execute(argcmd)
+char* argcmd;
+#endif
+{
+ reg char *s, *cmd, **argv, **p, *interp;
+ reg int n;
+
+ /* define interpreter */
+ if(!(interp = getenv("SHELL")) || !interp[0])
+ interp = "/bin/sh";
+
+ if(strcmp(interp,"/bin/sh") != 0 && strcmp(interp,"/bin/ksh") != 0 )
+ { if(access(interp,X_OK) == 0)
+ goto do_interp;
+ else interp = "/bin/sh";
+ }
+
+ /* if there is a meta character, let the shell do it */
+ for(s = (char*)argcmd; *s; ++s)
+ if(Meta[(uchar)s[0]])
+ goto do_interp;
+
+ /* try to construct argv */
+ if(!(cmd = (char*)malloc(strlen(argcmd)+1)) )
+ goto do_interp;
+ strcpy(cmd,argcmd);
+ if(!(argv = (char**)malloc(16*sizeof(char*))) )
+ goto do_interp;
+ for(n = 0, s = cmd;; )
+ { while(isspace(s[0]))
+ s += 1;
+ if(s[0] == 0)
+ break;
+
+ /* new argument */
+ argv[n++] = s;
+ if((n%16) == 0 && !(argv = (char**)realloc(argv,(n+16)*sizeof(char*))) )
+ goto do_interp;
+
+ /* make this into a C string */
+ while(s[0] && !isspace(s[0]))
+ s += 1;
+ if(!s[0])
+ *s++ = 0;
+ }
+ if(n == 0)
+ goto do_interp;
+ argv[n] = NIL(char*);
+
+ /* get the command name */
+ cmd = argv[0];
+ for(s = cmd+strlen(cmd)-1; s >= cmd; --s)
+ if(*s == '/')
+ break;
+ argv[0] = s+1;
+
+ /* Non-standard pathnames as in nDFS should be handled by the shell */
+ for(s = cmd+strlen(cmd)-1; s >= cmd+2; --s)
+ if(s[0] == '.' && s[-1] == '.' && s[-2] == '.')
+ goto do_interp;
+
+ if(cmd[0] == '/' ||
+ (cmd[0] == '.' && cmd[1] == '/') ||
+ (cmd[0] == '.' && cmd[1] == '.' && cmd[2] == '/') )
+ { if(access(cmd,X_OK) != 0)
+ goto do_interp;
+ else execv(cmd,argv);
+ }
+ else
+ { for(p = Path; *p; ++p)
+ { s = sfprints("%s/%s", *p, cmd);
+ if(access(s,X_OK) == 0)
+ execv(s,argv);
+ }
+ }
+
+ /* if get here, let the interpreter do it */
+do_interp:
+ for(s = interp+strlen(interp)-1; s >= interp; --s)
+ if(*s == '/')
+ break;
+ execl(interp, s+1, "-c", argcmd, NIL(char*));
+ _exit(EXIT_NOTFOUND);
+}
+
+#endif /*_PACKAGE_ast*/
+
+#if __STD_C
+Sfio_t* sfpopen(Sfio_t* f, const char* command, const char* mode)
+#else
+Sfio_t* sfpopen(f,command,mode)
+Sfio_t* f;
+char* command; /* command to execute */
+char* mode; /* mode of the stream */
+#endif
+{
+#if _PACKAGE_ast
+ reg Proc_t* proc;
+ reg int sflags;
+ reg long flags;
+ reg int pflags;
+ char* av[4];
+
+ if (!command || !command[0] || !mode)
+ return 0;
+ sflags = _sftype(mode, NiL, NiL);
+
+ if(f == (Sfio_t*)(-1))
+ { /* stdio compatibility mode */
+ f = NIL(Sfio_t*);
+ pflags = 1;
+ }
+ else pflags = 0;
+
+ flags = 0;
+ if (sflags & SF_READ)
+ flags |= PROC_READ;
+ if (sflags & SF_WRITE)
+ flags |= PROC_WRITE;
+ av[0] = "sh";
+ av[1] = "-c";
+ av[2] = (char*)command;
+ av[3] = 0;
+ if (!(proc = procopen(0, av, 0, 0, flags)))
+ return 0;
+ if (!(f = sfnew(f, NIL(Void_t*), (size_t)SF_UNBOUND,
+ (sflags&SF_READ) ? proc->rfd : proc->wfd, sflags|((sflags&SF_RDWR)?0:SF_READ))) ||
+ _sfpopen(f, (sflags&SF_READ) ? proc->wfd : -1, proc->pid, pflags) < 0)
+ {
+ if (f) sfclose(f);
+ procclose(proc);
+ return 0;
+ }
+ procfree(proc);
+ return f;
+#else
+ reg int pid, fd, pkeep, ckeep, sflags;
+ int stdio, parent[2], child[2];
+ Sfio_t sf;
+
+ /* set shell meta characters */
+ if(Meta[0] == 0)
+ { reg char* s;
+ Meta[0] = 1;
+ for(s = "!@#$%&*(){}[]:;<>~`'|\"\\"; *s; ++s)
+ Meta[(uchar)s[0]] = 1;
+ }
+ if(!Path)
+ Path = _sfgetpath("PATH");
+
+ /* sanity check */
+ if(!command || !command[0] || !mode)
+ return NIL(Sfio_t*);
+ sflags = _sftype(mode,NIL(int*),NIL(int*));
+
+ /* make pipes */
+ parent[0] = parent[1] = child[0] = child[1] = -1;
+ if(sflags&SF_RDWR)
+ { if(syspipef(parent) < 0)
+ goto error;
+ if((sflags&SF_RDWR) == SF_RDWR && syspipef(child) < 0)
+ goto error;
+ }
+
+ switch((pid = fork()) )
+ {
+ default : /* in parent process */
+ if(sflags&SF_READ)
+ { pkeep = READ; ckeep = WRITE; }
+ else { pkeep = WRITE; ckeep = READ; }
+
+ if(f == (Sfio_t*)(-1))
+ { /* stdio compatibility mode */
+ f = NIL(Sfio_t*);
+ stdio = 1;
+ }
+ else stdio = 0;
+
+ /* make the streams */
+ if(!(f = sfnew(f,NIL(Void_t*),(size_t)SF_UNBOUND,parent[pkeep],sflags|((sflags&SF_RDWR)?0:SF_READ))))
+ goto error;
+ if(sflags&SF_RDWR)
+ { CLOSE(parent[!pkeep]);
+ SETCLOEXEC(parent[pkeep]);
+ if((sflags&SF_RDWR) == SF_RDWR)
+ { CLOSE(child[!ckeep]);
+ SETCLOEXEC(child[ckeep]);
+ }
+ }
+
+ /* save process info */
+ fd = (sflags&SF_RDWR) == SF_RDWR ? child[ckeep] : -1;
+ if(_sfpopen(f,fd,pid,stdio) < 0)
+ { (void)sfclose(f);
+ goto error;
+ }
+
+ return f;
+
+ case 0 : /* in child process */
+ /* determine what to keep */
+ if(sflags&SF_READ)
+ { pkeep = WRITE; ckeep = READ; }
+ else { pkeep = READ; ckeep = WRITE; }
+
+ /* zap fd that we don't need */
+ if(sflags&SF_RDWR)
+ { CLOSE(parent[!pkeep]);
+ if((sflags&SF_RDWR) == SF_RDWR)
+ CLOSE(child[!ckeep]);
+ }
+
+ /* use sfsetfd to make these descriptors the std-ones */
+ SFCLEAR(&sf,NIL(Vtmutex_t*));
+
+ /* must be careful so not to close something useful */
+ if((sflags&SF_RDWR) == SF_RDWR && pkeep == child[ckeep])
+ if((child[ckeep] = sysdupf(pkeep)) < 0)
+ _exit(EXIT_NOTFOUND);
+
+ if(sflags&SF_RDWR)
+ { if (parent[pkeep] != pkeep)
+ { sf.file = parent[pkeep];
+ CLOSE(pkeep);
+ if(sfsetfd(&sf,pkeep) != pkeep)
+ _exit(EXIT_NOTFOUND);
+ }
+ if((sflags&SF_RDWR) == SF_RDWR && child[ckeep] != ckeep)
+ { sf.file = child[ckeep];
+ CLOSE(ckeep);
+ if(sfsetfd(&sf,ckeep) != ckeep)
+ _exit(EXIT_NOTFOUND);
+ }
+ }
+
+ execute(command);
+ return NIL(Sfio_t*);
+
+ case -1 : /* error */
+ error:
+ if(parent[0] >= 0)
+ { CLOSE(parent[0]); CLOSE(parent[1]); }
+ if(child[0] >= 0)
+ { CLOSE(child[0]); CLOSE(child[1]); }
+ return NIL(Sfio_t*);
+ }
+#endif /*_PACKAGE_ast*/
+}