aboutsummaryrefslogtreecommitdiffstats
path: root/filepopen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'filepopen.cpp')
-rw-r--r--filepopen.cpp1191
1 files changed, 1191 insertions, 0 deletions
diff --git a/filepopen.cpp b/filepopen.cpp
new file mode 100644
index 0000000..afcd184
--- /dev/null
+++ b/filepopen.cpp
@@ -0,0 +1,1191 @@
+// filepopen.cpp
+// Revision 6-feb-2005
+
+#include "file.h"
+
+#include "blassic.h"
+#include "error.h"
+#include "showerror.h"
+#include "sysvar.h"
+#include "util.h"
+using util::to_string;
+
+#include "trace.h"
+
+#include <iostream>
+using std::cerr;
+using std::endl;
+
+#ifndef BLASSIC_USE_WINDOWS
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+// Suggested in autoconf manual.
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(star_val) ((unsigned) (stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+
+#include <fcntl.h>
+#include <sys/poll.h>
+
+#else
+
+#include <windows.h>
+#undef min
+#undef max
+
+#endif
+
+// para strerror (errno)
+#include <string.h>
+#include <errno.h>
+
+#include <assert.h>
+
+#define ASSERT assert
+
+namespace blassic {
+
+namespace file {
+
+//***********************************************
+// BlFilePopen
+//***********************************************
+
+class BlFilePopen : public BlFile {
+protected:
+ virtual char getcharfrombuffer ()= 0;
+ virtual void outstring (const std::string & str)= 0;
+ virtual void outchar (char c)= 0;
+ bool forread;
+ bool forwrite;
+ bool witherr;
+public:
+ BlFilePopen (OpenMode nmode);
+ bool isfile () const { return true; }
+ virtual void closein ()= 0;
+ virtual void closeout ()= 0;
+ virtual bool eof ()= 0;
+ void flush ();
+ virtual void endline ()= 0;
+ virtual std::string read (size_t n)= 0;
+ virtual void getline (std::string & str, bool endline= true)= 0;
+ virtual bool poll ()= 0;
+};
+
+BlFilePopen::BlFilePopen (OpenMode nmode) :
+ BlFile (nmode)
+{
+ if (nmode & Input)
+ forread= true;
+ if (nmode & Output)
+ forwrite= true;
+ if (nmode & WithErr)
+ witherr= true;
+ const OpenMode checkmode= OpenMode (Input | Output | WithErr);
+ if ( (nmode | checkmode) != checkmode)
+ throw ErrFileMode;
+}
+
+void BlFilePopen::flush ()
+{
+}
+
+#ifdef BLASSIC_USE_WINDOWS
+
+//***********************************************
+// Auxiliary functions and classes for windows
+//***********************************************
+
+namespace {
+
+class ProtectHandle {
+public:
+ ProtectHandle () :
+ handle (INVALID_HANDLE_VALUE)
+ { }
+ ProtectHandle (HANDLE handle) :
+ handle (handle)
+ { }
+ ~ProtectHandle ()
+ {
+ close ();
+ }
+ void reset (HANDLE newhandle)
+ {
+ close ();
+ handle= newhandle;
+ }
+ void close ()
+ {
+ if (handle != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle (handle);
+ handle= INVALID_HANDLE_VALUE;
+ }
+ }
+ void release ()
+ {
+ handle= INVALID_HANDLE_VALUE;
+ }
+private:
+ HANDLE handle;
+};
+
+std::string makecommand (const std::string & str)
+{
+ const char * comspec= getenv ("COMSPEC");
+ if (comspec == NULL)
+ comspec= "C:\\COMMAND.COM";
+ std::string command= comspec;
+ command+= " /C ";
+ command+= str;
+ return command;
+}
+
+void create_pipe (HANDLE & hread, HANDLE & hwrite)
+{
+ if (CreatePipe (& hread, & hwrite, NULL, 0) == 0)
+ {
+ showlasterror ("CreatePipe failed");
+ throw ErrFunctionCall;
+ }
+}
+
+HANDLE duplicate_inherit (HANDLE handle, HANDLE current)
+{
+ HANDLE newhandle;
+ if (DuplicateHandle (current, handle,
+ current, & newhandle,
+ 0,
+ TRUE, // Inherited
+ DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS) == 0)
+ {
+ showlasterror ("DuplicateHandle hread failed");
+ throw ErrFunctionCall;
+ }
+ return newhandle;
+}
+
+HANDLE createnull ()
+{
+ SECURITY_ATTRIBUTES sec;
+ sec.nLength= sizeof sec;
+ sec.lpSecurityDescriptor= NULL;
+ sec.bInheritHandle= TRUE;
+
+ const HANDLE hnull= CreateFile ("NUL",
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ & sec, // Inheritable.
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (hnull == INVALID_HANDLE_VALUE)
+ {
+ showlasterror ("Error creating /dev/null handle");
+ throw ErrOperatingSystem;
+ }
+ return hnull;
+}
+
+void launchchild (const std::string & str,
+ bool forread, bool forwrite, bool witherr,
+ HANDLE & hreadpipe, HANDLE & hwritepipe, HANDLE & childprocess)
+{
+ TRACEFUNC (tr, "launchchild");
+
+ const std::string command= makecommand (str);
+
+ HANDLE current= GetCurrentProcess ();
+ const HANDLE hnull= createnull ();
+ ProtectHandle prnull (hnull);
+
+ STARTUPINFO start;
+ start.cb= sizeof start;
+ GetStartupInfo (& start);
+ start.dwFlags= STARTF_USESTDHANDLES;
+ start.hStdInput= hnull;
+ start.hStdOutput= hnull;
+ start.hStdError= hnull;
+ DWORD creationflags= 0;
+
+ HANDLE hwritechild;
+ ProtectHandle prreadpipe;
+ ProtectHandle prwritechild;
+ if (forread)
+ {
+ TRMESSAGE (tr, "Creating pipe input");
+
+ HANDLE hwrite;
+ create_pipe (hreadpipe, hwrite);
+ prreadpipe.reset (hreadpipe);
+ hwritechild= duplicate_inherit (hwrite, current);
+ prwritechild.reset (hwritechild);
+ start.hStdOutput= hwritechild;
+ if (witherr)
+ start.hStdError= hwritechild;
+ }
+ HANDLE hreadchild;
+ ProtectHandle prwritepipe;
+ ProtectHandle prreadchild;
+ if (forwrite)
+ {
+ TRMESSAGE (tr, "Creating pipe output");
+
+ // In Windows 95 or 98 detach the process from the
+ // actual console, without that the parent process
+ // gets blocked.
+ // Now seems inneccessary, and conflicts with
+ // bidirectional mode.
+ #if 0
+ {
+ OSVERSIONINFO osv;
+ osv.dwOSVersionInfoSize= sizeof (OSVERSIONINFO);
+ GetVersionEx (& osv);
+ if (osv.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
+ creationflags|= DETACHED_PROCESS;
+ }
+ #endif
+
+ HANDLE hread;
+ create_pipe (hread, hwritepipe);
+ prwritepipe.reset (hwritepipe);
+ hreadchild= duplicate_inherit (hread, current);
+ prreadchild.reset (hreadchild);
+ start.hStdInput= hreadchild;
+ }
+
+ TRMESSAGE (tr, "Creating pipe process");
+
+ PROCESS_INFORMATION procinfo;
+ if (CreateProcess (
+ NULL, (char *) command.c_str (),
+ NULL, NULL,
+ TRUE, // Inherit handles
+ creationflags,
+ NULL, NULL, & start, & procinfo) == 0)
+ {
+ showlasterror ( ("CreateProcess " + command +
+ " failed in POPEN").c_str () );
+ throw ErrFunctionCall;
+ }
+
+ // No need to close here the child handles, ProtectHandle
+ // takes care of him.
+
+ // Release protection of the parent handles.
+ prreadpipe.release ();
+ prwritepipe.release ();
+
+ childprocess= procinfo.hProcess;
+ CloseHandle (procinfo.hThread);
+}
+
+class CriticalSection {
+public:
+ CriticalSection ()
+ {
+ InitializeCriticalSection (& object);
+ }
+ ~CriticalSection ()
+ {
+ DeleteCriticalSection (& object);
+ }
+ CriticalSection (const CriticalSection &); // Forbidden
+ void operator= (const CriticalSection &); // Forbidden
+ void enter ()
+ {
+ EnterCriticalSection (& object);
+ }
+ void leave ()
+ {
+ LeaveCriticalSection (& object);
+ }
+private:
+ CRITICAL_SECTION object;
+};
+
+class CriticalLock {
+public:
+ CriticalLock (CriticalSection & crit) :
+ crit (crit)
+ {
+ crit.enter ();
+ }
+ ~CriticalLock ()
+ {
+ crit.leave ();
+ }
+private:
+ CriticalSection & crit;
+};
+
+class Event
+{
+public:
+ Event ()
+ {
+ hevent= CreateEvent (
+ NULL, // Default security
+ FALSE, // Automatic
+ FALSE, // Initially not signaled
+ NULL // Without name
+ );
+ if (hevent == NULL)
+ {
+ showlasterror ();
+ throw ErrOperatingSystem;
+ }
+ }
+ ~Event ()
+ {
+ CloseHandle (hevent);
+ }
+ void wait ()
+ {
+ WaitForSingleObject (hevent, INFINITE);
+ }
+ void set ()
+ {
+ SetEvent (hevent);
+ }
+ void reset ()
+ {
+ ResetEvent (hevent);
+ }
+private:
+ HANDLE hevent;
+};
+
+} // namespace
+
+//***********************************************
+// BlFilePopenWindows
+//***********************************************
+
+class BlFilePopenWindows : public BlFilePopen {
+public:
+ BlFilePopenWindows (const std::string & str, OpenMode mode);
+ ~BlFilePopenWindows ();
+ void closein ();
+ void closeout ();
+ bool eof ();
+ void endline ();
+ std::string read (size_t n);
+ void getline (std::string & str, bool endline= true);
+ bool poll ();
+private:
+ static const size_t bufsize= 4096;
+ char buffer [bufsize];
+ size_t bufpos, bufread;
+ void readbuffer ();
+ char getcharfrombuffer ();
+
+ void read_in_thread ();
+
+ void outstring (const std::string & str);
+ void outchar (char c);
+
+ CriticalSection critical;
+ Event canread;
+ bool nowreading;
+ Event hasread;
+ HANDLE hreadpipe, hwritepipe;
+ HANDLE hprocess;
+ HANDLE hreadthread;
+ bool finishthread;
+ bool error_reading;
+ bool eof_found;
+
+ friend DWORD WINAPI thread_popen_read (LPVOID param);
+ friend class ProtectThread;
+};
+
+class ProtectThread {
+public:
+ ProtectThread (BlFilePopenWindows & popen) :
+ popen (popen),
+ released (false)
+ { }
+ ~ProtectThread ()
+ {
+ if (! released && popen.hreadthread != NULL)
+ {
+ popen.finishthread= true;
+ ResumeThread (popen.hreadthread);
+ CloseHandle (popen.hreadthread);
+ }
+ }
+ void release ()
+ {
+ released= true;
+ }
+private:
+ BlFilePopenWindows & popen;
+ bool released;
+};
+
+DWORD WINAPI thread_popen_read (LPVOID param)
+{
+ BlFilePopenWindows & popen= *
+ reinterpret_cast <BlFilePopenWindows *> (param);
+ if (popen.finishthread)
+ return 0;
+
+ try
+ {
+ popen.read_in_thread ();
+ }
+ catch (...)
+ {
+ popen.error_reading= true;
+ popen.nowreading= false;
+ popen.hasread.set ();
+ }
+
+ return 0;
+}
+
+BlFilePopenWindows::BlFilePopenWindows
+ (const std::string & str, OpenMode nmode) :
+ BlFilePopen (nmode)
+{
+ TRACEFUNC (tr, "BlFilePopenWindows::BlFilePopenWindows");
+
+ hprocess= INVALID_HANDLE_VALUE;
+ hreadpipe= INVALID_HANDLE_VALUE;
+ hwritepipe= INVALID_HANDLE_VALUE;
+ hreadthread= NULL;
+ nowreading= false;
+ finishthread= false;
+ error_reading= false;
+ eof_found= false;
+
+ if (forread)
+ {
+ DWORD idthread;
+ hreadthread= CreateThread (NULL, 1024,
+ thread_popen_read, this,
+ CREATE_SUSPENDED, & idthread);
+ if (hreadthread == NULL)
+ {
+ showlasterror ();
+ throw ErrOperatingSystem;
+ }
+ }
+ ProtectThread protect (* this);
+ launchchild (str, forread, forwrite, witherr,
+ hreadpipe, hwritepipe, hprocess);
+ if (forread)
+ {
+ ResumeThread (hreadthread);
+ nowreading= true;
+ canread.set ();
+ }
+ protect.release ();
+}
+
+BlFilePopenWindows::~BlFilePopenWindows ()
+{
+ TRACEFUNC (tr, "BlFilePopenWindows::~BlFilePopenWindows");
+
+ // This is to workaround errors in C++ Builder.
+ if (hprocess == INVALID_HANDLE_VALUE)
+ return;
+
+ if (hreadthread != NULL)
+ {
+ finishthread= true;
+ CloseHandle (hreadthread);
+ hreadthread= NULL;
+ nowreading= true;
+ hasread.reset ();
+ canread.set ();
+ }
+ // Close the pipe before wait for termination,
+ // the child process can be waiting for
+ // input.
+ //CloseHandle (hpipe);
+ if (hreadpipe != INVALID_HANDLE_VALUE)
+ CloseHandle (hreadpipe);
+ if (hwritepipe != INVALID_HANDLE_VALUE)
+ CloseHandle (hwritepipe);
+
+ WaitForSingleObject (hprocess, INFINITE);
+ DWORD exitcode;
+ BOOL r= GetExitCodeProcess (hprocess, & exitcode);
+ if (r == 0)
+ {
+ showlasterror ();
+ }
+ CloseHandle (hprocess);
+ if (r == 0)
+ {
+ throw ErrOperatingSystem;
+ }
+ TRMESSAGE (tr, "Exit code: " + to_string (exitcode) );
+ sysvar::set (sysvar::ShellResult,
+ static_cast <BlChar> (exitcode & 0xFF) );
+}
+
+char BlFilePopenWindows::getcharfrombuffer ()
+{
+ {
+ CriticalLock lock (critical);
+ if (bufpos < bufread)
+ return buffer [bufpos++];
+ }
+
+ if (error_reading)
+ throw ErrOperatingSystem;
+ if (eof_found)
+ return 0;
+
+ readbuffer ();
+ hasread.wait ();
+
+ if (error_reading)
+ throw ErrOperatingSystem;
+ if (eof_found)
+ return 0;
+
+ return buffer [bufpos++];
+}
+
+void BlFilePopenWindows::closein ()
+{
+ TRACEFUNC (tr, "BlFilePopenWindows::closein");
+
+ if (! getmode () & Input)
+ throw ErrFileMode;
+ finishthread= true;
+ CloseHandle (hreadpipe);
+ hreadpipe= INVALID_HANDLE_VALUE;
+
+ critical.enter ();
+ nowreading= true;
+ hasread.reset ();
+ canread.set ();
+ critical.leave ();
+
+ CloseHandle (hreadthread);
+ hreadthread= INVALID_HANDLE_VALUE;
+}
+
+void BlFilePopenWindows::closeout ()
+{
+ TRACEFUNC (tr, "BlFilePopenWindows::closeout");
+
+ if (! getmode () & Output)
+ throw ErrFileMode;
+ CloseHandle (hwritepipe);
+ hwritepipe= INVALID_HANDLE_VALUE;
+}
+
+bool BlFilePopenWindows::eof ()
+{
+ TRACEFUNC (tr, "BlFilePopenWindows::eof");
+
+ if (! getmode () & Input)
+ throw ErrFileMode;
+
+ {
+ CriticalLock lock (critical);
+ if (bufpos < bufread)
+ return false;
+ }
+
+ readbuffer ();
+ hasread.wait ();
+
+ if (error_reading)
+ throw ErrOperatingSystem;
+ if (!eof_found)
+ {
+ ASSERT (bufpos < bufread);
+ }
+ return eof_found;
+}
+
+void BlFilePopenWindows::endline ()
+{
+ outstring ("\r\n");
+}
+
+std::string BlFilePopenWindows::read (size_t n)
+{
+ if (! getmode () & Input)
+ throw ErrFileMode;
+
+ std::string str;
+ for (size_t i= 0; i < n; ++i)
+ {
+ char c= getcharfrombuffer ();
+ if (c == 0 && eof_found)
+ break;
+ str+= c;
+ }
+ return str;
+}
+
+void BlFilePopenWindows::getline (std::string & str, bool)
+{
+ TRACEFUNC (tr, "BlFilePopenWindows::getline");
+
+ if (! getmode () & Input)
+ throw ErrFileMode;
+
+ str= std::string ();
+ char c;
+ while ( (c= getcharfrombuffer () ) != '\r' && c != '\n')
+ {
+ if (c == 0)
+ {
+ if (eof_found)
+ {
+ if (str.empty () )
+ throw ErrPastEof;
+ else
+ break;
+ }
+ }
+ else
+ str+= c;
+ }
+
+ if (c == '\r')
+ getcharfrombuffer ();
+}
+
+void BlFilePopenWindows::readbuffer ()
+{
+ if (finishthread | error_reading | eof_found)
+ return;
+ CriticalLock lock (critical);
+ if (nowreading)
+ return;
+ nowreading= true;
+ hasread.reset ();
+ canread.set ();
+}
+
+void BlFilePopenWindows::read_in_thread ()
+{
+ do
+ {
+ canread.wait ();
+ if (finishthread)
+ break;
+ if (hreadpipe == INVALID_HANDLE_VALUE)
+ break;
+ DWORD bytesread= 0;
+ if (ReadFile (hreadpipe, buffer, bufsize, & bytesread, NULL) == 0)
+ {
+ DWORD r= GetLastError ();
+ if (r != ERROR_BROKEN_PIPE)
+ {
+ error_reading= true;
+ }
+ else
+ {
+ eof_found= true;
+ }
+
+ CriticalLock lock (critical);
+ nowreading= false;
+ }
+ else
+ {
+ CriticalLock lock (critical);
+ if (bytesread == 0)
+ {
+ // This is not supposed to happen
+ // in an anonymous pipe.
+ error_reading= true;
+ }
+ bufpos= 0;
+ bufread= bytesread;
+ nowreading= false;
+ }
+ hasread.set ();
+ } while (! error_reading & ! eof_found & ! finishthread);
+}
+
+void BlFilePopenWindows::outstring (const std::string & str)
+{
+ TRACEFUNC (tr, "BlFilePopenWindows::outstring");
+
+ if (! getmode () & Output)
+ throw ErrFileMode;
+
+ const char * to= str.data ();
+ std::string::size_type l= str.size ();
+ DWORD written;
+ //WriteFile (hpipe, to, l, & written, NULL);
+ if (WriteFile (hwritepipe, to, l, & written, NULL) == 0)
+ {
+ showlasterror ();
+ throw ErrOperatingSystem;
+ }
+}
+
+void BlFilePopenWindows::outchar (char c)
+{
+ if (! getmode () & Output)
+ throw ErrFileMode;
+
+ DWORD written;
+ //WriteFile (hpipe, & c, 1, & written, NULL);
+ if (WriteFile (hwritepipe, & c, 1, & written, NULL) == 0)
+ {
+ showlasterror ();
+ throw ErrOperatingSystem;
+ }
+}
+
+bool BlFilePopenWindows::poll ()
+{
+ if (! getmode () & Input)
+ throw ErrFileMode;
+
+ {
+ CriticalLock lock (critical);
+ if (bufpos < bufread)
+ return true;
+ }
+
+ readbuffer ();
+
+ return false;
+}
+
+#else
+// No Windows
+
+//***********************************************
+// Auxiliary functions and classes for unix
+//***********************************************
+
+namespace {
+
+class ProtectHandle {
+public:
+ ProtectHandle (int handle) :
+ handle (handle)
+ { }
+ ~ProtectHandle ()
+ {
+ close ();
+ }
+ void close ()
+ {
+ if (handle != -1)
+ {
+ ::close (handle);
+ handle= -1;
+ }
+ }
+ void release ()
+ {
+ handle= -1;
+ }
+private:
+ int handle;
+};
+
+void exec_command (const std::string & str)
+{
+ const char * strShell= getenv ("SHELL");
+ if (! strShell)
+ strShell= "/bin/sh";
+
+ execlp (strShell, strShell, "-c",
+ str.c_str (), (char *) 0);
+
+ // If something fails:
+ exit (127);
+}
+
+void createpipe (int & hread, int & hwrite)
+{
+ int handlepair [2];
+ if (pipe (handlepair) != 0)
+ {
+ showlasterror ("Creating pipe");
+ throw ErrOperatingSystem;
+ }
+ hread= handlepair [0];
+ hwrite= handlepair [1];
+}
+
+void duporfail (int oldfd, int newfd)
+{
+ if (dup2 (oldfd, newfd) == -1)
+ exit (127);
+}
+
+void stdtonull ()
+{
+ int newstd= open ("/dev/null", O_RDWR);
+ if (newstd == -1)
+ exit (127);
+ duporfail (newstd, STDIN_FILENO);
+ duporfail (newstd, STDOUT_FILENO);
+ duporfail (newstd, STDERR_FILENO);
+ close (newstd);
+}
+
+void launchchild (const std::string & str,
+ bool forread, bool forwrite, bool witherr,
+ int & readhandle, int & writehandle, pid_t & childpid)
+{
+ TRACEFUNC (tr, "launchchild");
+
+ if (! forread && ! forwrite)
+ {
+ ASSERT (false);
+ throw ErrBlassicInternal;
+ }
+
+ int writechild= -1;
+ if (forread)
+ createpipe (readhandle, writechild);
+ ProtectHandle prrhandle (readhandle);
+ ProtectHandle prwchild (writechild);
+
+ int readchild= -1;
+ if (forwrite)
+ createpipe (readchild, writehandle);
+ ProtectHandle prwhandle (writehandle);
+ ProtectHandle prrchild (readchild);
+
+ childpid= fork ();
+ switch (childpid)
+ {
+ case pid_t (-1):
+ // Fork has failed.
+ showlasterror ();
+ throw ErrOperatingSystem;
+ case pid_t (0):
+ // Child process.
+ // No need to take care of handles in case of fail
+ // here, the process will be terminated.
+ stdtonull ();
+ if (forread)
+ {
+ prrhandle.close ();
+ duporfail (writechild, STDOUT_FILENO);
+ if (witherr)
+ duporfail (writechild, STDERR_FILENO);
+ prwchild.close ();
+ }
+ if (forwrite)
+ {
+ prwhandle.close ();
+ duporfail (readchild, STDIN_FILENO);
+ prrchild.close ();
+ }
+ exec_command (str);
+ break; // To avoid warnings.
+ default:
+ // Parent process.
+ if (forread)
+ {
+ prwchild.close ();
+ prrhandle.release ();
+ }
+ if (forwrite)
+ {
+ prrchild.close ();
+ prwhandle.release ();
+ }
+ }
+}
+
+} // namespace
+
+//***********************************************
+// BlFilePopenUnix
+//***********************************************
+
+class BlFilePopenUnix : public BlFilePopen {
+public:
+ BlFilePopenUnix (const std::string & str, OpenMode mode);
+ ~BlFilePopenUnix ();
+ void closein ();
+ void closeout ();
+ bool eof ();
+ void endline ();
+ std::string read (size_t n);
+ void getline (std::string & str, bool endline= true);
+ bool poll ();
+private:
+ static const size_t bufsize= 4096;
+ char buffer [bufsize];
+ size_t bufpos, bufread;
+ void readbuffer ();
+ char getcharfrombuffer ();
+ size_t do_readbuffer (char * buffer, size_t bytes);
+
+ void closeread ();
+ void closewrite ();
+ void outstring (const std::string & str);
+ void outchar (char c);
+
+ int readhandle;
+ int writehandle;
+ pid_t childpid;
+};
+
+BlFilePopenUnix::BlFilePopenUnix
+ (const std::string & str, OpenMode nmode) :
+ BlFilePopen (nmode)
+{
+ TRACEFUNC (tr, "BlFilePopenUnix::BlFilePopenUnix");
+ TRMESSAGE (tr, str);
+
+ readhandle= -1;
+ writehandle= -1;
+ childpid= -1;
+
+ launchchild (str, forread, forwrite, witherr,
+ readhandle, writehandle, childpid);
+}
+
+BlFilePopenUnix::~BlFilePopenUnix ()
+{
+ TRACEFUNC (tr, "BlFilePopenUnix::~BlFilePopenUnix");
+
+ // First close the pipes, the child can be waiting for input.
+ closeread ();
+ closewrite ();
+
+ int status;
+ if (waitpid (childpid, & status, 0) == -1 || ! WIFEXITED (status) )
+ {
+ showlasterror ();
+ throw ErrOperatingSystem;
+ }
+ TRMESSAGE (tr, "Exit code: " + to_string (WEXITSTATUS (status) ) );
+ sysvar::set (sysvar::ShellResult, WEXITSTATUS (status) );
+}
+
+char BlFilePopenUnix::getcharfrombuffer ()
+{
+ if (bufpos >= bufread)
+ {
+ readbuffer ();
+ if (bufread == 0)
+ return '\0';
+ }
+ return buffer [bufpos++];
+}
+
+void BlFilePopenUnix::closein ()
+{
+ TRACEFUNC (tr, "BlFilePopenUnix::closein");
+
+ if (! getmode () & Input)
+ throw ErrFileMode;
+
+ closeread ();
+}
+
+void BlFilePopenUnix::closeout ()
+{
+ TRACEFUNC (tr, "BlFilePopenUnix::closeout");
+
+ if (! getmode () & Output)
+ throw ErrFileMode;
+
+ closewrite ();
+}
+
+bool BlFilePopenUnix::eof ()
+{
+ TRACEFUNC (tr, "BlFilePopenUnix::eof");
+
+ if (! getmode () & Input)
+ throw ErrFileMode;
+
+ if (bufpos < bufread)
+ return false;
+ readbuffer ();
+ return bufread == 0;
+}
+
+void BlFilePopenUnix::endline ()
+{
+ outchar ('\n');
+}
+
+std::string BlFilePopenUnix::read (size_t n)
+{
+ if (! getmode () & Input)
+ throw ErrFileMode;
+
+ std::string str;
+ for (size_t i= 0; i < n; ++i)
+ {
+ char c= getcharfrombuffer ();
+ if (c == '\0')
+ {
+ if (eof () )
+ break;
+ else
+ str+= c;
+ }
+ else
+ str+= c;
+ }
+ return str;
+}
+
+void BlFilePopenUnix::getline (std::string & str, bool)
+{
+ TRACEFUNC (tr, "BlFilePopenUnix::getline");
+
+ if (! getmode () & Input)
+ throw ErrFileMode;
+
+ str= std::string ();
+ char c;
+ while ( (c= getcharfrombuffer () ) != '\r' && c != '\n')
+ {
+ if (c == '\0')
+ {
+ if (eof () )
+ {
+ if (str.empty () )
+ throw ErrPastEof;
+ else
+ break;
+ }
+ else
+ break;
+ }
+ else
+ str+= c;
+ }
+}
+
+void BlFilePopenUnix::closeread ()
+{
+ if (readhandle != -1)
+ {
+ close (readhandle);
+ readhandle= -1;
+ }
+}
+
+void BlFilePopenUnix::closewrite ()
+{
+ if (writehandle != -1)
+ {
+ close (writehandle);
+ writehandle= -1;
+ }
+}
+
+void BlFilePopenUnix::readbuffer ()
+{
+ bufpos= 0;
+ bufread= do_readbuffer (buffer, bufsize);
+}
+
+size_t BlFilePopenUnix::do_readbuffer (char * buffer, size_t bytes)
+{
+ TRACEFUNC (tr, "BlFilePopenUnix::do_readbuffer");
+
+ size_t bytesread= ::read (readhandle, buffer, bytes);
+ if (bytesread == size_t (-1) )
+ bytesread= 0;
+ return bytesread;
+}
+
+void BlFilePopenUnix::outstring (const std::string & str)
+{
+ TRACEFUNC (tr, "BlFilePopenUnix::outstring");
+
+ if (! getmode () & Output)
+ throw ErrFileMode;
+
+ const char * to= str.data ();
+ std::string::size_type l= str.size ();
+ if (write (writehandle, to, l) == -1)
+ {
+ showlasterror ();
+ throw ErrOperatingSystem;
+ }
+}
+
+void BlFilePopenUnix::outchar (char c)
+{
+ if (! getmode () & Output)
+ throw ErrFileMode;
+
+ if (write (writehandle, & c, 1) == -1)
+ {
+ showlasterror ();
+ throw ErrOperatingSystem;
+ }
+}
+
+bool BlFilePopenUnix::poll ()
+{
+ TRACEFUNC (tr, "BlFilePopenUnix::poll");
+
+ if (! getmode () & Input)
+ throw ErrFileMode;
+ struct pollfd data [1];
+ data [0].fd= readhandle;
+ data [0].events= POLLIN | POLLPRI;
+ switch (::poll (data, 1, 0) )
+ {
+ case 0:
+ return false;
+ case 1:
+ return true;
+ default:
+ showlasterror ();
+ throw ErrOperatingSystem;
+ }
+}
+
+#endif
+// Windows or unix
+
+//***********************************************
+// newBlFilePopen
+//***********************************************
+
+BlFile * newBlFilePopen (const std::string & name, OpenMode mode)
+{
+ #ifdef BLASSIC_USE_WINDOWS
+
+ return new BlFilePopenWindows (name, mode);
+
+ #else
+
+ return new BlFilePopenUnix (name, mode);
+
+ #endif
+}
+
+} // namespace file
+
+} // namespace blassic
+
+// End of filepopen.cpp
Un proyecto texto-plano.xyz