Skip to content

Latest commit

 

History

History
503 lines (395 loc) · 12.6 KB

platform_posix.c

File metadata and controls

503 lines (395 loc) · 12.6 KB
 
May 24, 2002
May 24, 2002
1
2
3
/*
* Posix-esque support routines for PhysicsFS.
*
Mar 11, 2007
Mar 11, 2007
4
* Please see the file LICENSE.txt in the source's root directory.
May 24, 2002
May 24, 2002
5
6
7
8
*
* This file written by Ryan C. Gordon.
*/
Mar 20, 2012
Mar 20, 2012
9
10
/* !!! FIXME: check for EINTR? */
Mar 11, 2007
Mar 11, 2007
11
12
13
14
#define __PHYSICSFS_INTERNAL__
#include "physfs_platforms.h"
#ifdef PHYSFS_PLATFORM_POSIX
Jun 29, 2002
Jun 29, 2002
15
May 24, 2002
May 24, 2002
16
#include <unistd.h>
Jun 2, 2002
Jun 2, 2002
17
#include <ctype.h>
May 24, 2002
May 24, 2002
18
19
#include <sys/types.h>
#include <sys/stat.h>
Jun 2, 2002
Jun 2, 2002
20
#include <pwd.h>
May 24, 2002
May 24, 2002
21
22
#include <dirent.h>
#include <errno.h>
Jun 2, 2002
Jun 2, 2002
23
#include <fcntl.h>
May 24, 2002
May 24, 2002
24
Jul 25, 2011
Jul 25, 2011
25
26
27
28
#if ((!defined PHYSFS_NO_THREAD_SUPPORT) && (!defined PHYSFS_PLATFORM_BEOS))
#include <pthread.h>
#endif
May 24, 2002
May 24, 2002
29
30
#include "physfs_internal.h"
Mar 20, 2012
Mar 20, 2012
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
static PHYSFS_ErrorCode errcodeFromErrnoError(const int err)
{
switch (err)
{
case 0: return PHYSFS_ERR_OK;
case EACCES: return PHYSFS_ERR_PERMISSION;
case EPERM: return PHYSFS_ERR_PERMISSION;
case EDQUOT: return PHYSFS_ERR_NO_SPACE;
case EIO: return PHYSFS_ERR_IO;
case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP;
case EMLINK: return PHYSFS_ERR_NO_SPACE;
case ENAMETOOLONG: return PHYSFS_ERR_BAD_FILENAME;
case ENOENT: return PHYSFS_ERR_NO_SUCH_PATH;
case ENOSPC: return PHYSFS_ERR_NO_SPACE;
case ENOTDIR: return PHYSFS_ERR_NO_SUCH_PATH;
case EISDIR: return PHYSFS_ERR_NOT_A_FILE;
case EROFS: return PHYSFS_ERR_READ_ONLY;
case ETXTBSY: return PHYSFS_ERR_BUSY;
case EBUSY: return PHYSFS_ERR_BUSY;
case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY;
case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY;
default: return PHYSFS_ERR_OS_ERROR;
} /* switch */
} /* errcodeFromErrnoError */
static inline PHYSFS_ErrorCode errcodeFromErrno(void)
{
return errcodeFromErrnoError(errno);
} /* errcodeFromErrno */
May 24, 2002
May 24, 2002
65
66
67
68
69
70
71
char *__PHYSFS_platformCopyEnvironmentVariable(const char *varname)
{
const char *envr = getenv(varname);
char *retval = NULL;
if (envr != NULL)
{
Mar 14, 2005
Mar 14, 2005
72
retval = (char *) allocator.Malloc(strlen(envr) + 1);
Mar 20, 2012
Mar 20, 2012
73
74
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
strcpy(retval, envr);
May 24, 2002
May 24, 2002
75
76
} /* if */
Jan 28, 2010
Jan 28, 2010
77
return retval;
May 24, 2002
May 24, 2002
78
79
80
81
82
83
84
85
86
87
88
89
} /* __PHYSFS_platformCopyEnvironmentVariable */
static char *getUserNameByUID(void)
{
uid_t uid = getuid();
struct passwd *pw;
char *retval = NULL;
pw = getpwuid(uid);
if ((pw != NULL) && (pw->pw_name != NULL))
{
Mar 14, 2005
Mar 14, 2005
90
retval = (char *) allocator.Malloc(strlen(pw->pw_name) + 1);
May 24, 2002
May 24, 2002
91
92
93
94
if (retval != NULL)
strcpy(retval, pw->pw_name);
} /* if */
Jan 28, 2010
Jan 28, 2010
95
return retval;
May 24, 2002
May 24, 2002
96
97
98
99
100
101
102
103
104
105
106
107
} /* getUserNameByUID */
static char *getUserDirByUID(void)
{
uid_t uid = getuid();
struct passwd *pw;
char *retval = NULL;
pw = getpwuid(uid);
if ((pw != NULL) && (pw->pw_dir != NULL))
{
Mar 14, 2005
Mar 14, 2005
108
retval = (char *) allocator.Malloc(strlen(pw->pw_dir) + 1);
May 24, 2002
May 24, 2002
109
110
111
112
if (retval != NULL)
strcpy(retval, pw->pw_dir);
} /* if */
Jan 28, 2010
Jan 28, 2010
113
return retval;
May 24, 2002
May 24, 2002
114
115
116
117
118
119
120
121
} /* getUserDirByUID */
char *__PHYSFS_platformGetUserName(void)
{
char *retval = getUserNameByUID();
if (retval == NULL)
retval = __PHYSFS_platformCopyEnvironmentVariable("USER");
Jan 28, 2010
Jan 28, 2010
122
return retval;
May 24, 2002
May 24, 2002
123
124
125
126
127
128
} /* __PHYSFS_platformGetUserName */
char *__PHYSFS_platformGetUserDir(void)
{
char *retval = __PHYSFS_platformCopyEnvironmentVariable("HOME");
Mar 21, 2010
Mar 21, 2010
129
130
131
132
133
134
135
136
137
138
139
140
/* if the environment variable was set, make sure it's really a dir. */
if (retval != NULL)
{
struct stat statbuf;
if ((stat(retval, &statbuf) == -1) || (S_ISDIR(statbuf.st_mode) == 0))
{
allocator.Free(retval);
retval = NULL;
} /* if */
} /* if */
May 24, 2002
May 24, 2002
141
142
if (retval == NULL)
retval = getUserDirByUID();
Mar 21, 2010
Mar 21, 2010
143
Jan 28, 2010
Jan 28, 2010
144
return retval;
May 24, 2002
May 24, 2002
145
146
147
} /* __PHYSFS_platformGetUserDir */
Sep 29, 2004
Sep 29, 2004
148
149
void __PHYSFS_platformEnumerateFiles(const char *dirname,
int omitSymLinks,
Sep 18, 2005
Sep 18, 2005
150
151
PHYSFS_EnumFilesCallback callback,
const char *origdir,
Sep 29, 2004
Sep 29, 2004
152
void *callbackdata)
May 24, 2002
May 24, 2002
153
154
155
156
157
158
159
{
DIR *dir;
struct dirent *ent;
int bufsize = 0;
char *buf = NULL;
int dlen = 0;
Sep 29, 2004
Sep 29, 2004
160
if (omitSymLinks) /* !!! FIXME: this malloc sucks. */
May 24, 2002
May 24, 2002
161
162
163
{
dlen = strlen(dirname);
bufsize = dlen + 256;
Mar 14, 2005
Mar 14, 2005
164
buf = (char *) allocator.Malloc(bufsize);
Sep 29, 2004
Sep 29, 2004
165
166
if (buf == NULL)
return;
May 24, 2002
May 24, 2002
167
168
169
170
171
172
173
174
175
176
177
178
strcpy(buf, dirname);
if (buf[dlen - 1] != '/')
{
buf[dlen++] = '/';
buf[dlen] = '\0';
} /* if */
} /* if */
errno = 0;
dir = opendir(dirname);
if (dir == NULL)
{
Mar 24, 2007
Mar 24, 2007
179
allocator.Free(buf);
Sep 29, 2004
Sep 29, 2004
180
return;
May 24, 2002
May 24, 2002
181
182
} /* if */
Jul 23, 2002
Jul 23, 2002
183
while ((ent = readdir(dir)) != NULL)
May 24, 2002
May 24, 2002
184
185
186
187
188
189
190
191
192
{
if (strcmp(ent->d_name, ".") == 0)
continue;
if (strcmp(ent->d_name, "..") == 0)
continue;
if (omitSymLinks)
{
Sep 5, 2010
Sep 5, 2010
193
194
PHYSFS_Stat statbuf;
int exists = 0;
May 24, 2002
May 24, 2002
195
196
197
198
char *p;
int len = strlen(ent->d_name) + dlen + 1;
if (len > bufsize)
{
Mar 14, 2005
Mar 14, 2005
199
p = (char *) allocator.Realloc(buf, len);
May 24, 2002
May 24, 2002
200
201
202
203
204
205
206
if (p == NULL)
continue;
buf = p;
bufsize = len;
} /* if */
strcpy(buf + dlen, ent->d_name);
Sep 5, 2010
Sep 5, 2010
207
Mar 9, 2012
Mar 9, 2012
208
209
210
211
212
213
if (!__PHYSFS_platformStat(buf, &exists, &statbuf))
continue;
else if (!exists)
continue; /* probably can't happen, but just in case. */
else if (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK)
continue;
May 24, 2002
May 24, 2002
214
215
} /* if */
Sep 18, 2005
Sep 18, 2005
216
callback(callbackdata, origdir, ent->d_name);
May 24, 2002
May 24, 2002
217
218
} /* while */
Mar 24, 2007
Mar 24, 2007
219
allocator.Free(buf);
May 24, 2002
May 24, 2002
220
221
222
223
224
225
closedir(dir);
} /* __PHYSFS_platformEnumerateFiles */
int __PHYSFS_platformMkDir(const char *path)
{
Mar 20, 2012
Mar 20, 2012
226
227
const int rc = mkdir(path, S_IRWXU);
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), 0);
Jan 28, 2010
Jan 28, 2010
228
return 1;
May 24, 2002
May 24, 2002
229
230
231
} /* __PHYSFS_platformMkDir */
Jun 2, 2002
Jun 2, 2002
232
static void *doOpen(const char *filename, int mode)
May 24, 2002
May 24, 2002
233
{
Apr 3, 2008
Apr 3, 2008
234
const int appending = (mode & O_APPEND);
Jun 2, 2002
Jun 2, 2002
235
236
int fd;
int *retval;
May 24, 2002
May 24, 2002
237
238
errno = 0;
Apr 3, 2008
Apr 3, 2008
239
240
241
/* O_APPEND doesn't actually behave as we'd like. */
mode &= ~O_APPEND;
Jun 2, 2002
Jun 2, 2002
242
fd = open(filename, mode, S_IRUSR | S_IWUSR);
Mar 20, 2012
Mar 20, 2012
243
BAIL_IF_MACRO(fd < 0, errcodeFromErrno(), NULL);
May 24, 2002
May 24, 2002
244
Apr 3, 2008
Apr 3, 2008
245
246
247
248
if (appending)
{
if (lseek(fd, 0, SEEK_END) < 0)
{
Mar 20, 2012
Mar 20, 2012
249
const int err = errno;
Apr 3, 2008
Apr 3, 2008
250
close(fd);
Mar 20, 2012
Mar 20, 2012
251
BAIL_MACRO(errcodeFromErrnoError(err), NULL);
Apr 3, 2008
Apr 3, 2008
252
253
254
} /* if */
} /* if */
Mar 14, 2005
Mar 14, 2005
255
retval = (int *) allocator.Malloc(sizeof (int));
Mar 20, 2012
Mar 20, 2012
256
if (!retval)
Jun 2, 2002
Jun 2, 2002
257
258
{
close(fd);
Mar 20, 2012
Mar 20, 2012
259
BAIL_MACRO(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
Jun 2, 2002
Jun 2, 2002
260
261
262
} /* if */
*retval = fd;
Jan 28, 2010
Jan 28, 2010
263
return ((void *) retval);
May 24, 2002
May 24, 2002
264
265
266
267
268
} /* doOpen */
void *__PHYSFS_platformOpenRead(const char *filename)
{
Jan 28, 2010
Jan 28, 2010
269
return doOpen(filename, O_RDONLY);
May 24, 2002
May 24, 2002
270
271
272
273
274
} /* __PHYSFS_platformOpenRead */
void *__PHYSFS_platformOpenWrite(const char *filename)
{
Jan 28, 2010
Jan 28, 2010
275
return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC);
May 24, 2002
May 24, 2002
276
277
278
279
280
} /* __PHYSFS_platformOpenWrite */
void *__PHYSFS_platformOpenAppend(const char *filename)
{
Jan 28, 2010
Jan 28, 2010
281
return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND);
May 24, 2002
May 24, 2002
282
283
284
285
} /* __PHYSFS_platformOpenAppend */
PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
Aug 21, 2010
Aug 21, 2010
286
PHYSFS_uint64 len)
May 24, 2002
May 24, 2002
287
{
Aug 29, 2010
Aug 29, 2010
288
const int fd = *((int *) opaque);
Aug 21, 2010
Aug 21, 2010
289
ssize_t rc = 0;
May 24, 2002
May 24, 2002
290
Mar 20, 2012
Mar 20, 2012
291
292
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1);
Jun 2, 2002
Jun 2, 2002
293
Aug 21, 2010
Aug 21, 2010
294
rc = read(fd, buffer, (size_t) len);
Mar 20, 2012
Mar 20, 2012
295
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), -1);
Aug 21, 2010
Aug 21, 2010
296
297
298
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
May 24, 2002
May 24, 2002
299
300
301
302
} /* __PHYSFS_platformRead */
PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
Aug 21, 2010
Aug 21, 2010
303
PHYSFS_uint64 len)
May 24, 2002
May 24, 2002
304
{
Aug 29, 2010
Aug 29, 2010
305
const int fd = *((int *) opaque);
Aug 21, 2010
Aug 21, 2010
306
ssize_t rc = 0;
Jun 2, 2002
Jun 2, 2002
307
Mar 20, 2012
Mar 20, 2012
308
309
if (!__PHYSFS_ui64FitsAddressSpace(len))
BAIL_MACRO(PHYSFS_ERR_INVALID_ARGUMENT, -1);
Jun 2, 2002
Jun 2, 2002
310
Aug 21, 2010
Aug 21, 2010
311
rc = write(fd, (void *) buffer, (size_t) len);
Mar 20, 2012
Mar 20, 2012
312
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), rc);
Aug 21, 2010
Aug 21, 2010
313
314
315
assert(rc >= 0);
assert(rc <= len);
return (PHYSFS_sint64) rc;
May 24, 2002
May 24, 2002
316
317
318
319
320
} /* __PHYSFS_platformWrite */
int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
{
Aug 29, 2010
Aug 29, 2010
321
const int fd = *((int *) opaque);
Mar 20, 2012
Mar 20, 2012
322
323
const int rc = lseek(fd, (off_t) pos, SEEK_SET);
BAIL_IF_MACRO(rc == -1, errcodeFromErrno(), 0);
Jan 28, 2010
Jan 28, 2010
324
return 1;
May 24, 2002
May 24, 2002
325
326
327
328
329
} /* __PHYSFS_platformSeek */
PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
{
Aug 29, 2010
Aug 29, 2010
330
const int fd = *((int *) opaque);
Jun 6, 2002
Jun 6, 2002
331
PHYSFS_sint64 retval;
Mar 18, 2012
Mar 18, 2012
332
retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR);
Mar 20, 2012
Mar 20, 2012
333
BAIL_IF_MACRO(retval == -1, errcodeFromErrno(), -1);
Jan 28, 2010
Jan 28, 2010
334
return retval;
May 24, 2002
May 24, 2002
335
336
337
338
339
} /* __PHYSFS_platformTell */
PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
{
Aug 29, 2010
Aug 29, 2010
340
const int fd = *((int *) opaque);
May 24, 2002
May 24, 2002
341
struct stat statbuf;
Mar 20, 2012
Mar 20, 2012
342
BAIL_IF_MACRO(fstat(fd, &statbuf) == -1, errcodeFromErrno(), -1);
Jan 28, 2010
Jan 28, 2010
343
return ((PHYSFS_sint64) statbuf.st_size);
May 24, 2002
May 24, 2002
344
345
346
347
348
} /* __PHYSFS_platformFileLength */
int __PHYSFS_platformFlush(void *opaque)
{
Aug 29, 2010
Aug 29, 2010
349
const int fd = *((int *) opaque);
Mar 20, 2012
Mar 20, 2012
350
BAIL_IF_MACRO(fsync(fd) == -1, errcodeFromErrno(), 0);
Jan 28, 2010
Jan 28, 2010
351
return 1;
May 24, 2002
May 24, 2002
352
353
354
} /* __PHYSFS_platformFlush */
Aug 30, 2010
Aug 30, 2010
355
void __PHYSFS_platformClose(void *opaque)
May 24, 2002
May 24, 2002
356
{
Aug 30, 2010
Aug 30, 2010
357
358
const int fd = *((int *) opaque);
(void) close(fd); /* we don't check this. You should have used flush! */
Mar 14, 2005
Mar 14, 2005
359
allocator.Free(opaque);
May 24, 2002
May 24, 2002
360
361
362
363
364
} /* __PHYSFS_platformClose */
int __PHYSFS_platformDelete(const char *path)
{
Mar 20, 2012
Mar 20, 2012
365
BAIL_IF_MACRO(remove(path) == -1, errcodeFromErrno(), 0);
Jan 28, 2010
Jan 28, 2010
366
return 1;
May 24, 2002
May 24, 2002
367
368
} /* __PHYSFS_platformDelete */
May 25, 2002
May 25, 2002
369
Feb 15, 2010
Feb 15, 2010
370
371
372
373
int __PHYSFS_platformStat(const char *filename, int *exists, PHYSFS_Stat *st)
{
struct stat statbuf;
Mar 9, 2012
Mar 9, 2012
374
if (lstat(filename, &statbuf) == -1)
Feb 15, 2010
Feb 15, 2010
375
{
Mar 9, 2012
Mar 9, 2012
376
*exists = (errno == ENOENT);
Mar 20, 2012
Mar 20, 2012
377
BAIL_MACRO(errcodeFromErrno(), 0);
Feb 15, 2010
Feb 15, 2010
378
379
} /* if */
Jun 22, 2011
Jun 22, 2011
380
381
*exists = 1;
Feb 15, 2010
Feb 15, 2010
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
if (S_ISREG(statbuf.st_mode))
{
st->filetype = PHYSFS_FILETYPE_REGULAR;
st->filesize = statbuf.st_size;
} /* if */
else if(S_ISDIR(statbuf.st_mode))
{
st->filetype = PHYSFS_FILETYPE_DIRECTORY;
st->filesize = 0;
} /* else if */
else
{
st->filetype = PHYSFS_FILETYPE_OTHER;
st->filesize = statbuf.st_size;
} /* else */
st->modtime = statbuf.st_mtime;
st->createtime = statbuf.st_ctime;
st->accesstime = statbuf.st_atime;
/* !!! FIXME: maybe we should just report full permissions? */
st->readonly = access(filename, W_OK);
Aug 21, 2010
Aug 21, 2010
406
return 1;
Feb 15, 2010
Feb 15, 2010
407
408
} /* __PHYSFS_platformStat */
Jul 25, 2011
Jul 25, 2011
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
#ifndef PHYSFS_PLATFORM_BEOS /* BeOS has its own code in platform_beos.cpp */
#if (defined PHYSFS_NO_THREAD_SUPPORT)
void *__PHYSFS_platformGetThreadID(void) { return ((void *) 0x0001); }
void *__PHYSFS_platformCreateMutex(void) { return ((void *) 0x0001); }
void __PHYSFS_platformDestroyMutex(void *mutex) {}
int __PHYSFS_platformGrabMutex(void *mutex) { return 1; }
void __PHYSFS_platformReleaseMutex(void *mutex) {}
#else
typedef struct
{
pthread_mutex_t mutex;
pthread_t owner;
PHYSFS_uint32 count;
} PthreadMutex;
void *__PHYSFS_platformGetThreadID(void)
{
return ( (void *) ((size_t) pthread_self()) );
} /* __PHYSFS_platformGetThreadID */
void *__PHYSFS_platformCreateMutex(void)
{
int rc;
PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex));
Mar 20, 2012
Mar 20, 2012
439
BAIL_IF_MACRO(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
Jul 25, 2011
Jul 25, 2011
440
441
442
443
rc = pthread_mutex_init(&m->mutex, NULL);
if (rc != 0)
{
allocator.Free(m);
Mar 20, 2012
Mar 20, 2012
444
BAIL_MACRO(PHYSFS_ERR_OS_ERROR, NULL);
Jul 25, 2011
Jul 25, 2011
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
} /* if */
m->count = 0;
m->owner = (pthread_t) 0xDEADBEEF;
return ((void *) m);
} /* __PHYSFS_platformCreateMutex */
void __PHYSFS_platformDestroyMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
/* Destroying a locked mutex is a bug, but we'll try to be helpful. */
if ((m->owner == pthread_self()) && (m->count > 0))
pthread_mutex_unlock(&m->mutex);
pthread_mutex_destroy(&m->mutex);
allocator.Free(m);
} /* __PHYSFS_platformDestroyMutex */
int __PHYSFS_platformGrabMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
pthread_t tid = pthread_self();
if (m->owner != tid)
{
if (pthread_mutex_lock(&m->mutex) != 0)
return 0;
m->owner = tid;
} /* if */
m->count++;
return 1;
} /* __PHYSFS_platformGrabMutex */
void __PHYSFS_platformReleaseMutex(void *mutex)
{
PthreadMutex *m = (PthreadMutex *) mutex;
Mar 20, 2012
Mar 20, 2012
485
486
assert(m->owner == pthread_self()); /* catch programming errors. */
assert(m->count > 0); /* catch programming errors. */
Jul 25, 2011
Jul 25, 2011
487
488
489
490
491
492
493
494
495
496
497
498
499
if (m->owner == pthread_self())
{
if (--m->count == 0)
{
m->owner = (pthread_t) 0xDEADBEEF;
pthread_mutex_unlock(&m->mutex);
} /* if */
} /* if */
} /* __PHYSFS_platformReleaseMutex */
#endif /* !PHYSFS_NO_THREAD_SUPPORT */
#endif /* !PHYSFS_PLATFORM_BEOS */
Mar 11, 2007
Mar 11, 2007
500
#endif /* PHYSFS_PLATFORM_POSIX */
Jun 29, 2002
Jun 29, 2002
501
May 24, 2002
May 24, 2002
502
/* end of posix.c ... */