0fe2308acb9623e7da5ef16d65c21348f117b352
[supertux.git] / src / unison / physfs-1.1.1 / platform / os2.c
1 /*
2  * OS/2 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_OS2
13
14 #define INCL_DOSSEMAPHORES
15 #define INCL_DOSDATETIME
16 #define INCL_DOSFILEMGR
17 #define INCL_DOSMODULEMGR
18 #define INCL_DOSERRORS
19 #define INCL_DOSPROCESS
20 #define INCL_DOSDEVICES
21 #define INCL_DOSDEVIOCTL
22 #define INCL_DOSMISC
23 #include <os2.h>
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <time.h>
30 #include <ctype.h>
31
32 #include "physfs_internal.h"
33
34 const char *__PHYSFS_platformDirSeparator = "\\";
35
36
37 static const char *get_os2_error_string(APIRET rc)
38 {
39     switch (rc)
40     {
41         case NO_ERROR: return(NULL);  /* not an error. */
42         case ERROR_INTERRUPT: return(NULL);  /* not an error. */
43         case ERROR_TIMEOUT: return(NULL);  /* not an error. */
44         case ERROR_NOT_ENOUGH_MEMORY: return(ERR_OUT_OF_MEMORY);
45         case ERROR_FILE_NOT_FOUND: return(ERR_NO_SUCH_FILE);
46         case ERROR_PATH_NOT_FOUND: return(ERR_NO_SUCH_PATH);
47         case ERROR_ACCESS_DENIED: return(ERR_ACCESS_DENIED);
48         case ERROR_NOT_DOS_DISK: return(ERR_NOT_A_DOS_DISK);
49         case ERROR_SHARING_VIOLATION: return(ERR_SHARING_VIOLATION);
50         case ERROR_CANNOT_MAKE: return(ERR_CANNOT_MAKE);
51         case ERROR_DEVICE_IN_USE: return(ERR_DEV_IN_USE);
52         case ERROR_OPEN_FAILED: return(ERR_OPEN_FAILED);
53         case ERROR_DISK_FULL: return(ERR_DISK_FULL);
54         case ERROR_PIPE_BUSY: return(ERR_PIPE_BUSY);
55         case ERROR_SHARING_BUFFER_EXCEEDED: return(ERR_SHARING_BUF_EXCEEDED);
56         case ERROR_FILENAME_EXCED_RANGE: return(ERR_BAD_FILENAME);
57         case ERROR_META_EXPANSION_TOO_LONG: return(ERR_BAD_FILENAME);
58         case ERROR_TOO_MANY_HANDLES: return(ERR_TOO_MANY_HANDLES);
59         case ERROR_TOO_MANY_OPEN_FILES: return(ERR_TOO_MANY_HANDLES);
60         case ERROR_NO_MORE_SEARCH_HANDLES: return(ERR_TOO_MANY_HANDLES);
61         case ERROR_SEEK_ON_DEVICE: return(ERR_SEEK_ERROR);
62         case ERROR_NEGATIVE_SEEK: return(ERR_SEEK_OUT_OF_RANGE);
63         /*!!! FIXME: Where did this go?  case ERROR_DEL_CURRENT_DIRECTORY: return(ERR_DEL_CWD);*/
64         case ERROR_WRITE_PROTECT: return(ERR_WRITE_PROTECT_ERROR);
65         case ERROR_WRITE_FAULT: return(ERR_WRITE_FAULT);
66         case ERROR_LOCK_VIOLATION: return(ERR_LOCK_VIOLATION);
67         case ERROR_GEN_FAILURE: return(ERR_GEN_FAILURE);
68         case ERROR_UNCERTAIN_MEDIA: return(ERR_UNCERTAIN_MEDIA);
69         case ERROR_PROTECTION_VIOLATION: return(ERR_PROT_VIOLATION);
70         case ERROR_BROKEN_PIPE: return(ERR_BROKEN_PIPE);
71
72         case ERROR_INVALID_PARAMETER:
73         case ERROR_INVALID_NAME:
74         case ERROR_INVALID_DRIVE:
75         case ERROR_INVALID_HANDLE:
76         case ERROR_INVALID_FUNCTION:
77         case ERROR_INVALID_LEVEL:
78         case ERROR_INVALID_CATEGORY:
79         case ERROR_DUPLICATE_NAME:
80         case ERROR_BUFFER_OVERFLOW:
81         case ERROR_BAD_LENGTH:
82         case ERROR_BAD_DRIVER_LEVEL:
83         case ERROR_DIRECT_ACCESS_HANDLE:
84         case ERROR_NOT_OWNER:
85             return(ERR_PHYSFS_BAD_OS_CALL);
86
87         default: return(ERR_OS2_GENERIC);
88     } /* switch */
89
90     return(NULL);
91 } /* get_os2_error_string */
92
93
94 static APIRET os2err(APIRET retval)
95 {
96     char buf[128];
97     const char *err = get_os2_error_string(retval);
98     if (err == ERR_OS2_GENERIC)
99     {
100         snprintf(buf, sizeof (buf), ERR_OS2_GENERIC, (int) retval);
101         err = buf;
102     } /* if */
103
104     if (err != NULL)
105         __PHYSFS_setError(err);
106
107     return(retval);
108 } /* os2err */
109
110
111 /* (be gentle, this function isn't very robust.) */
112 static void cvt_path_to_correct_case(char *buf)
113 {
114     char *fname = buf + 3;            /* point to first element. */
115     char *ptr = strchr(fname, '\\');  /* find end of first element. */
116
117     buf[0] = toupper(buf[0]);  /* capitalize drive letter. */
118
119     /*
120      * Go through each path element, and enumerate its parent dir until
121      *  a case-insensitive match is found. If one is (and it SHOULD be)
122      *  then overwrite the original element with the correct case.
123      * If there's an error, or the path has vanished for some reason, it
124      *  won't hurt to have the original case, so we just keep going.
125      */
126     while (fname != NULL)
127     {
128         char spec[CCHMAXPATH];
129         FILEFINDBUF3 fb;
130         HDIR hdir = HDIR_CREATE;
131         ULONG count = 1;
132         APIRET rc;
133
134         *(fname - 1) = '\0';  /* isolate parent dir string. */
135
136         strcpy(spec, buf);      /* copy isolated parent dir... */
137         strcat(spec, "\\*.*");  /*  ...and add wildcard search spec. */
138
139         if (ptr != NULL)  /* isolate element to find (fname is the start). */
140             *ptr = '\0';
141
142         rc = DosFindFirst(spec, &hdir, FILE_DIRECTORY,
143                           &fb, sizeof (fb), &count, FIL_STANDARD);
144         if (rc == NO_ERROR)
145         {
146             while (count == 1)  /* while still entries to enumerate... */
147             {
148                 if (__PHYSFS_stricmpASCII(fb.achName, fname) == 0)
149                 {
150                     strcpy(fname, fb.achName);
151                     break;  /* there it is. Overwrite and stop searching. */
152                 } /* if */
153
154                 DosFindNext(hdir, &fb, sizeof (fb), &count);
155             } /* while */
156             DosFindClose(hdir);
157         } /* if */
158
159         *(fname - 1) = '\\';   /* unisolate parent dir. */
160         fname = ptr;           /* point to next element. */
161         if (ptr != NULL)
162         {
163             *ptr = '\\';       /* unisolate element. */
164             ptr = strchr(++fname, '\\');  /* find next element. */
165         } /* if */
166     } /* while */
167 } /* cvt_file_to_correct_case */
168
169
170 static char *baseDir = NULL;
171
172 int __PHYSFS_platformInit(void)
173 {
174     char buf[CCHMAXPATH];
175     APIRET rc;
176     PTIB ptib;
177     PPIB ppib;
178     PHYSFS_sint32 len;
179
180     assert(baseDir == NULL);
181     BAIL_IF_MACRO(os2err(DosGetInfoBlocks(&ptib, &ppib)) != NO_ERROR, NULL, 0);
182     rc = DosQueryModuleName(ppib->pib_hmte, sizeof (buf), (PCHAR) buf);
183     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 0);
184
185     /* chop off filename, leave path. */
186     for (len = strlen(buf) - 1; len >= 0; len--)
187     {
188         if (buf[len] == '\\')
189         {
190             buf[len] = '\0';
191             break;
192         } /* if */
193     } /* for */
194
195     assert(len > 0);  /* should have been a "x:\\" on the front on string. */
196
197     /* The string is capitalized! Figure out the REAL case... */
198     cvt_path_to_correct_case(buf);
199
200     baseDir = (char *) allocator.Malloc(len + 1);
201     BAIL_IF_MACRO(baseDir == NULL, ERR_OUT_OF_MEMORY, 0);
202     strcpy(baseDir, buf);
203     return(1);  /* success. */
204 } /* __PHYSFS_platformInit */
205
206
207 int __PHYSFS_platformDeinit(void)
208 {
209     assert(baseDir != NULL);
210     allocator.Free(baseDir);
211     baseDir = NULL;
212     return(1);  /* success. */
213 } /* __PHYSFS_platformDeinit */
214
215
216 static int disc_is_inserted(ULONG drive)
217 {
218     int rc;
219     char buf[20];
220     DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION);
221     rc = DosQueryFSInfo(drive + 1, FSIL_VOLSER, buf, sizeof (buf));
222     DosError(FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION);
223     return(rc == NO_ERROR);
224 } /* is_cdrom_inserted */
225
226
227 /* looks like "CD01" in ASCII (littleendian)...used for an ioctl. */
228 #define CD01 0x31304443
229
230 static int is_cdrom_drive(ULONG drive)
231 {
232     PHYSFS_uint32 param, data;
233     ULONG ul1, ul2;
234     APIRET rc;
235     HFILE hfile = NULLHANDLE;
236     char drivename[3] = { 'A' + drive, ':', '\0' };
237
238     rc = DosOpen(drivename, &hfile, &ul1, 0, 0,
239                  OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
240                  OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR |
241                  OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE, NULL);
242     BAIL_IF_MACRO(rc != NO_ERROR, NULL, 0);
243
244     data = 0;
245     param = PHYSFS_swapULE32(CD01);
246     ul1 = ul2 = sizeof (PHYSFS_uint32);
247     rc = DosDevIOCtl(hfile, IOCTL_CDROMDISK, CDROMDISK_GETDRIVER,
248                      &param, sizeof (param), &ul1, &data, sizeof (data), &ul2);
249
250     DosClose(hfile);
251     return((rc == NO_ERROR) && (PHYSFS_swapULE32(data) == CD01));
252 } /* is_cdrom_drive */
253
254
255 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
256 {
257     ULONG dummy = 0;
258     ULONG drivemap = 0;
259     ULONG i, bit;
260     APIRET rc = DosQueryCurrentDisk(&dummy, &drivemap);
261     if (os2err(rc) != NO_ERROR)
262         return;
263
264     for (i = 0, bit = 1; i < 26; i++, bit <<= 1)
265     {
266         if (drivemap & bit)  /* this logical drive exists. */
267         {
268             if ((is_cdrom_drive(i)) && (disc_is_inserted(i)))
269             {
270                 char drive[4] = "x:\\";
271                 drive[0] = ('A' + i);
272                 cb(data, drive);
273             } /* if */
274         } /* if */
275     } /* for */
276 } /* __PHYSFS_platformDetectAvailableCDs */
277
278
279 char *__PHYSFS_platformCalcBaseDir(const char *argv0)
280 {
281     char *retval = (char *) allocator.Malloc(strlen(baseDir) + 1);
282     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
283     strcpy(retval, baseDir); /* calculated at init time. */
284     return(retval);
285 } /* __PHYSFS_platformCalcBaseDir */
286
287
288 char *__PHYSFS_platformGetUserName(void)
289 {
290     return(NULL);  /* (*shrug*) */
291 } /* __PHYSFS_platformGetUserName */
292
293
294 char *__PHYSFS_platformGetUserDir(void)
295 {
296     return(__PHYSFS_platformCalcBaseDir(NULL));
297 } /* __PHYSFS_platformGetUserDir */
298
299
300 int __PHYSFS_platformExists(const char *fname)
301 {
302     FILESTATUS3 fs;
303     APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs));
304     return(os2err(rc) == NO_ERROR);
305 } /* __PHYSFS_platformExists */
306
307
308 int __PHYSFS_platformIsSymLink(const char *fname)
309 {
310     return(0);  /* no symlinks in OS/2. */
311 } /* __PHYSFS_platformIsSymlink */
312
313
314 int __PHYSFS_platformIsDirectory(const char *fname)
315 {
316     FILESTATUS3 fs;
317     APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs));
318     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 0)
319     return((fs.attrFile & FILE_DIRECTORY) != 0);
320 } /* __PHYSFS_platformIsDirectory */
321
322
323 /* !!! FIXME: can we lose the malloc here? */
324 char *__PHYSFS_platformCvtToDependent(const char *prepend,
325                                       const char *dirName,
326                                       const char *append)
327 {
328     int len = ((prepend) ? strlen(prepend) : 0) +
329               ((append) ? strlen(append) : 0) +
330               strlen(dirName) + 1;
331     char *retval = allocator.Malloc(len);
332     char *p;
333
334     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
335
336     if (prepend)
337         strcpy(retval, prepend);
338     else
339         retval[0] = '\0';
340
341     strcat(retval, dirName);
342
343     if (append)
344         strcat(retval, append);
345
346     for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/'))
347         *p = '\\';
348
349     return(retval);
350 } /* __PHYSFS_platformCvtToDependent */
351
352
353 void __PHYSFS_platformEnumerateFiles(const char *dirname,
354                                      int omitSymLinks,
355                                      PHYSFS_EnumFilesCallback callback,
356                                      const char *origdir,
357                                      void *callbackdata)
358 {
359     char spec[CCHMAXPATH];
360     FILEFINDBUF3 fb;
361     HDIR hdir = HDIR_CREATE;
362     ULONG count = 1;
363     APIRET rc;
364
365     if (strlen(dirname) > sizeof (spec) - 5)
366     {
367         __PHYSFS_setError(ERR_BAD_FILENAME);
368         return;
369     } /* if */
370
371     strcpy(spec, dirname);
372     strcat(spec, (spec[strlen(spec) - 1] != '\\') ? "\\*.*" : "*.*");
373
374     rc = DosFindFirst(spec, &hdir,
375                       FILE_DIRECTORY | FILE_ARCHIVED |
376                       FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM,
377                       &fb, sizeof (fb), &count, FIL_STANDARD);
378
379     if (os2err(rc) != NO_ERROR)
380         return;
381
382     while (count == 1)
383     {
384         if ((strcmp(fb.achName, ".") != 0) && (strcmp(fb.achName, "..") != 0))
385             callback(callbackdata, origdir, fb.achName);
386
387         DosFindNext(hdir, &fb, sizeof (fb), &count);
388     } /* while */
389
390     DosFindClose(hdir);
391 } /* __PHYSFS_platformEnumerateFiles */
392
393
394 char *__PHYSFS_platformCurrentDir(void)
395 {
396     char *retval;
397     ULONG currentDisk;
398     ULONG dummy;
399     ULONG pathSize = 0;
400     APIRET rc;
401     BYTE byte;
402
403     rc = DosQueryCurrentDisk(&currentDisk, &dummy);
404     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, NULL);
405
406     /* The first call just tells us how much space we need for the string. */
407     rc = DosQueryCurrentDir(currentDisk, &byte, &pathSize);
408     pathSize++; /* Add space for null terminator. */
409     retval = (char *) allocator.Malloc(pathSize + 3);  /* plus "x:\\" */
410     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
411
412     /* Actually get the string this time. */
413     rc = DosQueryCurrentDir(currentDisk, (PBYTE) (retval + 3), &pathSize);
414     if (os2err(rc) != NO_ERROR)
415     {
416         allocator.Free(retval);
417         return(NULL);
418     } /* if */
419
420     retval[0] = ('A' + (currentDisk - 1));
421     retval[1] = ':';
422     retval[2] = '\\';
423     return(retval);
424 } /* __PHYSFS_platformCurrentDir */
425
426
427 char *__PHYSFS_platformRealPath(const char *path)
428 {
429     char buf[CCHMAXPATH];
430     char *retval;
431     APIRET rc = DosQueryPathInfo(path, FIL_QUERYFULLNAME, buf, sizeof (buf));
432     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, NULL);
433     retval = (char *) allocator.Malloc(strlen(buf) + 1);
434     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
435     strcpy(retval, buf);
436     return(retval);
437 } /* __PHYSFS_platformRealPath */
438
439
440 int __PHYSFS_platformMkDir(const char *path)
441 {
442     return(os2err(DosCreateDir(path, NULL)) == NO_ERROR);
443 } /* __PHYSFS_platformMkDir */
444
445
446 void *__PHYSFS_platformOpenRead(const char *filename)
447 {
448     ULONG actionTaken = 0;
449     HFILE hfile = NULLHANDLE;
450
451     /*
452      * File must be opened SHARE_DENYWRITE and ACCESS_READONLY, otherwise
453      *  DosQueryFileInfo() will fail if we try to get a file length, etc.
454      */
455     os2err(DosOpen(filename, &hfile, &actionTaken, 0, FILE_NORMAL,
456                    OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
457                    OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
458                    OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
459                    OPEN_ACCESS_READONLY, NULL));
460
461     return((void *) hfile);
462 } /* __PHYSFS_platformOpenRead */
463
464
465 void *__PHYSFS_platformOpenWrite(const char *filename)
466 {
467     ULONG actionTaken = 0;
468     HFILE hfile = NULLHANDLE;
469
470     /*
471      * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise
472      *  DosQueryFileInfo() will fail if we try to get a file length, etc.
473      */
474     os2err(DosOpen(filename, &hfile, &actionTaken, 0, FILE_NORMAL,
475                    OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
476                    OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
477                    OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
478                    OPEN_ACCESS_READWRITE, NULL));
479
480     return((void *) hfile);
481 } /* __PHYSFS_platformOpenWrite */
482
483
484 void *__PHYSFS_platformOpenAppend(const char *filename)
485 {
486     ULONG dummy = 0;
487     HFILE hfile = NULLHANDLE;
488     APIRET rc;
489
490     /*
491      * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise
492      *  DosQueryFileInfo() will fail if we try to get a file length, etc.
493      */
494     rc = os2err(DosOpen(filename, &hfile, &dummy, 0, FILE_NORMAL,
495                    OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
496                    OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
497                    OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
498                    OPEN_ACCESS_READWRITE, NULL));
499
500     if (rc == NO_ERROR)
501     {
502         if (os2err(DosSetFilePtr(hfile, 0, FILE_END, &dummy)) != NO_ERROR)
503         {
504             DosClose(hfile);
505             hfile = NULLHANDLE;
506         } /* if */
507     } /* if */
508
509     return((void *) hfile);
510 } /* __PHYSFS_platformOpenAppend */
511
512
513 PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
514                                     PHYSFS_uint32 size, PHYSFS_uint32 count)
515 {
516     HFILE hfile = (HFILE) opaque;
517     PHYSFS_sint64 retval;
518     ULONG br;
519
520     for (retval = 0; retval < count; retval++)
521     {
522         os2err(DosRead(hfile, buffer, size, &br));
523         if (br < size)
524         {
525             DosSetFilePtr(hfile, -br, FILE_CURRENT, &br); /* try to cleanup. */
526             return(retval);
527         } /* if */
528
529         buffer = (void *) ( ((char *) buffer) + size );
530     } /* for */
531
532     return(retval);
533 } /* __PHYSFS_platformRead */
534
535
536 PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
537                                      PHYSFS_uint32 size, PHYSFS_uint32 count)
538 {
539     HFILE hfile = (HFILE) opaque;
540     PHYSFS_sint64 retval;
541     ULONG bw;
542
543     for (retval = 0; retval < count; retval++)
544     {
545         os2err(DosWrite(hfile, buffer, size, &bw));
546         if (bw < size)
547         {
548             DosSetFilePtr(hfile, -bw, FILE_CURRENT, &bw); /* try to cleanup. */
549             return(retval);
550         } /* if */
551
552         buffer = (void *) ( ((char *) buffer) + size );
553     } /* for */
554
555     return(retval);
556 } /* __PHYSFS_platformWrite */
557
558
559 int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
560 {
561     ULONG dummy;
562     HFILE hfile = (HFILE) opaque;
563     LONG dist = (LONG) pos;
564
565     /* hooray for 32-bit filesystem limits!  :) */
566     BAIL_IF_MACRO((PHYSFS_uint64) dist != pos, ERR_SEEK_OUT_OF_RANGE, 0);
567
568     return(os2err(DosSetFilePtr(hfile, dist, FILE_BEGIN, &dummy)) == NO_ERROR);
569 } /* __PHYSFS_platformSeek */
570
571
572 PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
573 {
574     ULONG pos;
575     HFILE hfile = (HFILE) opaque;
576     APIRET rc = os2err(DosSetFilePtr(hfile, 0, FILE_CURRENT, &pos));
577     BAIL_IF_MACRO(rc != NO_ERROR, NULL, -1);
578     return((PHYSFS_sint64) pos);
579 } /* __PHYSFS_platformTell */
580
581
582 PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
583 {
584     FILESTATUS3 fs;
585     HFILE hfile = (HFILE) opaque;
586     APIRET rc = DosQueryFileInfo(hfile, FIL_STANDARD, &fs, sizeof (fs));
587     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, -1);
588     return((PHYSFS_sint64) fs.cbFile);
589 } /* __PHYSFS_platformFileLength */
590
591
592 int __PHYSFS_platformEOF(void *opaque)
593 {
594     PHYSFS_sint64 len, pos;
595
596     len = __PHYSFS_platformFileLength(opaque);
597     BAIL_IF_MACRO(len == -1, NULL, 1);  /* (*shrug*) */
598     pos = __PHYSFS_platformTell(opaque);
599     BAIL_IF_MACRO(pos == -1, NULL, 1);  /* (*shrug*) */
600
601     return(pos >= len);
602 } /* __PHYSFS_platformEOF */
603
604
605 int __PHYSFS_platformFlush(void *opaque)
606 {
607     return(os2err(DosResetBuffer((HFILE) opaque) == NO_ERROR));
608 } /* __PHYSFS_platformFlush */
609
610
611 int __PHYSFS_platformClose(void *opaque)
612 {
613     return(os2err(DosClose((HFILE) opaque) == NO_ERROR));
614 } /* __PHYSFS_platformClose */
615
616
617 int __PHYSFS_platformDelete(const char *path)
618 {
619     if (__PHYSFS_platformIsDirectory(path))
620         return(os2err(DosDeleteDir(path)) == NO_ERROR);
621
622     return(os2err(DosDelete(path) == NO_ERROR));
623 } /* __PHYSFS_platformDelete */
624
625
626 PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname)
627 {
628     PHYSFS_sint64 retval;
629     struct tm tm;
630     FILESTATUS3 fs;
631     APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs));
632     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, -1);
633
634     /* Convert to a format that mktime() can grok... */
635     tm.tm_sec = ((PHYSFS_uint32) fs.ftimeLastWrite.twosecs) * 2;
636     tm.tm_min = fs.ftimeLastWrite.minutes;
637     tm.tm_hour = fs.ftimeLastWrite.hours;
638     tm.tm_mday = fs.fdateLastWrite.day;
639     tm.tm_mon = fs.fdateLastWrite.month;
640     tm.tm_year = ((PHYSFS_uint32) fs.fdateLastWrite.year) + 80;
641     tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
642     tm.tm_yday = -1;
643     tm.tm_isdst = -1;
644
645     /* Convert to a format PhysicsFS can grok... */
646     retval = (PHYSFS_sint64) mktime(&tm);
647     BAIL_IF_MACRO(retval == -1, strerror(errno), -1);
648     return(retval);
649 } /* __PHYSFS_platformGetLastModTime */
650
651
652 PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
653 {
654     PTIB ptib;
655     PPIB ppib;
656
657     /*
658      * Allegedly, this API never fails, but we'll punt and return a
659      *  default value (zero might as well do) if it does.
660      */
661     BAIL_IF_MACRO(os2err(DosGetInfoBlocks(&ptib, &ppib)) != NO_ERROR, 0, 0);
662     return((PHYSFS_uint64) ptib->tib_ordinal);
663 } /* __PHYSFS_platformGetThreadID */
664
665
666 void *__PHYSFS_platformCreateMutex(void)
667 {
668     HMTX hmtx = NULLHANDLE;
669     os2err(DosCreateMutexSem(NULL, &hmtx, 0, 0));
670     return((void *) hmtx);
671 } /* __PHYSFS_platformCreateMutex */
672
673
674 void __PHYSFS_platformDestroyMutex(void *mutex)
675 {
676     DosCloseMutexSem((HMTX) mutex);
677 } /* __PHYSFS_platformDestroyMutex */
678
679
680 int __PHYSFS_platformGrabMutex(void *mutex)
681 {
682     /* Do _NOT_ call os2err() (which sets the physfs error msg) in here! */
683     return(DosRequestMutexSem((HMTX) mutex, SEM_INDEFINITE_WAIT) == NO_ERROR);
684 } /* __PHYSFS_platformGrabMutex */
685
686
687 void __PHYSFS_platformReleaseMutex(void *mutex)
688 {
689     DosReleaseMutexSem((HMTX) mutex);
690 } /* __PHYSFS_platformReleaseMutex */
691
692
693 /* !!! FIXME: Don't use C runtime for allocators? */
694 int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
695 {
696     return(0);  /* just use malloc() and friends. */
697 } /* __PHYSFS_platformSetDefaultAllocator */
698
699 #endif  /* PHYSFS_PLATFORM_OS2 */
700
701 /* end of os2.c ... */
702