summaryrefslogtreecommitdiff
path: root/Build/source/utils/sam2p/gensio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/utils/sam2p/gensio.cpp')
-rw-r--r--Build/source/utils/sam2p/gensio.cpp884
1 files changed, 884 insertions, 0 deletions
diff --git a/Build/source/utils/sam2p/gensio.cpp b/Build/source/utils/sam2p/gensio.cpp
new file mode 100644
index 00000000000..b81b9f34fb0
--- /dev/null
+++ b/Build/source/utils/sam2p/gensio.cpp
@@ -0,0 +1,884 @@
+/*
+ * gensio.cpp -- IO-specific methods
+ * by pts@fazekas.hu at Tue Feb 26 13:28:12 CET 2002
+ */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#if 0
+extern "C" int errno;
+/* OK: autodetect with autoconf */
+extern "C" int _l_stat(const char *file_name, struct stat *buf);
+/* OK: Imp: not in ANSI C, but we cannot emulate it! */
+extern "C" int _v_s_n_printf ( char *str, size_t n, const char *format, va_list ap );
+#else
+#undef __STRICT_ANSI__ /* for __MINGW32__ */
+#define _BSD_SOURCE 1 /* vsnprintf(); may be emulated with fixup_vsnprintf() */
+#ifndef __APPLE__ /* SUXX: Max OS X has #ifndef _POSIX_SOURCE around lstat() :-( */
+#define _POSIX_SOURCE 1 /* also popen() */
+#endif
+#define _POSIX_C_SOURCE 2 /* also popen() */
+#define _XOPEN_SOURCE_EXTENDED 1 /* Digital UNIX lstat */
+#ifndef _XPG4_2
+#define _XPG4_2 1 /* SunOS 5.7 lstat() */
+#endif
+#undef _XOPEN_SOURCE /* pacify gcc-3.1 */
+#define _XOPEN_SOURCE 1 /* popen() on Digital UNIX */
+#endif
+
+#include "gensio.hpp"
+#include "error.hpp"
+#include <string.h> /* strlen() */
+#include <stdarg.h> /* va_list */
+#if _MSC_VER > 1000
+// extern "C" int getpid(void);
+# include <process.h>
+#else
+# include <unistd.h> /* getpid() */
+#endif
+#include <sys/stat.h> /* struct stat */
+#include <stdlib.h> /* getenv() */
+#include <errno.h>
+#include <signal.h> /* signal() */ /* Imp: use sigaction */
+#if HAVE_DOS_BINARY
+#undef __STRICT_ANSI__
+#include <fcntl.h> /* O_BINARY */
+#include <io.h> /* setmode() */
+#endif
+
+#define USGE(a,b) ((unsigned char)(a))>=((unsigned char)(b))
+
+#if HAVE_PTS_VSNPRINTF /* Both old and c99 work OK */
+# define VSNPRINTF vsnprintf
+#else
+# if OBJDEP
+# warning REQUIRES: snprintf.o
+# endif
+# include "snprintf.h"
+# define VSNPRINTF fixup_vsnprintf /* Tested, C99. */
+#endif
+
+static void cleanup(int) {
+ Error::cexit(126);
+}
+
+void Files::doSignalCleanup() {
+ signal(SIGINT, cleanup);
+ signal(SIGTERM, cleanup);
+#ifdef SIGHUP
+ signal(SIGHUP, SIG_IGN);
+#endif
+ /* Dat: don't do cleanup for SIGQUIT */
+}
+
+GenBuffer::Writable& SimBuffer::B::vformat(slen_t n, char const *fmt, va_list ap) {
+ /* Imp: test this code in various systems and architectures. */
+ /* Dat: vsnprintf semantics are verified in configure AC_PTS_HAVE_VSNPRINTF,
+ * and a replacement vsnprintf is provided in case of problems. We don't
+ * depend on HAVE_PTS_VSNPRINTF_C99, because C99-vsnprintf is a run-time
+ * property.
+ *
+ * C99 vsnprintf semantics: vsnprintf() always returns a non-negative value:
+ * the number of characters (trailing \0 not included) that would have been
+ * printed if there were enough space. Only the first maxlen (@param)
+ * characters may be modified. The output is terminated by \0 iff maxlen!=0.
+ * NULL @param dststr is OK iff maxlen==0. Since glibc 2.1.
+ * old vsnprintf semantics: vsnprintf() returns a non-negative value or -1:
+ * 0 if maxlen==0, otherwise: -1 if maxlen is shorter than all the characters
+ * plus the trailing \0, otherwise: the number of characters (trailing \0 not
+ * included) that were printed. Only the first maxlen (@param)
+ * characters may be modified. The output is terminated by \0 iff maxlen!=0.
+ * NULL @param dststr is OK iff maxlen==0.
+ */
+ if (n>0) { /* avoid problems with old-style vsnprintf */
+ char *s; vi_grow2(0, n+1, 0, &s); len-=n+1; /* +1: sprintf appends '\0' */
+ const_cast<char*>(beg)[len]='\0'; /* failsafe sentinel */
+ slen_t did=VSNPRINTF(s, n+1, fmt, ap);
+ if (did>n) did=n;
+ /* ^^^ Dat: always true: (unsigned)-1>n, so this works with both old and c99 */
+ /* Now: did contains the # chars to append, without trailing '\0' */
+ /* Dat: we cannot check for -1, because `did' is unsigned */
+ /* Dat: trailer '\0' doesn't count into `did' */
+ len+=did;
+ }
+ return *this;
+}
+GenBuffer::Writable& SimBuffer::B::vformat(char const *fmt, va_list ap) {
+ char dummy, *s;
+ slen_t did=VSNPRINTF(&dummy, 1, fmt, ap), n;
+ if (did>0) { /* skip if nothing to be appended */
+ /* vvv Dat: we cannot check for -1, because `did' is unsigned */
+ if ((did+1)!=(slen_t)0) { /* C99 semantics; quick shortcut */
+ vi_grow2(0, (n=did)+1, 0, &s); len-=n+1;
+ ASSERT_SIDE2(VSNPRINTF(s, n+1, fmt, ap), * 1U==did);
+ } else { /* old semantics: grow the buffer incrementally */
+ if ((n=strlen(fmt))<16) n=16; /* initial guess */
+ while (1) {
+ vi_grow2(0, n+1, 0, &s); len-=n+1; /* +1: sprintf appends '\0' */
+ const_cast<char*>(beg)[len]='\0'; /* failsafe sentinel */
+ did=VSNPRINTF(s, n+1, fmt, ap);
+ if ((did+1)!=(slen_t)0) {
+ assert(did!=0); /* 0 is caught early in this function */
+ assert(did<=n); /* non-C99 semantics */
+ break;
+ }
+ n<<=1;
+ }
+ }
+ len+=did;
+ }
+ return *this;
+}
+
+GenBuffer::Writable& SimBuffer::B::format(slen_t n, char const *fmt, ...) {
+ va_list ap;
+ PTS_va_start(ap, fmt);
+ vformat(n, fmt, ap);
+ va_end(ap);
+ return *this;
+}
+GenBuffer::Writable& SimBuffer::B::format(char const *fmt, ...) {
+ va_list ap;
+ PTS_va_start(ap, fmt);
+ vformat(fmt, ap);
+ va_end(ap);
+ return *this;
+}
+
+/* --- */
+
+GenBuffer::Writable& Files::FILEW::vformat(slen_t n, char const *fmt, va_list ap) {
+ /* Dat: no vfnprintf :-( */
+ SimBuffer::B buf;
+ buf.vformat(n, fmt, ap);
+ fwrite(buf(), 1, buf.getLength(), f);
+ return*this;
+}
+GenBuffer::Writable& Files::FILEW::vformat(char const *fmt, va_list ap) {
+ vfprintf(f, fmt, ap);
+ return*this;
+}
+
+/* --- */
+
+/** Must be <=32767. Should be a power of two. */
+static const slen_t BUFLEN=4096;
+
+void Encoder::vi_putcc(char c) { vi_write(&c, 1); }
+int Decoder::vi_getcc() { char ret; return vi_read(&ret, 1)==1 ? (unsigned char)ret : -1; }
+void Encoder::writeFrom(GenBuffer::Writable& out, FILE *f) {
+ char *buf=new char[BUFLEN];
+ int wr;
+ while (1) {
+ if ((wr=fread(buf, 1, BUFLEN, f))<1) break;
+ out.vi_write(buf, wr);
+ }
+ delete [] buf;
+}
+void Encoder::writeFrom(GenBuffer::Writable& out, GenBuffer::Readable& in) {
+ char *buf=new char[BUFLEN];
+ int wr;
+ while (1) {
+ if ((wr=in.vi_read(buf, BUFLEN))<1) break;
+ out.vi_write(buf, wr);
+ }
+ delete [] buf;
+}
+
+/* --- */
+
+Filter::FILEE::FILEE(char const* filename) {
+ if (NULLP==(f=fopen(filename,"wb"))) Error::sev(Error::EERROR) << "Filter::FILEE: error open4write: " << FNQ2(filename,strlen(filename)) << (Error*)0;
+ closep=true;
+}
+void Filter::FILEE::vi_write(char const*buf, slen_t len) {
+ if (len==0) close(); else fwrite(buf, 1, len, f);
+}
+void Filter::FILEE::close() {
+ if (closep) { fclose(f); f=(FILE*)NULLP; closep=false; }
+}
+
+/* --- */
+
+static FILE* fopenErr(char const* filename, char const* errhead) {
+ FILE *f;
+ if (NULLP==(f=fopen(filename,"rb")))
+ Error::sev(Error::EERROR) << errhead << ": error open4read: " << FNQ2(filename,strlen(filename)) << (Error*)0;
+ return f;
+}
+
+Filter::FILED::FILED(char const* filename) {
+ f=fopenErr(filename, "Filter::FileD");
+ closep=true;
+}
+slen_t Filter::FILED::vi_read(char *buf, slen_t len) {
+ if (len==0) { close(); return 0; }
+ return fread(buf, 1, len, f);
+}
+void Filter::FILED::close() {
+ if (closep) { fclose(f); f=(FILE*)NULLP; closep=false; }
+}
+
+/* --- */
+
+Filter::UngetFILED::UngetFILED(char const* filename_, FILE *stdin_f, closeMode_t closeMode_) {
+ if (stdin_f!=NULLP && (filename_==NULLP || (filename_[0]=='-' && filename_[1]=='\0'))) {
+ f=stdin_f;
+ Files::set_binary_mode(fileno(f), true);
+ closeMode_&=~CM_unlinkp;
+ if (0!=(closeMode_&CM_keep_stdinp)) closeMode_&=~CM_closep;
+ filename_=(char const*)NULLP; /* BUGFIX at Tue Jan 4 23:45:31 CET 2005 */
+ } else {
+ f=fopenErr(filename_, "Filter::UngetFileD");
+ }
+ if (filename_!=NULLP) strcpy(const_cast<char*>(filename=new char[strlen(filename_)+1]), filename_);
+ else filename=(char*)NULLP;
+ /* fprintf(stderr,"filename:%s\n",filename); */
+ closeMode=closeMode_;
+ ftell_at=0;
+ ofs=0;
+}
+// Filter::UngetFILED::checkFILE() { }
+slen_t Filter::UngetFILED::vi_read(char *buf, slen_t len) {
+ slen_t delta;
+ if (len==0) {
+ close(); return 0;
+ } else if (unget.getLength()==0) { delta=0; do_read_f:
+ delta+=(f==NULLP ? 0 : fread(buf, 1, len, f));
+ ftell_at+=delta;
+ return delta;
+ // delta+=fread(buf, 1, len, f); // write(1, buf, delta); // return delta;
+ } else if (ofs+len<=unget.getLength()) {
+ // printf("\nul=%d ft=%ld\n", unget.getLength(), ftell(f)); fflush(stdout);
+ memcpy(buf, unget()+ofs, len); /* Dat: don't remove from unget yet */
+ ftell_at+=len;
+ // write(1, buf, len);
+ if ((ofs+=len)==unget.getLength()) { unget.forgetAll(); ofs=0; }
+ return len;
+ } else {
+ // printf("\num=%d ft=%d\n", unget.getLength(), ftell(f)); fflush(stdout);
+ delta=unget.getLength()-ofs; /* BUGFIX at Sat Apr 19 17:15:50 CEST 2003 */
+ memcpy(buf, unget()+ofs, delta);
+ // write(1, buf, delta);
+ unget.forgetAll(); ofs=0;
+ buf+=delta;
+ len-=delta;
+ goto do_read_f;
+ }
+}
+void Filter::UngetFILED::close() {
+ if (0!=(closeMode&CM_closep)) { fclose(f); f=(FILE*)NULLP; closeMode&=~CM_closep; }
+ unget.forgetAll(); ofs=0;
+ if (filename!=NULLP) {
+ if (0!=(closeMode&CM_unlinkp)) { remove(filename); }
+ delete [] filename;
+ }
+}
+
+int Filter::UngetFILED::vi_getcc() {
+ if (unget.getLength()==0) { do_getc:
+ int i=-1;
+ if (f!=NULLP && (i=MACRO_GETC(f))!=-1) ftell_at++;
+ return i;
+ }
+ if (ofs==unget.getLength()) { ofs=0; unget.forgetAll(); goto do_getc; }
+ ftell_at++;
+ return unget[ofs++];
+}
+int Filter::UngetFILED::getc_seekable() {
+ /* Glibc stdio doesn't allow fseek() even if the seek would go into the
+ * read buffer, and fseek(f, 0, SEEK_CUR) fails for unseekable files. Fine.
+ */
+ int c=MACRO_GETC(f);
+ return 0==fseek(f, -1L, SEEK_CUR) ? -2 : c;
+}
+bool Filter::UngetFILED::isSeekable() {
+ long pos, posend;
+ if (f==NULLP || 0!=(closeMode&CM_seekablep)) return true;
+ // return false;
+ clearerr(f); /* clears both the EOF and error indicators */
+ if (-1L==(pos=ftell(f)) /* sanity checks on ftell() and fseek() */
+ || 0!=fseek(f, 0L, SEEK_CUR)
+ || pos!=ftell(f)
+ || 0!=fseek(f, 0L, SEEK_END)
+ || (posend=ftell(f))==0 || posend<pos
+ || 0!=fseek(f, 0L, SEEK_SET)
+ || 0!=ftell(f)
+ || 0!=fseek(f, pos, SEEK_SET)
+ || pos!=ftell(f)
+ ) return false;
+ int c;
+ if ((c=getc_seekable())==-2 && pos==ftell(f)) {
+ // if (0!=fseek(f, -1L, SEEK_CUR) || pos!=ftell(f))
+ // Error::sev(Error::EERROR) << "Filter::UngetFILED: cannot seek back" << (Error*)0;
+ return true;
+ }
+ unget << (char)c; /* not seekable, must unget the test character just read */
+ return false;
+}
+FILE* Filter::UngetFILED::getFILE(bool seekable_p) {
+ FILE *tf;
+ if (!unget.isEmpty() || (seekable_p && !isSeekable())) { do_temp:
+ /* must create a temporary file */
+ SimBuffer::B tmpnam;
+ if (filename==NULLP) Error::sev(Error::NOTICE) << "Filter::UngetFILED" << ": using temp for" << " `-' (stdin)" << (Error*)0;
+ else Error::sev(Error::NOTICE) << "Filter::UngetFILED" << ": using temp for" << ": " << FNQ2(filename,strlen(filename)) << (Error*)0;
+ if (NULLP==(tf=Files::open_tmpnam(tmpnam, "wb+"))) {
+ if (filename==NULLP) Error::sev(Error::EERROR) << "Filter::UngetFILED" << ": cannot open temp file for" << " `-' (stdin)" << (Error*)0;
+ else Error::sev(Error::EERROR) << "Filter::UngetFILED" << ": cannot open temp file for" << ": " << FNQ2(filename,strlen(filename)) << (Error*)0;
+ }
+ tmpnam.term0();
+ /* vvv change filename, so CM_unlinkp can work */
+ if (filename!=NULLP) {
+ if (0!=(closeMode&CM_unlinkp)) { remove(filename); }
+ delete [] filename;
+ }
+ strcpy(const_cast<char*>(filename=new char[tmpnam.getLength()+1]), tmpnam());
+ Files::tmpRemoveCleanup(filename);
+ if (unget.getLength()-ofs==fwrite(unget()+ofs, 1, unget.getLength()-ofs, tf)) {
+ static const slen_t BUFSIZE=4096; /* BUGFIX at Sat Apr 19 15:43:59 CEST 2003 */
+ char *buf=new char[BUFSIZE];
+ unsigned got;
+ // fprintf(stderr, "ftf=%ld ofs=%d\n", ftell(f), ofs);
+ while ((0<(got=fread(buf, 1, BUFSIZE, f)))
+ && got==fwrite(buf, 1, got, tf)) {}
+ // fprintf(stderr,"got=%d ftell=%d\n", got, ftell(tf));
+ delete [] buf;
+ }
+ unget.forgetAll(); ofs=0;
+ fflush(tf); rewind(tf);
+ if (ferror(tf) || ferror(f))
+ Error::sev(Error::EERROR) << "Filter::UngetFILED" << ": cannot write temp file" << (Error*)0;
+ if (0!=(closeMode&CM_closep)) fclose(f);
+ closeMode|=CM_closep|CM_unlinkp; /* close and unlink the temporary file */
+ /* ^^^ Imp: verify VC++ compilation, +others */
+ f=tf;
+ } else if (f==NULLP) { /* no real file open */
+ #if OS_COTY==COTY_UNIX
+ tf=fopen("/dev/null","rb");
+ #else
+ #if OS_COPTY==COTY_WIN9X || OS_COTY==COTY_WINNT
+ tf=fopen("nul","rb");
+ #else
+ tf=(FILE*)NULLP;
+ #endif
+ #endif
+ if (tf==NULLP) goto do_temp; /* perhaps inside chroot() */
+ close();
+ closeMode|=CM_closep|CM_unlinkp; /* close and unlink the temporary file */
+ f=tf;
+ }
+ closeMode|=CM_seekablep;
+ return f;
+}
+void Filter::UngetFILED::seek(long abs_ofs) {
+ if (abs_ofs==vi_tell()) return;
+ (void) getFILE(true); /* ensure seekability */
+ if (0!=fseek(f, abs_ofs, SEEK_SET))
+ Error::sev(Error::EERROR) << "Filter::UngetFILED" << ": cannot seek" << (Error*)0;
+ assert(unget.isEmpty());
+ assert(ofs==0);
+ ftell_at=abs_ofs;
+}
+void Filter::UngetFILED::unread(char const *s, slen_t slen) {
+ ftell_at-=slen;
+ if (slen==0) {
+ } else if (slen<=ofs) {
+ memcpy(const_cast<char*>(unget()+(ofs-=slen)), s, slen);
+ } else {
+ slen-=ofs;
+ ofs=0;
+ if (!unget.isEmpty() || 0!=fseek(f, -slen, SEEK_CUR)) {
+ assert(unget.isEmpty()); // !!
+ unget.vi_write(s, slen); /* complete garbage unless unget was empty */
+ assert(unget.getLength());
+ }
+ }
+ // fprintf(stderr, "%d..\n", unget.getLength());
+}
+void Filter::UngetFILED::appendLine(GenBuffer::Writable &buf, int delimiter) {
+ // fprintf(stderr, "this=%p %d (%p)\n", this, unget.getLength(), unget());
+ unget.term0();
+ if (delimiter<0) {
+ char rbuf[4096];
+ slen_t got;
+ /* vvv Imp: less memory copying, less stack usage? */
+ while (0!=(got=vi_read(rbuf, sizeof(rbuf)))) buf.vi_write(rbuf, got);
+ } else if (unget.getLength()==0) { do_getc:
+ int i;
+ if (f!=NULLP) {
+ while ((i=MACRO_GETC(f))>=0 && i!=delimiter) { buf.vi_putcc(i); ftell_at++; }
+ if (i>=0) buf.vi_putcc(i);
+ }
+ } else {
+ char const *p=unget()+ofs, *p0=p, *pend=unget.end_();
+ assert(ofs<=unget.getLength());
+ while (p!=pend && *p!=delimiter) p++;
+ ftell_at+=p-p0;
+ if (p==pend) { buf.vi_write(p0, p-p0); ofs=0; unget.forgetAll(); goto do_getc; }
+ ftell_at++; p++; /* found delimiter in `unget' */
+ buf.vi_write(p0, p-p0);
+ ofs+=p-p0;
+ }
+}
+
+/* --- */
+
+Filter::PipeE::PipeE(GenBuffer::Writable &out_, char const*pipe_tmpl, slendiff_t i): tmpname(), out(out_), tmpename() {
+ /* <code similarity: Filter::PipeE::PipeE and Filter::PipeD::PipeD> */
+ param_assert(pipe_tmpl!=(char const*)NULLP);
+ SimBuffer::B *pp;
+ char const*s=pipe_tmpl;
+ lex: while (s[0]!='\0') { /* Interate throuh the template, substitute temporary filenames */
+ if (*s++=='%') switch (*s++) {
+ case '\0': case '%':
+ redir_cmd << '%';
+ break;
+ case 'i': /* the optional integer passed in param `i' */
+ redir_cmd << i;
+ break;
+ case '*': /* the optional unsafe string passed in param `i' */
+ redir_cmd << (char const*)i;
+ break;
+ case 'd': case 'D': /* temporary file for encoded data output */
+ pp=&tmpname;
+ put:
+ // if (*pp) Error::sev(Error::EERROR) << "Filter::PipeE" << ": multiple %escape" << (Error*)0;
+ /* ^^^ multiple %escape is now a supported feature */
+ if (!*pp && !Files::find_tmpnam(*pp)) Error::sev(Error::EERROR) << "Filter::PipeE" << ": tmpnam() failed" << (Error*)0;
+ assert(! !*pp); /* pacify VC6.0 */
+ // *pp << ext;
+ pp->term0();
+ if ((unsigned char)(s[-1]-'A')<(unsigned char)('Z'-'A'))
+ redir_cmd.appendFnq(*pp, /*preminus:*/ true); /* Capital letter: quote from the shell */
+ else redir_cmd << *pp;
+ break;
+ case 'e': case 'E': /* temporary file for error messages */
+ pp=&tmpename;
+ goto put;
+ case 's': case 'S': /* temporary source file */
+ pp=&tmpsname;
+ goto put;
+ default:
+ Error::sev(Error::EERROR) << "Filter::PipeE" << ": invalid %escape in pipe_tmpl" << (Error*)0;
+ } else redir_cmd << s[-1];
+ }
+ #if 0
+ if (!tmpname) Error::sev(Error::EERROR) << "Filter::PipeE" << ": no outname (%D) in cmd: " << (SimBuffer::B().appendDumpC(redir_cmd)) << (Error*)0;
+ #else
+ /* Append quoted file redirect to command, if missing */
+ if (!tmpname) { s=" >%D"; goto lex; }
+ #endif
+ #if !HAVE_PTS_POPEN
+ if (!tmpsname) { s=" <%S"; goto lex; }
+ #endif
+ // tmpname="tmp.name";
+ redir_cmd.term0();
+ if (tmpname) { Files::tmpRemoveCleanup(tmpname()); remove(tmpname()); } /* already term0() */
+ /* ^^^ Dat: remove() here introduces a race condition, but helps early error detection */
+ if (tmpename) Files::tmpRemoveCleanup(tmpename()); /* already term0() */
+ if (tmpsname) Files::tmpRemoveCleanup(tmpsname()); /* already term0() */
+ /* </code similarity: Filter::PipeE::PipeE and Filter::PipeD::PipeD> */
+
+ // fprintf(stderr, "rc: (%s)\n", redir_cmd());
+
+ #if HAVE_PTS_POPEN
+ if (!tmpsname) {
+ if (NULLP==(p=popen(redir_cmd(), "w"CFG_PTS_POPEN_B))) Error::sev(Error::EERROR) << "Filter::PipeE" << ": popen() failed: " << (SimBuffer::B().appendDumpC(redir_cmd)) << (Error*)0;
+ signal(SIGPIPE, SIG_IGN); /* Don't abort process with SIGPIPE signals if child cannot read our data */
+ } else {
+ #else
+ if (1) {
+ #endif
+ #if !HAVE_system_in_stdlib
+ Error::sev(Error::EERROR) << "Filter::PipeE" << ": no system() on this system" << (Error*)0;
+ #else
+ if (NULLP==(p=fopen(tmpsname(), "wb"))) Error::sev(Error::EERROR) << "Filter::PipeD" << ": fopen(w) failed: " << (SimBuffer::B().appendDumpC(redir_cmd)) << (Error*)0;
+ #endif
+ }
+}
+void Filter::PipeE::vi_copy(FILE *f) {
+ writeFrom(out, f);
+ if (ferror(f)) Error::sev(Error::EERROR) << "Filter::PipeE: vi_copy() failed" << (Error*)0;
+ fclose(f);
+}
+void Filter::PipeE::vi_write(char const*buf, slen_t len) {
+ assert(p!=NULLP);
+ int wr;
+ if (len==0) { /* EOF */
+ if (tmpsname) {
+ #if HAVE_system_in_stdlib
+ fclose(p);
+ if (0!=(Files::system3(redir_cmd()))) Error::sev(Error::EERROR) << "Filter::PipeE" << ": system() failed: " << (SimBuffer::B().appendDumpC(redir_cmd)) << (Error*)0;
+ remove(tmpsname());
+ #endif /* Dat: else is not required; would be unreachable code. */
+ } else {
+ #if HAVE_PTS_POPEN
+ if (0!=pclose(p)) Error::sev(Error::EERROR) << "Filter::PipeE" << ": pclose() failed; error in external prg" << (Error*)0;
+ #endif
+ }
+ vi_check();
+ p=(FILE*)NULLP;
+ FILE *f=fopen(tmpname(),"rb");
+ if (NULLP==f) Error::sev(Error::EERROR) << "Filter::PipeE" <<": fopen() after pclose() failed: " << tmpname << (Error*)0;
+ vi_copy(f);
+ // if (ferror(f)) Error::sev(Error::EERROR) << "Filter::Pipe: fread() tmpfile failed" << (Error*)0;
+ // fclose(f);
+ /* ^^^ interacts badly when Image::load() is called inside vi_copy(),
+ * Image::load() calls fclose()
+ */
+ if (tmpname ) remove(tmpname ());
+ if (tmpename) remove(tmpename());
+ if (tmpsname) remove(tmpsname());
+ out.vi_write(0,0); /* Signal EOF to subsequent filters. */
+ } else {
+ while (len!=0) {
+ wr=fwrite(buf, 1, len>0x4000?0x4000:len, p);
+// assert(!ferror(p));
+ if (ferror(p)) {
+ vi_check(); /* Give a chance to report a better error message when Broken File. */
+ Error::sev(Error::EERROR) << "Filter::PipeE" << ": pipe write failed" << (Error*)0;
+ }
+ buf+=wr; len-=wr;
+ }
+ }
+}
+Filter::PipeE::~PipeE() {}
+void Filter::PipeE::vi_check() {}
+
+/* --- */
+
+Filter::PipeD::PipeD(GenBuffer::Readable &in_, char const*pipe_tmpl, slendiff_t i): state(0), in(in_) {
+ /* <code similarity: Filter::PipeE::PipeE and Filter::PipeD::PipeD> */
+ param_assert(pipe_tmpl!=(char const*)NULLP);
+ SimBuffer::B *pp=(SimBuffer::B*)NULLP;
+ char const*s=pipe_tmpl;
+ lex: while (s[0]!='\0') { /* Interate throuh the template, substitute temporary filenames */
+ if (*s++=='%') switch (*s++) {
+ case '\0': case '%':
+ redir_cmd << '%';
+ break;
+ case 'i': /* the optional integer passed in param `i' */
+ redir_cmd << i;
+ break;
+ case '*': /* the optional unsafe string passed in param `i' */
+ redir_cmd << (char const*)i;
+ break;
+ case 'd': case 'D': /* temporary file for encoded data output */
+ pp=&tmpname;
+ put:
+ // if (*pp) Error::sev(Error::EERROR) << "Filter::PipeD: multiple %escape" << (Error*)0;
+ /* ^^^ multiple %escape is now a supported feature */
+ if (!*pp && !Files::find_tmpnam(*pp)) Error::sev(Error::EERROR) << "Filter::PipeD" << ": tmpnam() failed" << (Error*)0;
+ assert(*pp);
+ pp->term0();
+ if ((unsigned char)(s[-1]-'A')<(unsigned char)('Z'-'A'))
+ redir_cmd.appendFnq(*pp); /* Capital letter: quote from the shell */
+ else redir_cmd << *pp;
+ break;
+ case 'e': case 'E': /* temporary file for error messages */
+ pp=&tmpename;
+ goto put;
+ case 's': case 'S': /* temporary source file */
+ pp=&tmpsname;
+ goto put;
+ /* OK: implement temporary file for input, option to suppress popen() */
+ default:
+ Error::sev(Error::EERROR) << "Filter::PipeD: invalid %escape in pipe_tmpl" << (Error*)0;
+ } else redir_cmd << s[-1];
+ }
+ #if 0
+ if (!tmpname) Error::sev(Error::EERROR) << "Filter::PipeD" << ": no outname (%D) in cmd: " << (SimBuffer::B().appendDumpC(redir_cmd)) << (Error*)0;
+ #else
+ /* Append quoted file redirect to command, if missing */
+ if (!tmpname) { s=" >%D"; goto lex; }
+ #endif
+ #if !HAVE_PTS_POPEN
+ if (!tmpsname) { s=" <%S"; goto lex; }
+ #endif
+ // tmpname="tmp.name";
+ redir_cmd.term0();
+ if (tmpname) Files::tmpRemoveCleanup(tmpname ()); /* already term0() */
+ if (tmpename) Files::tmpRemoveCleanup(tmpename()); /* already term0() */
+ if (tmpsname) { Files::tmpRemoveCleanup(tmpsname()); remove(tmpsname()); } /* already term0() */
+ /* ^^^ Dat: remove() here introduces a race condition, but helps early error detection */
+ /* </code similarity: Filter::PipeE::PipeE and Filter::PipeD::PipeD> */
+}
+slen_t Filter::PipeD::vi_read(char *tobuf, slen_t tolen) {
+ assert(!(tolen!=0 && state==2));
+ if (state==2) return 0; /* Should really never happen. */
+ /* Normal read operation with tolen>0; OR tolen==0 */
+ if (state==0) { /* Read the whole stream from `in', write it to `tmpsname' */
+ #if HAVE_PTS_POPEN
+ if (!tmpsname) {
+ if (NULLP==(p=popen(redir_cmd(), "w"CFG_PTS_POPEN_B))) Error::sev(Error::EERROR) << "Filter::PipeD" << ": popen() failed: " << (SimBuffer::B().appendDumpC(redir_cmd)) << (Error*)0;
+ signal(SIGPIPE, SIG_IGN); /* Don't abort process with SIGPIPE signals if child cannot read our data */
+ vi_precopy();
+ in.vi_read(0,0);
+ if (0!=pclose(p)) Error::sev(Error::EERROR) << "Filter::PipeD" << ": pclose() failed; error in external prg" << (Error*)0;
+ } else {
+ #else
+ if (1) {
+ #endif
+ #if !HAVE_system_in_stdlib
+ Error::sev(Error::EERROR) << "Filter::PipeD" << ": no system() on this system" << (Error*)0;
+ #else
+ if (NULLP==(p=fopen(tmpsname(), "wb"))) Error::sev(Error::EERROR) << "Filter::PipeD" << ": fopen(w) failed: " << (SimBuffer::B().appendDumpC(redir_cmd)) << (Error*)0;
+ vi_precopy();
+ in.vi_read(0,0);
+ fclose(p);
+ if (0!=(Files::system3(redir_cmd()))) Error::sev(Error::EERROR) << "Filter::PipeD" << ": system() failed: " << (SimBuffer::B().appendDumpC(redir_cmd)) << (Error*)0;
+ remove(tmpsname());
+ #endif
+ }
+ vi_check();
+ if (NULLP==(p=fopen(tmpname(),"rb"))) Error::sev(Error::EERROR) << "Filter::PipeD" << ": fopen() after pclose() failed: " << tmpname << (Error*)0;
+ state=1;
+ } /* IF state==0 */
+ assert(state==1);
+ if (tolen==0 || 0==(tolen=fread(tobuf, 1, tolen, p))) do_close();
+ // putchar('{'); fwrite(tobuf, 1, tolen, stdout); putchar('}');
+ return tolen;
+}
+void Filter::PipeD::do_close() {
+ fclose(p); p=(FILE*)NULLP;
+ if (tmpname ) remove(tmpname ());
+ if (tmpename) remove(tmpename());
+ if (tmpsname) remove(tmpsname());
+ state=2;
+}
+void Filter::PipeD::vi_precopy() {
+ char *buf0=new char[BUFLEN], *buf;
+ slen_t len, wr;
+ while (0!=(len=in.vi_read(buf0, BUFLEN))) {
+ // printf("[%s]\n", buf0);
+ for (buf=buf0; len!=0; buf+=wr, len-=wr) {
+ wr=fwrite(buf, 1, len>0x4000?0x4000:len, p);
+ if (ferror(p)) {
+ vi_check(); /* Give a chance to report a better error message when Broken File. */
+ Error::sev(Error::EERROR) << "Filter::PipeD" << ": pipe write failed" << (Error*)0;
+ }
+ }
+ }
+ delete [] buf0;
+}
+int Filter::PipeD::vi_getcc() {
+ char ret; int i;
+ // fprintf(stderr,"state=%u\n", state);
+ switch (state) {
+ case 0: return vi_read(&ret, 1)==1 ? (unsigned char)ret : -1;
+ case 1: if (-1==(i=MACRO_GETC(p))) do_close(); return i;
+ /* case: 2: fall-through */
+ }
+ return -1;
+}
+void Filter::PipeD::vi_check() {}
+Filter::PipeD::~PipeD() { if (state!=2) vi_read(0,0); }
+
+Filter::BufR::BufR(GenBuffer const& buf_): bufp(&buf_) {
+ buf_.first_sub(sub);
+}
+int Filter::BufR::vi_getcc() {
+ if (bufp==(GenBuffer const*)NULLP) return -1; /* cast: pacify VC6.0 */
+ if (sub.len==0) {
+ bufp->next_sub(sub);
+ if (sub.len==0) { bufp=(GenBuffer const*)NULLP; return -1; }
+ }
+ sub.len--; return *sub.beg++;
+}
+slen_t Filter::BufR::vi_read(char *to_buf, slen_t max) {
+ if (max==0 || bufp==(GenBuffer const*)NULLP) return 0;
+ if (sub.len==0) {
+ bufp->next_sub(sub);
+ if (sub.len==0) { bufp=(GenBuffer const*)NULLP; return 0; }
+ }
+ if (max<sub.len) {
+ memcpy(to_buf, sub.beg, max);
+ sub.len-=max; sub.beg+=max;
+ return max;
+ }
+ max=sub.len; sub.len=0;
+ memcpy(to_buf, sub.beg, max);
+ return max;
+}
+void Filter::BufR::vi_rewind() { bufp->first_sub(sub); }
+
+Filter::FlatD::FlatD(char const* s_, slen_t slen_): s(s_), sbeg(s_), slen(slen_) {}
+Filter::FlatD::FlatD(char const* s_): s(s_), sbeg(s_), slen(strlen(s_)) {}
+void Filter::FlatD::vi_rewind() { s=sbeg; }
+int Filter::FlatD::vi_getcc() {
+ if (slen==0) return -1;
+ slen--; return *(unsigned char const*)s++;
+}
+slen_t Filter::FlatD::vi_read(char *to_buf, slen_t max) {
+ if (max>slen) max=slen;
+ memcpy(to_buf, s, max);
+ s+=max; slen-=max;
+ return max;
+}
+
+/* --- */
+
+
+#if HAVE_lstat_in_sys_stat
+# define PTS_lstat lstat
+#else
+# define PTS_lstat stat
+#endif
+
+/** @param fname must start with '/' (dir separator)
+ * @return true if file successfully created
+ */
+FILE *Files::try_dir(SimBuffer::B &dir, SimBuffer::B const&fname, char const*s1, char const*s2, char const*open_mode) {
+ if (dir.isEmpty() && s1==(char const*)NULLP) return (FILE*)NULLP;
+ SimBuffer::B full(s1!=(char const*)NULLP?s1:dir(),
+ s1!=(char const*)NULLP?strlen(s1):dir.getLength(),
+ s2!=(char const*)NULLP?s2:"",
+ s2!=(char const*)NULLP?strlen(s2):0, fname(), fname.getLength());
+ full.term0();
+ struct stat st;
+ FILE *f;
+ /* Imp: avoid race conditions with other processes pretending to be us... */
+ if (-1!=PTS_lstat(full(), &st)
+ || (0==(f=fopen(full(), open_mode)))
+ || ferror(f)
+ ) return (FILE*)NULLP;
+ dir=full;
+ return f;
+}
+
+#if OS_COTY==COTY_WIN9X || OS_COTY==COTY_WINNT
+# define DIR_SEP "\\"
+#else
+# define DIR_SEP "/"
+#endif
+
+FILE *Files::open_tmpnam(SimBuffer::B &dir, char const*open_mode, char const*extension) {
+ /* Imp: verify / on Win32... */
+ /* Imp: ensure uniqueness on NFS */
+ /* Imp: short file names */
+ static unsigned PTS_INT32_T counter=0;
+ assert(Error::tmpargv0!=(char const*)NULLP);
+ SimBuffer::B fname(DIR_SEP "tmp_", 5, Error::tmpargv0,strlen(Error::tmpargv0));
+ /* ^^^ Dat: we need DIR_SEP here, because the name of the tmp file may be
+ * passed to Win32 COMMAND.COM, which interprets "/" as a switch
+ */
+ long pid=getpid();
+ if (pid<0 && pid>-(1<<24)) pid=-pid;
+ fname << '_' << pid << '_' << counter++;
+ if (extension) fname << extension;
+ fname.term0();
+ FILE *f=(FILE*)NULLP;
+ // char const* open_mode=binary_p ? "wb" : "w"; /* Dat: "bw" is bad */
+ (void)( ((FILE*)NULLP!=(f=try_dir(dir, fname, 0, 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, PTS_CFG_P_TMPDIR, 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, getenv("TMPDIR"), 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, getenv("TMP"), 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, getenv("TEMP"), 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, "/tmp", 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, getenv("WINBOOTDIR"), "//temp", open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, getenv("WINDIR"), "//temp", open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, "c:/temp", 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, "c:/windows/temp", 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, "c:/winnt/temp", 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, "c:/tmp", 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, ".", 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, "..", 0, open_mode))) ||
+ ((FILE*)NULLP!=(f=try_dir(dir, fname, "../..", 0, open_mode))) );
+ return f;
+}
+
+bool Files::find_tmpnam(SimBuffer::B &dir) {
+ FILE *f=open_tmpnam(dir);
+ if (f!=NULL) { fclose(f); return true; }
+ return false;
+}
+
+bool Files::tmpRemove=true;
+
+static int cleanup_remove(Error::Cleanup *cleanup) {
+ if (Files::tmpRemove) return Files::removeIf(cleanup->getBuf());
+ Error::sev(Error::WARNING) << "keeping tmp file: " << cleanup->getBuf() << (Error*)0;
+ return 0;
+}
+
+void Files::tmpRemoveCleanup(char const* filename) {
+ Error::newCleanup(cleanup_remove, 0, filename);
+}
+
+static int cleanup_remove_cond(Error::Cleanup *cleanup) {
+ if (*(FILE**)cleanup->data!=NULLP) {
+ fclose(*(FILE**)cleanup->data);
+ if (Files::tmpRemove) return Files::removeIf(cleanup->getBuf());
+ Error::sev(Error::WARNING) << "keeping tmp2 file: " << cleanup->getBuf() << (Error*)0;
+ }
+ return 0;
+}
+
+void Files::tmpRemoveCleanup(char const* filename, FILE**p) {
+ param_assert(p!=NULLP);
+ Error::newCleanup(cleanup_remove_cond, (void*)p, filename);
+}
+
+int Files::removeIf(char const* filename) {
+ if (0==remove(filename) || errno==ENOENT) return 0;
+ return 1;
+}
+
+slen_t Files::statSize(char const* filename) {
+ struct stat st;
+ if (-1==PTS_lstat(filename, &st)) return (slen_t)-1;
+ return st.st_size;
+}
+
+/* Tue Jul 2 10:57:21 CEST 2002 */
+char const* Files::only_fext(char const*filename) {
+ char const *ret;
+ if (OS_COTY==COTY_WINNT || OS_COTY==COTY_WIN9X) {
+ if ((USGE('z'-'a',filename[0]-'a') || USGE('Z'-'A',filename[0]-'A'))
+ && filename[1]==':'
+ ) filename+=2; /* strip drive letter */
+ ret=filename;
+ while (*filename!='\0') {
+ if (*filename=='/' || *filename=='\\') ret=++filename;
+ else filename++;
+ }
+ } else { /* Everything else is treated as UNIX */
+ ret=filename;
+ while (filename[0]!='\0') if (*filename++=='/') ret=filename;
+ }
+ return ret;
+}
+
+#if HAVE_DOS_BINARY
+void Files::set_binary_mode(int fd, bool binary) {
+ /* Wed Dec 11 18:17:30 CET 2002 */
+ setmode(fd, binary ? O_BINARY : O_TEXT);
+}
+#endif
+
+int Files::system3(char const *commands) {
+ #if OS_COTY==COTY_WIN9X || OS_COTY==COTY_WINNT
+ char const *p;
+ p=commands; while (*p!='\0' && *p!='\n') p++;
+ if (*p=='\0') return system(commands); /* no newline -- simple run */
+ SimBuffer::B tmpnam;
+ FILE *f=Files::open_tmpnam(tmpnam, /*binary_p:*/false, ".bat");
+ tmpnam.term0();
+ Files::tmpRemoveCleanup(tmpnam());
+ fprintf(f, "@echo off\n%s\n", commands);
+ if (ferror(f)) Error::sev(Error::EERROR) << "system3: write to tmp .bat file: " << tmpnam << (Error*)NULLP;
+ fclose(f);
+ // printf("(%s)\n", tmpnam()); system("bash");
+ // int ret=system(("sh "+tmpnam)());
+ int ret=system(tmpnam());
+ remove(tmpnam());
+ return ret;
+ #else
+ return system(commands);
+ #endif
+}
+
+/* __END__ */