diff --git a/native/doscalls.c b/native/doscalls.c index 1966d42..186e7f9 100644 --- a/native/doscalls.c +++ b/native/doscalls.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "os2native.h" #include "doscalls.h" @@ -111,11 +112,16 @@ static int initDoscalls(LxLoaderState *lx_state) } // initDoscalls LX_NATIVE_MODULE_INIT({ if (!initDoscalls(lx_state)) return NULL; }) + LX_NATIVE_EXPORT(DosQueryPathInfo, 223), LX_NATIVE_EXPORT(DosQueryHType, 224), LX_NATIVE_EXPORT(DosScanEnv, 227), LX_NATIVE_EXPORT(DosGetDateTime, 230), LX_NATIVE_EXPORT(DosExit, 234), + LX_NATIVE_EXPORT(DosSetFilePtr, 256), + LX_NATIVE_EXPORT(DosClose, 257), LX_NATIVE_EXPORT(DosOpen, 273), + LX_NATIVE_EXPORT(DosQueryFileInfo, 279), + LX_NATIVE_EXPORT(DosRead, 281), LX_NATIVE_EXPORT(DosWrite, 282), LX_NATIVE_EXPORT(DosExitList, 296), LX_NATIVE_EXPORT(DosAllocMem, 299), @@ -124,28 +130,44 @@ LX_NATIVE_MODULE_INIT({ if (!initDoscalls(lx_state)) return NULL; }) LX_NATIVE_EXPORT(DosQueryModuleName, 320), LX_NATIVE_EXPORT(DosCreateEventSem, 324), LX_NATIVE_EXPORT(DosCreateMutexSem, 331), + LX_NATIVE_EXPORT(DosRequestMutexSem, 334), + LX_NATIVE_EXPORT(DosReleaseMutexSem, 335), LX_NATIVE_EXPORT(DosSubSetMem, 344), LX_NATIVE_EXPORT(DosSubAllocMem, 345), LX_NATIVE_EXPORT(DosQuerySysInfo, 348), LX_NATIVE_EXPORT(DosSetExceptionHandler, 354), LX_NATIVE_EXPORT(DosSetSignalExceptionFocus, 378), + LX_NATIVE_EXPORT(DosEnterMustComplete, 380), + LX_NATIVE_EXPORT(DosExitMustComplete, 381), LX_NATIVE_EXPORT(DosSetRelMaxFH, 382), LX_NATIVE_EXPORT(DosFlatToSel, 425) LX_NATIVE_MODULE_INIT_END() +static PTIB2 getTib2(void) +{ + // just read the FS register, since we have to stick it there anyhow... + PTIB2 ptib2; + __asm__ __volatile__ ( "movl %%fs:0xC, %0 \n\t" : "=r" (ptib2) ); + return ptib2; +} // getTib2 + +static PTIB getTib(void) +{ + // we store the TIB2 struct right after the TIB struct on the stack, + // so get the TIB2's linear address from %fs:0xC, then step back + // to the TIB's linear address. + uint8 *ptib2 = (uint8 *) getTib2(); + return (PTIB) (ptib2 - sizeof (TIB)); +} // getTib + + APIRET DosGetInfoBlocks(PTIB *pptib, PPIB *pppib) { TRACE_NATIVE("DosGetInfoBlocks(%p, %p)", pptib, pppib); if (pptib != NULL) { - // just read the FS register, since we have to stick it there anyhow... - uint8 *ptib2; - __asm__ __volatile__ ( "movl %%fs:0xC, %0 \n\t" : "=r" (ptib2) ); - // we store the TIB2 struct right after the TIB struct on the stack, - // so get the TIB2's linear address from %fs:0xC, then step back - // to the TIB's linear address. - *pptib = (PTIB) (ptib2 - sizeof (TIB)); + *pptib = getTib(); } // if if (pppib != NULL) { @@ -571,12 +593,12 @@ APIRET DosGetDateTime(PDATETIME pdt) // !!! FIXNE: length can change! static int locateOneElement(char *buf) { - if (access(buf, F_OK)) + if (access(buf, F_OK) == 0) return 1; // quick rejection: exists in current case. DIR *dirp; char *ptr = strrchr(buf, '/'); // find entry at end of path. - if (ptr == NULL) { + if ((ptr == NULL) || (ptr == buf)) { dirp = opendir("/"); ptr = buf; } else { @@ -607,7 +629,7 @@ int locatePathCaseInsensitive(char *buf) if (*ptr == '\0') return 0; // Uh...I guess that's success? - if (access(buf, F_OK)) + if (access(buf, F_OK) == 0) return 0; // quick rejection: exists in current case. while ( (ptr = strchr(ptr + 1, '/')) != NULL ) { @@ -771,7 +793,7 @@ APIRET DosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, ULONG cbFile, ULON return err; } // if - printf("DosOpen: converted '%s' to '%s'\n", pszFileName, unixpath); + if (strcmp(pszFileName, unixpath) != 0) { printf("DosOpen: converted '%s' to '%s'\n", pszFileName, unixpath); } info->fd = open(unixpath, flags, mode); @@ -840,5 +862,295 @@ APIRET DosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, ULONG cbFile, ULON return NO_ERROR; } // DosOpen +APIRET DosRequestMutexSem(HMTX hmtx, ULONG ulTimeout) +{ + TRACE_NATIVE("DosRequestMutexSem(%u, %u)", (uint) hmtx, (uint) ulTimeout); + FIXME("implement this when we can spin threads"); + return NO_ERROR; +} // DosRequestMutexSem + +APIRET DosReleaseMutexSem(HMTX hmtx) +{ + TRACE_NATIVE("DosReleaseMutexSem(%u)", (uint) hmtx); + FIXME("implement this when we can spin threads"); + return NO_ERROR; +} // DosReleaseMutexSem + +APIRET DosSetFilePtr(HFILE hFile, LONG ib, ULONG method, PULONG ibActual) +{ + TRACE_NATIVE("DosSetFilePtr(%u, %d, %u, %p)", (uint) hFile, (int) ib, (uint) method, ibActual); + + int whence; + switch (method) { + case FILE_BEGIN: whence = SEEK_SET; break; + case FILE_CURRENT: whence = SEEK_CUR; break; + case FILE_END: whence = SEEK_END; break; + default: return ERROR_INVALID_FUNCTION; + } // switch + + if ((hFile >= MaxHFiles) || (HFiles[hFile].fd == -1)) + return ERROR_INVALID_HANDLE; + + const off_t pos = lseek(HFiles[hFile].fd, (off_t) ib, whence); + if (pos == -1) { + if (errno == EINVAL) + return ERROR_NEGATIVE_SEEK; + return ERROR_INVALID_FUNCTION; // !!! FIXME: ? + } // if + + if (ibActual) + *ibActual = (ULONG) pos; + + return NO_ERROR; +} // DosSetFilePtr + +APIRET DosRead(HFILE hFile, PVOID pBuffer, ULONG cbRead, PULONG pcbActual) +{ + TRACE_NATIVE("DosRead(%u, %p, %u, %p)", (uint) hFile, pBuffer, (uint) cbRead, pcbActual); + + if ((hFile >= MaxHFiles) || (HFiles[hFile].fd == -1)) + return ERROR_INVALID_HANDLE; + + const ssize_t br = read(HFiles[hFile].fd, pBuffer, cbRead); + if (br == -1) + return ERROR_INVALID_FUNCTION; // !!! FIXME: ? + + if (pcbActual) + *pcbActual = (ULONG) br; + + return NO_ERROR; +} // DosRead + +APIRET DosClose(HFILE hFile) +{ + TRACE_NATIVE("DosClose(%u)", (uint) hFile); + + if ((hFile >= MaxHFiles) || (HFiles[hFile].fd == -1)) + return ERROR_INVALID_HANDLE; + + const int rc = close(HFiles[hFile].fd); + if (rc == -1) + return ERROR_ACCESS_DENIED; // !!! FIXME: ? + + HFiles[hFile].fd = -1; + HFiles[hFile].type = 0; + HFiles[hFile].attr = 0; + return NO_ERROR; +} // DosClose + +APIRET DosEnterMustComplete(PULONG pulNesting) +{ + TRACE_NATIVE("DosEnterMustComplete(%p)", pulNesting); + + PTIB2 tib2 = getTib2(); + if (tib2->tib2_usMCCount == 0xFFFF) { + return ERROR_NESTING_TOO_DEEP; + } else if (tib2->tib2_usMCCount == 0) { + FIXME("block signals here"); + } + + tib2->tib2_usMCCount++; + + if (pulNesting) + *pulNesting = tib2->tib2_usMCCount; + + return NO_ERROR; +} // DosEnterMustComplete + +APIRET DosExitMustComplete(PULONG pulNesting) +{ + TRACE_NATIVE("DosExitMustComplete(%p)", pulNesting); + + PTIB2 tib2 = getTib2(); + if (tib2->tib2_usMCCount == 0) { + return ERROR_ALREADY_RESET; + } else if (tib2->tib2_usMCCount == 1) { + FIXME("unblock signals here"); + } + + tib2->tib2_usMCCount--; + + if (pulNesting) + *pulNesting = tib2->tib2_usMCCount; + + return NO_ERROR; +} // DosExitMustComplete + +static void unixTimeToOs2(const time_t unixtime, FDATE *os2date, FTIME *os2time) +{ + struct tm lt; + localtime_r(&unixtime, <); + os2date->day = (USHORT) lt.tm_mday; + os2date->month = (USHORT) (lt.tm_mon + 1); + os2date->year = (USHORT) (lt.tm_year - 80); // wants years since 1980, apparently. + os2time->twosecs = (USHORT) (lt.tm_sec / 2); + os2time->minutes = (USHORT) lt.tm_min; + os2time->hours = (USHORT) lt.tm_hour; +} // unixTimeToOs2 + +static APIRET queryFileInfoStandardFromStat(const struct stat *statbuf, PVOID pInfoBuf, ULONG cbInfoBuf) +{ + FILESTATUS3 *st = (FILESTATUS3 *) pInfoBuf; + memset(st, '\0', sizeof (*st)); + + // we don't store creation date on Unix. OS/2 zeroes out fields that aren't + // appropriate to FAT file systems, but I don't want to risk apps using + // zeroes to decide a file in on a FAT disk and reducing features, + // shrinking filenames to 8.3, etc. + st->fdateCreation.day = 1; + st->fdateCreation.month = 1; + st->fdateCreation.year = 0; // years since 1980, apparently. + st->ftimeCreation.twosecs = 0; + st->ftimeCreation.minutes = 0; + st->ftimeCreation.hours = 0; + + unixTimeToOs2(statbuf->st_atime, &st->fdateLastAccess, &st->ftimeLastAccess); + unixTimeToOs2(statbuf->st_mtime, &st->fdateLastWrite, &st->ftimeLastWrite); + + st->cbFile = (ULONG) statbuf->st_size; + st->cbFileAlloc = (ULONG) (statbuf->st_blocks * 512); + + if (S_ISDIR(statbuf->st_mode)) + st->attrFile |= FILE_DIRECTORY; + if (statbuf->st_mode & ~S_IRUSR) // !!! FIXME: not accurate...? + st->attrFile |= FILE_READONLY; + + return NO_ERROR; +} // queryFileInfoStandardFromStat + +static APIRET queryPathInfoStandard(PSZ unixPath, PVOID pInfoBuf, ULONG cbInfoBuf) +{ + if (cbInfoBuf < sizeof (FILESTATUS3)) + return ERROR_BUFFER_OVERFLOW; + + struct stat statbuf; + if (stat(unixPath, &statbuf) == -1) { + return ERROR_PATH_NOT_FOUND; // !!! FIXME + } + + return queryFileInfoStandardFromStat(&statbuf, pInfoBuf, cbInfoBuf); +} // queryPathInfoStandard + +static APIRET queryPathInfoEaSize(PSZ unixPath, PVOID pInfoBuf, ULONG cbInfoBuf) +{ + if (cbInfoBuf < sizeof (FILESTATUS4)) + return ERROR_BUFFER_OVERFLOW; + const APIRET rc = queryPathInfoStandard(unixPath, pInfoBuf, cbInfoBuf); + if (rc != NO_ERROR) + return rc; + + FILESTATUS4 *st = (FILESTATUS4 *) pInfoBuf; + FIXME("write me"); + st->cbList = 0; + return NO_ERROR; +} // queryPathInfoEaSize + +static APIRET queryPathInfoFullName(PSZ unixPath, PVOID pInfoBuf, ULONG cbInfoBuf) +{ + char *real = realpath(unixPath, NULL); + if (real == NULL) + return ERROR_NOT_ENOUGH_MEMORY; // !!! FIXME: no + + if ((strlen(real) + 3) < cbInfoBuf) { + free(real); + return ERROR_BUFFER_OVERFLOW; + } // if + + char *dst = (char *) pInfoBuf; + *(dst++) = 'C'; + *(dst++) = ':'; + strcpy(dst, real); + free(real); + + while (*dst) { + if (*dst == '/') + *dst = '\\'; + dst++; + } // while + + return NO_ERROR; +} // queryPathInfoFullName + +static APIRET queryPathInfoEasFromList(PSZ unixPath, PVOID pInfoBuf, ULONG cbInfoBuf) +{ + FIXME("write me"); + return ERROR_INVALID_LEVEL; +} // queryPathInfoEasFromList + +static APIRET queryPathInfo(PSZ unixPath, ULONG ulInfoLevel, PVOID pInfoBuf, ULONG cbInfoBuf) +{ + switch (ulInfoLevel) { + case FIL_STANDARD: return queryPathInfoStandard(unixPath, pInfoBuf, cbInfoBuf); + case FIL_QUERYEASIZE: return queryPathInfoEaSize(unixPath, pInfoBuf, cbInfoBuf); + case FIL_QUERYEASFROMLIST: return queryPathInfoEasFromList(unixPath, pInfoBuf, cbInfoBuf); + case FIL_QUERYFULLNAME: return queryPathInfoFullName(unixPath, pInfoBuf, cbInfoBuf); + default: break; + } // switch + + return ERROR_INVALID_LEVEL; +} // queryPathInfo + +APIRET DosQueryPathInfo(PSZ pszPathName, ULONG ulInfoLevel, PVOID pInfoBuf, ULONG cbInfoBuf) +{ + TRACE_NATIVE("DosQueryPathInfo('%s', %u, %p, %u)", pszPathName, (uint) ulInfoLevel, pInfoBuf, (uint) cbInfoBuf); + + APIRET err = NO_ERROR; + char *unixPath = makeUnixPath(pszPathName, &err); + if (!unixPath) + return err; + err = queryPathInfo(unixPath, ulInfoLevel, pInfoBuf, cbInfoBuf); + free(unixPath); + return err; +} // DosQueryPathInfo + +static APIRET queryFileInfoStandard(const struct stat *statbuf, PVOID pInfoBuf, ULONG cbInfoBuf) +{ + if (cbInfoBuf < sizeof (FILESTATUS3)) + return ERROR_BUFFER_OVERFLOW; + return queryFileInfoStandardFromStat(statbuf, pInfoBuf, cbInfoBuf); +} // queryFileInfoStandard + +static APIRET queryFileInfoEaSize(const struct stat *statbuf, PVOID pInfoBuf, ULONG cbInfoBuf) +{ + if (cbInfoBuf < sizeof (FILESTATUS4)) + return ERROR_BUFFER_OVERFLOW; + const APIRET rc = queryFileInfoStandard(statbuf, pInfoBuf, cbInfoBuf); + if (rc != NO_ERROR) + return rc; + + FILESTATUS4 *st = (FILESTATUS4 *) pInfoBuf; + FIXME("write me"); + st->cbList = 0; + return NO_ERROR; +} // queryFileInfoEaSize + +static APIRET queryFileInfoEasFromList(const struct stat *statbuf, PVOID pInfoBuf, ULONG cbInfoBuf) +{ + FIXME("write me"); + return ERROR_INVALID_LEVEL; +} // queryFileInfoEasFromList + + +APIRET DosQueryFileInfo(HFILE hf, ULONG ulInfoLevel, PVOID pInfo, ULONG cbInfoBuf) +{ + TRACE_NATIVE("DosQueryFileInfo(%u, %u, %p, %u)", (uint) hf, (uint) ulInfoLevel, pInfo, (uint) cbInfoBuf); + + if ((hf >= MaxHFiles) || (HFiles[hf].fd == -1)) + return ERROR_INVALID_HANDLE; + + struct stat statbuf; + if (fstat(HFiles[hf].fd, &statbuf) == -1) + return ERROR_INVALID_HANDLE; // !!! FIXME: ...? + + switch (ulInfoLevel) { + case FIL_STANDARD: return queryFileInfoStandard(&statbuf, pInfo, cbInfoBuf); + case FIL_QUERYEASIZE: return queryFileInfoEaSize(&statbuf, pInfo, cbInfoBuf); + case FIL_QUERYEASFROMLIST: return queryFileInfoEasFromList(&statbuf, pInfo, cbInfoBuf); + default: break; + } // switch + + return ERROR_INVALID_LEVEL; +} // DosQueryFileInfo + // end of doscalls.c ... diff --git a/native/doscalls.h b/native/doscalls.h index b016bb6..a07ba50 100644 --- a/native/doscalls.h +++ b/native/doscalls.h @@ -166,6 +166,61 @@ enum OPEN_FLAGS_PROTECTED_HANDLE = 0x40000000 }; +enum +{ + FILE_BEGIN, + FILE_CURRENT, + FILE_END +}; + +enum +{ + FIL_STANDARD = 1, + FIL_QUERYEASIZE = 2, + FIL_QUERYEASFROMLIST = 3, + FIL_QUERYFULLNAME = 5 +}; + +typedef struct +{ + USHORT day : 5; + USHORT month : 4; + USHORT year : 7; +} FDATE, *PFDATE; + +typedef struct +{ + USHORT twosecs : 5; + USHORT minutes : 6; + USHORT hours : 5; +} FTIME, *PFTIME; + +typedef struct +{ + FDATE fdateCreation; + FTIME ftimeCreation; + FDATE fdateLastAccess; + FTIME ftimeLastAccess; + FDATE fdateLastWrite; + FTIME ftimeLastWrite; + ULONG cbFile; + ULONG cbFileAlloc; + ULONG attrFile; +} FILESTATUS3, *PFILESTATUS3; + +typedef struct +{ + FDATE fdateCreation; + FTIME ftimeCreation; + FDATE fdateLastAccess; + FTIME ftimeLastAccess; + FDATE fdateLastWrite; + FTIME ftimeLastWrite; + ULONG cbFile; + ULONG cbFileAlloc; + ULONG attrFile; + ULONG cbList; +} FILESTATUS4, *PFILESTATUS4; // !!! FIXME: these should probably get sorted alphabetically and/or grouped // !!! FIXME: into areas of functionality, but for now, I'm just listing them @@ -191,6 +246,15 @@ APIRET OS2API DosQueryHType(HFILE hFile, PULONG pType, PULONG pAttr); APIRET OS2API DosSetMem(PVOID pb, ULONG cb, ULONG flag); APIRET OS2API DosGetDateTime(PDATETIME pdt); APIRET OS2API DosOpen(PSZ pszFileName, PHFILE pHf, PULONG pulAction, ULONG cbFile, ULONG ulAttribute, ULONG fsOpenFlags, ULONG fsOpenMode, PEAOP2 peaop2); +APIRET OS2API DosRequestMutexSem(HMTX hmtx, ULONG ulTimeout); +APIRET OS2API DosReleaseMutexSem(HMTX hmtx); +APIRET OS2API DosSetFilePtr(HFILE hFile, LONG ib, ULONG method, PULONG ibActual); +APIRET OS2API DosRead(HFILE hFile, PVOID pBuffer, ULONG cbRead, PULONG pcbActual); +APIRET OS2API DosClose(HFILE hFile); +APIRET OS2API DosEnterMustComplete(PULONG pulNesting); +APIRET OS2API DosExitMustComplete(PULONG pulNesting); +APIRET OS2API DosQueryPathInfo(PSZ pszPathName, ULONG ulInfoLevel, PVOID pInfoBuf, ULONG cbInfoBuf); +APIRET OS2API DosQueryFileInfo(HFILE hf, ULONG ulInfoLevel, PVOID pInfo, ULONG cbInfoBuf); #ifdef __cplusplus } diff --git a/native/nls.c b/native/nls.c index dc565be..5265e12 100644 --- a/native/nls.c +++ b/native/nls.c @@ -11,8 +11,24 @@ APIRET DosQueryDBCSEnv(ULONG buflen, PCOUNTRYCODE pcc, PCHAR buf) return NO_ERROR; } // DosQueryDBCSEnv +APIRET DosMapCase(ULONG cb, PCOUNTRYCODE pcc, PCHAR pch) +{ + // !!! FIXME: implement this for real. + TRACE_NATIVE("DosMapCase(%u, %p, %p)", (uint) cb, pcc, pch); + if ((pcc->country != 0) || (pcc->codepage != 0)) + return ERROR_CODE_PAGE_NOT_FOUND; + + for (ULONG i = 0; i < cb; i++) { + const CHAR ch = *pch; + *(pch++) = ((ch >= 'A') && (ch <= 'Z')) ? ch - ('A' - 'a') : ch; + } // for + + return NO_ERROR; +} // DosMapCase + LX_NATIVE_MODULE_INIT() - LX_NATIVE_EXPORT(DosQueryDBCSEnv, 6) + LX_NATIVE_EXPORT(DosQueryDBCSEnv, 6), + LX_NATIVE_EXPORT(DosMapCase, 7) LX_NATIVE_MODULE_INIT_END() // end of nls.c ... diff --git a/native/nls.h b/native/nls.h index 6cbadd7..b46c93a 100644 --- a/native/nls.h +++ b/native/nls.h @@ -14,6 +14,7 @@ typedef struct COUNTRYCODE } COUNTRYCODE, *PCOUNTRYCODE; APIRET OS2API DosQueryDBCSEnv(ULONG cb, PCOUNTRYCODE pcc, PCHAR pBuf); +APIRET OS2API DosMapCase(ULONG cb, PCOUNTRYCODE pcc, PCHAR pch); #ifdef __cplusplus }