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