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