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