diff options
Diffstat (limited to 'graphics/asymptote/pipestream.cc')
-rw-r--r-- | graphics/asymptote/pipestream.cc | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/graphics/asymptote/pipestream.cc b/graphics/asymptote/pipestream.cc new file mode 100644 index 0000000000..c53287c28c --- /dev/null +++ b/graphics/asymptote/pipestream.cc @@ -0,0 +1,211 @@ +/* Pipestream: A simple C++ interface to UNIX pipes + Copyright (C) 2005-2014 John C. Bowman, + with contributions from Mojca Miklavec + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <iostream> +#include <cstring> +#include <cerrno> +#include <sstream> +#include <signal.h> + +#include "pipestream.h" +#include "common.h" +#include "errormsg.h" +#include "settings.h" +#include "util.h" +#include "interact.h" +#include "lexical.h" +#include "camperror.h" +#include "pen.h" + +void iopipestream::open(const mem::vector<string> &command, const char *hint, + const char *application, int out_fileno) +{ + if(pipe(in) == -1) { + ostringstream buf; + buf << "in pipe failed: "; + for(size_t i=0; i < command.size(); ++i) buf << command[i]; + camp::reportError(buf); + } + + if(pipe(out) == -1) { + ostringstream buf; + buf << "out pipe failed: "; + for(size_t i=0; i < command.size(); ++i) buf << command[i]; + camp::reportError(buf); + } + cout.flush(); // Flush stdout to avoid duplicate output. + + if((pid=fork()) < 0) { + ostringstream buf; + buf << "fork failed: "; + for(size_t i=0; i < command.size(); ++i) buf << command[i]; + camp::reportError(buf); + } + + if(pid == 0) { + if(interact::interactive) signal(SIGINT,SIG_IGN); + close(in[1]); + close(out[0]); + close(STDIN_FILENO); + close(out_fileno); + dup2(in[0],STDIN_FILENO); + dup2(out[1],out_fileno); + close(in[0]); + close(out[1]); + char **argv=args(command); + if(argv) execvp(argv[0],argv); + execError(argv[0],hint,application); + kill(0,SIGTERM); + _exit(-1); + } + close(out[1]); + close(in[0]); + *buffer=0; + pipeopen=true; + pipein=true; + Running=true; + block(false,true); +} + +void iopipestream::eof() +{ + if(pipeopen && pipein) { + close(in[1]); + pipein=false; + } +} + +void iopipestream::pipeclose() +{ + if(pipeopen) { +// kill(pid,SIGTERM); + eof(); + close(out[0]); + Running=false; + pipeopen=false; + waitpid(pid,NULL,0); // Avoid zombies. + } +} + +void iopipestream::block(bool write, bool read) +{ + if(pipeopen) { + int w=fcntl(in[1],F_GETFL); + int r=fcntl(out[0],F_GETFL); + fcntl(in[1],F_SETFL,write ? w & ~O_NONBLOCK : w | O_NONBLOCK); + fcntl(out[0],F_SETFL,read ? r & ~O_NONBLOCK : r | O_NONBLOCK); + } +} + +ssize_t iopipestream::readbuffer() +{ + ssize_t nc; + char *p=buffer; + ssize_t size=BUFSIZE-1; + errno=0; + for(;;) { + if((nc=read(out[0],p,size)) < 0) { + if(errno == EAGAIN || errno == EINTR) {p[0]=0; break;} + else { + ostringstream buf; + buf << "read from pipe failed: errno=" << errno; + camp::reportError(buf); + } + nc=0; + } + p[nc]=0; + if(nc == 0) { + if(waitpid(pid,NULL,WNOHANG) == pid) + Running=false; + break; + } + if(nc > 0) { + if(settings::verbose > 2) cerr << p; + break; + } + } + return nc; +} + +bool iopipestream::tailequals(const char *buf, size_t len, const char *prompt, + size_t plen) +{ + const char *a=buf+len; + const char *b=prompt+plen; + while(b >= prompt) { + if(a < buf) return false; + if(*a != *b) return false; + // Handle MSDOS incompatibility: + if(a > buf && *a == '\n' && *(a-1) == '\r') --a; + --a; --b; + } + return true; +} + +string iopipestream::readline() +{ + sbuffer.clear(); + int nc; + do { + nc=readbuffer(); + sbuffer.append(buffer); + } while(buffer[nc-1] != '\n' && Running); + return sbuffer; +} + +void iopipestream::wait(const char *prompt) +{ + sbuffer.clear(); + size_t plen=strlen(prompt); + + do { + readbuffer(); + sbuffer.append(buffer); + } while(!tailequals(sbuffer.c_str(),sbuffer.size(),prompt,plen)); +} + +int iopipestream::wait() +{ + for(;;) { + int status; + if (waitpid(pid,&status,0) == -1) { + if (errno == ECHILD) return 0; + if (errno != EINTR) { + ostringstream buf; + buf << "Process " << pid << " failed"; + camp::reportError(buf); + } + } else { + if(WIFEXITED(status)) return WEXITSTATUS(status); + else { + ostringstream buf; + buf << "Process " << pid << " exited abnormally"; + camp::reportError(buf); + } + } + } +} + +void iopipestream::Write(const string &s) +{ + ssize_t size=s.length(); + if(settings::verbose > 2) cerr << s; + if(write(in[1],s.c_str(),size) != size) { + camp::reportFatal("write to pipe failed"); + } +} |