1 /*
2  *
3  *  Copyright (C) 2006-2020, OFFIS e.V.
4  *  All rights reserved.  See COPYRIGHT file for details.
5  *
6  *  This software and supporting documentation were developed by
7  *
8  *    OFFIS e.V.
9  *    R&D Division Health
10  *    Escherweg 2
11  *    D-26121 Oldenburg, Germany
12  *
13  *
14  *  Module:  ofstd
15  *
16  *  Author:  Marco Eichelberg, Joerg Riesmeier
17  *
18  *  Purpose: C++ wrapper class for stdio FILE functions and
19  *           wide character filenames
20  *
21  */
22 
23 #ifndef OFFILE_H
24 #define OFFILE_H
25 
26 #include "dcmtk/config/osconfig.h"
27 
28 #include "dcmtk/ofstd/oftypes.h"    /* for class OFBool */
29 #include "dcmtk/ofstd/ofstring.h"   /* for class OFString */
30 #include "dcmtk/ofstd/ofstd.h"      /* for class OFStandard */
31 
32 #define INCLUDE_UNISTD
33 #define INCLUDE_CSTDIO
34 #define INCLUDE_CSTRING
35 #define INCLUDE_CSTDARG
36 #define INCLUDE_CERRNO
37 //#define INCLUDE_CWCHAR    /* not yet implemented in "ofstdinc.h" */
38 #include "dcmtk/ofstd/ofstdinc.h"
39 
40 BEGIN_EXTERN_C
41 #ifdef HAVE_SYS_STAT_H
42 #include <sys/stat.h>       /* needed for struct _stati64 on Win32 */
43 #endif
44 END_EXTERN_C
45 
46 #ifdef HAVE_UNIX_H
47 #include <unix.h>           /* needed for setlinebuf() on QNX */
48 #endif
49 
50 /* HP-UX has clearerr both as macro and as a function definition. We have to
51  * undef the macro so that we can define a function called "clearerr".
52  */
53 #if defined(__hpux) && defined(clearerr)
54 #undef clearerr
55 #endif
56 
57 /* When using the ISO C++ include files such as <cstdio>, <cstdarg> etc.,
58  * all ANSI C functions like fopen() are declared in namespace std,
59  * (e.g. we have to use std::fopen()), but non-ANSI Posix functions remain
60  * in global namespace, e.g. we have to use ::fopen64().
61  * To make things even more difficult, not all compilers really declare
62  * ANSI C functions in namespace std in accordance with the C++ standard.
63  * Yes, this is ugly.
64  */
65 
66 /* Find out whether to use explicit LFS function calls to handle
67  * large file support
68  */
69 #if defined(DCMTK_ENABLE_LFS) && DCMTK_ENABLE_LFS == DCMTK_LFS64
70 #define EXPLICIT_LFS_64
71 
72 // Use POSIX 64 bit file position type when available
73 #ifdef HAVE_FPOS64_T
74 typedef fpos64_t offile_fpos_t;
75 #else // Otherwise this should be sufficient
76 typedef fpos_t offile_fpos_t;
77 #endif
78 
79 // Use POSIX 64 bit file offset type when available
80 #ifdef HAVE_OFF64_T
81 typedef off64_t offile_off_t;
82 #elif !defined(OF_NO_SINT64) // Otherwise use a 64 bit integer
83 typedef Sint64 offile_off_t;
84 #else // Cry when 64 LFS is required but no 64 bit integer exists
85 #error \
86   Could not find a suitable offset-type for LFS64 support.
87 #endif
88 
89 #else // Implicit LFS or no LFS
90 
91 #if defined(DCMTK_ENABLE_LFS) && DCMTK_ENABLE_LFS == DCMTK_LFS
92 #if defined(SIZEOF_FPOS_T) && (!defined(SIZEOF_OFF_T) || SIZEOF_FPOS_T > SIZEOF_OFF_T)
93 // strange Windows LFS where sizeof(fpos_t) == 8 but sizeof(off_t) == 4
94 #ifndef OF_NO_SINT64 // Use a 64 bit integer
95 typedef Sint64 offile_off_t;
96 #else // Cry when LFS is required but no 64 bit integer exists
97 #error \
98   Could not find a suitable offset-type for LFS support.
99 #endif
100 #else
101 typedef off_t offile_off_t;
102 #endif
103 #elif defined(HAVE_FSEEKO)
104 typedef off_t offile_off_t;
105 #else
106 typedef long offile_off_t;
107 #endif
108 typedef fpos_t offile_fpos_t;
109 
110 #endif // basic type definitions
111 
112 // the type we use to store the last error.
113 typedef int offile_errno_t;
114 
115 
116 // forward declarations
117 class OFpath;
118 
119 /** class for managing filenames consisting either of conventional (8-bit) or
120  *  wide (e.g.\ 16-bit) characters.  The wide character support is currently
121  *  Windows-specific because most other operating systems use UTF-8, which is
122  *  compatible with conventional 8-bit character strings.
123  */
124 class DCMTK_OFSTD_EXPORT OFFilename
125 {
126 public:
127   /** default constructor
128    */
129   OFFilename();
130 
131   /** constructor expecting a conventional character string
132    *  @param filename filename to be stored (8-bit characters, e.g. UTF-8)
133    *  @param convert  convert given filename to wide character encoding as an
134    *    alternative representation.  Only works on Windows systems.
135    */
136   OFFilename(const char *filename,
137              const OFBool convert = OFFalse);
138 
139   /** constructor expecting a character string as an OFString instance
140    *  @param filename filename to be stored (8-bit characters, e.g. UTF-8)
141    *  @param convert  convert given filename to wide character encoding as an
142    *    alternative representation.  Only works on Windows systems.
143    */
144   OFFilename(const OFString &filename,
145              const OFBool convert = OFFalse);
146 
147   /** constructor expecting an OFpath instance
148    *  @param path    OFpath instance storing a filename in native format
149    *    (currently, identical to an 8-bit character string)
150    *  @param convert convert given filename to wide character encoding as an
151    *    alternative representation.  Only works on Windows systems.
152    */
153   OFFilename(const OFpath &path,
154              const OFBool convert = OFFalse);
155 
156 #if (defined(WIDE_CHAR_FILE_IO_FUNCTIONS) || defined(WIDE_CHAR_MAIN_FUNCTION)) && defined(_WIN32)
157   /** constructor expecting a wide character string
158    *  @remark This constructor is only available if DCMTK is compiled on Windows
159    *    Operating Systems with wide chars enabled (defining _WIN32 as well as
160    *    WIDE_CHAR_FILE_IO_FUNCTIONS or WIDE_CHAR_MAIN_FUNCTION).
161    *  @param filename filename to be stored (e.g. 16-bit characters)
162    *  @param convert  convert given filename to UTF-8 encoding as an
163    *    alternative representation.  Only works on Windows systems.
164    */
165   OFFilename(const wchar_t *filename,
166              const OFBool convert = OFTrue);
167 #endif
168 
169   /** copy constructor
170    *  @param arg filename object to be copied
171    */
172   OFFilename(const OFFilename &arg);
173 
174   /** destructor. Frees memory.
175    */
176   ~OFFilename();
177 
178   /** assignment operator
179    *  @param arg filename object to be copied
180    *  @return reference to this filename object
181    */
182   OFFilename &operator=(const OFFilename &arg);
183 
184   /** clear currently stored filename
185    */
186   void clear();
187 
188   /** fast, non-throwing swap function. The time complexity of this function
189    *  is constant.
190    *  @param arg filename object to swap with
191    */
192   void swap(OFFilename &arg);
193 
194   /** checks whether this object stores an empty filename
195    *  @return OFTrue if the filename is empty, OFFalse otherwise
196    */
197   OFBool isEmpty() const;
198 
199   /** checks whether this object stores a wide character filename
200    *  @return OFTrue if the filename uses wide characters, OFFalse otherwise
201    */
usesWideChars()202   inline OFBool usesWideChars() const
203   {
204 #if (defined(WIDE_CHAR_FILE_IO_FUNCTIONS) || defined(WIDE_CHAR_MAIN_FUNCTION)) && defined(_WIN32)
205     return (wfilename_ != NULL);
206 #else
207     return OFFalse;
208 #endif
209   }
210 
211   /** get stored filename consisting of conventional characters
212    *  @return filename (might be NULL if none is stored)
213    */
getCharPointer()214   inline const char *getCharPointer() const
215   {
216     return filename_;
217   }
218 
219 #if (defined(WIDE_CHAR_FILE_IO_FUNCTIONS) || defined(WIDE_CHAR_MAIN_FUNCTION)) && defined(_WIN32)
220   /** get stored filename consisting of wide characters
221    *  @remark This method is only available if DCMTK is compiled on Windows
222    *    Operating Systems with wide chars enabled (defining _WIN32 as well as
223    *    WIDE_CHAR_FILE_IO_FUNCTIONS or WIDE_CHAR_MAIN_FUNCTION).
224    *  @return wide char filename (might be NULL if none is stored)
225    */
getWideCharPointer()226   inline const wchar_t *getWideCharPointer() const
227   {
228     return wfilename_;
229   }
230 #endif
231 
232   /** replace currently stored filename by given value
233    *  @param filename filename to be stored (8-bit characters, e.g. UTF-8)
234    *  @param convert  convert given filename to wide character encoding as an
235    *    alternative representation.  Only works on Windows systems.
236    */
237   void set(const char *filename,
238            const OFBool convert = OFFalse);
239 
240   /** replace currently stored filename by given value
241    *  @param filename filename to be stored (8-bit characters, e.g. UTF-8)
242    *  @param convert  convert given filename to wide character encoding as an
243    *    alternative representation).  Only works on Windows systems.
244    */
245   void set(const OFString &filename,
246            const OFBool convert = OFFalse);
247 
248   /** replace currently stored filename by given value
249    *  @param OFpath  OFpath instance storing a filename in native format
250    *    (currently, identical to an 8-bit character string)
251    *  @param convert convert given filename to wide character encoding as an
252    *    alternative representation).  Only works on Windows systems.
253    */
254   void set(const OFpath &path,
255            const OFBool convert = OFFalse);
256 
257 #if (defined(WIDE_CHAR_FILE_IO_FUNCTIONS) || defined(WIDE_CHAR_MAIN_FUNCTION)) && defined(_WIN32)
258   /** replace currently stored filename by given value
259    *  @remark This method is only available if DCMTK is compiled on Windows
260    *    Operating Systems with wide chars enabled (defining _WIN32 as well as
261    *    WIDE_CHAR_FILE_IO_FUNCTIONS or WIDE_CHAR_MAIN_FUNCTION).
262    *  @param filename filename to be stored (e.g. 16-bit characters)
263    *  @param convert  convert given filename to UTF-8 encoding as an alternative
264    *    representation.  Only works on Windows systems.
265    */
266   void set(const wchar_t *filename,
267            const OFBool convert = OFTrue);
268 #endif
269 
270 private:
271   /// filename consisting of conventional characters (8-bit, e.g.\ UTF-8)
272   char *filename_;
273 #if (defined(WIDE_CHAR_FILE_IO_FUNCTIONS) || defined(WIDE_CHAR_MAIN_FUNCTION)) && defined(_WIN32)
274   /// filename consisting of wide characters (e.g. 16-bit on Windows)
275   /// @remark This member is only available if DCMTK is compiled on Windows
276   ///   Operating Systems with wide chars enabled (defining _WIN32 as well as
277   ///   WIDE_CHAR_FILE_IO_FUNCTIONS or WIDE_CHAR_MAIN_FUNCTION).
278   wchar_t *wfilename_;
279 #endif
280 };
281 
282 /** swap function for OFFilename class. The time complexity of this function
283  *  is constant.
284  *  @param lhs left-hand side filename
285  *  @param rhs right-hand side filename
286  */
swap(OFFilename & lhs,OFFilename & rhs)287 inline void swap(OFFilename &lhs, OFFilename &rhs)
288 {
289   lhs.swap(rhs);
290 }
291 
292 /** output filename to the given stream.
293  *  Only the string of conventional characters (e.g. ASCII or UTF-8) is printed since
294  *  we do not expect the output stream (console or logger) to support wide characters.
295  *  @param stream output stream
296  *  @param filename OFFilename object to print
297  *  @return reference to the output stream
298  */
299 DCMTK_OFSTD_EXPORT STD_NAMESPACE ostream &operator<<(STD_NAMESPACE ostream &stream, const OFFilename &filename);
300 
301 
302 /** this class provides a simple C++ encapsulation layer for stdio FILE pointers.
303  *  All stdio functions on files are directly mapped into member functions.
304  *  The handling of large files (64 bit file systems) is transparent. Instead
305  *  of type off_t, fseek() and ftell() use offile_off_t which is a 64 bit type
306  *  if available on the underlying platform. Similarly, getpos() and setpos() use
307  *  type offile_fpos_t, which is defined appropriately.
308  *  This class provides both fclose() and pclose(), but these are equivalent -
309  *  the code always closes pipes with pclose() and files with fclose().
310  *  Finally, an abstraction for errno is provided. Error codes should always
311  *  be retrieves using methods getLastError() and getLastErrorString() which
312  *  on Unix platforms are based on errno and strerror/strerror_r, but may be based
313  *  on other mechanisms on platforms where errno does not exist.
314  */
315 class OFFile
316 {
317 public:
318   /// default constructor, creates an object that is not associated with any file.
OFFile()319   OFFile(): file_(NULL), popened_(OFFalse), lasterror_(0) {}
320 
321   /** create object for given stdio FILE
322    *  @param f stdio FILE
323    */
OFFile(FILE * f)324   OFFile(FILE *f): file_(f), popened_(OFFalse), lasterror_(0) {}
325 
326   /// destructor. Closes file if still open.
~OFFile()327   ~OFFile()
328   {
329     if (file_) fclose();
330   }
331 
332   /** opens the file whose name is the string pointed to by path and associates
333    *  a stream with it.
334    *  @param filename path to file
335    *  @param modes "r", "w" or "a" with possible modifiers "+", "b"
336    *  @return true if stream was successfully created, false otherwise, in which
337    *    case the error code is set.
338    */
fopen(const char * filename,const char * modes)339   OFBool fopen(const char *filename, const char *modes)
340   {
341     if (file_) fclose();
342 #ifdef EXPLICIT_LFS_64
343     file_ = :: fopen64(filename, modes);
344 #else
345     file_ = STDIO_NAMESPACE fopen(filename, modes);
346 #endif
347     if (file_) popened_ = OFFalse; else storeLastError();
348     return (file_ != NULL);
349   }
350 
351 #if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
352   /** opens the file whose name is the wide character string pointed to by path and
353    *  associates a stream with it.
354    *  @remark This member is only available if DCMTK is compiled on Windows
355    *    Operating Systems with wide chars enabled (defining _WIN32 as well as
356    *    WIDE_CHAR_FILE_IO_FUNCTIONS).
357    *  @param filename Unicode filename path to file
358    *  @param modes "r", "w" or "a" with possible modifiers "+", "b", as a wide
359    *    character string
360    *  @return true if stream was successfully created, false otherwise, in which case
361    *    the error code is set.
362    */
wfopen(const wchar_t * filename,const wchar_t * modes)363   OFBool wfopen(const wchar_t *filename, const wchar_t *modes)
364   {
365     if (file_) fclose();
366     file_ = _wfopen(filename, modes);
367     if (file_) popened_ = OFFalse; else storeLastError();
368     return (file_ != NULL);
369   }
370 #endif
371 
372   /** opens the file whose name is a conventional or wide character string pointed to
373    *  by path and associates. The wide character support is currently Windows-specific.
374    *  @param filename object containing the filename path to file
375    *  @param modes "r", "w" or "a" with possible modifiers "+", "b"
376    *  @return true if stream was successfully created, false otherwise, in which case
377    *    the error code is set.
378    */
fopen(const OFFilename & filename,const char * modes)379   OFBool fopen(const OFFilename &filename, const char *modes)
380   {
381     OFBool result = OFFalse;
382 #if defined(WIDE_CHAR_FILE_IO_FUNCTIONS) && defined(_WIN32)
383     if (filename.usesWideChars())
384     {
385       // convert file mode to wide char string
386       const size_t length = strlen(modes) + 1;
387       wchar_t *wmodes = new wchar_t[length];
388       if (wmodes != NULL)
389       {
390         for (size_t i = 0; i < length; ++i)
391         {
392           // conversion of ASCII codes (7-bit) is easy
393           wmodes[i] = OFstatic_cast(wchar_t, modes[i]);
394         }
395         result = wfopen(filename.getWideCharPointer(), wmodes);
396       }
397       delete[] wmodes;
398     } else
399 #endif
400       result = fopen(filename.getCharPointer(), modes);
401     return result;
402   }
403 
404   /** associates a stream with the existing file descriptor 'fd'. The mode of
405    *  the stream (one of the values "r", "r+", "w", "w+", "a", "a+") must be
406    *  compatible with the mode of the file descriptor. The file position
407    *  indicator of the new stream is set to that belonging to 'fd', and the
408    *  error and end-of-file indicators are cleared. Modes "w" or "w+" do not
409    *  cause truncation of the file. The file descriptor is not dup'ed, and
410    *  will be closed when the stream created by fdopen is closed. The result
411    *  of applying fdopen to a shared memory object is undefined.
412    *  @param fd file descriptor
413    *  @param modes "r", "w" or "a" with possible modifiers "+", "b"
414    *  @return true if stream was successfully created, false otherwise, in
415    *    which case the error code is set.
416    */
fdopen(int fd,const char * modes)417   OFBool fdopen(int fd, const char *modes)
418   {
419     if (file_) fclose();
420     file_ = :: fdopen(fd, modes);
421     if (file_) popened_ = OFFalse; else storeLastError();
422     return (file_ != NULL);
423   }
424 
425   /** opens a process by creating a pipe, forking, and invoking the shell.
426    *  Since a pipe is by definition unidirectional, the type argument may
427    *  specify only reading or writing, not both; the resulting stream is
428    *  correspondingly read-only or write-only. If the object was already
429    *  associated with another file or pipe, that one is closed.
430    *  @param command shell command line
431    *  @param modes "r" or "w"
432    *  @return true if pipe was successfully created, false otherwise
433    */
popen(const char * command,const char * modes)434   OFBool popen(const char *command, const char *modes)
435   {
436     if (file_) fclose();
437 #ifdef HAVE_POPEN
438     file_ = :: popen(command, modes);
439 #else
440     file_ = _popen(command, modes);
441 #endif
442     if (file_) popened_ = OFTrue; else storeLastError();
443     return (file_ != NULL);
444   }
445 
446   /** opens the file whose name is the string pointed to by path and associates
447    *  the stream pointed maintained by this object with it. The original stream
448    *  (if it exists) is closed. The mode argument is used just as in the fopen
449    *  function. The primary use of the freopen function is to change the file
450    *  associated with a standard text stream (stderr, stdin, or stdout).
451    *  @param filename path to file
452    *  @param modes "r", "w" or "a" with possible modifiers "+", "b"
453    *  @return true if stream was successfully created, false otherwise, in
454    *    which case the error code is set.
455    */
freopen(const char * filename,const char * modes)456   OFBool freopen(const char *filename, const char *modes)
457   {
458 #if defined(EXPLICIT_LFS_64) && ! defined(__MINGW32__)
459     // MinGW has EXPLICIT_LFS_64 but no freopen64()
460     file_ = :: freopen64(filename, modes, file_);
461 #else
462     file_ = STDIO_NAMESPACE freopen(filename, modes, file_);
463 #endif
464     if (file_) popened_ = OFFalse; else storeLastError();
465     return (file_ != NULL);
466   }
467 
468   /** generates a unique temporary filename. The temporary file is then opened
469    *  in binary read/write (w+b) mode. The file will be automatically deleted
470    *  when it is closed or the program terminates normally.
471    *  @return true if stream was successfully created, false otherwise, in
472    *    which case the error code is set.
473    */
tmpfile()474   OFBool tmpfile()
475   {
476     if (file_) fclose();
477 #if defined(EXPLICIT_LFS_64) && ! defined(__MINGW32__)
478     // MinGW has EXPLICIT_LFS_64 but no tmpfile64()
479     file_ = :: tmpfile64();
480 #else
481     file_ = STDIO_NAMESPACE tmpfile();
482 #endif
483     if (file_) popened_ = OFFalse; else storeLastError();
484     return (file_ != NULL);
485   }
486 
487   /** dissociates the named stream from its underlying file or set of functions.
488    *  If the stream was being used for output, any buffered data is written
489    *  first, using fflush. Independent of the return value of this method,
490    *  any further access (including another call to fclose()) to the stream
491    *  maintained by this object results in undefined behaviour.
492    *  @return 0 upon success, EOF otherwise, in which case the error code is set.
493    */
fclose()494   int fclose()
495   {
496     int result = 0;
497     if (file_)
498     {
499       if (popened_)
500       {
501 #ifdef HAVE_PCLOSE
502         result = :: pclose(file_);
503 #else
504         result = _pclose(file_);
505 #endif
506       }
507       else
508       {
509         result = STDIO_NAMESPACE fclose(file_);
510       }
511       // After calling fclose() once, the FILE* is gone even if fclose() failed.
512       file_ = NULL;
513     }
514     if (result) storeLastError();
515     return result;
516   }
517 
518   /** waits for the associated process (created with popen) to terminate and
519    *  returns the exit status of the command as returned by wait4.
520    *  In this implementation, fclose and pclose can be used synonymously.
521    *  @return process ID of the child which exited, or -1 on error, in which
522    *    case the error code is set
523    */
pclose()524   int pclose() { return fclose(); }
525 
526   /** writes n elements of data, each size bytes long, to the stream, obtaining
527    *  them from the location given by ptr. Returns the number of items successfully written
528    *  (i.e., not the number of characters).  If an error occurs the return value is a short
529    *  item count (or zero).
530    *  @param ptr pointer to buffer
531    *  @param size size of item
532    *  @param n number of items
533    *  @return number of items written
534    */
fwrite(const void * ptr,size_t size,size_t n)535   size_t fwrite(const void *ptr, size_t size, size_t n)
536   {
537     return STDIO_NAMESPACE fwrite(ptr, size, n, file_);
538   }
539 
540   /** reads n elements of data, each size bytes long, from the stream, storing
541    *  them at the location given by ptr. Returns the number of items successfully
542    *  read (i.e., not the number of characters).  If an error occurs, or the
543    *  end-of-file is reached, the return value is a short item count (or zero).
544    *  fread does not distinguish between end-of-file and error, and callers must
545    *  use feof and ferror to determine which occurred.
546    *  @param ptr pointer to buffer
547    *  @param size size of item
548    *  @param n number of items
549    *  @return number of items read
550    */
fread(void * ptr,size_t size,size_t n)551   size_t fread(void *ptr, size_t size, size_t n)
552   {
553     return STDIO_NAMESPACE fread(ptr, size, n, file_);
554   }
555 
556   /** forces a write of all user-space buffered data for the given output or
557    *  update stream via the stream's underlying write function. The open status
558    *  of the stream is unaffected.
559    *  @return 0 upon success, EOF otherwise, in which case the error code is set.
560    */
fflush()561   int fflush()
562   {
563     int result = STDIO_NAMESPACE fflush(file_);
564     if (result) storeLastError();
565     return result;
566   }
567 
568   /** reads the next character from stream and returns it as an unsigned char
569    *  cast to an int, or EOF on end of file or error.
570    *  @return next character from stream or EOF
571    */
fgetc()572   int fgetc() { return STDIO_NAMESPACE fgetc(file_); }
573 
574   /** The three types of buffering available are unbuffered, block buffered, and
575    *  line buffered. When an output stream is unbuffered, information appears on
576    *  the destination file or terminal as soon as written; when it is block
577    *  buffered many characters are saved up and written as a block; when it is
578    *  line buffered characters are saved up until a newline is output or input
579    *  is read from any stream attached to a terminal device (typically stdin).
580    *  Normally all files are block buffered. if a stream refers to a  terminal
581    *  (as stdout normally does) it is line buffered. The standard error  stream
582    *  stderr is always unbuffered by default. this function allows to set the
583    *  mode of the stream to line buffered.
584    *  @return 0 upon success, nonzero otherwise, in which case the error code may be set
585    *
586    */
setlinebuf()587   void setlinebuf()
588   {
589 #if defined(_WIN32) || defined(__hpux)
590     this->setvbuf(NULL, _IOLBF, 0);
591 #else
592     :: setlinebuf(file_);
593 #endif
594   }
595 
596   /** sets the file position indicator for the stream pointed to by stream to
597    *  the beginning of the file. This is equivalent to fseek(0, SEEK_SET)
598    *  except that the error indicator for the stream is also cleared.
599    */
rewind()600   void rewind()
601   {
602 #if defined(_WIN32) || defined(__CYGWIN__)
603     /* On these platforms rewind() fails after reading to the end of file
604      * if the file is read-only. Using fseek() instead.
605      */
606     (void) this->fseek(0L, SEEK_SET);
607 #else
608     STDIO_NAMESPACE rewind(file_);
609 #endif
610   }
611 
612   /** clears the end-of-file and error indicators for the stream
613    */
clearerr()614   void clearerr() { STDIO_NAMESPACE clearerr(file_); }
615 
616   /** tests the end-of-file indicator for the stream, returning non-zero if it
617    *  is set. The end-of-file indicator can only be cleared by the function
618    *  clearerr. This method is called eof, not feof, because feof() is a macro
619    *  on some systems and, therefore, cannot be used as a method name.
620    *  @return non-zero if EOF, zero otherwise
621    */
eof()622   int eof() const
623   {
624 #ifdef feof
625     // feof is a macro on some systems. Macros never have namespaces.
626     return feof(file_);
627 #else
628     return STDIO_NAMESPACE feof(file_);
629 #endif
630   }
631 
632   /** tests the error indicator for the stream, returning non-zero if it is set.
633    *  This method is named error, not ferror, because ferror() is a macro
634    *  on some systems and, therefore, cannot be used as a method name.
635    *  The error indicator can only be reset by the clearerr function.
636    *  @return non-zero if error flag is set, zero otherwise
637    */
error()638   int error()
639   {
640 #ifdef ferror
641     // ferror is a macro on some systems. Macros never have namespaces.
642     return ferror(file_);
643 #else
644     return STDIO_NAMESPACE ferror(file_);
645 #endif
646   }
647 
648   /** returns the low-level file descriptor associated with the stream.
649    *  The spelling of this member function is different from stdio fileno()
650    *  because on some systems (such as MinGW) fileno() is a macro
651    *  and, therefore, cannot be used as a method name.
652    *  @return low-level file descriptor associated with stream
653    */
654 #ifdef fileno
fileNo()655   int fileNo() { return fileno(file_); }
656 #else
fileNo()657   int fileNo() { return :: fileno(file_); }
658 #endif
659 
660   /** The three types of buffering available are unbuffered, block buffered, and
661    *  line buffered. When an output stream is unbuffered, information appears on
662    *  the destination file or terminal as soon as written; when it is block
663    *  buffered many characters are saved up and written as a block; when it is
664    *  line buffered characters are saved up until a newline is output or input
665    *  is read from any stream attached to a terminal device (typically stdin).
666    *  Normally all files are block buffered. if a stream refers to a  terminal
667    *  (as stdout normally does) it is line buffered. The standard error  stream
668    *  stderr is always unbuffered by default. This function allows to set the
669    *  mode of the stream to unbuffered (if buf is NULL) or block buffered.
670    *  @param buf pointer to buffer of size BUFSIZ as declared in cstdio, or NULL
671    *  @return 0 upon success, nonzero otherwise, in which case the error code may be set
672    */
setbuf(char * buf)673   void setbuf(char *buf) { STDIO_NAMESPACE setbuf(file_, buf); }
674 
675   /** The three types of buffering available are unbuffered, block buffered, and
676    *  line buffered. When an output stream is unbuffered, information appears on
677    *  the destination file or terminal as soon as written; when it is block
678    *  buffered many characters are saved up and written as a block; when it is
679    *  line buffered characters are saved up until a newline is output or input
680    *  is read from any stream attached to a terminal device (typically stdin).
681    *  Normally all files are block buffered. if a stream refers to a  terminal
682    *  (as stdout normally does) it is line buffered. The standard error  stream
683    *  stderr is always unbuffered by default. This function allows to set the
684    *  stream mode.
685    *  @param buf pointer to buffer, may be NULL
686    *  @param modes _IONBF (unbuffered) _IOLBF (line buffered) or _IOFBF (fully buffered)
687    *  @param n size of buffer, in bytes
688    *  @return 0 upon success, nonzero otherwise, in which case the error code may be set
689    */
setvbuf(char * buf,int modes,size_t n)690   int setvbuf(char * buf, int modes, size_t n)
691   {
692     int result = STDIO_NAMESPACE setvbuf(file_, buf, modes, n);
693     if (result) storeLastError();
694     return result;
695   }
696 
697   /** The three types of buffering available are unbuffered, block buffered, and
698    *  line buffered. When an output stream is unbuffered, information appears on
699    *  the destination file or terminal as soon as written; when it is block
700    *  buffered many characters are saved up and written as a block; when it is
701    *  line buffered characters are saved up until a newline is output or input
702    *  is read from any stream attached to a terminal device (typically stdin).
703    *  Normally all files are block buffered. if a stream refers to a  terminal
704    *  (as stdout normally does) it is line buffered. The standard error  stream
705    *  stderr is always unbuffered by default. This function allows to set the
706    *  mode of the stream to unbuffered (if buf is NULL) or block buffered.
707    *  @param buf pointer to buffer
708    *  @param size size of buffer, in bytes
709    *  @return 0 upon success, nonzero otherwise, in which case the error code may be set
710    */
setbuffer(char * buf,size_t size)711   void setbuffer(char *buf, size_t size)
712   {
713 #if defined(_WIN32) || defined(__hpux)
714     this->setvbuf(NULL, buf ? _IOFBF : _IONBF, size);
715 #else
716     :: setbuffer(file_, buf, size);
717 #endif
718   }
719 
720   /** writes the character c, cast to an unsigned char, to stream.
721    *  @param c character
722    *  @return the character written as an unsigned char cast to an int or EOF on error
723    */
fputc(int c)724   int fputc(int c) { return STDIO_NAMESPACE fputc(c, file_); }
725 
726   /** reads in at most one less than n characters from stream and stores them
727    *  into the buffer pointed to by s. Reading stops after an EOF or a newline.
728    *  If a newline is read, it is stored into the buffer. A '@\0' is stored after
729    *  the last character in the buffer.
730    *  @param s pointer to buffer of size n
731    *  @param n buffer size
732    *  @return pointer to string
733    */
fgets(char * s,int n)734   char *fgets(char *s, int n) { return STDIO_NAMESPACE fgets(s, n, file_); }
735 
736   /** writes the string s to stream, without its trailing '@\0'.
737    *  @param s string to be written
738    *  @return a non-negative number on success, or EOF on error.
739    */
fputs(const char * s)740   int fputs(const char *s) { return STDIO_NAMESPACE fputs(s, file_); }
741 
742   /** pushes c back to stream, cast to unsigned char, where it is available for
743    *  subsequent read operations. Pushed - back characters will be returned in
744    *  reverse order; only one pushback is guaranteed.
745    *  @param c character to push back
746    *  @return c on success, or EOF on error.
747    */
ungetc(int c)748   int ungetc(int c) { return STDIO_NAMESPACE ungetc(c, file_); }
749 
750   /** sets the file position indicator for the stream pointed to by stream. The
751    *  new position, measured in bytes, is obtained by adding offset bytes to the
752    *  position specified by whence. If whence is set to SEEK_SET, SEEK_CUR, or
753    *  SEEK_END, the offset is relative to the start of the file, the current
754    *  position indicator, or end-of-file, respectively. A successful call to the
755    *  fseek function clears the end-of- file indicator for the stream and undoes
756    *  any effects of the ungetc function on the same stream.
757    *  @param off offset to seek to
758    *  @param whence SEEK_SET, SEEK_CUR, or SEEK_END
759    *  @return 0 upon success, -1 otherwise in which case the error code is set.
760    */
fseek(offile_off_t off,int whence)761   int fseek(offile_off_t off, int whence)
762   {
763     int result;
764 #ifdef _WIN32
765     // Windows does not have a 64-bit fseek.
766     // We emulate fseek through fsetpos, which does exist on Windows.
767     // fpos_t is (hopefully always) defined as __int64 on this platform
768     offile_fpos_t off2 = off;
769     fpos_t pos;
770     struct _stati64 buf;
771     switch (whence)
772     {
773       case SEEK_END:
774         // flush write buffer, if any, so that the file size is correct
775         STDIO_NAMESPACE fflush(file_);
776 #if 0
777         // Python implementation based on _lseeki64(). May be unsafe because
778         // there is no guarantee that fflush also empties read buffers.
779         STDIO_NAMESPACE fflush(file_);
780 #ifdef fileno
781         if (_lseeki64(   fileno(file_), 0, 2) == -1)
782 #else
783         if (_lseeki64(:: fileno(file_), 0, 2) == -1)
784 #endif
785         {
786           storeLastError();
787           return -1;
788         }
789         // fall through
790 #else
791         // determine file size (using underlying file descriptor). This should be safe.
792 #ifdef fileno
793         if (_fstati64(   fileno(file_), &buf) == -1)
794 #else
795         if (_fstati64(:: fileno(file_), &buf) == -1)
796 #endif
797         {
798           storeLastError();
799           return -1;
800         }
801 
802         // fsetpos position is offset + file size.
803         off2 += buf.st_size;
804         break;
805 #endif
806       case SEEK_CUR:
807         if (STDIO_NAMESPACE fgetpos(file_, &pos) != 0)
808         {
809           storeLastError();
810           return -1;
811         }
812 
813         off2 += pos;
814         break;
815       case SEEK_SET:
816         /* do nothing */
817         break;
818     }
819     result = this->fsetpos(&off2);
820 #elif defined(__BEOS__)
821     result = :: _fseek(file_, off, whence);
822 #else
823 #ifdef HAVE_FSEEKO
824 #ifdef EXPLICIT_LFS_64
825     result = :: fseeko64(file_, off, whence);
826 #else
827     result = :: fseeko(file_, off, whence);
828 #endif
829 #else
830     result = STDIO_NAMESPACE fseek(file_, off, whence);
831 #endif
832 #endif
833     if (result) storeLastError();
834     return result;
835   }
836 
837   /** obtains the current value of the file position indicator for the stream pointed to by the stream.
838    *  @return current file position
839    */
ftell()840   offile_off_t ftell()
841   {
842     offile_off_t result;
843 #ifdef _WIN32
844     // Windows does not have a 64-bit ftell, and _telli64 cannot be used
845     // because it operates on file descriptors and ignores FILE buffers.
846     // We emulate ftell through fgetpos, which does exist on Windows.
847     // fpos_t is (hopefully always) defined as __int64 on this platform.
848     offile_fpos_t pos;
849     if (this->fgetpos(&pos) != 0)
850     {
851       storeLastError();
852       return -1;
853     }
854     return pos;
855 #else
856 #ifdef HAVE_FSEEKO
857 #ifdef EXPLICIT_LFS_64
858     result = :: ftello64(file_);
859 #else
860     result = :: ftello(file_);
861 #endif
862 #else
863     result = STDIO_NAMESPACE ftell(file_);
864 #endif
865 #endif
866     if (result < 0) storeLastError();
867     return result;
868   }
869 
870   /** alternate interface equivalent to ftell, storing the current value of the
871    *  file offset into the object referenced by pos. On some non-UNIX systems an
872    *  fpos_t object may be a complex object and these routines may be the only
873    *  way to portably reposition a text stream.
874    *  @param pos pointer to offile_fpos_t structure
875    *  @return 0 upon success, -1 otherwise in which case the error code is set.
876    */
fgetpos(offile_fpos_t * pos)877   int fgetpos(offile_fpos_t *pos)
878   {
879     int result;
880 #if defined(EXPLICIT_LFS_64) && ! defined(__MINGW32__) && ! defined(__QNX__)
881     // MinGW and QNX have EXPLICIT_LFS_64 but no fgetpos64()
882     result = :: fgetpos64(file_, pos);
883 #else
884     result = STDIO_NAMESPACE fgetpos(file_, pos);
885 #endif
886     if (result) storeLastError();
887     return result;
888   }
889 
890   /** alternate interface equivalent to fseek (with whence set to SEEK_SET),
891    *  setting the current value of the file offset from the object referenced by
892    *  pos. On some non-UNIX systems an fpos_t object may be a complex object and
893    *  these routines may be the only way to portably reposition a text stream.
894    *  @param pos pointer to offile_fpos_t structure
895    *  @return 0 upon success, -1 otherwise in which case the error code is set.
896    */
fsetpos(offile_fpos_t * pos)897   int fsetpos(offile_fpos_t *pos)
898   {
899     int result;
900 #if defined(EXPLICIT_LFS_64) && ! defined(__MINGW32__) && ! defined(__QNX__)
901     // MinGW and QNX have EXPLICIT_LFS_64 but no fsetpos64()
902     result = :: fsetpos64(file_, pos);
903 #else
904     result = STDIO_NAMESPACE fsetpos(file_, pos);
905 #endif
906     if (result) storeLastError();
907     return result;
908   }
909 
910   /** print formatted string into stream, see printf(3)
911    *  @param format format string
912    *  @param ... further parameters according to format string
913    *  @return number of characters printed
914    */
fprintf(const char * format,...)915   int fprintf(const char *format, ...)
916   {
917     int result = 0;
918     va_list ap;
919     va_start(ap, format);
920     result = STDIO_NAMESPACE vfprintf(file_, format, ap);
921     va_end(ap);
922     return result;
923   }
924 
925   /** print formatted string into stream, see printf(3)
926    *  @param format format string
927    *  @param arg list of further parameters according to format string
928    *  @return number of characters printed
929    */
vfprintf(const char * format,va_list arg)930    int vfprintf(const char *format, va_list arg)
931    {
932      return STDIO_NAMESPACE vfprintf(file_, format, arg);
933    }
934 
935   // we cannot emulate fscanf because we would need vfscanf for this
936   // purpose, which does not exist, e.g. on Win32.
937 
938   /** return FILE pointer managed by this object. This allows the user
939    *  to call some stdio functions that are not encapsulated in this class
940    *  (but possibly should be).
941    *  @return pointer to FILE structure managed by this object
942    */
file()943   FILE *file() { return file_; }
944 
945   /** return true if this object is currently associated with a stream, false otherwise
946    *  @return true if this object is currently associated with a stream, false otherwise
947    */
open()948   OFBool open() const { return file_ != NULL; }
949 
950   /** return last error code for this stream
951    *  @return last error code for this stream
952    */
getLastError()953   offile_errno_t getLastError() const { return lasterror_; }
954 
955   /** return string describing last error code for this stream
956    *  @param s string describing last error code for this stream returned in this parameter
957    */
getLastErrorString(OFString & s)958   void getLastErrorString(OFString& s) const
959   {
960     char buf[1000];
961     s = OFStandard::strerror(lasterror_, buf, 1000);
962   }
963 
964 // wide character functions (disabled by default, since currently not used within DCMTK)
965 #ifdef WIDE_CHAR_FILE_IO_FUNCTIONS
966 
967   /** When mode is zero, the fwide function determines the current orientation
968    *  of stream. It returns a value > 0 if stream is wide-character oriented,
969    *  i.e.  if wide character I/O is permitted but char I/O is disallowed. It
970    *  returns a  value < 0 if stream is byte oriented, i.e. if char I/O is
971    *  permitted but wide  character I/O is disallowed. It returns zero if stream
972    *  has no orientation yet;  in this case the next I/O operation might change
973    *  the orientation (to byte  oriented if it is a char I/O operation, or to
974    *  wide-character oriented if it  is a wide character I/O operation).
975    *  Once a stream has an orientation, it cannot be changed and persists until
976    *  the stream is closed.
977    *  When mode is non-zero, the fwide function first attempts to set stream's
978    *  orientation (to wide-character oriented if mode > 0, or to byte oriented
979    *  if mode < 0). It then returns a value denoting the current orientation, as
980    *  above.
981    *  @param mode mode of operation for fwide
982    *  @return orientation of stream
983    */
fwide(int mode)984   int fwide(int mode)
985   {
986     return STDIO_NAMESPACE fwide(file_, mode);
987   }
988 
989   /** reads a wide character from stream and returns it. If the end of stream is
990    *  reached, or if ferror(stream) becomes true, it returns WEOF. If a wide
991    *  character conversion error occurs, it sets the error code to EILSEQ and returns
992    *  WEOF.
993    *  @return next character from stream or WEOF
994    */
fgetwc()995   wint_t fgetwc()
996   {
997     wint_t result = STDIO_NAMESPACE fgetwc(file_);
998     if (result == WEOF) storeLastError();
999     return result;
1000   }
1001 
1002   /** writes the wide character wc to stream. If ferror(stream) becomes true, it returns WEOF.
1003    *  If a wide character conversion error occurs, it sets the error code to EILSEQ and returns WEOF.
1004    *  Otherwise it returns wc.
1005    *  @param wc wide character to write to stream
1006    *  @return character written or WEOF
1007    */
fputwc(wchar_t wc)1008   wint_t fputwc(wchar_t wc)
1009   {
1010     wint_t result = STDIO_NAMESPACE fputwc(wc, file_);
1011     if (result == WEOF) storeLastError();
1012     return result;
1013   }
1014 
1015   /** pushes back a wide character onto stream and returns it. If wc is WEOF, it
1016    *  returns WEOF. If wc is an invalid wide character, it sets errno to EILSEQ
1017    *  and returns WEOF. If wc is a valid wide character, it is pushed back  onto
1018    *  the stream and thus becomes available for future wide character read
1019    *  operations. The file-position indicator is decremented by one or more.
1020    *  The end-of-file indicator is cleared. The backing storage of the file is
1021    *  not affected. Note: wc need not be the last wide character read from the
1022    *  stream; it can be any other valid wide character. If the implementation
1023    *  supports multiple push-back operations in a row, the pushed-back wide
1024    *  characters will be read in reverse order; however, only one level of
1025    *  push-back is guaranteed.
1026    *  @param wc wide character to put back to stream
1027    *  @return character put back or WEOF
1028    */
ungetwc(wint_t wc)1029   wint_t ungetwc(wint_t wc)
1030   {
1031     wint_t result = STDIO_NAMESPACE ungetwc(wc, file_);
1032     if (result == WEOF) storeLastError();
1033     return result;
1034   }
1035 
1036   /** print formatted wide string into stream, see wprintf(3)
1037    *  @param format format string
1038    *  @param ... further parameters according to format string
1039    *  @return number of characters printed
1040    */
fwprintf(const wchar_t * format,...)1041   int fwprintf(const wchar_t *format, ...)
1042   {
1043     int result = 0;
1044     va_list ap;
1045     va_start(ap, format);
1046     result = STDIO_NAMESPACE vfwprintf(file_, format, ap);
1047     va_end(ap);
1048     return result;
1049   }
1050 
1051   /** print formatted wide string into stream, see printf(3)
1052    *  @param format format string
1053    *  @param arg list of further parameters according to format string
1054    *  @return number of characters printed
1055    */
vfwprintf(const wchar_t * format,va_list arg)1056    int vfwprintf(const wchar_t *format, va_list arg)
1057    {
1058      return STDIO_NAMESPACE vfwprintf(file_, format, arg);
1059    }
1060 
1061   // we cannot emulate fwscanf because we would need vfwscanf for this
1062   // purpose, which does not exist, e.g. on Win32.
1063 
1064 #endif /* WIDE_CHAR_FILE_IO_FUNCTIONS */
1065 
1066 private:
1067 
1068   // private undefined copy constructor
1069   OFFile(const OFFile &arg);
1070 
1071   // private undefined assignment operator
1072   OFFile &operator=(const OFFile &arg);
1073 
1074   /// the file maintained by this object
1075   FILE *file_;
1076 
1077   /// a flag indicating whether or not this object was created with popen().
1078   OFBool popened_;
1079 
1080   /// the last error code for operations of this stream
1081   offile_errno_t lasterror_;
1082 
1083   /// store last error code. For now we simply store the content of errno.
storeLastError()1084   inline void storeLastError()
1085   {
1086     lasterror_ = errno;
1087   }
1088 
1089 };
1090 
1091 #endif
1092