1 /**
2  * \file z-file.c
3  * \brief Low-level file (and directory) handling
4  *
5  * Copyright (c) 1997-2007 Ben Harrison, pelpel, Andi Sidwell, Matthew Jones
6  *
7  * This work is free software; you can redistribute it and/or modify it
8  * under the terms of:
9  *
10  * b) the "Angband licence":
11  *    This software may be copied and distributed for educational, research,
12  *    and not for profit purposes provided that this copyright and statement
13  *    are included in all such copies.  Other copyrights may also apply.
14  */
15 /* Forward-compatibility with V4XX */
16 #undef true
17 #undef false
18 #define true TRUE
19 #define false FALSE
20 #define PATH_SEPC PATH_SEP[0]
21 #define mem_zalloc ralloc
22 #define assert(X) false
23 #define mem_free rnfree
24 #ifdef SET_UID
25 #define UNUX
26 #define SETGID
27 # if defined(SAFE_SETUID)
28 #   if defined(SAFE_SETUID_POSIX)
29 #      define HAVE_SETEGID
30 #   else
31 #      define HAVE_SETRESGID
32 #   endif
33 # endif
34 #endif /* SET_UID */
35 /* XXX */
36 #include "h-basic.h"
37 #include "z-file.h"
38 #include "z-form.h"
39 #include "z-util.h"
40 #include "z-virt.h"
41 
42 
43 #include <sys/types.h>
44 
45 #ifdef WINDOWS
46 # include <windows.h>
47 # include <io.h>
48 # ifndef CYGWIN
49 #  include <direct.h>
50 # endif
51 #endif
52 
53 #ifdef HAVE_FCNTL_H
54 # include <fcntl.h>
55 #endif
56 
57 #if defined (HAVE_DIRENT_H) || defined (CYGWIN)
58 # include <sys/types.h>
59 # include <dirent.h>
60 #endif
61 
62 #ifdef HAVE_STAT
63 # include <sys/stat.h>
64 #endif
65 
66 #if defined (WINDOWS) && !defined (CYGWIN)
67 # define my_mkdir(path, perms) mkdir(path)
68 #elif defined(HAVE_MKDIR) || defined(MACH_O_CARBON) || defined (CYGWIN)
69 # define my_mkdir(path, perms) mkdir(path, perms)
70 #else
71 # define my_mkdir(path, perms) false
72 #endif
73 
74 #if defined (WINDOWS) && !defined (CYGWIN)
75 #ifdef WIN32
76 #define INVALID_FILE_NAME (DWORD)0xFFFFFFFF
77 #else /* WIN32 -> WIN16/DOS */
78 #define FA_LABEL    0x08        /* Volume label */
79 #define FA_DIREC    0x10        /* Directory */
80 unsigned _cdecl _dos_getfileattr(const char *, unsigned *);
81 #endif /* WIN32/WIN16 */
82 #endif
83 
84 /**
85  * Player info
86  */
87 int player_uid = 0;
88 int player_euid = 0;
89 int player_egid = 0;
90 
91 
92 
93 
94 /**
95  * Drop permissions
96  */
safe_setuid_drop(void)97 void safe_setuid_drop(void)
98 {
99 #ifdef SETGID
100 # if defined(HAVE_SETRESGID)
101 
102 	if (setresgid(-1, getgid(), -1) != 0)
103 		quit("setegid(): cannot drop permissions correctly!");
104 
105 # else
106 
107 	if (setegid(getgid()) != 0)
108 		quit("setegid(): cannot drop permissions correctly!");
109 
110 # endif
111 #endif /* SETGID */
112 }
113 
114 
115 /**
116  * Grab permissions
117  */
safe_setuid_grab(void)118 void safe_setuid_grab(void)
119 {
120 #ifdef SETGID
121 # if defined(HAVE_SETRESGID)
122 
123 	if (setresgid(-1, player_egid, -1) != 0)
124 		quit("setegid(): cannot grab permissions correctly!");
125 
126 # elif defined(HAVE_SETEGID)
127 
128 	if (setegid(player_egid) != 0)
129 		quit("setegid(): cannot grab permissions correctly!");
130 
131 # endif
132 #endif /* SETGID */
133 }
134 
135 
136 
137 
138 /**
139  * Apply special system-specific processing before dealing with a filename.
140  */
path_parse(char * buf,size_t max,const char * file)141 static void path_parse(char *buf, size_t max, const char *file)
142 {
143 	/* Accept the filename */
144 	my_strcpy(buf, file, max);
145 }
146 
147 
path_process(char * buf,size_t len,size_t * cur_len,const char * path)148 static void path_process(char *buf, size_t len, size_t *cur_len,
149 						 const char *path)
150 {
151 #if defined(UNIX)
152 
153 	/* Home directory on Unixes */
154 	if (path[0] == '~') {
155 		const char *s;
156 		const char *username = path + 1;
157 
158 		struct passwd *pw;
159 		char user[128];
160 
161 		/* Look for non-user portion of the file */
162 		s = strstr(username, PATH_SEP);
163 		if (s) {
164 			int i;
165 
166 			/* Keep username a decent length */
167 			if (s >= username + sizeof(user)) return;
168 
169 			for (i = 0; username < s; ++i) user[i] = *username++;
170 			user[i] = '\0';
171 			username = user;
172 		}
173 
174 		/* Look up a user (or "current" user) */
175 		pw = username[0] ? getpwnam(username) : getpwuid(getuid());
176 		if (!pw) return;
177 
178 		/* Copy across */
179 		strnfcat(buf, len, cur_len, "%s%s", pw->pw_dir, PATH_SEP);
180 		if (s) strnfcat(buf, len, cur_len, "%s", s);
181 	} else
182 
183 #endif /* defined(UNIX) */
184 
185 		strnfcat(buf, len, cur_len, "%s", path);
186 }
187 
188 
189 
190 /**
191  * Create a new path string by appending a 'leaf' to 'base'.
192  *
193  * On Unixes, we convert a tidle at the beginning of a basename to mean the
194  * directory, complicating things a little, but better now than later.
195  *
196  * Remember to free the return value.
197  */
path_build(char * buf,size_t len,const char * base,const char * leaf)198 size_t path_build(char *buf, size_t len, const char *base, const char *leaf)
199 {
200 	size_t cur_len = 0;
201 	int starts_with_separator;
202 
203 	buf[0] = '\0';
204 
205 	if (!leaf || !leaf[0]) {
206 		if (base && base[0])
207 			path_process(buf, len, &cur_len, base);
208 
209 		return cur_len;
210 	}
211 
212 
213 	/*
214 	 * If the leafname starts with the seperator,
215 	 *   or with the tilde (on Unix),
216 	 *   or there's no base path,
217 	 * We use the leafname only.
218 	 */
219 	starts_with_separator = (!base || !base[0]) || prefix(leaf, PATH_SEP);
220 #if defined(UNIX)
221 	starts_with_separator = starts_with_separator || leaf[0] == '~';
222 #endif
223 	if (starts_with_separator) {
224 		path_process(buf, len, &cur_len, leaf);
225 		return cur_len;
226 	}
227 
228 
229 	/* There is both a relative leafname and a base path from which it is
230 	 * relative */
231 	path_process(buf, len, &cur_len, base);
232 
233 	if (!suffix(base, PATH_SEP)) {
234 		/* Append separator if it isn't already in the string. */
235 		strnfcat(buf, len, &cur_len, "%s", PATH_SEP);
236 	}
237 
238 	path_process(buf, len, &cur_len, leaf);
239 
240 	return cur_len;
241 }
242 
243 /**
244  * Return the index of the filename in a path, using PATH_SEPC. If no path
245  * separator is found, return 0.
246  */
path_filename_index(const char * path)247 size_t path_filename_index(const char *path)
248 {
249 	int i;
250 
251 	if (strlen(path) == 0)
252 		return 0;
253 
254 	for (i = strlen(path) - 1; i >= 0; i--) {
255 		if (path[i] == PATH_SEPC)
256 			return i + 1;
257 	}
258 
259 	return 0;
260 }
261 
262 /**
263  * ------------------------------------------------------------------------
264  * File-handling API
265  * ------------------------------------------------------------------------ */
266 
267 
268 /* Some defines for compatibility between various build platforms */
269 #ifndef S_IRUSR
270 #define S_IRUSR S_IREAD
271 #endif
272 
273 #ifndef S_IWUSR
274 #define S_IWUSR S_IWRITE
275 #endif
276 
277 /* if the flag O_BINARY is not defined, it is not needed , but we still
278  * need it defined so it will compile */
279 #ifndef O_BINARY
280 #define O_BINARY 0
281 #endif
282 
283 /* Avoid a compiler warning when cross compiling for windows */
284 #ifdef __STRICT_ANSI__
285 FILE *fdopen(int handle, const char *mode);
286 #endif
287 
288 #ifdef USE_SDL_RWOPS
289 #include <SDL.h>
290 #endif
291 
292 /* Private structure to hold file pointers and useful info. */
293 struct ang_file
294 {
295 #ifdef USE_SDL_RWOPS
296 	SDL_RWops *fh;
297 	int error;
298 #else
299 	FILE *fh;
300 #endif
301 	char *fname;
302 	file_mode mode;
303 };
304 
305 
306 
307 /** Utility functions **/
308 
309 /**
310  * Delete file 'fname'.
311  */
file_delete(const char * fname)312 bool file_delete(const char *fname)
313 {
314 	char buf[1024];
315 
316 	/* Get the system-specific paths */
317 	path_parse(buf, sizeof(buf), fname);
318 
319 	return (remove(buf) == 0);
320 }
321 
322 /**
323  * Move file 'fname' to 'newname'.
324  */
file_move(const char * fname,const char * newname)325 bool file_move(const char *fname, const char *newname)
326 {
327 	char buf[1024];
328 	char aux[1024];
329 
330 	/* Get the system-specific paths */
331 	path_parse(buf, sizeof(buf), fname);
332 	path_parse(aux, sizeof(aux), newname);
333 
334 	return (rename(buf, aux) == 0);
335 }
336 
337 
338 /**
339  * Decide whether a file exists or not.
340  */
341 
342 #if defined(HAVE_STAT)
343 
file_exists(const char * fname)344 bool file_exists(const char *fname)
345 {
346 	struct stat st;
347 	return (stat(fname, &st) == 0);
348 }
349 
350 #elif defined(WINDOWS)
351 
file_exists(const char * fname)352 bool file_exists(const char *fname)
353 {
354 	char path[MAX_PATH];
355 	DWORD attrib;
356 
357 	/* API says we mustn't pass anything larger than MAX_PATH */
358 	my_strcpy(path, fname, sizeof(path));
359 
360 	attrib = GetFileAttributes(path);
361 	if (attrib == INVALID_FILE_NAME) return false;
362 	if (attrib & FILE_ATTRIBUTE_DIRECTORY) return false;
363 
364 	return true;
365 }
366 
367 #else
368 
file_exists(const char * fname)369 bool file_exists(const char *fname)
370 {
371 	ang_file *f = file_open(fname, MODE_READ, 0);
372 
373 	if (f) file_close(f);
374 	return (f ? true : false);
375 }
376 
377 #endif
378 
379 /**
380  * Return true if first is newer than second, false otherwise.
381  */
file_newer(const char * first,const char * second)382 bool file_newer(const char *first, const char *second)
383 {
384 #ifdef HAVE_STAT
385 	struct stat stat1, stat2;
386 
387 	/* If the first doesn't exist, the first is not newer. */
388 	if (stat(first, &stat1) != 0) return false;
389 
390 	/* If the second doesn't exist, the first is always newer. */
391 	if (stat(second, &stat2) != 0) return true;
392 
393 	/* Compare modification times. */
394 	return stat1.st_mtime > stat2.st_mtime ? true : false;
395 #else /* HAVE_STAT */
396 	return false;
397 #endif /* !HAVE_STAT */
398 }
399 
400 
401 
402 
403 /** File-handle functions **/
404 
405 void (*file_open_hook)(const char *path, file_type ftype);
406 
407 /**
408  * Open file 'fname', in mode 'mode', with filetype 'ftype'.
409  * Returns file handle or NULL.
410  */
file_open(const char * fname,file_mode mode,file_type ftype)411 ang_file *file_open(const char *fname, file_mode mode, file_type ftype)
412 {
413 	ang_file *f = mem_zalloc(sizeof(ang_file));
414 	char buf[1024];
415 
416 	(void)ftype;
417 
418 	/* Get the system-specific path */
419 	path_parse(buf, sizeof(buf), fname);
420 
421 #ifdef USE_SDL_RWOPS
422 {
423 	char *rwm;
424 	switch (mode) {
425 		case MODE_WRITE: rwm = "wb"; break;
426 		case MODE_READ: rwm = "rb"; break;
427 		case MODE_APPEND: rwm = "a+"; break;
428 		case MODE_READWRITE: rwm = "r+b"; break;
429 		default: assert(0); break;
430 	}
431 	f->error = 0;
432 	f->fh = SDL_RWFromFile(buf, rwm);
433 }
434 #else
435 	switch (mode) {
436 		case MODE_WRITE: {
437 			if (ftype == FTYPE_SAVE) {
438 				/* open only if the file does not exist */
439 				int fd;
440 				fd = open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, S_IRUSR | S_IWUSR);
441 				if (fd < 0) {
442 					/* there was some error */
443 					f->fh = NULL;
444 				} else {
445 					f->fh = fdopen(fd, "wb");
446 				}
447 			} else {
448 				f->fh = fopen(buf, "wb");
449 			}
450 			break;
451 		}
452 		case MODE_READ:
453 			f->fh = fopen(buf, "rb");
454 			break;
455 		case MODE_APPEND:
456 			f->fh = fopen(buf, "a+");
457 			break;
458 		case MODE_READWRITE:
459 			f->fh = fopen(buf, "r+b");
460 			break;
461 		default:
462 			assert(0);
463 	}
464 #endif
465 	if (f->fh == NULL) {
466 		mem_free(f);
467 		return NULL;
468 	}
469 
470 	f->fname = (char*)string_make(buf);
471 	f->mode = mode;
472 
473 	if (mode != MODE_READ && file_open_hook)
474 		file_open_hook(buf, ftype);
475 
476 	return f;
477 }
478 
479 
480 /**
481  * Close file handle 'f'.
482  */
file_close(ang_file * f)483 bool file_close(ang_file *f)
484 {
485 #ifdef USE_SDL_RWOPS
486 	if (SDL_RWclose(f->fh) != 0)
487 		return false;
488 #else
489 	if (fclose(f->fh) != 0)
490 		return false;
491 #endif
492 
493 	mem_free(f->fname);
494 	mem_free(f);
495 
496 	return true;
497 }
498 
499 
500 /**
501  * Check errors in file handle 'f'.
502  */
file_error(ang_file * f)503 bool file_error(ang_file *f)
504 {
505 #ifdef USE_SDL_RWOPS
506 	if (f->error) return true;
507 	return false;
508 #else
509 	if (ferror(f->fh) != 0)
510 		return true;
511 
512 	if (f->mode == MODE_WRITE && fflush(f->fh) == EOF)
513 		return true;
514 
515 	return false;
516 #endif
517 }
518 
519 
520 /** Locking functions **/
521 
522 /**
523  * Lock a file using POSIX locks, on platforms where this is supported.
524  */
file_lock(ang_file * f)525 bool file_lock(ang_file *f)
526 {
527 #if defined(HAVE_FCNTL_H) && defined(UNIX)
528 	struct flock lock;
529 	lock.l_type = (f->mode == MODE_READ ? F_RDLCK : F_WRLCK);
530 	lock.l_whence = SEEK_SET;
531 	lock.l_start = 0;
532 	lock.l_len = 0;
533 	lock.l_pid = 0;
534 	fcntl(fileno(f->fh), F_SETLKW, &lock);
535 #endif /* HAVE_FCNTL_H && UNIX */
536 	return true;
537 }
538 
539 /**
540  * Unlock a file locked using file_lock().
541  */
file_unlock(ang_file * f)542 bool file_unlock(ang_file *f)
543 {
544 #if defined(HAVE_FCNTL_H) && defined(UNIX)
545 	struct flock lock;
546 	lock.l_type = F_UNLCK;
547 	lock.l_whence = SEEK_SET;
548 	lock.l_start = 0;
549 	lock.l_len = 0;
550 	lock.l_pid = 0;
551 	fcntl(fileno(f->fh), F_SETLK, &lock);
552 #endif /* HAVE_FCNTL_H && UNIX */
553 	return true;
554 }
555 
556 
557 /** Byte-based IO and functions **/
558 
559 /**
560  * Seek to location 'pos' in file 'f', from the current position.
561  */
file_skip(ang_file * f,int bytes)562 bool file_skip(ang_file *f, int bytes)
563 {
564 #ifdef USE_SDL_RWOPS
565 	return (SDL_RWseek(f->fh, bytes, RW_SEEK_CUR) > -1);
566 #else
567 	return (fseek(f->fh, bytes, SEEK_CUR) == 0);
568 #endif
569 }
570 
571 /**
572  * Seek to location 'pos' in file 'f', from the start of file.
573  */
file_seek(ang_file * f,int bytes)574 bool file_seek(ang_file *f, int bytes)
575 {
576 #ifdef USE_SDL_RWOPS
577 	return (SDL_RWseek(f->fh, bytes, RW_SEEK_SET) > -1);
578 #else
579 	return (fseek(f->fh, bytes, SEEK_SET) == 0);
580 #endif
581 }
582 
583 /**
584  * Return current location in file 'f'.
585  */
file_tell(ang_file * f)586 size_t file_tell(ang_file *f)
587 {
588 #ifdef USE_SDL_RWOPS
589 	Sint64 pos = SDL_RWtell(f->fh);
590 	if (pos < 0)
591 	{
592 		f->error = TRUE;
593 		return 0;
594 	}
595 	return (size_t)pos;
596 #else
597 	return ftell(f->fh);
598 #endif
599 }
600 
601 /**
602  * Read a single, 8-bit character from file 'f'.
603  */
file_readc(ang_file * f,byte * b)604 bool file_readc(ang_file *f, byte *b)
605 {
606 #ifdef USE_SDL_RWOPS
607 	size_t i = SDL_RWread(f->fh, b, 1, 1);
608 	if (i == 0) return false;
609 	return true;
610 #else
611 	int i = fgetc(f->fh);
612 
613 	if (i == EOF)
614 		return false;
615 
616 	*b = (byte)i;
617 	return true;
618 #endif
619 }
620 
621 /**
622  * Write a single, 8-bit character 'b' to file 'f'.
623  */
file_writec(ang_file * f,byte b)624 bool file_writec(ang_file *f, byte b)
625 {
626 #ifdef USE_SDL_RWOPS
627 	size_t i = SDL_RWwrite(f->fh, (void*)&b, 1, 1);
628 	if (i == 0) return false;
629 	return true;
630 #else
631 	return file_write(f, (const char *)&b, 1);
632 #endif
633 }
634 
635 /**
636  * Read 'n' bytes from file 'f' into array 'buf'.
637  */
file_read(ang_file * f,char * buf,size_t n)638 size_t file_read(ang_file *f, char *buf, size_t n)
639 {
640 #ifdef USE_SDL_RWOPS
641 	size_t read;
642 
643 	SDL_ClearError();
644 	read = SDL_RWread(f->fh, (void*)buf, 1, n);
645 
646 	if (read == 0 && SDL_GetError()[0] == '\0')
647 		return -1;
648 	else
649 		return read;
650 #else
651 	size_t read = fread(buf, 1, n, f->fh);
652 
653 	if (read == 0 && ferror(f->fh))
654 		return -1;
655 	else
656 		return read;
657 #endif
658 }
659 
660 /**
661  * Append 'n' bytes of array 'buf' to file 'f'.
662  */
file_write(ang_file * f,const char * buf,size_t n)663 bool file_write(ang_file *f, const char *buf, size_t n)
664 {
665 #ifdef USE_SDL_RWOPS
666 	return SDL_RWwrite(f->fh, (void*)buf, 1, n) == n;
667 #else
668 	return fwrite(buf, 1, n, f->fh) == n;
669 #endif
670 }
671 
672 /** Line-based IO **/
673 
674 /**
675  * Read a line of text from file 'f' into buffer 'buf' of size 'n' bytes.
676  *
677  * Support both \r\n and \n as line endings, but not the outdated \r that used
678  * to be used on Macs.  Replace non-printables with '?', and \ts with ' '.
679  */
680 #define TAB_COLUMNS 4
681 
file_getl(ang_file * f,char * buf,size_t len)682 bool file_getl(ang_file *f, char *buf, size_t len)
683 {
684 	bool seen_cr = false;
685 	byte b;
686 	size_t i = 0;
687 
688 	/* Leave a byte for the terminating 0 */
689 	size_t max_len = len - 1;
690 
691 	while (i < max_len) {
692 		char c;
693 
694 		if (!file_readc(f, &b)) {
695 			buf[i] = '\0';
696 			return (i == 0) ? false : true;
697 		}
698 
699 		c = (char) b;
700 
701 		if (c == '\r') {
702 			seen_cr = true;
703 			continue;
704 		}
705 
706 		if (seen_cr && c != '\n') {
707 			file_skip(f, -1);//fseek(f->fh, -1, SEEK_CUR);
708 			buf[i] = '\0';
709 			return true;
710 		}
711 
712 		if (c == '\n') {
713 			buf[i] = '\0';
714 			return true;
715 		}
716 
717 		/* Expand tabs */
718 		if (c == '\t') {
719 			/* Next tab stop */
720 			size_t tabstop = ((i + TAB_COLUMNS) / TAB_COLUMNS) * TAB_COLUMNS;
721 			if (tabstop >= len) break;
722 
723 			/* Convert to spaces */
724 			while (i < tabstop)
725 				buf[i++] = ' ';
726 
727 			continue;
728 		}
729 
730 		buf[i++] = c;
731 	}
732 
733 	buf[i] = '\0';
734 	return true;
735 }
736 
737 /**
738  * Append a line of text 'buf' to the end of file 'f', using system-dependent
739  * line ending.
740  */
file_put(ang_file * f,const char * buf)741 bool file_put(ang_file *f, const char *buf)
742 {
743 	return file_write(f, buf, strlen(buf));
744 }
745 
746 /*
747  * The comp.lang.c FAQ recommends this pairing for varargs functions.
748  * See <http://c-faq.com/varargs/handoff.html>
749  */
750 
751 /**
752  * Append a formatted line of text to the end of file 'f'.
753  *
754  * file_putf() is the ellipsis version. Most file output will call this
755  * version. It calls file_vputf() to do the real work. It returns true
756  * if the write was successful and false otherwise.
757  */
file_putf(ang_file * f,const char * fmt,...)758 bool file_putf(ang_file *f, const char *fmt, ...)
759 {
760 	va_list vp;
761 	bool status;
762 
763 	if (!f) return false;
764 
765 	va_start(vp, fmt);
766 	status = file_vputf(f, fmt, vp);
767 	va_end(vp);
768 
769 	return status;
770 }
771 
772 /**
773  * Append a formatted line of text to the end of file 'f'.
774  *
775  * file_vputf() is the va_list version. It returns true if the write was
776  * successful and false otherwise.
777  */
file_vputf(ang_file * f,const char * fmt,va_list vp)778 bool file_vputf(ang_file *f, const char *fmt, va_list vp)
779 {
780 	char buf[1024];
781 	int n;
782 
783 	if (!f) return false;
784 
785 	n = vstrnfmt(buf, sizeof(buf), fmt, vp);
786 
787 	/* The semantics of vstrnfmt are weird and its return
788 	 * value is ill-defined. However, return value of zero
789 	 * almost definitely means there was an error (unless you pass it
790 	 * an empty format string with zero varargs, I guess). */
791 	if (n == 0) return false;
792 
793 	return file_put(f, buf);
794 }
795 
796 /**
797  * Copy a file.
798  */
file_copy(const char * src,const char * dst,file_type ftype)799 bool file_copy(const char *src, const char *dst, file_type ftype)
800 {
801 	ang_file *sfile;
802 	ang_file *dfile;
803 	char buf[1024];
804 	size_t n;
805 
806 	sfile = file_open(src, MODE_READ, ftype);
807 	if (sfile == NULL)
808 	{
809 		return false;
810 	}
811 
812 	dfile = file_open(dst, MODE_WRITE, ftype);
813 	if (dfile == NULL)
814 	{
815 		file_close(sfile);
816 		return false;
817 	}
818 
819 	while ((n = file_read(sfile, buf, 1024)))
820 	{
821 		if (!file_write(dfile, buf, n)) break;
822     }
823 
824 	file_close(sfile);
825 	file_close(dfile);
826 	return true;
827 }
828 
829 #ifdef WINDOWS
830 #ifndef INVALID_FILE_NAME
831 #define INVALID_FILE_NAME (DWORD)0xFFFFFFFF
832 #endif
833 #endif
834 
dir_exists(const char * path)835 bool dir_exists(const char *path)
836 {
837 #ifdef WINDOWS
838 # ifdef WIN32
839 	DWORD attrib;
840 
841 	/* Examine */
842 	attrib = GetFileAttributes(path);
843 
844 	/* Require valid filename */
845 	if (attrib == INVALID_FILE_NAME) return (FALSE);
846 
847 	/* Require directory */
848 	if (!(attrib & FILE_ATTRIBUTE_DIRECTORY)) return (FALSE);
849 # else /* WIN16 */
850 	unsigned int attrib;
851 	/* Examine and verify */
852 	if (_dos_getfileattr(path, &attrib)) return (FALSE);
853 
854 	/* Prohibit something */
855 	if (attrib & FA_LABEL) return (FALSE);
856 
857 	/* Require directory */
858 	if (!(attrib & FA_DIREC)) return (FALSE);
859 
860 # endif /* WIN16/WIN32 */
861 #else /* Not on WINDOWS */
862 #ifdef HAVE_STAT
863 	struct stat buf;
864 	if (stat(path, &buf) != 0)
865 	{
866 		return false;
867 	}
868 	else if (buf.st_mode & S_IFDIR)
869 	{
870 		return true;
871 	}
872 	else
873 		return false;
874 #endif
875 #endif
876 	/* If we got here, we can't reliably tell
877 	 * if a directory exists.. */
878 	return true;
879 }
880 
881 #ifdef HAVE_STAT
dir_create(const char * path)882 bool dir_create(const char *path)
883 {
884 	const char *ptr;
885 	char buf[512];
886 
887 	/* If the directory already exists then we're done */
888 	if (dir_exists(path)) return true;
889 
890 	#ifdef WINDOWS
891 	/* If we're on windows, we need to skip past the "C:" part. */
892 	if (isalpha(path[0]) && path[1] == ':') path += 2;
893 	#endif
894 
895 	/* Iterate through the path looking for path segements. At each step,
896 	 * create the path segment if it doesn't already exist. */
897 	for (ptr = path; *ptr; ptr++) {
898 		if (*ptr == PATH_SEPC) {
899 			/* Find the length of the parent path string */
900 			size_t len = (size_t)(ptr - path);
901 
902 			/* Skip the initial slash */
903 			if (len == 0) continue;
904 
905 			/* If this is a duplicate path separator, continue */
906 			if (*(ptr - 1) == PATH_SEPC) continue;
907 
908 			/* We can't handle really big filenames */
909 			if (len - 1 > 512) return false;
910 
911 			/* Create the parent path string, plus null-padding */
912 			my_strcpy(buf, path, len + 1);
913 
914 			/* Skip if the parent exists */
915 			if (dir_exists(buf)) continue;
916 
917 			/* The parent doesn't exist, so create it or fail */
918 			if (my_mkdir(buf, 0755) != 0) return false;
919 		}
920 	}
921 	return my_mkdir(path, 0755) == 0 ? true : false;
922 }
923 
924 #else /* HAVE_STAT */
dir_create(const char * path)925 bool dir_create(const char *path) { return false; }
926 #endif /* !HAVE_STAT */
927 
928 /**
929  * ------------------------------------------------------------------------
930  * Directory scanning API
931  * ------------------------------------------------------------------------ */
932 
933 
934 /*
935  * For information on what these are meant to do, please read the header file.
936  */
937 
938 #ifdef WINDOWS
939 
940 
941 /* System-specific struct */
942 struct ang_dir
943 {
944 	HANDLE h;
945 	char *first_file;
946 };
947 
my_dopen(const char * dirname)948 ang_dir *my_dopen(const char *dirname)
949 {
950 	WIN32_FIND_DATA fd;
951 	HANDLE h;
952 	ang_dir *dir;
953 
954 	/* Try to open it */
955 	h = FindFirstFile(format("%s\\*", dirname), &fd);
956 
957 	/* Abort */
958 	if (h == INVALID_HANDLE_VALUE)
959 		return NULL;
960 
961 	/* Set up the handle */
962 	dir = mem_zalloc(sizeof(ang_dir));
963 	dir->h = h;
964 	dir->first_file = string_make(fd.cFileName);
965 
966 	/* Success */
967 	return dir;
968 }
969 
my_dread(ang_dir * dir,char * fname,size_t len)970 bool my_dread(ang_dir *dir, char *fname, size_t len)
971 {
972 	WIN32_FIND_DATA fd;
973 	bool ok;
974 
975 	/* Try the first file */
976 	if (dir->first_file) {
977 		/* Copy the string across, then free it */
978 		my_strcpy(fname, dir->first_file, len);
979 		mem_free(dir->first_file);
980 		dir->first_file = NULL;
981 
982 		/* Wild success */
983 		return true;
984 	}
985 
986 	/* Try the next file */
987 	while (1) {
988 		ok = FindNextFile(dir->h, &fd);
989 		if (!ok) return false;
990 
991 		/* Skip directories */
992 		if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ||
993 		    strcmp(fd.cFileName, ".") == 0 ||
994 		    strcmp(fd.cFileName, "..") == 0)
995 			continue;
996 
997 		/* Take this one */
998 		break;
999 	}
1000 
1001 	/* Copy name */
1002 	my_strcpy(fname, fd.cFileName, len);
1003 
1004 	return true;
1005 }
1006 
my_dclose(ang_dir * dir)1007 void my_dclose(ang_dir *dir)
1008 {
1009 	/* Close directory */
1010 	if (dir->h)
1011 		FindClose(dir->h);
1012 
1013 	/* Free memory */
1014 	mem_free(dir->first_file);
1015 	mem_free(dir);
1016 }
1017 
1018 #else /* WINDOWS */
1019 
1020 #ifdef HAVE_DIRENT_H
1021 
1022 /* Define our ang_dir type */
1023 struct ang_dir
1024 {
1025 	DIR *d;
1026 	char *dirname;
1027 };
1028 
my_dopen(const char * dirname)1029 ang_dir *my_dopen(const char *dirname)
1030 {
1031 	ang_dir *dir;
1032 	DIR *d;
1033 
1034 	/* Try to open the directory */
1035 	d = opendir(dirname);
1036 	if (!d) return NULL;
1037 
1038 	/* Allocate memory for the handle */
1039 	dir = mem_zalloc(sizeof(ang_dir));
1040 	if (!dir) {
1041 		closedir(d);
1042 		return NULL;
1043 	}
1044 
1045 	/* Set up the handle */
1046 	dir->d = d;
1047 	dir->dirname = (char*)string_make(dirname);
1048 
1049 	/* Success */
1050 	return dir;
1051 }
1052 
my_dread(ang_dir * dir,char * fname,size_t len)1053 bool my_dread(ang_dir *dir, char *fname, size_t len)
1054 {
1055 	struct dirent *entry;
1056 	struct stat filedata;
1057 	char path[1024];
1058 
1059 	assert(dir != NULL);
1060 
1061 	/* Try reading another entry */
1062 	while (1) {
1063 		entry = readdir(dir->d);
1064 		if (!entry) return false;
1065 
1066 		path_build(path, sizeof path, dir->dirname, entry->d_name);
1067 
1068 		/* Check to see if it exists */
1069 		if (stat(path, &filedata) != 0)
1070 			continue;
1071 
1072 		/* Check to see if it's a directory */
1073 		if (S_ISDIR(filedata.st_mode))
1074 			continue;
1075 
1076 		/* We've found something worth returning */
1077 		break;
1078 	}
1079 
1080 	/* Copy the filename */
1081 	my_strcpy(fname, entry->d_name, len);
1082 
1083 	return true;
1084 }
1085 
my_dclose(ang_dir * dir)1086 void my_dclose(ang_dir *dir)
1087 {
1088 	/* Close directory */
1089 	if (dir->d)
1090 		closedir(dir->d);
1091 
1092 	/* Free memory */
1093 	mem_free(dir->dirname);
1094 	mem_free(dir);
1095 }
1096 
1097 #endif /* HAVE_DIRENT_H */
1098 #endif /* WINDOWS */
1099 
1100