diff --git a/src/runtime_methods/net.cpp b/src/runtime_methods/net.cpp index 35ba77e..3f97658 100644 --- a/src/runtime_methods/net.cpp +++ b/src/runtime_methods/net.cpp @@ -452,6 +452,7 @@ namespace Tesses::CrossLang TCallable* callable; TDictionary* dict; TServerHeapObject* server; + TClassObject* clsObj; if(GetObjectHeap(this->obj,callable)) { GCList ls2(this->ls->GetGC()); @@ -474,6 +475,23 @@ namespace Tesses::CrossLang return result; } } + else if(GetObjectHeap(this->obj,clsObj) && clsObj->HasMethod("","Handle")) + { + GCList ls2(this->ls->GetGC()); + auto res = CreateDictionaryFromServerContext(ls2,&ctx); + bool result; + auto callableO = clsObj->GetValue("","Handle"); + TCallable* callable; + if(GetObjectHeap(callableO, callable)) + { + auto out = callable->Call(ls2,{res}); + if(GetObject(out,result)) + { + return result; + } + } + + } else if(GetObjectHeap(this->obj,server)) { return server->server->Handle(ctx); @@ -536,6 +554,59 @@ namespace Tesses::CrossLang } return nullptr; } + class HttpServerNativeObject : public TNativeObject + { + HttpServer* server; + public: + HttpServerNativeObject(uint16_t port, TObjectHttpServer* httpServer,bool printIps) + { + server=new HttpServer(port,httpServer,true,printIps); + } + HttpServerNativeObject(std::string unixPath, TObjectHttpServer* httpServer) + { + server=new HttpServer(unixPath,httpServer,true); + } + std::string TypeName() + { + return "Net.Http.HttpServer"; + } + TObject CallMethod(GCList& ls, std::string key, std::vector args) + { + if(key == "getPort") + { + return (int64_t)server->GetPort(); + } + if(key == "StartAccepting") + server->StartAccepting(); + return nullptr; + } + ~HttpServerNativeObject() + { + delete server; + } + }; + + static TObject Net_Http_HttpServer(GCList& ls, std::vector args,TRootEnvironment* env) + { + int64_t port; + std::string pathStr; + if(GetArgument(args,1,port)) + { + bool printIPs=true; + GetArgument(args,2,printIPs); + TObjectHttpServer* httpServer = new TObjectHttpServer(ls.GetGC(),args[0]); + uint16_t p = (uint16_t)port; + return TNativeObject::Create(ls,port,httpServer,printIPs); + } + if(GetArgument(args,1,pathStr) && env->permissions.canRegisterLocalFS) + { + TObjectHttpServer* httpServer = new TObjectHttpServer(ls.GetGC(),args[0]); + + return TNativeObject::Create(ls,pathStr,httpServer); + + } + return nullptr; + } static TObject Net_Http_ListenSimpleWithLoop(GCList& ls, std::vector args) { int64_t port; @@ -1007,6 +1078,9 @@ namespace Tesses::CrossLang http->DeclareFunction(gc, "DownloadToString","Return the http file's contents as a string",{"url"},Net_Http_DownloadToString); http->DeclareFunction(gc, "DownloadToStream","Download file to stream",{"url","stream"},Net_Http_DownloadToStream); http->DeclareFunction(gc, "DownloadToFile","Download file to file in vfs",{"url","vfs","path"},Net_Http_DownloadToFile); + http->DeclareFunction(gc, "HttpServer", "Create a http server (allows multiple)",{"server","portOrUnixPath","$printIPs"},[env](GCList& ls, std::vector args)->TObject{ + return Net_Http_HttpServer(ls,args,env); + }); http->DeclareFunction(gc, "ListenSimpleWithLoop", "Listen (creates application loop)", {"server","port"},Net_Http_ListenSimpleWithLoop); http->DeclareFunction(gc, "ListenOnUnusedPort","Listen on unused localhost port and print Port: theport",{"server"},Net_Http_ListenOnUnusedPort); //FileServer svr() diff --git a/src/runtime_methods/process.cpp b/src/runtime_methods/process.cpp index 6c8fd16..471dbec 100644 --- a/src/runtime_methods/process.cpp +++ b/src/runtime_methods/process.cpp @@ -1,16 +1,189 @@ #include "CrossLang.hpp" -#if defined(GEKKO) || defined(__SWITCH__) -#undef CROSSLANG_ENABLE_PROCESS -#endif -#if defined(CROSSLANG_ENABLE_PROCESS) -#include "subprocess.h" -#endif namespace Tesses::CrossLang { - #if defined(CROSSLANG_ENABLE_PROCESS) + class ProcessObject : public TNativeObject { + public: + ProcessObject() + { + arguments=nullptr; + environment=nullptr; + + } + ProcessObject(GCList& ls) + { + arguments = TList::Create(ls); + environment = TList::Create(ls); + process.includeThisEnv=true; + process.redirectStdIn=false; + + process.redirectStdOut=false; + + process.redirectStdErr=false; + } + TList* arguments; + TList* environment; + Tesses::Framework::Platform::Process process; + std::string TypeName() + { + return "Process"; + } + void Mark() + { + if(this->marked) return; + this->marked=true; + if(arguments != nullptr) arguments->Mark(); + if(environment != nullptr) environment->Mark(); + } + TObject CallMethod(GCList& ls, std::string key, std::vector args) + { + if(key == "setFileName" && GetArgument(args,0,process.name)) + { + return process.name; + } + if(key == "getFileName") + { + return process.name; + } + + if(key == "setRedirectStandardInput" && GetArgument(args,0,process.redirectStdIn)) + { + return process.redirectStdIn; + } + if(key == "getRedirectStandardInput") + { + return process.redirectStdIn; + } + if(key == "setRedirectStandardOutput" && GetArgument(args,0,process.redirectStdOut)) + { + return process.redirectStdOut; + } + if(key == "getRedirectStandardOutput") + { + return process.redirectStdOut; + } + if(key == "setRedirectStandardError" && GetArgument(args,0,process.redirectStdErr)) + { + return process.redirectStdErr; + } + if(key == "getRedirectStandardError") + { + return process.redirectStdErr; + } + if(key == "setWorkingDirectory" && GetArgument(args,0,process.workingDirectory)) + { + return process.workingDirectory; + } + if(key == "getWorkingDirectory") + { + return process.workingDirectory; + } + if(key == "setInheritParentEnvironment" && GetArgument(args,0,process.includeThisEnv)) + { + return process.includeThisEnv; + } + if(key == "getInheritParentEnvironment") + { + return process.includeThisEnv; + } + if(key == "getArguments") + { + if(arguments == nullptr) return nullptr; + return arguments; + } + if(key == "setArguments") + { + if(GetArgumentHeap(args,0,arguments)) + { + return arguments; + } + } + if(key == "getEnvironment") + { + if(environment == nullptr) return nullptr; + return environment; + } + if(key == "getEnvironment") + { + if(GetArgumentHeap(args,0,environment)) + { + return environment; + } + } + if(key == "Start") + { + this->process.args.push_back(process.name); + if(arguments != nullptr) + { + for(int64_t i = 0; i < arguments->Count(); i++) + { + TObject argVal; + std::string argValStr; + if(GetObject(argVal,argValStr)) + this->process.args.push_back(argValStr); + } + } + if(environment != nullptr) + { + for(int64_t i = 0; i < environment->Count(); i++) + { + TObject arg = environment->Get(i); + std::string argstr; + if(GetObject(arg,argstr)) + { + auto kvp = Tesses::Framework::Http::HttpUtils::SplitString(argstr,"=",2); + if(kvp.size()==2) + { + process.env.push_back(std::pair(kvp[0],kvp[1])); + } + else if(kvp.size()==1) + { + process.env.push_back(std::pair(kvp[0],"")); + } + } + } + } + return process.Start(); + } + if(key == "Join" || key == "WaitForExit") + { + return (int64_t)process.WaitForExit(); + } + if(key == "getHasExited") + { + return process.HasExited(); + } + if(key == "Terminate") + { + process.Kill(SIGTERM); + } + if(key == "CloseStdInNow") + { + process.CloseStdInNow(); + + } + int64_t k; + if(key == "Kill" && GetArgument(args,0,k)) + { + process.Kill((int)k); + } + if(key == "getStandardInput") + { + return TStreamHeapObject::Create(ls,process.GetStdinStream()); + } + if(key == "getStandardOutput") + { + return TStreamHeapObject::Create(ls,process.GetStdoutStream()); + } + if(key == "getStandardError") + { + return TStreamHeapObject::Create(ls,process.GetStderrStream()); + } + return Undefined(); + } + }; static TObject Process_Start(GCList& ls, std::vector args) { @@ -23,142 +196,88 @@ namespace Tesses::CrossLang TDictionary* dict; - if(GetArgumentHeap(args, 0, dict)) + if(GetArgumentHeap(args,0,dict)) { - auto gc = ls.GetGC(); - gc->BarrierBegin(); + auto process = TNativeObject::Create(ls); + auto name = dict->GetValue("FileName"); + auto inh = dict->GetValue("InheritParentEnvironment"); + auto rStdIn = dict->GetValue("RedirectStandardInput"); + auto rStdOut = dict->GetValue("RedirectStandardOutput"); - auto fobj = dict->GetValue("FileName"); - auto myargs = dict->GetValue("Arguments"); - auto env = dict->GetValue("Environment"); - auto opt = dict->GetValue("Options"); - //for any C# version - //RedirectStandardIn - //RedirectStandardOut - //RedirectStandardError + auto rStdErr = dict->GetValue("RedirectStandardError"); + auto workingDirectory = dict->GetValue("WorkingDirectory"); + process->process.includeThisEnv=true; + process->process.redirectStdIn=false; - std::string filename; - TList* _args; - TList* env0; - std::vector _args2; - std::vector _env; - int64_t options; + process->process.redirectStdOut=false; - bool hasEnv=false; + process->process.redirectStdErr=false; + GetObject(inh,process->process.includeThisEnv); + GetObject(rStdIn,process->process.redirectStdIn); + GetObject(rStdOut,process->process.redirectStdOut); + GetObject(rStdErr,process->process.redirectStdErr); + + GetObject(name,process->process.name); - if(!GetObject(fobj,filename)) + Tesses::Framework::Filesystem::VFSPath wdPath; + + if(GetObject(workingDirectory,wdPath)) { - gc->BarrierEnd(); - return nullptr; + process->process.workingDirectory= wdPath.MakeAbsolute().ToString(); } - _args2.push_back(filename); - if(GetObjectHeap(myargs,_args)) + + GetObject(workingDirectory,process->process.workingDirectory); + + + + process->process.args.push_back(process->process.name); + auto argumentsO = dict->GetValue("Arguments"); + TList* arguments; + if(GetObjectHeap(argumentsO,arguments)) { - for(auto a : _args->items) + for(int64_t i = 0; i < arguments->Count(); i++) { - std::string a2; - if(GetObject(a,a2)) + TObject arg = arguments->Get(i); + std::string argstr; + if(GetObject(arg,argstr)) { - _args2.push_back(a2); + process->process.args.push_back(argstr); } } } - if(GetObjectHeap(env,env0)) + argumentsO = dict->GetValue("Environment"); + if(GetObjectHeap(argumentsO,arguments)) { - hasEnv=true; - for(auto a : env0->items) + for(int64_t i = 0; i < arguments->Count(); i++) { - std::string a2; - if(GetObject(a,a2)) + TObject arg = arguments->Get(i); + std::string argstr; + if(GetObject(arg,argstr)) { - _env.push_back(a2); + auto kvp = Tesses::Framework::Http::HttpUtils::SplitString(argstr,"=",2); + if(kvp.size()==2) + { + process->process.env.push_back(std::pair(kvp[0],kvp[1])); + } + else if(kvp.size()==1) + { + process->process.env.push_back(std::pair(kvp[0],"")); + } } } } - - const char** args3 = new const char*[sizeof(char*) * (_args2.size()+1)]; - const char** env3 = hasEnv ? new const char*[sizeof(char*) * (_env.size()+1)] : nullptr; - - for(size_t i = 0; i < _args2.size();i++) - args3[i] = _args2[i].c_str(); - args3[_args2.size()]=NULL; - - if(hasEnv) + if(process->process.Start()) { - for(size_t i = 0; i < _env.size();i++) - env3[i] = _env[i].c_str(); - env3[_env.size()]=NULL; - } - - - if(!GetObject(opt,options))options=hasEnv ? 0 : subprocess_option_inherit_environment; - - gc->BarrierEnd(); - struct subprocess_s* subprocess=new struct subprocess_s(); - int r = subprocess_create_ex(args3,(int)options,env3,subprocess); - if(r != 0) - { - - delete[] args3; - delete[] env3; - delete subprocess; - return nullptr; - } - else - { - delete[] args3; - delete[] env3; - - TDictionary* dict=TDictionary::Create(ls); - auto r = TNative::Create(ls,subprocess,[](void* v)->void{ - delete (struct subprocess_s*)v; - }); - gc->BarrierBegin(); - dict->SetValue("_native",r); - gc->BarrierEnd(); - dict->DeclareFunction(gc,"getHasExited","Gets whether process has stopped",{},[r](GCList& ls, std::vector args)->TObject{ - if(r->GetDestroyed()) return true; - return subprocess_alive((struct subprocess_s*)r->GetPointer()) == 0; - }); - dict->DeclareFunction(gc,"Join","Wait till process exits and get its return code",{},[r](GCList& ls, std::vector args)->TObject{ - if(r->GetDestroyed()) return nullptr; - int returnCode; - if(subprocess_join((struct subprocess_s*)r->GetPointer(),&returnCode) == 0) - { - return (int64_t)returnCode; - } - return nullptr; - }); - - dict->DeclareFunction(gc,"Terminate","Terminate the process",{},[r](GCList& ls, std::vector args)->TObject{ - if(r->GetDestroyed()) return nullptr; - subprocess_terminate((struct subprocess_s*)r->GetPointer()); - return nullptr; - }); - dict->DeclareFunction(gc,"Close","Wait till process exits and get its return code",{},[r](GCList& ls, std::vector args)->TObject{ - if(r->GetDestroyed()) return nullptr; - r->Destroy(); - return nullptr; - }); - - dict->DeclareFunction(gc,"getStandardInput","Gets the standard input stream",{},[r](GCList& ls,std::vector args)->TObject { - if(r->GetDestroyed()) return nullptr; - return TStreamHeapObject::Create(ls,new Tesses::Framework::Streams::FileStream(subprocess_stdin((struct subprocess_s*)r->GetPointer()),false,"w",false)); - }); - dict->DeclareFunction(gc,"getStandardOutput","Gets the standard output stream",{},[r](GCList& ls,std::vector args)->TObject { - if(r->GetDestroyed()) return nullptr; - return TStreamHeapObject::Create(ls,new Tesses::Framework::Streams::FileStream(subprocess_stdout((struct subprocess_s*)r->GetPointer()),false,"r",false)); - }); - dict->DeclareFunction(gc,"getStandardError","Gets the standard error stream",{},[r](GCList& ls,std::vector args)->TObject { - if(r->GetDestroyed()) return nullptr; - return TStreamHeapObject::Create(ls,new Tesses::Framework::Streams::FileStream(subprocess_stderr((struct subprocess_s*)r->GetPointer()),false,"r",false)); - }); - return dict; + return process; } } + return nullptr; } - #endif + static TObject New_Process(GCList& ls, std::vector args) + { + return TNativeObject::Create(ls,ls); + } void TStd::RegisterProcess(GC* gc,TRootEnvironment* env) { @@ -166,11 +285,12 @@ namespace Tesses::CrossLang env->permissions.canRegisterProcess=true; GCList ls(gc); TDictionary* dict = TDictionary::Create(ls); - #if defined(CROSSLANG_ENABLE_PROCESS) dict->DeclareFunction(gc,"Start","Start a process",{"process_object"},Process_Start); - #endif + gc->BarrierBegin(); env->SetVariable("Process",dict); + auto process = env->EnsureDictionary(gc,"New"); + process->DeclareFunction(gc, "Process", "Create process",{},New_Process); gc->BarrierEnd(); } } diff --git a/src/runtime_methods/sdl2.cpp b/src/runtime_methods/sdl2.cpp index 4c220ba..173afce 100644 --- a/src/runtime_methods/sdl2.cpp +++ b/src/runtime_methods/sdl2.cpp @@ -1017,7 +1017,7 @@ namespace Tesses::CrossLang TTF_CloseFont(res); return res2; } - else if(args.size() > 1 && !std::holds_alternative(args[1])) + else if(args.size() > 1 && !std::holds_alternative(args[1])) { auto res = TTF_OpenFontRW(Tesses::Framework::SDL2::RwopsFromStream(new TObjectStream(ls.GetGC(),args[1])),1,pt); diff --git a/src/runtime_methods/std.cpp b/src/runtime_methods/std.cpp index e79c040..e4ffc73 100644 --- a/src/runtime_methods/std.cpp +++ b/src/runtime_methods/std.cpp @@ -30,6 +30,331 @@ namespace Tesses::CrossLang { + class DocumentationParser : public TNativeObject + { + public: + + std::vector> items; + DocumentationParser(std::string doc) + { + size_t i=0; + int state=-1; + std::string key={}; + std::string value={}; + + auto add = [&]()->void{ + if(value == "true") { + items.push_back(std::pair(key,true)); + } else if(value == "false") { + items.push_back(std::pair(key,true)); + } else if(value == "null") { + items.push_back(std::pair(key,nullptr)); + } else { + if(value[0] >= '0' && value[0] <= '9') + { + try { + if(value.find('.') != std::string::npos) + { + + items.push_back(std::pair(key,std::stod(value))); + } + else { + items.push_back(std::pair(key,std::stoll(value))); + } + }catch(...) {} + } + else { + items.push_back(std::pair(key,value)); + } + } + }; + + auto ReadChr = [&]() -> std::pair { + int read=i < doc.size() ? (int)doc[i++] : -1; + + if(read == -1) + { + + return std::pair(-1,false); + } + + + + if(read == '\\') + { + read = i < doc.size() ? (int)doc[i++] : -1; + + if(read == -1) + { + return std::pair(-1,true); + } + else if(read == 'n') + { + return std::pair('\n',true); + } + else if(read == 'r') + { + return std::pair('\r',true); + } + else if(read == 'f') + { + return std::pair('\f',true); + } + else if(read == 'b') + { + return std::pair('\b',true); + } + else if(read == 'a') + { + return std::pair('\a',true); + } + else if(read == '0') + { + return std::pair('\0',true); + } + else if(read == 'v') + { + return std::pair('\v',true); + } + else if(read == 'e') + { + return std::pair('\x1B',true); + } + else if(read == 't') + { + return std::pair('\t',true); + } + else if(read == 'x') + { + int r1 = i < doc.size() ? (int)doc[i++] : -1; + + if(r1 == -1) + { + return std::pair(-1,true); + } + int r2 = i < doc.size() ? (int)doc[i++] : -1; + + if(r2 == -1) + { + return std::pair(-1,true); + } + + uint8_t c = (uint8_t)std::stoi(std::string{(char)r1,(char)r2},nullptr,16); + + return std::pair(c,true); + } + else + { + return std::pair(read,true); + } + } + else + { + return std::pair(read,false); + } + }; + + + + + for(; i < doc.size(); i++) + { + if(doc[i] == '@') + { + state = 0; + key={}; + value={}; + if(i + 1 < doc.size()) + { + if(doc[i+1] == '@') + { + i++; + continue; + } + else { + i++; + for(; i < doc.size(); i++) + { + switch(doc[i]) + { + case '\'': + { + i++; + auto c = ReadChr(); + i++; + if(c.first != -1) + { + if(state == 0) + key+={(char)c.first}; + else { + this->items.push_back(std::pair(key,(char)c.first)); + state = -1; + goto outer; + } + } + } + break; + case '\"': + { + if(state == 0) + { + i++; + auto rChr = ReadChr(); + while(rChr.first != '\"' || rChr.second) + { + if(rChr.first == -1) break; + key += (char)rChr.first; + rChr = ReadChr(); + } + i--; + } + else { + i++; + auto rChr = ReadChr(); + + while(rChr.first != '\"' || rChr.second) + { + if(rChr.first == -1) break; + value += (char)rChr.first; + rChr = ReadChr(); + } + this->items.push_back(std::pair(key,value)); + state = -1; + goto outer; + } + } + break; + case ' ': + case '\n': + case '\t': + case '\r': + if(state == 0 && !key.empty()) + state=1; + else + if(state == 1 && !value.empty()) + { + add(); + state = -1; + goto outer; + } + break; + default: + if(state == 0) key += doc[i]; + else value += doc[i]; + break; + } + } + } + outer:; + } + + } + } + if(state == 1 && !value.empty()) + { + add(); + } + } + TObject CallMethod(GCList& ls, std::string key, std::vector args); + std::string TypeName() + { + return "DocumentationParser"; + } + }; + class DocumentationParserEnumerator : public TEnumerator + { + int index; + DocumentationParser* dict; + public: + static DocumentationParserEnumerator* Create(GCList& ls, DocumentationParser* dict) + { + auto dpe=new DocumentationParserEnumerator(); + auto gc = ls.GetGC(); + ls.Add(dpe); + gc->Watch(dpe); + dpe->dict = dict; + dpe->index=-1; + return dpe; + } + + bool MoveNext(GC* ls) + { + ls->BarrierBegin(); + bool r = ++index < this->dict->items.size(); + + ls->BarrierEnd(); + return r; + } + TObject GetCurrent(GCList& ls) + { + std::pair item; + ls.GetGC()->BarrierBegin(); + if(this->index > -1 && this->index < this->dict->items.size()) + { + item = this->dict->items[(size_t)this->index]; + } + ls.GetGC()->BarrierEnd(); + + return TDictionary::Create(ls,{ + TDItem("Key", item.first), + TDItem("Value", item.second) + }); + } + void Mark() + { + if(this->marked) return; + this->marked=true; + dict->Mark(); + } + }; + + + TObject DocumentationParser::CallMethod(GCList& ls, std::string key, std::vector args) + { + std::string myKey; + if(key == "GetAt" && GetArgument(args,0,myKey)) + { + for(auto item : this->items) + if(item.first == myKey) return item.second; + } + if(key == "ToString") + { + std::string n={}; + for(auto item : this->items) + { + n.push_back('@'); + n.append(item.first); + n.push_back(' '); + std::string str; + if(GetObject(item.second,str)) + { + n.append(EscapeString(str,true)); + } + else n.append(ToString(ls.GetGC(), item.second)); + n.push_back('\n'); + } + return n; + } + if(key == "GetEnumerator") + { + return DocumentationParserEnumerator::Create(ls,this); + } + return Undefined(); + + } + TObject New_DocumentationParser(GCList& ls, std::vector args) + { + std::string doc; + + if(GetArgument(args,0,doc)) + { + return TNativeObject::Create(ls, doc); + } + + return Undefined(); + } + + #if defined(CROSSLANG_ENABLE_SHARED) class DL { void* handle; @@ -1032,6 +1357,7 @@ namespace Tesses::CrossLang newTypes->DeclareFunction(gc,"AArray","alias for new AssociativeArray",{},[](GCList& ls, std::vector args)->TObject { return TAssociativeArray::Create(ls); }); + newTypes->DeclareFunction(gc,"DocumentationParser","Parse documentation blocks",{"documentationString"},New_DocumentationParser); newTypes->DeclareFunction(gc,"ByteArray","Create bytearray, with optional either size (to size it) or string argument (to fill byte array)",{"$data"},ByteArray); env->DeclareVariable("Version", TDictionary::Create(ls,{ diff --git a/src/runtime_methods/subprocess.h b/src/runtime_methods/subprocess.h deleted file mode 100644 index 3e40bae..0000000 --- a/src/runtime_methods/subprocess.h +++ /dev/null @@ -1,1203 +0,0 @@ -/* - The latest version of this library is available on GitHub; - https://github.com/sheredom/subprocess.h -*/ - -/* - This is free and unencumbered software released into the public domain. - - Anyone is free to copy, modify, publish, use, compile, sell, or - distribute this software, either in source code form or as a compiled - binary, for any purpose, commercial or non-commercial, and by any - means. - - In jurisdictions that recognize copyright laws, the author or authors - of this software dedicate any and all copyright interest in the - software to the public domain. We make this dedication for the benefit - of the public at large and to the detriment of our heirs and - successors. We intend this dedication to be an overt act of - relinquishment in perpetuity of all present and future rights to this - software under copyright law. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - - For more information, please refer to -*/ - -#ifndef SHEREDOM_SUBPROCESS_H_INCLUDED -#define SHEREDOM_SUBPROCESS_H_INCLUDED - -#if defined(_MSC_VER) -#pragma warning(push, 1) - -/* disable warning: '__cplusplus' is not defined as a preprocessor macro, - * replacing with '0' for '#if/#elif' */ -#pragma warning(disable : 4668) -#endif - -#include -#include - -#if defined(_MSC_VER) -#pragma warning(pop) -#endif - -#if defined(__TINYC__) -#define SUBPROCESS_ATTRIBUTE(a) __attribute((a)) -#else -#define SUBPROCESS_ATTRIBUTE(a) __attribute__((a)) -#endif - -#if defined(_MSC_VER) -#define subprocess_pure -#define subprocess_weak __inline -#define subprocess_tls __declspec(thread) -#elif defined(__MINGW32__) -#define subprocess_pure SUBPROCESS_ATTRIBUTE(pure) -#define subprocess_weak static SUBPROCESS_ATTRIBUTE(used) -#define subprocess_tls __thread -#elif defined(__clang__) || defined(__GNUC__) || defined(__TINYC__) -#define subprocess_pure SUBPROCESS_ATTRIBUTE(pure) -#define subprocess_weak SUBPROCESS_ATTRIBUTE(weak) -#define subprocess_tls __thread -#else -#error Non clang, non gcc, non MSVC compiler found! -#endif - -struct subprocess_s; - -enum subprocess_option_e { - // stdout and stderr are the same FILE. - subprocess_option_combined_stdout_stderr = 0x1, - - // The child process should inherit the environment variables of the parent. - subprocess_option_inherit_environment = 0x2, - - // Enable asynchronous reading of stdout/stderr before it has completed. - subprocess_option_enable_async = 0x4, - - // Enable the child process to be spawned with no window visible if supported - // by the platform. - subprocess_option_no_window = 0x8, - - // Search for program names in the PATH variable. Always enabled on Windows. - // Note: this will **not** search for paths in any provided custom environment - // and instead uses the PATH of the spawning process. - subprocess_option_search_user_path = 0x10 -}; - -#if defined(__cplusplus) -extern "C" { -#endif - -/// @brief Create a process. -/// @param command_line An array of strings for the command line to execute for -/// this process. The last element must be NULL to signify the end of the array. -/// The memory backing this parameter only needs to persist until this function -/// returns. -/// @param options A bit field of subprocess_option_e's to pass. -/// @param out_process The newly created process. -/// @return On success zero is returned. -subprocess_weak int subprocess_create(const char *const command_line[], - int options, - struct subprocess_s *const out_process); - -/// @brief Create a process (extended create). -/// @param command_line An array of strings for the command line to execute for -/// this process. The last element must be NULL to signify the end of the array. -/// The memory backing this parameter only needs to persist until this function -/// returns. -/// @param options A bit field of subprocess_option_e's to pass. -/// @param environment An optional array of strings for the environment to use -/// for a child process (each element of the form FOO=BAR). The last element -/// must be NULL to signify the end of the array. -/// @param out_process The newly created process. -/// @return On success zero is returned. -/// -/// If `options` contains `subprocess_option_inherit_environment`, then -/// `environment` must be NULL. -subprocess_weak int -subprocess_create_ex(const char *const command_line[], int options, - const char *const environment[], - struct subprocess_s *const out_process); - -/// @brief Get the standard input file for a process. -/// @param process The process to query. -/// @return The file for standard input of the process. -/// -/// The file returned can be written to by the parent process to feed data to -/// the standard input of the process. -subprocess_pure subprocess_weak FILE * -subprocess_stdin(const struct subprocess_s *const process); - -/// @brief Get the standard output file for a process. -/// @param process The process to query. -/// @return The file for standard output of the process. -/// -/// The file returned can be read from by the parent process to read data from -/// the standard output of the child process. -subprocess_pure subprocess_weak FILE * -subprocess_stdout(const struct subprocess_s *const process); - -/// @brief Get the standard error file for a process. -/// @param process The process to query. -/// @return The file for standard error of the process. -/// -/// The file returned can be read from by the parent process to read data from -/// the standard error of the child process. -/// -/// If the process was created with the subprocess_option_combined_stdout_stderr -/// option bit set, this function will return NULL, and the subprocess_stdout -/// function should be used for both the standard output and error combined. -subprocess_pure subprocess_weak FILE * -subprocess_stderr(const struct subprocess_s *const process); - -/// @brief Wait for a process to finish execution. -/// @param process The process to wait for. -/// @param out_return_code The return code of the returned process (can be -/// NULL). -/// @return On success zero is returned. -/// -/// Joining a process will close the stdin pipe to the process. -subprocess_weak int subprocess_join(struct subprocess_s *const process, - int *const out_return_code); - -/// @brief Destroy a previously created process. -/// @param process The process to destroy. -/// @return On success zero is returned. -/// -/// If the process to be destroyed had not finished execution, it may out live -/// the parent process. -subprocess_weak int subprocess_destroy(struct subprocess_s *const process); - -/// @brief Terminate a previously created process. -/// @param process The process to terminate. -/// @return On success zero is returned. -/// -/// If the process to be destroyed had not finished execution, it will be -/// terminated (i.e killed). -subprocess_weak int subprocess_terminate(struct subprocess_s *const process); - -/// @brief Read the standard output from the child process. -/// @param process The process to read from. -/// @param buffer The buffer to read into. -/// @param size The maximum number of bytes to read. -/// @return The number of bytes actually read into buffer. Can only be 0 if the -/// process has complete. -/// -/// The only safe way to read from the standard output of a process during it's -/// execution is to use the `subprocess_option_enable_async` option in -/// conjunction with this method. -subprocess_weak unsigned -subprocess_read_stdout(struct subprocess_s *const process, char *const buffer, - unsigned size); - -/// @brief Read the standard error from the child process. -/// @param process The process to read from. -/// @param buffer The buffer to read into. -/// @param size The maximum number of bytes to read. -/// @return The number of bytes actually read into buffer. Can only be 0 if the -/// process has complete. -/// -/// The only safe way to read from the standard error of a process during it's -/// execution is to use the `subprocess_option_enable_async` option in -/// conjunction with this method. -subprocess_weak unsigned -subprocess_read_stderr(struct subprocess_s *const process, char *const buffer, - unsigned size); - -/// @brief Returns if the subprocess is currently still alive and executing. -/// @param process The process to check. -/// @return If the process is still alive non-zero is returned. -subprocess_weak int subprocess_alive(struct subprocess_s *const process); - -#if defined(__cplusplus) -#define SUBPROCESS_CAST(type, x) static_cast(x) -#define SUBPROCESS_PTR_CAST(type, x) reinterpret_cast(x) -#define SUBPROCESS_CONST_CAST(type, x) const_cast(x) -#define SUBPROCESS_NULL NULL -#else -#define SUBPROCESS_CAST(type, x) ((type)(x)) -#define SUBPROCESS_PTR_CAST(type, x) ((type)(x)) -#define SUBPROCESS_CONST_CAST(type, x) ((type)(x)) -#define SUBPROCESS_NULL 0 -#endif - -#if !defined(_WIN32) -#include -#include -#include -#include -#include -#include -#endif - -#if defined(_WIN32) - -#if (_MSC_VER < 1920) -#ifdef _WIN64 -typedef __int64 subprocess_intptr_t; -typedef unsigned __int64 subprocess_size_t; -#else -typedef int subprocess_intptr_t; -typedef unsigned int subprocess_size_t; -#endif -#else -#include - -typedef intptr_t subprocess_intptr_t; -typedef size_t subprocess_size_t; -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wreserved-identifier" -#endif - -typedef struct _PROCESS_INFORMATION *LPPROCESS_INFORMATION; -typedef struct _SECURITY_ATTRIBUTES *LPSECURITY_ATTRIBUTES; -typedef struct _STARTUPINFOA *LPSTARTUPINFOA; -typedef struct _OVERLAPPED *LPOVERLAPPED; - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#ifdef _MSC_VER -#pragma warning(push, 1) -#endif -#ifdef __MINGW32__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wpedantic" -#endif - -struct subprocess_subprocess_information_s { - void *hProcess; - void *hThread; - unsigned long dwProcessId; - unsigned long dwThreadId; -}; - -struct subprocess_security_attributes_s { - unsigned long nLength; - void *lpSecurityDescriptor; - int bInheritHandle; -}; - -struct subprocess_startup_info_s { - unsigned long cb; - char *lpReserved; - char *lpDesktop; - char *lpTitle; - unsigned long dwX; - unsigned long dwY; - unsigned long dwXSize; - unsigned long dwYSize; - unsigned long dwXCountChars; - unsigned long dwYCountChars; - unsigned long dwFillAttribute; - unsigned long dwFlags; - unsigned short wShowWindow; - unsigned short cbReserved2; - unsigned char *lpReserved2; - void *hStdInput; - void *hStdOutput; - void *hStdError; -}; - -struct subprocess_overlapped_s { - uintptr_t Internal; - uintptr_t InternalHigh; - union { - struct { - unsigned long Offset; - unsigned long OffsetHigh; - } DUMMYSTRUCTNAME; - void *Pointer; - } DUMMYUNIONNAME; - - void *hEvent; -}; - -#ifdef __MINGW32__ -#pragma GCC diagnostic pop -#endif -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -__declspec(dllimport) unsigned long __stdcall GetLastError(void); -__declspec(dllimport) int __stdcall SetHandleInformation(void *, unsigned long, - unsigned long); -__declspec(dllimport) int __stdcall CreatePipe(void **, void **, - LPSECURITY_ATTRIBUTES, - unsigned long); -__declspec(dllimport) void *__stdcall CreateNamedPipeA( - const char *, unsigned long, unsigned long, unsigned long, unsigned long, - unsigned long, unsigned long, LPSECURITY_ATTRIBUTES); -__declspec(dllimport) int __stdcall ReadFile(void *, void *, unsigned long, - unsigned long *, LPOVERLAPPED); -__declspec(dllimport) unsigned long __stdcall GetCurrentProcessId(void); -__declspec(dllimport) unsigned long __stdcall GetCurrentThreadId(void); -__declspec(dllimport) void *__stdcall CreateFileA(const char *, unsigned long, - unsigned long, - LPSECURITY_ATTRIBUTES, - unsigned long, unsigned long, - void *); -__declspec(dllimport) void *__stdcall CreateEventA(LPSECURITY_ATTRIBUTES, int, - int, const char *); -__declspec(dllimport) int __stdcall CreateProcessA( - const char *, char *, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, int, - unsigned long, void *, const char *, LPSTARTUPINFOA, LPPROCESS_INFORMATION); -__declspec(dllimport) int __stdcall CloseHandle(void *); -__declspec(dllimport) unsigned long __stdcall WaitForSingleObject( - void *, unsigned long); -__declspec(dllimport) int __stdcall GetExitCodeProcess( - void *, unsigned long *lpExitCode); -__declspec(dllimport) int __stdcall TerminateProcess(void *, unsigned int); -__declspec(dllimport) unsigned long __stdcall WaitForMultipleObjects( - unsigned long, void *const *, int, unsigned long); -__declspec(dllimport) int __stdcall GetOverlappedResult(void *, LPOVERLAPPED, - unsigned long *, int); - -#if defined(_DLL) -#define SUBPROCESS_DLLIMPORT __declspec(dllimport) -#else -#define SUBPROCESS_DLLIMPORT -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wreserved-identifier" -#endif - -SUBPROCESS_DLLIMPORT int __cdecl _fileno(FILE *); -SUBPROCESS_DLLIMPORT int __cdecl _open_osfhandle(subprocess_intptr_t, int); -SUBPROCESS_DLLIMPORT subprocess_intptr_t __cdecl _get_osfhandle(int); - -#ifndef __MINGW32__ -void *__cdecl _alloca(subprocess_size_t); -#else -#include -#endif - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#else -typedef size_t subprocess_size_t; -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif -struct subprocess_s { - FILE *stdin_file; - FILE *stdout_file; - FILE *stderr_file; - -#if defined(_WIN32) - void *hProcess; - void *hStdInput; - void *hEventOutput; - void *hEventError; -#else - pid_t child; - int return_status; -#endif - - subprocess_size_t alive; -}; -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#if defined(__clang__) -#if __has_warning("-Wunsafe-buffer-usage") -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" -#endif -#endif - -#if defined(_WIN32) -subprocess_weak int subprocess_create_named_pipe_helper(void **rd, void **wr); -int subprocess_create_named_pipe_helper(void **rd, void **wr) { - const unsigned long pipeAccessInbound = 0x00000001; - const unsigned long fileFlagOverlapped = 0x40000000; - const unsigned long pipeTypeByte = 0x00000000; - const unsigned long pipeWait = 0x00000000; - const unsigned long genericWrite = 0x40000000; - const unsigned long openExisting = 3; - const unsigned long fileAttributeNormal = 0x00000080; - const void *const invalidHandleValue = - SUBPROCESS_PTR_CAST(void *, ~(SUBPROCESS_CAST(subprocess_intptr_t, 0))); - struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), - SUBPROCESS_NULL, 1}; - char name[256] = {0}; - static subprocess_tls long index = 0; - const long unique = index++; - -#if defined(_MSC_VER) && _MSC_VER < 1900 -#pragma warning(push, 1) -#pragma warning(disable : 4996) - _snprintf(name, sizeof(name) - 1, - "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld", - GetCurrentProcessId(), GetCurrentThreadId(), unique); -#pragma warning(pop) -#else - snprintf(name, sizeof(name) - 1, - "\\\\.\\pipe\\sheredom_subprocess_h.%08lx.%08lx.%ld", - GetCurrentProcessId(), GetCurrentThreadId(), unique); -#endif - - *rd = - CreateNamedPipeA(name, pipeAccessInbound | fileFlagOverlapped, - pipeTypeByte | pipeWait, 1, 4096, 4096, SUBPROCESS_NULL, - SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr)); - - if (invalidHandleValue == *rd) { - return -1; - } - - *wr = CreateFileA(name, genericWrite, SUBPROCESS_NULL, - SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), - openExisting, fileAttributeNormal, SUBPROCESS_NULL); - - if (invalidHandleValue == *wr) { - return -1; - } - - return 0; -} -#endif - -int subprocess_create(const char *const commandLine[], int options, - struct subprocess_s *const out_process) { - return subprocess_create_ex(commandLine, options, SUBPROCESS_NULL, - out_process); -} - -int subprocess_create_ex(const char *const commandLine[], int options, - const char *const environment[], - struct subprocess_s *const out_process) { -#if defined(_WIN32) - int fd; - void *rd, *wr; - char *commandLineCombined; - subprocess_size_t len; - int i, j; - int need_quoting; - unsigned long flags = 0; - const unsigned long startFUseStdHandles = 0x00000100; - const unsigned long handleFlagInherit = 0x00000001; - const unsigned long createNoWindow = 0x08000000; - struct subprocess_subprocess_information_s processInfo; - struct subprocess_security_attributes_s saAttr = {sizeof(saAttr), - SUBPROCESS_NULL, 1}; - char *used_environment = SUBPROCESS_NULL; - struct subprocess_startup_info_s startInfo = {0, - SUBPROCESS_NULL, - SUBPROCESS_NULL, - SUBPROCESS_NULL, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - SUBPROCESS_NULL, - SUBPROCESS_NULL, - SUBPROCESS_NULL, - SUBPROCESS_NULL}; - - startInfo.cb = sizeof(startInfo); - startInfo.dwFlags = startFUseStdHandles; - - if (subprocess_option_no_window == (options & subprocess_option_no_window)) { - flags |= createNoWindow; - } - - if (subprocess_option_inherit_environment != - (options & subprocess_option_inherit_environment)) { - if (SUBPROCESS_NULL == environment) { - used_environment = SUBPROCESS_CONST_CAST(char *, "\0\0"); - } else { - // We always end with two null terminators. - len = 2; - - for (i = 0; environment[i]; i++) { - for (j = 0; '\0' != environment[i][j]; j++) { - len++; - } - - // For the null terminator too. - len++; - } - - used_environment = SUBPROCESS_CAST(char *, _alloca(len)); - - // Re-use len for the insertion position - len = 0; - - for (i = 0; environment[i]; i++) { - for (j = 0; '\0' != environment[i][j]; j++) { - used_environment[len++] = environment[i][j]; - } - - used_environment[len++] = '\0'; - } - - // End with the two null terminators. - used_environment[len++] = '\0'; - used_environment[len++] = '\0'; - } - } else { - if (SUBPROCESS_NULL != environment) { - return -1; - } - } - - if (!CreatePipe(&rd, &wr, SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), - 0)) { - return -1; - } - - if (!SetHandleInformation(wr, handleFlagInherit, 0)) { - return -1; - } - - fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, wr), 0); - - if (-1 != fd) { - out_process->stdin_file = _fdopen(fd, "wb"); - - if (SUBPROCESS_NULL == out_process->stdin_file) { - return -1; - } - } - - startInfo.hStdInput = rd; - - if (options & subprocess_option_enable_async) { - if (subprocess_create_named_pipe_helper(&rd, &wr)) { - return -1; - } - } else { - if (!CreatePipe(&rd, &wr, - SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) { - return -1; - } - } - - if (!SetHandleInformation(rd, handleFlagInherit, 0)) { - return -1; - } - - fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0); - - if (-1 != fd) { - out_process->stdout_file = _fdopen(fd, "rb"); - - if (SUBPROCESS_NULL == out_process->stdout_file) { - return -1; - } - } - - startInfo.hStdOutput = wr; - - if (subprocess_option_combined_stdout_stderr == - (options & subprocess_option_combined_stdout_stderr)) { - out_process->stderr_file = out_process->stdout_file; - startInfo.hStdError = startInfo.hStdOutput; - } else { - if (options & subprocess_option_enable_async) { - if (subprocess_create_named_pipe_helper(&rd, &wr)) { - return -1; - } - } else { - if (!CreatePipe(&rd, &wr, - SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 0)) { - return -1; - } - } - - if (!SetHandleInformation(rd, handleFlagInherit, 0)) { - return -1; - } - - fd = _open_osfhandle(SUBPROCESS_PTR_CAST(subprocess_intptr_t, rd), 0); - - if (-1 != fd) { - out_process->stderr_file = _fdopen(fd, "rb"); - - if (SUBPROCESS_NULL == out_process->stderr_file) { - return -1; - } - } - - startInfo.hStdError = wr; - } - - if (options & subprocess_option_enable_async) { - out_process->hEventOutput = - CreateEventA(SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 1, 1, - SUBPROCESS_NULL); - out_process->hEventError = - CreateEventA(SUBPROCESS_PTR_CAST(LPSECURITY_ATTRIBUTES, &saAttr), 1, 1, - SUBPROCESS_NULL); - } else { - out_process->hEventOutput = SUBPROCESS_NULL; - out_process->hEventError = SUBPROCESS_NULL; - } - - // Combine commandLine together into a single string - len = 0; - for (i = 0; commandLine[i]; i++) { - // for the trailing \0 - len++; - - // Quote the argument if it has a space in it - if (strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL || - commandLine[i][0] == SUBPROCESS_NULL) - len += 2; - - for (j = 0; '\0' != commandLine[i][j]; j++) { - switch (commandLine[i][j]) { - default: - break; - case '\\': - if (commandLine[i][j + 1] == '"') { - len++; - } - - break; - case '"': - len++; - break; - } - len++; - } - } - - commandLineCombined = SUBPROCESS_CAST(char *, _alloca(len)); - - if (!commandLineCombined) { - return -1; - } - - // Gonna re-use len to store the write index into commandLineCombined - len = 0; - - for (i = 0; commandLine[i]; i++) { - if (0 != i) { - commandLineCombined[len++] = ' '; - } - - need_quoting = strpbrk(commandLine[i], "\t\v ") != SUBPROCESS_NULL || - commandLine[i][0] == SUBPROCESS_NULL; - if (need_quoting) { - commandLineCombined[len++] = '"'; - } - - for (j = 0; '\0' != commandLine[i][j]; j++) { - switch (commandLine[i][j]) { - default: - break; - case '\\': - if (commandLine[i][j + 1] == '"') { - commandLineCombined[len++] = '\\'; - } - - break; - case '"': - commandLineCombined[len++] = '\\'; - break; - } - - commandLineCombined[len++] = commandLine[i][j]; - } - if (need_quoting) { - commandLineCombined[len++] = '"'; - } - } - - commandLineCombined[len] = '\0'; - - if (!CreateProcessA( - SUBPROCESS_NULL, - commandLineCombined, // command line - SUBPROCESS_NULL, // process security attributes - SUBPROCESS_NULL, // primary thread security attributes - 1, // handles are inherited - flags, // creation flags - used_environment, // used environment - SUBPROCESS_NULL, // use parent's current directory - SUBPROCESS_PTR_CAST(LPSTARTUPINFOA, - &startInfo), // STARTUPINFO pointer - SUBPROCESS_PTR_CAST(LPPROCESS_INFORMATION, &processInfo))) { - return -1; - } - - out_process->hProcess = processInfo.hProcess; - - out_process->hStdInput = startInfo.hStdInput; - - // We don't need the handle of the primary thread in the called process. - CloseHandle(processInfo.hThread); - - if (SUBPROCESS_NULL != startInfo.hStdOutput) { - CloseHandle(startInfo.hStdOutput); - - if (startInfo.hStdError != startInfo.hStdOutput) { - CloseHandle(startInfo.hStdError); - } - } - - out_process->alive = 1; - - return 0; -#else - int stdinfd[2]; - int stdoutfd[2]; - int stderrfd[2]; - pid_t child; - extern char **environ; - char *const empty_environment[1] = {SUBPROCESS_NULL}; - posix_spawn_file_actions_t actions; - char *const *used_environment; - - if (subprocess_option_inherit_environment == - (options & subprocess_option_inherit_environment)) { - if (SUBPROCESS_NULL != environment) { - return -1; - } - } - - if (0 != pipe(stdinfd)) { - return -1; - } - - if (0 != pipe(stdoutfd)) { - return -1; - } - - if (subprocess_option_combined_stdout_stderr != - (options & subprocess_option_combined_stdout_stderr)) { - if (0 != pipe(stderrfd)) { - return -1; - } - } - - if (environment) { -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcast-qual" -#pragma clang diagnostic ignored "-Wold-style-cast" -#endif - used_environment = SUBPROCESS_CONST_CAST(char *const *, environment); -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - } else if (subprocess_option_inherit_environment == - (options & subprocess_option_inherit_environment)) { - used_environment = environ; - } else { - used_environment = empty_environment; - } - - if (0 != posix_spawn_file_actions_init(&actions)) { - return -1; - } - - // Close the stdin write end - if (0 != posix_spawn_file_actions_addclose(&actions, stdinfd[1])) { - posix_spawn_file_actions_destroy(&actions); - return -1; - } - - // Map the read end to stdin - if (0 != - posix_spawn_file_actions_adddup2(&actions, stdinfd[0], STDIN_FILENO)) { - posix_spawn_file_actions_destroy(&actions); - return -1; - } - - // Close the stdout read end - if (0 != posix_spawn_file_actions_addclose(&actions, stdoutfd[0])) { - posix_spawn_file_actions_destroy(&actions); - return -1; - } - - // Map the write end to stdout - if (0 != - posix_spawn_file_actions_adddup2(&actions, stdoutfd[1], STDOUT_FILENO)) { - posix_spawn_file_actions_destroy(&actions); - return -1; - } - - if (subprocess_option_combined_stdout_stderr == - (options & subprocess_option_combined_stdout_stderr)) { - if (0 != posix_spawn_file_actions_adddup2(&actions, STDOUT_FILENO, - STDERR_FILENO)) { - posix_spawn_file_actions_destroy(&actions); - return -1; - } - } else { - // Close the stderr read end - if (0 != posix_spawn_file_actions_addclose(&actions, stderrfd[0])) { - posix_spawn_file_actions_destroy(&actions); - return -1; - } - // Map the write end to stdout - if (0 != posix_spawn_file_actions_adddup2(&actions, stderrfd[1], - STDERR_FILENO)) { - posix_spawn_file_actions_destroy(&actions); - return -1; - } - } - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wcast-qual" -#pragma clang diagnostic ignored "-Wold-style-cast" -#endif - if (subprocess_option_search_user_path == - (options & subprocess_option_search_user_path)) { - if (0 != posix_spawnp(&child, commandLine[0], &actions, SUBPROCESS_NULL, - SUBPROCESS_CONST_CAST(char *const *, commandLine), - used_environment)) { - posix_spawn_file_actions_destroy(&actions); - return -1; - } - } else { - if (0 != posix_spawn(&child, commandLine[0], &actions, SUBPROCESS_NULL, - SUBPROCESS_CONST_CAST(char *const *, commandLine), - used_environment)) { - posix_spawn_file_actions_destroy(&actions); - return -1; - } - } -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - // Close the stdin read end - close(stdinfd[0]); - // Store the stdin write end - out_process->stdin_file = fdopen(stdinfd[1], "wb"); - - // Close the stdout write end - close(stdoutfd[1]); - // Store the stdout read end - out_process->stdout_file = fdopen(stdoutfd[0], "rb"); - - if (subprocess_option_combined_stdout_stderr == - (options & subprocess_option_combined_stdout_stderr)) { - out_process->stderr_file = out_process->stdout_file; - } else { - // Close the stderr write end - close(stderrfd[1]); - // Store the stderr read end - out_process->stderr_file = fdopen(stderrfd[0], "rb"); - } - - // Store the child's pid - out_process->child = child; - - out_process->alive = 1; - - posix_spawn_file_actions_destroy(&actions); - return 0; -#endif -} - -FILE *subprocess_stdin(const struct subprocess_s *const process) { - return process->stdin_file; -} - -FILE *subprocess_stdout(const struct subprocess_s *const process) { - return process->stdout_file; -} - -FILE *subprocess_stderr(const struct subprocess_s *const process) { - if (process->stdout_file != process->stderr_file) { - return process->stderr_file; - } else { - return SUBPROCESS_NULL; - } -} - -int subprocess_join(struct subprocess_s *const process, - int *const out_return_code) { -#if defined(_WIN32) - const unsigned long infinite = 0xFFFFFFFF; - - if (process->stdin_file) { - fclose(process->stdin_file); - process->stdin_file = SUBPROCESS_NULL; - } - - if (process->hStdInput) { - CloseHandle(process->hStdInput); - process->hStdInput = SUBPROCESS_NULL; - } - - WaitForSingleObject(process->hProcess, infinite); - - if (out_return_code) { - if (!GetExitCodeProcess( - process->hProcess, - SUBPROCESS_PTR_CAST(unsigned long *, out_return_code))) { - return -1; - } - } - - process->alive = 0; - - return 0; -#else - int status; - - if (process->stdin_file) { - fclose(process->stdin_file); - process->stdin_file = SUBPROCESS_NULL; - } - - if (process->child) { - if (process->child != waitpid(process->child, &status, 0)) { - return -1; - } - - process->child = 0; - - if (WIFEXITED(status)) { - process->return_status = WEXITSTATUS(status); - } else { - process->return_status = EXIT_FAILURE; - } - - process->alive = 0; - } - - if (out_return_code) { - *out_return_code = process->return_status; - } - - return 0; -#endif -} - -int subprocess_destroy(struct subprocess_s *const process) { - if (process->stdin_file) { - fclose(process->stdin_file); - process->stdin_file = SUBPROCESS_NULL; - } - - if (process->stdout_file) { - fclose(process->stdout_file); - - if (process->stdout_file != process->stderr_file) { - fclose(process->stderr_file); - } - - process->stdout_file = SUBPROCESS_NULL; - process->stderr_file = SUBPROCESS_NULL; - } - -#if defined(_WIN32) - if (process->hProcess) { - CloseHandle(process->hProcess); - process->hProcess = SUBPROCESS_NULL; - - if (process->hStdInput) { - CloseHandle(process->hStdInput); - } - - if (process->hEventOutput) { - CloseHandle(process->hEventOutput); - } - - if (process->hEventError) { - CloseHandle(process->hEventError); - } - } -#endif - - return 0; -} - -int subprocess_terminate(struct subprocess_s *const process) { -#if defined(_WIN32) - unsigned int killed_process_exit_code; - int success_terminate; - int windows_call_result; - - killed_process_exit_code = 99; - windows_call_result = - TerminateProcess(process->hProcess, killed_process_exit_code); - success_terminate = (windows_call_result == 0) ? 1 : 0; - return success_terminate; -#else - int result; - result = kill(process->child, 9); - return result; -#endif -} - -unsigned subprocess_read_stdout(struct subprocess_s *const process, - char *const buffer, unsigned size) { -#if defined(_WIN32) - void *handle; - unsigned long bytes_read = 0; - struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL}; - overlapped.hEvent = process->hEventOutput; - - handle = SUBPROCESS_PTR_CAST(void *, - _get_osfhandle(_fileno(process->stdout_file))); - - if (!ReadFile(handle, buffer, size, &bytes_read, - SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) { - const unsigned long errorIoPending = 997; - unsigned long error = GetLastError(); - - // Means we've got an async read! - if (error == errorIoPending) { - if (!GetOverlappedResult(handle, - SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped), - &bytes_read, 1)) { - const unsigned long errorIoIncomplete = 996; - const unsigned long errorHandleEOF = 38; - error = GetLastError(); - - if ((error != errorIoIncomplete) && (error != errorHandleEOF)) { - return 0; - } - } - } - } - - return SUBPROCESS_CAST(unsigned, bytes_read); -#else - const int fd = fileno(process->stdout_file); - const ssize_t bytes_read = read(fd, buffer, size); - - if (bytes_read < 0) { - return 0; - } - - return SUBPROCESS_CAST(unsigned, bytes_read); -#endif -} - -unsigned subprocess_read_stderr(struct subprocess_s *const process, - char *const buffer, unsigned size) { -#if defined(_WIN32) - void *handle; - unsigned long bytes_read = 0; - struct subprocess_overlapped_s overlapped = {0, 0, {{0, 0}}, SUBPROCESS_NULL}; - overlapped.hEvent = process->hEventError; - - handle = SUBPROCESS_PTR_CAST(void *, - _get_osfhandle(_fileno(process->stderr_file))); - - if (!ReadFile(handle, buffer, size, &bytes_read, - SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped))) { - const unsigned long errorIoPending = 997; - unsigned long error = GetLastError(); - - // Means we've got an async read! - if (error == errorIoPending) { - if (!GetOverlappedResult(handle, - SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped), - &bytes_read, 1)) { - const unsigned long errorIoIncomplete = 996; - const unsigned long errorHandleEOF = 38; - error = GetLastError(); - - if ((error != errorIoIncomplete) && (error != errorHandleEOF)) { - return 0; - } - } - } - } - - return SUBPROCESS_CAST(unsigned, bytes_read); -#else - const int fd = fileno(process->stderr_file); - const ssize_t bytes_read = read(fd, buffer, size); - - if (bytes_read < 0) { - return 0; - } - - return SUBPROCESS_CAST(unsigned, bytes_read); -#endif -} - -int subprocess_alive(struct subprocess_s *const process) { - int is_alive = SUBPROCESS_CAST(int, process->alive); - - if (!is_alive) { - return 0; - } -#if defined(_WIN32) - { - const unsigned long zero = 0x0; - const unsigned long wait_object_0 = 0x00000000L; - - is_alive = wait_object_0 != WaitForSingleObject(process->hProcess, zero); - } -#else - { - int status; - is_alive = 0 == waitpid(process->child, &status, WNOHANG); - - // If the process was successfully waited on we need to cleanup now. - if (!is_alive) { - if (WIFEXITED(status)) { - process->return_status = WEXITSTATUS(status); - } else { - process->return_status = EXIT_FAILURE; - } - - // Since we've already successfully waited on the process, we need to wipe - // the child now. - process->child = 0; - - if (subprocess_join(process, SUBPROCESS_NULL)) { - return -1; - } - } - } -#endif - - if (!is_alive) { - process->alive = 0; - } - - return is_alive; -} - -#if defined(__clang__) -#if __has_warning("-Wunsafe-buffer-usage") -#pragma clang diagnostic pop -#endif -#endif - -#if defined(__cplusplus) -} // extern "C" -#endif - -#endif /* SHEREDOM_SUBPROCESS_H_INCLUDED */ diff --git a/src/vm/vm.cpp b/src/vm/vm.cpp index 30b53fc..4b176c2 100644 --- a/src/vm/vm.cpp +++ b/src/vm/vm.cpp @@ -2409,6 +2409,26 @@ namespace Tesses::CrossLang { GCList ls(gc); std::regex regex; TVMVersion version; + if(std::holds_alternative(instance)) + { + if(key == "ToString") + { + cse.back()->Push(gc,"null"); + return false; + } + cse.back()->Push(gc,Undefined()); + return false; + }else if(std::holds_alternative(instance)) + { + if(key == "ToString") + { + cse.back()->Push(gc,"undefined"); + return false; + } + cse.back()->Push(gc,Undefined()); + return false; + } + else if(GetObject(instance, version)) { if(key == "ToString")