a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
a20af62000-09-26Fredrik Hübinette (Hubbe) 
8f6e2e2001-01-19Per Hedbor constant create_process = __builtin.create_process;
dad69b2003-03-01Henrik Grubbström (Grubba) #if constant(__builtin.TraceProcess) constant TraceProcess = __builtin.TraceProcess; #endif
7f12112003-01-02Martin Nilsson //! Slightly polished version of @[create_process]. //! @seealso //! @[create_process]
8f6e2e2001-01-19Per Hedbor class Process {
4d18352003-11-11Henrik Grubbström (Grubba)  inherit create_process;
7448562003-09-20David Gourdelier  static function(Process:void) read_cb; static function(Process:void) timeout_cb;
8f6e2e2001-01-19Per Hedbor 
7f12112003-01-02Martin Nilsson  //! @param args //! Either a command line array, as the command_args
e65ef02003-09-24Henrik Grubbström (Grubba)  //! argument to @[create_process()], or a string that
7f12112003-01-02Martin Nilsson  //! will be splitted into a command line array by
e65ef02003-09-24Henrik Grubbström (Grubba)  //! calling @[split_quoted_string()] in an operating //! system dependant mode.
7448562003-09-20David Gourdelier  //! @param m //! In addition to the modifiers that @[create_process] accepts, //! this object also accepts //! @mapping //! @member function(Process:void) "read_callback" //! This callback is called when there is data to be read //! from the process. //! @member function(Process:void) "timeout_callback" //! This callback is called if the process times out. //! @member int "timeout" //! The time it takes for the process to time out. Default is //! 15 seconds. //! @endmapping
7f12112003-01-02Martin Nilsson  //! @seealso //! @[create_process], @[split_quoted_string]
7448562003-09-20David Gourdelier  static void create( string|array(string) args, void|mapping(string:mixed) m )
8f6e2e2001-01-19Per Hedbor  {
e65ef02003-09-24Henrik Grubbström (Grubba)  if( stringp( args ) ) { args = split_quoted_string( [string]args #ifdef __NT__ ,1 #endif /* __NT__ */ ); }
b7f61b2005-04-09Martin Nilsson  if( !m )
ecbfe12003-04-27Martin Nilsson  ::create( [array(string)]args );
b7f61b2005-04-09Martin Nilsson  else { ::create( [array(string)]args, [mapping(string:mixed)]m );
7448562003-09-20David Gourdelier 
b7f61b2005-04-09Martin Nilsson  if(read_cb=m->read_callback) call_out(watcher, 0.1);
7448562003-09-20David Gourdelier 
b7f61b2005-04-09Martin Nilsson  if( (timeout_cb=m->timeout_callback) || m->timeout ) call_out(killer, m->timeout||15); }
7448562003-09-20David Gourdelier  } static void destroy() { remove_call_out(watcher); remove_call_out(killer); } static void watcher() { // It was another sigchld, but not one from our process. if(::status()==0) call_out(watcher, 0.1); else { remove_call_out(killer); if(read_cb) read_cb(this); } } static void killer() { remove_call_out(watcher); #if constant(kill) ::kill(signum("SIGKILL")); #endif if(timeout_cb) timeout_cb(this);
8f6e2e2001-01-19Per Hedbor  } }
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
dd9b5e2003-12-19Marcus Comstedt #if constant(exece)
970edb2002-08-09Martin Nilsson //!
8d49101998-01-21Fredrik Hübinette (Hubbe) int exec(string file,string ... foo)
c2a4061997-02-06Fredrik Hübinette (Hubbe) {
bcfe4f1997-06-09John W. Pierce  if (sizeof(file)) { string path;
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
8a98bb2002-11-15Martin Nilsson  if(has_value(file,"/"))
ecbfe12003-04-27Martin Nilsson  return exece(combine_path(getcwd(),file),foo, [mapping(string:string)]getenv());
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
ecbfe12003-04-27Martin Nilsson  path=[string]getenv("PATH");
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
b6277d1997-06-10Fredrik Hübinette (Hubbe)  foreach(path ? path/":" : ({}) , path)
bcfe4f1997-06-09John W. Pierce  if(file_stat(path=combine_path(path,file)))
ecbfe12003-04-27Martin Nilsson  return exece(path, foo, [mapping(string:string)]getenv());
bcfe4f1997-06-09John W. Pierce  }
c2a4061997-02-06Fredrik Hübinette (Hubbe)  return 69; }
dd9b5e2003-12-19Marcus Comstedt #endif
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
476ff22000-12-18Mirar (Pontus Hagland) static array(string) search_path_entries=0;
970edb2002-08-09Martin Nilsson  //!
476ff22000-12-18Mirar (Pontus Hagland) string search_path(string command) { if (command=="" || command[0]=='/') return command; if (!search_path_entries) { array(string) e=(getenv("PATH")||"")/":"-({""}); multiset(string) filter=(<>); search_path_entries=({}); foreach (e,string s) { string t; if (s[0]=='~') // some shells allow ~-expansion in PATH {
ecbfe12003-04-27Martin Nilsson  if (s[0..1]=="~/" && (t=[string]getenv("HOME")))
476ff22000-12-18Mirar (Pontus Hagland)  s=t+s[1..]; else { // expand user? } } if (!filter[s] /* && directory exist */ ) { search_path_entries+=({s}); filter[s]=1; } } } foreach (search_path_entries, string path) { string p=combine_path(path,command); Stdio.Stat s=file_stat(p); if (s && s->mode&0111) return p; } return 0; }
970edb2002-08-09Martin Nilsson //!
98bfd81998-04-16Fredrik Hübinette (Hubbe) string sh_quote(string s) { return replace(s, ({"\\", "'", "\"", " "}), ({"\\\\", "\\'", "\\\"","\\ "})); }
970edb2002-08-09Martin Nilsson //!
a656b22003-09-24Henrik Grubbström (Grubba) array(string) split_quoted_string(string s, int(0..1)|void nt_mode)
c2a4061997-02-06Fredrik Hübinette (Hubbe) {
a656b22003-09-24Henrik Grubbström (Grubba)  // Remove initial white-space.
52fa582000-07-23Fredrik Hübinette (Hubbe)  sscanf(s,"%*[ \n\t]%s",s);
a656b22003-09-24Henrik Grubbström (Grubba)  // Prefix interresting characters with NUL, // and split on NUL.
be5bb72000-07-11Fredrik Hübinette (Hubbe)  s=replace(s,
a656b22003-09-24Henrik Grubbström (Grubba)  ({"\"", "'", "\\", " ", "\t", "\n", "\0"}), ({"\0\"","\0'","\0\\","\0 ","\0\t","\0\n", "\0\0"}));
4755b72000-02-18Henrik Grubbström (Grubba)  array(string) x=s/"\0"; array(string) ret=({x[0]});
8392231998-01-08Fredrik Hübinette (Hubbe)  for(int e=1;e<sizeof(x);e++) {
a656b22003-09-24Henrik Grubbström (Grubba)  if (!sizeof(x[e])) { // Escaped NUL. ret[-1] += "\0"; e++; continue; }
8392231998-01-08Fredrik Hübinette (Hubbe)  switch(x[e][0]) { case '"': ret[-1]+=x[e][1..];
be5bb72000-07-11Fredrik Hübinette (Hubbe)  while(x[++e][0]!='"') {
ead9722003-01-20Martin Nilsson  if(sizeof(x[e])==1 && x[e][0]=='\\' && x[e+1][0]=='"') e++;
be5bb72000-07-11Fredrik Hübinette (Hubbe)  ret[-1]+=x[e]; }
8392231998-01-08Fredrik Hübinette (Hubbe)  ret[-1]+=x[e][1..]; break; case '\'': ret[-1]+=x[e][1..];
e5fa3b2001-01-22Fredrik Hübinette (Hubbe)  while(x[++e][0]!='\'') ret[-1]+=x[e];
8392231998-01-08Fredrik Hübinette (Hubbe)  ret[-1]+=x[e][1..]; break; case '\\':
ead9722003-01-20Martin Nilsson  if(sizeof(x[e])>1)
8392231998-01-08Fredrik Hübinette (Hubbe)  {
a656b22003-09-24Henrik Grubbström (Grubba)  if (nt_mode) { // On NT we only escape special characters with \; // other \'s we keep verbatim. ret[-1]+=x[e]; } else { ret[-1]+=x[e][1..]; }
8392231998-01-08Fredrik Hübinette (Hubbe)  }else{
a656b22003-09-24Henrik Grubbström (Grubba)  // Escaped special character.
8392231998-01-08Fredrik Hübinette (Hubbe)  ret[-1]+=x[++e]; } break; case ' ':
be5bb72000-07-11Fredrik Hübinette (Hubbe)  case '\t': case '\n':
ead9722003-01-20Martin Nilsson  while(sizeof(x[e])==1)
f43b942000-07-23Fredrik Hübinette (Hubbe)  { if(e+1 < sizeof(x)) { if((<' ','\t','\n'>) [x[e+1][0]]) e++; else break; }else{
be5bb72000-07-11Fredrik Hübinette (Hubbe)  return ret;
f43b942000-07-23Fredrik Hübinette (Hubbe)  } }
be5bb72000-07-11Fredrik Hübinette (Hubbe)  ret+=({x[e][1..]});
8392231998-01-08Fredrik Hübinette (Hubbe)  break; default: ret[-1]+="\0"+x[e]; break; } } return ret; }
4ec92d2005-02-10Martin Stjernholm Process spawn(string command, void|Stdio.Stream stdin, void|Stdio.Stream stdout, void|Stdio.Stream stderr, // These aren't used. Seems to be part of something unfinished. /mast //function|void cleanup, mixed ... args ) //! Spawns a process that executes @[command] as a command shell //! statement ("@expr{/bin/sh -c @[command]@}" for Unix, "@expr{cmd /c //! @[command]@}" for Windows).
970edb2002-08-09Martin Nilsson //!
4ec92d2005-02-10Martin Stjernholm //! @param stdin //! @param stdout //! @param stderr //! Stream objects to use as standard input, standard output and //! standard error, respectively, for the created process. The //! corresponding streams for this process are used for those that //! are left out. //! //! @returns //! Returns a @[Process.Process] object for the created process. //! //! @seealso //! @[system], @[popen]
8392231998-01-08Fredrik Hübinette (Hubbe) {
ecbfe12003-04-27Martin Nilsson  mapping(string:mixed) data=(["env":getenv()]);
8392231998-01-08Fredrik Hübinette (Hubbe)  if(stdin) data->stdin=stdin; if(stdout) data->stdout=stdout; if(stderr) data->stderr=stderr;
dc10f22001-10-12Tomas Nilsson #if defined(__NT__)
4ec92d2005-02-10Martin Stjernholm  // if the command string command is not quoted, add double quotes to
dc10f22001-10-12Tomas Nilsson  // make sure it is not modified by create_process
4ec92d2005-02-10Martin Stjernholm  if (sizeof(command) <= 1 || command[0] != '\"' || command[sizeof(command)-1] != '\"') command = "\"" + command + "\""; return create_process(({ "cmd", "/c", command }),data);
dc10f22001-10-12Tomas Nilsson #elif defined(__amigaos__)
4ec92d2005-02-10Martin Stjernholm  return create_process(split_quoted_string(command),data);
bf2b451998-11-23Marcus Comstedt #else /* !__NT__||__amigaos__ */
4ec92d2005-02-10Martin Stjernholm  return create_process(({ "/bin/sh", "-c", command }),data);
bf2b451998-11-23Marcus Comstedt #endif /* __NT__||__amigaos__ */
8392231998-01-08Fredrik Hübinette (Hubbe) 
c2a4061997-02-06Fredrik Hübinette (Hubbe) }
eb77b92004-05-10Martin Nilsson //! @decl string popen(string command)
4ec92d2005-02-10Martin Stjernholm //! Executes @[command] as a shell statement ("@expr{/bin/sh -c //! @[command]@}" for Unix, "@expr{cmd /c @[command]@}" for Windows), //! waits until it has finished and returns the result as a string. //! //! @seealso //! @[system], @[spawn]
eb77b92004-05-10Martin Nilsson  //! @decl Stdio.FILE popen(string command, string mode)
cb960a2004-05-01Dan Nelson //! Open a "process" for reading or writing. The @[command] is executed
4ec92d2005-02-10Martin Stjernholm //! as a shell statement ("@expr{/bin/sh -c @[command]@}" for Unix, //! "@expr{cmd /c @[command]@}" for Windows). The parameter @[mode] //! should be one of the following letters:
cb960a2004-05-01Dan Nelson //! @string
4ec92d2005-02-10Martin Stjernholm //! @value "r"
cb960a2004-05-01Dan Nelson //! Open for reading. Data written by the process to stdout //! is available for read.
4ec92d2005-02-10Martin Stjernholm //! @value "w"
cb960a2004-05-01Dan Nelson //! Open for writing. Data written to the file is available //! to the process on stdin. //! @endstring
4ec92d2005-02-10Martin Stjernholm //! //! @seealso //! @[system], @[spawn]
eb77b92004-05-10Martin Nilsson  Stdio.FILE|string popen(string s, string|void mode) { if(mode) return fpopen(s,mode); else return fpopen(s)->read(); } static Stdio.FILE fpopen(string s, string|void mode)
c2a4061997-02-06Fredrik Hübinette (Hubbe) {
cb960a2004-05-01Dan Nelson  Stdio.FILE f = Stdio.FILE(); if (!f) error("Popen failed. (couldn't create file)\n");
c2a4061997-02-06Fredrik Hübinette (Hubbe) 
cb960a2004-05-01Dan Nelson  Stdio.File p = f->pipe();
c2a4061997-02-06Fredrik Hübinette (Hubbe)  if(!p) error("Popen failed. (couldn't create pipe)\n");
cb960a2004-05-01Dan Nelson  if (mode == "w")
4ec92d2005-02-10Martin Stjernholm  spawn(s, p, 0, 0, /*destruct, f*/);
cb960a2004-05-01Dan Nelson  else
4ec92d2005-02-10Martin Stjernholm  spawn(s, 0, p, 0, /*destruct, f*/);
ef293f1997-05-07Henrik Grubbström (Grubba)  p->close();
c2a4061997-02-06Fredrik Hübinette (Hubbe)  destruct(p);
cb960a2004-05-01Dan Nelson  return f; }
4ec92d2005-02-10Martin Stjernholm int system(string command, void|Stdio.Stream stdin, void|Stdio.Stream stdout, void|Stdio.Stream stderr) //! Executes @[command] as a shell statement ("@expr{/bin/sh -c //! @[command]@}" for Unix, "@expr{cmd /c @[command]@}" for Windows), //! waits until it has finished and returns its return value.
970edb2002-08-09Martin Nilsson //!
4ec92d2005-02-10Martin Stjernholm //! @param stdin //! @param stdout //! @param stderr //! Stream objects to use as standard input, standard output and //! standard error, respectively, for the created process. The //! corresponding streams for this process are used for those that //! are left out. //! //! @seealso //! @[spawn], @[popen]
c2a4061997-02-06Fredrik Hübinette (Hubbe) {
4ec92d2005-02-10Martin Stjernholm  return spawn(command, stdin, stdout, stderr)->wait();
c2a4061997-02-06Fredrik Hübinette (Hubbe) }
cb119a1997-04-07Fredrik Hübinette (Hubbe) 
782bca2000-09-16Per Hedbor #ifndef __NT__
8392231998-01-08Fredrik Hübinette (Hubbe) #if constant(fork)
04c34c1998-01-01Fredrik Hübinette (Hubbe) constant fork = predef::fork; #endif
cb119a1997-04-07Fredrik Hübinette (Hubbe) 
04c34c1998-01-01Fredrik Hübinette (Hubbe) #if constant(exece) constant exece = predef::exece; #endif
df67f71997-12-14Mirar (Pontus Hagland) 
17f8722004-01-05H. William Welliver III #if constant(fork) && constant(exece)
970edb2002-08-09Martin Nilsson  //!
df67f71997-12-14Mirar (Pontus Hagland) class Spawn { object stdin; object stdout; object stderr; array(object) fd;
8392231998-01-08Fredrik Hübinette (Hubbe)  object pid;
df67f71997-12-14Mirar (Pontus Hagland) 
ecbfe12003-04-27Martin Nilsson  private object low_spawn(array(Stdio.File) fdp, array(Stdio.File) fd_to_close,
4755b72000-02-18Henrik Grubbström (Grubba)  string cmd, void|array(string) args,
8392231998-01-08Fredrik Hübinette (Hubbe)  void|mapping(string:string) env, string|void cwd)
df67f71997-12-14Mirar (Pontus Hagland)  {
ecbfe12003-04-27Martin Nilsson  Stdio.File pie, pied; // interprocess communication
8392231998-01-08Fredrik Hübinette (Hubbe)  object pid;
df67f71997-12-14Mirar (Pontus Hagland)  pie=Stdio.File(); pied=pie->pipe(); if(!(pid=fork())) { mixed err=catch { if(cwd && !cd(cwd)) {
a1c95e2001-12-20Martin Nilsson  error( "pike: cannot change cwd to "+cwd+ ": "+strerror(errno())+"\n" );
df67f71997-12-14Mirar (Pontus Hagland)  } if (sizeof(fdp)>0 && fdp[0]) fdp[0]->dup2(Stdio.File("stdin")); if (sizeof(fdp)>1 && fdp[1]) fdp[1]->dup2(Stdio.File("stdout")); if (sizeof(fdp)>2 && fdp[2]) fdp[2]->dup2(Stdio.File("stderr")); /* dup2 fdd[3..] here FIXME FIXME */
ecbfe12003-04-27Martin Nilsson  foreach (fd_to_close, Stdio.File f)
df67f71997-12-14Mirar (Pontus Hagland)  if (objectp(f)) { f->close(); destruct(f); } pie->close(); destruct(pie); pied->set_close_on_exec(1); if (env) exece(cmd,args||({}),env); else exece(cmd,args||({}));
a1c95e2001-12-20Martin Nilsson  error( "pike: failed to exece "+cmd+ ": "+strerror(errno())+"\n" );
df67f71997-12-14Mirar (Pontus Hagland)  }; pied->write(encode_value(err)); exit(1); } foreach (fdp,object f) if (objectp(f)) { f->close(); destruct(f); } pied->close(); destruct(pied); mixed err=pie->read(); if (err && err!="") throw(decode_value(err)); pie->close(); destruct(pie); return pid; }
8a98bb2002-11-15Martin Nilsson  //!
df67f71997-12-14Mirar (Pontus Hagland)  void create(string cmd, void|array(string) args, void|mapping(string:string) env, string|void cwd, void|array(object(Stdio.File)|void) ownpipes, void|array(object(Stdio.File)|void) fds_to_close) { if (!ownpipes) { stdin=Stdio.File(); stdout=Stdio.File(); stderr=Stdio.File(); fd=({stdin->pipe(),stdout->pipe(),stderr->pipe()}); fds_to_close=({stdin,stdout,stderr}); } else { fd=ownpipes; if (sizeof(fd)>0) stdin=fd[0]; else stdin=0; if (sizeof(fd)>1) stdout=fd[1]; else stdout=0; if (sizeof(fd)>2) stderr=fd[2]; else stderr=0; } pid=low_spawn(fd,fds_to_close||({}),cmd,args,env,cwd); }
8392231998-01-08Fredrik Hübinette (Hubbe) #if constant(kill)
8a98bb2002-11-15Martin Nilsson  //! int kill(int signal)
df67f71997-12-14Mirar (Pontus Hagland)  { return predef::kill(pid,signal); }
8392231998-01-08Fredrik Hübinette (Hubbe) #endif
df67f71997-12-14Mirar (Pontus Hagland) 
8a98bb2002-11-15Martin Nilsson  //!
8392231998-01-08Fredrik Hübinette (Hubbe)  int wait()
df67f71997-12-14Mirar (Pontus Hagland)  {
8392231998-01-08Fredrik Hübinette (Hubbe)  return pid->wait();
df67f71997-12-14Mirar (Pontus Hagland)  } // void set_done_callback(function foo,mixed ... args); // int result(); // array rusage(); }
04c34c1998-01-01Fredrik Hübinette (Hubbe) #endif
782bca2000-09-16Per Hedbor #endif