RegExp::RegExp(const UString &p, int flags) : _flags(flags), _numSubPatterns(0) { #ifdef HAVE_PCREPOSIX int options = PCRE_UTF8; // Note: the Global flag is already handled by RegExpProtoFunc::execute. if (flags & IgnoreCase) options |= PCRE_CASELESS; if (flags & Multiline) options |= PCRE_MULTILINE; const char *errorMessage; int errorOffset; UString nullTerminated(p); char null(0); nullTerminated.append(null); _regex = pcre_compile(reinterpret_cast<const uint16_t *>(nullTerminated.data()), options, &errorMessage, &errorOffset, NULL); if (!_regex) { #ifndef NDEBUG fprintf(stderr, "KJS: pcre_compile() failed with '%s'\n", errorMessage); #endif return; } #ifdef PCRE_INFO_CAPTURECOUNT // Get number of subpatterns that will be returned. pcre_fullinfo(_regex, NULL, PCRE_INFO_CAPTURECOUNT, &_numSubPatterns); #endif #else /* HAVE_PCREPOSIX */ int regflags = 0; #ifdef REG_EXTENDED regflags |= REG_EXTENDED; #endif #ifdef REG_ICASE if ( f & IgnoreCase ) regflags |= REG_ICASE; #endif //NOTE: Multiline is not feasible with POSIX regex. //if ( f & Multiline ) // ; // Note: the Global flag is already handled by RegExpProtoFunc::execute regcomp(&_regex, p.ascii(), regflags); /* TODO check for errors */ #endif }
// DecodeUTF8 // decodes the specified *unterminated* UTF-8 byte array wxWCharBuffer DecodeUTF8( const void* data, // an unterminated UTF-8 encoded byte array size_t size // the byte length of data ) { // the decoder requires a null terminated buffer. // the input data is not null terminated. // copy to null terminated buffer wxCharBuffer nullTerminated( size+1 ); memcpy( nullTerminated.data(), data, size ); nullTerminated.data()[size] = 0; return wxConvUTF8.cMB2WC(nullTerminated.data()); }
Process::ExecState Process::startInternal(const Path &command, const List<String> &a, const List<String> &environment, int timeout, unsigned int execFlags) { mErrorString.clear(); const char *path = 0; for (const auto &it : environment) { if (it.startsWith("PATH=")) { path = it.constData() + 5; break; } } Path cmd = findCommand(command, path); if (cmd.isEmpty()) { mErrorString = "Command not found"; return Error; } List<String> arguments = a; int err; int closePipe[2]; eintrwrap(err, ::pipe(closePipe)); #ifdef HAVE_CLOEXEC if (!SocketClient::setFlags(closePipe[1], FD_CLOEXEC, F_GETFD, F_SETFD)) { mErrorString = "Unable to set FD_CLOEXEC"; eintrwrap(err, ::close(closePipe[0])); eintrwrap(err, ::close(closePipe[1])); return Error; } #else #warning No CLOEXEC, Process might have problematic behavior #endif eintrwrap(err, ::pipe(mStdIn)); eintrwrap(err, ::pipe(mStdOut)); eintrwrap(err, ::pipe(mStdErr)); if (mMode == Sync) eintrwrap(err, ::pipe(mSync)); const char **args = new const char*[arguments.size() + 2]; // const char* args[arguments.size() + 2]; args[arguments.size() + 1] = 0; args[0] = cmd.nullTerminated(); int pos = 1; for (List<String>::const_iterator it = arguments.begin(); it != arguments.end(); ++it) { args[pos] = it->nullTerminated(); // printf("arg: '%s'\n", args[pos]); ++pos; } const bool hasEnviron = !environment.empty(); const char **env = new const char*[environment.size() + 1]; env[environment.size()] = 0; if (hasEnviron) { pos = 0; //printf("fork, about to exec '%s'\n", cmd.nullTerminated()); for (List<String>::const_iterator it = environment.begin(); it != environment.end(); ++it) { env[pos] = it->nullTerminated(); //printf("env: '%s'\n", env[pos]); ++pos; } } ProcessThread::setPending(1); mPid = ::fork(); if (mPid == -1) { //printf("fork, something horrible has happened %d\n", errno); // bail out ProcessThread::setPending(-1); eintrwrap(err, ::close(mStdIn[1])); eintrwrap(err, ::close(mStdIn[0])); eintrwrap(err, ::close(mStdOut[1])); eintrwrap(err, ::close(mStdOut[0])); eintrwrap(err, ::close(mStdErr[1])); eintrwrap(err, ::close(mStdErr[0])); eintrwrap(err, ::close(closePipe[1])); eintrwrap(err, ::close(closePipe[0])); mErrorString = "Fork failed"; delete[] env; delete[] args; return Error; } else if (mPid == 0) { //printf("fork, in child\n"); // child, should do some error checking here really eintrwrap(err, ::close(closePipe[0])); eintrwrap(err, ::close(mStdIn[1])); eintrwrap(err, ::close(mStdOut[0])); eintrwrap(err, ::close(mStdErr[0])); eintrwrap(err, ::close(STDIN_FILENO)); eintrwrap(err, ::close(STDOUT_FILENO)); eintrwrap(err, ::close(STDERR_FILENO)); eintrwrap(err, ::dup2(mStdIn[0], STDIN_FILENO)); eintrwrap(err, ::close(mStdIn[0])); eintrwrap(err, ::dup2(mStdOut[1], STDOUT_FILENO)); eintrwrap(err, ::close(mStdOut[1])); eintrwrap(err, ::dup2(mStdErr[1], STDERR_FILENO)); eintrwrap(err, ::close(mStdErr[1])); int ret; if (!mChRoot.isEmpty() && ::chroot(mChRoot.constData())) { goto error; } if (!mCwd.isEmpty() && ::chdir(mCwd.constData())) { goto error; } if (hasEnviron) { ret = ::execve(cmd.nullTerminated(), const_cast<char* const*>(args), const_cast<char* const*>(env)); } else { ret = ::execv(cmd.nullTerminated(), const_cast<char* const*>(args)); } // notify the parent process error: const char c = 'c'; eintrwrap(err, ::write(closePipe[1], &c, 1)); eintrwrap(err, ::close(closePipe[1])); ::_exit(1); (void)ret; //printf("fork, exec seemingly failed %d, %d %s\n", ret, errno, Rct::strerror().constData()); } else { delete[] env; delete[] args; // parent eintrwrap(err, ::close(closePipe[1])); eintrwrap(err, ::close(mStdIn[0])); eintrwrap(err, ::close(mStdOut[1])); eintrwrap(err, ::close(mStdErr[1])); //printf("fork, in parent\n"); int flags; eintrwrap(flags, fcntl(mStdIn[1], F_GETFL, 0)); eintrwrap(flags, fcntl(mStdIn[1], F_SETFL, flags | O_NONBLOCK)); eintrwrap(flags, fcntl(mStdOut[0], F_GETFL, 0)); eintrwrap(flags, fcntl(mStdOut[0], F_SETFL, flags | O_NONBLOCK)); eintrwrap(flags, fcntl(mStdErr[0], F_GETFL, 0)); eintrwrap(flags, fcntl(mStdErr[0], F_SETFL, flags | O_NONBLOCK)); // block until exec is called in the child or until exec fails { char c; eintrwrap(err, ::read(closePipe[0], &c, 1)); (void)c; if (err == -1) { // bad eintrwrap(err, ::close(closePipe[0])); mErrorString = "Failed to read from closePipe during process start"; mPid = -1; ProcessThread::setPending(-1); mReturn = ReturnCrashed; return Error; } else if (err == 0) { // process has started successfully eintrwrap(err, ::close(closePipe[0])); } else if (err == 1) { // process start failed eintrwrap(err, ::close(closePipe[0])); mErrorString = "Process failed to start"; mReturn = ReturnCrashed; mPid = -1; ProcessThread::setPending(-1); return Error; } } ProcessThread::addPid(mPid, this, (mMode == Async)); //printf("fork, about to add fds: stdin=%d, stdout=%d, stderr=%d\n", mStdIn[1], mStdOut[0], mStdErr[0]); if (mMode == Async) { if (EventLoop::SharedPtr loop = EventLoop::eventLoop()) { loop->registerSocket(mStdOut[0], EventLoop::SocketRead, std::bind(&Process::processCallback, this, std::placeholders::_1, std::placeholders::_2)); loop->registerSocket(mStdErr[0], EventLoop::SocketRead, std::bind(&Process::processCallback, this, std::placeholders::_1, std::placeholders::_2)); } } else { // select and stuff timeval started, now, timeoutForSelect; if (timeout > 0) { Rct::gettime(&started); timeoutForSelect.tv_sec = timeout / 1000; timeoutForSelect.tv_usec = (timeout % 1000) * 1000; } if (!(execFlags & NoCloseStdIn)) { closeStdIn(CloseForce); mWantStdInClosed = false; } for (;;) { // set up all the select crap fd_set rfds, wfds; FD_ZERO(&rfds); FD_ZERO(&wfds); int max = 0; FD_SET(mStdOut[0], &rfds); max = std::max(max, mStdOut[0]); FD_SET(mStdErr[0], &rfds); max = std::max(max, mStdErr[0]); FD_SET(mSync[0], &rfds); max = std::max(max, mSync[0]); if (mStdIn[1] != -1) { FD_SET(mStdIn[1], &wfds); max = std::max(max, mStdIn[1]); } int ret; eintrwrap(ret, ::select(max + 1, &rfds, &wfds, 0, timeout > 0 ? &timeoutForSelect : 0)); if (ret == -1) { // ow mErrorString = "Sync select failed: "; mErrorString += Rct::strerror(); return Error; } // check fds and stuff if (FD_ISSET(mStdOut[0], &rfds)) handleOutput(mStdOut[0], mStdOutBuffer, mStdOutIndex, mReadyReadStdOut); if (FD_ISSET(mStdErr[0], &rfds)) handleOutput(mStdErr[0], mStdErrBuffer, mStdErrIndex, mReadyReadStdErr); if (mStdIn[1] != -1 && FD_ISSET(mStdIn[1], &wfds)) handleInput(mStdIn[1]); if (FD_ISSET(mSync[0], &rfds)) { // we're done { std::lock_guard<std::mutex> lock(mMutex); assert(mSync[1] == -1); // try to read all remaining data on stdout and stderr handleOutput(mStdOut[0], mStdOutBuffer, mStdOutIndex, mReadyReadStdOut); handleOutput(mStdErr[0], mStdErrBuffer, mStdErrIndex, mReadyReadStdErr); closeStdOut(); closeStdErr(); int w; eintrwrap(w, ::close(mSync[0])); mSync[0] = -1; } mFinished(this); return Done; } if (timeout) { Rct::gettime(&now); // lasted is the amount of time we spent until now in ms const int lasted = Rct::timevalDiff(&now, &started); if (lasted >= timeout) { // timeout, we're done kill(); // attempt to kill // we need to remove this Process object from // ProcessThread, because ProcessThread will try to // finish() this object. However, this object may // already have been deleted *before* ProcessThread // runs, creating a segfault. ProcessThread::removePid(mPid); mErrorString = "Timed out"; return TimedOut; } // (timeout - lasted) is guaranteed to be > 0 because of // the check above. timeoutForSelect.tv_sec = (timeout - lasted) / 1000; timeoutForSelect.tv_usec = ((timeout - lasted) % 1000) * 1000; } } } } return Done; }