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