Debug more.
[asdcplib.git] / src / KM_fileio.cpp
1 /*
2 Copyright (c) 2004-2014, 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 <asdcp/KM_fileio.h>
33 #include <asdcp/KM_log.h>
34 #include <fcntl.h>
35 #include <iomanip>
36
37 #include <assert.h>
38
39 #include <iostream>
40
41 #ifdef KM_WIN32
42 #include <direct.h>
43 #else
44 #define _getcwd getcwd
45 #define _unlink unlink
46 #define _rmdir rmdir
47 #endif
48
49 // only needed by GetExecutablePath()
50 #if defined(KM_MACOSX)
51 #include <mach-o/dyld.h>
52 #endif
53
54 using namespace Kumu;
55
56 #ifdef KM_WIN32
57 typedef struct _stati64 fstat_t;
58 #define S_IFLNK 0
59
60
61 // win32 has WriteFileGather() and ReadFileScatter() but they
62 // demand page alignment and page sizing, making them unsuitable
63 // for use with arbitrary buffer sizes.
64 struct iovec {
65   char* iov_base; // stupid iovec uses char*
66   int   iov_len;
67 };
68 #else
69 # if defined(__linux__)
70 #   include <sys/statfs.h>
71 # else
72 #  include <sys/param.h>
73 #  include <sys/mount.h>
74 # endif
75
76 #include <sys/stat.h>
77 #include <sys/uio.h>
78 typedef struct stat     fstat_t;
79 #endif
80
81
82 //
83 static Kumu::Result_t
84 do_stat(const char* path, fstat_t* stat_info)
85 {
86   KM_TEST_NULL_STR_L(path);
87   KM_TEST_NULL_L(stat_info);
88
89   Kumu::Result_t result = Kumu::RESULT_OK;
90
91 #ifdef KM_WIN32
92   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
93
94   int const wn = MultiByteToWideChar (CP_UTF8, 0, path, -1, 0, 0);
95   wchar_t* buffer = new wchar_t[wn];
96   if (MultiByteToWideChar (CP_UTF8, 0, path, -1, buffer, wn) == 0)
97     {
98       delete[] buffer;
99       return Kumu::RESULT_FAIL;
100     }
101
102   if ( _wstati64(buffer, stat_info) == (__int64)-1 )
103     result = Kumu::RESULT_FILEOPEN;
104
105   delete[] buffer;
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__) || defined(__FreeBSD__)
672   size_t size = X_BUFSIZE;
673   ssize_t rc = readlink("/proc/curproc/file", path, size);
674   success = ( rc != -1 );
675 #elif defined(__FreeBSD__)
676   size_t size = X_BUFSIZE;
677   ssize_t rc = readlink("/proc/curproc/file", path, size);
678   success = ( rc != -1 );
679 #elif defined(__NetBSD__)
680   size_t size = X_BUFSIZE;
681   ssize_t rc = readlink("/proc/curproc/file", path, size);
682   success = ( rc != -1 );
683 #else
684 #error GetExecutablePath --> Create a method for obtaining the executable name
685 #endif
686
687   if ( success )
688     {
689       return Kumu::PathMakeCanonical(path);
690     }
691
692   return default_path;
693 }
694
695
696 //------------------------------------------------------------------------------------------
697 // portable aspects of the file classes
698
699 const int IOVecMaxEntries = 32; // we never use more that 3, but that number seems somehow small...
700
701 //
702 class Kumu::FileWriter::h__iovec
703 {
704 public:
705   int            m_Count;
706   struct iovec   m_iovec[IOVecMaxEntries];
707   h__iovec() : m_Count(0) {}
708 };
709
710
711
712 //
713 Kumu::fsize_t
714 Kumu::FileReader::Size() const
715 {
716 #ifdef KM_WIN32
717   return FileSize(m_Filename.c_str());
718 #else
719   fstat_t info;
720
721   if ( KM_SUCCESS(do_fstat(m_Handle, &info)) )
722     {
723       if ( info.st_mode & ( S_IFREG|S_IFLNK ) )
724         return(info.st_size);
725     }
726 #endif
727
728   return 0;
729 }
730
731 // these are declared here instead of in the header file
732 // because we have a mem_ptr that is managing a hidden class
733 Kumu::FileWriter::FileWriter()
734   : m_Hashing(false)
735 {}
736 Kumu::FileWriter::~FileWriter() {}
737
738 //
739 Kumu::Result_t
740 Kumu::FileWriter::Writev(const byte_t* buf, ui32_t buf_len)
741 {
742   assert( ! m_IOVec.empty() );
743   h__iovec* iov = m_IOVec;
744   KM_TEST_NULL_L(buf);
745
746   if ( iov->m_Count >= IOVecMaxEntries )
747     {
748       DefaultLogSink().Error("The iovec is full! Only %u entries allowed before a flush.\n",
749                              IOVecMaxEntries);
750       return RESULT_WRITEFAIL;
751     }
752
753   iov->m_iovec[iov->m_Count].iov_base = (char*)buf; // stupid iovec uses char*
754   iov->m_iovec[iov->m_Count].iov_len = buf_len;
755   iov->m_Count++;
756
757   return RESULT_OK;
758 }
759
760 void
761 Kumu::FileWriter::StartHashing()
762 {
763   m_Hashing = true;
764   MD5_Init (&m_MD5Context);
765 }
766
767 void
768 Kumu::FileWriter::MaybeHash(void const * data, int size)
769 {
770   if (m_Hashing)
771     {
772       MD5_Update (&m_MD5Context, data, size);
773     }
774 }
775
776 std::string
777 Kumu::FileWriter::StopHashing()
778 {
779   m_Hashing = false;
780
781   unsigned char digest[MD5_DIGEST_LENGTH];
782   MD5_Final (digest, &m_MD5Context);
783
784   char hex[MD5_DIGEST_LENGTH * 2 + 1];
785   for (int i = 0; i < MD5_DIGEST_LENGTH; ++i)
786     {
787       sprintf(hex + i * 2, "%02x", digest[i]);
788     }
789
790   return hex;
791 }
792
793
794
795
796 #ifdef KM_WIN32
797 //------------------------------------------------------------------------------------------
798 //
799
800 Kumu::Result_t
801 Kumu::FileReader::OpenRead(const std::string& filename) const
802 {
803   const_cast<FileReader*>(this)->m_Filename = filename;
804
805   // suppress popup window on error
806   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
807
808   std::cout << "OpenRead " << filename << "\n";
809
810   int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
811   wchar_t* buffer = new wchar_t[wn];
812   if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0)
813     {
814       delete[] buffer;
815       return Kumu::RESULT_FAIL;
816     }
817
818   std::wcout << "converted to " << buffer << "\n";
819   const_cast<FileReader*>(this)->m_Handle = ::CreateFileW(buffer,
820                           (GENERIC_READ),                // open for reading
821                           FILE_SHARE_READ,               // share for reading
822                           NULL,                          // no security
823                           OPEN_EXISTING,                 // read
824                           FILE_ATTRIBUTE_NORMAL,         // normal file
825                           NULL                           // no template file
826                           );
827
828   delete[] buffer;
829   HRESULT const last_error = GetLastError();
830
831   ::SetErrorMode(prev);
832
833   if (m_Handle == INVALID_HANDLE_VALUE)
834     {
835       DefaultLogSink().Error("CreateFileW failed: %lu\n", last_error);
836       return Kumu::RESULT_FILEOPEN;
837     }
838
839   return Kumu::RESULT_OK;
840 }
841
842 //
843 Kumu::Result_t
844 Kumu::FileReader::Close() const
845 {
846   if ( m_Handle == INVALID_HANDLE_VALUE )
847     return Kumu::RESULT_FILEOPEN;
848
849   // suppress popup window on error
850   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
851   BOOL result = ::CloseHandle(m_Handle);
852   ::SetErrorMode(prev);
853   const_cast<FileReader*>(this)->m_Handle = INVALID_HANDLE_VALUE;
854
855   return ( result == 0 ) ? Kumu::RESULT_FAIL : Kumu::RESULT_OK;
856 }
857
858 //
859 Kumu::Result_t
860 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
861 {
862   if ( m_Handle == INVALID_HANDLE_VALUE )
863     return Kumu::RESULT_STATE;
864
865   LARGE_INTEGER in;
866   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
867   in.QuadPart = position;
868   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, whence);
869   HRESULT LastError = GetLastError();
870   ::SetErrorMode(prev);
871
872   if ( (LastError != NO_ERROR
873         && (in.LowPart == INVALID_SET_FILE_POINTER
874             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
875     return Kumu::RESULT_READFAIL;
876
877   return Kumu::RESULT_OK;
878 }
879
880 //
881 Kumu::Result_t
882 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
883 {
884   KM_TEST_NULL_L(pos);
885
886   if ( m_Handle == INVALID_HANDLE_VALUE )
887     return Kumu::RESULT_FILEOPEN;
888
889   LARGE_INTEGER in;
890   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
891   in.QuadPart = (__int64)0;
892   in.LowPart = ::SetFilePointer(m_Handle, in.LowPart, &in.HighPart, FILE_CURRENT);
893   HRESULT LastError = GetLastError();
894   ::SetErrorMode(prev);
895
896   if ( (LastError != NO_ERROR
897         && (in.LowPart == INVALID_SET_FILE_POINTER
898             || in.LowPart == ERROR_NEGATIVE_SEEK )) )
899     return Kumu::RESULT_READFAIL;
900
901   *pos = (Kumu::fpos_t)in.QuadPart;
902   return Kumu::RESULT_OK;
903 }
904
905 //
906 Kumu::Result_t
907 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
908 {
909   KM_TEST_NULL_L(buf);
910   Result_t result = Kumu::RESULT_OK;
911   DWORD    tmp_count;
912   ui32_t tmp_int;
913
914   if ( read_count == 0 )
915     read_count = &tmp_int;
916
917   *read_count = 0;
918
919   if ( m_Handle == INVALID_HANDLE_VALUE )
920     return Kumu::RESULT_FILEOPEN;
921
922   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
923   if ( ::ReadFile(m_Handle, buf, buf_len, &tmp_count, NULL) == 0 )
924     result = Kumu::RESULT_READFAIL;
925
926   ::SetErrorMode(prev);
927
928   if ( tmp_count == 0 ) /* EOF */
929     result = Kumu::RESULT_ENDOFFILE;
930
931   if ( KM_SUCCESS(result) )
932     *read_count = tmp_count;
933
934   return result;
935 }
936
937
938
939 //------------------------------------------------------------------------------------------
940 //
941
942 //
943 Kumu::Result_t
944 Kumu::FileWriter::OpenWrite(const std::string& filename)
945 {
946   m_Filename = filename;
947
948   // suppress popup window on error
949   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
950
951   int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
952   wchar_t* buffer = new wchar_t[wn];
953   if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0)
954     {
955       delete[] buffer;
956       return Kumu::RESULT_FAIL;
957     }
958
959   m_Handle = ::CreateFileW(buffer,
960                           (GENERIC_WRITE|GENERIC_READ),  // open for reading
961                           FILE_SHARE_READ,               // share for reading
962                           NULL,                          // no security
963                           CREATE_ALWAYS,                 // overwrite (beware!)
964                           FILE_ATTRIBUTE_NORMAL,         // normal file
965                           NULL                           // no template file
966                           );
967
968   delete[] buffer;
969   HRESULT const last_error = GetLastError();
970
971   ::SetErrorMode(prev);
972
973   if (m_Handle == INVALID_HANDLE_VALUE)
974     {
975       DefaultLogSink().Error("CreateFileW failed: %lu\n", last_error);
976       return Kumu::RESULT_FILEOPEN;
977     }
978
979   m_IOVec = new h__iovec;
980   return Kumu::RESULT_OK;
981 }
982
983 /** @param filename File name (UTF-8 encoded) */
984 Kumu::Result_t
985 Kumu::FileWriter::OpenModify(const std::string& filename)
986 {
987   m_Filename = filename;
988
989   // suppress popup window on error
990   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
991
992   int const wn = MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, 0, 0);
993   wchar_t* buffer = new wchar_t[wn];
994   if (MultiByteToWideChar (CP_UTF8, 0, filename.c_str(), -1, buffer, wn) == 0) {
995     delete[] buffer;
996     return Kumu::RESULT_FAIL;
997   }
998
999   m_Handle = ::CreateFileW(buffer,
1000                           (GENERIC_WRITE|GENERIC_READ),  // open for reading
1001                           FILE_SHARE_READ,               // share for reading
1002                           NULL,                          // no security
1003                           OPEN_ALWAYS,                   // don't truncate existing
1004                           FILE_ATTRIBUTE_NORMAL,         // normal file
1005                           NULL                           // no template file
1006                           );
1007
1008   delete[] buffer;
1009   HRESULT const last_error = GetLastError();
1010
1011   ::SetErrorMode(prev);
1012
1013   if (m_Handle == INVALID_HANDLE_VALUE)
1014     {
1015       DefaultLogSink().Error("CreateFileW failed: %lu\n", last_error);
1016       return Kumu::RESULT_FILEOPEN;
1017     }
1018
1019   m_IOVec = new h__iovec;
1020   return Kumu::RESULT_OK;
1021 }
1022
1023
1024 //
1025 Kumu::Result_t
1026 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1027 {
1028   assert( ! m_IOVec.empty() );
1029   register h__iovec* iov = m_IOVec;
1030   ui32_t tmp_int;
1031
1032   if ( bytes_written == 0 )
1033     bytes_written = &tmp_int;
1034
1035   if ( m_Handle == INVALID_HANDLE_VALUE )
1036     return Kumu::RESULT_STATE;
1037
1038   *bytes_written = 0;
1039   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1040   Result_t result = Kumu::RESULT_OK;
1041
1042   // AFAIK, there is no writev() equivalent in the win32 API
1043   for ( register int i = 0; i < iov->m_Count; i++ )
1044     {
1045       ui32_t tmp_count = 0;
1046       BOOL wr_result = ::WriteFile(m_Handle,
1047                                    iov->m_iovec[i].iov_base,
1048                                    iov->m_iovec[i].iov_len,
1049                                    (DWORD*)&tmp_count,
1050                                    NULL);
1051
1052       if ( wr_result == 0 || tmp_count != iov->m_iovec[i].iov_len)
1053         {
1054           DefaultLogSink().Error("Writev failed (%d)", wr_result);
1055           result = Kumu::RESULT_WRITEFAIL;
1056           break;
1057         }
1058
1059       MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1060       *bytes_written += tmp_count;
1061     }
1062
1063   ::SetErrorMode(prev);
1064   iov->m_Count = 0; // error nor not, all is lost
1065
1066   return result;
1067 }
1068
1069 //
1070 Kumu::Result_t
1071 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1072 {
1073   KM_TEST_NULL_L(buf);
1074   ui32_t tmp_int;
1075
1076   if ( bytes_written == 0 )
1077     bytes_written = &tmp_int;
1078
1079   if ( m_Handle == INVALID_HANDLE_VALUE )
1080     return Kumu::RESULT_STATE;
1081
1082   // suppress popup window on error
1083   UINT prev = ::SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
1084   BOOL result = ::WriteFile(m_Handle, buf, buf_len, (DWORD*)bytes_written, NULL);
1085   ::SetErrorMode(prev);
1086
1087   if ( result == 0 || *bytes_written != buf_len )
1088     {
1089       DefaultLogSink().Error("WriteFile failed (%d)", result);
1090       return Kumu::RESULT_WRITEFAIL;
1091     }
1092
1093   MaybeHash(buf, buf_len);
1094
1095   return Kumu::RESULT_OK;
1096 }
1097
1098 #else // KM_WIN32
1099 //------------------------------------------------------------------------------------------
1100 // POSIX
1101
1102 //
1103 Kumu::Result_t
1104 Kumu::FileReader::OpenRead(const std::string& filename) const
1105 {
1106   const_cast<FileReader*>(this)->m_Filename = filename;
1107   const_cast<FileReader*>(this)->m_Handle = open(filename.c_str(), O_RDONLY, 0);
1108   return ( m_Handle == -1L ) ? RESULT_FILEOPEN : RESULT_OK;
1109 }
1110
1111 //
1112 Kumu::Result_t
1113 Kumu::FileReader::Close() const
1114 {
1115   if ( m_Handle == -1L )
1116     return RESULT_FILEOPEN;
1117
1118   close(m_Handle);
1119   const_cast<FileReader*>(this)->m_Handle = -1L;
1120   return RESULT_OK;
1121 }
1122
1123 //
1124 Kumu::Result_t
1125 Kumu::FileReader::Seek(Kumu::fpos_t position, SeekPos_t whence) const
1126 {
1127   if ( m_Handle == -1L )
1128     return RESULT_FILEOPEN;
1129
1130   if ( lseek(m_Handle, position, whence) == -1L )
1131     return RESULT_BADSEEK;
1132
1133   return RESULT_OK;
1134 }
1135
1136 //
1137 Kumu::Result_t
1138 Kumu::FileReader::Tell(Kumu::fpos_t* pos) const
1139 {
1140   KM_TEST_NULL_L(pos);
1141
1142   if ( m_Handle == -1L )
1143     return RESULT_FILEOPEN;
1144
1145   Kumu::fpos_t tmp_pos;
1146
1147   if (  (tmp_pos = lseek(m_Handle, 0, SEEK_CUR)) == -1 )
1148     return RESULT_READFAIL;
1149
1150   *pos = tmp_pos;
1151   return RESULT_OK;
1152 }
1153
1154 //
1155 Kumu::Result_t
1156 Kumu::FileReader::Read(byte_t* buf, ui32_t buf_len, ui32_t* read_count) const
1157 {
1158   KM_TEST_NULL_L(buf);
1159   i32_t  tmp_count = 0;
1160   ui32_t tmp_int = 0;
1161
1162   if ( read_count == 0 )
1163     read_count = &tmp_int;
1164
1165   *read_count = 0;
1166
1167   if ( m_Handle == -1L )
1168     return RESULT_FILEOPEN;
1169
1170   if ( (tmp_count = read(m_Handle, buf, buf_len)) == -1L )
1171     return RESULT_READFAIL;
1172
1173   *read_count = tmp_count;
1174   return (tmp_count == 0 ? RESULT_ENDOFFILE : RESULT_OK);
1175 }
1176
1177
1178 //------------------------------------------------------------------------------------------
1179 //
1180
1181 //
1182 Kumu::Result_t
1183 Kumu::FileWriter::OpenWrite(const std::string& filename)
1184 {
1185   m_Filename = filename;
1186   m_Handle = open(filename.c_str(), O_RDWR|O_CREAT|O_TRUNC, 0664);
1187
1188   if ( m_Handle == -1L )
1189     {
1190       DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1191       return RESULT_FILEOPEN;
1192     }
1193
1194   m_IOVec = new h__iovec;
1195   return RESULT_OK;
1196 }
1197
1198 //
1199 Kumu::Result_t
1200 Kumu::FileWriter::OpenModify(const std::string& filename)
1201 {
1202   m_Filename = filename;
1203   m_Handle = open(filename.c_str(), O_RDWR|O_CREAT, 0664);
1204
1205   if ( m_Handle == -1L )
1206     {
1207       DefaultLogSink().Error("Error opening file %s: %s\n", filename.c_str(), strerror(errno));
1208       return RESULT_FILEOPEN;
1209     }
1210
1211   m_IOVec = new h__iovec;
1212   return RESULT_OK;
1213 }
1214
1215 //
1216 Kumu::Result_t
1217 Kumu::FileWriter::Writev(ui32_t* bytes_written)
1218 {
1219   assert( ! m_IOVec.empty() );
1220   h__iovec* iov = m_IOVec;
1221   ui32_t tmp_int;
1222
1223   if ( bytes_written == 0 )
1224     bytes_written = &tmp_int;
1225
1226   if ( m_Handle == -1L )
1227     return RESULT_STATE;
1228
1229   int total_size = 0;
1230   for ( int i = 0; i < iov->m_Count; i++ )
1231     total_size += iov->m_iovec[i].iov_len;
1232
1233   int write_size = writev(m_Handle, iov->m_iovec, iov->m_Count);
1234
1235   if ( write_size == -1L || write_size != total_size )
1236     {
1237       DefaultLogSink().Error("writev failed (%d)", errno);
1238       return RESULT_WRITEFAIL;
1239     }
1240
1241   for (int i = 0; i < iov->m_Count; ++i)
1242     {
1243       MaybeHash(iov->m_iovec[i].iov_base, iov->m_iovec[i].iov_len);
1244     }
1245
1246   iov->m_Count = 0;
1247   *bytes_written = write_size;
1248   return RESULT_OK;
1249 }
1250
1251 //
1252 Kumu::Result_t
1253 Kumu::FileWriter::Write(const byte_t* buf, ui32_t buf_len, ui32_t* bytes_written)
1254 {
1255   KM_TEST_NULL_L(buf);
1256   ui32_t tmp_int;
1257
1258   if ( bytes_written == 0 )
1259     bytes_written = &tmp_int;
1260
1261   if ( m_Handle == -1L )
1262     return RESULT_STATE;
1263
1264   int write_size = write(m_Handle, buf, buf_len);
1265   MaybeHash(buf, buf_len);
1266
1267   if ( write_size == -1L || (ui32_t)write_size != buf_len )
1268     {
1269       DefaultLogSink().Error("write failed (%d)", errno);
1270       return RESULT_WRITEFAIL;
1271     }
1272
1273   *bytes_written = write_size;
1274   return RESULT_OK;
1275 }
1276
1277
1278 #endif // KM_WIN32
1279
1280 //------------------------------------------------------------------------------------------
1281
1282
1283 //
1284 Kumu::Result_t
1285 Kumu::ReadFileIntoString(const std::string& filename, std::string& outString, ui32_t max_size)
1286 {
1287   fsize_t    fsize = 0;
1288   ui32_t     read_size = 0;
1289   FileReader File;
1290   ByteString ReadBuf;
1291
1292   Result_t result = File.OpenRead(filename);
1293
1294   if ( KM_SUCCESS(result) )
1295     {
1296       fsize = File.Size();
1297
1298       if ( fsize > (Kumu::fpos_t)max_size )
1299         {
1300           DefaultLogSink().Error("%s: exceeds available buffer size (%u)\n", filename.c_str(), max_size);
1301           return RESULT_ALLOC;
1302         }
1303
1304       if ( fsize == 0 )
1305         {
1306           DefaultLogSink().Error("%s: zero file size\n", filename.c_str());
1307           return RESULT_READFAIL;
1308         }
1309
1310       result = ReadBuf.Capacity((ui32_t)fsize);
1311     }
1312
1313   if ( KM_SUCCESS(result) )
1314     result = File.Read(ReadBuf.Data(), ReadBuf.Capacity(), &read_size);
1315
1316   if ( KM_SUCCESS(result) )
1317     outString.assign((const char*)ReadBuf.RoData(), read_size);
1318
1319   return result;
1320 }
1321
1322
1323 //
1324 Kumu::Result_t
1325 Kumu::WriteStringIntoFile(const std::string& filename, const std::string& inString)
1326 {
1327   FileWriter File;
1328   ui32_t write_count = 0;
1329
1330   Result_t result = File.OpenWrite(filename);
1331
1332   if ( KM_SUCCESS(result) )
1333     result = File.Write((byte_t*)inString.c_str(), inString.length(), &write_count);
1334
1335   return result;
1336 }
1337
1338 //------------------------------------------------------------------------------------------
1339
1340
1341 //
1342 Kumu::Result_t
1343 Kumu::ReadFileIntoObject(const std::string& Filename, Kumu::IArchive& Object, ui32_t)
1344 {
1345   ByteString Buffer;
1346   ui32_t file_size = static_cast<ui32_t>(FileSize(Filename));
1347   Result_t result = Buffer.Capacity(file_size);
1348
1349   if ( KM_SUCCESS(result) )
1350     {
1351       ui32_t read_count = 0;
1352       FileWriter Reader;
1353
1354       result = Reader.OpenRead(Filename);
1355
1356       if ( KM_SUCCESS(result) )
1357         result = Reader.Read(Buffer.Data(), file_size, &read_count);
1358
1359       if ( KM_SUCCESS(result) )
1360         {
1361           assert(file_size == read_count);
1362           Buffer.Length(read_count);
1363           MemIOReader MemReader(&Buffer);
1364           result = Object.Unarchive(&MemReader) ? RESULT_OK : RESULT_READFAIL;
1365         }
1366     }
1367
1368   return result;
1369 }
1370
1371 //
1372 Kumu::Result_t
1373 Kumu::WriteObjectIntoFile(const Kumu::IArchive& Object, const std::string& Filename)
1374 {
1375   ByteString Buffer;
1376   Result_t result = Buffer.Capacity(Object.ArchiveLength());
1377
1378   if ( KM_SUCCESS(result) )
1379     {
1380       ui32_t write_count = 0;
1381       FileWriter Writer;
1382       MemIOWriter MemWriter(&Buffer);
1383
1384       result = Object.Archive(&MemWriter) ? RESULT_OK : RESULT_WRITEFAIL;
1385
1386       if ( KM_SUCCESS(result) )
1387         {
1388           Buffer.Length(MemWriter.Length());
1389           result = Writer.OpenWrite(Filename);
1390         }
1391       else
1392         {
1393           DefaultLogSink().Error("Object.Archive() failed in WriteObjectIntoFile()");
1394         }
1395
1396       if ( KM_SUCCESS(result) )
1397         result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1398     }
1399
1400   return result;
1401 }
1402
1403 //------------------------------------------------------------------------------------------
1404 //
1405
1406 //
1407 Result_t
1408 Kumu::ReadFileIntoBuffer(const std::string& Filename, Kumu::ByteString& Buffer, ui32_t)
1409 {
1410   ui32_t file_size = FileSize(Filename);
1411   Result_t result = Buffer.Capacity(file_size);
1412
1413   if ( KM_SUCCESS(result) )
1414     {
1415       ui32_t read_count = 0;
1416       FileWriter Reader;
1417
1418       result = Reader.OpenRead(Filename);
1419
1420       if ( KM_SUCCESS(result) )
1421         result = Reader.Read(Buffer.Data(), file_size, &read_count);
1422
1423       if ( KM_SUCCESS(result) )
1424         {
1425           if ( file_size != read_count)
1426             return RESULT_READFAIL;
1427
1428           Buffer.Length(read_count);
1429         }
1430     }
1431
1432   return result;
1433 }
1434
1435 //
1436 Result_t
1437 Kumu::WriteBufferIntoFile(const Kumu::ByteString& Buffer, const std::string& Filename)
1438 {
1439   ui32_t write_count = 0;
1440   FileWriter Writer;
1441
1442   Result_t result = Writer.OpenWrite(Filename);
1443
1444   if ( KM_SUCCESS(result) )
1445     result = Writer.Write(Buffer.RoData(), Buffer.Length(), &write_count);
1446
1447   if ( KM_SUCCESS(result) && Buffer.Length() != write_count)
1448   {
1449     DefaultLogSink().Error("WriteBufferIntoFile failed (%d)", result.Value());
1450     return RESULT_WRITEFAIL;
1451   }
1452
1453   return result;
1454 }
1455
1456 //------------------------------------------------------------------------------------------
1457 //
1458
1459 Kumu::DirScanner::DirScanner()
1460   : _iterator(boost::filesystem::directory_iterator())
1461 {
1462 }
1463
1464
1465 Result_t
1466 Kumu::DirScanner::Open(const std::string& filename)
1467 {
1468   if (!boost::filesystem::is_directory(filename))
1469     return RESULT_NOT_FOUND;
1470
1471   _iterator = boost::filesystem::directory_iterator(filename);
1472   return RESULT_OK;
1473 }
1474
1475
1476 //
1477 Result_t
1478 Kumu::DirScanner::GetNext(char* filename)
1479 {
1480   if (_iterator == boost::filesystem::directory_iterator())
1481     return RESULT_ENDOFFILE;
1482
1483 #if BOOST_FILESYSTEM_VERSION == 3
1484   std::string f = boost::filesystem::path(*_iterator).filename().generic_string();
1485 #else
1486   std::string f = boost::filesystem::path(*_iterator).filename();
1487 #endif
1488   strncpy(filename, f.c_str(), MaxFilePath);
1489   ++_iterator;
1490   return RESULT_OK;
1491 }
1492
1493 //------------------------------------------------------------------------------------------
1494
1495 //
1496 // Attention Windows users: make sure to use the proper separator character
1497 // with these functions.
1498 //
1499
1500 // given a path string, create any missing directories so that PathIsDirectory(Path) is true.
1501 //
1502 Result_t
1503 Kumu::CreateDirectoriesInPath(const std::string& Path)
1504 {
1505   bool abs = PathIsAbsolute(Path);
1506   PathCompList_t PathComps, TmpPathComps;
1507
1508   PathToComponents(Path, PathComps);
1509
1510   while ( ! PathComps.empty() )
1511     {
1512       TmpPathComps.push_back(PathComps.front());
1513       PathComps.pop_front();
1514       std::string tmp_path = abs ? ComponentsToAbsolutePath(TmpPathComps) : ComponentsToPath(TmpPathComps);
1515
1516       if ( ! PathIsDirectory(tmp_path) )
1517         {
1518 #ifdef KM_WIN32
1519           if ( _mkdir(tmp_path.c_str()) != 0 )
1520 #else // KM_WIN32
1521           if ( mkdir(tmp_path.c_str(), 0775) != 0 )
1522 #endif // KM_WIN32
1523             {
1524               DefaultLogSink().Error("CreateDirectoriesInPath mkdir %s: %s\n",
1525                                      tmp_path.c_str(), strerror(errno));
1526               return RESULT_DIR_CREATE;
1527             }
1528         }
1529     }
1530
1531   return RESULT_OK;
1532 }
1533
1534
1535 //
1536 Result_t
1537 Kumu::DeleteFile(const std::string& filename)
1538 {
1539   if ( _unlink(filename.c_str()) == 0 )
1540     return RESULT_OK;
1541
1542   switch ( errno )
1543     {
1544     case ENOENT:
1545     case ENOTDIR: return RESULT_NOTAFILE;
1546
1547     case EROFS:
1548     case EBUSY:
1549     case EACCES:
1550     case EPERM:   return RESULT_NO_PERM;
1551     }
1552
1553   DefaultLogSink().Error("DeleteFile %s: %s\n", filename.c_str(), strerror(errno));
1554   return RESULT_FAIL;
1555 }
1556
1557 //
1558 Result_t
1559 h__DeletePath(const std::string& pathname)
1560 {
1561   if ( pathname.empty() )
1562     return RESULT_NULL_STR;
1563
1564   Result_t result = RESULT_OK;
1565
1566   if ( ! PathIsDirectory(pathname) )
1567     {
1568       result = DeleteFile(pathname);
1569     }
1570   else
1571     {
1572       {
1573         DirScanner TestDir;
1574         char       next_file[Kumu::MaxFilePath];
1575         result = TestDir.Open(pathname.c_str());
1576
1577         while ( KM_SUCCESS(result) && KM_SUCCESS(TestDir.GetNext(next_file)) )
1578           {
1579             if ( next_file[0] == '.' )
1580               {
1581                 if ( next_file[1] ==  0 )
1582                   continue; // don't delete 'this'
1583
1584                 if ( next_file[1] == '.' && next_file[2] ==  0 )
1585                   continue; // don't delete 'this' parent
1586               }
1587
1588             result = h__DeletePath(pathname + std::string("/") + next_file);
1589           }
1590       }
1591
1592       if ( _rmdir(pathname.c_str()) != 0 )
1593         {
1594           switch ( errno )
1595             {
1596             case ENOENT:
1597             case ENOTDIR:
1598               result = RESULT_NOTAFILE;
1599               break;
1600
1601             case EROFS:
1602             case EBUSY:
1603             case EACCES:
1604             case EPERM:
1605               result = RESULT_NO_PERM;
1606               break;
1607
1608             default:
1609               DefaultLogSink().Error("DeletePath %s: %s\n", pathname.c_str(), strerror(errno));
1610               result = RESULT_FAIL;
1611             }
1612         }
1613     }
1614
1615   return result;
1616 }
1617
1618 //
1619 Result_t
1620 Kumu::DeletePath(const std::string& pathname)
1621 {
1622   std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
1623   DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
1624   return h__DeletePath(c_pathname);
1625 }
1626
1627
1628 //
1629 Result_t
1630 Kumu::DeleteDirectoryIfEmpty(const std::string& path)
1631 {
1632   DirScanner source_dir;
1633   char next_file[Kumu::MaxFilePath];
1634
1635   Result_t result = source_dir.Open(path);
1636
1637   if ( KM_FAILURE(result) )
1638     return result;
1639
1640   while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
1641     {
1642       if ( ( next_file[0] == '.' && next_file[1] == 0 )
1643            || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
1644         continue;
1645
1646       return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
1647     }
1648
1649   return DeletePath(path);
1650 }
1651
1652
1653 //------------------------------------------------------------------------------------------
1654 //
1655
1656
1657 Result_t
1658 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
1659 {
1660 #ifdef KM_WIN32
1661   ULARGE_INTEGER lTotalNumberOfBytes;
1662   ULARGE_INTEGER lTotalNumberOfFreeBytes;
1663
1664   BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
1665   if ( fResult )
1666     {
1667       free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
1668       total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
1669       return RESULT_OK;
1670     }
1671
1672   HRESULT last_error = ::GetLastError();
1673
1674   DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
1675   return RESULT_FAIL;
1676 #else // KM_WIN32
1677   struct statfs s;
1678
1679   if ( statfs(path.c_str(), &s) == 0 )
1680     {
1681       if ( s.f_blocks < 1 )
1682         {
1683           DefaultLogSink().Error("File system %s has impossible size: %ld\n",
1684                                  path.c_str(), s.f_blocks);
1685           return RESULT_FAIL;
1686         }
1687
1688       free_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_bavail;
1689       total_space = (Kumu::fsize_t)s.f_bsize * (Kumu::fsize_t)s.f_blocks;
1690       return RESULT_OK;
1691     }
1692
1693   switch ( errno )
1694     {
1695     case ENOENT:
1696     case ENOTDIR: return RESULT_NOTAFILE;
1697     case EACCES:  return RESULT_NO_PERM;
1698     }
1699
1700   DefaultLogSink().Error("FreeSpaceForPath statfs %s: %s\n", path.c_str(), strerror(errno));
1701   return RESULT_FAIL;
1702 #endif // KM_WIN32
1703 }
1704
1705
1706 //
1707 // end KM_fileio.cpp
1708 //