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