Ticket #137: rough.patch

File rough.patch, 113.8 kB (added by takkaria, 1 year ago)

This compiles and works on Linux (at least). Contains unit tests.

  • init.h

    old new  
    4040 
    4141typedef errr (*parse_info_txt_func)(char *buf, header *head); 
    4242typedef errr (*eval_info_power_func)(header *head); 
    43 typedef errr (*emit_info_txt_index_func)(FILE *fp, header *head, int i); 
    44 typedef errr (*emit_info_txt_always_func)(FILE *fp, header *head); 
     43typedef errr (*emit_info_txt_index_func)(ang_file *fp, header *head, int i); 
     44typedef errr (*emit_info_txt_always_func)(ang_file *fp, header *head); 
    4545 
    4646/* 
    4747 * Template file header information (see "init.c").  16 bytes. 
     
    102102         
    103103}; 
    104104 
    105 extern errr init_info_txt(FILE *fp, char *buf, header *head, 
     105extern errr init_info_txt(ang_file *fp, char *buf, header *head, 
    106106                          parse_info_txt_func parse_info_txt_line); 
    107107 
    108108extern errr eval_info(eval_info_power_func eval_info_process, header *head); 
    109109 
    110 extern errr emit_info_txt(FILE *fp, FILE *template, char *buf, header *head, 
     110extern errr emit_info_txt(ang_file *fp, ang_file *template, char *buf, header *head, 
    111111   emit_info_txt_index_func emit_info_txt_index, emit_info_txt_always_func emit_info_txt_always); 
    112112 
    113113#ifdef ALLOW_TEMPLATES 
  • h-basic.h

    old new  
    4949 
    5050 
    5151/* 
    52  * Everyone except RISC OS has fcntl.h 
     52 * Everyone except RISC OS has fcntl.h and sys/stat.h 
    5353 */ 
    5454#ifndef RISCOS 
    5555# define HAVE_FCNTL_H 
     56# define HAVE_STAT 
    5657#endif 
    5758 
    5859#endif /* HAVE_CONFIG_H */ 
     
    107108#endif 
    108109 
    109110 
    110 /* 
    111  * Mac support 
    112  */ 
    113 #ifdef MACH_O_CARBON 
    114  
    115 /* OS X uses filetypes when creating files. */ 
    116 # define FILE_TYPE_TEXT 'TEXT' 
    117 # define FILE_TYPE_DATA 'DATA' 
    118 # define FILE_TYPE_SAVE 'SAVE' 
    119 # define FILE_TYPE(X) (_ftype = (X)) 
    120  
    121 #else 
    122  
    123 # define FILE_TYPE(X) ((void)0) 
    124  
    125 #endif 
    126  
    127  
    128  
    129111/*** Include the library header files ***/ 
    130112 
    131113/* Use various POSIX functions if available */ 
  • util.c

    old new  
    22972297                        /* Output the indent */ 
    22982298                        for (i = 0; i < text_out_indent; i++) 
    22992299                        { 
    2300                                 fputc(' ', text_out_file); 
     2300                                file_writec(text_out_file, ' '); 
    23012301                                pos++; 
    23022302                        } 
    23032303                } 
     
    23282328                        else 
    23292329                        { 
    23302330                                /* Begin a new line */ 
    2331                                 fputc('\n', text_out_file); 
     2331                                file_writec(text_out_file, '\n'); 
    23322332 
    23332333                                /* Reset */ 
    23342334                                pos = 0; 
     
    23522352                        ch = (isprint((unsigned char) s[n]) ? s[n] : ' '); 
    23532353 
    23542354                        /* Write out the character */ 
    2355                         fputc(ch, text_out_file); 
     2355                        file_writec(text_out_file, ch); 
    23562356 
    23572357                        /* Increment */ 
    23582358                        pos++; 
     
    23682368                if (*s == '\n') s++; 
    23692369 
    23702370                /* Begin a new line */ 
    2371                 fputc('\n', text_out_file); 
     2371                file_writec(text_out_file, '\n'); 
    23722372 
    23732373                /* Reset */ 
    23742374                pos = 0; 
  • Makefile.std

    old new  
    8181LIBS += $(patsubst -D%,,$(patsubst -I%,, $(MODULES))) 
    8282 
    8383 
    84 # Extract system we're running on 
    85 uname = $(shell uname -s) 
    86  
    87 # Enable linux-specific modules, if requested. 
    88 ifeq ($(uname),Linux) 
    89   CFLAGS += -DHAVE_MKSTEMP 
    90 endif 
    91  
    92  
    9384# Object definitions 
    9485X11OBJS = maid-x11.o main-x11.o main-xaw.o main-gtk.o 
    9586MAINOBJS = main.o main-gcu.o main-sdl.o snd-sdl.o $(X11OBJS) 
  • z-file.c

    old new  
    22 * File: z-file.c 
    33 * Purpose: Low-level file (and directory) handling 
    44 * 
    5  * Copyright (c) 1997-2007 Ben Harrison, pelpel, Andrew Sidwell 
     5 * Copyright (c) 1997-2007 Ben Harrison, pelpel, Andrew Sidwell, Matthew Jones 
    66 * 
    77 * This work is free software; you can redistribute it and/or modify it 
    88 * under the terms of either: 
     
    1515 *    and not for profit purposes provided that this copyright and statement 
    1616 *    are included in all such copies.  Other copyrights may also apply. 
    1717 */ 
    18 #include "angband.h" 
    1918#include "z-file.h" 
     19#include "z-virt.h" 
     20#include "z-util.h" 
     21#include "z-form.h" 
    2022 
    2123#ifndef RISCOS 
    2224# include <sys/types.h> 
    23 # include <sys/stat.h> 
    2425#endif 
    2526 
    2627#ifdef WINDOWS 
     28# include <windows.h> 
    2729# include <io.h> 
    2830#endif 
    2931 
     32#ifdef HAVE_FCNTL_H 
     33# include <fcntl.h> 
     34#endif 
    3035 
     36#ifdef HAVE_DIRENT_H 
     37# include <sys/types.h> 
     38# include <dirent.h> 
     39#endif 
    3140 
     41#ifdef HAVE_STAT 
     42# include <sys/stat.h> 
     43#endif 
     44 
    3245/* 
     46 * Player info 
     47 */ 
     48int player_uid; 
     49int player_egid; 
     50 
     51 
     52 
     53#ifdef MACH_O_CARBON 
     54extern u32b _fcreator; 
     55extern void fsetfileinfo(cptr path, u32b fcreator, u32b ftype); 
     56#endif 
     57 
     58 
     59 
     60/* 
    3361 * Drop permissions 
    3462 */ 
    3563void safe_setuid_drop(void) 
     
    72100 
    73101 
    74102 
     103 
    75104/* 
    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. 
    79  * 
    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 
    86  * 
    87  * path_temp() should probably return a "canonical" filename.  XXX 
    88  * 
    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. 
    91  * 
    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. 
     105 * Apply special system-specific processing before dealing with a filename. 
    100106 */ 
     107static errr path_parse(char *buf, size_t max, cptr file) 
     108{ 
     109        /* Accept the filename */ 
     110        my_strcpy(buf, file, max); 
    101111 
     112#ifdef RISCOS 
    102113 
    103 #ifdef HAVE_MKSTEMP 
     114        /* Defined in main-ros.c */ 
     115        char *riscosify_name(const char *path); 
     116        my_strcpy(buf, riscosify_name(path), max); 
    104117 
    105 FILE *my_fopen_temp(char *buf, size_t max) 
     118#endif 
     119 
     120        /* Success */ 
     121        return (0); 
     122
     123 
     124 
     125 
     126static void path_process(char *buf, size_t len, size_t *cur_len, const char *path) 
    106127{ 
    107         int fd; 
     128#if defined(SET_UID) || defined(USE_PRIVATE_PATHS) 
    108129 
    109         /* Prepare the buffer for mkstemp */ 
    110         my_strcpy(buf, "/tmp/anXXXXXX", max); 
     130        /* Home directory on Unixes */ 
     131        if (path[0] == '~') 
     132        { 
     133                const char *s; 
     134                const char *username = path + 1; 
    111135 
    112         /* Secure creation of a temporary file */ 
    113         fd = mkstemp(buf)
     136               struct passwd *pw; 
     137               char user[128]
    114138 
    115         /* Check the file-descriptor */ 
    116         if (fd < 0) return (NULL); 
     139                /* Look for non-user portion of the file */ 
     140                s = strstr(username, PATH_SEP); 
     141                if (s) 
     142                { 
     143                        int i; 
    117144 
    118         /* Return a file stream */ 
    119         return (fdopen(fd, "w")); 
    120 
     145                        /* Keep username a decent length */ 
     146                        if (s >= username + sizeof(user)) return; 
    121147 
    122 #else /* HAVE_MKSTEMP */ 
     148                        for (i = 0; username < s; ++i) user[i] = *username++; 
     149                        user[i] = '\0'; 
     150                        username = user; 
     151                } 
    123152 
    124 /* 
    125  * Consider rewriting this so it uses its own buffer. 
    126  */ 
    127 FILE *my_fopen_temp(char *buf, size_t max) 
    128 
    129         const char *s; 
     153                /* Fallback -- try the "current" user */ 
     154                if (username[0] == '\0') 
     155                        username = getlogin(); 
    130156 
    131         /* Temp file */ 
    132         s = tmpnam(NULL); 
     157                /* Look up a user (or "current" user) */ 
     158                if (username) pw = getpwnam(username); 
     159                else          pw = getpwuid(getuid()); 
    133160 
    134         /* Oops */ 
    135         if (!s) return (NULL); 
     161                if (!pw) return; 
    136162 
    137         /* Copy to buffer */ 
    138         my_strcpy(buf, s, max); 
     163                /* Copy across */ 
     164                strnfcat(buf, len, cur_len, "%s%s", pw->pw_dir, PATH_SEP); 
     165                if (s) strnfcat(buf, len, cur_len, "%s", s); 
     166        } 
     167        else 
    139168 
    140         /* Open the file */ 
    141         return (my_fopen(buf, "w")); 
     169#endif 
     170 
     171        { 
     172                strnfcat(buf, len, cur_len, "%s", path); 
     173        } 
    142174} 
    143175 
    144 #endif /* HAVE_MKSTEMP */ 
    145176 
    146177 
     178 
    147179/* 
    148  * Hack -- replacement for "fgets()" 
     180 * Create a new path string by appending a 'leaf' to 'base'. 
    149181 * 
    150  * Read a string, without a newline, to a file 
     182 * On Unixes, we convert a tidle at the beginning of a basename to mean the 
     183 * directory, complicating things a little, but better now than later. 
    151184 * 
    152  * Process tabs, strip internal non-printables 
     185 * Remember to free the return value. 
    153186 */ 
    154 #define TAB_COLUMNS   8 
    155  
    156 errr my_fgets(FILE *fff, char *buf, size_t n) 
     187size_t path_build(char *buf, size_t len, const char *base, const char *leaf) 
    157188{ 
    158         u16b i = 0; 
    159         char *s = buf; 
    160         int len; 
    161          
    162 #ifdef MACH_O_CARBON 
    163          
    164         /* For the \r vs. \r\n vs \n handling code below */ 
    165         bool seen_cr = FALSE; 
     189        size_t cur_len = 0; 
     190        buf[0] = '\0'; 
    166191 
    167 #endif /* MACH_O_CARBON */ 
     192        if (!leaf || !leaf[0]) 
     193        { 
     194                if (base && base[0]) 
     195                        path_process(buf, len, &cur_len, base); 
    168196 
    169         /* Paranoia */ 
    170         if (n <= 0) return (1); 
     197               return cur_len; 
     198        } 
    171199 
    172         /* Enforce historical upper bound */ 
    173         if (n > 1024) n = 1024; 
    174200 
    175         /* Leave a byte for terminating null */ 
    176         len = n - 1; 
    177  
    178         /* While there's room left in the buffer */ 
    179         while (i < len) 
     201        /* 
     202         * If the leafname starts with the seperator, 
     203         *   or with the tilde (on Unix), 
     204         *   or there's no base path, 
     205         * We use the leafname only. 
     206         */ 
     207#if defined(SET_UID) || defined(USE_PRIVATE_PATHS) 
     208        if ((!base || !base[0]) || prefix(leaf, PATH_SEP) || leaf[0] == '~') 
     209#else 
     210        if ((!base || !base[0]) || prefix(leaf, PATH_SEP)) 
     211#endif 
    180212        { 
    181                 int c; 
     213                path_process(buf, len, &cur_len, leaf); 
     214                return cur_len; 
     215        } 
    182216 
    183                 /* 
    184                  * Read next character - stdio buffers I/O, so there's no 
    185                  * need to buffer it again using fgets. 
    186                  */ 
    187                 c = fgetc(fff); 
    188217 
    189                 /* End of file */ 
    190                 if (c == EOF) 
    191                 { 
    192                         /* No characters read -- signal error */ 
    193                         if (i == 0) break; 
     218        /* There is both a relative leafname and a base path from which it is relative */ 
     219        path_process(buf, len, &cur_len, base); 
     220        strnfcat(buf, len, &cur_len, "%s", PATH_SEP); 
     221        path_process(buf, len, &cur_len, leaf); 
    194222 
    195                         /* 
    196                          * Be nice to DOS/Windows, where a last line of a file isn't 
    197                          * always \n terminated. 
    198                          */ 
    199                         *s = '\0'; 
     223        return cur_len; 
     224
    200225 
    201                         /* Success */ 
    202                         return (0); 
    203                 } 
    204226 
    205 #ifdef MACH_O_CARBON 
    206227 
    207                 /* 
    208                  * Be nice to the Macintosh, where a file can have Mac or Unix 
    209                  * end of line, especially since the introduction of OS X. 
    210                  * MPW tools were also very tolerant to the Unix EOL. 
    211                  * 
    212                  * Watch for \r; when found, set flag and advance. 
    213                  * If the next character isn't \n, rewind the file one character  
    214                  * and act like we found \n anyway to end the line. 
    215                  */ 
    216                 if (c == '\r')  
    217                 { 
    218                         seen_cr = TRUE; 
    219                         continue; 
    220                 } 
    221                 else 
    222                         seen_cr = FALSE; 
    223                          
    224                 if (seen_cr && c != '\n') 
    225                 { 
    226                         fseek(fff, -1, SEEK_CUR); 
    227                          
    228                         /* Put a fake newline in to end the line */ 
    229                         c = '\n'; 
    230                 } 
    231228 
    232 #endif /* MACH_O_CARBON */ 
     229/*** File-handling API ***/ 
    233230 
    234                /* End of line */ 
    235                if (c == '\n'
    236                 { 
    237                         /* Null terminate */ 
    238                         *s = '\0'; 
     231/* On Windows, fwrite() and fread() are broken. */ 
     232#if defined(WINDOWS) || defined(SET_UID
     233# define HAVE_WRITE 
     234# define HAVE_READ 
     235#endif 
    239236 
    240                         /* Success */ 
    241                         return (0); 
    242                 } 
    243237 
    244                 /* Expand a tab into spaces */ 
    245                 if (c == '\t') 
    246                 { 
    247                         int tabstop; 
     238/* Private structure to hold file pointers and useful info. */ 
     239struct ang_file 
     240
     241        FILE *fh; 
     242        char *fname; 
     243        file_mode mode; 
     244        bool temp; 
     245}; 
    248246 
    249                         /* Next tab stop */ 
    250                         tabstop = ((i + TAB_COLUMNS) / TAB_COLUMNS) * TAB_COLUMNS; 
    251247 
    252                         /* Bounds check */ 
    253                         if (tabstop >= len) break; 
    254248 
    255                         /* Convert it to spaces */ 
    256                         while (i < tabstop) 
    257                         { 
    258                                 /* Store space */ 
    259                                 *s++ = ' '; 
     249/** Utility functions **/ 
    260250 
    261                                 /* Count */ 
    262                                 i++; 
    263                         } 
    264                 } 
    265  
    266                 /* Ignore non-printables */ 
    267                 else if (isprint(c)) 
    268                 { 
    269                         /* Store character in the buffer */ 
    270                         *s++ = c; 
    271  
    272                         /* Count number of characters in the buffer */ 
    273                         i++; 
    274                 } 
    275         } 
    276  
    277         /* Buffer overflow or EOF - return an empty string */ 
    278         buf[0] = '\0'; 
    279  
    280         /* Error */ 
    281         return (1); 
    282 } 
    283  
    284  
    285251/* 
    286  * Hack -- replacement for "fputs()" 
    287  * 
    288  * Dump a string, plus a newline, to a file 
    289  * 
    290  * Perhaps this function should handle internal weirdness. 
     252 * Delete file 'fname'. 
    291253 */ 
    292 errr my_fputs(FILE *fff, cptr buf, size_t n) 
     254bool file_delete(const char *fname)  
    293255{ 
    294         /* Unused paramter */ 
    295         (void)n; 
     256        char buf[1024]; 
    296257 
    297         /* Dump, ignore errors */ 
    298         (void)fprintf(fff, "%s\n", buf)
     258        /* Get the system-specific paths */ 
     259        if (path_parse(buf, sizeof(buf), fname)) return FALSE
    299260 
    300         /* Success */ 
    301         return (0); 
     261        return (remove(buf) == 0); 
    302262} 
    303263 
    304  
    305264/* 
    306  * Check to see if a file exists, by opening it read-only. 
    307  * 
    308  * Return TRUE if it does, FALSE if it does not. 
     265 * Delete file 'fname' to 'newname'. 
    309266 */ 
    310 bool my_fexists(const char *fname) 
     267bool file_move(const char *fname, const char *newname) 
    311268{ 
    312         int fd; 
     269        char buf[1024]; 
     270        char aux[1024]; 
    313271 
    314         /* Try to open it */ 
    315         fd = fd_open(fname, O_RDONLY); 
     272        /* Get the system-specific paths */ 
     273        if (path_parse(buf, sizeof(buf), fname)) return (-1); 
     274        if (path_parse(aux, sizeof(aux), newname)) return (-1); 
    316275 
    317         /* It worked */ 
    318         if (fd >= 0) 
    319         { 
    320                 fd_close(fd); 
    321                 return TRUE; 
    322         } 
    323         else 
    324         { 
    325                 return FALSE; 
    326         } 
     276        return (rename(buf, aux) == 0); 
    327277} 
    328278 
    329279 
    330  
    331 /* The file routines for RISC OS are in main-ros.c. */ 
    332 #ifndef RISCOS 
    333  
    334  
    335 #if defined(SET_UID) || defined(USE_PRIVATE_PATHS) 
    336  
    337280/* 
    338  * Extract a "parsed" path from an initial filename 
    339  * Normally, we simply copy the filename into the buffer 
    340  * But leading tilde symbols must be handled in a special way 
    341  * Replace "~user/" by the home directory of the user named "user" 
    342  * Replace "~/" by the home directory of the current user 
     281 * Decide whether a file exists or not. 
    343282 */ 
    344 errr path_parse(char *buf, size_t max, cptr file) 
    345 
    346         cptr u, s; 
    347         struct passwd   *pw; 
    348         char user[128]; 
     283bool file_exists(const char *fname); 
    349284 
     285#if defined(HAVE_STAT) 
    350286 
    351         /* Assume no result */ 
    352         buf[0] = '\0'; 
     287bool file_exists(const char *fname) 
     288
     289        struct stat st; 
     290        return (stat(fname, &st) == 0); 
     291
    353292 
    354         /* No file? */ 
    355         if (!file) return (-1); 
     293#elif defined(WINDOWS) 
    356294 
    357         /* File needs no parsing */ 
    358         if (file[0] != '~') 
    359         { 
    360                 my_strcpy(buf, file, max); 
    361                 return (0); 
    362         } 
     295bool file_exists(const char *fname) 
     296
     297        char path[MAX_PATH]; 
     298        DWORD attrib; 
    363299 
    364         /* Point at the user */ 
    365         u = file+1
     300        /* API says we mustn't pass anything larger than MAX_PATH */ 
     301        my_strcpy(path, s, sizeof(path))
    366302 
    367         /* Look for non-user portion of the file */ 
    368         s = strstr(u, PATH_SEP); 
     303        attrib = GetFileAttributes(path); 
     304        if (attrib == INVALID_FILE_NAME) return FALSE; 
     305        if (attrib & FILE_ATTRIBUTE_DIRECTORY) return FALSE; 
    369306 
    370         /* Hack -- no long user names */ 
    371         if (s && (s >= u + sizeof(user))) return (1); 
     307        return TRUE; 
     308
    372309 
    373         /* Extract a user name */ 
    374         if (s) 
    375         { 
    376                 int i; 
    377                 for (i = 0; u < s; ++i) user[i] = *u++; 
    378                 user[i] = '\0'; 
    379                 u = user; 
    380         } 
     310#else 
    381311 
    382         /* Look up the "current" user */ 
    383         if (u[0] == '\0') u = getlogin(); 
     312bool file_exists(const char *fname) 
     313
     314        ang_file *f = file_open(fname, MODE_READ, 0); 
    384315 
    385         /* Look up a user (or "current" user) */ 
    386         if (u) pw = getpwnam(u); 
    387         else pw = getpwuid(getuid()); 
    388  
    389         /* Nothing found? */ 
    390         if (!pw) return (1); 
    391  
    392         /* Make use of the info */ 
    393         my_strcpy(buf, pw->pw_dir, max); 
    394  
    395         /* Append the rest of the filename, if any */ 
    396         if (s) my_strcat(buf, s, max); 
    397  
    398         /* Success */ 
    399         return (0); 
     316        if (f) file_close(f); 
     317        return (f ? TRUE : FALSE); 
    400318} 
    401319 
     320#endif 
    402321 
    403 #else /* SET_UID */ 
    404322 
     323#ifndef RISCOS 
     324#ifdef HAVE_STAT 
    405325 
    406326/* 
    407  * Extract a "parsed" path from an initial filename 
    408  * 
    409  * This requires no special processing on simple machines, 
    410  * except for verifying the size of the filename. 
     327 * Return TRUE if first is newer than second, FALSE otherwise. 
    411328 */ 
    412 errr path_parse(char *buf, size_t max, cptr file
     329bool file_newer(const char *first, const char *second
    413330{ 
    414         /* Accept the filename */ 
    415         my_strcpy(buf, file, max); 
     331        struct stat first_stat, second_stat; 
    416332 
    417 # ifdef MACH_O_CARBON 
     333        bool second_exists = stat(second, &second_stat) ? FALSE : TRUE; 
     334        bool first_exists = stat(first, &first_stat) ? FALSE : TRUE; 
    418335 
    419         /* Fix it according to the current operating system */ 
    420         convert_pathname(buf); 
     336        /* 
     337         * If the first doesn't exist, the first is not newer; 
     338         * If the second doesn't exist, the first is always newer. 
     339         */ 
     340        if (!first_exists)  return FALSE; 
     341        if (!second_exists) return TRUE; 
    421342 
    422 # endif 
     343        if (first_stat.st_mtime >= second_stat.st_mtime) 
     344                return TRUE; 
    423345 
    424         /* Success */ 
    425         return (0); 
     346        return FALSE; 
    426347} 
    427348 
     349#else /* HAVE_STAT */ 
    428350 
    429 #endif /* SET_UID */ 
     351bool file_newer(const char *first, const char *second) 
     352
     353        /* Assume newer */ 
     354        return FALSE; 
     355
    430356 
     357#endif /* HAVE_STAT */ 
     358#endif /* RISCOS */ 
    431359 
    432360 
    433361 
     362 
     363/** File-handle functions **/ 
     364 
    434365/* 
    435  * Create a new path by appending a file (or directory) to a path 
    436  * 
    437  * This requires no special processing on simple machines, except 
    438  * for verifying the size of the filename, but note the ability to 
    439  * bypass the given "path" with certain special file-names. 
    440  * 
    441  * Note that the "file" may actually be a "sub-path", including 
    442  * a path and a file. 
    443  * 
    444  * Note that this function yields a path which must be "parsed" 
    445  * using the "parse" function above. 
     366 * Open file 'fname', in mode 'mode', with filetype 'ftype'. 
     367 * Returns file handle or NULL. 
    446368 */ 
    447 errr path_build(char *buf, size_t max, cptr path, cptr file) 
     369ang_file *file_open(const char *fname, file_mode mode, file_type ftype) 
    448370{ 
    449         /* Special file */ 
    450         if (file[0] == '~') 
    451         { 
    452                 /* Use the file itself */ 
    453                 my_strcpy(buf, file, max); 
    454         } 
     371        ang_file *f = ZNEW(ang_file); 
     372        char modestr[3] = "__"; 
     373        char buf[1024]; 
    455374 
    456         /* Absolute file, on "normal" systems */ 
    457         else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, "")) 
    458         { 
    459                 /* Use the file itself */ 
    460                 my_strcpy(buf, file, max); 
    461         } 
     375        /* Get the system-specific path */ 
     376        if (path_parse(buf, sizeof(buf), fname)) 
     377                return NULL; 
    462378 
    463         /* No path given */ 
    464         else if (!path[0]) 
     379        switch (mode) 
    465380        { 
    466                 /* Use the file itself */ 
    467                 my_strcpy(buf, file, max); 
     381                case MODE_WRITE: 
     382                        modestr[0] = 'w'; 
     383                        modestr[1] = 'b'; 
     384                        break; 
     385                case MODE_READ: 
     386                        modestr[0] = 'r'; 
     387                        modestr[1] = 'b'; 
     388                        break; 
     389                case MODE_APPEND: 
     390                        modestr[0] = 'w'; 
     391                        modestr[1] = 'a'; 
     392                        break; 
     393                default: 
     394                        break; 
    468395        } 
    469396 
    470         /* Path and File */ 
    471         else 
     397        f->fh = fopen(buf, modestr); 
     398 
     399        if (f->fh == NULL) 
    472400        { 
    473                 /* Build the new path */ 
    474                 strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file)
     401                FREE(f); 
     402                return NULL
    475403        } 
    476404 
    477         /* Success */ 
    478         return (0); 
    479 
     405        f->fname = string_make(buf); 
     406        f->mode = mode; 
    480407 
     408#if 0 
    481409 
    482 /* 
    483  * Hack -- replacement for "fopen()" 
    484  */ 
    485 FILE *my_fopen(cptr file, cptr mode) 
    486 
    487         char buf[1024]; 
    488         FILE *fff; 
     410This needs a whole bunch of work.  Essentially, each port should 
     411be checking the type paramater and converting it to the 
     412system-specific filetype. 
    489413 
    490         /* Hack -- Try to parse the path */ 
    491         if (path_parse(buf, sizeof(buf), file)) return (NULL); 
     414#ifdef MACH_O_CARBON 
     415        /* OS X uses its own kind of filetypes */ 
     416        if (mode != MODE_READ) 
     417                fsetfileinfo(buf, _fcreator, ftype); 
     418#endif 
    492419 
    493         /* Attempt to fopen the file anyway */ 
    494         fff = fopen(buf, mode); 
    495  
    496 #if defined(MACH_O_CARBON) 
    497  
    498         /* Set file creator and type */ 
    499         if (fff && strchr(mode, 'w')) fsetfileinfo(buf, _fcreator, _ftype); 
    500  
     420#if defined(RISCOS) && 0 
     421        /* do something for RISC OS here? */ 
     422        if (mode != MODE_READ) 
     423                File_SetType(n, ftype); 
    501424#endif 
     425#endif 
    502426 
    503         /* Return open file or NULL */ 
    504         return (fff); 
     427        return f; 
    505428} 
    506429 
    507  
    508430/* 
    509  * Hack -- replacement for "fclose()" 
     431 * Close file handle 'f'. 
    510432 */ 
    511 errr my_fclose(FILE *fff) 
     433bool file_close(ang_file *f) 
    512434{ 
    513         /* Require a file */ 
    514         if (!fff) return (-1)
     435        if (fclose(f->fh) != 0) 
     436               return FALSE
    515437 
    516         /* Close, check for error */ 
    517         if (fclose(fff) == EOF) return (1); 
     438        if (f->temp) 
     439               file_delete(f->fname); 
    518440 
    519         /* Success */ 
    520         return (0); 
     441        FREE(f->fname); 
     442        FREE(f); 
     443        return TRUE; 
    521444} 
    522445 
    523446 
    524447/* 
    525  * Hack -- attempt to delete a file 
     448 * Create a temporary file handle, which is deleted when closing the file. 
    526449 */ 
    527 errr fd_kill(cptr file) 
    528 
    529         char buf[1024]; 
     450ang_file *file_temp(void); 
    530451 
    531         /* Hack -- Try to parse the path */ 
    532         if (path_parse(buf, sizeof(buf), file)) return (-1); 
     452#ifdef HAVE_MKSTEMP 
    533453 
    534         /* Remove, return 0 on success, non-zero on failure */ 
    535         return (remove(buf)); 
    536 
    537  
    538  
    539 /* 
    540  * Hack -- attempt to move a file 
    541  */ 
    542 errr fd_move(cptr file, cptr what) 
     454ang_file *file_temp(void) 
    543455{ 
     456        ang_file *f; 
    544457        char buf[1024]; 
    545         char aux[1024]
     458        int fd
    546459 
    547         /* Hack -- Try to parse the path */ 
    548         if (path_parse(buf, sizeof(buf), file)) return (-1); 
     460        my_strcpy(buf, "/tmp/anXXXXXX", sizeof(buf)); 
     461        fd = mkstemp(buf); 
     462        if (fd < 0) return NULL; 
    549463 
    550         /* Hack -- Try to parse the path */ 
    551         if (path_parse(aux, sizeof(aux), what)) return (-1); 
     464        /* Allocate */ 
     465        f = ZNEW(ang_file); 
     466        f->fh = fdopen(fd, "a+"); 
    552467 
    553         /* Rename, return 0 on success, non-zero on failure */ 
    554         return (rename(buf, aux)); 
     468        if (f->fh == NULL) 
     469        { 
     470                FREE(f); 
     471                close(fd); 
     472                return NULL; 
     473        } 
     474 
     475        f->temp = TRUE; 
     476        f->fname = string_make(buf); 
     477        f->mode = MODE_READ;            /* Should really be MODE_RDWR */ 
     478 
     479        return f; 
    555480} 
    556481 
     482#else /* HAVE_MKSTEMP */ 
    557483 
    558 /* 
    559  * Hack -- attempt to open a file descriptor (create file) 
    560  * 
    561  * This function should fail if the file already exists 
    562  * 
    563  * Note that we assume that the file should be "binary" 
    564  */ 
    565 int fd_make(cptr file, int mode) 
     484ang_file *my_fopen_temp(char *buf, size_t max) 
    566485{ 
    567         char buf[1024]
    568         int fd
     486        ang_file *f
     487        const char *tempname
    569488 
    570         /* Hack -- Try to parse the path */ 
    571         if (path_parse(buf, sizeof(buf), file)) return (-1)
     489        tempname = tmpnam(NULL); 
     490        if (!tempname) return NULL
    572491 
    573         /* Create the file, fail if exists, write-only, binary */ 
    574         fd = open(buf, O_CREAT | O_EXCL | O_WRONLY | O_BINARY, mode); 
     492        f = ZNEW(ang_file); 
     493        f->fh = fopen(tempname, "a+"); 
    575494 
    576 #ifdef MACH_O_CARBON 
     495        if (f->fh == NULL) 
     496        { 
     497                FREE(f); 
     498                return NULL; 
     499        } 
    577500 
    578         /* Set file creator and type */ 
    579         if (fd >= 0) fsetfileinfo(buf, _fcreator, _ftype); 
     501        f->temp = TRUE; 
     502        f->fname = string_make(tempname); 
     503        f->mode = MODE_READ;            /* Should really be MODE_RDWR */ 
    580504 
    581 #endif 
    582  
    583         /* Return descriptor */ 
    584         return (fd); 
     505        return f; 
    585506} 
    586507 
     508#endif /* HAVE_MKSTEMP */ 
    587509 
     510 
     511 
     512/** Locking functions **/ 
     513 
    588514/* 
    589  * Hack -- attempt to open a file descriptor (existing file) 
    590  * 
    591  * Note that we assume that the file should be "binary" 
     515 * Lock a file using POSIX locks, on platforms where this is supported. 
    592516 */ 
    593 int fd_open(cptr file, int flags
     517void file_lock(ang_file *f
    594518{ 
    595         char buf[1024]; 
    596  
    597         /* Hack -- Try to parse the path */ 
    598         if (path_parse(buf, sizeof(buf), file)) return (-1); 
    599  
    600         /* Attempt to open the file */ 
    601         return (open(buf, flags | O_BINARY, 0)); 
     519#if defined(HAVE_FCNTL_H) && defined(SET_UID) 
     520        struct flock lock; 
     521        lock.l_type = (f->mode == MODE_READ ? F_RDLCK : F_WRLCK); 
     522        lock.l_whence = SEEK_SET; 
     523        lock.l_start = 0; 
     524        lock.l_len = 0; 
     525        lock.l_pid = 0; 
     526        fcntl(fileno(f->fh), F_SETLKW, &lock); 
     527#endif /* HAVE_FCNTL_H && SET_UID */ 
    602528} 
    603529 
    604  
    605530/* 
    606  * Attempt to lock a file descriptor 
    607  * 
    608  * Legal lock types -- F_UNLCK, F_RDLCK, F_WRLCK 
     531 * Unlock a file locked using file_lock(). 
    609532 */ 
    610 errr fd_lock(int fd, int what
     533void file_unlock(ang_file *f
    611534{ 
    612535#if defined(HAVE_FCNTL_H) && defined(SET_UID) 
    613  
    614536        struct flock lock; 
     537        lock.l_type = F_UNLCK; 
     538        lock.l_whence = SEEK_SET; 
     539        lock.l_start = 0; 
     540        lock.l_len = 0; 
     541        lock.l_pid = 0; 
     542        fcntl(fileno(f->fh), F_SETLK, &lock); 
     543#endif /* HAVE_FCNTL_H && SET_UID */ 
     544} 
    615545 
    616         /* Verify the fd */ 
    617         if (fd < 0) return (-1); 
    618546 
    619         lock.l_type = what; 
    620         lock.l_start = 0; /* Lock the entire file */ 
    621         lock.l_whence = SEEK_SET; /* Lock the entire file */ 
    622         lock.l_len = 0; /* Lock the entire file */ 
     547/** Byte-based IO and functions **/ 
    623548 
    624         /* Wait for access and set lock status */ 
    625         /* 
    626          * Change F_SETLKW to F_SETLK if it's preferable to return 
    627          * without locking and reporting an error instead of waiting. 
    628          */ 
    629         return (fcntl(fd, F_SETLKW, &lock)); 
    630  
    631 #else /* HAVE_FCNTL_H */ 
    632  
    633         /* Unused parameters */ 
    634         (void)fd; 
    635         (void)what; 
    636  
    637         /* Success */ 
    638         return (0); 
    639  
    640 #endif /* SET_UID */ 
    641  
     549/* 
     550 * Seek to location 'pos' in file 'f'. 
     551 */ 
     552bool file_seek(ang_file *f, u32b pos) 
     553
     554        return (fseek(f->fh, pos, SEEK_SET) == 0); 
    642555} 
    643556 
    644  
    645557/* 
    646  * Hack -- attempt to seek on a file descriptor 
     558 * Read a single, 8-bit character from file 'f'. 
    647559 */ 
    648 errr fd_seek(int fd, long n
     560bool file_readc(ang_file *f, byte *b
    649561{ 
    650         long p
     562        int i = fgetc(f->fh)
    651563 
    652         /* Verify fd */ 
    653         if (fd < 0) return (-1)
     564        if (i == EOF) 
     565               return FALSE
    654566 
    655         /* Seek to the given position */ 
    656         p = lseek(fd, n, SEEK_SET); 
     567        *b = (byte)i; 
     568        return TRUE; 
     569
    657570