Angband 3.0.9b.
[angband.git] / src / z-file.c
blob4498da3d9906627f650b107a0d0f72ea5ed6c912
1 /*
2 * File: z-file.c
3 * Purpose: Low-level file (and directory) handling
5 * Copyright (c) 1997-2007 Ben Harrison, pelpel, Andrew Sidwell
7 * This work is free software; you can redistribute it and/or modify it
8 * under the terms of either:
10 * a) the GNU General Public License as published by the Free Software
11 * Foundation, version 2, or
13 * b) the "Angband licence":
14 * This software may be copied and distributed for educational, research,
15 * and not for profit purposes provided that this copyright and statement
16 * are included in all such copies. Other copyrights may also apply.
18 #include "angband.h"
19 #include "z-file.h"
21 #ifndef RISCOS
22 # include <sys/types.h>
23 # include <sys/stat.h>
24 #endif
26 #ifdef WINDOWS
27 # include <io.h>
28 #endif
33 * Drop permissions
35 void safe_setuid_drop(void)
37 #ifdef SET_UID
38 # if defined(HAVE_SETRESGID)
40 if (setresgid(-1, getgid(), -1) != 0)
41 quit("setegid(): cannot drop permissions correctly!");
43 # else
45 if (setegid(getgid()) != 0)
46 quit("setegid(): cannot drop permissions correctly!");
48 # endif
49 #endif /* SET_UID */
54 * Grab permissions
56 void safe_setuid_grab(void)
58 #ifdef SET_UID
59 # if defined(HAVE_SETRESGID)
61 if (setresgid(-1, player_egid, -1) != 0)
62 quit("setegid(): cannot grab permissions correctly!");
64 # elif defined(HAVE_SETEGID)
66 if (setegid(player_egid) != 0)
67 quit("setegid(): cannot grab permissions correctly!");
69 # endif
70 #endif /* SET_UID */
76 * The concept of the file routines is that all file handling should be done
77 * using as few routines as possible, since every machine is slightly
78 * different, but these routines always have the same semantics.
80 * Prhaps we should use "path_parse()" to convert from "canonical" filenames
81 * (optional leading tildes, internal wildcards, slash as the path seperator,
82 * etc) to "system" filenames (no special symbols, system-specific path
83 * seperator, etc). This would allow the program itself to assume that all
84 * filenames are "Unix" filenames, and explicitly "extract" such filenames if
85 * needed (by "path_parse()", or perhaps "path_canon()"). XXX
87 * path_temp() should probably return a "canonical" filename. XXX
89 * Note that "my_fopen()" and "my_open()" and "my_make()" and "my_kill()"
90 * and "my_move()" and "my_copy()" should all take "canonical" filenames.
92 * Canonical filenames use a leading slash to indicate an absolute path, and a
93 * leading tilde to indicate a special directory. They default to a relative
94 * path. DOS/Windows uses a leading "drivename plus colon" to indicate the
95 * use of a "special drive", and then the rest of the path is parsed normally,
96 * and MACINTOSH uses a leading colon to indicate a relative path, and an
97 * embedded colon to indicate a "drive plus absolute path", and finally
98 * defaults to a file in the current working directory, which may or may
99 * not be defined.
103 #ifdef HAVE_MKSTEMP
105 FILE *my_fopen_temp(char *buf, size_t max)
107 int fd;
109 /* Prepare the buffer for mkstemp */
110 my_strcpy(buf, "/tmp/anXXXXXX", max);
112 /* Secure creation of a temporary file */
113 fd = mkstemp(buf);
115 /* Check the file-descriptor */
116 if (fd < 0) return (NULL);
118 /* Return a file stream */
119 return (fdopen(fd, "w"));
122 #else /* HAVE_MKSTEMP */
125 * Consider rewriting this so it uses its own buffer.
127 FILE *my_fopen_temp(char *buf, size_t max)
129 const char *s;
131 /* Temp file */
132 s = tmpnam(NULL);
134 /* Oops */
135 if (!s) return (NULL);
137 /* Copy to buffer */
138 my_strcpy(buf, s, max);
140 /* Open the file */
141 return (my_fopen(buf, "w"));
144 #endif /* HAVE_MKSTEMP */
148 * Hack -- replacement for "fgets()"
150 * Read a string, without a newline, to a file
152 * Process tabs, strip internal non-printables
154 #define TAB_COLUMNS 8
156 errr my_fgets(FILE *fff, char *buf, size_t n)
158 u16b i = 0;
159 char *s = buf;
160 int len;
163 /* Paranoia */
164 if (n <= 0) return (1);
166 /* Enforce historical upper bound */
167 if (n > 1024) n = 1024;
169 /* Leave a byte for terminating null */
170 len = n - 1;
172 /* While there's room left in the buffer */
173 while (i < len)
175 int c;
178 * Read next character - stdio buffers I/O, so there's no
179 * need to buffer it again using fgets.
181 c = fgetc(fff);
183 /* End of file */
184 if (c == EOF)
186 /* No characters read -- signal error */
187 if (i == 0) break;
190 * Be nice to DOS/Windows, where a last line of a file isn't
191 * always \n terminated.
193 *s = '\0';
195 /* Success */
196 return (0);
199 #ifdef MACH_O_CARBON
202 * Be nice to the Macintosh, where a file can have Mac or Unix
203 * end of line, especially since the introduction of OS X.
204 * MPW tools were also very tolerant to the Unix EOL.
206 if (c == '\r') c = '\n';
208 #endif /* MACH_O_CARBON */
210 /* End of line */
211 if (c == '\n')
213 /* Null terminate */
214 *s = '\0';
216 /* Success */
217 return (0);
220 /* Expand a tab into spaces */
221 if (c == '\t')
223 int tabstop;
225 /* Next tab stop */
226 tabstop = ((i + TAB_COLUMNS) / TAB_COLUMNS) * TAB_COLUMNS;
228 /* Bounds check */
229 if (tabstop >= len) break;
231 /* Convert it to spaces */
232 while (i < tabstop)
234 /* Store space */
235 *s++ = ' ';
237 /* Count */
238 i++;
242 /* Ignore non-printables */
243 else if (isprint(c))
245 /* Store character in the buffer */
246 *s++ = c;
248 /* Count number of characters in the buffer */
249 i++;
253 /* Buffer overflow or EOF - return an empty string */
254 buf[0] = '\0';
256 /* Error */
257 return (1);
262 * Hack -- replacement for "fputs()"
264 * Dump a string, plus a newline, to a file
266 * Perhaps this function should handle internal weirdness.
268 errr my_fputs(FILE *fff, cptr buf, size_t n)
270 /* Unused paramter */
271 (void)n;
273 /* Dump, ignore errors */
274 (void)fprintf(fff, "%s\n", buf);
276 /* Success */
277 return (0);
282 * Check to see if a file exists, by opening it read-only.
284 * Return TRUE if it does, FALSE if it does not.
286 bool my_fexists(const char *fname)
288 int fd;
290 /* Try to open it */
291 fd = fd_open(fname, O_RDONLY);
293 /* It worked */
294 if (fd >= 0)
296 fd_close(fd);
297 return TRUE;
299 else
301 return FALSE;
307 /* The file routines for RISC OS are in main-ros.c. */
308 #ifndef RISCOS
311 #ifdef SET_UID
314 * Find a default user name from the system.
316 void user_name(char *buf, size_t len, int id)
318 struct passwd *pw;
320 /* Look up the user name */
321 if ((pw = getpwuid(id)))
323 /* Get the first 15 characters of the user name */
324 my_strcpy(buf, pw->pw_name, len);
326 /* Capitalize the user name */
327 buf[0] = toupper((unsigned char)buf[0]);
329 return;
332 /* Oops. Hack -- default to "PLAYER" */
333 my_strcpy(buf, "PLAYER", len);
336 #endif /* SET_UID */
339 #if defined(SET_UID) || defined(USE_PRIVATE_PATHS)
342 * Extract a "parsed" path from an initial filename
343 * Normally, we simply copy the filename into the buffer
344 * But leading tilde symbols must be handled in a special way
345 * Replace "~user/" by the home directory of the user named "user"
346 * Replace "~/" by the home directory of the current user
348 errr path_parse(char *buf, size_t max, cptr file)
350 cptr u, s;
351 struct passwd *pw;
352 char user[128];
355 /* Assume no result */
356 buf[0] = '\0';
358 /* No file? */
359 if (!file) return (-1);
361 /* File needs no parsing */
362 if (file[0] != '~')
364 my_strcpy(buf, file, max);
365 return (0);
368 /* Point at the user */
369 u = file+1;
371 /* Look for non-user portion of the file */
372 s = strstr(u, PATH_SEP);
374 /* Hack -- no long user names */
375 if (s && (s >= u + sizeof(user))) return (1);
377 /* Extract a user name */
378 if (s)
380 int i;
381 for (i = 0; u < s; ++i) user[i] = *u++;
382 user[i] = '\0';
383 u = user;
386 /* Look up the "current" user */
387 if (u[0] == '\0') u = getlogin();
389 /* Look up a user (or "current" user) */
390 if (u) pw = getpwnam(u);
391 else pw = getpwuid(getuid());
393 /* Nothing found? */
394 if (!pw) return (1);
396 /* Make use of the info */
397 my_strcpy(buf, pw->pw_dir, max);
399 /* Append the rest of the filename, if any */
400 if (s) my_strcat(buf, s, max);
402 /* Success */
403 return (0);
407 #else /* SET_UID */
411 * Extract a "parsed" path from an initial filename
413 * This requires no special processing on simple machines,
414 * except for verifying the size of the filename.
416 errr path_parse(char *buf, size_t max, cptr file)
418 /* Accept the filename */
419 my_strcpy(buf, file, max);
421 # ifdef MACH_O_CARBON
423 /* Fix it according to the current operating system */
424 convert_pathname(buf);
426 # endif
428 /* Success */
429 return (0);
433 #endif /* SET_UID */
439 * Create a new path by appending a file (or directory) to a path
441 * This requires no special processing on simple machines, except
442 * for verifying the size of the filename, but note the ability to
443 * bypass the given "path" with certain special file-names.
445 * Note that the "file" may actually be a "sub-path", including
446 * a path and a file.
448 * Note that this function yields a path which must be "parsed"
449 * using the "parse" function above.
451 errr path_build(char *buf, size_t max, cptr path, cptr file)
453 /* Special file */
454 if (file[0] == '~')
456 /* Use the file itself */
457 my_strcpy(buf, file, max);
460 /* Absolute file, on "normal" systems */
461 else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, ""))
463 /* Use the file itself */
464 my_strcpy(buf, file, max);
467 /* No path given */
468 else if (!path[0])
470 /* Use the file itself */
471 my_strcpy(buf, file, max);
474 /* Path and File */
475 else
477 /* Build the new path */
478 strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file);
481 /* Success */
482 return (0);
487 * Hack -- replacement for "fopen()"
489 FILE *my_fopen(cptr file, cptr mode)
491 char buf[1024];
492 FILE *fff;
494 /* Hack -- Try to parse the path */
495 if (path_parse(buf, sizeof(buf), file)) return (NULL);
497 /* Attempt to fopen the file anyway */
498 fff = fopen(buf, mode);
500 #if defined(MACH_O_CARBON)
502 /* Set file creator and type */
503 if (fff && strchr(mode, 'w')) fsetfileinfo(buf, _fcreator, _ftype);
505 #endif
507 /* Return open file or NULL */
508 return (fff);
513 * Hack -- replacement for "fclose()"
515 errr my_fclose(FILE *fff)
517 /* Require a file */
518 if (!fff) return (-1);
520 /* Close, check for error */
521 if (fclose(fff) == EOF) return (1);
523 /* Success */
524 return (0);
529 * Hack -- attempt to delete a file
531 errr fd_kill(cptr file)
533 char buf[1024];
535 /* Hack -- Try to parse the path */
536 if (path_parse(buf, sizeof(buf), file)) return (-1);
538 /* Remove, return 0 on success, non-zero on failure */
539 return (remove(buf));
544 * Hack -- attempt to move a file
546 errr fd_move(cptr file, cptr what)
548 char buf[1024];
549 char aux[1024];
551 /* Hack -- Try to parse the path */
552 if (path_parse(buf, sizeof(buf), file)) return (-1);
554 /* Hack -- Try to parse the path */
555 if (path_parse(aux, sizeof(aux), what)) return (-1);
557 /* Rename, return 0 on success, non-zero on failure */
558 return (rename(buf, aux));
563 * Hack -- attempt to open a file descriptor (create file)
565 * This function should fail if the file already exists
567 * Note that we assume that the file should be "binary"
569 int fd_make(cptr file, int mode)
571 char buf[1024];
572 int fd;
574 /* Hack -- Try to parse the path */
575 if (path_parse(buf, sizeof(buf), file)) return (-1);
577 /* Create the file, fail if exists, write-only, binary */
578 fd = open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode);
580 #ifdef MACH_O_CARBON
582 /* Set file creator and type */
583 if (fd >= 0) fsetfileinfo(buf, _fcreator, _ftype);
585 #endif
587 /* Return descriptor */
588 return (fd);
593 * Hack -- attempt to open a file descriptor (existing file)
595 * Note that we assume that the file should be "binary"
597 int fd_open(cptr file, int flags)
599 char buf[1024];
601 /* Hack -- Try to parse the path */
602 if (path_parse(buf, sizeof(buf), file)) return (-1);
604 /* Attempt to open the file */
605 return (open(buf, flags | O_BINARY, 0));
610 * Attempt to lock a file descriptor
612 * Legal lock types -- F_UNLCK, F_RDLCK, F_WRLCK
614 errr fd_lock(int fd, int what)
616 #if defined(HAVE_FCNTL_H) && defined(SET_UID)
618 struct flock lock;
620 /* Verify the fd */
621 if (fd < 0) return (-1);
623 lock.l_type = what;
624 lock.l_start = 0; /* Lock the entire file */
625 lock.l_whence = SEEK_SET; /* Lock the entire file */
626 lock.l_len = 0; /* Lock the entire file */
628 /* Wait for access and set lock status */
630 * Change F_SETLKW to F_SETLK if it's preferable to return
631 * without locking and reporting an error instead of waiting.
633 return (fcntl(fd, F_SETLKW, &lock));
635 #else /* HAVE_FCNTL_H */
637 /* Unused parameters */
638 (void)fd;
639 (void)what;
641 /* Success */
642 return (0);
644 #endif /* SET_UID */
650 * Hack -- attempt to seek on a file descriptor
652 errr fd_seek(int fd, long n)
654 long p;
656 /* Verify fd */
657 if (fd < 0) return (-1);
659 /* Seek to the given position */
660 p = lseek(fd, n, SEEK_SET);
662 /* Failure */
663 if (p < 0) return (1);
665 /* Failure */
666 if (p != n) return (1);
668 /* Success */
669 return (0);
673 #ifndef SET_UID
674 #define FILE_BUF_SIZE 16384
675 #endif
679 * Hack -- attempt to read data from a file descriptor
681 errr fd_read(int fd, char *buf, size_t n)
683 /* Verify the fd */
684 if (fd < 0) return (-1);
686 #ifndef SET_UID
688 /* Read pieces */
689 while (n >= FILE_BUF_SIZE)
691 /* Read a piece */
692 if (read(fd, buf, FILE_BUF_SIZE) != FILE_BUF_SIZE) return (1);
694 /* Shorten the task */
695 buf += FILE_BUF_SIZE;
697 /* Shorten the task */
698 n -= FILE_BUF_SIZE;
701 #endif
703 /* Read the final piece */
704 if (read(fd, buf, n) != (int)n) return (1);
706 /* Success */
707 return (0);
712 * Hack -- Attempt to write data to a file descriptor
714 errr fd_write(int fd, cptr buf, size_t n)
716 /* Verify the fd */
717 if (fd < 0) return (-1);
719 #ifndef SET_UID
721 /* Write pieces */
722 while (n >= FILE_BUF_SIZE)
724 /* Write a piece */
725 if (write(fd, buf, FILE_BUF_SIZE) != FILE_BUF_SIZE) return (1);
727 /* Shorten the task */
728 buf += FILE_BUF_SIZE;
730 /* Shorten the task */
731 n -= FILE_BUF_SIZE;
734 #endif
736 /* Write the final piece */
737 if (write(fd, buf, n) != (int)n) return (1);
739 /* Success */
740 return (0);
745 * Hack -- attempt to close a file descriptor
747 errr fd_close(int fd)
749 /* Verify the fd */
750 if (fd < 0) return (-1);
752 /* Close, return 0 on success, -1 on failure */
753 return (close(fd));
757 #if defined(CHECK_MODIFICATION_TIME)
759 errr check_modification_date(int fd, cptr template_file)
761 char buf[1024];
763 struct stat txt_stat, raw_stat;
765 /* Build the filename */
766 path_build(buf, sizeof(buf), ANGBAND_DIR_EDIT, template_file);
768 /* Access stats on text file */
769 if (stat(buf, &txt_stat))
771 /* No text file - continue */
774 /* Access stats on raw file */
775 else if (fstat(fd, &raw_stat))
777 /* Error */
778 return (-1);
781 /* Ensure text file is not newer than raw file */
782 else if (txt_stat.st_mtime > raw_stat.st_mtime)
784 /* Reprocess text file */
785 return (-1);
788 return (0);
791 #endif /* CHECK_MODIFICATION_TIME */
793 #endif /* RISCOS */
795 /*** Directory scanning code ***/
798 * This code was originally written for the SDL port so it could scan for fonts
799 * without needing a fontlist text file.
804 * Opens a directory handle.
806 * `dirname` must be a system-specific pathname to the directory
807 * you want scanned.
809 * Returns a valid directory handle on success, NULL otherwise.
811 ang_dir *my_dopen(const char *dirname);
815 * Reads a directory entry.
817 * `dir` must point to a directory handle previously returned by my_dopen().
818 * `fname` must be a pointer to a writeable chunk of memory `len` long.
820 * Returns TRUE on successful reading, FALSE otherwise.
821 * (FALSE generally indicates that there are no more files to be read.)
823 bool my_dread(ang_dir *dir, char *fname, size_t len);
827 * Close a directory handle.
829 void my_dclose(ang_dir *dir);
834 #ifdef WINDOWS
836 /* Include Windows header */
837 #include <windows.h>
839 /* System-specific struct */
840 struct ang_dir
842 HANDLE h;
843 const char *first_file;
846 /* Specified above */
847 ang_dir *my_dopen(const char *dirname)
849 WIN32_FIND_DATA fd;
850 HANDLE h;
851 ang_dir *dir;
853 /* Try to open it */
854 h = FindFirstFile(format("%s\\*", dirname), &fd);
856 /* Abort */
857 if (h == INVALID_HANDLE_VALUE)
858 return NULL;
860 /* Allocate for the handle */
861 dir = ralloc(sizeof dir);
862 if (!dir) return NULL;
864 /* Remember details */
865 dir->h = h;
866 dir->first_file = string_make(fd.cFileName);
868 /* Success */
869 return dir;
872 /* Specified above */
873 bool my_dread(ang_dir *dir, char *fname, size_t len)
875 WIN32_FIND_DATA fd;
876 BOOL ok;
878 /* Try the first file */
879 if (dir->first_file)
881 /* Copy the string across, then free it */
882 my_strcpy(fname, dir->first_file, len);
883 string_free(dir->first_file);
884 dir->first_file = NULL;
886 /* Wild success */
887 return TRUE;
890 /* Try the next file */
891 while (1)
893 ok = FindNextFile(dir->h, &fd);
894 if (!ok) return FALSE;
896 /* Skip directories */
897 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ||
898 strcmp(fd.cFileName, ".") == 0 ||
899 strcmp(fd.cFileName, "..") == 0)
900 continue;
902 /* Take this one */
903 break;
906 /* Copy name */
907 my_strcpy(fname, fd.cFileName, len);
909 return TRUE;
912 void my_dclose(ang_dir *dir)
914 /* Close directory */
915 if (dir->h)
916 FindClose(dir->h);
918 /* Free memory */
919 FREE(dir);
922 #endif /* WINDOWS */
925 #ifdef HAVE_DIRENT_H
927 /* Include relevant types */
928 #include <sys/types.h>
929 #include <dirent.h>
931 /* Define our ang_dir type */
932 struct ang_dir
934 DIR *d;
935 const char *dirname;
938 /* Specified above */
939 ang_dir *my_dopen(const char *dirname)
941 ang_dir *dir;
942 DIR *d;
944 /* Try to open the directory */
945 d = opendir(dirname);
946 if (!d) return NULL;
948 /* Allocate memory for the handle */
949 dir = ralloc(sizeof dir);
950 if (!dir)
952 closedir(d);
953 return NULL;
956 /* Set up the handle */
957 dir->d = d;
958 dir->dirname = string_make(dirname);
960 if (!dir->dirname)
962 closedir(d);
963 rnfree(dir);
964 return NULL;
967 /* Success */
968 return dir;
971 /* Specified above */
972 bool my_dread(ang_dir *dir, char *fname, size_t len)
974 struct dirent *entry;
975 struct stat filedata;
976 char path[1024] = "";
978 assert(dir != NULL);
980 /* Try reading another entry */
981 while (1)
983 entry = readdir(dir->d);
984 if (!entry) return FALSE;
986 path_build(path, sizeof path, dir->dirname, entry->d_name);
988 /* Check to see if it exists */
989 if (stat(path, &filedata) != 0)
990 continue;
992 /* Check to see if it's a directory */
993 if (S_ISDIR(filedata.st_mode))
994 continue;
996 /* We've found something worth returning */
997 break;
1000 /* Copy the filename */
1001 my_strcpy(fname, entry->d_name, len);
1003 return TRUE;
1006 void my_dclose(ang_dir *dir)
1008 /* Close directory */
1009 if (dir->d)
1011 closedir(dir->d);
1012 string_free(dir->dirname);
1015 /* Free memory */
1016 FREE(dir);
1019 #endif /* HAVE_DIRENT_H */