Upload 2.0.2
[physicsfs] / platform / posix.c
1 /*
2  * Posix-esque support routines for PhysicsFS.
3  *
4  * Please see the file LICENSE.txt in the source's root directory.
5  *
6  *  This file written by Ryan C. Gordon.
7  */
8
9 #define __PHYSICSFS_INTERNAL__
10 #include "physfs_platforms.h"
11
12 #ifdef PHYSFS_PLATFORM_POSIX
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <ctype.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <pwd.h>
22 #include <dirent.h>
23 #include <errno.h>
24 #include <fcntl.h>
25
26 #ifdef PHYSFS_HAVE_LLSEEK
27 #include <linux/unistd.h>
28 #endif
29
30 #include "physfs_internal.h"
31
32
33 const char *__PHYSFS_platformDirSeparator = "/";
34
35
36 char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname)
37 {
38     const char *envr = getenv(varname);
39     char *retval = NULL;
40
41     if (envr != NULL)
42     {
43         retval = (char *) allocator.Malloc(strlen(envr) + 1);
44         if (retval != NULL)
45             strcpy(retval, envr);
46     } /* if */
47
48     return(retval);
49 } /* __PHYSFS_platformCopyEnvironmentVariable */
50
51
52 static char *getUserNameByUID(void)
53 {
54     uid_t uid = getuid();
55     struct passwd *pw;
56     char *retval = NULL;
57
58     pw = getpwuid(uid);
59     if ((pw != NULL) && (pw->pw_name != NULL))
60     {
61         retval = (char *) allocator.Malloc(strlen(pw->pw_name) + 1);
62         if (retval != NULL)
63             strcpy(retval, pw->pw_name);
64     } /* if */
65     
66     return(retval);
67 } /* getUserNameByUID */
68
69
70 static char *getUserDirByUID(void)
71 {
72     uid_t uid = getuid();
73     struct passwd *pw;
74     char *retval = NULL;
75
76     pw = getpwuid(uid);
77     if ((pw != NULL) && (pw->pw_dir != NULL))
78     {
79         retval = (char *) allocator.Malloc(strlen(pw->pw_dir) + 1);
80         if (retval != NULL)
81             strcpy(retval, pw->pw_dir);
82     } /* if */
83     
84     return(retval);
85 } /* getUserDirByUID */
86
87
88 char *__PHYSFS_platformGetUserName(void)
89 {
90     char *retval = getUserNameByUID();
91     if (retval == NULL)
92         retval = __PHYSFS_platformCopyEnvironmentVariable("USER");
93     return(retval);
94 } /* __PHYSFS_platformGetUserName */
95
96
97 char *__PHYSFS_platformGetUserDir(void)
98 {
99     char *retval = __PHYSFS_platformCopyEnvironmentVariable("HOME");
100
101     /* if the environment variable was set, make sure it's really a dir. */
102     if (retval != NULL)
103     {
104         struct stat statbuf;
105         if ((stat(retval, &statbuf) == -1) || (S_ISDIR(statbuf.st_mode) == 0))
106         {
107             allocator.Free(retval);
108             retval = NULL;
109         } /* if */
110     } /* if */
111
112     if (retval == NULL)
113         retval = getUserDirByUID();
114
115     return(retval);
116 } /* __PHYSFS_platformGetUserDir */
117
118
119 int __PHYSFS_platformExists(const char *fname)
120 {
121     struct stat statbuf;
122     BAIL_IF_MACRO(lstat(fname, &statbuf) == -1, strerror(errno), 0);
123     return(1);
124 } /* __PHYSFS_platformExists */
125
126
127 int __PHYSFS_platformIsSymLink(const char *fname)
128 {
129     struct stat statbuf;
130     BAIL_IF_MACRO(lstat(fname, &statbuf) == -1, strerror(errno), 0);
131     return( (S_ISLNK(statbuf.st_mode)) ? 1 : 0 );
132 } /* __PHYSFS_platformIsSymlink */
133
134
135 int __PHYSFS_platformIsDirectory(const char *fname)
136 {
137     struct stat statbuf;
138     BAIL_IF_MACRO(stat(fname, &statbuf) == -1, strerror(errno), 0);
139     return( (S_ISDIR(statbuf.st_mode)) ? 1 : 0 );
140 } /* __PHYSFS_platformIsDirectory */
141
142
143 char *__PHYSFS_platformCvtToDependent(const char *prepend,
144                                       const char *dirName,
145                                       const char *append)
146 {
147     int len = ((prepend) ? strlen(prepend) : 0) +
148               ((append) ? strlen(append) : 0) +
149               strlen(dirName) + 1;
150     char *retval = (char *) allocator.Malloc(len);
151
152     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
153
154     /* platform-independent notation is Unix-style already.  :)  */
155
156     if (prepend)
157         strcpy(retval, prepend);
158     else
159         retval[0] = '\0';
160
161     strcat(retval, dirName);
162
163     if (append)
164         strcat(retval, append);
165
166     return(retval);
167 } /* __PHYSFS_platformCvtToDependent */
168
169
170
171 void __PHYSFS_platformEnumerateFiles(const char *dirname,
172                                      int omitSymLinks,
173                                      PHYSFS_EnumFilesCallback callback,
174                                      const char *origdir,
175                                      void *callbackdata)
176 {
177     DIR *dir;
178     struct dirent *ent;
179     int bufsize = 0;
180     char *buf = NULL;
181     int dlen = 0;
182
183     if (omitSymLinks)  /* !!! FIXME: this malloc sucks. */
184     {
185         dlen = strlen(dirname);
186         bufsize = dlen + 256;
187         buf = (char *) allocator.Malloc(bufsize);
188         if (buf == NULL)
189             return;
190         strcpy(buf, dirname);
191         if (buf[dlen - 1] != '/')
192         {
193             buf[dlen++] = '/';
194             buf[dlen] = '\0';
195         } /* if */
196     } /* if */
197
198     errno = 0;
199     dir = opendir(dirname);
200     if (dir == NULL)
201     {
202         allocator.Free(buf);
203         return;
204     } /* if */
205
206     while ((ent = readdir(dir)) != NULL)
207     {
208         if (strcmp(ent->d_name, ".") == 0)
209             continue;
210
211         if (strcmp(ent->d_name, "..") == 0)
212             continue;
213
214         if (omitSymLinks)
215         {
216             char *p;
217             int len = strlen(ent->d_name) + dlen + 1;
218             if (len > bufsize)
219             {
220                 p = (char *) allocator.Realloc(buf, len);
221                 if (p == NULL)
222                     continue;
223                 buf = p;
224                 bufsize = len;
225             } /* if */
226
227             strcpy(buf + dlen, ent->d_name);
228             if (__PHYSFS_platformIsSymLink(buf))
229                 continue;
230         } /* if */
231
232         callback(callbackdata, origdir, ent->d_name);
233     } /* while */
234
235     allocator.Free(buf);
236     closedir(dir);
237 } /* __PHYSFS_platformEnumerateFiles */
238
239
240 int __PHYSFS_platformMkDir(const char *path)
241 {
242     int rc;
243     errno = 0;
244     rc = mkdir(path, S_IRWXU);
245     BAIL_IF_MACRO(rc == -1, strerror(errno), 0);
246     return(1);
247 } /* __PHYSFS_platformMkDir */
248
249
250 static void *doOpen(const char *filename, int mode)
251 {
252     const int appending = (mode & O_APPEND);
253     int fd;
254     int *retval;
255     errno = 0;
256
257     /* O_APPEND doesn't actually behave as we'd like. */
258     mode &= ~O_APPEND;
259
260     fd = open(filename, mode, S_IRUSR | S_IWUSR);
261     BAIL_IF_MACRO(fd < 0, strerror(errno), NULL);
262
263     if (appending)
264     {
265         if (lseek(fd, 0, SEEK_END) < 0)
266         {
267             close(fd);
268             BAIL_MACRO(strerror(errno), NULL);
269         } /* if */
270     } /* if */
271
272     retval = (int *) allocator.Malloc(sizeof (int));
273     if (retval == NULL)
274     {
275         close(fd);
276         BAIL_MACRO(ERR_OUT_OF_MEMORY, NULL);
277     } /* if */
278
279     *retval = fd;
280     return((void *) retval);
281 } /* doOpen */
282
283
284 void *__PHYSFS_platformOpenRead(const char *filename)
285 {
286     return(doOpen(filename, O_RDONLY));
287 } /* __PHYSFS_platformOpenRead */
288
289
290 void *__PHYSFS_platformOpenWrite(const char *filename)
291 {
292     return(doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC));
293 } /* __PHYSFS_platformOpenWrite */
294
295
296 void *__PHYSFS_platformOpenAppend(const char *filename)
297 {
298     return(doOpen(filename, O_WRONLY | O_CREAT | O_APPEND));
299 } /* __PHYSFS_platformOpenAppend */
300
301
302 PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
303                                     PHYSFS_uint32 size, PHYSFS_uint32 count)
304 {
305     int fd = *((int *) opaque);
306     int max = size * count;
307     int rc = read(fd, buffer, max);
308
309     BAIL_IF_MACRO(rc == -1, strerror(errno), rc);
310     assert(rc <= max);
311
312     if ((rc < max) && (size > 1))
313         lseek(fd, -(rc % size), SEEK_CUR); /* rollback to object boundary. */
314
315     return(rc / size);
316 } /* __PHYSFS_platformRead */
317
318
319 PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
320                                      PHYSFS_uint32 size, PHYSFS_uint32 count)
321 {
322     int fd = *((int *) opaque);
323     int max = size * count;
324     int rc = write(fd, (void *) buffer, max);
325
326     BAIL_IF_MACRO(rc == -1, strerror(errno), rc);
327     assert(rc <= max);
328
329     if ((rc < max) && (size > 1))
330         lseek(fd, -(rc % size), SEEK_CUR); /* rollback to object boundary. */
331
332     return(rc / size);
333 } /* __PHYSFS_platformWrite */
334
335
336 int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
337 {
338     int fd = *((int *) opaque);
339
340     #ifdef PHYSFS_HAVE_LLSEEK
341       unsigned long offset_high = ((pos >> 32) & 0xFFFFFFFF);
342       unsigned long offset_low = (pos & 0xFFFFFFFF);
343       loff_t retoffset;
344       int rc = llseek(fd, offset_high, offset_low, &retoffset, SEEK_SET);
345       BAIL_IF_MACRO(rc == -1, strerror(errno), 0);
346     #else
347       BAIL_IF_MACRO(lseek(fd, (int) pos, SEEK_SET) == -1, strerror(errno), 0);
348     #endif
349
350     return(1);
351 } /* __PHYSFS_platformSeek */
352
353
354 PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
355 {
356     int fd = *((int *) opaque);
357     PHYSFS_sint64 retval;
358
359     #ifdef PHYSFS_HAVE_LLSEEK
360       loff_t retoffset;
361       int rc = llseek(fd, 0, &retoffset, SEEK_CUR);
362       BAIL_IF_MACRO(rc == -1, strerror(errno), -1);
363       retval = (PHYSFS_sint64) retoffset;
364     #else
365       retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR);
366       BAIL_IF_MACRO(retval == -1, strerror(errno), -1);
367     #endif
368
369     return(retval);
370 } /* __PHYSFS_platformTell */
371
372
373 PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
374 {
375     int fd = *((int *) opaque);
376     struct stat statbuf;
377     BAIL_IF_MACRO(fstat(fd, &statbuf) == -1, strerror(errno), -1);
378     return((PHYSFS_sint64) statbuf.st_size);
379 } /* __PHYSFS_platformFileLength */
380
381
382 int __PHYSFS_platformEOF(void *opaque)
383 {
384     PHYSFS_sint64 pos = __PHYSFS_platformTell(opaque);
385     PHYSFS_sint64 len = __PHYSFS_platformFileLength(opaque);
386     return(pos >= len);
387 } /* __PHYSFS_platformEOF */
388
389
390 int __PHYSFS_platformFlush(void *opaque)
391 {
392     int fd = *((int *) opaque);
393     BAIL_IF_MACRO(fsync(fd) == -1, strerror(errno), 0);
394     return(1);
395 } /* __PHYSFS_platformFlush */
396
397
398 int __PHYSFS_platformClose(void *opaque)
399 {
400     int fd = *((int *) opaque);
401     BAIL_IF_MACRO(close(fd) == -1, strerror(errno), 0);
402     allocator.Free(opaque);
403     return(1);
404 } /* __PHYSFS_platformClose */
405
406
407 int __PHYSFS_platformDelete(const char *path)
408 {
409     BAIL_IF_MACRO(remove(path) == -1, strerror(errno), 0);
410     return(1);
411 } /* __PHYSFS_platformDelete */
412
413
414 PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname)
415 {
416     struct stat statbuf;
417     BAIL_IF_MACRO(stat(fname, &statbuf) < 0, strerror(errno), -1);
418     return statbuf.st_mtime;
419 } /* __PHYSFS_platformGetLastModTime */
420
421 #endif  /* PHYSFS_PLATFORM_POSIX */
422
423 /* end of posix.c ... */
424