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.
22 # include <sys/types.h>
23 # include <sys/stat.h>
35 void safe_setuid_drop(void)
38 # if defined(HAVE_SETRESGID)
40 if (setresgid(-1, getgid(), -1) != 0)
41 quit("setegid(): cannot drop permissions correctly!");
45 if (setegid(getgid()) != 0)
46 quit("setegid(): cannot drop permissions correctly!");
56 void safe_setuid_grab(void)
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!");
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
105 FILE *my_fopen_temp(char *buf
, size_t max
)
109 /* Prepare the buffer for mkstemp */
110 my_strcpy(buf
, "/tmp/anXXXXXX", max
);
112 /* Secure creation of a temporary file */
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
)
135 if (!s
) return (NULL
);
138 my_strcpy(buf
, s
, max
);
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
)
164 if (n
<= 0) return (1);
166 /* Enforce historical upper bound */
167 if (n
> 1024) n
= 1024;
169 /* Leave a byte for terminating null */
172 /* While there's room left in the buffer */
178 * Read next character - stdio buffers I/O, so there's no
179 * need to buffer it again using fgets.
186 /* No characters read -- signal error */
190 * Be nice to DOS/Windows, where a last line of a file isn't
191 * always \n terminated.
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 */
220 /* Expand a tab into spaces */
226 tabstop
= ((i
+ TAB_COLUMNS
) / TAB_COLUMNS
) * TAB_COLUMNS
;
229 if (tabstop
>= len
) break;
231 /* Convert it to spaces */
242 /* Ignore non-printables */
245 /* Store character in the buffer */
248 /* Count number of characters in the buffer */
253 /* Buffer overflow or EOF - return an empty string */
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 */
273 /* Dump, ignore errors */
274 (void)fprintf(fff
, "%s\n", buf
);
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
)
291 fd
= fd_open(fname
, O_RDONLY
);
307 /* The file routines for RISC OS are in main-ros.c. */
314 * Find a default user name from the system.
316 void user_name(char *buf
, size_t len
, int id
)
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]);
332 /* Oops. Hack -- default to "PLAYER" */
333 my_strcpy(buf
, "PLAYER", len
);
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
)
355 /* Assume no result */
359 if (!file
) return (-1);
361 /* File needs no parsing */
364 my_strcpy(buf
, file
, max
);
368 /* Point at the user */
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 */
381 for (i
= 0; u
< s
; ++i
) user
[i
] = *u
++;
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());
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
);
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
);
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
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
)
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
);
470 /* Use the file itself */
471 my_strcpy(buf
, file
, max
);
477 /* Build the new path */
478 strnfmt(buf
, max
, "%s%s%s", path
, PATH_SEP
, file
);
487 * Hack -- replacement for "fopen()"
489 FILE *my_fopen(cptr file
, cptr mode
)
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
);
507 /* Return open file or NULL */
513 * Hack -- replacement for "fclose()"
515 errr
my_fclose(FILE *fff
)
518 if (!fff
) return (-1);
520 /* Close, check for error */
521 if (fclose(fff
) == EOF
) return (1);
529 * Hack -- attempt to delete a file
531 errr
fd_kill(cptr file
)
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
)
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
)
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
);
582 /* Set file creator and type */
583 if (fd
>= 0) fsetfileinfo(buf
, _fcreator
, _ftype
);
587 /* Return descriptor */
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
)
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)
621 if (fd
< 0) return (-1);
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 */
650 * Hack -- attempt to seek on a file descriptor
652 errr
fd_seek(int fd
, long n
)
657 if (fd
< 0) return (-1);
659 /* Seek to the given position */
660 p
= lseek(fd
, n
, SEEK_SET
);
663 if (p
< 0) return (1);
666 if (p
!= n
) return (1);
674 #define FILE_BUF_SIZE 16384
679 * Hack -- attempt to read data from a file descriptor
681 errr
fd_read(int fd
, char *buf
, size_t n
)
684 if (fd
< 0) return (-1);
689 while (n
>= FILE_BUF_SIZE
)
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 */
703 /* Read the final piece */
704 if (read(fd
, buf
, n
) != (int)n
) return (1);
712 * Hack -- Attempt to write data to a file descriptor
714 errr
fd_write(int fd
, cptr buf
, size_t n
)
717 if (fd
< 0) return (-1);
722 while (n
>= FILE_BUF_SIZE
)
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 */
736 /* Write the final piece */
737 if (write(fd
, buf
, n
) != (int)n
) return (1);
745 * Hack -- attempt to close a file descriptor
747 errr
fd_close(int fd
)
750 if (fd
< 0) return (-1);
752 /* Close, return 0 on success, -1 on failure */
757 #if defined(CHECK_MODIFICATION_TIME)
759 errr
check_modification_date(int fd
, cptr template_file
)
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
))
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 */
791 #endif /* CHECK_MODIFICATION_TIME */
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
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
);
836 /* Include Windows header */
839 /* System-specific struct */
843 const char *first_file
;
846 /* Specified above */
847 ang_dir
*my_dopen(const char *dirname
)
854 h
= FindFirstFile(format("%s\\*", dirname
), &fd
);
857 if (h
== INVALID_HANDLE_VALUE
)
860 /* Allocate for the handle */
861 dir
= ralloc(sizeof dir
);
862 if (!dir
) return NULL
;
864 /* Remember details */
866 dir
->first_file
= string_make(fd
.cFileName
);
872 /* Specified above */
873 bool my_dread(ang_dir
*dir
, char *fname
, size_t len
)
878 /* Try the 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
;
890 /* Try the next file */
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)
907 my_strcpy(fname
, fd
.cFileName
, len
);
912 void my_dclose(ang_dir
*dir
)
914 /* Close directory */
927 /* Include relevant types */
928 #include <sys/types.h>
931 /* Define our ang_dir type */
938 /* Specified above */
939 ang_dir
*my_dopen(const char *dirname
)
944 /* Try to open the directory */
945 d
= opendir(dirname
);
948 /* Allocate memory for the handle */
949 dir
= ralloc(sizeof dir
);
956 /* Set up the handle */
958 dir
->dirname
= string_make(dirname
);
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] = "";
980 /* Try reading another entry */
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)
992 /* Check to see if it's a directory */
993 if (S_ISDIR(filedata
.st_mode
))
996 /* We've found something worth returning */
1000 /* Copy the filename */
1001 my_strcpy(fname
, entry
->d_name
, len
);
1006 void my_dclose(ang_dir
*dir
)
1008 /* Close directory */
1012 string_free(dir
->dirname
);
1019 #endif /* HAVE_DIRENT_H */