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