/*********************************************************************** $Id: wrappers.c 1544 2010-09-30 13:00:59Z gyoung $ Wrappers with error checking Copyright (c) 2006, 2008 Steven H.Levine 22 Jul 06 SHL Baseline 29 Jul 06 SHL Add xgets_stripped 18 Aug 06 SHL Correct Runtime_Error line number report 20 Aug 07 GKY Move #pragma alloc_text to end for OpenWatcom compat 01 Sep 07 GKY Add xDosSetPathInfo to fix case where FS3 buffer crosses 64k boundry 06 Oct 07 SHL Add xDos...() wrappers to support systems wo/large file support (Gregg, Steven) 05 May 08 SHL Add FORTIFY support 25 Dec 08 GKY Add code to allow write verify to be turned off on a per drive basis 17 Jun 09 SHL Correct missing rc set 12 Jul 09 GKY Add xDosQueryAppType and xDosAlloc... to allow FM/2 to load in high memory 15 Nov 09 GKY Rework xDosQueryAppType to remove HIMEM ifdefs ***********************************************************************/ #include #include #include #include #define INCL_WIN #define INCL_DOS #define INCL_DOSERRORS #define INCL_LONGLONG #include #include "fm3dll.h" #include "init.h" // Data declaration(s) #include "wrappers.h" #include "fm3str.h" #include "errutil.h" // Dos_Error... #include "strutil.h" // GetPString #include "command.h" #include "tools.h" #include "avl.h" #include "strips.h" // bstrip #include "fortify.h" // GetPString #include "info.h" // driveflags #include "notebook.h" // fVerify #include "pathutil.h" // MaxComLineStrg // Data definitions static PSZ pszSrcFile = __FILE__; #pragma data_seg(GLOBAL1) BOOL fNoLargeFileSupport; APIRET xDosQueryAppType(PCSZ pszName, PULONG pFlags) { APIRET rc; CHAR szPgm[CCHMAXPATH]; strcpy(szPgm, pszName); rc = DosQueryAppType(szPgm, pFlags); return rc; } APIRET xDosAllocSharedMem(PPVOID ppb, PSZ pszName, ULONG cb, ULONG flag) { APIRET rc; ; rc = DosAllocSharedMem(ppb, pszName, cb, flag | OBJ_ANY); //DbgMsg(pszSrcFile, __LINE__, "ppb %p", *ppb); if (rc) rc = DosAllocSharedMem(ppb, pszName, cb, flag); return rc; } APIRET xDosAllocMem(PPVOID ppb, ULONG cb, ULONG flag, PCSZ pszSrcFile, UINT uiLineNumber) { APIRET rc; rc = DosAllocMem(ppb, cb, flag | OBJ_ANY); //DbgMsg(pszSrcFile, uiLineNumber, "ppb %p %x", *ppb, rc); if (rc) rc = DosAllocMem(ppb, cb, flag); //DbgMsg(pszSrcFile, uiLineNumber, "ppb %p", *ppb); return rc; } APIRET xDosFindFirst(PSZ pszFileSpec, PHDIR phdir, ULONG flAttribute, PVOID pfindbuf, ULONG cbBuf, PULONG pcFileNames, ULONG ulInfoLevel) { APIRET rc; if (fNoLargeFileSupport) { switch (ulInfoLevel) { case FIL_STANDARDL: { FILEFINDBUF3 ffb3; ulInfoLevel = FIL_STANDARD; *pcFileNames = 1; // fixme to support larger counts rc = DosFindFirst(pszFileSpec, phdir, flAttribute, &ffb3, sizeof(ffb3), pcFileNames, ulInfoLevel); if (!rc) { *(PFILEFINDBUF3)pfindbuf = ffb3; // Copy aligned data ((PFILEFINDBUF3L)pfindbuf)->cbFile = ffb3.cbFile; // Copy unaligned data ((PFILEFINDBUF3L)pfindbuf)->cbFileAlloc = ffb3.cbFileAlloc; ((PFILEFINDBUF3L)pfindbuf)->attrFile = ffb3.attrFile; ((PFILEFINDBUF3L)pfindbuf)->cchName = ffb3.cchName; memcpy(((PFILEFINDBUF3L)pfindbuf)->achName, ffb3.achName, ffb3.cchName + 1); } } break; case FIL_QUERYEASIZEL: { FILEFINDBUF4 ffb4; *pcFileNames = 1; // fixme to support larger counts ulInfoLevel = FIL_QUERYEASIZE; rc = DosFindFirst(pszFileSpec, phdir, flAttribute, &ffb4, sizeof(ffb4), pcFileNames, ulInfoLevel); if (!rc) { *(PFILEFINDBUF4)pfindbuf = ffb4; // Copy aligned data ((PFILEFINDBUF4L)pfindbuf)->cbFile = ffb4.cbFile; // Copy unaligned data ((PFILEFINDBUF4L)pfindbuf)->cbFileAlloc = ffb4.cbFileAlloc; ((PFILEFINDBUF4L)pfindbuf)->attrFile = ffb4.attrFile; ((PFILEFINDBUF4L)pfindbuf)->cbList = ffb4.cbList; ((PFILEFINDBUF4L)pfindbuf)->cchName = ffb4.cchName; memcpy(((PFILEFINDBUF4L)pfindbuf)->achName, ffb4.achName, ffb4.cchName + 1); } } break; default: Runtime_Error(pszSrcFile, __LINE__, "ulInfoLevel %u unexpected", ulInfoLevel); rc = ERROR_INVALID_PARAMETER; } // switch } else rc = DosFindFirst(pszFileSpec, phdir, flAttribute, pfindbuf, cbBuf, pcFileNames, ulInfoLevel); return rc; } APIRET xDosFindNext(HDIR hDir, PVOID pfindbuf, ULONG cbfindbuf, PULONG pcFileNames, ULONG ulInfoLevel) { APIRET rc; if (fNoLargeFileSupport) { switch (ulInfoLevel) { case FIL_STANDARDL: { FILEFINDBUF3 ffb3; *pcFileNames = 1; // fixme to support larger counts rc = DosFindNext(hDir, &ffb3, sizeof(ffb3), pcFileNames); if (!rc) { *(PFILEFINDBUF3)pfindbuf = ffb3; // Copy aligned data ((PFILEFINDBUF3L)pfindbuf)->cbFile = ffb3.cbFile; // Copy unaligned data ((PFILEFINDBUF3L)pfindbuf)->cbFileAlloc = ffb3.cbFileAlloc; ((PFILEFINDBUF3L)pfindbuf)->attrFile = ffb3.attrFile; ((PFILEFINDBUF3L)pfindbuf)->cchName = ffb3.cchName; memcpy(((PFILEFINDBUF3L)pfindbuf)->achName, ffb3.achName, ffb3.cchName + 1); } } break; case FIL_QUERYEASIZEL: { FILEFINDBUF4 ffb4; *pcFileNames = 1; // fixme to support larger counts rc = DosFindNext(hDir, &ffb4, sizeof(ffb4), pcFileNames); if (!rc) { *(PFILEFINDBUF4)pfindbuf = ffb4; // Copy aligned data ((PFILEFINDBUF4L)pfindbuf)->cbFile = ffb4.cbFile; // Copy unaligned data ((PFILEFINDBUF4L)pfindbuf)->cbFileAlloc = ffb4.cbFileAlloc; ((PFILEFINDBUF4L)pfindbuf)->attrFile = ffb4.attrFile; ((PFILEFINDBUF4L)pfindbuf)->cbList = ffb4.cbList; ((PFILEFINDBUF4L)pfindbuf)->cchName = ffb4.cchName; memcpy(((PFILEFINDBUF4L)pfindbuf)->achName, ffb4.achName, ffb4.cchName + 1); } } break; default: Runtime_Error(pszSrcFile, __LINE__, "ulInfoLevel %u unexpected", ulInfoLevel); rc = ERROR_INVALID_PARAMETER; } // switch } else rc = DosFindNext(hDir, pfindbuf, cbfindbuf, pcFileNames); return rc; } /** * DosQueryPathInfo wrapper * Translate request for systems without large file support */ APIRET xDosQueryPathInfo (PSZ pszPathName, ULONG ulInfoLevel, PVOID pInfoBuf, ULONG cbInfoBuf) { FILESTATUS3 fs3; FILESTATUS4 fs4; APIRET rc; if (fNoLargeFileSupport) { switch (ulInfoLevel) { case FIL_STANDARDL: rc = DosQueryPathInfo(pszPathName, ulInfoLevel, &fs3, sizeof(fs3)); if (!rc) { *(PFILESTATUS3)pInfoBuf = fs3; // Copy aligned data ((PFILESTATUS3L)pInfoBuf)->cbFile = fs3.cbFile; // Copy unaligned data ((PFILESTATUS3L)pInfoBuf)->cbFileAlloc = fs3.cbFileAlloc; ((PFILESTATUS3L)pInfoBuf)->attrFile = fs3.attrFile; } break; case FIL_QUERYEASIZEL: rc = DosQueryPathInfo(pszPathName, ulInfoLevel, &fs4, sizeof(fs4)); if (!rc) { *(PFILESTATUS4)pInfoBuf = fs4; // Copy aligned data ((PFILESTATUS4L)pInfoBuf)->cbFile = fs4.cbFile; // Copy unaligned data ((PFILESTATUS4L)pInfoBuf)->cbFileAlloc = fs4.cbFileAlloc; ((PFILESTATUS4L)pInfoBuf)->attrFile = fs4.attrFile; ((PFILESTATUS4L)pInfoBuf)->cbList = fs4.cbList; } break; default: Runtime_Error(pszSrcFile, __LINE__, "ulInfoLevel %u unexpected", ulInfoLevel); rc = ERROR_INVALID_PARAMETER; } // switch } else rc = DosQueryPathInfo (pszPathName, ulInfoLevel, pInfoBuf, cbInfoBuf); return rc; } /** * Wrap DosSetPathInfo to avoid spurious ERROR_INVALID_NAME returns and * support systems without large file support * * Some kernels to do not correctly handle FILESTATUS3 and PEAOP2 buffers * that cross a 64K boundary. * When this occurs, they return ERROR_INVALID_NAME. * This code works around the problem because if the passed buffer crosses * the boundary the alternate buffer will not because both are on the stack * and we don't put enough additional data on the stack for this to occur. * It is caller's responsitibility to report errors * @param pInfoBuf pointer to FILESTATUS3(L) or EAOP2 buffer * @param ulInfoLevel FIL_STANDARD(L) or FIL_QUERYEASIZE * @returns Same as DosSetPathInfo */ APIRET xDosSetPathInfo(PSZ pszPathName, ULONG ulInfoLevel, PVOID pInfoBuf, ULONG cbInfoBuf, ULONG flOptions) { FILESTATUS3 fs3; FILESTATUS3 fs3_a; FILESTATUS3L fs3l; EAOP2 eaop2; APIRET rc; BOOL crosses = ((ULONG)pInfoBuf ^ ((ULONG)pInfoBuf + cbInfoBuf - 1)) & ~0xffff; BOOL fResetVerify = FALSE; if (fVerify && driveflags[toupper(*pszPathName) - 'A'] & DRIVE_WRITEVERIFYOFF) { DosSetVerify(FALSE); fResetVerify = TRUE; } switch (ulInfoLevel) { case FIL_STANDARD: if (crosses) { fs3 = *(PFILESTATUS3)pInfoBuf; // Copy to buffer that does not cross 64K boundary rc = DosSetPathInfo(pszPathName, ulInfoLevel, &fs3, cbInfoBuf, flOptions); } else rc = DosSetPathInfo(pszPathName, ulInfoLevel, pInfoBuf, cbInfoBuf, flOptions); break; case FIL_STANDARDL: if (fNoLargeFileSupport) { ulInfoLevel = FIL_STANDARD; fs3 = *(PFILESTATUS3)pInfoBuf; // Copy aligned data // Check size too big to handle if (((PFILESTATUS3L)pInfoBuf)->cbFile >= 1LL << 32 || ((PFILESTATUS3L)pInfoBuf)->cbFileAlloc >= 2LL << 32) { rc = ERROR_INVALID_PARAMETER; } else { fs3.cbFile = ((PFILESTATUS3L)pInfoBuf)->cbFile; // Copy unaligned data fs3.cbFileAlloc = ((PFILESTATUS3L)pInfoBuf)->cbFileAlloc; fs3.attrFile = ((PFILESTATUS3L)pInfoBuf)->attrFile; rc = DosSetPathInfo(pszPathName, ulInfoLevel, &fs3, sizeof(fs3), flOptions); } if (rc == ERROR_INVALID_NAME) { // fixme to validate counts? fs3_a = fs3; // Copy to buffer that does not cross rc = DosSetPathInfo(pszPathName, ulInfoLevel, &fs3_a, sizeof(fs3_a), flOptions); } } else { rc = DosSetPathInfo(pszPathName, ulInfoLevel, pInfoBuf, cbInfoBuf, flOptions); if (rc == ERROR_INVALID_NAME) { fs3l = *(PFILESTATUS3L)pInfoBuf; // Copy to buffer that does not cross rc = DosSetPathInfo(pszPathName, ulInfoLevel, &fs3l, sizeof(fs3l), flOptions); } } break; case FIL_QUERYEASIZE: rc = DosSetPathInfo(pszPathName, ulInfoLevel, pInfoBuf, cbInfoBuf, flOptions); if (rc == ERROR_INVALID_NAME) { // fixme to validate counts? eaop2 = *(PEAOP2)pInfoBuf; // Copy to buffer that does not cross rc = DosSetPathInfo(pszPathName, ulInfoLevel, &eaop2, sizeof(eaop2), flOptions); } break; default: Runtime_Error(pszSrcFile, __LINE__, "ulInfoLevel %u unexpected", ulInfoLevel); rc = ERROR_INVALID_PARAMETER; } // switch if (fResetVerify) { DosSetVerify(fVerify); fResetVerify = FALSE; } return rc; } PSZ xfgets(PSZ pszBuf, size_t cMaxBytes, FILE * fp, PCSZ pszSrcFile, UINT uiLineNumber) { PSZ psz = fgets(pszBuf, cMaxBytes, fp); if (!psz) { if (ferror(fp)) Runtime_Error(pszSrcFile, uiLineNumber, "fgets"); } else { size_t c = strlen(psz); if (c + 1 > cMaxBytes) Runtime_Error(pszSrcFile, uiLineNumber, "buffer overflow"); else if (!c || (psz[c - 1] != '\n' && psz[c - 1] != '\r')) Runtime_Error(pszSrcFile, uiLineNumber, "missing EOL"); } return psz; } PSZ xfgets_bstripcr(PSZ pszBuf, size_t cMaxBytes, FILE * fp, PCSZ pszSrcFile, UINT uiLineNumber) { PSZ psz = xfgets(pszBuf, cMaxBytes, fp, pszSrcFile, uiLineNumber); if (psz) bstripcr(psz); return psz; } /** * Wrapper for fopen it works around DosOpenL's failure to * thunk properly so that fm2 can be loaded in high memory * It also gives the option of reporting file open errors * If fSilent is TRUE it fails silently; if FALSE it produces a * runtime error dialog. Note pszMode must be passed on the stack * to xfopen to avoid the thunking problem. */ FILE *xfopen(PCSZ pszFileName, PCSZ pszMode, PCSZ pszSrcFile, UINT uiLineNumber, BOOL fSilent) { CHAR FileName[CCHMAXPATH]; FILE *fp; strcpy(FileName, pszFileName); fp = fopen(FileName, pszMode); if (!fp && !fSilent) Runtime_Error(pszSrcFile, uiLineNumber, "fopen"); return fp; } /** * Wrapper for _fsopen it works around DosOpenL's failure to * thunk properly so that fm2 can be loaded in high memory * It also gives the option of reporting file open errors * If fSilent is TRUE it fails silently; if FALSE it produces a * runtime error dialog. Note pszMode must be passed on the stack * to xfopen to avoid the thunking problem */ FILE *xfsopen(PCSZ pszFileName, PCSZ pszMode, INT fSharemode, PCSZ pszSrcFile, UINT uiLineNumber, BOOL fSilent) { CHAR FileName[CCHMAXPATH]; FILE *fp; strcpy(FileName, pszFileName); fp = _fsopen(FileName, pszMode, fSharemode); if (!fp && !fSilent) Runtime_Error(pszSrcFile, uiLineNumber, "_fsopen"); return fp; } //== xfree - safe free == VOID xfree(PVOID pv, PCSZ pszSrcFile, UINT uiLineNumber) { if (pv && pv != NullStr) { # ifdef FORTIFY Fortify_free(pv, pszSrcFile, uiLineNumber); # else free(pv); # endif } } //== xmalloc() malloc with error checking == PVOID xmalloc(size_t cBytes, PCSZ pszSrcFile, UINT uiLineNumber) { # ifdef FORTIFY PVOID pv = Fortify_malloc(cBytes, pszSrcFile, uiLineNumber); # else PVOID pv = malloc(cBytes); # endif if (!pv) Runtime_Error(pszSrcFile, uiLineNumber, GetPString(IDS_OUTOFMEMORY)); return pv; } //== xmallocz() malloc and zero with error checking == PVOID xmallocz(size_t cBytes, PCSZ pszSrcFile, UINT uiLineNumber) { PVOID pv = xmalloc(cBytes, pszSrcFile, uiLineNumber); if (pv) memset(pv, 0, cBytes); return pv; } //== xrealloc() realloc with error checking == PVOID xrealloc(PVOID pvIn, size_t cBytes, PCSZ pszSrcFile, UINT uiLineNumber) { if (pvIn != NullStr) { # ifdef FORTIFY PVOID pv = Fortify_realloc(pvIn, cBytes, pszSrcFile, uiLineNumber); # else PVOID pv = realloc(pvIn, cBytes); # endif if (!pv && cBytes) Runtime_Error(pszSrcFile, uiLineNumber, GetPString(IDS_OUTOFMEMORY)); return pv; } else return xmalloc(cBytes, pszSrcFile, uiLineNumber); } //== xstrdup() strdup with error checking == PVOID xstrdup(PCSZ pszIn, PCSZ pszSrcFile, UINT uiLineNumber) { # ifdef FORTIFY PSZ psz = Fortify_strdup(pszIn, pszSrcFile, uiLineNumber); # else PSZ psz = strdup(pszIn); # endif if (!psz) Runtime_Error(pszSrcFile, uiLineNumber, GetPString(IDS_OUTOFMEMORY)); return psz; } #pragma alloc_text(WRAPPERS1,xfree,xfopen,xfsopen,xmalloc,xrealloc,xstrdup) #pragma alloc_text(WRAPPERS2,xDosSetPathInfo,xDosFindFirst,xDosFindNext)