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