1 //  This file is part of par2cmdline (a PAR 2.0 compatible file verification and
2 //  repair tool). See http://parchive.sourceforge.net for details of PAR 2.0.
3 //
4 //  Copyright (c) 2003 Peter Brian Clements
5 //  Copyright (c) 2019 Michael D. Nahas
6 //
7 //  par2cmdline is free software; you can redistribute it and/or modify
8 //  it under the terms of the GNU General Public License as published by
9 //  the Free Software Foundation; either version 2 of the License, or
10 //  (at your option) any later version.
11 //
12 //  par2cmdline is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 
21 #include "libpar2internal.h"
22 
23 #ifdef _MSC_VER
24 #ifdef _DEBUG
25 #undef THIS_FILE
26 static char THIS_FILE[]=__FILE__;
27 #define new DEBUG_NEW
28 #endif
29 #endif
30 
31 #if defined(__FreeBSD_kernel__)
32 #include <sys/disk.h>
33 #define BLKGETSIZE64 DIOCGMEDIASIZE
34 #endif
35 
36 
37 #ifdef _WIN32
38 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
39 
40 #define OffsetType __int64
41 #define MaxOffset 0x7fffffffffffffffI64
42 
DiskFile(std::ostream & sout,std::ostream & serr)43 DiskFile::DiskFile(std::ostream &sout, std::ostream &serr)
44 : sout(&sout)
45 , serr(&serr)
46 {
47   filename = "";
48   filesize = 0;
49   offset = 0;
50 
51   hFile = INVALID_HANDLE_VALUE;
52 
53   exists = false;
54 }
55 
56 
~DiskFile(void)57 DiskFile::~DiskFile(void)
58 {
59   if (hFile != INVALID_HANDLE_VALUE)
60     ::CloseHandle(hFile);
61 }
62 
CreateParentDirectory(string _pathname)63 bool DiskFile::CreateParentDirectory(string _pathname)
64 {
65   // do we have a path separator in the filename ?
66   string::size_type where;
67   if (string::npos != (where = _pathname.find_last_of('/')) ||
68       string::npos != (where = _pathname.find_last_of('\\')))
69   {
70     string path = filename.substr(0, where);
71 
72     struct stat st;
73     if (stat(path.c_str(), &st) == 0)
74       return true; // let the caller deal with non-directories
75 
76     if (!DiskFile::CreateParentDirectory(path))
77       return false;
78 
79     if (!CreateDirectory(path.c_str(), NULL))
80     {
81       DWORD error = ::GetLastError();
82 
83       *serr << "Could not create the " << path << " directory: " << ErrorMessage(error) << endl;
84 
85       return false;
86     }
87   }
88   return true;
89 }
90 
91 // Create new file on disk and make sure that there is enough
92 // space on disk for it.
Create(string _filename,u64 _filesize)93 bool DiskFile::Create(string _filename, u64 _filesize)
94 {
95   assert(hFile == INVALID_HANDLE_VALUE);
96 
97   filename = _filename;
98   filesize = _filesize;
99 
100   if (!DiskFile::CreateParentDirectory(filename))
101     return false;
102 
103   // Create the file
104   hFile = ::CreateFileA(_filename.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
105   if (hFile == INVALID_HANDLE_VALUE)
106   {
107     DWORD error = ::GetLastError();
108 
109     *serr << "Could not create \"" << _filename << "\": " << ErrorMessage(error) << endl;
110 
111     return false;
112   }
113 
114   if (filesize > 0)
115   {
116     // Seek to the end of the file
117     LONG* ptrfilesize = (LONG*)&filesize;
118     LONG lowoffset = ptrfilesize[0];
119     LONG highoffset = ptrfilesize[1];
120 
121     if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, lowoffset, &highoffset, FILE_BEGIN))
122     {
123       DWORD error = ::GetLastError();
124 
125       *serr << "Could not set size of \"" << _filename << "\": " << ErrorMessage(error) << endl;
126 
127       ::CloseHandle(hFile);
128       hFile = INVALID_HANDLE_VALUE;
129       ::DeleteFile(_filename.c_str());
130 
131       return false;
132     }
133 
134     // Set the end of the file
135     if (!::SetEndOfFile(hFile))
136     {
137       DWORD error = ::GetLastError();
138 
139       *serr << "Could not set size of \"" << _filename << "\": " << ErrorMessage(error) << endl;
140 
141       ::CloseHandle(hFile);
142       hFile = INVALID_HANDLE_VALUE;
143       ::DeleteFile(_filename.c_str());
144 
145       return false;
146     }
147   }
148 
149   offset = filesize;
150 
151   exists = true;
152   return true;
153 }
154 
155 // Write some data to disk
156 
Write(u64 _offset,const void * buffer,size_t length,LengthType maxlength)157 bool DiskFile::Write(u64 _offset, const void *buffer, size_t length, LengthType maxlength)
158 {
159   assert(hFile != INVALID_HANDLE_VALUE);
160 
161   if (offset != _offset)
162   {
163     LONG* ptroffset = (LONG*)&_offset;
164     LONG lowoffset = ptroffset[0];
165     LONG highoffset = ptroffset[1];
166 
167     // Seek to the required offset
168     if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, lowoffset, &highoffset, FILE_BEGIN))
169     {
170       DWORD error = ::GetLastError();
171 
172       *serr << "Could not write " << (u64)length << " bytes to \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << endl;
173 
174       return false;
175     }
176     offset = _offset;
177   }
178 
179 
180   while (length > 0) {
181 
182     DWORD write;
183     if (length > maxlength)
184       write = maxlength;
185     else
186       write = (LengthType) length;
187     DWORD wrote = 0;
188 
189     // Write the data
190     if (!::WriteFile(hFile, buffer, write, &wrote, NULL))
191     {
192       DWORD error = ::GetLastError();
193 
194       *serr << "Could not write " << write << " bytes to \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << endl;
195 
196       return false;
197     }
198 
199     if (wrote != write)
200     {
201       *serr << "INFO: Incomplete write to \"" << filename << "\" at offset " << _offset << ".  Expected to write " << write << " bytes and wrote " << wrote << " bytes." << endl;
202     }
203 
204     offset += wrote;
205     length -= wrote;
206     buffer = ((char *) buffer) + wrote;
207 
208     if (filesize < offset)
209     {
210       filesize = offset;
211     }
212   }
213 
214   return true;
215 }
216 
217 // Open the file
218 
Open(const string & _filename,u64 _filesize)219 bool DiskFile::Open(const string &_filename, u64 _filesize)
220 {
221   assert(hFile == INVALID_HANDLE_VALUE);
222 
223   filename = _filename;
224   filesize = _filesize;
225 
226   hFile = ::CreateFileA(_filename.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
227   if (hFile == INVALID_HANDLE_VALUE)
228   {
229     DWORD error = ::GetLastError();
230 
231     switch (error)
232     {
233     case ERROR_FILE_NOT_FOUND:
234     case ERROR_PATH_NOT_FOUND:
235       break;
236     default:
237       *serr << "Could not open \"" << _filename << "\": " << ErrorMessage(error) << endl;
238     }
239 
240     return false;
241   }
242 
243   offset = 0;
244   exists = true;
245 
246   return true;
247 }
248 
249 // Read some data from disk
250 
Read(u64 _offset,void * buffer,size_t length,LengthType maxlength)251 bool DiskFile::Read(u64 _offset, void *buffer, size_t length, LengthType maxlength)
252 {
253   assert(hFile != INVALID_HANDLE_VALUE);
254 
255   if (offset != _offset)
256   {
257     LONG* ptroffset = (LONG*)&_offset;
258     LONG lowoffset = ptroffset[0];
259     LONG highoffset = ptroffset[1];
260 
261     // Seek to the required offset
262     if (INVALID_SET_FILE_POINTER == SetFilePointer(hFile, lowoffset, &highoffset, FILE_BEGIN))
263     {
264       DWORD error = ::GetLastError();
265 
266       *serr << "Could not read " << (u64)length << " bytes from \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << endl;
267 
268       return false;
269     }
270     offset = _offset;
271   }
272 
273   while (length > 0) {
274 
275     DWORD want;
276     if (length > maxlength)
277       want = maxlength;
278     else
279       want = (LengthType)length;
280     DWORD got = 0;
281 
282     // Read the data
283     if (!::ReadFile(hFile, buffer, want, &got, NULL))
284     {
285       DWORD error = ::GetLastError();
286 
287       *serr << "Could not read " << (u64)length << " bytes from \"" << filename << "\" at offset " << _offset << ": " << ErrorMessage(error) << endl;
288 
289       return false;
290     }
291 
292     if (want != got)
293     {
294       *serr << "Incomplete read from \"" << filename << "\" at offset " << offset << ".  Tried to read " << want << " bytes and received " << got << " bytes." << endl;
295     }
296 
297     offset += got;
298     length -= got;
299     buffer = ((char *) buffer) + got;
300 
301     // write updates filesize.  Do we want to do that here?
302   }
303 
304   return true;
305 }
306 
Close(void)307 void DiskFile::Close(void)
308 {
309   if (hFile != INVALID_HANDLE_VALUE)
310   {
311     ::CloseHandle(hFile);
312     hFile = INVALID_HANDLE_VALUE;
313   }
314 }
315 
GetCanonicalPathname(string filename)316 string DiskFile::GetCanonicalPathname(string filename)
317 {
318   char fullname[MAX_PATH];
319   char *filepart;
320 
321   // Resolve a relative path to a full path
322   unsigned int length = ::GetFullPathName(filename.c_str(), sizeof(fullname), fullname, &filepart);
323   if (length <= 0 || sizeof(fullname) < length)
324     return filename;
325 
326   // Make sure the drive letter is upper case.
327   fullname[0] = toupper(fullname[0]);
328 
329   // Translate all /'s to \'s
330   char *current = strchr(fullname, '/');
331   while (current)
332   {
333     *current++ = '\\';
334     current  = strchr(current, '/');
335   }
336 
337   // Copy the root directory to the output string
338   string longname(fullname, 3);
339 
340   // Start processing at the first path component
341   current = &fullname[3];
342   char *limit = &fullname[length];
343 
344   // Process until we reach the end of the full name
345   while (current < limit)
346   {
347     char *tail;
348 
349     // Find the next \, or the end of the string
350     (tail = strchr(current, '\\')) || (tail = limit);
351     *tail = 0;
352 
353     // Create a wildcard to search for the path
354     string wild = longname + current;
355     WIN32_FIND_DATA finddata;
356     HANDLE hFind = ::FindFirstFile(wild.c_str(), &finddata);
357     if (hFind == INVALID_HANDLE_VALUE)
358     {
359       // If the component was not found then just copy the rest of the path to the
360       // output buffer verbatim.
361       longname += current;
362       break;
363     }
364     ::FindClose(hFind);
365 
366     // Copy the component found to the output
367     longname += finddata.cFileName;
368 
369     current = tail + 1;
370 
371     // If we have not reached the end of the name, add a "\"
372     if (current < limit)
373       longname += '\\';
374   }
375 
376   return longname;
377 }
378 
FindFiles(string path,string wildcard,bool recursive)379 std::unique_ptr< list<string> > DiskFile::FindFiles(string path, string wildcard, bool recursive)
380 {
381   // check path, if not ending with path separator, add one
382   char pathend = *path.rbegin();
383   if (pathend != '\\')
384   {
385     path += '\\';
386   }
387   list<string> *matches = new list<string>;
388 
389   wildcard = path + wildcard;
390   WIN32_FIND_DATA fd;
391   HANDLE h = ::FindFirstFile(wildcard.c_str(), &fd);
392   if (h != INVALID_HANDLE_VALUE)
393   {
394     do
395     {
396       if (0 == (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
397       {
398         matches->push_back(path + fd.cFileName);
399       }
400       else if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
401       {
402         if (fd.cFileName[0] == '.') {
403           continue;
404         }
405 
406         string nwwildcard="*";
407 	std::unique_ptr< list<string> > dirmatches(
408 						 DiskFile::FindFiles(fd.cFileName, nwwildcard, true)
409 						 );
410 
411         matches->merge(*dirmatches);
412       }
413     } while (::FindNextFile(h, &fd));
414     ::FindClose(h);
415   }
416 
417   return std::unique_ptr< list<string> >(matches);
418 }
419 
GetFileSize(string filename)420 u64 DiskFile::GetFileSize(string filename)
421 {
422   struct _stati64 st;
423   if ((0 == _stati64(filename.c_str(), &st)) && (0 != (st.st_mode & S_IFREG)))
424   {
425     return st.st_size;
426   }
427   else
428   {
429     return 0;
430   }
431 }
432 
FileExists(string filename)433 bool DiskFile::FileExists(string filename)
434 {
435   struct _stati64 st;
436   return ((0 == _stati64(filename.c_str(), &st)) && (0 != (st.st_mode & S_IFREG)));
437 }
438 
439 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
440 #else // !_WIN32
441 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
442 
443 #ifdef HAVE_FSEEKO
444 # define OffsetType off_t
445 # define MaxOffset ((off_t)0x7fffffffffffffffULL)
446 # define fseek fseeko
447 #else
448 # if _FILE_OFFSET_BITS == 64
449 #  define OffsetType unsigned long long
450 #  define MaxOffset 0x7fffffffffffffffULL
451 # else
452 #  define OffsetType long
453 #  define MaxOffset 0x7fffffffUL
454 # endif
455 #endif
456 
457 
DiskFile(std::ostream & sout,std::ostream & serr)458 DiskFile::DiskFile(std::ostream &sout, std::ostream &serr)
459 : sout(&sout)
460 , serr(&serr)
461 {
462   //filename;
463   filesize = 0;
464   offset = 0;
465 
466   file = 0;
467 
468   exists = false;
469 }
470 
471 
~DiskFile(void)472 DiskFile::~DiskFile(void)
473 {
474   if (file != 0)
475     fclose(file);
476 }
477 
CreateParentDirectory(string _pathname)478 bool DiskFile::CreateParentDirectory(string _pathname)
479 {
480   // do we have a path separator in the filename ?
481   string::size_type where;
482   if (string::npos != (where = _pathname.find_last_of('/')) ||
483       string::npos != (where = _pathname.find_last_of('\\')))
484   {
485     string path = filename.substr(0, where);
486 
487     struct stat st;
488     if (stat(path.c_str(), &st) == 0)
489       return true; // let the caller deal with non-directories
490 
491     if (!DiskFile::CreateParentDirectory(path))
492       return false;
493 
494     if (mkdir(path.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH))
495     {
496       *serr << "Could not create the " << path << " directory: " << strerror(errno) << endl;
497       return false;
498     }
499   }
500   return true;
501 }
502 
503 // Create new file on disk and make sure that there is enough
504 // space on disk for it.
Create(string _filename,u64 _filesize)505 bool DiskFile::Create(string _filename, u64 _filesize)
506 {
507   assert(file == 0);
508 
509   filename = _filename;
510   filesize = _filesize;
511 
512   if (!DiskFile::CreateParentDirectory(filename))
513     return false;
514 
515   // This is after CreateParentDirectory because
516   // the Windows code would error out after too.
517   if (FileExists(filename))
518   {
519     *serr << "Could not create \"" << _filename << "\": File already exists." << endl;
520     return false;
521   }
522 
523   file = fopen(_filename.c_str(), "wb");
524   if (file == 0)
525   {
526     *serr << "Could not create " << _filename << ": " << strerror(errno) << endl;
527 
528     return false;
529   }
530 
531   if (_filesize > (u64)MaxOffset)
532   {
533     *serr << "Requested file size for " << _filename << " is too large." << endl;
534     return false;
535   }
536 
537   if (_filesize > 0)
538   {
539     if (fseek(file, (OffsetType)_filesize-1, SEEK_SET))
540     {
541       *serr << "Could not set end of file of " << _filename << ": " << strerror(errno) << endl;
542 
543       fclose(file);
544       file = 0;
545       ::remove(filename.c_str());
546       return false;
547     }
548 
549     if (1 != fwrite(&_filesize, 1, 1, file))
550     {
551       *serr << "Could not set end of file of " << _filename << ": " << strerror(errno) << endl;
552 
553       fclose(file);
554       file = 0;
555       ::remove(filename.c_str());
556       return false;
557     }
558   }
559 
560   offset = filesize;
561 
562   exists = true;
563   return true;
564 }
565 
566 // Write some data to disk
567 
Write(u64 _offset,const void * buffer,size_t length,LengthType maxlength)568 bool DiskFile::Write(u64 _offset, const void *buffer, size_t length, LengthType maxlength)
569 {
570   assert(file != 0);
571 
572   if (offset != _offset)
573   {
574     if (_offset > (u64)MaxOffset)
575     {
576         *serr << "Could not write " << (u64)length << " bytes to " << filename << " at offset " << _offset << endl;
577       return false;
578     }
579 
580 
581     if (fseek(file, (OffsetType)_offset, SEEK_SET))
582     {
583       *serr << "Could not write " << (u64)length << " bytes to " << filename << " at offset " << _offset << ": " << strerror(errno) << endl;
584       return false;
585     }
586     offset = _offset;
587   }
588 
589   while (length > 0) {
590 
591     LengthType write;
592     if (length > maxlength)
593       write = maxlength;
594     else
595       write = length;
596 
597     LengthType wrote = fwrite(buffer, 1, write, file);
598     if (wrote != write)
599     {
600       *serr << "Could not write " << (u64)length << " bytes to " << filename << " at offset " << _offset << ": " << strerror(errno) << endl;
601       return false;
602     }
603 
604     offset += wrote;
605     length -= wrote;
606     buffer = ((char *) buffer) + wrote;
607 
608     if (filesize < offset)
609     {
610       filesize = offset;
611     }
612   }
613 
614   return true;
615 }
616 
617 // Open the file
618 
Open(const string & _filename,u64 _filesize)619 bool DiskFile::Open(const string &_filename, u64 _filesize)
620 {
621   assert(file == 0);
622 
623   filename = _filename;
624   filesize = _filesize;
625 
626   if (_filesize > (u64)MaxOffset)
627   {
628     *serr << "File size for " << _filename << " is too large." << endl;
629     return false;
630   }
631 
632   file = fopen(filename.c_str(), "rb");
633   if (file == 0)
634   {
635     return false;
636   }
637 
638   offset = 0;
639   exists = true;
640 
641   return true;
642 }
643 
644 // Read some data from disk
645 
Read(u64 _offset,void * buffer,size_t length,LengthType maxlength)646 bool DiskFile::Read(u64 _offset, void *buffer, size_t length, LengthType maxlength)
647 {
648   assert(file != 0);
649 
650   if (offset != _offset)
651   {
652     if (_offset > (u64)MaxOffset)
653     {
654       *serr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << endl;
655       return false;
656     }
657 
658 
659     if (fseek(file, (OffsetType)_offset, SEEK_SET))
660     {
661       *serr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << ": " << strerror(errno) << endl;
662       return false;
663     }
664     offset = _offset;
665   }
666 
667 
668   while (length > 0) {
669 
670     LengthType want;
671     if (length > maxlength)
672       want = maxlength;
673     else
674       want = length;
675 
676     LengthType got = fread(buffer, 1, want, file);
677     if (got != want)
678     {
679       // NOTE: This can happen on error or when hitting the end-of-file.
680 
681       *serr << "Could not read " << (u64)length << " bytes from " << filename << " at offset " << _offset << ": " << strerror(errno) << endl;
682       return false;
683     }
684 
685     offset += got;
686     length -= got;
687     buffer = ((char *) buffer) + got;
688 
689     // Write() updates filesize.  Should we do that here too?
690   }
691 
692   return true;
693 }
694 
Close(void)695 void DiskFile::Close(void)
696 {
697   if (file != 0)
698   {
699     fclose(file);
700     file = 0;
701   }
702 }
703 
704 // Attempt to get the full pathname of the file
GetCanonicalPathname(string filename)705 string DiskFile::GetCanonicalPathname(string filename)
706 {
707   // Is the supplied path already an absolute one
708   if (filename.size() == 0 || filename[0] == '/')
709     return filename;
710 
711   // Get the current directory
712 #ifdef PATH_MAX
713   char curdir[PATH_MAX];
714   if (0 == getcwd(curdir, sizeof(curdir)))
715 #else
716   // Avoid unconditional use of PATH_MAX (not defined on hurd)
717   char *curdir = get_current_dir_name();
718   if (curdir == NULL)
719 #endif
720   {
721     return filename;
722   }
723 
724 
725   // Allocate a work buffer and copy the resulting full path into it.
726   char *work = new char[strlen(curdir) + filename.size() + 2];
727   strcpy(work, curdir);
728 #ifndef PATH_MAX
729   free(curdir);
730 #endif
731   if (work[strlen(work)-1] != '/')
732     strcat(work, "/");
733   strcat(work, filename.c_str());
734 
735   char *in = work;
736   char *out = work;
737 
738   while (*in)
739   {
740     if (*in == '/')
741     {
742       if (in[1] == '.' && in[2] == '/')
743       {
744         // skip the input past /./
745         in += 2;
746       }
747       else if (in[1] == '.' && in[2] == '.' && in[3] == '/')
748       {
749         // backtrack the output if /../ was found on the input
750         in += 3;
751         if (out > work)
752         {
753           do
754           {
755             out--;
756           } while (out > work && *out != '/');
757         }
758       }
759       else
760       {
761         *out++ = *in++;
762       }
763     }
764     else
765     {
766       *out++ = *in++;
767     }
768   }
769   *out = 0;
770 
771   string result = work;
772   delete [] work;
773 
774   return result;
775 }
776 
FindFiles(string path,string wildcard,bool recursive)777 std::unique_ptr< list<string> > DiskFile::FindFiles(string path, string wildcard, bool recursive)
778 {
779   // check path, if not ending with path separator, add one
780   char pathend = *path.rbegin();
781   if (pathend != '/')
782   {
783     path += '/';
784   }
785   list<string> *matches = new list<string>;
786 
787   string::size_type where;
788 
789   if ((where = wildcard.find_first_of('*')) != string::npos ||
790       (where = wildcard.find_first_of('?')) != string::npos)
791   {
792     string front = wildcard.substr(0, where);
793     bool multiple = wildcard[where] == '*';
794     string back = wildcard.substr(where+1);
795 
796     DIR *dirp = opendir(path.c_str());
797     if (dirp != 0)
798     {
799       struct dirent *d;
800       while ((d = readdir(dirp)) != 0)
801       {
802         string name = d->d_name;
803 
804         if (name == "." || name == "..")
805           continue;
806 
807         if (multiple)
808         {
809           if (name.size() >= wildcard.size() &&
810               name.substr(0, where) == front &&
811               name.substr(name.size()-back.size()) == back)
812           {
813             struct stat st;
814             string fn = path + name;
815             if (stat(fn.c_str(), &st) == 0)
816             {
817               if (S_ISDIR(st.st_mode) &&
818                   recursive == true)
819               {
820 
821                 string nwwildcard="*";
822                 std::unique_ptr< list<string> > dirmatches(
823 							 DiskFile::FindFiles(fn, nwwildcard, true)
824 							 );
825                 matches->merge(*dirmatches);
826               }
827               else if (S_ISREG(st.st_mode))
828               {
829                 matches->push_back(path + name);
830               }
831             }
832           }
833         }
834         else
835         {
836           if (name.size() == wildcard.size())
837           {
838             string::const_iterator pw = wildcard.begin();
839             string::const_iterator pn = name.begin();
840             while (pw != wildcard.end())
841             {
842               if (*pw != '?' && *pw != *pn)
843                 break;
844               ++pw;
845               ++pn;
846             }
847 
848             if (pw == wildcard.end())
849             {
850               struct stat st;
851               string fn = path + name;
852               if (stat(fn.c_str(), &st) == 0)
853               {
854                 if (S_ISDIR(st.st_mode) &&
855                     recursive == true)
856                 {
857 
858                   string nwwildcard="*";
859 		  std::unique_ptr< list<string> > dirmatches(
860 							   DiskFile::FindFiles(fn, nwwildcard, true)
861 							   );
862 
863                   matches->merge(*dirmatches);
864                 }
865                 else if (S_ISREG(st.st_mode))
866                 {
867                   matches->push_back(path + name);
868                 }
869               }
870             }
871           }
872         }
873 
874       }
875       closedir(dirp);
876     }
877   }
878   else
879   {
880     struct stat st;
881     string fn = path + wildcard;
882     if (stat(fn.c_str(), &st) == 0)
883     {
884       if (S_ISDIR(st.st_mode) &&
885           recursive == true)
886       {
887         string nwwildcard="*";
888 	std::unique_ptr< list<string> > dirmatches(
889 						 DiskFile::FindFiles(fn, nwwildcard, true)
890 						 );
891 
892         matches->merge(*dirmatches);
893       }
894       else if (S_ISREG(st.st_mode))
895       {
896         matches->push_back(path + wildcard);
897       }
898     }
899   }
900 
901   return std::unique_ptr< list<string> >(matches);
902 }
903 
GetFileSize(string filename)904 u64 DiskFile::GetFileSize(string filename)
905 {
906   struct stat st;
907   if ((0 == stat(filename.c_str(), &st)) && (0 != (st.st_mode & S_IFREG)))
908   {
909     return st.st_size;
910   }
911   else
912   {
913     return 0;
914   }
915 }
916 
FileExists(string filename)917 bool DiskFile::FileExists(string filename)
918 {
919   struct stat st;
920   return ((0 == stat(filename.c_str(), &st)) && (0 != (st.st_mode & S_IFREG)));
921 }
922 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
923 #endif
924 
Open(void)925 bool DiskFile::Open(void)
926 {
927   string _filename = filename;
928 
929   return Open(_filename);
930 }
931 
Open(const string & _filename)932 bool DiskFile::Open(const string &_filename)
933 {
934   return Open(_filename, GetFileSize(_filename));
935 }
936 
937 // Delete the file
938 
Delete(void)939 bool DiskFile::Delete(void)
940 {
941 #ifdef _WIN32
942   assert(hFile == INVALID_HANDLE_VALUE);
943 #else
944   assert(file == 0);
945 #endif
946 
947   if (filename.size() > 0 && 0 == unlink(filename.c_str()))
948   {
949     exists = false;
950     return true;
951   }
952   else
953   {
954     *serr << "Cannot delete " << filename << endl;
955 
956     return false;
957   }
958 }
959 
960 //string DiskFile::GetPathFromFilename(string filename)
961 //{
962 //  string::size_type where;
963 //
964 //  if (string::npos != (where = filename.find_last_of('/')) ||
965 //      string::npos != (where = filename.find_last_of('\\')))
966 //  {
967 //    return filename.substr(0, where+1);
968 //  }
969 //  else
970 //  {
971 //    return "." PATHSEP;
972 //  }
973 //}
974 
SplitFilename(string filename,string & path,string & name)975 void DiskFile::SplitFilename(string filename, string &path, string &name)
976 {
977   string::size_type where;
978 
979   if (string::npos != (where = filename.find_last_of('/')) ||
980       string::npos != (where = filename.find_last_of('\\')))
981   {
982     path = filename.substr(0, where+1);
983     name = filename.substr(where+1);
984   }
985   else
986   {
987     path = "." PATHSEP;
988     name = filename;
989   }
990 }
991 
SplitRelativeFilename(string filename,string basepath,string & name)992 void DiskFile::SplitRelativeFilename(string filename, string basepath, string &name)
993 {
994   name = filename;
995   name.erase(0, basepath.length());
996 }
997 
Rename(void)998 bool DiskFile::Rename(void)
999 {
1000   char newname[_MAX_PATH+1];
1001   u32 index = 0;
1002 
1003   struct stat st;
1004 
1005   do
1006   {
1007     int length = snprintf(newname, _MAX_PATH, "%s.%u", filename.c_str(), (unsigned int) ++index);
1008     if (length < 0)
1009     {
1010       *serr << filename << " cannot be renamed." << endl;
1011       return false;
1012     }
1013     else if (length > _MAX_PATH)
1014     {
1015       *serr << filename << " pathlength is more than " << _MAX_PATH << "." << endl;
1016       return false;
1017     }
1018     newname[length] = 0;
1019   } while (stat(newname, &st) == 0);
1020 
1021   return Rename(newname);
1022 }
1023 
Rename(string _filename)1024 bool DiskFile::Rename(string _filename)
1025 {
1026 #ifdef _WIN32
1027   assert(hFile == INVALID_HANDLE_VALUE);
1028 #else
1029   assert(file == 0);
1030 #endif
1031 
1032   if (::rename(filename.c_str(), _filename.c_str()) == 0)
1033   {
1034     filename = _filename;
1035 
1036     return true;
1037   }
1038   else
1039   {
1040     *serr << filename << " cannot be renamed to " << _filename << endl;
1041 
1042     return false;
1043   }
1044 }
1045 
1046 #ifdef _WIN32
ErrorMessage(DWORD error)1047 string DiskFile::ErrorMessage(DWORD error)
1048 {
1049   string result;
1050 
1051   LPVOID lpMsgBuf;
1052   if (::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
1053                        NULL,
1054                        error,
1055                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
1056                        (LPSTR)&lpMsgBuf,
1057                        0,
1058                        NULL))
1059   {
1060     result = (char*)lpMsgBuf;
1061     LocalFree(lpMsgBuf);
1062   }
1063   else
1064   {
1065     char message[40];
1066     snprintf(message, sizeof(message), "Unknown error code (%lu)", error);
1067     result = message;
1068   }
1069 
1070   return result;
1071 }
1072 #endif
1073 
DiskFileMap(void)1074 DiskFileMap::DiskFileMap(void)
1075 {
1076 }
1077 
~DiskFileMap(void)1078 DiskFileMap::~DiskFileMap(void)
1079 {
1080   map<string, DiskFile*>::iterator fi = diskfilemap.begin();
1081   while (fi != diskfilemap.end())
1082   {
1083     delete (*fi).second;
1084 
1085     ++fi;
1086   }
1087 }
1088 
Insert(DiskFile * diskfile)1089 bool DiskFileMap::Insert(DiskFile *diskfile)
1090 {
1091   string filename = diskfile->FileName();
1092   assert(filename.length() != 0);
1093 
1094   pair<map<string,DiskFile*>::const_iterator,bool> location = diskfilemap.insert(pair<string,DiskFile*>(filename, diskfile));
1095 
1096   return location.second;
1097 }
1098 
Remove(DiskFile * diskfile)1099 void DiskFileMap::Remove(DiskFile *diskfile)
1100 {
1101   string filename = diskfile->FileName();
1102   assert(filename.length() != 0);
1103 
1104   diskfilemap.erase(filename);
1105 }
1106 
Find(string filename) const1107 DiskFile* DiskFileMap::Find(string filename) const
1108 {
1109   assert(filename.length() != 0);
1110 
1111   map<string, DiskFile*>::const_iterator f = diskfilemap.find(filename);
1112 
1113   return (f != diskfilemap.end()) ?  f->second : 0;
1114 }
1115 
1116 
FileSizeCache()1117 FileSizeCache::FileSizeCache()
1118 {
1119 }
1120 
get(const string & filename)1121 u64 FileSizeCache::get(const string &filename) {
1122   map<string, u64>::const_iterator f = cache.find(filename);
1123   if (f != cache.end())
1124     return f->second;
1125 
1126   // go to disk
1127   u64 filesize = DiskFile::GetFileSize(filename);
1128 
1129   cache.insert(pair<string,u64>(filename, filesize));
1130   //  pair<map<string,u64>::const_iterator,bool> location = cache.insert(pair<string,u64>(filename, filesize));
1131   //  if (!location.second) {
1132   //    throw exception?
1133   //  }
1134   return filesize;
1135 }
1136