platform/os2.c
changeset 404 d08723d994c1
child 407 81646aa3f6f8
equal deleted inserted replaced
403:6da7fe0dd020 404:d08723d994c1
       
     1 /*
       
     2  * OS/2 support routines for PhysicsFS.
       
     3  *
       
     4  * Please see the file LICENSE in the source's root directory.
       
     5  *
       
     6  *  This file written by Ryan C. Gordon.
       
     7  */
       
     8 
       
     9 #if HAVE_CONFIG_H
       
    10 #  include <config.h>
       
    11 #endif
       
    12 
       
    13 #if (defined OS2)
       
    14 
       
    15 #define INCL_DOSSEMAPHORES
       
    16 #define INCL_DOSDATETIME
       
    17 #define INCL_DOSFILEMGR
       
    18 #define INCL_DOSMODULEMGR
       
    19 #define INCL_DOSERRORS
       
    20 #define INCL_DOSDEVICES
       
    21 #include <os2.h>
       
    22 
       
    23 #include <stdlib.h>
       
    24 #include <errno.h>
       
    25 #include <string.h>
       
    26 #include <assert.h>
       
    27 
       
    28 
       
    29 static APIRET os2err(APIRET retval)
       
    30 {
       
    31     const char *err = NULL;
       
    32 
       
    33     /*
       
    34      * The ones that say "OS/2 reported" are more for PhysicsFS developer
       
    35      *  debugging. We give more generic messages for ones that are likely to
       
    36      *  fall through to an application.
       
    37      */
       
    38     switch (retval)
       
    39     {
       
    40         case NO_ERROR: /* Don't set the PhysicsFS error message for these... */
       
    41         case ERROR_INTERRUPT:
       
    42         case ERROR_TIMEOUT:
       
    43             break;
       
    44 
       
    45         case ERROR_NOT_ENOUGH_MEMORY:
       
    46             err = ERR_OUT_OF_MEMORY;
       
    47             break;
       
    48 
       
    49         case ERROR_FILE_NOT_FOUND:
       
    50             err = "File not found";
       
    51             break;
       
    52 
       
    53         case ERROR_PATH_NOT_FOUND:
       
    54             err = "Path not found";
       
    55             break;
       
    56 
       
    57         case ERROR_ACCESS_DENIED:
       
    58             err = "Access denied";
       
    59             break;
       
    60 
       
    61         case ERROR_NOT_DOS_DISK:
       
    62             err = "Not a DOS disk";
       
    63             break;
       
    64 
       
    65         case ERROR_DRIVE_LOCKED:
       
    66             err = "Drive is locked";
       
    67             break;
       
    68 
       
    69         case ERROR_SHARING_VIOLATION:
       
    70             err = "Sharing violation";
       
    71             break;
       
    72 
       
    73         case ERROR_CANNOT_MAKE:
       
    74             err = "Cannot make";
       
    75             break;
       
    76 
       
    77         case ERROR_DEVICE_IN_USE:
       
    78             err = "Device already in use";
       
    79             break;
       
    80 
       
    81         case ERROR_DRIVE_LOCKED:
       
    82             err = "Drive is locked";
       
    83             break;
       
    84 
       
    85         case ERROR_OPEN_FAILED:
       
    86             err = "Open failed";
       
    87             break;
       
    88 
       
    89         case ERROR_DISK_FULL:
       
    90             err = "Disk is full";
       
    91             break;
       
    92 
       
    93         case ERROR_DISK_FULL:
       
    94             err = "Pipe busy";
       
    95             break;
       
    96 
       
    97         case ERROR_SHARING_BUFFER_EXCEEDED:
       
    98             err = "Sharing buffer exceeded";
       
    99             break;
       
   100 
       
   101         case ERROR_FILENAME_EXCED_RANGE:
       
   102         case ERROR_META_EXPANSION_TOO_LONG:
       
   103             err = "Filename too big";
       
   104             break;
       
   105 
       
   106         case ERROR_TOO_MANY_HANDLES:
       
   107         case ERROR_TOO_OPEN_FILES:
       
   108         case ERROR_NO_MORE_SEARCH_HANDLES:
       
   109             err = "Too many open handles";
       
   110             break;
       
   111 
       
   112         case ERROR_SEEK_ON_DEVICE:
       
   113             err = "Seek error";  /* Is that what this error means? */
       
   114             break;
       
   115 
       
   116         case ERROR_NEGATIVE_SEEK:
       
   117             err = "Seek past start of file";
       
   118             break;
       
   119 
       
   120         case ERROR_CURRENT_DIRECTORY:
       
   121             err = "Trying to delete current working directory";
       
   122             break;
       
   123 
       
   124         case ERROR_WRITE_PROTECT:
       
   125             err = "Write protect error";
       
   126             break;
       
   127 
       
   128         case ERROR_WRITE_FAULT:
       
   129             err = "Write fault";
       
   130             break;
       
   131 
       
   132         case ERROR_LOCK_VIOLATION:
       
   133             err = "Lock violation";
       
   134             break;
       
   135 
       
   136         case ERROR_GEN_FAILURE:
       
   137             err = "General failure";
       
   138             break;
       
   139 
       
   140         case ERROR_UNCERTAIN_MEDIA:
       
   141             err = "Uncertain media";
       
   142             break;
       
   143 
       
   144         case ERROR_PROTECTION_VIOLATION:
       
   145             err = "Protection violation";
       
   146             break;
       
   147 
       
   148         case ERROR_INVALID_PARAMETER:
       
   149         case ERROR_INVALID_NAME:
       
   150         case ERROR_INVALID_DRIVE:
       
   151         case ERROR_INVALID_HANDLE:
       
   152         case ERROR_INVALID_FUNCTION:
       
   153         case ERROR_INVALID_LEVEL:
       
   154         case ERROR_INVALID_CATEGORY:
       
   155         case ERROR_DUPLICATE_NAME:
       
   156         case ERROR_BUFFER_OVERFLOW:
       
   157         case ERROR_BAD_LENGTH:
       
   158         case ERROR_BAD_DRIVER_LEVEL:
       
   159         case ERROR_DIRECT_ACCESS_HANDLE:
       
   160             err = "OS/2 reported an invalid parameter to an API function";
       
   161             break;
       
   162 
       
   163         case ERROR_NO_VOLUME_LABEL:
       
   164             err = "OS/2 reported no volume label";
       
   165             break;
       
   166 
       
   167         case ERROR_NO_MORE_FILES:
       
   168             err = "OS/2 reported no more files";
       
   169             break;
       
   170 
       
   171         case ERROR_MONITORS_NOT_SUPPORTED:
       
   172             err = "OS/2 reported monitors not supported";
       
   173             break;
       
   174 
       
   175         case ERROR_BROKEN_PIPE:
       
   176             err = "OS/2 reported a broken pipe";
       
   177             break;
       
   178 
       
   179         case ERROR_MORE_DATA:
       
   180             err = "OS/2 reported \"more data\" (?)";
       
   181             break;
       
   182 
       
   183         case ERROR_EAS_DIDNT_FIT:
       
   184             err = "OS/2 reported Extended Attributes didn't fit";
       
   185             break;
       
   186 
       
   187         case ERROR_INVALID_EA_NAME:
       
   188             err = "OS/2 reported an invalid Extended Attribute name";
       
   189             break;
       
   190 
       
   191         case ERROR_EA_LIST_INCONSISTENT:
       
   192             err = "OS/2 reported an inconsistent Extended Attribute list";
       
   193             break;
       
   194 
       
   195         case ERROR_EA_VALUE_UNSUPPORTABLE:
       
   196             err = "OS/2 reported an unsupportable Extended Attribute value";
       
   197             break;
       
   198 
       
   199         case ERROR_SEM_OWNER_DIED:
       
   200             err = "OS/2 reported that semaphore owner died";
       
   201             break;
       
   202 
       
   203         case ERROR_TOO_MANY_SEM_REQUESTS:
       
   204             err = "OS/2 reported too many semaphore requests";
       
   205             break;
       
   206 
       
   207         case ERROR_SEM_BUSY:
       
   208             err = "OS/2 reported a blocked semaphore";
       
   209             break;
       
   210 
       
   211         case ERROR_NOT_OWNER:
       
   212             err = "OS/2 reported that we used a resource we don't own.";
       
   213             break;
       
   214 
       
   215         default:
       
   216             err = "OS/2 reported back with unrecognized error code";
       
   217             break;
       
   218     } /* switch */
       
   219 
       
   220     if (err != NULL)
       
   221         __PHYSFS_setError(err);
       
   222 
       
   223     return(retval);
       
   224 } /* os2err */
       
   225 
       
   226 
       
   227 static char *baseDir = NULL;
       
   228 
       
   229 int __PHYSFS_platformInit(void)
       
   230 {
       
   231     char buf[CCHMAXPATH];
       
   232     APIRET rc;
       
   233     TIB tib;
       
   234     PIB pib;
       
   235 
       
   236     assert(baseDir == NULL);
       
   237 
       
   238     BAIL_IF_MACRO(os2err(DosGetInfoBlocks(&tib, &pib)) != NO_ERROR, NULL, 0);
       
   239     rc = DosQueryModuleName(pib.pib_hmte, sizeof (buf), (PCHAR) buf);
       
   240     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 0);
       
   241 
       
   242     baseDir = (char *) malloc(strlen(buf) + 1);
       
   243     BAIL_IF_MACRO(baseDir == NULL, ERR_OUT_OF_MEMORY, 0);
       
   244     strcpy(baseDir);
       
   245 
       
   246     return(1);  /* success. */
       
   247 } /* __PHYSFS_platformInit */
       
   248 
       
   249 
       
   250 int __PHYSFS_platformDeinit(void)
       
   251 {
       
   252     assert(baseDir != NULL);
       
   253     free(baseDir);
       
   254     baseDir = NULL;
       
   255     return(1);  /* success. */
       
   256 } /* __PHYSFS_platformDeinit */
       
   257 
       
   258 
       
   259 static int disc_is_inserted(ULONG drive)
       
   260 {
       
   261     int rc;
       
   262     char buf[20];
       
   263     DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION);
       
   264     rc = DosQueryFSInfo(drive + 1, FSIL_VOLSER, buf, sizeof (buf));
       
   265     DosError(FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION);
       
   266     return(rc == NO_ERROR);
       
   267 } /* is_cdrom_inserted */
       
   268 
       
   269 
       
   270 static int is_cdrom_drive(ULONG drive)
       
   271 {
       
   272     PHYSFS_uint16 cmd = (((drive & 0xFF) << 8) | 0);
       
   273     BIOSPARAMETERBLOCK bpb;
       
   274     ULONG ul1, ul2;
       
   275     APIRET rc;
       
   276 
       
   277     rc = DosDevIOCtl((HFILE) -1, IOCTL_DISK,
       
   278                      DSK_GETDEVICEPARAMS,
       
   279                      &cmd, sizeof (cmd), &ul1,
       
   280                      &bpb, sizeof (bpb), &u2);
       
   281 
       
   282     /*
       
   283      * !!! FIXME: Note that this tells us that the media is REMOVABLE...
       
   284      * !!! FIXME:  but it might not be a CD-ROM...check driver name?
       
   285      */
       
   286     return((rc == NO_ERROR) && ((DiskData.fsDeviceAttr & 0x0001) == 0));
       
   287 } /* is_cdrom_drive */
       
   288 
       
   289 
       
   290 char **__PHYSFS_platformDetectAvailableCDs(void)
       
   291 {
       
   292     ULONG dummy;
       
   293     ULONG drivemap;
       
   294     ULONG i, bit;
       
   295     APIRET rc;
       
   296     char **retval;
       
   297     PHYSFS_uint32 cd_count = 0;
       
   298 
       
   299     retval = (char **) malloc(sizeof (char *));
       
   300     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
       
   301     *retval = NULL;
       
   302 
       
   303     rc = DosQueryCurrentDisk(&dummy, &drivemap);
       
   304     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, retval);
       
   305 
       
   306     /* !!! FIXME: the a, b, and c drives are almost certainly NOT cdroms... */
       
   307     for (i = 0, bit = 1; i < 26; i++, bit << 1)
       
   308     {
       
   309         if (drivemap & bit)  /* this logical drive exists. */
       
   310         {
       
   311             if ((is_cdrom_drive(i)) && (disc_is_inserted(i)))
       
   312             {
       
   313                 char **tmp = realloc(retval, sizeof (char *) * (cd_count + 1));
       
   314                 if (tmp)
       
   315                 {
       
   316                     char *str = (char *) malloc(4);
       
   317                     retval = tmp;
       
   318                     retval[cd_count - 1] = str;
       
   319                     if (str)
       
   320                     {
       
   321                         str[0] = ('A' + i);
       
   322                         str[1] = ':';
       
   323                         str[2] = '\\';
       
   324                         str[3] = '\0';
       
   325                         cd_count++;
       
   326                     } /* if */
       
   327                 } /* if */
       
   328             } /* if */
       
   329         } /* if */
       
   330     } /* for */
       
   331 
       
   332     return(retval);
       
   333 } /* __PHYSFS_platformDetectAvailableCDs */
       
   334 
       
   335 
       
   336 char *__PHYSFS_platformCalcBaseDir(const char *argv0)
       
   337 {
       
   338     char *retval = (char *) malloc(strlen(baseDir) + 1);
       
   339     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
       
   340     strcpy(retval, baseDir); /* calculated at init time. */
       
   341     return(retval);
       
   342 } /* __PHYSFS_platformCalcBaseDir */
       
   343 
       
   344 
       
   345 char *__PHYSFS_platformGetUserName(void)
       
   346 {
       
   347     return(NULL);  /* (*shrug*) */
       
   348 } /* __PHYSFS_platformGetUserName */
       
   349 
       
   350 
       
   351 char *__PHYSFS_platformGetUserDir(void)
       
   352 {
       
   353     return(__PHYSFS_platformCalcBaseDir(NULL));
       
   354 } /* __PHYSFS_platformGetUserDir */
       
   355 
       
   356 
       
   357 int __PHYSFS_platformStricmp(const char *x, const char *y)
       
   358 {
       
   359     int ux, uy;
       
   360 
       
   361     do
       
   362     {
       
   363         ux = toupper((int) *x);
       
   364         uy = toupper((int) *y);
       
   365         if (ux > uy)
       
   366             return(1);
       
   367         else if (ux < uy)
       
   368             return(-1);
       
   369         x++;
       
   370         y++;
       
   371     } while ((ux) && (uy));
       
   372 
       
   373     return(0);
       
   374 } /* __PHYSFS_platformStricmp */
       
   375 
       
   376 
       
   377 int __PHYSFS_platformExists(const char *fname)
       
   378 {
       
   379     FILESTATUS3 fs;
       
   380     APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs));
       
   381     return(os2err(rc) == NO_ERROR);
       
   382 } /* __PHYSFS_platformExists */
       
   383 
       
   384 
       
   385 int __PHYSFS_platformIsSymLink(const char *fname)
       
   386 {
       
   387     return(0);  /* no symlinks in OS/2. */
       
   388 } /* __PHYSFS_platformIsSymlink */
       
   389 
       
   390 
       
   391 int __PHYSFS_platformIsDirectory(const char *fname)
       
   392 {
       
   393     FILESTATUS3 fs;
       
   394     APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs));
       
   395     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 0)
       
   396     return((fs.attrFile & FILE_DIRECTORY) != 0);
       
   397 } /* __PHYSFS_platformIsDirectory */
       
   398 
       
   399 
       
   400 char *__PHYSFS_platformCvtToDependent(const char *prepend,
       
   401                                       const char *dirName,
       
   402                                       const char *append)
       
   403 {
       
   404     int len = ((prepend) ? strlen(prepend) : 0) +
       
   405               ((append) ? strlen(append) : 0) +
       
   406               strlen(dirName) + 1;
       
   407     char *retval = malloc(len);
       
   408     char *p;
       
   409 
       
   410     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
       
   411 
       
   412     if (prepend)
       
   413         strcpy(retval, prepend);
       
   414     else
       
   415         retval[0] = '\0';
       
   416 
       
   417     strcat(retval, dirName);
       
   418 
       
   419     if (append)
       
   420         strcat(retval, append);
       
   421 
       
   422     for (p = strchr(retval, '/'); p != NULL; p = strchr(p + 1, '/'))
       
   423         *p = '\\';
       
   424 
       
   425     return(retval);
       
   426 } /* __PHYSFS_platformCvtToDependent */
       
   427 
       
   428 
       
   429 LinkedStringList *__PHYSFS_platformEnumerateFiles(const char *dirname,
       
   430                                                   int omitSymLinks)
       
   431 {
       
   432     char spec[CCHMAXPATH];
       
   433     LinkedStringList *retval = NULL, *p = NULL;
       
   434     FINDFILEBUF3 fb;
       
   435     HDIR hdir = HDIR_CREATE;
       
   436     ULONG count = 1;
       
   437     APIRET rc;
       
   438 
       
   439     BAIL_IF_MACRO(strlen(dirname) > sizeof (spec) - 4, ERR_OS_ERROR, NULL);
       
   440     strcpy(spec, dname)
       
   441     strcat(spec, "*.*");
       
   442 
       
   443     rc = DosFindFirst(spec, &hdir,
       
   444                       FILE_DIRECTORY | FILE_ARCHIVED |
       
   445                       FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM,
       
   446                       &fb, sizeof (fb), &count, FIL_STANDARD)
       
   447     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, 0);
       
   448     while (count == 1)
       
   449     {
       
   450         if (strcmp(fb.achName, ".") == 0)
       
   451             continue;
       
   452 
       
   453         if (strcmp(fb.achName, "..") == 0)
       
   454             continue;
       
   455 
       
   456         retval = __PHYSFS_addToLinkedStringList(retval, &p, fb.achName, -1);
       
   457 
       
   458         DosFindNext(hdir, &fb, sizeof (fb), &count);
       
   459     } /* while */
       
   460 
       
   461     DosFindClose(hdir);
       
   462     return(retval);
       
   463 } /* __PHYSFS_platformEnumerateFiles */
       
   464 
       
   465 
       
   466 char *__PHYSFS_platformCurrentDir(void)
       
   467 {
       
   468     char *retval;
       
   469     ULONG currentDisk;
       
   470     ULONG dummy;
       
   471     ULONG pathSize = 0;
       
   472     APIRET rc;
       
   473     BYTE byte;
       
   474 
       
   475     rc = DosQueryCurrentDisk(&currentDisk, &dummy);
       
   476     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, NULL);
       
   477 
       
   478     /* The first call just tells us how much space we need for the string. */
       
   479     rc = DosQueryCurrentDir(currentDisk, &byte, &pathSize);
       
   480     pathSize++; /* Add space for null terminator. */
       
   481     retval = (char *) malloc(pathSize + 3);  /* plus "x:\\" */
       
   482     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
       
   483 
       
   484     /* Actually get the string this time. */
       
   485     rc = DosQueryCurrentDir(currentDisk, (PBYTE) (retval + 3), &pathSize);
       
   486     if (os2err(rc) != NO_ERROR)
       
   487     {
       
   488         free(retval);
       
   489         return(NULL);
       
   490     } /* if */
       
   491 
       
   492     retval[0] = ('A' + (currentDisk - 1));
       
   493     retval[1] = ':';
       
   494     retval[2] = '\\';
       
   495     return(retval);
       
   496 } /* __PHYSFS_platformCurrentDir */
       
   497 
       
   498 
       
   499 char *__PHYSFS_platformRealPath(const char *path)
       
   500 {
       
   501     char buf[CCHMAXPATH];
       
   502     char *retval;
       
   503     APIRET rc = DosQueryPathInfo(fname, FIL_QUERYFULLNAME, buf, sizeof (buf));
       
   504     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, NULL);
       
   505     retval = (char *) malloc(strlen(buf) + 1);
       
   506     BAIL_IF_MACRO(retval == NULL, ERR_OUT_OF_MEMORY, NULL);
       
   507     strcpy(retval, buf);
       
   508     return(retval);
       
   509 } /* __PHYSFS_platformRealPath */
       
   510 
       
   511 
       
   512 int __PHYSFS_platformMkDir(const char *path)
       
   513 {
       
   514     return(os2err(DosCreateDir(path, NULL)) == NO_ERROR);
       
   515 } /* __PHYSFS_platformMkDir */
       
   516 
       
   517 
       
   518 void *__PHYSFS_platformOpenRead(const char *filename)
       
   519 {
       
   520     ULONG actionTaken = 0;
       
   521     HFILE hfile = NULLHANDLE;
       
   522 
       
   523     /*
       
   524      * File must be opened SHARE_DENYWRITE and ACCESS_READONLY, otherwise
       
   525      *  DosQueryFileInfo() will fail if we try to get a file length, etc.
       
   526      */
       
   527     os2err(DosOpen(filename, &hfile, &actionTaken, 0, FILE_NORMAL,
       
   528                    OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
       
   529                    OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
       
   530                    OPEN_FLAGS_NO_INHERIT | OPEN_SHARE_DENYWRITE |
       
   531                    OPEN_ACCESS_READONLY, NULL));
       
   532 
       
   533     return((void *) hfile);
       
   534 } /* __PHYSFS_platformOpenRead */
       
   535 
       
   536 
       
   537 void *__PHYSFS_platformOpenWrite(const char *filename)
       
   538 {
       
   539     ULONG actionTaken = 0;
       
   540     HFILE hfile = NULLHANDLE;
       
   541 
       
   542     /*
       
   543      * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise
       
   544      *  DosQueryFileInfo() will fail if we try to get a file length, etc.
       
   545      */
       
   546     os2err(DosOpen(filename, &hfile, &actionTaken, 0, FILE_NORMAL,
       
   547                    OPEN_ACTION_REPLACE_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
       
   548                    OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
       
   549                    OPEN_FLAGS_NO_INHERIT | OPEN_SHARE_DENYWRITE |
       
   550                    OPEN_ACCESS_READWRITE, NULL));
       
   551 
       
   552     return((void *) hfile);
       
   553 } /* __PHYSFS_platformOpenWrite */
       
   554 
       
   555 
       
   556 void *__PHYSFS_platformOpenAppend(const char *filename)
       
   557 {
       
   558     ULONG dummy = 0;
       
   559     HFILE hfile = NULLHANDLE;
       
   560 
       
   561     /*
       
   562      * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise
       
   563      *  DosQueryFileInfo() will fail if we try to get a file length, etc.
       
   564      */
       
   565     rc = os2err(DosOpen(filename, &hfile, &dummy, 0, FILE_NORMAL,
       
   566                    OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
       
   567                    OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY |
       
   568                    OPEN_FLAGS_NO_INHERIT | OPEN_SHARE_DENYWRITE |
       
   569                    OPEN_ACCESS_READWRITE, NULL));
       
   570 
       
   571     if (rc == NO_ERROR)
       
   572     {
       
   573         if (os2err(DosSetFilePtr(hfile, 0, FILE_END, &dummy)) != NO_ERROR)
       
   574         {
       
   575             DosClose(hfile);
       
   576             hfile = NULLHANDLE;
       
   577         } /* if */
       
   578     } /* if */
       
   579 
       
   580     return((void *) hfile);
       
   581 } /* __PHYSFS_platformOpenAppend */
       
   582 
       
   583 
       
   584 PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer,
       
   585                                     PHYSFS_uint32 size, PHYSFS_uint32 count)
       
   586 {
       
   587     HFILE hfile = (HFILE) opaque;
       
   588     PHYSFS_sint64 retval;
       
   589     ULONG br;
       
   590 
       
   591     for (retval = 0; retval < count; retval++)
       
   592     {
       
   593         APIRET rc = os2err(DosRead(hfile, buffer, size, &br));
       
   594         if (br < size)
       
   595         {
       
   596             DosSetFilePtr(hfile, -bw, FILE_CURRENT, &br); /* try to cleanup. */
       
   597             return(retval);
       
   598         } /* if */
       
   599 
       
   600         buffer = (void *) ( ((char *) buffer) + size) );
       
   601     } /* for */
       
   602 
       
   603     return(retval);
       
   604 } /* __PHYSFS_platformRead */
       
   605 
       
   606 
       
   607 PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer,
       
   608                                      PHYSFS_uint32 size, PHYSFS_uint32 count)
       
   609 {
       
   610     HFILE hfile = (HFILE) opaque;
       
   611     PHYSFS_sint64 retval;
       
   612     ULONG bw;
       
   613 
       
   614     for (retval = 0; retval < count; retval++)
       
   615     {
       
   616         APIRET rc = os2err(DosWrite(hfile, buffer, size, &bw));
       
   617         if (bw < size)
       
   618         {
       
   619             DosSetFilePtr(hfile, -bw, FILE_CURRENT, &bw); /* try to cleanup. */
       
   620             return(retval);
       
   621         } /* if */
       
   622 
       
   623         buffer = (void *) ( ((char *) buffer) + size) );
       
   624     } /* for */
       
   625 
       
   626     return(retval);
       
   627 } /* __PHYSFS_platformWrite */
       
   628 
       
   629 
       
   630 int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos)
       
   631 {
       
   632     ULONG dummy;
       
   633     HFILE hfile = (HFILE) opaque;
       
   634     LONG dist = (LONG) pos;
       
   635 
       
   636     /* hooray for 32-bit filesystem limits!  :) */
       
   637     BAIL_IF_MACRO((PHYSFS_uint64) dist != pos, ERR_SEEK_OUT_OF_RANGE, 0);
       
   638 
       
   639     return(os2err(DosSetFilePtr(hfile, dist, FILE_BEGIN, &dummy)) == NO_ERROR);
       
   640 } /* __PHYSFS_platformSeek */
       
   641 
       
   642 
       
   643 PHYSFS_sint64 __PHYSFS_platformTell(void *opaque)
       
   644 {
       
   645     ULONG pos;
       
   646     HFILE hfile = (HFILE) opaque;
       
   647     APIRET rc = os2err(DosSetFilePtr(hfile, 0, FILE_CURRENT, &pos));
       
   648     BAIL_IF_MACRO(rc != NO_ERROR, NULL, -1);
       
   649     return((PHYSFS_sint64) pos);
       
   650 } /* __PHYSFS_platformTell */
       
   651 
       
   652 
       
   653 PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque)
       
   654 {
       
   655     FILESTATUS3 fs;
       
   656     HFILE hfile = (HFILE) opaque;
       
   657     APIRET rc = DosQueryFileInfo(hfile, FIL_STANDARD, &fs, sizeof (fs));
       
   658     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, -1);
       
   659     return((PHYSFS_sint64) fs.cbFile);
       
   660 } /* __PHYSFS_platformFileLength */
       
   661 
       
   662 
       
   663 int __PHYSFS_platformEOF(void *opaque)
       
   664 {
       
   665     PHYSFS_sint64 len, pos;
       
   666 
       
   667     len = __PHYSFS_platformFileLength(opaque);
       
   668     BAIL_IF_MACRO(len == -1, NULL, 1);  /* (*shrug*) */
       
   669     pos = __PHYSFS_platformTell(opaque);
       
   670     BAIL_IF_MACRO(pos == -1, NULL, 1);  /* (*shrug*) */
       
   671 
       
   672     return(pos >= len);
       
   673 } /* __PHYSFS_platformEOF */
       
   674 
       
   675 
       
   676 int __PHYSFS_platformFlush(void *opaque)
       
   677 {
       
   678     return(os2err(DosResetBuffers((HFILE) opaque) == NO_ERROR));
       
   679 } /* __PHYSFS_platformFlush */
       
   680 
       
   681 
       
   682 int __PHYSFS_platformClose(void *opaque)
       
   683 {
       
   684     return(os2err(DosClose((HFILE) opaque) == NO_ERROR));
       
   685 } /* __PHYSFS_platformClose */
       
   686 
       
   687 
       
   688 int __PHYSFS_platformDelete(const char *path)
       
   689 {
       
   690     if (__PHYSFS_platformIsDirectory(path))
       
   691         return(os2err(DosDeleteDir(path)) == NO_ERROR);
       
   692 
       
   693     return(os2err(DosDelete(path) == NO_ERROR));
       
   694 } /* __PHYSFS_platformDelete */
       
   695 
       
   696 
       
   697 PHYSFS_sint64 __PHYSFS_platformGetLastModTime(const char *fname)
       
   698 {
       
   699     struct tm tm;
       
   700     FILESTATUS3 fs;
       
   701     APIRET rc = DosQueryPathInfo(fname, FIL_STANDARD, &fs, sizeof (fs));
       
   702     BAIL_IF_MACRO(os2err(rc) != NO_ERROR, NULL, -1);
       
   703 
       
   704     /* Convert to a format that mktime() can grok... */
       
   705     tm.tm_sec = ((PHYSFS_uint32) fs.ftimeLastWrite.twosecs) * 2;
       
   706     tm.tm_min = fs.ftimeLastWrite.minutes;
       
   707     tm.tm_hour = fs.ftimeLastWrite.hours;
       
   708     tm.tm_mday = fs.fdateLastWrite.day;
       
   709     tm.tm_mon = fs.fdateLastWrite.month;
       
   710     tm.tm_year = ((PHYSFS_uint32) fs.fdateLastWrite.year) + 80;
       
   711     tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/;
       
   712     tm.tm_yday = -1;
       
   713     tm.tm_isdst = -1;
       
   714 
       
   715     /* Convert to a format PhysicsFS can grok... */
       
   716     retval = (PHYSFS_sint64) mktime(&tm);
       
   717     BAIL_IF_MACRO(retval == -1, strerror(errno), -1);
       
   718     return(retval);
       
   719 } /* __PHYSFS_platformGetLastModTime */
       
   720 
       
   721 
       
   722 /* Much like my college days, try to sleep for 10 milliseconds at a time... */
       
   723 void __PHYSFS_platformTimeslice(void)
       
   724 {
       
   725     DosSleep(10);
       
   726 } /* __PHYSFS_platformTimeslice(void) */
       
   727 
       
   728 
       
   729 PHYSFS_uint64 __PHYSFS_platformGetThreadID(void)
       
   730 {
       
   731     TIB tib;
       
   732     PIB pib;
       
   733 
       
   734     /*
       
   735      * Allegedly, this API never fails, but we'll punt and return a
       
   736      *  default value (zero might as well do) if it does.
       
   737      */
       
   738     BAIL_IF_MACRO(os2err(DosGetInfoBlocks(&tib, &pib)) != NO_ERROR, 0, 0);
       
   739     return((PHYSFS_uint64) tib.tib_ordinal);
       
   740 } /* __PHYSFS_platformGetThreadID */
       
   741 
       
   742 
       
   743 void *__PHYSFS_platformCreateMutex(void)
       
   744 {
       
   745     HMTX hmtx = NULLHANDLE;
       
   746     os2err(DosCreateMutexSem(NULL, &hmtx, 0, 0));
       
   747     return((void *) hmtx);
       
   748 } /* __PHYSFS_platformCreateMutex */
       
   749 
       
   750 
       
   751 void __PHYSFS_platformDestroyMutex(void *mutex)
       
   752 {
       
   753     DosCloseMutexSem((HMTX) mutex);
       
   754 } /* __PHYSFS_platformDestroyMutex */
       
   755 
       
   756 
       
   757 int __PHYSFS_platformGrabMutex(void *mutex)
       
   758 {
       
   759     /* Do _NOT_ call os2err() (which sets the physfs error msg) in here! */
       
   760     return(DosRequestMutexSem((HMTX) mutex, SEM_INDEFINITE_WAIT) == NO_ERROR);
       
   761 } /* __PHYSFS_platformGrabMutex */
       
   762 
       
   763 
       
   764 void __PHYSFS_platformReleaseMutex(void *mutex)
       
   765 {
       
   766     DosReleaseMutexSem((HMTX) mutex);
       
   767 } /* __PHYSFS_platformReleaseMutex */
       
   768 
       
   769 #endif  /* defined OS2 */
       
   770 
       
   771 /* end of os2.c ... */
       
   772