1 /* bzflag
2 * Copyright (c) 1993-2021 Tim Riker
3 *
4 * This package is free software; you can redistribute it and/or
5 * modify it under the terms of the license found in the file
6 * named COPYING that should have accompanied this file.
7 *
8 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12
13 // interface header
14 #include "OSFile.h"
15
16 // system headers
17 #include <vector>
18 #include <string> //std::string
19 #include <algorithm>
20 #include <string.h> //c-style string
21 #include <stdio.h>
22 #ifndef _MSC_VER
23 #include <stddef.h>
24 #endif
25
26 // local implementation headers
27 #include "common.h"
28 #include "TextUtils.h"
29
30 typedef std::vector<std::string> fileNameList;
31
32 // These are the core file name conversions
33 // std pathnames are unix style
34 // If you are adding a platform
35 // this is one place you need to add conversions to/from your OS
36
37 // utility functions
OSFileStdToOSDir(std::string & dir)38 void OSFileStdToOSDir(std::string &dir)
39 {
40 if (dir=="") return;
41 #ifndef _WIN32
42 return;
43 #else
44 std::replace(dir.begin(), dir.end(), '/', '\\');
45 #endif//WIN32
46 }
47
OSFileOSToStdDir(std::string & dir)48 void OSFileOSToStdDir(std::string &dir)
49 {
50 //#ifndef _WIN32
51 // return;
52 //#else
53 std::replace(dir.begin(), dir.end(), '\\', '/');
54 //#endif//WIN32
55 }
56
57 // Windows specific make path
58 #ifdef _WIN32
windowsMakePath(const std::string & path)59 void windowsMakePath(const std::string &path)
60 {
61 std::string::size_type pos = 0;
62 std::string::size_type delimitpos;
63
64 do
65 {
66 delimitpos = path.find('\\', pos);
67 std::string nextdir = path.substr(pos, delimitpos);
68 mkdir(nextdir.c_str());
69 pos = delimitpos + 1;
70 }
71 while (delimitpos != std::string::npos);
72 }
73 #endif // windows
74
75
76 /// global make path
osMakePath(const std::string & path)77 void osMakePath(const std::string &path)
78 {
79 if (path=="") return;
80 #ifdef _WIN32
81 windowsMakePath(path);
82 #endif
83 }
84
85
86 // global path stuff
87 std::string stdBaseDir;
88 std::string osBaseDir;
89
setOSFileBaseDir(const std::string & dir)90 void setOSFileBaseDir(const std::string &dir)
91 {
92 stdBaseDir = dir;
93 osBaseDir = dir;
94 OSFileStdToOSDir(osBaseDir);
95 }
96
97 // OSFileClass
98 class OSFile::OSFileInfo
99 {
100 public:
101 OSFileInfo();
102 OSFileInfo(const OSFile::OSFileInfo &r);
103 std::string stdName;
104 std::string osName;
105 std::string title;
106 std::string osPath;
107 bool useGlobalPath;
108 FILE *fp;
109 };
110
OSFileInfo()111 OSFile::OSFileInfo::OSFileInfo()
112 {
113 fp = NULL;
114 useGlobalPath = true;
115 }
116
OSFileInfo(const OSFile::OSFileInfo & r)117 OSFile::OSFileInfo::OSFileInfo(const OSFile::OSFileInfo &r)
118 {
119 // never copy the file
120 fp = NULL;
121 osName = r.osName;
122 stdName = r.stdName;
123 title = r.title;
124 useGlobalPath = r.useGlobalPath;
125 }
126
setUseGlobalPath(bool use)127 void OSFile::setUseGlobalPath(bool use)
128 {
129 info->useGlobalPath = use;
130 }
131
OSFile()132 OSFile::OSFile()
133 {
134 info = new OSFileInfo;
135 }
136
OSFile(const std::string & name)137 OSFile::OSFile(const std::string &name)
138 {
139 info = new OSFileInfo;
140 stdName(name);
141 }
142
OSFile(const char * name)143 OSFile::OSFile(const char* name)
144 {
145 info = new OSFileInfo;
146 stdName(std::string(name));
147 }
148
OSFile(const OSFile & r)149 OSFile::OSFile(const OSFile &r)
150 {
151 info = new OSFileInfo(*r.info);
152 }
153
operator =(const OSFile & r)154 OSFile& OSFile::operator = (const OSFile &r)
155 {
156 if (this == &r)
157 return *this;
158
159 if (info)
160 delete (info);
161
162 info = new OSFileInfo(*r.info);
163 return *this;
164 }
165
OSFile(const std::string & name,const char * mode)166 OSFile::OSFile(const std::string &name, const char *mode)
167 {
168 info = new OSFileInfo;
169 stdName(name);
170 open(name.c_str(), mode);
171 }
172
~OSFile()173 OSFile::~OSFile()
174 {
175 close();
176 delete (info);
177 }
178
open(const std::string & name,const char * mode)179 bool OSFile::open(const std::string &name, const char *mode)
180 {
181 close();
182 stdName(name);
183 return open(mode);
184 }
185
open(const char * mode)186 bool OSFile::open(const char *mode)
187 {
188 close();
189
190 char modeToUse[32];
191
192 if (!mode)
193 sprintf(modeToUse, "rb");
194 else
195 {
196 strncpy(modeToUse, mode, sizeof(modeToUse) - 1);
197 modeToUse[31] = '\0';
198 }
199
200 std::string fileName;
201
202 if (info->useGlobalPath)
203 fileName = osBaseDir;
204 fileName += info->osName;
205
206 info->fp = fopen(fileName.c_str(),modeToUse);
207
208 // we may need to make the file path to the file, if we are writing then lets get on with it.
209 if (!info->fp && strchr(modeToUse,'w'))
210 {
211 osMakePath(fileName);
212 info->fp = fopen(fileName.c_str(),modeToUse);
213 }
214
215 return isOpen();
216 }
217
close()218 bool OSFile::close()
219 {
220 if (info->fp)
221 fclose(info->fp);
222
223 info->fp = NULL;
224
225 return (!isOpen());
226 }
227
read(void * data,int _size,int count)228 int OSFile::read(void* data, int _size, int count)
229 {
230 if (!isOpen())
231 return 0;
232
233 return (int)fread(data, _size, count, info->fp);
234 }
235
readChar()236 unsigned char OSFile::readChar()
237 {
238 if (!isOpen())
239 return 0;
240
241 int c = getc(info->fp);
242 if (c!=EOF)
243 return c;
244 else
245 return 0;
246 }
247
readLine()248 std::string OSFile::readLine()
249 {
250 std::string line;
251 // reserve initial space for about a typical line length to speed up things
252 line.reserve(20);
253
254 char c = readChar();
255 while (c != 0 && c != '\n' && c != 10)
256 {
257 line += c;
258 c = readChar();
259 }
260 return line;
261 }
262
scanChar(unsigned char * pChar)263 int OSFile::scanChar(unsigned char *pChar)
264 {
265 if (!pChar || !isOpen())
266 return 0;
267
268 return fscanf(info->fp,"%c",pChar);
269 }
270
scanStr()271 const char* OSFile::scanStr()
272 {
273 if (!isOpen())
274 return 0;
275
276 static char temp[1024] = {0};
277 if (fscanf(info->fp,"%s",temp)!=1)
278 return NULL;
279 return temp;
280 }
281
write(const void * data,int _size)282 int OSFile::write(const void* data, int _size)
283 {
284 if (!isOpen())
285 return 0;
286
287 return (int)fwrite(data, _size, 1, info->fp);
288 }
289
flush()290 void OSFile::flush()
291 {
292 fflush(info->fp);
293 }
294
295
seek(teFilePos ePos,int iOffset)296 int OSFile::seek(teFilePos ePos, int iOffset)
297 {
298 if (!isOpen())
299 return 0;
300
301 long iMode;
302 switch (ePos)
303 {
304 case eFileStart:
305 iMode = SEEK_SET;
306 break;
307
308 case eFileEnd:
309 iMode = SEEK_END ;
310 break;
311
312 case eCurentPos:
313 default:
314 iMode = SEEK_CUR ;
315 break;
316 }
317
318 return fseek(info->fp,iOffset,iMode);
319 }
320
size()321 unsigned int OSFile::size()
322 {
323 if (!isOpen())
324 return 0;
325
326 unsigned int pos = ftell(info->fp);
327 fseek(info->fp,0,SEEK_END);
328 unsigned int len = ftell(info->fp);
329 fseek(info->fp,pos, SEEK_SET);
330
331 return len;
332 }
333
tell()334 unsigned int OSFile::tell()
335 {
336 if (!isOpen())
337 return 0;
338 return ftell(info->fp);
339 }
340
341
stdName(const std::string & name)342 void OSFile::stdName(const std::string &name)
343 {
344 info->stdName = name;
345 info->osName = name;
346 OSFileStdToOSDir(info->osName);
347 }
348
osName(const std::string & name)349 void OSFile::osName(const std::string &name)
350 {
351 info->stdName = name;
352 info->osName = name;
353 OSFileOSToStdDir(info->stdName);
354 }
355
getFileName()356 std::string OSFile::getFileName()
357 {
358 std::string::size_type lastslash = info->stdName.rfind('/');
359 info->title = lastslash==std::string::npos ?
360 info->stdName : info->stdName.substr(lastslash+1);
361
362 // strip extension
363 info->title.erase(info->title.rfind('.'));
364
365 return info->title;
366 }
367
368 // this CAN return npos, cus the file may not have an extenstion, if it just happens to end in a '.' then well, your really weird Mr. File.
getExtension()369 std::string OSFile::getExtension()
370 {
371 std::string::size_type dot = info->stdName.rfind('.');
372 return dot==std::string::npos ? "" : info->stdName.substr(dot+1);
373 }
374
getOSFileDir()375 std::string OSFile::getOSFileDir()
376 {
377 std::string::size_type lastslash = info->stdName.rfind('/');
378 info->osPath = lastslash==std::string::npos ?
379 info->stdName : info->stdName.substr(0, lastslash);
380 OSFileStdToOSDir(info->osPath);
381
382 return info->osPath;
383 }
384
getFullOSPath()385 std::string OSFile::getFullOSPath()
386 {
387 std::string path = osBaseDir;
388 path += info->osName;
389 return path;
390 }
391
getFile()392 FILE* OSFile::getFile()
393 {
394 return info->fp;
395 }
396
getStdName()397 std::string OSFile::getStdName()
398 {
399 return info->stdName;
400 }
401
getOSName()402 std::string OSFile::getOSName()
403 {
404 return info->osName;
405 }
406
isOpen()407 bool OSFile::isOpen()
408 {
409 return info->fp != NULL;
410 }
411
412 // OS Dir classes
413 class OSDir::OSDirInfo
414 {
415 public:
416 OSDirInfo();
417 OSDirInfo(const OSDir::OSDirInfo &r);
418
419 std::string baseStdDir;
420 std::string baseOSDir;
421 fileNameList nameList;
422 int namePos;
423 };
424
OSDirInfo()425 OSDir::OSDirInfo::OSDirInfo()
426 {
427 namePos = -1;
428 }
429
OSDirInfo(const OSDir::OSDirInfo & r)430 OSDir::OSDirInfo::OSDirInfo(const OSDir::OSDirInfo &r)
431 {
432 baseStdDir = r.baseStdDir;
433 baseOSDir = r.baseOSDir;
434 nameList = r.nameList;
435 namePos = r.namePos;
436 }
437
OSDir()438 OSDir::OSDir()
439 {
440 info = new OSDirInfo;
441 }
442
OSDir(const OSDir & r)443 OSDir::OSDir(const OSDir& r)
444 {
445 info = new OSDirInfo(*r.info);
446 }
447
operator =(const OSDir & r)448 OSDir& OSDir::operator = (const OSDir& r)
449 {
450 if (this == &r)
451 return *this;
452
453 delete info;
454 info = new OSDirInfo(*r.info);
455
456 return *this;
457 }
458
OSDir(const std::string & DirName)459 OSDir::OSDir(const std::string &DirName)
460 {
461 info = new OSDirInfo;
462 setStdDir(DirName);
463 }
464
~OSDir()465 OSDir::~OSDir()
466 {
467 if (info)
468 {
469 info->nameList.clear();
470 delete(info);
471 }
472 }
473
setStdDir(const std::string & DirName)474 void OSDir::setStdDir(const std::string &DirName)
475 {
476 info->baseStdDir = DirName;
477 info->baseOSDir = DirName;
478 OSFileStdToOSDir(info->baseOSDir);
479 }
480
setOSDir(const std::string & DirName)481 void OSDir::setOSDir(const std::string &DirName)
482 {
483 info->baseStdDir = DirName;
484 info->baseOSDir = DirName;
485 OSFileOSToStdDir(info->baseStdDir);
486 }
487
makeStdDir(const std::string & DirName)488 void OSDir::makeStdDir(const std::string &DirName)
489 {
490 setStdDir(DirName);
491 #ifdef _WIN32
492 mkdir(info->baseOSDir.c_str());
493 #else
494 mkdir(info->baseOSDir.c_str(), 0777);
495 #endif
496 }
497
makeOSDir(const std::string & DirName)498 void OSDir::makeOSDir(const std::string &DirName)
499 {
500 setOSDir(DirName);
501 #ifdef _WIN32
502 mkdir(info->baseOSDir.c_str());
503 #else
504 mkdir(info->baseOSDir.c_str(), 0777);
505 #endif
506 }
507
getStdName()508 std::string OSDir::getStdName()
509 {
510 return info->baseStdDir;
511 }
512
getOSName()513 std::string OSDir::getOSName()
514 {
515 return info->baseOSDir.c_str();
516 }
517
getFullOSPath()518 std::string OSDir::getFullOSPath()
519 {
520 std::string FilePath = osBaseDir;
521 FilePath += info->baseOSDir;
522 return FilePath;
523 }
524
getNextFile(OSFile & oFile,bool bRecursive)525 bool OSDir::getNextFile(OSFile &oFile, bool bRecursive)
526 {
527 return getNextFile(oFile, NULL, bRecursive);
528 }
529
getFileScanCount()530 int OSDir::getFileScanCount()
531 {
532 if (info)
533 return (int)info->nameList.size();
534 else
535 return -1;
536 }
537
getNextFile(OSFile & oFile,const char * fileMask,bool bRecursive)538 bool OSDir::getNextFile(OSFile &oFile, const char* fileMask, bool bRecursive)
539 {
540 #ifdef _WIN32
541 std::string realMask = "*.*"; //FIXME -- could this also be just '*' ?
542 #else
543 std::string realMask = "*";
544 #endif
545 if (fileMask)
546 realMask = fileMask;
547
548 realMask = TextUtils::toupper(realMask);
549
550 if (info->namePos == -1)
551 {
552 info->nameList.clear();
553 //FIXME -- just do the #ifdef'ing here?
554 windowsAddFileStack(getFullOSPath(), realMask, bRecursive);
555 linuxAddFileStack(getFullOSPath(), realMask, bRecursive);
556
557 info->namePos = 0;
558 }
559
560 int size = info->nameList.size();
561 if (info->namePos >= size)
562 {
563 info->namePos = -1;
564 return false;
565 }
566
567 std::string fileName = info->nameList[info->namePos];
568
569 if (osBaseDir.size()>1)
570 {
571 std::string temp = &(fileName.c_str()[osBaseDir.size()]);
572 fileName = temp;
573 }
574
575 oFile.osName(fileName);
576 info->namePos++;
577
578 return true;
579 }
580
windowsAddFileStack(std::string pathName,std::string fileMask,bool bRecursive)581 bool OSDir::windowsAddFileStack(std::string pathName, std::string fileMask, bool bRecursive)
582 {
583 #ifdef _WIN32
584 struct _finddata_t fileInfo;
585
586 long hFile;
587 std::string searchstr;
588
589 std::string FilePath;
590
591 bool bDone = false;
592
593 searchstr = pathName;
594 searchstr += "\\";
595 if (fileMask.size() > 0)
596 searchstr += fileMask;
597 else
598 searchstr += "*.*";
599
600 hFile = (long)_findfirst(searchstr.c_str(), &fileInfo);
601
602 if (hFile != -1)
603 {
604 while (!bDone)
605 {
606 if ((strlen(fileInfo.name) >0) && (strcmp(fileInfo.name,".") != 0) &&
607 (strcmp(fileInfo.name,"..") != 0))
608 {
609 FilePath = pathName;
610 FilePath += "\\";
611 FilePath += fileInfo.name;
612
613 if ((fileInfo.attrib & _A_SUBDIR) && bRecursive)
614 windowsAddFileStack(FilePath,fileMask,bRecursive);
615 else if (!(fileInfo.attrib & _A_SUBDIR))
616 info->nameList.push_back(FilePath);
617 }
618 if (_findnext(hFile,&fileInfo) == -1)
619 bDone = true;
620 }
621 }
622 return true;
623 #else
624 // quell warnings
625 if (!bRecursive)
626 {
627 fileMask.size();
628 pathName.size();
629 }
630 return false;
631 #endif
632 }
633
634 // linux mask filter functions
635 // we don't need these for windows as it can do it right in findNextFile
636 #ifndef _WIN32
match_multi(const char ** mask,const char ** string)637 static int match_multi(const char **mask, const char **string)
638 {
639 const char *msk;
640 const char *str;
641 const char *msktop;
642 const char *strtop;
643
644 msk = *mask;
645 str = *string;
646
647 while ((*msk != '\0') && (*msk == '*'))
648 msk++; /* get rid of multiple '*'s */
649
650 if (*msk == '\0') /* '*' was last, auto-match */
651 return +1;
652
653 msktop = msk;
654 strtop = str;
655
656 while (*msk != '\0')
657 {
658 if (*msk == '*')
659 {
660 *mask = msk;
661 *string = str;
662 return 0; /* matched this segment */
663 }
664 else if (*str == '\0')
665 return -1; /* can't match */
666 else
667 {
668 if ((*msk == '?') || (*msk == *str))
669 {
670 msk++;
671 str++;
672 if ((*msk == '\0') && (*str != '\0')) /* advanced check */
673 {
674 str++;
675 strtop++;
676 str = strtop;
677 msk = msktop;
678 }
679 }
680 else
681 {
682 str++;
683 strtop++;
684 str = strtop;
685 msk = msktop;
686 }
687 }
688 }
689
690 *mask = msk;
691 *string = str;
692 return +1; /* full match */
693 }
694
match_mask(const char * mask,const char * string)695 static int match_mask (const char *mask, const char *string)
696 {
697 if (mask == NULL)
698 return 0;
699
700 if (string == NULL)
701 return 0;
702
703 if ((mask[0] == '*') && (mask[1] == '\0'))
704 return 1; /* instant match */
705
706 while (*mask != '\0')
707 {
708 if (*mask == '*')
709 {
710 mask++;
711 switch (match_multi (&mask, &string))
712 {
713 case +1:
714 return 1;
715 case -1:
716 return 0;
717 }
718 }
719 else if (*string == '\0')
720 return 0;
721 else if ((*mask == '?') || (*mask == *string))
722 {
723 mask++;
724 string++;
725 }
726 else
727 return 0;
728 }
729
730 if (*string == '\0')
731 return 1;
732 else
733 return 0;
734 }
735 #endif
736
linuxAddFileStack(std::string pathName,std::string fileMask,bool bRecursive)737 bool OSDir::linuxAddFileStack(std::string pathName, std::string fileMask, bool bRecursive)
738 {
739 #ifdef _WIN32
740 // quell warnings
741 if (!bRecursive)
742 {
743 fileMask.size();
744 pathName.size();
745 }
746 return false;
747 #else
748 DIR *directory;
749 dirent *fileInfo;
750 struct stat statbuf;
751 char searchstr[1024];
752 std::string FilePath;
753
754 strncpy(searchstr, pathName.c_str(), sizeof(searchstr) - 1);
755 searchstr[1023] = '\0';
756 size_t pathLen = strlen(searchstr);
757 if ((pathLen < 1022) && searchstr[pathLen - 1] != '/')
758 searchstr[pathLen] = '/';
759 directory = opendir(searchstr);
760 if (!directory)
761 return false;
762
763 // TODO: make it use the filemask
764 while ((fileInfo = readdir(directory)))
765 {
766 if (!((strcmp(fileInfo->d_name, ".") == 0) || (strcmp(fileInfo->d_name, "..") == 0)))
767 {
768 FilePath = searchstr;
769 FilePath += fileInfo->d_name;
770 strcpy(fileInfo->d_name, TextUtils::toupper(fileInfo->d_name).c_str());
771
772 stat(FilePath.c_str(), &statbuf);
773
774 if (S_ISDIR(statbuf.st_mode) && bRecursive)
775 linuxAddFileStack(FilePath,fileMask,bRecursive);
776 else if (match_mask(fileMask.c_str(), fileInfo->d_name))
777 info->nameList.push_back(FilePath);
778 }
779 }
780 closedir(directory);
781 return true;
782 #endif// !Win32
783 }
784
785 // Local Variables: ***
786 // mode: C++ ***
787 // tab-width: 4 ***
788 // c-basic-offset: 4 ***
789 // indent-tabs-mode: nil ***
790 // End: ***
791 // ex: shiftwidth=4 tabstop=4
792