o Moved to re-entrant forms of wcrtomb() and mbrtowc()
[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 Kumu::Result_t
768 Kumu::wbstr_to_utf8(const Kumu::ByteString& in, std::string& out)
769 {
770   out.erase();
771   assert(in.Length()%sizeof(wchar_t)==0);
772   const wchar_t* p = (const wchar_t*)in.RoData();
773
774   int stringLength = static_cast<int>( in.Length() );
775   int len = WideCharToMultiByte(CP_UTF8, 0, p, stringLength, NULL, 0, NULL, NULL);
776   char *mb_buf = new char[len];
777   WideCharToMultiByte(CP_UTF8, 0, p, stringLength, mb_buf, len, NULL, NULL);
778   out = mb_buf;
779   delete [] mb_buf;
780   return RESULT_OK;
781 }
782
783 //
784 Kumu::Result_t
785 Kumu::utf8_to_wbstr(const std::string& in, Kumu::ByteString& out)
786 {
787   Result_t result = out.Capacity((in.size()+1)*sizeof(wchar_t));
788
789   if ( KM_FAILURE(result) )
790     {
791       return result;
792     }
793
794   assert(in.size()*sizeof(wchar_t)<=out.Capacity());
795   const char* read_pos = in.c_str();
796   wchar_t character, *write_pos = (wchar_t*)out.Data();
797
798   int stringLength = static_cast<int>( in.length() ) + 1;
799   int len = MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, 0, 0);
800   result = out.Capacity(len*sizeof(wchar_t));
801   if ( KM_FAILURE(result) )
802     {
803       return result;
804     }
805   MultiByteToWideChar(CP_UTF8, 0, in.c_str(), stringLength, write_pos, len);
806   out.Length(len*sizeof(wchar_t));
807
808   return RESULT_OK;
809 }
810
811 #endif
812
813
814 //------------------------------------------------------------------------------------------
815 //
816
817 Kumu::Result_t
818 Kumu::FileReader::OpenRead(const std::string& filename) const
819 {
820   const_cast<FileReader*>(this)->m_Filename = filename;
821   ByteString wb_filename;
822   Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
823
824   if ( KM_FAILURE(result) )
825     {
826       return result;
827     }
828
829   // suppress popup window on error
830   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
831
832   const_cast<FileReader*>(this)->m_Handle =
833             ::CreateFileW((wchar_t*)wb_filename.RoData(),
834                           (GENERIC_READ),                // open for reading
835                           FILE_SHARE_READ,               // share for reading
836                           NULL,                          // no security
837                           OPEN_EXISTING,                 // read
838                           FILE_ATTRIBUTE_NORMAL,         // normal file
839                           NULL                           // no template file
840                           );
841
842   ::SetErrorMode(prev);
843
844   return ( m_Handle == INVALID_HANDLE_VALUE ) ?
845     Kumu::RESULT_FILEOPEN : Kumu::RESULT_OK;
846 }
847
848 //
849 Kumu::Result_t
850 Kumu::FileReader::Close() const
851 {
852   if ( m_Handle == INVALID_HANDLE_VALUE )
853     return Kumu::RESULT_FILEOPEN;
854
855   // suppress popup window on error
856   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
857   BOOL result = ::CloseHandle(m_Handle);
858   ::SetErrorMode(prev);
859   const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
860
861   return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
862 }
863
864 //
865 Kumu::Result_t
866 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
867 {
868   if ( m_Handle == INVALID_HANDLE_VALUE )
869     return Kumu::RESULT_STATE;
870
871   LARGE_INTEGER in;
872   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
873   in.QuadPart = position;
874   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
875   HRESULT LastError = GetLastError();
876   ::SetErrorMode(prev);
877
878   if ( (LastError != NO_ERROR
879         && (in.LowPart == INVALID_SET_FILE_POINTER
880             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
881     return Kumu::RESULT_READFAIL;
882   
883   return Kumu::RESULT_OK;
884 }
885
886 //
887 Kumu::Result_t
888 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
889 {
890   KM_TEST_NULL_L(pos);
891
892   if ( m_Handle == INVALID_HANDLE_VALUE )
893     return Kumu::RESULT_FILEOPEN;
894
895   LARGE_INTEGER in;
896   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
897   in.QuadPart = (__int64)0;
898   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
899   HRESULT LastError = GetLastError();
900   ::SetErrorMode(prev);
901
902   if ( (LastError != NO_ERROR
903         && (in.LowPart == INVALID_SET_FILE_POINTER
904             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
905     return Kumu::RESULT_READFAIL;
906
907   *pos = (Kumu::fpos_t)in.QuadPart;
908   return Kumu::RESULT_OK;
909 }
910
911 //
912 Kumu::Result_t
913 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
914 {
915   KM_TEST_NULL_L(buf);
916   Result_t result = Kumu::RESULT_OK;
917   DWORD    tmp_count;
918   ui32_t tmp_int;
919
920   if ( read_count == 0 )
921     read_count = &tmp_int;
922
923   *read_count = 0;
924
925   if ( m_Handle == INVALID_HANDLE_VALUE )
926     return Kumu::RESULT_FILEOPEN;
927   
928   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
929   if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
930     result = Kumu::RESULT_READFAIL;
931
932   ::SetErrorMode(prev);
933
934   if ( tmp_count == 0 ) /* EOF */
935     result = Kumu::RESULT_ENDOFFILE;
936
937   if ( KM_SUCCESS(result) )
938     *read_count = tmp_count;
939
940   return result;
941 }
942
943
944
945 //------------------------------------------------------------------------------------------
946 //
947
948
949 //
950 Kumu::Result_t
951 Kumu::FileWriter::OpenWrite(const std::string& filename)
952 {
953   m_Filename = filename;
954   ByteString wb_filename;
955   Result_t result = utf8_to_wbstr(m_Filename, wb_filename);
956
957   if ( KM_FAILURE(result) )
958     {
959       return result;
960     }
961
962   // suppress popup window on error
963   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
964
965   m_Handle = ::CreateFileW((wchar_t*)wb_filename.RoData(),
966                           (GENERIC_WRITE|GENERIC_READ),  // open for reading
967                           FILE_SHARE_READ,               // share for reading
968                           NULL,                          // no security
969                           CREATE_ALWAYS,                 // overwrite (beware!)
970                           FILE_ATTRIBUTE_NORMAL,         // normal file
971                           NULL                           // no template file
972                           );
973
974   ::SetErrorMode(prev);
975
976   if ( m_Handle == INVALID_HANDLE_VALUE )
977     return Kumu::RESULT_FILEOPEN;
978   
979   m_IOVec = new h__iovec;
980   return Kumu::RESULT_OK;
981 }
982
983 //
984 Kumu::Result_t
985 Kumu::FileWriter::Writev(ui32_t* bytes_written)
986 {
987   assert( ! m_IOVec.empty() );
988   register h__iovec* iov = m_IOVec;
989   ui32_t tmp_int;
990
991   if ( bytes_written == 0 )
992     bytes_written = &tmp_int;
993
994   if ( m_Handle == INVALID_HANDLE_VALUE )
995     return Kumu::RESULT_STATE;
996
997   *bytes_written = 0;
998   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
999   Result_t result = Kumu::RESULT_OK;
1000
1001   // AFAIK, there is no writev() equivalent in the win32 API
1002   for ( register int i = 0; i < iov->m_Count; i++ )
1003     {
1004       ui32_t tmp_count = 0;
1005       BOOL wr_result = ::WriteFile(m_Handle,
1006                                    iov->m_iovec[i].iov_base,
1007                                    iov->m_iovec[i].iov_len,
1008                                    (DWORD*)&tmp_count,
1009                                    NULL);
1010
1011       if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1012         {
1013           result = Kumu::RESULT_WRITEFAIL;
1014           break;
1015         }
1016
1017       *bytes_written += tmp_count;
1018     }
1019
1020   ::SetErrorMode(prev);
1021   iov->m_Count = 0; // error nor not, all is lost
1022
1023   return result;
1024 }
1025
1026 //
1027 Kumu::Result_t
1028 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1029 {
1030   KM_TEST_NULL_L(buf);
1031   ui32_t tmp_int;
1032
1033   if ( bytes_written == 0 )
1034     bytes_written = &tmp_int;
1035
1036   if ( m_Handle == INVALID_HANDLE_VALUE )
1037     return Kumu::RESULT_STATE;
1038
1039   // suppress popup window on error
1040   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1041   BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1042   ::SetErrorMode(prev);
1043
1044   if ( result == 0 || *bytes_written != buf_len )
1045     return Kumu::RESULT_WRITEFAIL;
1046
1047   return Kumu::RESULT_OK;
1048 }
1049
1050 #else // KM_WIN32
1051 //------------------------------------------------------------------------------------------
1052 // POSIX
1053
1054 //
1055 Kumu::Result_t
1056 Kumu::FileReader::OpenRead(const std::string& filename) const
1057 {
1058   const_cast<FileReader*>(this)->m_Filename = filename;
1059   const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1060   return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1061 }
1062
1063 //
1064 Kumu::Result_t
1065 Kumu::FileReader::Close() const
1066 {
1067   if ( m_Handle == -1L )
1068     return RESULT_FILEOPEN;
1069
1070   close(m_Handle);
1071   const_cast<FileReader*>(this)->m_Handle = -1L;
1072   return RESULT_OK;
1073 }
1074
1075 //
1076 Kumu::Result_t
1077 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1078 {
1079   if ( m_Handle == -1L )
1080     return RESULT_FILEOPEN;
1081
1082   if ( lseek(m_Handle, position, whence) == -1L )
1083     return RESULT_BADSEEK;
1084
1085   return RESULT_OK;
1086 }
1087
1088 //
1089 Kumu::Result_t
1090 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1091 {
1092   KM_TEST_NULL_L(pos);
1093
1094   if ( m_Handle == -1L )
1095     return RESULT_FILEOPEN;
1096
1097   Kumu::fpos_t tmp_pos;
1098
1099   if (  (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1100     return RESULT_READFAIL;
1101
1102   *pos = tmp_pos;
1103   return RESULT_OK;
1104 }
1105
1106 //
1107 Kumu::Result_t
1108 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1109 {
1110   KM_TEST_NULL_L(buf);
1111   i32_t  tmp_count = 0;
1112   ui32_t tmp_int = 0;
1113
1114   if ( read_count == 0 )
1115     read_count = &tmp_int;
1116
1117   *read_count = 0;
1118
1119   if ( m_Handle == -1L )
1120     return RESULT_FILEOPEN;
1121
1122   if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1123     return RESULT_READFAIL;
1124
1125   *read_count = tmp_count;
1126   return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1127 }
1128
1129
1130 //------------------------------------------------------------------------------------------
1131 //
1132
1133 //
1134 Kumu::Result_t
1135 Kumu::FileWriter::OpenWrite(const std::string& filename)
1136 {
1137   m_Filename = filename;
1138   m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0666);
1139
1140   if ( m_Handle == -1L )
1141     {
1142       DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1143       return RESULT_FILEOPEN;
1144     }
1145
1146   m_IOVec = new h__iovec;
1147   return RESULT_OK;
1148 }
1149
1150 //
1151 Kumu::Result_t
1152 Kumu::FileWriter::OpenModify(const std::string& filename)
1153 {
1154   m_Filename = filename;
1155   m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0666);
1156
1157   if ( m_Handle == -1L )
1158     {
1159       DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1160       return RESULT_FILEOPEN;
1161     }
1162
1163   m_IOVec = new h__iovec;
1164   return RESULT_OK;
1165 }
1166
1167 //
1168 Kumu::Result_t
1169 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1170 {
1171   assert( ! m_IOVec.empty() );
1172   register h__iovec* iov = m_IOVec;
1173   ui32_t tmp_int;
1174
1175   if ( bytes_written == 0 )
1176     bytes_written = &tmp_int;
1177
1178   if ( m_Handle == -1L )
1179     return RESULT_STATE;
1180
1181   int total_size = 0;
1182   for ( int i = 0; i < iov->m_Count; i++ )
1183     total_size += iov->m_iovec[i].iov_len;
1184
1185   int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1186   
1187   if ( write_size == -1L || write_size != total_size )
1188     return RESULT_WRITEFAIL;
1189
1190   iov->m_Count = 0;
1191   *bytes_written = write_size;  
1192   return RESULT_OK;
1193 }
1194
1195 //
1196 Kumu::Result_t
1197 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1198 {
1199   KM_TEST_NULL_L(buf);
1200   ui32_t tmp_int;
1201
1202   if ( bytes_written == 0 )
1203     bytes_written = &tmp_int;
1204
1205   if ( m_Handle == -1L )
1206     return RESULT_STATE;
1207
1208   int write_size = write(m_Handle, buf, buf_len);
1209
1210   if ( write_size == -1L || (ui32_t)write_size != buf_len )
1211     return RESULT_WRITEFAIL;
1212
1213   *bytes_written = write_size;
1214   return RESULT_OK;
1215 }
1216
1217
1218 #endif // KM_WIN32
1219
1220 //------------------------------------------------------------------------------------------
1221
1222
1223 //
1224 Kumu::Result_t
1225 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1226 {
1227   fsize_t    fsize = 0;
1228   ui32_t     read_size = 0;
1229   FileReader File;
1230   ByteString ReadBuf;
1231
1232   Result_t result = File.OpenRead(filename);
1233
1234   if ( KM_SUCCESS(result) )
1235     {
1236       fsize = File.Size();
1237
1238       if ( fsize > (Kumu::fpos_t)max_size )
1239         {
1240           DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1241           return RESULT_ALLOC;
1242         }
1243
1244       if ( fsize == 0 )
1245         {
1246           DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1247           return RESULT_READFAIL;
1248         }
1249
1250       result = ReadBuf.Capacity((ui32_t)fsize);
1251     }
1252
1253   if ( KM_SUCCESS(result) )
1254     result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1255
1256   if ( KM_SUCCESS(result) )
1257     outString.assign((const char*)ReadBuf.RoData(), read_size);
1258
1259   return result;
1260 }
1261
1262
1263 //
1264 Kumu::Result_t
1265 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1266 {
1267   FileWriter File;
1268   ui32_t write_count = 0;
1269
1270   Result_t result = File.OpenWrite(filename);
1271
1272   if ( KM_SUCCESS(result) )
1273     result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1274
1275   return result;
1276 }
1277
1278 //------------------------------------------------------------------------------------------
1279
1280
1281 //
1282 Kumu::Result_t
1283 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t max_size)
1284 {
1285   ByteString Buffer;
1286   ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1287   Result_t result = Buffer.Capacity(file_size);
1288
1289   if ( KM_SUCCESS(result) )
1290     {
1291       ui32_t read_count = 0;
1292       FileReader Reader;
1293
1294       result = Reader.OpenRead(Filename);
1295
1296       if ( KM_SUCCESS(result) )
1297         result = Reader.Read(Buffer.Data(), file_size, &read_count);
1298     
1299       if ( KM_SUCCESS(result) )
1300         {
1301           assert(file_size == read_count);
1302           Buffer.Length(read_count);
1303           MemIOReader MemReader(&Buffer);
1304           result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1305         }
1306     }
1307
1308   return result;
1309 }
1310
1311 //
1312 Kumu::Result_t
1313 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1314 {
1315   ByteString Buffer;
1316   Result_t result = Buffer.Capacity(Object.ArchiveLength());
1317
1318   if ( KM_SUCCESS(result) )
1319     {
1320       ui32_t write_count = 0;
1321       FileWriter Writer;
1322       MemIOWriter MemWriter(&Buffer);
1323
1324       result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1325
1326       if ( KM_SUCCESS(result) )
1327         {
1328           Buffer.Length(MemWriter.Length());
1329           result = Writer.OpenWrite(Filename);
1330         }
1331
1332       if ( KM_SUCCESS(result) )
1333         result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1334     }
1335
1336   return result;
1337 }
1338
1339 //------------------------------------------------------------------------------------------
1340 //
1341
1342 //
1343 Result_t
1344 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t max_size)
1345 {
1346   ui32_t file_size = FileSize(Filename);
1347   Result_t result = Buffer.Capacity(file_size);
1348
1349   if ( KM_SUCCESS(result) )
1350     {
1351       ui32_t read_count = 0;
1352       FileReader Reader;
1353
1354       result = Reader.OpenRead(Filename);
1355
1356       if ( KM_SUCCESS(result) )
1357         result = Reader.Read(Buffer.Data(), file_size, &read_count);
1358     
1359       if ( KM_SUCCESS(result) )
1360         {
1361           if ( file_size != read_count) 
1362             return RESULT_READFAIL;
1363
1364           Buffer.Length(read_count);
1365         }
1366     }
1367   
1368   return result;
1369 }
1370
1371 //
1372 Result_t
1373 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1374 {
1375   ui32_t write_count = 0;
1376   FileWriter Writer;
1377
1378   Result_t result = Writer.OpenWrite(Filename);
1379
1380   if ( KM_SUCCESS(result) )
1381     result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1382
1383   if ( KM_SUCCESS(result) && Buffer.Length() != write_count) 
1384     return RESULT_WRITEFAIL;
1385
1386   return result;
1387 }
1388
1389 //------------------------------------------------------------------------------------------
1390 //
1391
1392
1393 //
1394 Kumu::DirScanner::DirScanner(void) : m_Handle(NULL) {}
1395
1396 //
1397 Result_t
1398 Kumu::DirScanner::Open(const std::string& dirname)
1399 {
1400   Result_t result = RESULT_OK;
1401
1402   if ( ( m_Handle = opendir(dirname.c_str()) ) == NULL )
1403     {
1404       switch ( errno )
1405         {
1406         case ENOENT:
1407         case ENOTDIR:
1408           result = RESULT_NOTAFILE;
1409         case EACCES:
1410           result = RESULT_NO_PERM;
1411         case ELOOP:
1412         case ENAMETOOLONG:
1413           result = RESULT_PARAM;
1414         case EMFILE:
1415         case ENFILE:
1416           result = RESULT_STATE;
1417         default:
1418           DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1419           result = RESULT_FAIL;
1420         }
1421     }
1422
1423   return result;
1424 }
1425
1426
1427 //
1428 Result_t
1429 Kumu::DirScanner::Close()
1430 {
1431   if ( m_Handle == NULL )
1432     return RESULT_FILEOPEN;
1433
1434   if ( closedir(m_Handle) == -1 ) {
1435     switch ( errno )
1436       {
1437       case EBADF:
1438       case EINTR:
1439         return RESULT_STATE;
1440       default:
1441         DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1442         return RESULT_FAIL;
1443       }
1444   }
1445
1446   m_Handle = NULL;
1447   return RESULT_OK;
1448 }
1449
1450
1451 //
1452 Result_t
1453 Kumu::DirScanner::GetNext(char* filename)
1454 {
1455   KM_TEST_NULL_L(filename);
1456
1457   if ( m_Handle == NULL )
1458     return RESULT_FILEOPEN;
1459
1460   struct dirent* entry;
1461
1462   for (;;)
1463     {
1464       if ( ( entry = readdir(m_Handle)) == NULL )
1465         return RESULT_ENDOFFILE;
1466
1467       break;
1468     }
1469
1470   strncpy(filename, entry->d_name, MaxFilePath);
1471   return RESULT_OK;
1472 }
1473
1474 //------------------------------------------------------------------------------------------
1475
1476 //
1477 Kumu::DirScannerEx::DirScannerEx() : m_Handle(0) {}
1478
1479 //
1480 Result_t
1481 Kumu::DirScannerEx::Open(const std::string& dirname)
1482 {
1483   Result_t result = RESULT_OK;
1484
1485   if ( ( m_Handle = opendir(dirname.c_str()) ) == 0 )
1486     {
1487       switch ( errno )
1488         {
1489         case ENOENT:
1490         case ENOTDIR:
1491           result = RESULT_NOTAFILE;
1492         case EACCES:
1493           result = RESULT_NO_PERM;
1494         case ELOOP:
1495         case ENAMETOOLONG:
1496           result = RESULT_PARAM;
1497         case EMFILE:
1498         case ENFILE:
1499           result = RESULT_STATE;
1500         default:
1501           DefaultLogSink().Error("DirScanner::Open(%s): %s\n", dirname.c_str(), strerror(errno));
1502           result = RESULT_FAIL;
1503         }
1504     }
1505
1506   if ( KM_SUCCESS(result) )
1507     m_Dirname = dirname;
1508
1509   KM_RESULT_STATE_TEST_IMPLICIT();
1510   return result;
1511 }
1512
1513 //
1514 Result_t
1515 Kumu::DirScannerEx::Close()
1516 {
1517   if ( m_Handle == NULL )
1518     return RESULT_FILEOPEN;
1519
1520   if ( closedir(m_Handle) == -1 )
1521     {
1522       switch ( errno )
1523         {
1524         case EBADF:
1525         case EINTR:
1526           KM_RESULT_STATE_HERE();
1527           return RESULT_STATE;
1528
1529         default:
1530           DefaultLogSink().Error("DirScanner::Close(): %s\n", strerror(errno));
1531           return RESULT_FAIL;
1532         }
1533     }
1534
1535   m_Handle = 0;
1536   return RESULT_OK;
1537 }
1538
1539 //
1540 Result_t
1541 Kumu::DirScannerEx::GetNext(std::string& next_item_name, DirectoryEntryType_t& next_item_type)
1542 {
1543   if ( m_Handle == 0 )
1544     return RESULT_FILEOPEN;
1545
1546 #if defined(__sun) && defined(__SVR4)
1547   struct stat s;
1548 #endif
1549   struct dirent* entry;
1550
1551   for (;;)
1552     {
1553       if ( ( entry = readdir(m_Handle) ) == 0 )
1554         return RESULT_ENDOFFILE;
1555
1556       break;
1557     }
1558
1559   next_item_name.assign(entry->d_name, strlen(entry->d_name));
1560
1561 #if defined(__sun) && defined(__SVR4)
1562
1563   stat(entry->d_name, &s);
1564
1565   switch ( s.st_mode )
1566     {
1567     case S_IFDIR:
1568       next_item_type = DET_DIR;
1569       break;
1570
1571     case S_IFREG:
1572       next_item_type = DET_FILE;
1573       break;
1574
1575     case S_IFLNK:
1576       next_item_type = DET_LINK;
1577       break;
1578
1579     default:
1580       next_item_type = DET_DEV;
1581     }
1582 #else // __sun 
1583   switch ( entry->d_type )
1584     {
1585     case DT_DIR:
1586       next_item_type = DET_DIR;
1587       break;
1588
1589     case DT_REG:
1590       next_item_type = DET_FILE;
1591       break;
1592
1593     case DT_LNK:
1594       next_item_type = DET_LINK;
1595       break;
1596
1597     default:
1598       next_item_type = DET_DEV;
1599     }
1600 #endif // __sun
1601   return RESULT_OK;
1602 }
1603
1604
1605 //------------------------------------------------------------------------------------------
1606
1607 //
1608 // Attention Windows users: make sure to use the proper separator character
1609 // with these functions.
1610 //
1611
1612 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1613 //
1614 Result_t
1615 Kumu::CreateDirectoriesInPath(const std::string& Path)
1616 {
1617   bool abs = PathIsAbsolute(Path);
1618   PathCompList_t PathComps, TmpPathComps;
1619
1620   PathToComponents(Path, PathComps);
1621
1622   while ( ! PathComps.empty() )
1623     {
1624       TmpPathComps.push_back(PathComps.front());
1625       PathComps.pop_front();
1626       std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1627
1628       if ( ! PathIsDirectory(tmp_path) )
1629         {
1630 #ifdef KM_WIN32
1631           if ( _mkdir(tmp_path.c_str()) != 0 )
1632 #else // KM_WIN32
1633           if ( mkdir(tmp_path.c_str(), 0777) != 0 )
1634 #endif // KM_WIN32
1635             {
1636               DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1637                                      tmp_path.c_str(), strerror(errno));
1638               return RESULT_DIR_CREATE;
1639             }
1640         }
1641     }
1642
1643   return RESULT_OK;
1644 }
1645
1646
1647 //
1648 Result_t
1649 Kumu::DeleteFile(const std::string& filename)
1650 {
1651   if ( _unlink(filename.c_str()) == 0 )
1652     return RESULT_OK;
1653
1654   switch ( errno )
1655     {
1656     case ENOENT:
1657     case ENOTDIR: return RESULT_NOTAFILE;
1658
1659     case EROFS:
1660     case EBUSY:
1661     case EACCES:
1662     case EPERM:   return RESULT_NO_PERM;
1663     }
1664
1665   DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1666   return RESULT_FAIL;
1667 }
1668
1669 //
1670 Result_t
1671 h__DeletePath(const std::string& pathname)
1672 {
1673   if ( pathname.empty() )
1674     return RESULT_NULL_STR;
1675
1676   Result_t result = RESULT_OK;
1677
1678   if ( ! PathIsDirectory(pathname) )
1679     {
1680       result = DeleteFile(pathname);
1681     }
1682   else
1683     {
1684       {
1685         DirScanner TestDir;
1686         char       next_file[Kumu::MaxFilePath];
1687         result = TestDir.Open(pathname.c_str());
1688
1689         while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1690           {
1691             if ( next_file[0] == '.' )
1692               {
1693                 if ( next_file[1] ==  0 )
1694                   continue; // don't delete 'this'
1695                 
1696                 if ( next_file[1] == '.' && next_file[2] ==  0 )
1697                   continue; // don't delete 'this' parent
1698               }
1699
1700             result = h__DeletePath(pathname + std::string("/") + next_file);
1701           }
1702       }
1703
1704       if ( _rmdir(pathname.c_str()) != 0 )
1705         {
1706           switch ( errno )
1707             {
1708             case ENOENT:
1709             case ENOTDIR:
1710               result = RESULT_NOTAFILE;
1711               break;
1712
1713             case EROFS:
1714             case EBUSY:
1715             case EACCES:
1716             case EPERM:
1717               result = RESULT_NO_PERM;
1718               break;
1719
1720             default:
1721               DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1722               result = RESULT_FAIL;
1723             }
1724         }
1725     }
1726
1727   return result;
1728 }
1729
1730 //
1731 Result_t
1732 Kumu::DeletePath(const std::string& pathname)
1733 {
1734   std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1735   DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1736   return h__DeletePath(c_pathname);
1737 }
1738
1739
1740 //
1741 Result_t
1742 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1743 {
1744   DirScanner source_dir;
1745   char next_file[Kumu::MaxFilePath];
1746
1747   Result_t result = source_dir.Open(path);
1748
1749   if ( KM_FAILURE(result) )
1750     return result;
1751
1752   while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1753     {
1754       if ( ( next_file[0] == '.' && next_file[1] == 0 )
1755            || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1756         continue;
1757
1758       return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1759     }
1760
1761   return DeletePath(path);
1762 }
1763
1764
1765 //------------------------------------------------------------------------------------------
1766 //
1767
1768
1769 Result_t
1770 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1771 {
1772 #ifdef KM_WIN32
1773   ULARGE_INTEGER lTotalNumberOfBytes;
1774   ULARGE_INTEGER lTotalNumberOfFreeBytes;
1775
1776   BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1777   if ( fResult )
1778     {
1779       free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1780       total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1781       return RESULT_OK;
1782     }
1783
1784   HRESULT last_error = ::GetLastError();
1785
1786   DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1787   return RESULT_FAIL;
1788 #else // KM_WIN32
1789   struct statfs s;
1790
1791 #if defined(__sun) && defined(__SVR4)
1792   if ( statfs(path.c_str(), &s, s.f_bsize, s.f_fstyp ) == 0 )
1793     {      if ( s.f_blocks < 1 )
1794         {
1795           DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1796                                  path.c_str(), s.f_blocks);
1797           return RESULT_FAIL;
1798         }
1799
1800       free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bfree;
1801       total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1802       return RESULT_OK;
1803     }
1804 #else
1805   if ( statfs(path.c_str(), &s) == 0 )
1806     {
1807       if ( s.f_blocks < 1 )
1808         {
1809           DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1810                                  path.c_str(), s.f_blocks);
1811           return RESULT_FAIL;
1812         }
1813
1814       free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1815       total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1816       return RESULT_OK;
1817     }
1818
1819   switch ( errno )
1820     {
1821     case ENOENT:
1822     case ENOTDIR: return RESULT_NOTAFILE;
1823     case EACCES:  return RESULT_NO_PERM;
1824     }
1825
1826   DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1827   return RESULT_FAIL;
1828 #endif // __sun 
1829 #endif // KM_WIN32
1830
1831
1832
1833 //
1834 // end KM_fileio.cpp
1835 //