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