Fix ADSCP warnings.
[libdcp.git] / asdcplib / src / KM_fileio.cpp
1 /*
2 Copyright (c) 2004-2011, 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    KM_fileio.cpp
28     \version $Id: KM_fileio.cpp,v 1.31 2011/03/08 19:03:47 jhurst Exp $
29     \brief   portable file i/o
30   */
31
32 #include <KM_fileio.h>
33 #include <KM_log.h>
34 #include <fcntl.h>
35
36 #include <assert.h>
37
38 #ifdef KM_WIN32
39 #include <direct.h>
40 #else
41 #define _getcwd getcwd
42 #define _unlink unlink
43 #define _rmdir rmdir
44 #endif
45
46 using namespace Kumu;
47
48 #ifdef KM_WIN32
49 typedef struct _stati64 fstat_t;
50 #define S_IFLNK 0
51
52
53 // win32 has WriteFileGather() and ReadFileScatter() but they
54 // demand page alignment and page sizing, making them unsuitable
55 // for use with arbitrary buffer sizes.
56 struct iovec {
57   char* iov_base; // stupid iovec uses char*
58   int   iov_len;
59 };
60 #else
61 # if defined(__linux__)
62 #   include <sys/statfs.h>
63 # else
64 #  include <sys/param.h>
65 #  include <sys/mount.h>
66 # endif
67
68 #include <sys/stat.h>
69 #include <sys/uio.h>
70 typedef struct stat     fstat_t;
71 #endif
72
73 //
74 static void
75 split(const std::string& str, char separator, std::list<std::string>& components)
76 {
77   const char* pstr = str.c_str();
78   const char* r = strchr(pstr, separator);
79
80   while ( r != 0 )
81     {
82       assert(r >= pstr);
83       if ( r > pstr )
84         {
85           std::string tmp_str;
86           tmp_str.assign(pstr, (r - pstr));
87           components.push_back(tmp_str);
88         }
89
90       pstr = r + 1;
91       r = strchr(pstr, separator);
92     }
93
94   if( strlen(pstr) > 0 )
95     components.push_back(std::string(pstr));
96 }
97
98
99 //
100 static Kumu::Result_t
101 do_stat(const char* path, fstat_t* stat_info)
102 {
103   KM_TEST_NULL_STR_L(path);
104   KM_TEST_NULL_L(stat_info);
105
106   Kumu::Result_t result = Kumu::RESULT_OK;
107
108 #ifdef KM_WIN32
109   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
110
111   if ( _stati64(path, stat_info) == (__int64)-1 )
112     result = Kumu::RESULT_FILEOPEN;
113
114   ::SetErrorMode( prev );
115 #else
116   if ( stat(path, stat_info) == -1L )
117     result = Kumu::RESULT_FILEOPEN;
118
119   if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
120     result = Kumu::RESULT_FILEOPEN;
121 #endif
122
123   return result;
124 }
125
126 #ifndef KM_WIN32
127
128 //
129 static Kumu::Result_t
130 do_fstat(FileHandle handle, fstat_t* stat_info)
131 {
132   KM_TEST_NULL_L(stat_info);
133
134   Kumu::Result_t result = Kumu::RESULT_OK;
135
136   if ( fstat(handle, stat_info) == -1L )
137     result = Kumu::RESULT_FILEOPEN;
138
139   if ( (stat_info->st_mode & (S_IFREG|S_IFLNK|S_IFDIR)) == 0 )
140     result = Kumu::RESULT_FILEOPEN;
141
142   return result;
143 }
144
145 #endif
146
147
148 //
149 bool
150 Kumu::PathExists(const std::string& pathname)
151 {
152   if ( pathname.empty() )
153     return false;
154
155   fstat_t info;
156
157   if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
158     return true;
159
160   return false;
161 }
162
163 //
164 bool
165 Kumu::PathIsFile(const std::string& pathname)
166 {
167   if ( pathname.empty() )
168     return false;
169
170   fstat_t info;
171
172   if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
173     {
174       if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
175         return true;
176     }
177
178   return false;
179 }
180
181
182 //
183 bool
184 Kumu::PathIsDirectory(const std::string& pathname)
185 {
186   if ( pathname.empty() )
187     return false;
188
189   fstat_t info;
190
191   if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
192     {
193       if ( info.st_mode & S_IFDIR )
194         return true;
195     }
196
197   return false;
198 }
199
200 //
201 Kumu::fsize_t
202 Kumu::FileSize(const std::string& pathname)
203 {
204   if ( pathname.empty() )
205     return 0;
206
207   fstat_t info;
208
209   if ( KM_SUCCESS(do_stat(pathname.c_str(), &info)) )
210     {
211       if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
212         return(info.st_size);
213     }
214
215   return 0;
216 }
217
218 //
219 static PathCompList_t&
220 s_PathMakeCanonical(PathCompList_t& CList, bool is_absolute)
221 {
222   PathCompList_t::iterator ci, ri; // component and removal iterators
223
224   for ( ci = CList.begin(); ci != CList.end(); ci++ )
225     {
226       if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
227         {
228           ri = ci++;
229           CList.erase(ri);
230         }
231       else if ( *ci == ".." && ci != CList.begin() )
232         {
233           ri = ci;
234           ri--;
235               
236           if ( *ri != ".." )
237             {
238               CList.erase(ri);
239               ri = ci++;
240               CList.erase(ri);
241             }
242         }
243     }
244
245   return CList;
246 }
247
248 //
249 std::string
250 Kumu::PathMakeCanonical(const std::string& Path, char separator)
251 {
252   PathCompList_t CList;
253   bool is_absolute = PathIsAbsolute(Path, separator);
254   s_PathMakeCanonical(PathToComponents(Path, CList, separator), is_absolute);
255
256   if ( is_absolute )
257     return ComponentsToAbsolutePath(CList, separator);
258
259   return ComponentsToPath(CList, separator);
260 }
261
262 //
263 bool
264 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
265 {
266   return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
267 }
268
269 //
270 Kumu::PathCompList_t&
271 Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
272 {
273   split(Path, separator, CList);
274   return CList;
275 }
276
277 //
278 std::string
279 Kumu::ComponentsToPath(const PathCompList_t& CList, char separator)
280 {
281   if ( CList.empty() )
282     return "";
283
284   PathCompList_t::const_iterator ci = CList.begin();
285   std::string out_path = *ci;
286
287   for ( ci++; ci != CList.end(); ci++ )
288     out_path += separator + *ci;
289
290   return out_path;
291 }
292
293 //
294 std::string
295 Kumu::ComponentsToAbsolutePath(const PathCompList_t& CList, char separator)
296 {
297   std::string out_path;
298
299   if ( CList.empty() )
300     out_path = separator;
301   else
302     {
303       PathCompList_t::const_iterator ci;
304
305       for ( ci = CList.begin(); ci != CList.end(); ci++ )
306         out_path += separator + *ci;
307     }
308
309   return out_path;
310 }
311
312 //
313 bool
314 Kumu::PathHasComponents(const std::string& Path, char separator)
315 {
316   if ( strchr(Path.c_str(), separator) == 0 )
317     return false;
318
319   return true;
320 }
321
322 //
323 bool
324 Kumu::PathIsAbsolute(const std::string& Path, char separator)
325 {
326   if ( Path.empty() )
327     return false;
328
329   if ( Path[0] == separator)
330     return true;
331
332   return false;
333 }
334
335 //
336 std::string
337 Kumu::PathMakeAbsolute(const std::string& Path, char separator)
338 {
339   if ( Path.empty() )
340     {
341       std::string out_path;
342       out_path = separator;
343       return out_path;
344     }
345
346   if ( PathIsAbsolute(Path, separator) )
347     return Path;
348
349   char cwd_buf [MaxFilePath];
350   if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
351     {
352       DefaultLogSink().Error("Error retrieving current working directory.");
353       return "";
354     }
355
356   PathCompList_t CList;
357   PathToComponents(cwd_buf, CList);
358   CList.push_back(Path);
359
360   return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, true), separator);
361 }
362
363 //
364 std::string
365 Kumu::PathMakeLocal(const std::string& Path, const std::string& Parent)
366 {
367   size_t pos = Path.find(Parent);
368
369   if ( pos == 0 ) // Parent found at offset 0
370     return Path.substr(Parent.size()+1);
371
372   return Path;
373 }
374
375 //
376 std::string
377 Kumu::PathBasename(const std::string& Path, char separator)
378 {
379   PathCompList_t CList;
380   PathToComponents(Path, CList, separator);
381
382   if ( CList.empty() )
383     return "";
384
385   return CList.back();
386 }
387
388 //
389 std::string
390 Kumu::PathDirname(const std::string& Path, char separator)
391 {
392   PathCompList_t CList;
393   bool is_absolute = PathIsAbsolute(Path, separator);
394   PathToComponents(Path, CList, separator);
395
396   if ( CList.empty() )
397     return is_absolute ? "/" : "";
398
399   CList.pop_back();
400
401   if ( is_absolute )
402     return ComponentsToAbsolutePath(CList, separator);
403
404   return ComponentsToPath(CList, separator);
405 }
406
407 //
408 std::string
409 Kumu::PathGetExtension(const std::string& Path)
410 {
411   std::string Basename = PathBasename(Path);
412   const char* p = strrchr(Basename.c_str(), '.'); 
413
414   if ( p++ == 0 )
415     return "";
416
417   return p;
418 }
419
420 //
421 std::string
422 Kumu::PathSetExtension(const std::string& Path, const std::string& Extension) // empty extension removes
423 {
424   std::string Basename = PathBasename(Path);
425   const char* p = strrchr(Basename.c_str(), '.'); 
426
427   if ( p != 0 )
428     Basename = Basename.substr(0, p - Basename.c_str());
429
430   if ( Extension.empty() )
431     return Basename;
432
433   return Basename + "." + Extension;
434 }
435
436 //
437 std::string
438 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, char separator)
439 {
440   return Path1 + separator + Path2;
441 }
442
443 //
444 std::string
445 Kumu::PathJoin(const std::string& Path1, const std::string& Path2, const std::string& Path3, char separator)
446 {
447   return Path1 + separator + Path2 + separator + Path3;
448 }
449
450 //
451 std::string
452 Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
453                const std::string& Path3, const std::string& Path4, char separator)
454 {
455   return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
456 }
457
458 //
459 Kumu::PathList_t&
460 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
461                   Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
462 {
463   PathList_t::const_iterator si;
464   for ( si = SearchPaths.begin(); si != SearchPaths.end(); si++ )
465     {
466       FindInPath(Pattern, *si, FoundPaths, one_shot, separator);
467
468       if ( one_shot && ! FoundPaths.empty() )
469         break;
470     }
471
472   return FoundPaths;
473 }
474
475 //
476 Kumu::PathList_t&
477 Kumu::FindInPath(const IPathMatch& Pattern, const std::string& SearchDir,
478                   Kumu::PathList_t& FoundPaths, bool one_shot, char separator)
479 {
480   char name_buf[MaxFilePath];
481   DirScanner Dir;
482
483   if ( KM_SUCCESS(Dir.Open(SearchDir.c_str())) )
484     {
485       while ( KM_SUCCESS(Dir.GetNext(name_buf)) )
486         {
487           if ( name_buf[0] == '.' ) continue; // no hidden files
488           std::string tmp_path = SearchDir + separator + name_buf;
489
490           if ( PathIsDirectory(tmp_path.c_str()) )
491             FindInPath(Pattern, tmp_path, FoundPaths, one_shot, separator);
492           
493           else if ( Pattern.Match(name_buf) )
494             {
495               FoundPaths.push_back(SearchDir + separator + name_buf);
496               if ( one_shot )
497                 break;
498             }
499         }
500     }
501
502   return FoundPaths;
503 }
504
505
506 #ifndef KM_WIN32
507
508 //
509 Kumu::PathMatchRegex::PathMatchRegex(const std::string& s)
510 {
511   int result = regcomp(&m_regex, s.c_str(), REG_NOSUB); // (REG_EXTENDED|REG_NOSUB|REG_NEWLINE));
512
513   if ( result )
514     {
515       char buf[128];
516       regerror(result, &m_regex, buf, 128);
517       DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
518       regfree(&m_regex);
519     }
520 }
521
522 Kumu::PathMatchRegex::PathMatchRegex(const PathMatchRegex& rhs) : IPathMatch() {
523   m_regex = rhs.m_regex;
524 }
525
526 Kumu::PathMatchRegex::~PathMatchRegex() {
527   regfree(&m_regex);
528 }
529
530 bool
531 Kumu::PathMatchRegex::Match(const std::string& s) const {
532   return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
533 }
534
535
536
537 //
538 Kumu::PathMatchGlob::PathMatchGlob(const std::string& glob)
539 {
540   std::string regex; // convert glob to regex
541
542   for ( const char* p = glob.c_str(); *p != 0; p++ )
543     {
544       switch (*p)
545         {
546         case '.':  regex += "\\.";  break;
547         case '*':  regex += ".*";   break;
548         case '?':  regex += ".?";   break;
549         default:   regex += *p;
550         }
551     }
552   regex += '$';
553
554   int result = regcomp(&m_regex, regex.c_str(), REG_NOSUB);
555
556   if ( result )
557     {
558       char buf[128];
559       regerror(result, &m_regex, buf, 128);
560       DefaultLogSink().Error("PathMatchRegex: %s\n", buf);
561       regfree(&m_regex);
562     }
563 }
564
565 Kumu::PathMatchGlob::PathMatchGlob(const PathMatchGlob& rhs) : IPathMatch() {
566   m_regex = rhs.m_regex;
567 }
568
569 Kumu::PathMatchGlob::~PathMatchGlob() {
570   regfree(&m_regex);
571 }
572
573 bool
574 Kumu::PathMatchGlob::Match(const std::string& s) const {
575   return ( regexec(&m_regex, s.c_str(), 0, 0, 0) == 0 );
576 }
577
578 #endif
579
580 //------------------------------------------------------------------------------------------
581 // portable aspects of the file classes
582
583 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
584
585 //
586 class Kumu::FileWriter::h__iovec
587 {
588 public:
589   int            m_Count;
590   struct iovec   m_iovec[IOVecMaxEntries];
591   h__iovec() : m_Count(0) {}
592 };
593
594
595
596 //
597 Kumu::fsize_t
598 Kumu::FileReader::Size() const
599 {
600 #ifdef KM_WIN32
601   return FileSize(m_Filename.c_str());
602 #else
603   fstat_t info;
604
605   if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
606     {
607       if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
608         return(info.st_size);
609     }
610 #endif
611
612   return 0;
613 }
614
615 // these are declared here instead of in the header file
616 // because we have a mem_ptr that is managing a hidden class
617 Kumu::FileWriter::FileWriter() {}
618 Kumu::FileWriter::~FileWriter() {}
619
620 //
621 Kumu::Result_t
622 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
623 {
624   assert( ! m_IOVec.empty() );
625   register h__iovec* iov = m_IOVec;
626   KM_TEST_NULL_L(buf);
627
628   if ( iov->m_Count >= IOVecMaxEntries )
629     {
630       DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
631                              IOVecMaxEntries);
632       return RESULT_WRITEFAIL;
633     }
634
635   iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
636   iov->m_iovec[iov->m_Count].iov_len = buf_len;
637   iov->m_Count++;
638
639   return RESULT_OK;
640 }
641
642
643 #ifdef KM_WIN32
644 //------------------------------------------------------------------------------------------
645 //
646
647 Kumu::Result_t
648 Kumu::FileReader::OpenRead(const char* filename) const
649 {
650   KM_TEST_NULL_STR_L(filename);
651   const_cast<FileReader*>(this)->m_Filename = filename;
652   
653   // suppress popup window on error
654   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
655
656   const_cast<FileReader*>(this)->m_Handle = ::CreateFileA(filename,
657                           (GENERIC_READ),                // open for reading
658                           FILE_SHARE_READ,               // share for reading
659                           NULL,                          // no security
660                           OPEN_EXISTING,                 // read
661                           FILE_ATTRIBUTE_NORMAL,         // normal file
662                           NULL                           // no template file
663                           );
664
665   ::SetErrorMode(prev);
666
667   return ( m_Handle == INVALID_HANDLE_VALUE ) ?
668     Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
669 }
670
671 //
672 Kumu::Result_t
673 Kumu::FileReader::Close() const
674 {
675   if ( m_Handle == INVALID_HANDLE_VALUE )
676     return Kumu::RESULT_FILEOPEN;
677
678   // suppress popup window on error
679   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
680   BOOL result = ::CloseHandle(m_Handle);
681   ::SetErrorMode(prev);
682   const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
683
684   return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
685 }
686
687 //
688 Kumu::Result_t
689 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
690 {
691   if ( m_Handle == INVALID_HANDLE_VALUE )
692     return Kumu::RESULT_STATE;
693
694   LARGE_INTEGER in;
695   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
696   in.QuadPart = position;
697   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
698   HRESULT LastError = GetLastError();
699   ::SetErrorMode(prev);
700
701   if ( (LastError != NO_ERROR
702         && (in.LowPart == INVALID_SET_FILE_POINTER
703             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
704     return Kumu::RESULT_READFAIL;
705   
706   return Kumu::RESULT_OK;
707 }
708
709 //
710 Kumu::Result_t
711 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
712 {
713   KM_TEST_NULL_L(pos);
714
715   if ( m_Handle == INVALID_HANDLE_VALUE )
716     return Kumu::RESULT_FILEOPEN;
717
718   LARGE_INTEGER in;
719   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
720   in.QuadPart = (__int64)0;
721   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
722   HRESULT LastError = GetLastError();
723   ::SetErrorMode(prev);
724
725   if ( (LastError != NO_ERROR
726         && (in.LowPart == INVALID_SET_FILE_POINTER
727             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
728     return Kumu::RESULT_READFAIL;
729
730   *pos = (Kumu::fpos_t)in.QuadPart;
731   return Kumu::RESULT_OK;
732 }
733
734 //
735 Kumu::Result_t
736 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
737 {
738   KM_TEST_NULL_L(buf);
739   Result_t result = Kumu::RESULT_OK;
740   DWORD    tmp_count;
741   ui32_t tmp_int;
742
743   if ( read_count == 0 )
744     read_count = &tmp_int;
745
746   *read_count = 0;
747
748   if ( m_Handle == INVALID_HANDLE_VALUE )
749     return Kumu::RESULT_FILEOPEN;
750   
751   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
752   if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
753     result = Kumu::RESULT_READFAIL;
754
755   ::SetErrorMode(prev);
756
757   if ( tmp_count == 0 ) /* EOF */
758     result = Kumu::RESULT_ENDOFFILE;
759
760   if ( KM_SUCCESS(result) )
761     *read_count = tmp_count;
762
763   return result;
764 }
765
766
767
768 //------------------------------------------------------------------------------------------
769 //
770
771 //
772 Kumu::Result_t
773 Kumu::FileWriter::OpenWrite(const char* filename)
774 {
775   KM_TEST_NULL_STR_L(filename);
776   m_Filename = filename;
777   
778   // suppress popup window on error
779   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
780
781   m_Handle = ::CreateFileA(filename,
782                           (GENERIC_WRITE|GENERIC_READ),  // open for reading
783                           FILE_SHARE_READ,               // share for reading
784                           NULL,                          // no security
785                           CREATE_ALWAYS,                 // overwrite (beware!)
786                           FILE_ATTRIBUTE_NORMAL,         // normal file
787                           NULL                           // no template file
788                           );
789
790   ::SetErrorMode(prev);
791
792   if ( m_Handle == INVALID_HANDLE_VALUE )
793     return Kumu::RESULT_FILEOPEN;
794   
795   m_IOVec = new h__iovec;
796   return Kumu::RESULT_OK;
797 }
798
799 //
800 Kumu::Result_t
801 Kumu::FileWriter::Writev(ui32_t* bytes_written)
802 {
803   assert( ! m_IOVec.empty() );
804   register h__iovec* iov = m_IOVec;
805   ui32_t tmp_int;
806
807   if ( bytes_written == 0 )
808     bytes_written = &tmp_int;
809
810   if ( m_Handle == INVALID_HANDLE_VALUE )
811     return Kumu::RESULT_STATE;
812
813   *bytes_written = 0;
814   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
815   Result_t result = Kumu::RESULT_OK;
816
817   // AFAIK, there is no writev() equivalent in the win32 API
818   for ( register int i = 0; i < iov->m_Count; i++ )
819     {
820       ui32_t tmp_count = 0;
821       BOOL wr_result = ::WriteFile(m_Handle,
822                                    iov->m_iovec[i].iov_base,
823                                    iov->m_iovec[i].iov_len,
824                                    (DWORD*)&tmp_count,
825                                    NULL);
826
827       if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
828         {
829           result = Kumu::RESULT_WRITEFAIL;
830           break;
831         }
832
833       *bytes_written += tmp_count;
834     }
835
836   ::SetErrorMode(prev);
837   iov->m_Count = 0; // error nor not, all is lost
838
839   return result;
840 }
841
842 //
843 Kumu::Result_t
844 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
845 {
846   KM_TEST_NULL_L(buf);
847   ui32_t tmp_int;
848
849   if ( bytes_written == 0 )
850     bytes_written = &tmp_int;
851
852   if ( m_Handle == INVALID_HANDLE_VALUE )
853     return Kumu::RESULT_STATE;
854
855   // suppress popup window on error
856   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
857   BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
858   ::SetErrorMode(prev);
859
860   if ( result == 0 || *bytes_written != buf_len )
861     return Kumu::RESULT_WRITEFAIL;
862
863   return Kumu::RESULT_OK;
864 }
865
866 #else // KM_WIN32
867 //------------------------------------------------------------------------------------------
868 // POSIX
869
870 //
871 Kumu::Result_t
872 Kumu::FileReader::OpenRead(const char* filename) const
873 {
874   KM_TEST_NULL_STR_L(filename);
875   const_cast<FileReader*>(this)->m_Filename = filename;
876   const_cast<FileReader*>(this)->m_Handle = open(filename, O_RDONLY, 0);
877   return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
878 }
879
880 //
881 Kumu::Result_t
882 Kumu::FileReader::Close() const
883 {
884   if ( m_Handle == -1L )
885     return RESULT_FILEOPEN;
886
887   close(m_Handle);
888   const_cast<FileReader*>(this)->m_Handle = -1L;
889   return RESULT_OK;
890 }
891
892 //
893 Kumu::Result_t
894 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
895 {
896   if ( m_Handle == -1L )
897     return RESULT_FILEOPEN;
898
899   if ( lseek(m_Handle, position, whence) == -1L )
900     return RESULT_BADSEEK;
901
902   return RESULT_OK;
903 }
904
905 //
906 Kumu::Result_t
907 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
908 {
909   KM_TEST_NULL_L(pos);
910
911   if ( m_Handle == -1L )
912     return RESULT_FILEOPEN;
913
914   Kumu::fpos_t tmp_pos;
915
916   if (  (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
917     return RESULT_READFAIL;
918
919   *pos = tmp_pos;
920   return RESULT_OK;
921 }
922
923 //
924 Kumu::Result_t
925 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
926 {
927   KM_TEST_NULL_L(buf);
928   i32_t  tmp_count = 0;
929   ui32_t tmp_int = 0;
930
931   if ( read_count == 0 )
932     read_count = &tmp_int;
933
934   *read_count = 0;
935
936   if ( m_Handle == -1L )
937     return RESULT_FILEOPEN;
938
939   if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
940     return RESULT_READFAIL;
941
942   *read_count = tmp_count;
943   return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
944 }
945
946
947 //------------------------------------------------------------------------------------------
948 //
949
950 //
951 Kumu::Result_t
952 Kumu::FileWriter::OpenWrite(const char* filename)
953 {
954   KM_TEST_NULL_STR_L(filename);
955   m_Filename = filename;
956   m_Handle = open(filename, O_RDWR|O_CREAT|O_TRUNC, 0664);
957
958   if ( m_Handle == -1L )
959     {
960       DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
961       return RESULT_FILEOPEN;
962     }
963
964   m_IOVec = new h__iovec;
965   return RESULT_OK;
966 }
967
968 //
969 Kumu::Result_t
970 Kumu::FileWriter::OpenModify(const char* filename)
971 {
972   KM_TEST_NULL_STR_L(filename);
973   m_Filename = filename;
974   m_Handle = open(filename, O_RDWR|O_CREAT, 0664);
975
976   if ( m_Handle == -1L )
977     {
978       DefaultLogSink().Error("Error opening file %s: %s\n", filename, strerror(errno));
979       return RESULT_FILEOPEN;
980     }
981
982   m_IOVec = new h__iovec;
983   return RESULT_OK;
984 }
985
986 //
987 Kumu::Result_t
988 Kumu::FileWriter::Writev(ui32_t* bytes_written)
989 {
990   assert( ! m_IOVec.empty() );
991   register h__iovec* iov = m_IOVec;
992   ui32_t tmp_int;
993
994   if ( bytes_written == 0 )
995     bytes_written = &tmp_int;
996
997   if ( m_Handle == -1L )
998     return RESULT_STATE;
999
1000   int total_size = 0;
1001   for ( int i = 0; i < iov->m_Count; i++ )
1002     total_size += iov->m_iovec[i].iov_len;
1003
1004   int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1005   
1006   if ( write_size == -1L || write_size != total_size )
1007     return RESULT_WRITEFAIL;
1008
1009   iov->m_Count = 0;
1010   *bytes_written = write_size;  
1011   return RESULT_OK;
1012 }
1013
1014 //
1015 Kumu::Result_t
1016 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1017 {
1018   KM_TEST_NULL_L(buf);
1019   ui32_t tmp_int;
1020
1021   if ( bytes_written == 0 )
1022     bytes_written = &tmp_int;
1023
1024   if ( m_Handle == -1L )
1025     return RESULT_STATE;
1026
1027   int write_size = write(m_Handle, buf, buf_len);
1028
1029   if ( write_size == -1L || (ui32_t)write_size != buf_len )
1030     return RESULT_WRITEFAIL;
1031
1032   *bytes_written = write_size;
1033   return RESULT_OK;
1034 }
1035
1036
1037 #endif // KM_WIN32
1038
1039 //------------------------------------------------------------------------------------------
1040
1041
1042 //
1043 Kumu::Result_t
1044 Kumu::ReadFileIntoString(const char* filename, std::string& outString, ui32_t max_size)
1045 {
1046   fsize_t    fsize = 0;
1047   ui32_t     read_size = 0;
1048   FileReader File;
1049   ByteString ReadBuf;
1050
1051   KM_TEST_NULL_STR_L(filename);
1052
1053   Result_t result = File.OpenRead(filename);
1054
1055   if ( KM_SUCCESS(result) )
1056     {
1057       fsize = File.Size();
1058
1059       if ( fsize > (Kumu::fpos_t)max_size )
1060         {
1061           DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename, max_size);
1062           return RESULT_ALLOC;
1063         }
1064
1065       if ( fsize == 0 )
1066         {
1067           DefaultLogSink().Error("%s: zero file size\n", filename);
1068           return RESULT_READFAIL;
1069         }
1070
1071       result = ReadBuf.Capacity((ui32_t)fsize);
1072     }
1073
1074   if ( KM_SUCCESS(result) )
1075     result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1076
1077   if ( KM_SUCCESS(result) )
1078     outString.assign((const char*)ReadBuf.RoData(), read_size);
1079
1080   return result;
1081 }
1082
1083
1084 //
1085 Kumu::Result_t
1086 Kumu::WriteStringIntoFile(const char* filename, const std::string& inString)
1087 {
1088   FileWriter File;
1089   ui32_t write_count = 0;
1090   KM_TEST_NULL_STR_L(filename);
1091
1092   Result_t result = File.OpenWrite(filename);
1093
1094   if ( KM_SUCCESS(result) )
1095     result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1096
1097   return result;
1098 }
1099
1100 //------------------------------------------------------------------------------------------
1101
1102
1103 //
1104 Kumu::Result_t
1105 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1106 {
1107   ByteString Buffer;
1108   ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1109   Result_t result = Buffer.Capacity(file_size);
1110
1111   if ( KM_SUCCESS(result) )
1112     {
1113       ui32_t read_count = 0;
1114       FileWriter Reader;
1115
1116       result = Reader.OpenRead(Filename.c_str());
1117
1118       if ( KM_SUCCESS(result) )
1119         result = Reader.Read(Buffer.Data(), file_size, &read_count);
1120     
1121       if ( KM_SUCCESS(result) )
1122         {
1123           assert(file_size == read_count);
1124           Buffer.Length(read_count);
1125           MemIOReader MemReader(&Buffer);
1126           result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1127         }
1128     }
1129
1130   return result;
1131 }
1132
1133 //
1134 Kumu::Result_t
1135 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1136 {
1137   ByteString Buffer;
1138   Result_t result = Buffer.Capacity(Object.ArchiveLength());
1139
1140   if ( KM_SUCCESS(result) )
1141     {
1142       ui32_t write_count = 0;
1143       FileWriter Writer;
1144       MemIOWriter MemWriter(&Buffer);
1145
1146       result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1147
1148       if ( KM_SUCCESS(result) )
1149         {
1150           Buffer.Length(MemWriter.Length());
1151           result = Writer.OpenWrite(Filename.c_str());
1152         }
1153
1154       if ( KM_SUCCESS(result) )
1155         result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1156     }
1157
1158   return result;
1159 }
1160
1161 //------------------------------------------------------------------------------------------
1162 //
1163
1164 //
1165 Result_t
1166 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1167 {
1168   ui32_t file_size = FileSize(Filename);
1169   Result_t result = Buffer.Capacity(file_size);
1170
1171   if ( KM_SUCCESS(result) )
1172     {
1173       ui32_t read_count = 0;
1174       FileWriter Reader;
1175
1176       result = Reader.OpenRead(Filename.c_str());
1177
1178       if ( KM_SUCCESS(result) )
1179         result = Reader.Read(Buffer.Data(), file_size, &read_count);
1180     
1181       if ( KM_SUCCESS(result) )
1182         {
1183           if ( file_size != read_count) 
1184             return RESULT_READFAIL;
1185
1186           Buffer.Length(read_count);
1187         }
1188     }
1189   
1190   return result;
1191 }
1192
1193 //
1194 Result_t
1195 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1196 {
1197   ui32_t write_count = 0;
1198   FileWriter Writer;
1199
1200   Result_t result = Writer.OpenWrite(Filename.c_str());
1201
1202   if ( KM_SUCCESS(result) )
1203     result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1204
1205   if ( KM_SUCCESS(result) && Buffer.Length() != write_count) 
1206     return RESULT_WRITEFAIL;
1207
1208   return result;
1209 }
1210
1211 //------------------------------------------------------------------------------------------
1212 //
1213
1214
1215 // Win32 directory scanner
1216 //
1217 #ifdef KM_WIN32
1218
1219 //
1220 Kumu::DirScanner::DirScanner(void) : m_Handle(-1) {}
1221
1222 //
1223 //
1224 Result_t
1225 Kumu::DirScanner::Open(const char* filename)
1226 {
1227   KM_TEST_NULL_STR_L(filename);
1228
1229   // we need to append a '*' to read the entire directory
1230   ui32_t fn_len = strlen(filename); 
1231   char* tmp_file = (char*)malloc(fn_len + 8);
1232
1233   if ( tmp_file == 0 )
1234     return RESULT_ALLOC;
1235
1236   strcpy(tmp_file, filename);
1237   char* p = &tmp_file[fn_len] - 1;
1238
1239   if ( *p != '/' && *p != '\\' )
1240     {
1241       p++;
1242       *p++ = '/';
1243     }
1244
1245   *p++ = '*';
1246   *p = 0;
1247   // whew...
1248
1249   m_Handle = _findfirsti64(tmp_file, &m_FileInfo);
1250   Result_t result = RESULT_OK;
1251
1252   if ( m_Handle == -1 )
1253     result = RESULT_NOT_FOUND;
1254
1255   return result;
1256 }
1257
1258
1259 //
1260 //
1261 Result_t
1262 Kumu::DirScanner::Close()
1263 {
1264   if ( m_Handle == -1 )
1265     return RESULT_FILEOPEN;
1266
1267   if ( _findclose((long)m_Handle) == -1 )
1268     return RESULT_FAIL;
1269
1270   m_Handle = -1;
1271   return RESULT_OK;
1272 }
1273
1274
1275 // This sets filename param to the same per-instance buffer every time, so
1276 // the value will change on the next call
1277 Result_t
1278 Kumu::DirScanner::GetNext(char* filename)
1279 {
1280   KM_TEST_NULL_L(filename);
1281
1282   if ( m_Handle == -1 )
1283     return RESULT_FILEOPEN;
1284
1285   if ( m_FileInfo.name[0] == '\0' )
1286     return RESULT_ENDOFFILE;
1287
1288   strncpy(filename, m_FileInfo.name, MaxFilePath);
1289   Result_t result = RESULT_OK;
1290
1291   if ( _findnexti64((long)m_Handle, &m_FileInfo) == -1 )
1292     {
1293       m_FileInfo.name[0] = '\0';
1294           
1295       if ( errno != ENOENT )
1296         result = RESULT_FAIL;
1297     }
1298
1299   return result;
1300 }
1301
1302
1303 #else // KM_WIN32
1304
1305 // POSIX directory scanner
1306
1307 //
1308 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1309
1310 //
1311 Result_t
1312 Kumu::DirScanner::Open(const char* filename)
1313 {
1314   KM_TEST_NULL_STR_L(filename);
1315
1316   Result_t result = RESULT_OK;
1317
1318   if ( ( m_Handle = opendir(filename) ) == NULL )
1319     {
1320       switch ( errno )
1321         {
1322         case ENOENT:
1323         case ENOTDIR:
1324           result = RESULT_NOTAFILE;
1325         case EACCES:
1326           result = RESULT_NO_PERM;
1327         case ELOOP:
1328         case ENAMETOOLONG:
1329           result = RESULT_PARAM;
1330         case EMFILE:
1331         case ENFILE:
1332           result = RESULT_STATE;
1333         default:
1334           DefaultLogSink().Error("DirScanner::Open(%s): %s\n", filename, strerror(errno));
1335           result = RESULT_FAIL;
1336         }
1337     }
1338
1339   return result;
1340 }
1341
1342
1343 //
1344 Result_t
1345 Kumu::DirScanner::Close()
1346 {
1347   if ( m_Handle == NULL )
1348     return RESULT_FILEOPEN;
1349
1350   if ( closedir(m_Handle) == -1 ) {
1351     switch ( errno )
1352       {
1353       case EBADF:
1354       case EINTR:
1355         return RESULT_STATE;
1356       default:
1357         DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1358         return RESULT_FAIL;
1359       }
1360   }
1361
1362   m_Handle = NULL;
1363   return RESULT_OK;
1364 }
1365
1366
1367 //
1368 Result_t
1369 Kumu::DirScanner::GetNext(char* filename)
1370 {
1371   KM_TEST_NULL_L(filename);
1372
1373   if ( m_Handle == NULL )
1374     return RESULT_FILEOPEN;
1375
1376   struct dirent* entry;
1377
1378   for (;;)
1379     {
1380       if ( ( entry = readdir(m_Handle)) == NULL )
1381         return RESULT_ENDOFFILE;
1382
1383       break;
1384     }
1385
1386   strncpy(filename, entry->d_name, MaxFilePath);
1387   return RESULT_OK;
1388 }
1389
1390
1391 #endif // KM_WIN32
1392
1393
1394 //------------------------------------------------------------------------------------------
1395
1396 //
1397 // Attention Windows users: make sure to use the proper separator character
1398 // with these functions.
1399 //
1400
1401 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1402 //
1403 Result_t
1404 Kumu::CreateDirectoriesInPath(const std::string& Path)
1405 {
1406   bool abs = PathIsAbsolute(Path);
1407   PathCompList_t PathComps, TmpPathComps;
1408
1409   PathToComponents(Path, PathComps);
1410
1411   while ( ! PathComps.empty() )
1412     {
1413       TmpPathComps.push_back(PathComps.front());
1414       PathComps.pop_front();
1415       std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1416
1417       if ( ! PathIsDirectory(tmp_path) )
1418         {
1419 #ifdef KM_WIN32
1420           if ( _mkdir(tmp_path.c_str()) != 0 )
1421 #else // KM_WIN32
1422           if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1423 #endif // KM_WIN32
1424             {
1425               DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1426                                      tmp_path.c_str(), strerror(errno));
1427               return RESULT_DIR_CREATE;
1428             }
1429         }
1430     }
1431
1432   return RESULT_OK;
1433 }
1434
1435
1436 //
1437 Result_t
1438 Kumu::DeleteFile(const std::string& filename)
1439 {
1440   if ( _unlink(filename.c_str()) == 0 )
1441     return RESULT_OK;
1442
1443   switch ( errno )
1444     {
1445     case ENOENT:
1446     case ENOTDIR: return RESULT_NOTAFILE;
1447
1448     case EROFS:
1449     case EBUSY:
1450     case EACCES:
1451     case EPERM:   return RESULT_NO_PERM;
1452     }
1453
1454   DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1455   return RESULT_FAIL;
1456 }
1457
1458 //
1459 Result_t
1460 h__DeletePath(const std::string& pathname)
1461 {
1462   if ( pathname.empty() )
1463     return RESULT_NULL_STR;
1464
1465   Result_t result = RESULT_OK;
1466
1467   if ( ! PathIsDirectory(pathname) )
1468     {
1469       result = DeleteFile(pathname);
1470     }
1471   else
1472     {
1473       {
1474         DirScanner TestDir;
1475         char       next_file[Kumu::MaxFilePath];
1476         result = TestDir.Open(pathname.c_str());
1477
1478         while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1479           {
1480             if ( next_file[0] == '.' )
1481               {
1482                 if ( next_file[1] ==  0 )
1483                   continue; // don't delete 'this'
1484                 
1485                 if ( next_file[1] == '.' && next_file[2] ==  0 )
1486                   continue; // don't delete 'this' parent
1487               }
1488
1489             result = h__DeletePath(pathname + std::string("/") + next_file);
1490           }
1491       }
1492
1493       if ( _rmdir(pathname.c_str()) != 0 )
1494         {
1495           switch ( errno )
1496             {
1497             case ENOENT:
1498             case ENOTDIR:
1499               result = RESULT_NOTAFILE;
1500               break;
1501
1502             case EROFS:
1503             case EBUSY:
1504             case EACCES:
1505             case EPERM:
1506               result = RESULT_NO_PERM;
1507               break;
1508
1509             default:
1510               DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1511               result = RESULT_FAIL;
1512             }
1513         }
1514     }
1515
1516   return result;
1517 }
1518
1519 //
1520 Result_t
1521 Kumu::DeletePath(const std::string& pathname)
1522 {
1523   std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
1524   DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1525   return h__DeletePath(c_pathname);
1526 }
1527
1528
1529 //------------------------------------------------------------------------------------------
1530 //
1531
1532
1533 Result_t
1534 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1535 {
1536 #ifdef KM_WIN32
1537         ULARGE_INTEGER lTotalNumberOfBytes;
1538         ULARGE_INTEGER lTotalNumberOfFreeBytes;
1539
1540         BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1541         if (fResult) {
1542       free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1543       total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1544       return RESULT_OK;
1545         }
1546         HRESULT LastError = ::GetLastError();
1547
1548         DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
1549         return RESULT_FAIL;
1550 #else // KM_WIN32
1551   struct statfs s;
1552
1553   if ( statfs(path.c_str(), &s) == 0 )
1554     {
1555       if ( s.f_blocks < 1 )
1556         {
1557           DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1558                                  path.c_str(), s.f_blocks);
1559           return RESULT_FAIL;
1560         }
1561
1562       free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1563       total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1564       return RESULT_OK;
1565     }
1566
1567   switch ( errno )
1568     {
1569     case ENOENT:
1570     case ENOTDIR: return RESULT_NOTAFILE;
1571     case EACCES:  return RESULT_NO_PERM;
1572     }
1573
1574   DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1575   return RESULT_FAIL;
1576 #endif // KM_WIN32
1577
1578
1579
1580 //
1581 // end KM_fileio.cpp
1582 //