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 &in;
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