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