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