1 /* gensio.hpp -- generic char buffer and I/O facilities 2 * by pts@fazekas.hu at Tue Feb 26 13:30:02 CET 2002 3 */ 4 5 #ifdef __GNUC__ 6 #ifndef __clang__ 7 #pragma interface 8 #endif 9 #endif 10 11 #ifndef GENSIO_HPP 12 #define GENSIO_HPP 1 13 14 #include "config2.h" 15 #include <stdarg.h> /* va_list */ 16 #include "gensi.hpp" 17 #include <stdio.h> 18 19 /* Naming conventions: *Encode is a specific, well-known PostScript or PDF 20 * encoding filter documented by Adobe, *Encoder is something more general. 21 */ 22 23 /** Writing 0 bytes for vi_write is interpreted as a close() operation. 24 * Constructor must not write anything to the underlying (lower-level) 25 * Encoder. Implementors must redefine vi_write(). Encoders may be stacked 26 * atop of each other. 27 */ 28 class Encoder: public GenBuffer::Writable { public: 29 /** Default: calls vi_write. */ 30 virtual void vi_putcc(char c); 31 /** vi_write() must be called with positive slen_t for normal writing, and 32 * vi_write(?,0); must be called to signal EOF. After that, it is prohibited 33 * to call vi_write() either way. 34 */ 35 virtual void vi_write(char const*buf, slen_t len) =0; 36 /** Copies all data (till EOF) from stream `f' to (this). */ 37 static void writeFrom(GenBuffer::Writable& out, FILE *f); 38 /** Copies all data (till EOF) from stream `f' to (this). */ 39 static void writeFrom(GenBuffer::Writable& out, GenBuffer::Readable &in); 40 }; /* class Encoder */ 41 42 /** Implementors must redefine vi_read(), and they may redefine vi_getcc(). 43 * A .vi_read(?,0) has special meaning in a Decoder (but not in a normal 44 * Readable). 45 */ 46 class Decoder: public GenBuffer::Readable { public: 47 /** Calls vi_read(&ret,1). Note that this is the inverse of 48 * GenBuffer::Readable, because there vi_read() calls vi_getcc(). Decoders 49 * may be stacked atop of each other. 50 */ 51 virtual int vi_getcc(); 52 /** vi_read() must be called with positive slen_t for normal reading, and 53 * vi_read(?,0); must be called to signal that the caller would not read from 54 * the Decoder again, so the decoder is allowed to release the resources 55 * used. After that, it is prohibited to call vi_read() either way. 56 */ 57 virtual slen_t vi_read(char *to_buf, slen_t max) =0; 58 }; /* class Decoder */ 59 60 class DecoderTeller: public Decoder { public: 61 /** Equivalent of ftell() */ 62 virtual long vi_tell() const=0; 63 }; 64 65 class Filter { public: 66 /** Starts a child process (with popen(pipe_tmpl,"wb")), pipes data to it, 67 * writes output to a temporary file. After _everything_ is written, the 68 * temporary file is read and fed to out_. 69 */ 70 class PipeE: public Encoder { 71 public: 72 PipeE(GenBuffer::Writable &out_, char const*pipe_tmpl, slendiff_t i=0); 73 virtual ~PipeE(); 74 virtual void vi_write(char const*buf, slen_t len); 75 protected: 76 FILE *p; 77 SimBuffer::B tmpname; 78 GenBuffer::Writable &out; 79 SimBuffer::B tmpename, redir_cmd; 80 /** Temporary source file name `%S', forces system() instead of popen() */ 81 SimBuffer::B tmpsname; 82 /** vi_check() is called by this->vi_write(...) (and also when Broken Pipe) 83 * to check whether the encoding succeeded. vi_check() should raise an Error 84 * if it detects failure or simply return if it detects success. Default 85 * implementation: empty body which simly returns. 86 */ 87 virtual void vi_check(); 88 protected: 89 /** Copies the rest of (seekable) file `f' to `out' (subsequent filters). 90 * `f' is initially positioned at the beginning. Must call fclose(f). 91 * vi_copy() should raise Error::...s. The default implementation 92 * just copies the data bytes verbatim. `out.vi_write(0,0);' will be called 93 * by vi_write(). 94 */ 95 virtual void vi_copy(FILE *f); 96 }; 97 98 /** Gobbles all data written to it, just like /dev/null */ 99 class NullE: public Encoder { 100 public: NullE()101 inline NullE() {} ~NullE()102 inline virtual ~NullE() {} vi_putcc(char)103 inline virtual void vi_putcc(char) {} vi_write(char const *,slen_t)104 inline virtual void vi_write(char const*, slen_t) {} 105 }; 106 107 class VerbatimE: public Encoder { 108 public: VerbatimE(GenBuffer::Writable & out_)109 inline VerbatimE(GenBuffer::Writable& out_): out(&out_) {} vi_write(char const * buf,slen_t len)110 inline virtual void vi_write(char const*buf, slen_t len) { out->vi_write(buf,len); } setOut(GenBuffer::Writable & out_)111 inline void setOut(GenBuffer::Writable& out_) { out=&out_; } getOut() const112 inline GenBuffer::Writable& getOut() const { return *out; } 113 protected: 114 GenBuffer::Writable* out; 115 }; 116 117 class VerbatimCountE: public Encoder { 118 public: VerbatimCountE(GenBuffer::Writable & out_)119 inline VerbatimCountE(GenBuffer::Writable& out_): out(&out_), count(0) {} vi_write(char const * buf,slen_t len)120 inline virtual void vi_write(char const*buf, slen_t len) { out->vi_write(buf,len); count+=len; } setOut(GenBuffer::Writable & out_)121 inline void setOut(GenBuffer::Writable& out_) { out=&out_; } getOut() const122 inline GenBuffer::Writable& getOut() const { return *out; } getCount() const123 inline slen_t getCount() const { return count; } 124 protected: 125 GenBuffer::Writable* out; 126 /** Number of bytes already written */ 127 slen_t count; 128 }; 129 130 class FILEE: public Encoder { 131 public: FILEE(FILE * f_,bool closep_)132 inline FILEE(FILE *f_,bool closep_): f(f_), closep(closep_) {} 133 FILEE(char const* filename); vi_putcc(char c)134 inline virtual void vi_putcc(char c) { MACRO_PUTC(c,f); } 135 virtual void vi_write(char const*buf, slen_t len); 136 void close(); 137 protected: 138 FILE *f; 139 bool closep; 140 }; 141 142 class PipeD: public Decoder { public: 143 PipeD(GenBuffer::Readable &in_, char const*pipe_tmpl, slendiff_t i=0); 144 virtual ~PipeD(); 145 /** Equivalent to vi_read(&ret,1). */ 146 virtual int vi_getcc(); 147 /** Upon the first non-zero call, opens `p', calls vi_precopy() and 148 * closes `in'. Upon all non-zero calls, 149 * reads the temporary file with fread(). Upon vi_read(?,0), removes the 150 * the temporary files. 151 */ 152 virtual slen_t vi_read(char *to_buf, slen_t max); 153 protected: 154 /** 0: never-read (initial state), 1: vi_precopy() already called, 2: EOF reached, `in' closed */ 155 int state; 156 /** opened with popen(?,"w") in state:0->1, then with fopen(?,"rb") in state==1 */ 157 FILE *p; 158 /** Temporary destination file name `%D' */ 159 SimBuffer::B tmpname; 160 GenBuffer::Readable ∈ 161 SimBuffer::B tmpename, redir_cmd; 162 /** Temporary source file name `%S', forces system() instead of popen() */ 163 SimBuffer::B tmpsname; 164 165 /** Copies the whole `in' to writable pipe `p'. `p' will be closed by the 166 * caller; in.read(0,0) will be called by the caller. 167 * vi_precopy() should raise Error::...s. The default implementation 168 * just copies the data bytes verbatim. 169 */ 170 virtual void vi_precopy(); 171 /** vi_check() is called by this->vi_write(...) (and also when Broken Pipe) 172 * to check whether the encoding succeeded. vi_check() should raise an Error 173 * if it detects failure or simply return if it detects success. Default 174 * implementation: empty body which simly returns. 175 */ 176 virtual void vi_check(); 177 private: 178 void do_close(); 179 }; 180 181 class VerbatimD: public Decoder { 182 public: VerbatimD(GenBuffer::Readable & in_)183 inline VerbatimD(GenBuffer::Readable& in_): in(in_) {} 184 /** Works fine even if len==0. */ vi_read(char * buf,slen_t len)185 inline virtual slen_t vi_read(char *buf, slen_t len) { return in.vi_read(buf,len); } 186 protected: 187 GenBuffer::Readable& in; 188 }; 189 190 class FILED: public DecoderTeller { 191 public: FILED(FILE * f_,bool closep_)192 inline FILED(FILE *f_,bool closep_): f(f_), closep(closep_) {} 193 FILED(char const* filename); ~FILED()194 inline virtual ~FILED() { close(); } vi_getcc()195 inline virtual int vi_getcc() { return MACRO_GETC(f); } 196 virtual slen_t vi_read(char *buf, slen_t len); 197 void close(); vi_tell() const198 inline virtual long vi_tell() const { return ftell(f); } 199 protected: 200 FILE *f; 201 bool closep; 202 }; 203 204 /** 205 * Sat Apr 19 12:05:49 CEST 2003 206 * Always opens the file in binary mode. 207 * First reads from the unget buffer, then reads from a FILE*. A typical 208 * usage is: 209 * { Filter::UngetFILED f("in.txt"); // fopen("in.txt","rb"), true); 210 * f.getUnget() << "Prepend this in front of first line.\n"; 211 * int c; 212 * while ((c=f.vi_getcc()!=-1)) putchar(c); 213 * } 214 */ 215 class UngetFILED: public DecoderTeller { 216 public: getUnget()217 GenBuffer::Writable& getUnget() { return unget; } BEGIN_STATIC_ENUM(unsigned char,closeMode_t)218 BEGIN_STATIC_ENUM(unsigned char, closeMode_t) 219 CM_closep=1, 220 CM_unlinkp=2, 221 CM_keep_stdinp=4, /* CM_unlinkp, but keep STDIN open */ 222 CM_seekablep=8, /* it is sure that this->f is seekable */ 223 CM_MAX=4 224 END_STATIC_ENUM() 225 inline UngetFILED(FILE *f_, closeMode_t closep_): f(f_), closeMode(closep_), ftell_at(0), ofs(0) {} 226 UngetFILED(char const* filename_, FILE* stdin_f=(FILE*)NULLP, closeMode_t closeMode_=CM_closep); ~UngetFILED()227 inline virtual ~UngetFILED() { close(); } 228 virtual int vi_getcc(); 229 virtual slen_t vi_read(char *buf, slen_t len); 230 /** Appends a line to buf, including delimiter unless EOF. 231 * @param delimiter: a char or negative to read to EOF 232 */ 233 void appendLine(GenBuffer::Writable &buf, int delimiter='\n'); 234 void close(); 235 // void checkFILE(); vi_tell() const236 inline virtual long vi_tell() const { return ftell_at; } // return ftell_add+ofs+(f!=NULLP ? ftell(f) : 0); } 237 /** 238 * @return equivalent to getc(f) if this->f is not seekable, but returns 239 * -2 if this->f is seekable. 240 */ 241 int getc_seekable(); 242 /** 243 * Actively tests whether FILE* is seekable. Doesn't work for regular files 244 * of 0 bytes. 245 */ 246 bool isSeekable(); 247 /** Reading (this) and reading the returned FILE* will be equivalent 248 * (unless this->getUnget() is used later). 249 * Creates a temporary file if this->unget is not empty. 250 * @param seekable_p if true, the returned FILE* must be seekable. Possibly 251 * creates a temporary file. 252 */ 253 FILE* getFILE(bool seekable_p); 254 /** Implies a getFILE() */ 255 void seek(long abs_ofs); 256 /** Tries to do an fseek(f, -slen, SEEK_CUR). On failure, appends to unget. 257 * The user should only unread() data obtained from vi_read(). 258 */ 259 void unread(char const *s, slen_t slen); getFilename() const260 inline char const* getFilename() const { return filename; } getFilenameDefault(char const * def) const261 inline char const* getFilenameDefault(char const *def) const { return filename==NULLP ? def : filename; } hadError() const262 inline bool hadError() const { return f!=NULLP && ferror(f); } 263 protected: 264 FILE *f; 265 unsigned char closeMode; 266 slen_t ftell_at; 267 slen_t ofs; 268 SimBuffer::B unget; 269 char const* filename; 270 }; 271 272 /** Reads from a memory of a GenBuffer via first_sub and next_sub. The 273 * GenBuffer should not be changed during the read. The GenBuffer isn't 274 * delete()d by (this) 275 */ 276 class BufR: public GenBuffer::Readable { 277 public: 278 BufR(GenBuffer const& buf_); 279 virtual int vi_getcc(); 280 virtual slen_t vi_read(char *to_buf, slen_t max); 281 virtual void vi_rewind(); 282 protected: 283 GenBuffer const* bufp; 284 GenBuffer::Sub sub; 285 }; 286 287 /** Reads from a consecutive memory area, which won't be 288 * delete()d by (this) 289 */ 290 class FlatD: public DecoderTeller /*GenBuffer::Readable*/ { 291 public: 292 FlatD(char const* s_, slen_t slen_); 293 FlatD(char const* s_); 294 virtual int vi_getcc(); 295 virtual slen_t vi_read(char *to_buf, slen_t max); 296 virtual void vi_rewind(); getcc()297 inline int getcc() { return slen!=0 ? (slen--, *(unsigned char const*)s++) : -1; } vi_tell() const298 inline virtual long vi_tell() const { return s-sbeg; } tell() const299 inline long tell() const { return s-sbeg; } 300 protected: 301 char const *s, *sbeg; 302 slen_t slen; 303 }; 304 }; /* class Filter */ 305 306 class Files { 307 public: 308 /** Formerly `class WritableFILE' */ 309 class FILEW: public GenBuffer::Writable { 310 public: FILEW(FILE * f_)311 inline FILEW(FILE *f_): f(f_) {} vi_putcc(char c)312 inline virtual void vi_putcc(char c) { MACRO_PUTC(c,f); } vi_write(char const * buf,slen_t len)313 inline virtual void vi_write(char const*buf, slen_t len) { fwrite(buf, 1, len, f); } 314 virtual GenBuffer::Writable& vformat(slen_t n, char const *fmt, va_list ap); 315 /** appends; uses SimBuffer::B as temp */ 316 virtual GenBuffer::Writable& vformat(char const *fmt, va_list ap); close()317 inline void close() { fclose(f); } setF(FILE * f_)318 inline void setF(FILE *f_) { f=f_; } 319 private: 320 FILE *f; 321 }; 322 323 /** Formerly `class ReadableFILE'. Doesn't close the its FILE* automatically. */ 324 class FILER: public GenBuffer::Readable { 325 public: FILER(FILE * f_)326 inline FILER(FILE *f_): f(f_) {} vi_getcc()327 inline virtual int vi_getcc() { return MACRO_GETC(f); } vi_read(char * to_buf,slen_t max)328 inline virtual slen_t vi_read(char *to_buf, slen_t max) { return fread(to_buf, 1, max, f); } close()329 inline void close() { fclose(f); } vi_rewind()330 inline virtual void vi_rewind() { rewind(f); } 331 private: 332 FILE *f; 333 }; 334 335 /** @return the beginning of the last substring in param filename that does 336 * not contain '/' (dir separator) 337 */ 338 static char const* only_fext(char const*filename); 339 340 /** true iff temporary files should be removed at program finish end */ 341 static bool tmpRemove; 342 /** @param fname must start with '/' (dir separator) 343 * @return true if file successfully created 344 */ 345 static FILE *try_dir(SimBuffer::B &dir, SimBuffer::B const&fname, char const*s1, char const*s2, char const*open_mode="wb"); 346 /* The file will be opened for writing only. It won't be registered for 347 * for automatic removal. 348 * @param dir `dir' is empty: appends a unique filename for a temporary 349 * file. Otherwise: returns a unique filename in the specified directory. 350 * Creates the new file with 0 size. 351 * @param extension NULLP or a string specifying the extension of the file 352 * to create (should beginn with ".") 353 * @param open_mode "w", "wb" or "wb+" 354 * @return FILE* opened for writing for success, NULLP on failure 355 * --return true on success, false on failure 356 */ 357 static FILE *open_tmpnam(SimBuffer::B &dir, char const*open_mode="wb", char const*extension=(char const*)NULLP); 358 // static FILE *open_tmpnam(SimBuffer::B &dir, bool binary_p=true, char const*extension=(char const*)NULLP); 359 static bool find_tmpnam(SimBuffer::B &dir); 360 /** Calls lstat(). 361 * @return (slen_t)-1 on error, the size otherwise 362 */ 363 static slen_t statSize(char const* filename); 364 /** Ensures that file will be removed (if possible...) when the process 365 * terminates. Makes a copy of the filename array, it won't use filename 366 * after returning. 367 */ 368 static void tmpRemoveCleanup(char const* filename); 369 /** Ensures that file will be removed (if possible...) when the process 370 * terminates. Copies the string filename. The file will be removed iff 371 * (*p!=NULLP) when the cleanup handler runs. The cleanup handler fclose()s 372 * the file before removing it. 373 */ 374 static void tmpRemoveCleanup(char const* filename, FILE**p); 375 /** Removes the file/entity if exists. 376 * @return 0 on success (file hadn't existed, a directory component 377 * hadn't existed, or the file has been successfully removed), 378 * 1 otherwise 379 */ 380 static int removeIf(char const *filename); 381 382 /** Sat Sep 7 20:58:54 CEST 2002 */ 383 static void doSignalCleanup(); 384 385 /** Set the specified file descriptor to binary mode. Useful for stdin (0), 386 * stdout (1), stderr (2) on Win32 and DOS systems. On these systems, this 387 * call has the effect of `fopen(..., "rb");' On UNIX, file 388 * descriptors are always binary. 389 */ 390 #if HAVE_DOS_BINARY 391 static void set_binary_mode(int fd, bool binary); 392 #else set_binary_mode(int,bool)393 static inline void set_binary_mode(int,bool) {} 394 #endif 395 396 /** Like the system(3) call, but it is able to run a string containing 397 * multiple lines of commands. On Win32, it creates a batch file if 398 * necessary. 399 */ 400 static int system3(char const *commands); 401 }; 402 403 #endif 404