builds on Darwin
[asdcplib.git] / FileIO.cpp
1 /*
2 Copyright (c) 2003-2005, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 /*! \file    FileIO.cpp
28     \version $Id$
29     \brief   Cross-platform, simple file accessors
30 */
31
32
33 #include <FileIO.h>
34 #include <fcntl.h>
35 #include <assert.h>
36
37 #ifdef WIN32
38 typedef struct _stati64 fstat_t;
39
40 // AFAIK, there is no iovec equivalent in the win32 API
41 struct iovec {
42   char* iov_base; // stupid iovec uses char*
43   int   iov_len;
44 };
45 #else
46 #include <sys/uio.h>
47 typedef struct stat     fstat_t;
48 #endif
49
50 //
51 //
52 static ASDCP::Result_t
53 do_stat(const char* path, fstat_t* stat_info)
54 {
55   ASDCP_TEST_NULL_STR(path);
56   ASDCP_TEST_NULL(stat_info);
57
58   ASDCP::Result_t result = ASDCP::RESULT_OK;
59
60 #ifdef WIN32
61   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
62
63   if ( _stati64(path, stat_info) == (__int64)-1 )
64     result = ASDCP::RESULT_FILEOPEN;
65
66   ::SetErrorMode( prev );
67 #else
68   if ( stat(path, stat_info) == -1L )
69     result = ASDCP::RESULT_FILEOPEN;
70
71   if ( stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR) == 0 )
72     result = ASDCP::RESULT_FILEOPEN;
73 #endif
74
75   return result;
76 }
77
78
79
80 //
81 bool
82 ASDCP::PathIsFile(const char* pathname)
83 {
84   assert(pathname);
85   fstat_t info;
86
87   if ( ASDCP_SUCCESS(do_stat(pathname, &info)) )
88     {
89       if ( info.st_mode & S_IFREG )
90         return true;
91     }
92
93   return false;
94 }
95
96
97 //
98 bool
99 ASDCP::PathIsDirectory(const char* pathname)
100 {
101   assert(pathname);
102   fstat_t info;
103
104   if ( ASDCP_SUCCESS(do_stat(pathname, &info)) )
105     {
106       if ( info.st_mode & S_IFDIR )
107         return true;
108     }
109
110   return false;
111 }
112
113
114 //
115 ASDCP::fsize_t
116 ASDCP::FileSize(const char* pathname)
117 {
118   assert(pathname);
119   fstat_t info;
120
121   if ( ASDCP_SUCCESS(do_stat(pathname, &info)) )
122     {
123       if ( info.st_mode & S_IFREG )
124         return(info.st_size);
125     }
126
127   return 0;
128 }
129
130 //------------------------------------------------------------------------------------------
131 // portable aspects of the file classes
132
133 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
134
135 //
136 class ASDCP::FileWriter::h__iovec
137 {
138 public:
139   int            m_Count;
140   struct iovec   m_iovec[IOVecMaxEntries];
141   h__iovec() : m_Count(0) {}
142 };
143
144
145
146 //
147 ASDCP::fsize_t
148 ASDCP::FileReader::Size() const
149 {
150   return ASDCP::FileSize(m_Filename.c_str());
151 }
152
153 // these are declared here instead of in the header file
154 // because we have a mem_ptr that is managing a hidden class
155 ASDCP::FileWriter::FileWriter() {}
156 ASDCP::FileWriter::~FileWriter() {}
157
158 //
159 ASDCP::Result_t
160 ASDCP::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
161 {
162   assert( ! m_IOVec.empty() );
163   register h__iovec* iov = m_IOVec;
164   ASDCP_TEST_NULL(buf);
165
166   if ( iov->m_Count >= IOVecMaxEntries )
167     {
168       DefaultLogSink().Error("The iovec is full! Only %lu entries allowed before a flush.\n",
169                              IOVecMaxEntries);
170       return RESULT_FAIL;
171     }
172
173   iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
174   iov->m_iovec[iov->m_Count].iov_len = buf_len;
175   iov->m_Count++;
176
177   return RESULT_OK;
178 }
179
180
181 #ifdef WIN32
182 //------------------------------------------------------------------------------------------
183 //
184
185 ASDCP::Result_t
186 ASDCP::FileReader::OpenRead(const char* filename) const
187 {
188   ASDCP_TEST_NULL_STR(filename);
189   const_cast<FileReader*>(this)->m_Filename = filename;
190   
191   // suppress popup window on error
192   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
193
194   const_cast<FileReader*>(this)->m_Handle = ::CreateFile(filename,
195                           (GENERIC_READ),                // open for reading
196                           FILE_SHARE_READ,               // share for reading
197                           NULL,                          // no security
198                           OPEN_EXISTING,                 // read
199                           FILE_ATTRIBUTE_NORMAL,         // normal file
200                           NULL                           // no template file
201                           );
202
203   ::SetErrorMode(prev);
204
205   return ( m_Handle == INVALID_HANDLE_VALUE ) ?
206     ASDCP::RESULT_FILEOPEN : ASDCP::RESULT_OK;
207 }
208
209 //
210 ASDCP::Result_t
211 ASDCP::FileReader::Close() const
212 {
213   if ( m_Handle == INVALID_HANDLE_VALUE )
214     return ASDCP::RESULT_FILEOPEN;
215
216   // suppress popup window on error
217   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
218   BOOL result = ::CloseHandle(m_Handle);
219   ::SetErrorMode(prev);
220   const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
221
222   return ( result == 0 ) ? ASDCP::RESULT_FAIL : ASDCP::RESULT_OK;
223 }
224
225 //
226 ASDCP::Result_t
227 ASDCP::FileReader::Seek(ASDCP::fpos_t position, SeekPos_t whence) const
228 {
229   if ( m_Handle == INVALID_HANDLE_VALUE )
230     return ASDCP::RESULT_STATE;
231
232   LARGE_INTEGER in;
233   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
234   in.QuadPart = position;
235   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
236   HRESULT LastError = GetLastError();
237   ::SetErrorMode(prev);
238
239   if ( (LastError != NO_ERROR
240         && (in.LowPart == INVALID_SET_FILE_POINTER
241             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
242     return ASDCP::RESULT_READFAIL;
243   
244   return ASDCP::RESULT_OK;
245 }
246
247 //
248 ASDCP::Result_t
249 ASDCP::FileReader::Tell(ASDCP::fpos_t* pos) const
250 {
251   ASDCP_TEST_NULL(pos);
252
253   if ( m_Handle == (HANDLE)-1L )
254     return ASDCP::RESULT_FILEOPEN;
255
256   LARGE_INTEGER in;
257   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
258   in.QuadPart = (__int64)0;
259   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
260   HRESULT LastError = GetLastError();
261   ::SetErrorMode(prev);
262
263   if ( (LastError != NO_ERROR
264         && (in.LowPart == INVALID_SET_FILE_POINTER
265             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
266     return ASDCP::RESULT_READFAIL;
267
268   *pos = (ASDCP::fpos_t)in.QuadPart;
269   return ASDCP::RESULT_OK;
270 }
271
272 //
273 ASDCP::Result_t
274 ASDCP::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
275 {
276   ASDCP_TEST_NULL(buf);
277   Result_t result = ASDCP::RESULT_OK;
278   DWORD    tmp_count;
279   ui32_t tmp_int;
280
281   if ( read_count == 0 )
282     read_count = &tmp_int;
283
284   *read_count = 0;
285
286   if ( m_Handle == INVALID_HANDLE_VALUE )
287     return ASDCP::RESULT_FILEOPEN;
288   
289   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
290   if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
291     result = ASDCP::RESULT_READFAIL;
292
293   ::SetErrorMode(prev);
294
295   if ( tmp_count == 0 ) /* EOF */
296     result = ASDCP::RESULT_ENDOFFILE;
297
298   if ( ASDCP_SUCCESS(result) )
299     *read_count = tmp_count;
300
301   return result;
302 }
303
304
305
306 //------------------------------------------------------------------------------------------
307 //
308
309 //
310 ASDCP::Result_t
311 ASDCP::FileWriter::OpenWrite(const char* filename)
312 {
313   ASDCP_TEST_NULL_STR(filename);
314   m_Filename = filename;
315   
316   // suppress popup window on error
317   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
318
319   m_Handle = ::CreateFile(filename,
320                           (GENERIC_WRITE|GENERIC_READ),  // open for reading
321                           FILE_SHARE_READ,               // share for reading
322                           NULL,                          // no security
323                           CREATE_ALWAYS,                 // overwrite (beware!)
324                           FILE_ATTRIBUTE_NORMAL,         // normal file
325                           NULL                           // no template file
326                           );
327
328   ::SetErrorMode(prev);
329
330   if ( m_Handle == INVALID_HANDLE_VALUE )
331     return ASDCP::RESULT_FILEOPEN;
332   
333   m_IOVec = new h__iovec;
334   return ASDCP::RESULT_OK;
335 }
336
337 //
338 ASDCP::Result_t
339 ASDCP::FileWriter::Writev(ui32_t* bytes_written)
340 {
341   assert( ! m_IOVec.empty() );
342   register h__iovec* iov = m_IOVec;
343   ui32_t tmp_int;
344
345   if ( bytes_written == 0 )
346     bytes_written = &tmp_int;
347
348   if ( m_Handle == INVALID_HANDLE_VALUE )
349     return ASDCP::RESULT_STATE;
350
351   *bytes_written = 0;
352   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
353   Result_t result = ASDCP::RESULT_OK;
354
355   // AFAIK, there is no writev() equivalent in the win32 API
356   for ( register int i = 0; i < iov->m_Count; i++ )
357     {
358       ui32_t tmp_count = 0;
359       BOOL wr_result = ::WriteFile(m_Handle,
360                                    iov->m_iovec[i].iov_base,
361                                    iov->m_iovec[i].iov_len,
362                                    (DWORD*)&tmp_count,
363                                    NULL);
364
365       if ( wr_result == 0 )
366         {
367           result = ASDCP::RESULT_WRITEFAIL;
368           break;
369         }
370
371       assert(iov->m_iovec[i].iov_len == tmp_count);
372       *bytes_written += tmp_count;
373     }
374
375   ::SetErrorMode(prev);
376   iov->m_Count = 0; // error nor not, all is lost
377
378   return result;
379 }
380
381 //
382 ASDCP::Result_t
383 ASDCP::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
384 {
385   ASDCP_TEST_NULL(buf);
386   ui32_t tmp_int;
387
388   if ( bytes_written == 0 )
389     bytes_written = &tmp_int;
390
391   if ( m_Handle == INVALID_HANDLE_VALUE )
392     return ASDCP::RESULT_STATE;
393
394   // suppress popup window on error
395   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
396   BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
397   ::SetErrorMode(prev);
398
399   return ( result == 0 ) ? ASDCP::RESULT_WRITEFAIL : ASDCP::RESULT_OK;
400 }
401
402 #else // WIN32
403 //------------------------------------------------------------------------------------------
404 // POSIX
405
406 //
407 ASDCP::Result_t
408 ASDCP::FileReader::OpenRead(const char* filename) const
409 {
410   ASDCP_TEST_NULL_STR(filename);
411   const_cast<FileReader*>(this)->m_Filename = filename;
412   const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
413   return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
414 }
415
416 //
417 ASDCP::Result_t
418 ASDCP::FileReader::Close() const
419 {
420   if ( m_Handle == -1L )
421     return RESULT_FILEOPEN;
422
423   close(m_Handle);
424   const_cast<FileReader*>(this)->m_Handle = -1L;
425   return RESULT_OK;
426 }
427
428 //
429 ASDCP::Result_t
430 ASDCP::FileReader::Seek(ASDCP::fpos_t position, SeekPos_t whence) const
431 {
432   if ( m_Handle == -1L )
433     return RESULT_FILEOPEN;
434
435   if ( lseek(m_Handle, position, whence) == -1L )
436     return RESULT_BADSEEK;
437
438   return RESULT_OK;
439 }
440
441 //
442 ASDCP::Result_t
443 ASDCP::FileReader::Tell(ASDCP::fpos_t* pos) const
444 {
445   ASDCP_TEST_NULL(pos);
446
447   if ( m_Handle == -1L )
448     return RESULT_FILEOPEN;
449
450   ASDCP::fpos_t tmp_pos;
451
452   if (  (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
453     return RESULT_READFAIL;
454
455   *pos = tmp_pos;
456   return RESULT_OK;
457 }
458
459 //
460 ASDCP::Result_t
461 ASDCP::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
462 {
463   ASDCP_TEST_NULL(buf);
464   i32_t  tmp_count = 0;
465   ui32_t tmp_int = 0;
466
467   if ( read_count == 0 )
468     read_count = &tmp_int;
469
470   *read_count = 0;
471
472   if ( m_Handle == -1L )
473     return RESULT_FILEOPEN;
474
475   if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
476     return RESULT_READFAIL;
477
478   *read_count = tmp_count;
479   return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
480 }
481
482
483 //------------------------------------------------------------------------------------------
484 //
485
486 //
487 ASDCP::Result_t
488 ASDCP::FileWriter::OpenWrite(const char* filename)
489 {
490   ASDCP_TEST_NULL_STR(filename);
491   m_Filename = filename;
492   m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0644);
493
494   if ( m_Handle == -1L )
495     {
496       perror(filename);
497       return RESULT_FILEOPEN;
498     }
499
500   m_IOVec = new h__iovec;
501   return RESULT_OK;
502 }
503
504 //
505 ASDCP::Result_t
506 ASDCP::FileWriter::Writev(ui32_t* bytes_written)
507 {
508   assert( ! m_IOVec.empty() );
509   register h__iovec* iov = m_IOVec;
510   ui32_t tmp_int;
511
512   if ( bytes_written == 0 )
513     bytes_written = &tmp_int;
514
515   if ( m_Handle == -1L )
516     return RESULT_STATE;
517
518   int read_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
519   
520   if ( read_size == -1L )
521     return RESULT_WRITEFAIL;
522
523   iov->m_Count = 0;
524   *bytes_written = read_size;  
525   return RESULT_OK;
526 }
527
528 //
529 ASDCP::Result_t
530 ASDCP::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
531 {
532   ASDCP_TEST_NULL(buf);
533   ui32_t tmp_int;
534
535   if ( bytes_written == 0 )
536     bytes_written = &tmp_int;
537
538   if ( m_Handle == -1L )
539     return RESULT_STATE;
540
541   int read_size = write(m_Handle, buf, buf_len);
542   
543   if ( read_size == -1L )
544     return RESULT_WRITEFAIL;
545
546   *bytes_written = read_size;  
547   return RESULT_OK;
548 }
549
550
551 #endif // WIN32
552
553 //
554 // end FileIO.cpp
555 //