1 //------------------------------------------------------------------------
2 // File Utilities
3 //------------------------------------------------------------------------
4 //
5 // Eureka DOOM Editor
6 //
7 // Copyright (C) 2006-2016 Andrew Apted
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 //------------------------------------------------------------------------
20
21 #include "main.h"
22
23 #ifdef WIN32
24 #include <io.h>
25 #else // UNIX or MACOSX
26 #include <dirent.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #endif
31
32 #ifdef __APPLE__
33 #include <sys/param.h>
34 #include <mach-o/dyld.h> // _NSGetExecutablePath
35 #endif
36
37 #ifndef PATH_MAX
38 #define PATH_MAX 2048
39 #endif
40
41
FileExists(const char * filename)42 bool FileExists(const char *filename)
43 {
44 FILE *fp = fopen(filename, "rb");
45
46 if (fp)
47 {
48 fclose(fp);
49 return true;
50 }
51
52 return false;
53 }
54
55
HasExtension(const char * filename)56 bool HasExtension(const char *filename)
57 {
58 int A = (int)strlen(filename) - 1;
59
60 if (A > 0 && filename[A] == '.')
61 return false;
62
63 for (; A >= 0 ; A--)
64 {
65 if (filename[A] == '.')
66 return true;
67
68 if (filename[A] == '/')
69 break;
70
71 #ifdef WIN32
72 if (filename[A] == '\\' || filename[A] == ':')
73 break;
74 #endif
75 }
76
77 return false;
78 }
79
80 //
81 // MatchExtension
82 //
83 // When ext is NULL, checks if the file has no extension.
84 //
MatchExtension(const char * filename,const char * ext)85 bool MatchExtension(const char *filename, const char *ext)
86 {
87 if (! ext)
88 return ! HasExtension(filename);
89
90 int A = (int)strlen(filename) - 1;
91 int B = (int)strlen(ext) - 1;
92
93 for (; B >= 0 ; B--, A--)
94 {
95 if (A < 0)
96 return false;
97
98 if (toupper(filename[A]) != toupper(ext[B]))
99 return false;
100 }
101
102 return (A >= 1) && (filename[A] == '.');
103 }
104
105
106 //
107 // ReplaceExtension
108 //
109 // When ext is NULL, any existing extension is removed.
110 //
111 // Returned string is a COPY.
112 //
ReplaceExtension(const char * filename,const char * ext)113 char *ReplaceExtension(const char *filename, const char *ext)
114 {
115 SYS_ASSERT(filename[0] != 0);
116
117 size_t total_len = strlen(filename) + (ext ? strlen(ext) : 0);
118
119 char *buffer = StringNew((int)total_len + 10);
120
121 strcpy(buffer, filename);
122
123 char *dot_pos = buffer + strlen(buffer) - 1;
124
125 for (; dot_pos >= buffer && *dot_pos != '.' ; dot_pos--)
126 {
127 if (*dot_pos == '/')
128 break;
129
130 #ifdef WIN32
131 if (*dot_pos == '\\' || *dot_pos == ':')
132 break;
133 #endif
134 }
135
136 if (dot_pos < buffer || *dot_pos != '.')
137 dot_pos = NULL;
138
139 if (! ext)
140 {
141 if (dot_pos)
142 dot_pos[0] = 0;
143
144 return buffer;
145 }
146
147 if (dot_pos)
148 dot_pos[1] = 0;
149 else
150 strcat(buffer, ".");
151
152 strcat(buffer, ext);
153
154 return buffer;
155 }
156
157
FindBaseName(const char * filename)158 const char *FindBaseName(const char *filename)
159 {
160 // Find the base name of the file (i.e. without any path).
161 // The result always points within the given string.
162 //
163 // Example: "C:\Foo\Bar.wad" -> "Bar.wad"
164
165 const char *pos = filename + strlen(filename) - 1;
166
167 for (; pos >= filename ; pos--)
168 {
169 if (*pos == '/')
170 return pos + 1;
171
172 #ifdef WIN32
173 if (*pos == '\\' || *pos == ':')
174 return pos + 1;
175 #endif
176 }
177
178 return filename;
179 }
180
181
FilenameIsBare(const char * filename)182 bool FilenameIsBare(const char *filename)
183 {
184 if (strchr(filename, '.')) return false;
185 if (strchr(filename, '/')) return false;
186 if (strchr(filename, '\\')) return false;
187 if (strchr(filename, ':')) return false;
188
189 return true;
190 }
191
192
FilenameStripBase(char * buffer)193 void FilenameStripBase(char *buffer)
194 {
195 char *pos = buffer + strlen(buffer) - 1;
196
197 for (; pos > buffer ; pos--)
198 {
199 if (*pos == '/')
200 break;
201
202 #ifdef WIN32
203 if (*pos == '\\')
204 break;
205
206 if (*pos == ':')
207 {
208 pos[1] = 0;
209 return;
210 }
211 #endif
212 }
213
214 if (pos > buffer)
215 *pos = 0;
216 else
217 strcpy(buffer, ".");
218 }
219
220
221 //
222 // takes the basename in 'filename' and prepends the path from 'othername'.
223 // returns a newly allocated string.
224 //
FilenameReposition(const char * filename,const char * othername)225 const char *FilenameReposition(const char *filename, const char *othername)
226 {
227 filename = fl_filename_name(filename);
228
229 const char *op = fl_filename_name(othername);
230
231 if (op <= othername)
232 return StringDup(filename);
233
234 size_t dir_len = op - othername;
235 size_t len = strlen(filename) + dir_len;
236
237 char *result = StringNew((int)len + 10);
238
239 memcpy(result, othername, dir_len);
240 result[dir_len] = 0;
241
242 strcat(result, filename);
243
244 return result;
245 }
246
247
FilenameGetPath(char * dest,size_t maxsize,const char * filename)248 void FilenameGetPath(char *dest, size_t maxsize, const char *filename)
249 {
250 size_t len = (size_t)(FindBaseName(filename) - filename);
251
252 // remove trailing slash (except when following "C:" or similar)
253 if (len >= 1 &&
254 (filename[len-1] == '/' || filename[len-1] == '\\') &&
255 ! (len >= 2 && filename[len-2] == ':'))
256 {
257 len--;
258 }
259
260 if (len == 0)
261 {
262 strcpy(dest, ".");
263 return;
264 }
265
266 if (len >= maxsize)
267 len = maxsize - 1;
268
269 strncpy(dest, filename, len);
270 dest[len] = 0;
271 }
272
273
FileCopy(const char * src_name,const char * dest_name)274 bool FileCopy(const char *src_name, const char *dest_name)
275 {
276 char buffer[1024];
277
278 FILE *src = fopen(src_name, "rb");
279 if (! src)
280 return false;
281
282 FILE *dest = fopen(dest_name, "wb");
283 if (! dest)
284 {
285 fclose(src);
286 return false;
287 }
288
289 while (true)
290 {
291 size_t rlen = fread(buffer, 1, sizeof(buffer), src);
292 if (rlen == 0)
293 break;
294
295 size_t wlen = fwrite(buffer, 1, rlen, dest);
296 if (wlen != rlen)
297 break;
298 }
299
300 bool was_OK = !ferror(src) && !ferror(dest);
301
302 fclose(dest);
303 fclose(src);
304
305 return was_OK;
306 }
307
308
FileRename(const char * old_name,const char * new_name)309 bool FileRename(const char *old_name, const char *new_name)
310 {
311 #ifdef WIN32
312 return (::MoveFile(old_name, new_name) != 0);
313
314 #else // UNIX or MACOSX
315
316 return (rename(old_name, new_name) == 0);
317 #endif
318 }
319
320
FileDelete(const char * filename)321 bool FileDelete(const char *filename)
322 {
323 #ifdef WIN32
324 return (::DeleteFile(filename) != 0);
325
326 #else // UNIX or MACOSX
327
328 return (remove(filename) == 0);
329 #endif
330 }
331
332
FileChangeDir(const char * dir_name)333 bool FileChangeDir(const char *dir_name)
334 {
335 #ifdef WIN32
336 return (::SetCurrentDirectory(dir_name) != 0);
337
338 #else // UNIX or MACOSX
339
340 return (chdir(dir_name) == 0);
341 #endif
342 }
343
344
FileMakeDir(const char * dir_name)345 bool FileMakeDir(const char *dir_name)
346 {
347 #ifdef WIN32
348 return (::CreateDirectory(dir_name, NULL) != 0);
349
350 #else // UNIX or MACOSX
351
352 return (mkdir(dir_name, 0775) == 0);
353 #endif
354 }
355
356
FileLoad(const char * filename,int * length)357 u8_t * FileLoad(const char *filename, int *length)
358 {
359 *length = 0;
360
361 FILE *fp = fopen(filename, "rb");
362
363 if (! fp)
364 return NULL;
365
366 // determine size of file (via seeking)
367 fseek(fp, 0, SEEK_END);
368 {
369 (*length) = (int)ftell(fp);
370 }
371 fseek(fp, 0, SEEK_SET);
372
373 if (ferror(fp) || *length < 0)
374 {
375 fclose(fp);
376 return NULL;
377 }
378
379 u8_t *data = (u8_t *) malloc(*length + 1);
380
381 if (! data)
382 FatalError("Out of memory (%d bytes for FileLoad)\n", *length);
383
384 // ensure buffer is NUL-terminated
385 data[*length] = 0;
386
387 if (*length > 0 && 1 != fread(data, *length, 1, fp))
388 {
389 FileFree(data);
390 fclose(fp);
391 return NULL;
392 }
393
394 fclose(fp);
395
396 return data;
397 }
398
399
FileFree(u8_t * mem)400 void FileFree(u8_t *mem)
401 {
402 free((void*) mem);
403 }
404
405
406 //
407 // Note: returns false when the path doesn't exist.
408 //
PathIsDirectory(const char * path)409 bool PathIsDirectory(const char *path)
410 {
411 #ifdef WIN32
412 char old_dir[MAX_PATH+1];
413
414 if (GetCurrentDirectory(MAX_PATH, (LPSTR)old_dir) == FALSE)
415 return false;
416
417 bool result = SetCurrentDirectory(path);
418
419 SetCurrentDirectory(old_dir);
420
421 return result;
422
423 #else // UNIX or MACOSX
424
425 struct stat finfo;
426
427 if (stat(path, &finfo) != 0)
428 return false;
429
430 return (S_ISDIR(finfo.st_mode)) ? true : false;
431 #endif
432 }
433
434
FileFindInPath(const char * paths,const char * base_name)435 const char * FileFindInPath(const char *paths, const char *base_name)
436 {
437 // search through the path list (separated by ';') to find the file.
438 // If found, the complete filename is returned (which must be freed
439 // using StringFree). If not found, NULL is returned.
440
441 for (;;)
442 {
443 const char *sep = strchr(paths, ';');
444 size_t len = sep ? (sep - paths) : strlen(paths);
445
446 SYS_ASSERT(len > 0);
447
448 const char *filename = StringPrintf("%.*s/%s", (int)len, paths, base_name);
449
450 // fprintf(stderr, "Trying data file: [%s]\n", filename);
451
452 if (FileExists(filename))
453 return filename;
454
455 StringFree(filename);
456
457 if (! sep)
458 return NULL; // not found
459
460 paths = sep + 1;
461 }
462 }
463
464
465 //------------------------------------------------------------------------
466
ScanDirectory(const char * path,directory_iter_f func,void * priv_dat)467 int ScanDirectory(const char *path, directory_iter_f func, void *priv_dat)
468 {
469 int count = 0;
470
471 #ifdef WIN32
472
473 // this is a bit clunky. We set the current directory to the
474 // target and use FindFirstFile with "*.*" as the pattern.
475 // Afterwards we restore the current directory.
476
477 char old_dir[MAX_PATH+1];
478
479 if (GetCurrentDirectory(MAX_PATH, (LPSTR)old_dir) == FALSE)
480 return SCAN_ERROR;
481
482 if (SetCurrentDirectory(path) == FALSE)
483 return SCAN_ERR_NoExist;
484
485 WIN32_FIND_DATA fdata;
486
487 HANDLE handle = FindFirstFile("*.*", &fdata);
488 if (handle == INVALID_HANDLE_VALUE)
489 {
490 SetCurrentDirectory(old_dir);
491
492 return 0; //??? (GetLastError() == ERROR_FILE_NOT_FOUND) ? 0 : SCAN_ERROR;
493 }
494
495 do
496 {
497 int flags = 0;
498
499 if (fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
500 flags |= SCAN_F_IsDir;
501
502 if (fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
503 flags |= SCAN_F_ReadOnly;
504
505 if (fdata.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
506 flags |= SCAN_F_Hidden;
507
508 // minor kludge for consistency with Unix
509 if (fdata.cFileName[0] == '.' && isalpha(fdata.cFileName[1]))
510 flags |= SCAN_F_Hidden;
511
512 if (strcmp(fdata.cFileName, ".") == 0 ||
513 strcmp(fdata.cFileName, "..") == 0)
514 {
515 // skip the funky "." and ".." dirs
516 }
517 else
518 {
519 (* func)(fdata.cFileName, flags, priv_dat);
520
521 count++;
522 }
523 }
524 while (FindNextFile(handle, &fdata) != FALSE);
525
526 FindClose(handle);
527
528 SetCurrentDirectory(old_dir);
529
530
531 #else // ---- UNIX ------------------------------------------------
532
533 DIR *handle = opendir(path);
534 if (handle == NULL)
535 return SCAN_ERR_NoExist;
536
537 for (;;)
538 {
539 const struct dirent *fdata = readdir(handle);
540 if (fdata == NULL)
541 break;
542
543 if (strlen(fdata->d_name) == 0)
544 continue;
545
546 // skip the funky "." and ".." dirs
547 if (strcmp(fdata->d_name, ".") == 0 ||
548 strcmp(fdata->d_name, "..") == 0)
549 continue;
550
551
552 const char *full_name = StringPrintf("%s/%s", path, fdata->d_name);
553
554 struct stat finfo;
555
556 if (stat(full_name, &finfo) != 0)
557 {
558 DebugPrintf(".... stat failed: %s\n", strerror(errno));
559 StringFree(full_name);
560 continue;
561 }
562
563 StringFree(full_name);
564
565
566 int flags = 0;
567
568 if (S_ISDIR(finfo.st_mode))
569 flags |= SCAN_F_IsDir;
570
571 if ((finfo.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0)
572 flags |= SCAN_F_ReadOnly;
573
574 if (fdata->d_name[0] == '.' && isalpha(fdata->d_name[1]))
575 flags |= SCAN_F_Hidden;
576
577 (* func)(fdata->d_name, flags, priv_dat);
578
579 count++;
580 }
581
582 closedir(handle);
583 #endif
584
585 return count;
586 }
587
588
589 //------------------------------------------------------------------------
590
GetExecutablePath(const char * argv0)591 const char *GetExecutablePath(const char *argv0)
592 {
593 char *path;
594
595 #ifdef WIN32
596 path = StringNew(PATH_MAX+2);
597
598 int length = GetModuleFileName(GetModuleHandle(NULL), path, PATH_MAX);
599
600 if (length > 0 && length < PATH_MAX)
601 {
602 if (access(path, 0) == 0) // sanity check
603 {
604 FilenameStripBase(path);
605 return path;
606 }
607 }
608
609 // didn't work, free the memory
610 StringFree(path);
611
612 #elif !defined(__APPLE__) // UNIX
613 path = StringNew(PATH_MAX+2);
614
615 int length = readlink("/proc/self/exe", path, PATH_MAX);
616
617 if (length > 0)
618 {
619 path[length] = 0; // add the missing NUL
620
621 if (access(path, 0) == 0) // sanity check
622 {
623 FilenameStripBase(path);
624 return path;
625 }
626 }
627
628 // didn't work, free the memory
629 StringFree(path);
630
631 #else
632 /*
633 from http://www.hmug.org/man/3/NSModule.html
634
635 extern int _NSGetExecutablePath(char *buf, uint32_t *bufsize);
636
637 _NSGetExecutablePath copies the path of the executable
638 into the buffer and returns 0 if the path was successfully
639 copied in the provided buffer. If the buffer is not large
640 enough, -1 is returned and the expected buffer size is
641 copied in *bufsize.
642 */
643 uint32_t pathlen = PATH_MAX * 2;
644
645 path = StringNew(pathlen+2);
646
647 if (0 == _NSGetExecutablePath(path, &pathlen))
648 {
649 // FIXME: will this be _inside_ the .app folder???
650 FilenameStripBase(path);
651 return path;
652 }
653
654 // didn't work, free the memory
655 StringFree(path);
656 #endif
657
658 // fallback method: use argv[0]
659 path = StringDup(argv0);
660
661 #ifdef __APPLE__
662 // FIXME: check if _inside_ the .app folder
663 #endif
664
665 FilenameStripBase(path);
666 return path;
667 }
668
669 //--- editor settings ---
670 // vi:ts=4:sw=4:noexpandtab
671