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