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_DOSMODULEMGR
15 #define INCL_DOSSEMAPHORES
16 #define INCL_DOSDATETIME
17 #define INCL_DOSFILEMGR
18 #define INCL_DOSMODULEMGR
19 #define INCL_DOSERRORS
20 #define INCL_DOSPROCESS
21 #define INCL_DOSDEVICES
22 #define INCL_DOSDEVIOCTL
23 #define INCL_DOSMISC
24 #include <os2.h>
25 #include <uconv.h>
26 
27 #include <errno.h>
28 #include <time.h>
29 #include <ctype.h>
30 
31 #include "physfs_internal.h"
32 
33 static HMODULE uconvdll = 0;
34 static UconvObject uconv = 0;
35 static int (_System *pUniCreateUconvObject)(UniChar *, UconvObject *) = NULL;
36 static int (_System *pUniFreeUconvObject)(UconvObject *) = NULL;
37 static int (_System *pUniUconvToUcs)(UconvObject,void **,size_t *, UniChar**, size_t *, size_t *) = NULL;
38 static int (_System *pUniUconvFromUcs)(UconvObject,UniChar **,size_t *,void **,size_t *,size_t *) = NULL;
39 
errcodeFromAPIRET(const APIRET rc)40 static PHYSFS_ErrorCode errcodeFromAPIRET(const APIRET rc)
41 {
42     switch (rc)
43     {
44         case NO_ERROR: return PHYSFS_ERR_OK;  /* not an error. */
45         case ERROR_INTERRUPT: return PHYSFS_ERR_OK;  /* not an error. */
46         case ERROR_TIMEOUT: return PHYSFS_ERR_OK;  /* not an error. */
47         case ERROR_NOT_ENOUGH_MEMORY: return PHYSFS_ERR_OUT_OF_MEMORY;
48         case ERROR_FILE_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
49         case ERROR_PATH_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND;
50         case ERROR_ACCESS_DENIED: return PHYSFS_ERR_PERMISSION;
51         case ERROR_NOT_DOS_DISK: return PHYSFS_ERR_NOT_FOUND;
52         case ERROR_SHARING_VIOLATION: return PHYSFS_ERR_PERMISSION;
53         case ERROR_CANNOT_MAKE: return PHYSFS_ERR_IO;  /* maybe this is wrong? */
54         case ERROR_DEVICE_IN_USE: return PHYSFS_ERR_BUSY;
55         case ERROR_OPEN_FAILED: return PHYSFS_ERR_IO;  /* maybe this is wrong? */
56         case ERROR_DISK_FULL: return PHYSFS_ERR_NO_SPACE;
57         case ERROR_PIPE_BUSY: return PHYSFS_ERR_BUSY;
58         case ERROR_SHARING_BUFFER_EXCEEDED: return PHYSFS_ERR_IO;
59         case ERROR_FILENAME_EXCED_RANGE: return PHYSFS_ERR_BAD_FILENAME;
60         case ERROR_META_EXPANSION_TOO_LONG: return PHYSFS_ERR_BAD_FILENAME;
61         case ERROR_TOO_MANY_HANDLES: return PHYSFS_ERR_IO;
62         case ERROR_TOO_MANY_OPEN_FILES: return PHYSFS_ERR_IO;
63         case ERROR_NO_MORE_SEARCH_HANDLES: return PHYSFS_ERR_IO;
64         case ERROR_SEEK_ON_DEVICE: return PHYSFS_ERR_IO;
65         case ERROR_NEGATIVE_SEEK: return PHYSFS_ERR_INVALID_ARGUMENT;
66         case ERROR_WRITE_PROTECT: return PHYSFS_ERR_PERMISSION;
67         case ERROR_WRITE_FAULT: return PHYSFS_ERR_IO;
68         case ERROR_UNCERTAIN_MEDIA: return PHYSFS_ERR_IO;
69         case ERROR_PROTECTION_VIOLATION: return PHYSFS_ERR_IO;
70         case ERROR_BROKEN_PIPE: return PHYSFS_ERR_IO;
71 
72         /* !!! FIXME: some of these might be PHYSFS_ERR_BAD_FILENAME, etc */
73         case ERROR_LOCK_VIOLATION:
74         case ERROR_GEN_FAILURE:
75         case ERROR_INVALID_PARAMETER:
76         case ERROR_INVALID_NAME:
77         case ERROR_INVALID_DRIVE:
78         case ERROR_INVALID_HANDLE:
79         case ERROR_INVALID_FUNCTION:
80         case ERROR_INVALID_LEVEL:
81         case ERROR_INVALID_CATEGORY:
82         case ERROR_DUPLICATE_NAME:
83         case ERROR_BUFFER_OVERFLOW:
84         case ERROR_BAD_LENGTH:
85         case ERROR_BAD_DRIVER_LEVEL:
86         case ERROR_DIRECT_ACCESS_HANDLE:
87         case ERROR_NOT_OWNER:
88             return PHYSFS_ERR_OS_ERROR;
89 
90         default: break;
91     } /* switch */
92 
93     return PHYSFS_ERR_OTHER_ERROR;
94 } /* errcodeFromAPIRET */
95 
cvtUtf8ToCodepage(const char * utf8str)96 static char *cvtUtf8ToCodepage(const char *utf8str)
97 {
98     const size_t len = strlen(utf8str) + 1;
99     const size_t uc2buflen = len * sizeof (UniChar);
100     UniChar *uc2ptr = (UniChar *) __PHYSFS_smallAlloc(uc2buflen);
101     UniChar *uc2str = uc2ptr;
102     char *cpptr = NULL;
103     char *cpstr = NULL;
104     size_t subs = 0;
105     size_t unilen;
106 
107     BAIL_IF(!uc2str, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
108     PHYSFS_utf8ToUcs2(utf8str, (PHYSFS_uint16 *) uc2str, uc2buflen);
109     for (unilen = 0; uc2str[unilen]; unilen++) { /* spin */ }
110     unilen++;  /* null terminator. */
111 
112     if (!uconvdll)
113     {
114         /* There's really not much we can do on older OS/2s except pray this
115            is latin1-compatible. */
116         size_t i;
117         cpptr = (char *) allocator.Malloc(unilen);
118         cpstr = cpptr;
119         GOTO_IF(!cpptr, PHYSFS_ERR_OUT_OF_MEMORY, failed);
120         for (i = 0; i < unilen; i++)
121         {
122             const UniChar ch = uc2str[i];
123             GOTO_IF(ch > 0xFF, PHYSFS_ERR_BAD_FILENAME, failed);
124             cpptr[i] = (char) ((unsigned char) ch);
125         } /* for */
126 
127         __PHYSFS_smallFree(uc2ptr);
128         return cpstr;
129     } /* if */
130     else
131     {
132         int rc;
133         size_t cplen = unilen * 4; /* overallocate, just in case. */
134         cpptr = (char *) allocator.Malloc(cplen);
135         GOTO_IF(!cpptr, PHYSFS_ERR_OUT_OF_MEMORY, failed);
136         cpstr = cpptr;
137 
138         rc = pUniUconvFromUcs(uconv, &uc2str, &unilen, (void **) &cpstr, &cplen, &subs);
139         GOTO_IF(rc != ULS_SUCCESS, PHYSFS_ERR_BAD_FILENAME, failed);
140         GOTO_IF(subs > 0, PHYSFS_ERR_BAD_FILENAME, failed);
141         assert(unilen == 0);
142 
143         __PHYSFS_smallFree(uc2ptr);
144         return cpptr;
145     } /* else */
146 
147 failed:
148     __PHYSFS_smallFree(uc2ptr);
149     allocator.Free(cpptr);
150 
151     return NULL;
152 } /* cvtUtf8ToCodepage */
153 
cvtCodepageToUtf8(const char * cpstr)154 static char *cvtCodepageToUtf8(const char *cpstr)
155 {
156     const size_t len = strlen(cpstr) + 1;
157     char *retvalbuf = (char *) allocator.Malloc(len * 4);
158     char *retval = NULL;
159 
160     BAIL_IF(!retvalbuf, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
161 
162     if (!uconvdll)
163     {
164         /* There's really not much we can do on older OS/2s except pray this
165            is latin1-compatible. */
166         retval = retvalbuf;
167         PHYSFS_utf8FromLatin1(cpstr, retval, len * 4);
168     } /* if */
169     else
170     {
171         int rc;
172         size_t cplen = len;
173         size_t unilen = len;
174         size_t subs = 0;
175         UniChar *uc2ptr = __PHYSFS_smallAlloc(len * sizeof (UniChar));
176         UniChar *uc2str = uc2ptr;
177 
178         BAIL_IF(!uc2ptr, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
179         rc = pUniUconvToUcs(uconv, (void **) &cpstr, &cplen, &uc2str, &unilen, &subs);
180         GOTO_IF(rc != ULS_SUCCESS, PHYSFS_ERR_BAD_FILENAME, done);
181         GOTO_IF(subs > 0, PHYSFS_ERR_BAD_FILENAME, done);
182         assert(cplen == 0);
183         retval = retvalbuf;
184         PHYSFS_utf8FromUcs2((const PHYSFS_uint16 *) uc2ptr, retval, len * 4);
185         done:
186         __PHYSFS_smallFree(uc2ptr);
187     } /* else */
188 
189     return retval;
190 } /* cvtCodepageToUtf8 */
191 
192 
193 /* (be gentle, this function isn't very robust.) */
cvtPathToCorrectCase(char * buf)194 static char *cvtPathToCorrectCase(char *buf)
195 {
196     char *retval = buf;
197     char *fname = buf + 3;            /* point to first element. */
198     char *ptr = strchr(fname, '\\');  /* find end of first element. */
199 
200     buf[0] = toupper(buf[0]);  /* capitalize drive letter. */
201 
202     /*
203      * Go through each path element, and enumerate its parent dir until
204      *  a case-insensitive match is found. If one is (and it SHOULD be)
205      *  then overwrite the original element with the correct case.
206      * If there's an error, or the path has vanished for some reason, it
207      *  won't hurt to have the original case, so we just keep going.
208      */
209     while ((fname != NULL) && (*fname != '\0'))
210     {
211         char spec[CCHMAXPATH];
212         FILEFINDBUF3 fb;
213         HDIR hdir = HDIR_CREATE;
214         ULONG count = 1;
215         APIRET rc;
216 
217         *(fname - 1) = '\0';  /* isolate parent dir string. */
218 
219         strcpy(spec, buf);      /* copy isolated parent dir... */
220         strcat(spec, "\\*.*");  /*  ...and add wildcard search spec. */
221 
222         if (ptr != NULL)  /* isolate element to find (fname is the start). */
223             *ptr = '\0';
224 
225         rc = DosFindFirst((unsigned char *) spec, &hdir, FILE_DIRECTORY,
226                           &fb, sizeof (fb), &count, FIL_STANDARD);
227         if (rc == NO_ERROR)
228         {
229             while (count == 1)  /* while still entries to enumerate... */
230             {
231                 int cmp;
232                 char *utf8 = cvtCodepageToUtf8(fb.achName);
233                 if (!utf8) /* ugh, maybe we'll get lucky with the C runtime. */
234                     cmp = stricmp(fb.achName, fname);
235                 else
236                 {
237                     cmp = PHYSFS_utf8stricmp(utf8, fname);
238                     allocator.Free(utf8);
239                 } /* else */
240 
241                 if (cmp == 0)
242                 {
243                     strcpy(fname, fb.achName);
244                     break;  /* there it is. Overwrite and stop searching. */
245                 } /* if */
246 
247                 DosFindNext(hdir, &fb, sizeof (fb), &count);
248             } /* while */
249             DosFindClose(hdir);
250         } /* if */
251 
252         *(fname - 1) = '\\';   /* unisolate parent dir. */
253         fname = ptr;           /* point to next element. */
254         if (ptr != NULL)
255         {
256             *ptr = '\\';       /* unisolate element. */
257             ptr = strchr(++fname, '\\');  /* find next element. */
258         } /* if */
259     } /* while */
260 
261     return retval;
262 } /* cvtPathToCorrectCase */
263 
prepUnicodeSupport(void)264 static void prepUnicodeSupport(void)
265 {
266     /* really old OS/2 might not have Unicode support _at all_, so load
267        the system library and do without if it doesn't exist. */
268     int ok = 0;
269     char buf[CCHMAXPATH];
270     UniChar defstr[] = { 0 };
271     if (DosLoadModule(buf, sizeof (buf) - 1, "uconv", &uconvdll) == NO_ERROR)
272     {
273         #define LOAD(x) (DosQueryProcAddr(uconvdll,0,#x,(PFN*)&p##x)==NO_ERROR)
274         ok = LOAD(UniCreateUconvObject) &&
275              LOAD(UniFreeUconvObject) &&
276              LOAD(UniUconvToUcs) &&
277              LOAD(UniUconvFromUcs);
278         #undef LOAD
279     } /* else */
280 
281     if (!ok || (pUniCreateUconvObject(defstr, &uconv) != ULS_SUCCESS))
282     {
283         /* oh well, live without it. */
284         if (uconvdll)
285         {
286             if (uconv)
287                 pUniFreeUconvObject(uconv);
288             DosFreeModule(uconvdll);
289             uconvdll = 0;
290         } /* if */
291     } /* if */
292 } /* prepUnicodeSupport */
293 
294 
__PHYSFS_platformInit(void)295 int __PHYSFS_platformInit(void)
296 {
297     prepUnicodeSupport();
298     return 1;  /* ready to go! */
299 } /* __PHYSFS_platformInit */
300 
301 
__PHYSFS_platformDeinit(void)302 void __PHYSFS_platformDeinit(void)
303 {
304     if (uconvdll)
305     {
306         pUniFreeUconvObject(uconv);
307         uconv = 0;
308         DosFreeModule(uconvdll);
309         uconvdll = 0;
310     } /* if */
311 } /* __PHYSFS_platformDeinit */
312 
313 
discIsInserted(ULONG drive)314 static int discIsInserted(ULONG drive)
315 {
316     int rc;
317     char buf[20];
318     DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION);
319     rc = DosQueryFSInfo(drive + 1, FSIL_VOLSER, buf, sizeof (buf));
320     DosError(FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION);
321     return (rc == NO_ERROR);
322 } /* is_cdrom_inserted */
323 
324 
325 /* looks like "CD01" in ASCII (littleendian)...used for an ioctl. */
326 #define CD01 0x31304443
327 
isCdRomDrive(ULONG drive)328 static int isCdRomDrive(ULONG drive)
329 {
330     PHYSFS_uint32 param, data;
331     ULONG ul1, ul2;
332     APIRET rc;
333     HFILE hfile = NULLHANDLE;
334     unsigned char drivename[3] = { 0, ':', '\0' };
335 
336     drivename[0] = 'A' + drive;
337 
338     rc = DosOpen(drivename, &hfile, &ul1, 0, 0,
339                  OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
340                  OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR |
341                  OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE, NULL);
342     if (rc != NO_ERROR)
343         return 0;
344 
345     data = 0;
346     param = PHYSFS_swapULE32(CD01);
347     ul1 = ul2 = sizeof (PHYSFS_uint32);
348     rc = DosDevIOCtl(hfile, IOCTL_CDROMDISK, CDROMDISK_GETDRIVER,
349                      &param, sizeof (param), &ul1, &data, sizeof (data), &ul2);
350 
351     DosClose(hfile);
352     return ((rc == NO_ERROR) && (PHYSFS_swapULE32(data) == CD01));
353 } /* isCdRomDrive */
354 
355 
__PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb,void * data)356 void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data)
357 {
358     ULONG dummy = 0;
359     ULONG drivemap = 0;
360     ULONG i, bit;
361     const APIRET rc = DosQueryCurrentDisk(&dummy, &drivemap);
362     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc),);
363 
364     for (i = 0, bit = 1; i < 26; i++, bit <<= 1)
365     {
366         if (drivemap & bit)  /* this logical drive exists. */
367         {
368             if ((isCdRomDrive(i)) && (discIsInserted(i)))
369             {
370                 char drive[4] = "x:\\";
371                 drive[0] = ('A' + i);
372                 cb(data, drive);
373             } /* if */
374         } /* if */
375     } /* for */
376 } /* __PHYSFS_platformDetectAvailableCDs */
377 
378 
__PHYSFS_platformCalcBaseDir(const char * argv0)379 char *__PHYSFS_platformCalcBaseDir(const char *argv0)
380 {
381     char *retval = NULL;
382     char buf[CCHMAXPATH];
383     APIRET rc;
384     PTIB ptib;
385     PPIB ppib;
386     PHYSFS_sint32 len;
387 
388     rc = DosGetInfoBlocks(&ptib, &ppib);
389     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
390     rc = DosQueryModuleName(ppib->pib_hmte, sizeof (buf), (PCHAR) buf);
391     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
392     retval = cvtCodepageToUtf8(buf);
393     BAIL_IF_ERRPASS(!retval, NULL);
394 
395     /* chop off filename, leave path. */
396     for (len = strlen(retval) - 1; len >= 0; len--)
397     {
398         if (retval[len] == '\\')
399         {
400             retval[len + 1] = '\0';
401             break;
402         } /* if */
403     } /* for */
404 
405     assert(len > 0);  /* should have been a "x:\\" on the front on string. */
406 
407     /* The string is capitalized! Figure out the REAL case... */
408     return cvtPathToCorrectCase(retval);
409 } /* __PHYSFS_platformCalcBaseDir */
410 
__PHYSFS_platformCalcUserDir(void)411 char *__PHYSFS_platformCalcUserDir(void)
412 {
413     return __PHYSFS_platformCalcBaseDir(NULL);  /* !!! FIXME: ? */
414 } /* __PHYSFS_platformCalcUserDir */
415 
__PHYSFS_platformCalcPrefDir(const char * org,const char * app)416 char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
417 {
418     return __PHYSFS_platformCalcBaseDir(NULL);  /* !!! FIXME: ? */
419 } /* __PHYSFS_platformCalcPrefDir */
420 
__PHYSFS_platformEnumerate(const char * dirname,PHYSFS_EnumerateCallback callback,const char * origdir,void * callbackdata)421 PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname,
422                                PHYSFS_EnumerateCallback callback,
423                                const char *origdir, void *callbackdata)
424 {
425     PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
426     size_t utf8len = strlen(dirname);
427     char *utf8 = (char *) __PHYSFS_smallAlloc(utf8len + 5);
428     char *cpspec = NULL;
429     FILEFINDBUF3 fb;
430     HDIR hdir = HDIR_CREATE;
431     ULONG count = 1;
432     APIRET rc;
433 
434     BAIL_IF(!utf8, PHYSFS_ERR_OUT_OF_MEMORY, PHYSFS_ENUM_ERROR);
435 
436     strcpy(utf8, dirname);
437     if (utf8[utf8len - 1] != '\\')
438         strcpy(utf8 + utf8len, "\\*.*");
439     else
440         strcpy(utf8 + utf8len, "*.*");
441 
442     cpspec = cvtUtf8ToCodepage(utf8);
443     __PHYSFS_smallFree(utf8);
444     BAIL_IF_ERRPASS(!cpspec, PHYSFS_ENUM_ERROR);
445 
446     rc = DosFindFirst((unsigned char *) cpspec, &hdir,
447                       FILE_DIRECTORY | FILE_ARCHIVED |
448                       FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM,
449                       &fb, sizeof (fb), &count, FIL_STANDARD);
450     allocator.Free(cpspec);
451 
452     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), PHYSFS_ENUM_ERROR);
453 
454     while (count == 1)
455     {
456         if ((strcmp(fb.achName, ".") != 0) && (strcmp(fb.achName, "..") != 0))
457         {
458             utf8 = cvtCodepageToUtf8(fb.achName);
459             if (!utf8)
460                 retval = PHYSFS_ENUM_ERROR;
461             else
462             {
463                 retval = callback(callbackdata, origdir, utf8);
464                 allocator.Free(utf8);
465                 if (retval == PHYSFS_ENUM_ERROR)
466                     PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
467             } /* else */
468         } /* if */
469 
470         if (retval != PHYSFS_ENUM_OK)
471             break;
472 
473         DosFindNext(hdir, &fb, sizeof (fb), &count);
474     } /* while */
475 
476     DosFindClose(hdir);
477 
478     return retval;
479 } /* __PHYSFS_platformEnumerate */
480 
481 
__PHYSFS_platformCurrentDir(void)482 char *__PHYSFS_platformCurrentDir(void)
483 {
484     char *retval;
485     char *cpstr;
486     char *utf8;
487     ULONG currentDisk;
488     ULONG dummy;
489     ULONG pathSize = 0;
490     APIRET rc;
491     BYTE byte;
492 
493     rc = DosQueryCurrentDisk(&currentDisk, &dummy);
494     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), NULL);
495 
496     /* The first call just tells us how much space we need for the string. */
497     rc = DosQueryCurrentDir(currentDisk, &byte, &pathSize);
498     pathSize++; /* Add space for null terminator. */
499     cpstr = (char *) __PHYSFS_smallAlloc(pathSize);
500     BAIL_IF(cpstr == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
501 
502     /* Actually get the string this time. */
503     rc = DosQueryCurrentDir(currentDisk, (PBYTE) cpstr, &pathSize);
504     if (rc != NO_ERROR)
505     {
506         __PHYSFS_smallFree(cpstr);
507         BAIL(errcodeFromAPIRET(rc), NULL);
508     } /* if */
509 
510     utf8 = cvtCodepageToUtf8(cpstr);
511     __PHYSFS_smallFree(cpstr);
512     BAIL_IF_ERRPASS(utf8 == NULL, NULL);
513 
514     /* +4 for "x:\\" drive selector and null terminator. */
515     retval = (char *) allocator.Malloc(strlen(utf8) + 4);
516     if (retval == NULL)
517     {
518         allocator.Free(utf8);
519         BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL);
520     } /* if */
521 
522     retval[0] = ('A' + (currentDisk - 1));
523     retval[1] = ':';
524     retval[2] = '\\';
525     strcpy(retval + 3, utf8);
526 
527     allocator.Free(utf8);
528 
529     return retval;
530 } /* __PHYSFS_platformCurrentDir */
531 
532 
__PHYSFS_platformMkDir(const char * filename)533 int __PHYSFS_platformMkDir(const char *filename)
534 {
535     APIRET rc;
536     char *cpstr = cvtUtf8ToCodepage(filename);
537     BAIL_IF_ERRPASS(!cpstr, 0);
538     rc = DosCreateDir((unsigned char *) cpstr, NULL);
539     allocator.Free(cpstr);
540     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
541     return 1;
542 } /* __PHYSFS_platformMkDir */
543 
544 
openFile(const char * filename,const ULONG flags,const ULONG mode)545 static HFILE openFile(const char *filename, const ULONG flags, const ULONG mode)
546 {
547     char *cpfname = cvtUtf8ToCodepage(filename);
548     ULONG action = 0;
549     HFILE hfile = NULLHANDLE;
550     APIRET rc;
551 
552     BAIL_IF_ERRPASS(!cpfname, 0);
553 
554     rc = DosOpen(cpfname, &hfile, &action, 0, FILE_NORMAL, flags, mode, NULL);
555     allocator.Free(cpfname);
556     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
557 
558     return hfile;
559 } /* openFile */
560 
__PHYSFS_platformOpenRead(const char * filename)561 void *__PHYSFS_platformOpenRead(const char *filename)
562 {
563     /*
564      * File must be opened SHARE_DENYWRITE and ACCESS_READONLY, otherwise
565      *  DosQueryFileInfo() will fail if we try to get a file length, etc.
566      */
567     return (void *) openFile(filename,
568                         OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
569                         OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
570                         OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
571                         OPEN_ACCESS_READONLY);
572 } /* __PHYSFS_platformOpenRead */
573 
574 
__PHYSFS_platformOpenWrite(const char * filename)575 void *__PHYSFS_platformOpenWrite(const char *filename)
576 {
577     return (void *) openFile(filename,
578                         OPEN_ACTION_REPLACE_IF_EXISTS |
579                         OPEN_ACTION_CREATE_IF_NEW,
580                         OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
581                         OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE);
582 } /* __PHYSFS_platformOpenWrite */
583 
584 
__PHYSFS_platformOpenAppend(const char * filename)585 void *__PHYSFS_platformOpenAppend(const char *filename)
586 {
587     APIRET rc;
588     ULONG dummy = 0;
589     HFILE hfile;
590 
591     /*
592      * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise
593      *  DosQueryFileInfo() will fail if we try to get a file length, etc.
594      */
595     hfile = openFile(filename,
596                         OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
597                         OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
598                         OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE |
599                         OPEN_ACCESS_READWRITE);
600     BAIL_IF_ERRPASS(!hfile, NULL);
601 
602     rc = DosSetFilePtr(hfile, 0, FILE_END, &dummy);
603     if (rc != NO_ERROR)
604     {
605         DosClose(hfile);
606         BAIL(errcodeFromAPIRET(rc), NULL);
607     } /* if */
608 
609     return ((void *) hfile);
610 } /* __PHYSFS_platformOpenAppend */
611 
612 
__PHYSFS_platformRead(void * opaque,void * buf,PHYSFS_uint64 len)613 PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len)
614 {
615     ULONG br = 0;
616     APIRET rc;
617     BAIL_IF(!__PHYSFS_ui64FitsAddressSpace(len),PHYSFS_ERR_INVALID_ARGUMENT,-1);
618     rc = DosRead((HFILE) opaque, buf, (ULONG) len, &br);
619     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), (br > 0) ? ((PHYSFS_sint64) br) : -1);
620     return (PHYSFS_sint64) br;
621 } /* __PHYSFS_platformRead */
622 
623 
__PHYSFS_platformWrite(void * opaque,const void * buf,PHYSFS_uint64 len)624 PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buf,
625                                      PHYSFS_uint64 len)
626 {
627     ULONG bw = 0;
628     APIRET rc;
629     BAIL_IF(!__PHYSFS_ui64FitsAddressSpace(len),PHYSFS_ERR_INVALID_ARGUMENT,-1);
630     rc = DosWrite((HFILE) opaque, (void *) buf, (ULONG) len, &bw);
631     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), (bw > 0) ? ((PHYSFS_sint64) bw) : -1);
632     return (PHYSFS_sint64) bw;
633 } /* __PHYSFS_platformWrite */
634 
635 
__PHYSFS_platformSeek(void * opaque,PHYSFS_uint64 pos)636 int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
637 {
638     ULONG dummy;
639     HFILE hfile = (HFILE) opaque;
640     LONG dist = (LONG) pos;
641     APIRET rc;
642 
643     /* hooray for 32-bit filesystem limits!  :) */
644     BAIL_IF((PHYSFS_uint64) dist != pos, PHYSFS_ERR_INVALID_ARGUMENT, 0);
645     rc = DosSetFilePtr(hfile, dist, FILE_BEGIN, &dummy);
646     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
647     return 1;
648 } /* __PHYSFS_platformSeek */
649 
650 
__PHYSFS_platformTell(void * opaque)651 PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
652 {
653     ULONG pos;
654     HFILE hfile = (HFILE) opaque;
655     const APIRET rc = DosSetFilePtr(hfile, 0, FILE_CURRENT, &pos);
656     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), -1);
657     return ((PHYSFS_sint64) pos);
658 } /* __PHYSFS_platformTell */
659 
660 
__PHYSFS_platformFileLength(void * opaque)661 PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
662 {
663     FILESTATUS3 fs;
664     HFILE hfile = (HFILE) opaque;
665     const APIRET rc = DosQueryFileInfo(hfile, FIL_STANDARD, &fs, sizeof (fs));
666     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), -1);
667     return ((PHYSFS_sint64) fs.cbFile);
668 } /* __PHYSFS_platformFileLength */
669 
670 
__PHYSFS_platformFlush(void * opaque)671 int __PHYSFS_platformFlush(void *opaque)
672 {
673     const APIRET rc = DosResetBuffer((HFILE) opaque);
674     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
675     return 1;
676 } /* __PHYSFS_platformFlush */
677 
678 
__PHYSFS_platformClose(void * opaque)679 void __PHYSFS_platformClose(void *opaque)
680 {
681     DosClose((HFILE) opaque);  /* ignore errors. You should have flushed! */
682 } /* __PHYSFS_platformClose */
683 
684 
__PHYSFS_platformDelete(const char * path)685 int __PHYSFS_platformDelete(const char *path)
686 {
687     char *cppath = cvtUtf8ToCodepage(path);
688     FILESTATUS3 fs;
689     APIRET rc;
690     int retval = 0;
691 
692     BAIL_IF_ERRPASS(!cppath, 0);
693     rc = DosQueryPathInfo(cppath, FIL_STANDARD, &fs, sizeof (fs));
694     GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
695     rc = (fs.attrFile & FILE_DIRECTORY) ? DosDeleteDir(path) : DosDelete(path);
696     GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
697     retval = 1;  /* success */
698 
699 done:
700     allocator.Free(cppath);
701     return retval;
702 } /* __PHYSFS_platformDelete */
703 
704 
705 /* Convert to a format PhysicsFS can grok... */
os2TimeToUnixTime(const FDATE * date,const FTIME * time)706 PHYSFS_sint64 os2TimeToUnixTime(const FDATE *date, const FTIME *time)
707 {
708     struct tm tm;
709 
710     tm.tm_sec = ((PHYSFS_uint32) time->twosecs) * 2;
711     tm.tm_min = time->minutes;
712     tm.tm_hour = time->hours;
713     tm.tm_mday = date->day;
714     tm.tm_mon = date->month;
715     tm.tm_year = ((PHYSFS_uint32) date->year) + 80;
716     tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
717     tm.tm_yday = -1;
718     tm.tm_isdst = -1;
719 
720     return (PHYSFS_sint64) mktime(&tm);
721 } /* os2TimeToUnixTime */
722 
723 
__PHYSFS_platformStat(const char * filename,PHYSFS_Stat * stat,const int follow)724 int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *stat, const int follow)
725 {
726     char *cpfname = cvtUtf8ToCodepage(filename);
727     FILESTATUS3 fs;
728     int retval = 0;
729     APIRET rc;
730 
731     BAIL_IF_ERRPASS(!cpfname, 0);
732 
733     rc = DosQueryPathInfo(cpfname, FIL_STANDARD, &fs, sizeof (fs));
734     GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done);
735 
736     if (fs.attrFile & FILE_DIRECTORY)
737     {
738         stat->filetype = PHYSFS_FILETYPE_DIRECTORY;
739         stat->filesize = 0;
740     } /* if */
741     else
742     {
743         stat->filetype = PHYSFS_FILETYPE_REGULAR;
744         stat->filesize = fs.cbFile;
745     } /* else */
746 
747     stat->modtime = os2TimeToUnixTime(&fs.fdateLastWrite, &fs.ftimeLastWrite);
748     if (stat->modtime < 0)
749         stat->modtime = 0;
750 
751     stat->accesstime = os2TimeToUnixTime(&fs.fdateLastAccess, &fs.ftimeLastAccess);
752     if (stat->accesstime < 0)
753         stat->accesstime = 0;
754 
755     stat->createtime = os2TimeToUnixTime(&fs.fdateCreation, &fs.ftimeCreation);
756     if (stat->createtime < 0)
757         stat->createtime = 0;
758 
759     stat->readonly = ((fs.attrFile & FILE_READONLY) == FILE_READONLY);
760     return 1;  /* success */
761 
762 done:
763     allocator.Free(cpfname);
764     return retval;
765 } /* __PHYSFS_platformStat */
766 
767 
__PHYSFS_platformGetThreadID(void)768 void *__PHYSFS_platformGetThreadID(void)
769 {
770     PTIB ptib;
771     PPIB ppib;
772 
773     /*
774      * Allegedly, this API never fails, but we'll punt and return a
775      *  default value (zero might as well do) if it does.
776      */
777     const APIRET rc = DosGetInfoBlocks(&ptib, &ppib);
778     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0);
779     return ((void *) ptib->tib_ordinal);
780 } /* __PHYSFS_platformGetThreadID */
781 
782 
__PHYSFS_platformCreateMutex(void)783 void *__PHYSFS_platformCreateMutex(void)
784 {
785     HMTX hmtx = NULLHANDLE;
786     const APIRET rc = DosCreateMutexSem(NULL, &hmtx, 0, 0);
787     BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), NULL);
788     return ((void *) hmtx);
789 } /* __PHYSFS_platformCreateMutex */
790 
791 
__PHYSFS_platformDestroyMutex(void * mutex)792 void __PHYSFS_platformDestroyMutex(void *mutex)
793 {
794     DosCloseMutexSem((HMTX) mutex);
795 } /* __PHYSFS_platformDestroyMutex */
796 
797 
__PHYSFS_platformGrabMutex(void * mutex)798 int __PHYSFS_platformGrabMutex(void *mutex)
799 {
800     /* Do _NOT_ set the physfs error message in here! */
801     return (DosRequestMutexSem((HMTX) mutex, SEM_INDEFINITE_WAIT) == NO_ERROR);
802 } /* __PHYSFS_platformGrabMutex */
803 
804 
__PHYSFS_platformReleaseMutex(void * mutex)805 void __PHYSFS_platformReleaseMutex(void *mutex)
806 {
807     DosReleaseMutexSem((HMTX) mutex);
808 } /* __PHYSFS_platformReleaseMutex */
809 
810 #endif  /* PHYSFS_PLATFORM_OS2 */
811 
812 /* end of physfs_platform_os2.c ... */
813