summaryrefslogtreecommitdiffstats
path: root/src/lib/kStuff/kRdr
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/kStuff/kRdr')
-rw-r--r--src/lib/kStuff/kRdr/Makefile.kmk48
-rw-r--r--src/lib/kStuff/kRdr/kRdr.cpp281
-rw-r--r--src/lib/kStuff/kRdr/kRdrBuffered.cpp750
-rw-r--r--src/lib/kStuff/kRdr/kRdrFile.cpp1308
-rw-r--r--src/lib/kStuff/kRdr/kRdrInternal.h122
5 files changed, 2509 insertions, 0 deletions
diff --git a/src/lib/kStuff/kRdr/Makefile.kmk b/src/lib/kStuff/kRdr/Makefile.kmk
new file mode 100644
index 0000000..357acf6
--- /dev/null
+++ b/src/lib/kStuff/kRdr/Makefile.kmk
@@ -0,0 +1,48 @@
+# $Id: Makefile.kmk 29 2009-07-01 20:30:29Z bird $
+## @file
+# kRdr - The File Provider, sub-makefile.
+#
+
+#
+# Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+#
+# Permission is hereby granted, free of charge, to any person
+# obtaining a copy of this software and associated documentation
+# files (the "Software"), to deal in the Software without
+# restriction, including without limitation the rights to use,
+# copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following
+# conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+# OTHER DEALINGS IN THE SOFTWARE.
+#
+
+DEPTH ?= ..
+SUB_DEPTH = ..
+include $(PATH_KBUILD)/subheader.kmk
+
+#
+# kRdrStatic - The file provider module.
+#
+LIBRARIES += kRdrStatic
+kRdrStatic_TEMPLATE = kStuffLIB
+kRdrStatic_DEFS = KDBG_BUILDING
+kRdrStatic_SOURCES = \
+ kRdr.cpp \
+ kRdrFile.cpp \
+ kRdrBuffered.cpp
+
+# Generate the rules
+include $(PATH_KBUILD)/subfooter.kmk
+
diff --git a/src/lib/kStuff/kRdr/kRdr.cpp b/src/lib/kStuff/kRdr/kRdr.cpp
new file mode 100644
index 0000000..5952cb1
--- /dev/null
+++ b/src/lib/kStuff/kRdr/kRdr.cpp
@@ -0,0 +1,281 @@
+/* $Id: kRdr.cpp 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kRdr - The File Provider.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "kRdrInternal.h"
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** The list of file providers. */
+static PCKRDROPS g_pRdrHead = &g_kRdrFileOps;
+
+
+/**
+ * Adds a new file provider.
+ *
+ * @param pAdd The new file provider.
+ */
+KRDR_DECL(void) kRdrAddProvider(PKRDROPS pAdd)
+{
+ pAdd->pNext = g_pRdrHead;
+ g_pRdrHead = pAdd;
+}
+
+
+/**
+ * Tries to opens a file.
+ *
+ * @returns 0 on success, OS status code on failure.
+ * @param ppRdr Where to store the file provider instance.
+ * @param pszFilename The filename.
+ */
+KRDR_DECL(int) kRdrOpen(PPKRDR ppRdr, const char *pszFilename)
+{
+ int rc = -1;
+ PCKRDROPS pCur;
+ for (pCur = g_pRdrHead; pCur; pCur = pCur->pNext)
+ {
+ rc = pCur->pfnCreate(ppRdr, pszFilename);
+ if (!rc)
+ return 0;
+ }
+ return rc;
+}
+
+
+/**
+ * Closes the file.
+ *
+ * @returns 0 on success, OS specific error code on failure.
+ * On failure, the file provider instance will be in an indeterminate state - don't touch it!
+ * @param pRdr The file provider instance.
+ */
+KRDR_DECL(int) kRdrClose(PKRDR pRdr)
+{
+ KRDR_VALIDATE(pRdr);
+ return pRdr->pOps->pfnDestroy(pRdr);
+}
+
+
+/** Read bits from the file.
+ *
+ * @returns 0 on success, OS specific error code on failure.
+ * @param pRdr The file provider instance.
+ * @param pvBuf Where to put the bits.
+ * @param cb The number of bytes to read.
+ * @param off Where to start reading.
+ */
+KRDR_DECL(int) kRdrRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off)
+{
+ KRDR_VALIDATE(pRdr);
+ return pRdr->pOps->pfnRead(pRdr, pvBuf, cb, off);
+}
+
+
+/** Map all the file bits into memory (read only).
+ *
+ * @returns 0 on success, OS specific error code on failure.
+ * @param pRdr The file provider instance.
+ * @param ppvBits Where to store the address of the mapping.
+ * The size can be obtained using pfnSize.
+ */
+KRDR_DECL(int) kRdrAllMap(PKRDR pRdr, const void **ppvBits)
+{
+ KRDR_VALIDATE(pRdr);
+ return pRdr->pOps->pfnAllMap(pRdr, ppvBits);
+}
+
+
+/** Unmap a file bits mapping obtained by KRDROPS::pfnAllMap.
+ *
+ * @returns 0 on success, OS specific error code on failure.
+ * @param pRdr The file provider instance.
+ * @param pvBits The mapping address.
+ */
+KRDR_DECL(int) kRdrAllUnmap(PKRDR pRdr, const void *pvBits)
+{
+ KRDR_VALIDATE(pRdr);
+ return pRdr->pOps->pfnAllUnmap(pRdr, pvBits);
+}
+
+
+/** Get the file size.
+ *
+ * @returns The file size. Returns -1 on failure.
+ * @param pRdr The file provider instance.
+ */
+KRDR_DECL(KFOFF) kRdrSize(PKRDR pRdr)
+{
+ KRDR_VALIDATE(pRdr);
+ return pRdr->pOps->pfnSize(pRdr);
+}
+
+
+/** Get the file pointer offset.
+ *
+ * @returns The file pointer offset. Returns -1 on failure.
+ * @param pRdr The file provider instance.
+ */
+KRDR_DECL(KFOFF) kRdrTell(PKRDR pRdr)
+{
+ KRDR_VALIDATE(pRdr);
+ return pRdr->pOps->pfnTell(pRdr);
+}
+
+
+/** Get the file name.
+ *
+ * @returns The file name. Returns NULL on failure.
+ * @param pRdr The file provider instance.
+ */
+KRDR_DECL(const char *) kRdrName(PKRDR pRdr)
+{
+ KRDR_VALIDATE_EX(pRdr, NULL);
+ return pRdr->pOps->pfnName(pRdr);
+}
+
+
+/** Get the native file handle if possible.
+ *
+ * @returns The native file handle. Returns -1 if not available.
+ * @param pRdr The file provider instance.
+ */
+KRDR_DECL(KIPTR) kRdrNativeFH(PKRDR pRdr)
+{
+ KRDR_VALIDATE_EX(pRdr, -1);
+ return pRdr->pOps->pfnNativeFH(pRdr);
+}
+
+
+/**
+ * Gets the page size used when mapping sections of the file.
+ *
+ * @returns The page size.
+ * @param pRdr The file provider instance.
+ */
+KRDR_DECL(KSIZE) kRdrPageSize(PKRDR pRdr)
+{
+ KRDR_VALIDATE_EX(pRdr, 0x10000);
+ return pRdr->pOps->pfnPageSize(pRdr);
+}
+
+
+/**
+ * Maps the segments of a image into memory.
+ *
+ * The file reader will be using the RVA member of each segment to figure out where
+ * it goes relative to the image base address.
+ *
+ * @returns 0 on success, OS specific error code on failure.
+ * @param pRdr The file provider instance.
+ * @param ppvBase On input when fFixed is set, this contains the base address of the mapping.
+ * On output this contains the base of the image mapping.
+ * @param cSegments The number of segments in the array pointed to by paSegments.
+ * @param paSegments The segments thats going to be mapped.
+ * @param fFixed If set, the address at *ppvBase should be the base address of the mapping.
+ */
+KRDR_DECL(int) kRdrMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
+{
+ KRDR_VALIDATE(pRdr);
+ return pRdr->pOps->pfnMap(pRdr, ppvBase, cSegments, paSegments, fFixed);
+}
+
+
+/**
+ * Reloads dirty pages in mapped image.
+ *
+ * @returns 0 on success, OS specific error code on failure.
+ * @param pRdr The file provider instance.
+ * @param pvBase The base address of the image mapping.
+ * @param cSegments The number of segments in the array pointed to by paSegments.
+ * @param paSegments The segments thats going to be mapped.
+ */
+KRDR_DECL(int) kRdrRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
+{
+ KRDR_VALIDATE(pRdr);
+ return pRdr->pOps->pfnRefresh(pRdr, pvBase, cSegments, paSegments);
+}
+
+
+/**
+ * Protects or unprotects an image mapping.
+ *
+ * This is typically used for getting write access to read or execute only
+ * pages while applying fixups.
+ *
+ * @returns 0 on success, OS specific error code on failure.
+ * @param pRdr The file provider instance.
+ * @param pvBase The base address of the image mapping.
+ * @param cSegments The number of segments in the array pointed to by paSegments.
+ * @param paSegments The segments thats going to be mapped.
+ * @param fUnprotectOrProtect When set the all mapped segments are made writable.
+ * When clean the segment protection is restored.
+ */
+KRDR_DECL(int) kRdrProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
+{
+ KRDR_VALIDATE(pRdr);
+ return pRdr->pOps->pfnProtect(pRdr, pvBase, cSegments, paSegments, fUnprotectOrProtect);
+}
+
+
+/**
+ * Unmaps a image mapping.
+ *
+ * @returns 0 on success, OS specific error code on failure.
+ * @param pRdr The file provider instance.
+ * @param pvBase The base address of the image mapping.
+ * @param cSegments The number of segments in the array pointed to by paSegments.
+ * @param paSegments The segments thats going to be mapped.
+ */
+KRDR_DECL(int) kRdrUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
+{
+ KRDR_VALIDATE(pRdr);
+ return pRdr->pOps->pfnUnmap(pRdr, pvBase, cSegments, paSegments);
+}
+
+
+/**
+ * We're done reading from the file but would like to keep file mappings.
+ *
+ * If the OS support closing the file handle while the file is mapped,
+ * the reader should do so.
+ *
+ * @param pRdr The file provider instance.
+ */
+KRDR_DECL(void) kRdrDone(PKRDR pRdr)
+{
+ KRDR_VALIDATE_VOID(pRdr);
+ pRdr->pOps->pfnDone(pRdr);
+}
+
diff --git a/src/lib/kStuff/kRdr/kRdrBuffered.cpp b/src/lib/kStuff/kRdr/kRdrBuffered.cpp
new file mode 100644
index 0000000..fc589cd
--- /dev/null
+++ b/src/lib/kStuff/kRdr/kRdrBuffered.cpp
@@ -0,0 +1,750 @@
+/* $Id: kRdrBuffered.cpp 79 2016-07-27 14:25:09Z bird $ */
+/** @file
+ * kRdrBuffered - Buffered File Provider.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "kRdrInternal.h"
+#include <k/kHlpAlloc.h>
+#include <k/kHlpString.h>
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * The buffered file provier instance.
+ * This is just a wrapper around another file provider.
+ */
+typedef struct KRDRBUF
+{
+ /** The file reader vtable. */
+ KRDR Core;
+ /** The actual file provider that we're wrapping. */
+ PKRDR pRdr;
+ /** The current file offset. */
+ KFOFF offFile;
+ /** The file size. */
+ KFOFF cbFile;
+ /** The offset of the buffer. */
+ KFOFF offBuf;
+ /** The offset of the end of the buffer. */
+ KFOFF offBufEnd;
+ /** The number of valid buffer bytes. */
+ KSIZE cbBufValid;
+ /** The size of the buffer. */
+ KSIZE cbBuf;
+ /** The buffer. */
+ KU8 *pbBuf;
+ /** Whether the pRdr instance should be closed together with us or not. */
+ KBOOL fCloseIt;
+ /** Set if the buffer has been messed up by kRdrBufLineQ. */
+ KBOOL fTainedByLineQ;
+} KRDRBUF, *PKRDRBUF;
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static void krdrBufDone(PKRDR pRdr);
+static int krdrBufUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
+static int krdrBufProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect);
+static int krdrBufRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
+static int krdrBufMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed);
+static KSIZE krdrBufPageSize(PKRDR pRdr);
+static const char *krdrBufName(PKRDR pRdr);
+static KIPTR krdrBufNativeFH(PKRDR pRdr);
+static KFOFF krdrBufTell(PKRDR pRdr);
+static KFOFF krdrBufSize(PKRDR pRdr);
+static int krdrBufAllUnmap(PKRDR pRdr, const void *pvBits);
+static int krdrBufAllMap(PKRDR pRdr, const void **ppvBits);
+static int krdrBufRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off);
+static int krdrBufDestroy(PKRDR pRdr);
+static int krdrBufCreate(PPKRDR ppRdr, const char *pszFilename);
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** Native file provider operations.
+ *
+ * @remark This is not in the file provider list as its intended for wrapping
+ * other kRdr instances.
+ */
+static const KRDROPS g_krdrBufOps =
+{
+ "Buffered kRdr",
+ NULL,
+ krdrBufCreate,
+ krdrBufDestroy,
+ krdrBufRead,
+ krdrBufAllMap,
+ krdrBufAllUnmap,
+ krdrBufSize,
+ krdrBufTell,
+ krdrBufName,
+ krdrBufNativeFH,
+ krdrBufPageSize,
+ krdrBufMap,
+ krdrBufRefresh,
+ krdrBufProtect,
+ krdrBufUnmap,
+ krdrBufDone,
+ 42
+};
+
+
+/** @copydoc KRDROPS::pfnDone */
+static void krdrBufDone(PKRDR pRdr)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->pRdr->pOps->pfnDone(pThis->pRdr);
+}
+
+
+/** @copydoc KRDROPS::pfnUnmap */
+static int krdrBufUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->pRdr->pOps->pfnUnmap(pThis->pRdr, pvBase, cSegments, paSegments);
+}
+
+
+/** @copydoc KRDROPS::pfnProtect */
+static int krdrBufProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->pRdr->pOps->pfnProtect(pThis->pRdr, pvBase, cSegments, paSegments, fUnprotectOrProtect);
+}
+
+
+/** @copydoc KRDROPS::pfnRefresh */
+static int krdrBufRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->pRdr->pOps->pfnRefresh(pThis->pRdr, pvBase, cSegments, paSegments);
+}
+
+
+/** @copydoc KRDROPS::pfnMap */
+static int krdrBufMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->pRdr->pOps->pfnMap(pThis->pRdr, ppvBase, cSegments, paSegments, fFixed);
+}
+
+
+/** @copydoc KRDROPS::pfnPageSize */
+static KSIZE krdrBufPageSize(PKRDR pRdr)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->pRdr->pOps->pfnPageSize(pThis->pRdr);
+}
+
+
+/** @copydoc KRDROPS::pfnName */
+static const char *krdrBufName(PKRDR pRdr)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->pRdr->pOps->pfnName(pThis->pRdr);
+}
+
+
+/** @copydoc KRDROPS::pfnNativeFH */
+static KIPTR krdrBufNativeFH(PKRDR pRdr)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->pRdr->pOps->pfnNativeFH(pThis->pRdr);
+}
+
+
+/** @copydoc KRDROPS::pfnTell */
+static KFOFF krdrBufTell(PKRDR pRdr)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->offFile;
+}
+
+
+/** @copydoc KRDROPS::pfnSize */
+static KFOFF krdrBufSize(PKRDR pRdr)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->cbFile;
+}
+
+
+/** @copydoc KRDROPS::pfnAllUnmap */
+static int krdrBufAllUnmap(PKRDR pRdr, const void *pvBits)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->pRdr->pOps->pfnAllUnmap(pThis->pRdr, pvBits);
+}
+
+
+/** @copydoc KRDROPS::pfnAllMap */
+static int krdrBufAllMap(PKRDR pRdr, const void **ppvBits)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ return pThis->pRdr->pOps->pfnAllMap(pThis->pRdr, ppvBits);
+}
+
+
+/**
+ * Fills the buffer with file bits starting at the specified offset.
+ *
+ * @returns 0 on success, pfnRead error code on failure.
+ * @param pThis The instance.
+ * @param off Where to start reading.
+ */
+static int krdrBufFillBuffer(PKRDRBUF pThis, KFOFF off)
+{
+ kRdrAssert(off < pThis->cbFile);
+
+ /* Reposition the buffer if it's past the end of the file so that
+ we maximize its usability. We leave one unused byte at the end
+ of the buffer so kRdrBufLineQ can terminate its string properly.
+ Of course, this might end up re-reading a lot of stuff for no
+ future gain, but whatever... */
+ kRdrAssert(pThis->cbBuf <= pThis->cbFile + 1);
+ KFOFF cbLeft = pThis->cbFile - off;
+ KSIZE cbRead = pThis->cbBuf;
+ if ((KSSIZE)cbRead - 1 >= cbLeft)
+ {
+ cbRead--;
+ off = pThis->cbFile - cbRead;
+ }
+ int rc = pThis->pRdr->pOps->pfnRead(pThis->pRdr, pThis->pbBuf, cbRead, off);
+ if (!rc)
+ {
+ pThis->offBuf = off;
+ pThis->offBufEnd = off + cbRead;
+ pThis->cbBufValid = cbRead;
+ }
+ else
+ {
+ pThis->offBuf = pThis->offBufEnd = 0;
+ pThis->cbBufValid = 0;
+ }
+ pThis->fTainedByLineQ = K_FALSE;
+ return rc;
+}
+
+
+/** @copydoc KRDROPS::pfnRead */
+static int krdrBufRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+
+ /*
+ * We need to validate and update the file offset before
+ * we start making partial reads from the buffer and stuff.
+ */
+ KFOFF offEnd = off + cb;
+ if ( off >= pThis->cbFile
+ || offEnd > pThis->cbFile
+ || offEnd < off)
+ return KERR_OUT_OF_RANGE; /* includes EOF. */
+ pThis->offFile = offEnd;
+ if (!cb)
+ return 0;
+
+ /*
+ * Scratch the buffer if kRdrBufLineQ has tained it.
+ */
+ if (pThis->fTainedByLineQ)
+ {
+ pThis->offBuf = pThis->offBufEnd = 0;
+ pThis->cbBufValid = 0;
+ }
+
+ /*
+ * Is any part of the request in the buffer?
+ *
+ * We will currently ignore buffer hits in the middle of the
+ * request because it's annoying to implement and it's
+ * questionable whether it'll benefit much performance wise.
+ */
+ if (pThis->cbBufValid > 0)
+ {
+ if (off >= pThis->offBuf)
+ {
+ if (off < pThis->offBufEnd)
+ {
+ /* head (or all) of the request is in the buffer. */
+ KSIZE cbMaxChunk = (KSIZE)(pThis->offBufEnd - off);
+ KSIZE cbChunk = K_MIN(cb, cbMaxChunk);
+ kHlpMemCopy(pvBuf, &pThis->pbBuf[off - pThis->offBuf], cbChunk);
+ if (cbChunk == cb)
+ return 0;
+
+ cb -= cbChunk;
+ pvBuf = (KU8 *)pvBuf + cbChunk;
+ off += cbChunk;
+ }
+ }
+ else if ( offEnd > pThis->offBuf
+ && offEnd <= pThis->offBufEnd)
+ {
+ /* the end of the request is in the buffer. */
+ KSIZE cbChunk = (KSIZE)(pThis->offBufEnd - (offEnd));
+ kHlpMemCopy((KU8 *)pvBuf + (pThis->offBuf - off), pThis->pbBuf, cbChunk);
+ kRdrAssert(cbChunk < cb);
+ cb -= cbChunk;
+ offEnd -= cbChunk;
+ }
+ }
+
+ /*
+ * If the buffer is larger than the read request, read a full buffer
+ * starting at the requested offset. Otherwise perform an unbuffered
+ * read.
+ */
+ if (pThis->cbBuf > cb)
+ {
+ int rc = krdrBufFillBuffer(pThis, off);
+ if (rc)
+ return rc;
+ if (pThis->offBuf == off)
+ kHlpMemCopy(pvBuf, pThis->pbBuf, cb);
+ else
+ {
+ kRdrAssert(off > pThis->offBuf);
+ kRdrAssert(off + cb <= pThis->offBufEnd);
+ kHlpMemCopy(pvBuf, pThis->pbBuf + (off - pThis->offBuf), cb);
+ }
+ }
+ else
+ {
+ int rc = pThis->pRdr->pOps->pfnRead(pThis->pRdr, pvBuf, cb, off);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+
+/** @copydoc KRDROPS::pfnDestroy */
+static int krdrBufDestroy(PKRDR pRdr)
+{
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+
+ /* Close the kRdr instance that we're wrapping. */
+ if (pThis->fCloseIt)
+ {
+ int rc = pThis->pRdr->pOps->pfnDestroy(pThis->pRdr);
+ if (rc)
+ return rc;
+ pThis->fCloseIt = K_FALSE;
+ pThis->pRdr = NULL;
+ }
+
+ kHlpFree(pThis->pbBuf);
+ pThis->pbBuf = NULL;
+ kHlpFree(pRdr);
+ return 0;
+}
+
+
+/** @copydoc KRDROPS::pfnCreate */
+static int krdrBufCreate(PPKRDR ppRdr, const char *pszFilename)
+{
+ K_NOREF(ppRdr);
+ K_NOREF(pszFilename);
+ return KERR_NOT_IMPLEMENTED;
+}
+
+
+/**
+ * Worker for kRdrBufOpen and kRdrBufWrap.
+ *
+ * It's essentially kRdrBufWrap without error checking.
+ *
+ * @returns 0 on success, one of the kErrors status code on failure.
+ * @param ppRdr Where to store the new file provider instance.
+ * @param pRdrWrapped The file provider instance to buffer.
+ * @param fCloseIt Whether it the pRdrWrapped instance should be closed
+ * when the new instance is closed.
+ */
+static int krdrBufWrapIt(PPKRDR ppRdr, PKRDR pRdrWrapped, KBOOL fCloseIt)
+{
+ PKRDRBUF pThis = (PKRDRBUF)kHlpAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->Core.u32Magic = KRDR_MAGIC;
+ pThis->Core.pOps = &g_krdrBufOps;
+ pThis->pRdr = pRdrWrapped;
+ pThis->offFile = pRdrWrapped->pOps->pfnTell(pRdrWrapped);
+ pThis->cbFile = pRdrWrapped->pOps->pfnSize(pRdrWrapped);
+ pThis->offBuf = pThis->offBufEnd = 0;
+ pThis->cbBufValid = 0;
+ pThis->fCloseIt = fCloseIt;
+ pThis->fTainedByLineQ = K_FALSE;
+ if (pThis->cbFile < 128*1024)
+ pThis->cbBuf = (KSIZE)pThis->cbFile + 1; /* need space for the kRdrBufLineQ terminator. */
+ else
+ pThis->cbBuf = 64*1024;
+ pThis->pbBuf = (KU8 *)kHlpAlloc(pThis->cbBuf);
+ if (pThis->pbBuf)
+ {
+ *ppRdr = &pThis->Core;
+ return 0;
+ }
+
+ pThis->Core.u32Magic = 0;
+ kHlpFree(pThis);
+ }
+ return KERR_NO_MEMORY;
+}
+
+
+/**
+ * Opens a file provider with a buffered wrapper.
+ *
+ * @returns 0 on success, KERR_* on failure.
+ * @param ppRdr Where to store the buffered file reader instance on success.
+ * @param pszFilename The name of the file that should be opened.
+ */
+KRDR_DECL(int) kRdrBufOpen(PPKRDR ppRdr, const char *pszFilename)
+{
+ kRdrAssertPtrReturn(ppRdr, KERR_INVALID_POINTER);
+ *ppRdr = NULL;
+
+ PKRDR pRdrWrapped;
+ int rc = kRdrOpen(&pRdrWrapped, pszFilename);
+ if (!rc)
+ {
+ rc = krdrBufWrapIt(ppRdr, pRdrWrapped, K_TRUE);
+ if (rc)
+ kRdrClose(pRdrWrapped);
+ }
+ return rc;
+}
+
+
+/**
+ * Creates a buffered file provider instance for an existing one.
+ *
+ * @returns 0 on success, KERR_* on failure.
+ * @param ppRdr Where to store the new file provider pointer.
+ * @param pRdr The file provider instance to wrap.
+ * @param fCLoseIt Whether it the wrapped reader should be automatically
+ * closed when the wrapper closes.
+ */
+KRDR_DECL(int) kRdrBufWrap(PPKRDR ppRdr, PKRDR pRdr, KBOOL fCloseIt)
+{
+ KRDR_VALIDATE(pRdr);
+ return krdrBufWrapIt(ppRdr, pRdr, fCloseIt);
+}
+
+
+/**
+ * Checks whether the file provider instance is of the buffered type or not.
+ *
+ * @returns K_TRUE if it is, otherwise K_FALSE.
+ * @param pRdr The file provider instance to check.
+ */
+KRDR_DECL(KBOOL) kRdrBufIsBuffered(PKRDR pRdr)
+{
+ KRDR_VALIDATE_EX(pRdr, K_FALSE);
+ return pRdr->pOps == &g_krdrBufOps;
+}
+
+
+/**
+ * Reads a line from a buffered file provider.
+ *
+ * The trailing '\n' or '\r\n' is stripped.
+ *
+ * @returns 0 on success. KERR_* on failure.
+ * @retval KRDR_ERR_LINE_TOO_LONG if the line is too long to fit in the passed in buffer.
+ * @retval KRDR_ERR_NOT_BUFFERED_RDR if pRdr isn't a buffered reader.
+ * @param pRdr The buffered file reader.
+ * @param pszLine Where to store the line.
+ * @param cbLine The size of the the line buffer.
+ */
+KRDR_DECL(int) kRdrBufLine(PKRDR pRdr, char *pszLine, KSIZE cbLine)
+{
+ return kRdrBufLineEx(pRdr, pszLine, &cbLine);
+}
+
+
+/**
+ * Reads a line from a buffered file provider.
+ *
+ * The trailing '\n' or '\r\n' is stripped.
+ *
+ * @returns 0 on success. KERR_* on failure.
+ * @retval KRDR_ERR_LINE_TOO_LONG if the line is too long to fit in the passed in buffer.
+ * @retval KRDR_ERR_NOT_BUFFERED_RDR if pRdr isn't a buffered reader.
+ * @param pRdr The buffered file reader.
+ * @param pszLine Where to store the line.
+ * @param pcbLine The size of the the line buffer on input, the length of the
+ * returned line on output.
+ */
+KRDR_DECL(int) kRdrBufLineEx(PKRDR pRdr, char *pszLine, KSIZE *pcbLine)
+{
+ /*
+ * Validate input.
+ */
+ kRdrAssertPtrReturn(pcbLine, KERR_INVALID_POINTER);
+ KSIZE cbLeft = *pcbLine;
+ *pcbLine = 0;
+ kRdrAssertReturn(cbLeft > 0, KERR_INVALID_PARAMETER);
+ KRDR_VALIDATE(pRdr);
+ kRdrAssertReturn(pRdr->pOps != &g_krdrBufOps, KRDR_ERR_NOT_BUFFERED_RDR);
+ kRdrAssertPtrReturn(pszLine, KERR_INVALID_POINTER);
+
+ /* check for EOF */
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ if (pThis->offFile >= pThis->cbFile)
+ {
+ kRdrAssert(pThis->offFile == pThis->cbFile);
+ *pszLine = '\0';
+ *pcbLine = 0;
+ return KERR_EOF;
+ }
+
+ /*
+ * Scratch the buffer if kRdrBufLineQ has tained it.
+ */
+ if (pThis->fTainedByLineQ)
+ {
+ pThis->offBuf = pThis->offBufEnd = 0;
+ pThis->cbBufValid = 0;
+ }
+
+ /*
+ * Buffered read loop.
+ *
+ * The overflow logic is a bit fishy wrt to overflowing at an "\r\n"
+ * that arrives at a buffer boundrary. The current policy is to try
+ * our best to not to fail with overflow in the EOL sequence or EOF.
+ * If it's the end of the buffer, it will not be refilled just to
+ * check for this because that's too much work.
+ */
+ cbLeft--; /* reserve space for the terminator. */
+ char *pszOut = pszLine;
+ for (;;)
+ {
+ /*
+ * Do we need to (re-)fill the buffer or does it contain something
+ * that we can work on already?
+ */
+ if ( !pThis->cbBufValid
+ || pThis->offFile >= pThis->offBufEnd
+ || pThis->offFile < pThis->offBuf)
+ {
+ int rc = krdrBufFillBuffer(pThis, pThis->offFile);
+ if (rc)
+ {
+ *pszOut = '\0';
+ return rc;
+ }
+ }
+
+ /*
+ * Parse the buffer looking for the EOL indicator.
+ */
+ kRdrAssert(pThis->offFile >= pThis->offBuf && pThis->offFile < pThis->offBufEnd);
+ kRdrAssert(sizeof(char) == sizeof(*pThis->pbBuf));
+ const char * const pszStart = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
+ const char * const pszEnd = (const char *)&pThis->pbBuf[pThis->cbBufValid];
+ const char *psz = pszStart;
+ while (psz < pszEnd)
+ {
+ const char ch = *psz;
+ if (ch == '\n')
+ {
+ /* found the EOL, update file position and line length. */
+ pThis->offFile += psz - pszStart + 1;
+ *pcbLine += psz - pszStart;
+
+ /* terminate the string, checking for "\r\n" first. */
+ if ( *pcbLine
+ && pszOut[-1] == '\r')
+ {
+ *pcbLine -= 1;
+ pszOut--;
+ }
+ *pszOut = '\0';
+ return 0;
+ }
+ if (!cbLeft)
+ {
+ /* the line is *probably* too long. */
+ pThis->offFile += psz - pszStart;
+ *pcbLine += psz - pszStart;
+ *pszOut = '\0';
+
+ /* The only possible case where the line actually isn't too long
+ is if we're at a "\r\n" sequence. We will re-fill the buffer
+ if necessary to check for the '\n' as it's not that much work. */
+ if ( ch == '\r'
+ && pThis->offFile + 2 <= pThis->cbFile)
+ {
+ if (psz + 1 >= pszEnd)
+ {
+ int rc = krdrBufFillBuffer(pThis, pThis->offFile);
+ if (rc)
+ {
+ *pszOut = '\0';
+ return rc;
+ }
+ }
+ psz = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
+ kRdrAssert(*psz == '\r');
+ if (psz[1] == '\n')
+ {
+ *pcbLine -= 1;
+ pszOut[-1] = '\0';
+ pThis->offFile += 2;
+ return 0;
+ }
+ }
+ return KRDR_ERR_LINE_TOO_LONG;
+ }
+
+ /* copy and advance */
+ *pszOut++ = ch;
+ cbLeft--;
+ psz++;
+ }
+
+ /* advance past the buffer and check for EOF. */
+ *pcbLine += pszEnd - pszStart;
+ pThis->offFile = pThis->offBufEnd;
+ if (pThis->offFile >= pThis->cbFile)
+ {
+ kRdrAssert(pThis->offFile == pThis->cbFile);
+ *pszOut = '\0';
+ return 0;
+ }
+ }
+}
+
+
+/**
+ * Worker for kRdrBufLineQ that searches the current buffer for EOL or EOF.
+ *
+ * When a EOF marker is found
+ *
+ *
+ * @returns NULL if EOL/EOF isn't found the buffer.
+ * @param pThis The buffered reader instance.
+ */
+static const char * krdrBufLineQWorker(PKRDRBUF pThis)
+{
+ kRdrAssert(pThis->offFile >= pThis->offBuf && pThis->offFile < pThis->offBufEnd);
+
+ /*
+ * Search the buffer.
+ */
+ kRdrAssert(sizeof(char) == sizeof(*pThis->pbBuf));
+ const char * const pszStart = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf];
+ const char * const pszEnd = (const char *)&pThis->pbBuf[pThis->cbBufValid];
+ char *psz = (char *)pszStart;
+ while (psz < pszEnd)
+ {
+ char ch = *psz;
+ if (ch == '\n')
+ {
+ pThis->offFile += psz - pszStart;
+ pThis->fTainedByLineQ = K_TRUE;
+ *psz = '\0';
+ if ( psz > pszStart
+ && psz[-1] == '\r')
+ *--psz = '\0';
+ return pszStart;
+ }
+ psz++;
+ }
+
+ /*
+ * Check for EOF. There must be room for a terminator char here.
+ */
+ if ( pThis->offBufEnd >= pThis->cbFile
+ && (pThis->offBufEnd - pThis->offBuf) < (KSSIZE)pThis->cbBuf)
+ {
+ pThis->offFile = pThis->cbFile;
+ pThis->pbBuf[pThis->cbBufValid] = '\0';
+ return pszStart;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * Get the pointer to the next next line in the buffer.
+ * The returned line is zero terminated.
+ *
+ * @returns A pointer to the line on success. This becomes invalid
+ * upon the next call to this kRdr instance.
+ * @returns NULL on EOF, read error of if the line was too long.
+ * @param pRdr The buffered file reader.
+ */
+KRDR_DECL(const char *) kRdrBufLineQ(PKRDR pRdr)
+{
+ /*
+ * Validate input.
+ */
+ KRDR_VALIDATE_EX(pRdr, NULL);
+ kRdrAssertReturn(pRdr->pOps != &g_krdrBufOps, NULL);
+
+ /* check for EOF */
+ PKRDRBUF pThis = (PKRDRBUF)pRdr;
+ if (pThis->offFile >= pThis->cbFile)
+ {
+ kRdrAssert(pThis->offFile == pThis->cbFile);
+ return NULL;
+ }
+
+ /*
+ * Search the current buffer if possible
+ */
+ if ( pThis->cbBufValid
+ && pThis->offFile >= pThis->offBuf
+ && pThis->offFile < pThis->offBufEnd)
+ {
+ const char *psz = krdrBufLineQWorker(pThis);
+ if (psz)
+ return psz;
+ }
+
+ /*
+ * Fill the buffer in an optimal way and look for the EOL/EOF (again).
+ */
+ int rc = krdrBufFillBuffer(pThis, pThis->offFile);
+ if (rc)
+ return NULL;
+ return krdrBufLineQWorker(pThis);
+}
+
diff --git a/src/lib/kStuff/kRdr/kRdrFile.cpp b/src/lib/kStuff/kRdr/kRdrFile.cpp
new file mode 100644
index 0000000..27dd803
--- /dev/null
+++ b/src/lib/kStuff/kRdr/kRdrFile.cpp
@@ -0,0 +1,1308 @@
+/* $Id: kRdrFile.cpp 81 2016-08-18 22:10:38Z bird $ */
+/** @file
+ * kRdrFile - The Native File Provider
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+/*******************************************************************************
+* Header Files *
+*******************************************************************************/
+#include "kRdrInternal.h"
+#include <k/kHlpAlloc.h>
+#include <k/kHlpString.h>
+#include <k/kErrors.h>
+
+#if K_OS == K_OS_DARWIN \
+ || K_OS == K_OS_FREEBSD \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_SOLARIS
+# include <k/kHlpSys.h>
+# include <sys/fcntl.h>
+# include <sys/mman.h>
+# include <unistd.h>
+
+#elif K_OS == K_OS_OS2
+# define INCL_ERRORS
+# define INCL_BASE
+# include <os2.h>
+
+#elif K_OS == K_OS_WINDOWS
+# define WIN32_NO_STATUS
+# include <Windows.h>
+# include <ntsecapi.h>
+# include <ntstatus.h>
+
+# ifdef __cplusplus
+ extern "C" {
+# endif
+
+ /** @todo find a non-conflicting header with NTSTATUS, NTAPI, ++ */
+ typedef LONG NTSTATUS;
+ #define NT_SUCCESS(x) ((x)>=0)
+
+ typedef struct _OBJECT_ATTRIBUTES
+ {
+ ULONG Length;
+ HANDLE RootDirectory;
+ PUNICODE_STRING ObjectName;
+ ULONG Attributes;
+ PVOID SecurityDescriptor;
+ PVOID SecurityQualityOfService;
+ } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
+
+ typedef enum _SECTION_INHERIT
+ {
+ ViewShare = 1,
+ ViewUnmap = 2
+ } SECTION_INHERIT;
+
+# define NTOSAPI __declspec(dllimport)
+# define NtCurrentProcess() GetCurrentProcess()
+
+# ifndef MEM_DOS_LIM
+# define MEM_DOS_LIM 0x40000000UL
+# endif
+
+ NTOSAPI
+ NTSTATUS
+ NTAPI
+ NtCreateSection(
+ OUT PHANDLE SectionHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
+ IN PLARGE_INTEGER SectionSize OPTIONAL,
+ IN ULONG Protect,
+ IN ULONG Attributes,
+ IN HANDLE FileHandle OPTIONAL
+ );
+
+ NTOSAPI
+ NTSTATUS
+ NTAPI
+ NtMapViewOfSection(
+ IN HANDLE SectionHandle,
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID *BaseAddress,
+ IN ULONG ZeroBits,
+ IN ULONG CommitSize,
+ IN OUT PLARGE_INTEGER SectionOffset OPTIONAL,
+ IN OUT PSIZE_T ViewSize,
+ IN SECTION_INHERIT InheritDisposition,
+ IN ULONG AllocationType,
+ IN ULONG Protect
+ );
+
+ NTOSAPI
+ NTSTATUS
+ NTAPI
+ NtUnmapViewOfSection(
+ IN HANDLE ProcessHandle,
+ IN PVOID BaseAddress
+ );
+
+ NTOSAPI
+ NTSTATUS
+ NTAPI
+ NtClose(
+ IN HANDLE Handle
+ );
+
+ NTOSAPI
+ NTSTATUS
+ NTAPI
+ ZwProtectVirtualMemory(
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID *BaseAddress,
+ IN OUT PSIZE_T ProtectSize,
+ IN ULONG NewProtect,
+ OUT PULONG OldProtect
+ );
+# define NtProtectVirtualMemory ZwProtectVirtualMemory
+
+ NTOSAPI
+ NTSTATUS
+ NTAPI
+ NtAllocateVirtualMemory(
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID *BaseAddress,
+ IN ULONG ZeroBits,
+ IN OUT PSIZE_T AllocationSize,
+ IN ULONG AllocationType,
+ IN ULONG Protect
+ );
+
+ NTOSAPI
+ NTSTATUS
+ NTAPI
+ NtFreeVirtualMemory(
+ IN HANDLE ProcessHandle,
+ IN OUT PVOID *BaseAddress,
+ IN OUT PSIZE_T FreeSize,
+ IN ULONG FreeType
+ );
+
+# ifdef __cplusplus
+ }
+# endif
+
+#else
+# error "port me"
+#endif
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Prepared stuff.
+ */
+typedef struct KRDRFILEPREP
+{
+ /** The address of the prepared region. */
+ void *pv;
+ /** The size of the prepared region. */
+ KSIZE cb;
+#if K_OS == K_OS_WINDOWS
+ /** Handle to the section created to map the file. */
+ HANDLE hSection;
+#endif
+} KRDRFILEPREP, *PKRDRFILEPREP;
+
+/**
+ * The file provier instance for native files.
+ */
+typedef struct KRDRFILE
+{
+ /** The file reader vtable. */
+ KRDR Core;
+ /** The file handle. */
+#if K_OS == K_OS_DARWIN \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_SOLARIS
+ int File;
+#elif K_OS == K_OS_OS2
+ HFILE File;
+#elif K_OS == K_OS_WINDOWS
+ HANDLE File;
+#else
+# error "Port me!"
+#endif
+ /** The current file offset. */
+ KFOFF off;
+ /** The file size. */
+ KFOFF cb;
+ /** Array where we stuff the mapping area data. */
+ KRDRFILEPREP aPreps[4];
+ /** The number of current preps. */
+ KU32 cPreps;
+ /** Number of mapping references. */
+ KI32 cMappings;
+ /** The memory mapping. */
+ void *pvMapping;
+ /** The filename. */
+ char szFilename[1];
+} KRDRFILE, *PKRDRFILE;
+
+
+/*******************************************************************************
+* Internal Functions *
+*******************************************************************************/
+static void krdrFileDone(PKRDR pRdr);
+static int krdrFileUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
+static int krdrFileGenericUnmap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments);
+static int krdrFileProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect);
+static int krdrFileGenericProtect(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect);
+static int krdrFileRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments);
+static int krdrFileGenericRefresh(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments);
+static int krdrFileMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed);
+static int krdrFileGenericMap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed);
+static KSIZE krdrFilePageSize(PKRDR pRdr);
+static const char *krdrFileName(PKRDR pRdr);
+static KIPTR krdrFileNativeFH(PKRDR pRdr);
+static KFOFF krdrFileTell(PKRDR pRdr);
+static KFOFF krdrFileSize(PKRDR pRdr);
+static int krdrFileAllUnmap(PKRDR pRdr, const void *pvBits);
+static int krdrFileAllMap(PKRDR pRdr, const void **ppvBits);
+static int krdrFileRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off);
+static int krdrFileDestroy(PKRDR pRdr);
+static int krdrFileCreate(PPKRDR ppRdr, const char *pszFilename);
+
+
+/*******************************************************************************
+* Global Variables *
+*******************************************************************************/
+/** Native file provider operations. */
+const KRDROPS g_kRdrFileOps =
+{
+ "native file",
+ NULL,
+ krdrFileCreate,
+ krdrFileDestroy,
+ krdrFileRead,
+ krdrFileAllMap,
+ krdrFileAllUnmap,
+ krdrFileSize,
+ krdrFileTell,
+ krdrFileName,
+ krdrFileNativeFH,
+ krdrFilePageSize,
+ krdrFileMap,
+ krdrFileRefresh,
+ krdrFileProtect,
+ krdrFileUnmap,
+ krdrFileDone,
+ 42
+};
+
+
+#if K_OS == K_OS_WINDOWS
+/**
+ * Converts a kLdr segment protection to NT protection for a mapping.
+ *
+ * @returns Nt page protection.
+ * @param enmProt kLdr protection.
+ */
+static ULONG krdrFileGetNtMapProt(KPROT enmProt)
+{
+ switch (enmProt)
+ {
+ case KPROT_NOACCESS: return PAGE_NOACCESS;
+ case KPROT_READONLY: return PAGE_READONLY;
+ case KPROT_READWRITE: return PAGE_READWRITE;
+ case KPROT_WRITECOPY: return PAGE_WRITECOPY;
+ case KPROT_EXECUTE: return PAGE_EXECUTE;
+ case KPROT_EXECUTE_READ: return PAGE_EXECUTE_READ;
+ case KPROT_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE;
+ case KPROT_EXECUTE_WRITECOPY: return PAGE_EXECUTE_WRITECOPY;
+ default: return ~(ULONG)0;
+ }
+}
+
+
+/**
+ * Converts a kLdr segment protection to NT protection for a allocation.
+ *
+ * @returns Nt page protection.
+ * @param enmProt kLdr protection.
+ */
+static ULONG krdrFileGetNtAllocProt(KPROT enmProt)
+{
+ switch (enmProt)
+ {
+ case KPROT_NOACCESS: return PAGE_NOACCESS;
+ case KPROT_READONLY: return PAGE_READONLY;
+ case KPROT_WRITECOPY:
+ case KPROT_READWRITE: return PAGE_READWRITE;
+ case KPROT_EXECUTE: return PAGE_EXECUTE;
+ case KPROT_EXECUTE_READ: return PAGE_EXECUTE_READ;
+ case KPROT_EXECUTE_WRITECOPY:
+ case KPROT_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE;
+ default: return ~(ULONG)0;
+ }
+}
+#endif
+
+
+/** @copydoc KRDROPS::pfnDone */
+static void krdrFileDone(PKRDR pRdr)
+{
+}
+
+
+/**
+ * Finds a prepared mapping region.
+ *
+ * @returns Pointer to the aPrep entry.
+ * @param pFile The instance data.
+ * @param pv The base of the region.
+ */
+static PKRDRFILEPREP krdrFileFindPrepExact(PKRDRFILE pFile, void *pv)
+{
+ KI32 i = pFile->cPreps;
+ while (i-- > 0)
+ if (pFile->aPreps[i].pv == pv)
+ return &pFile->aPreps[i];
+ return NULL;
+}
+
+
+/** @copydoc KRDROPS::pfnUnmap */
+static int krdrFileUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+ PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase);
+ int rc;
+ if (!pPrep)
+ return KERR_INVALID_PARAMETER;
+
+#if K_OS == K_OS_WINDOWS
+ if (pPrep->hSection != NULL)
+ {
+ /** @todo implement me. */
+ return -1;
+ }
+#endif
+
+ rc = krdrFileGenericUnmap(pRdr, pPrep, cSegments, paSegments);
+
+ /* remove the mapping data on success. */
+ if (!rc)
+ {
+ pRdrFile->cPreps--;
+ if (pPrep != &pRdrFile->aPreps[pRdrFile->cPreps])
+ *pPrep = pRdrFile->aPreps[pRdrFile->cPreps];
+ }
+ return rc;
+}
+
+
+/** Generic implementation of krdrFileUnmap. */
+static int krdrFileGenericUnmap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments)
+{
+ krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
+ return kHlpPageFree(pPrep->pv, pPrep->cb);
+}
+
+
+/** @copydoc KRDROPS::pfnProtect */
+static int krdrFileProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+ PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase);
+ if (!pPrep)
+ return KERR_INVALID_PARAMETER;
+
+#if K_OS == K_OS_WINDOWS
+ if (pPrep->hSection != NULL)
+ {
+ /** @todo implement me. */
+ return -1;
+ }
+#endif
+
+ return krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, fUnprotectOrProtect);
+}
+
+
+/** Generic implementation of krdrFileProtect. */
+static int krdrFileGenericProtect(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect)
+{
+ KU32 i;
+
+ /*
+ * Iterate the segments and apply memory protection changes.
+ */
+ for (i = 0; i < cSegments; i++)
+ {
+ int rc;
+ void *pv;
+ KPROT enmProt;
+
+ if (paSegments[i].RVA == NIL_KLDRADDR)
+ continue;
+
+ /* calc new protection. */
+ enmProt = (KPROT)paSegments[i].enmProt; /** @todo drop cast */
+ if (fUnprotectOrProtect)
+ {
+ switch (enmProt)
+ {
+ case KPROT_NOACCESS:
+ case KPROT_READONLY:
+ case KPROT_READWRITE:
+ case KPROT_WRITECOPY:
+ enmProt = KPROT_READWRITE;
+ break;
+ case KPROT_EXECUTE:
+ case KPROT_EXECUTE_READ:
+ case KPROT_EXECUTE_READWRITE:
+ case KPROT_EXECUTE_WRITECOPY:
+ enmProt = KPROT_EXECUTE_READWRITE;
+ break;
+ default:
+ kRdrAssert(!"bad enmProt");
+ return -1;
+ }
+ }
+ else
+ {
+ /* copy on write -> normal write. */
+ if (enmProt == KPROT_EXECUTE_WRITECOPY)
+ enmProt = KPROT_EXECUTE_READWRITE;
+ else if (enmProt == KPROT_WRITECOPY)
+ enmProt = KPROT_READWRITE;
+ }
+
+ pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
+
+ rc = kHlpPageProtect(pv, paSegments[i].cbMapped, enmProt);
+ if (rc)
+ break;
+ }
+
+ return 0;
+}
+
+
+/** @copydoc KRDROPS::pfnRefresh */
+static int krdrFileRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+ PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase);
+ if (!pPrep)
+ return KERR_INVALID_PARAMETER;
+
+#if K_OS == K_OS_WINDOWS
+ if (pPrep->hSection != NULL)
+ {
+ /** @todo implement me. */
+ return -1;
+ }
+#endif
+
+ return krdrFileGenericRefresh(pRdr, pPrep, cSegments, paSegments);
+}
+
+
+/** Generic implementation of krdrFileRefresh. */
+static int krdrFileGenericRefresh(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments)
+{
+ int rc;
+ int rc2;
+ KU32 i;
+
+ /*
+ * Make everything writable again.
+ */
+ rc = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
+ if (rc)
+ {
+ krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
+ return rc;
+ }
+
+ /*
+ * Clear everything.
+ */
+ /** @todo only zero the areas not covered by raw file bits. */
+ kHlpMemSet(pPrep->pv, 0, pPrep->cb);
+
+ /*
+ * Reload all the segments.
+ * We could possibly skip some segments, but we currently have
+ * no generic way of figuring out which at the moment.
+ */
+ for (i = 0; i < cSegments; i++)
+ {
+ void *pv;
+
+ if ( paSegments[i].RVA == NIL_KLDRADDR
+ || paSegments[i].cbFile <= 0)
+ continue;
+
+ pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
+ rc = pRdr->pOps->pfnRead(pRdr, pv, paSegments[i].cbFile, paSegments[i].offFile);
+ if (rc)
+ break;
+ }
+
+ /*
+ * Protect the bits again.
+ */
+ rc2 = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
+ if (rc2 && rc)
+ rc = rc2;
+
+ return rc;
+}
+
+
+/** @copydoc KRDROPS::pfnMap */
+static int krdrFileMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+ PKRDRFILEPREP pPrep = &pRdrFile->aPreps[pRdrFile->cPreps];
+ KLDRSIZE cbTotal;
+ const KSIZE cbPage = pRdr->pOps->pfnPageSize(pRdr);
+ int rc;
+ KU32 i;
+
+ if (pRdrFile->cPreps >= K_ELEMENTS(pRdrFile->aPreps))
+ return KRDR_ERR_TOO_MANY_MAPPINGS;
+
+ /*
+ * Calc the total mapping space needed.
+ */
+ cbTotal = 0;
+ for (i = 0; i < cSegments; i++)
+ {
+ KLDRSIZE uRVASegmentEnd;
+ if (paSegments[i].RVA == NIL_KLDRADDR)
+ continue;
+ uRVASegmentEnd = paSegments[i].RVA + paSegments[i].cbMapped;
+ if (cbTotal < uRVASegmentEnd)
+ cbTotal = uRVASegmentEnd;
+ }
+ pPrep->cb = (KSIZE)cbTotal;
+ if (pPrep->cb != cbTotal)
+ return KLDR_ERR_ADDRESS_OVERFLOW;
+ pPrep->cb = (pPrep->cb + (cbPage - 1)) & ~(cbPage- 1);
+
+#if K_OS == K_OS_DARWIN \
+ || K_OS == K_OS_FREEBSD \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_SOLARIS
+ /** @todo */
+
+#elif K_OS == K_OS_WINDOWS
+ /*
+ * The NT memory mapped file API sucks in a lot of ways. Unless you're actually
+ * trying to map a PE image and the kernel can parse the file for it self, the
+ * API just isn't up to scratch.
+ *
+ * Problems:
+ * 1. Reserving memory for the views is risky because you can't reserve and
+ * map into the reserved space. So, other threads might grab the memory
+ * before we get to it.
+ * 2. The page aligning of file offsets makes it impossible to map most
+ * executable images since these are commonly sector aligned.
+ * 3. When mapping a read+execute file, its not possible to create section
+ * larger than the file since the section size is bound to the data file
+ * size. This wouldn't have been such a problem if it was possible to
+ * map views beyond the section restriction, i.e. have a file size and
+ * view size.
+ * 4. Only x86 can map views at page granularity it seems, and that only
+ * using an undocument flag. The default granularity is 64KB.
+ * 5. There is more crappyness here...
+ *
+ * So, first we'll have to check if we can the file using the crappy NT APIs.
+ * Chances are we can't.
+ */
+ for (i = 0; i < cSegments; i++)
+ {
+ if (paSegments[i].RVA == NIL_KLDRADDR)
+ continue;
+
+ /* The file backing of the segments must be page aligned. */
+ if ( paSegments[i].cbFile > 0
+ && paSegments[i].offFile & (cbPage - 1))
+ break;
+
+ /* Only page alignment gaps between the file size and the mapping size. */
+ if ( paSegments[i].cbFile > 0
+ && (paSegments[i].cbFile & ~(cbPage - 1)) != (paSegments[i].cbMapped & ~(cbPage - 1)) )
+ break;
+
+ /* The mapping addresses of the segments must be page aligned.
+ * Non-x86 will probably require 64KB alignment here. */
+ if (paSegments[i].RVA & (cbPage - 1))
+ break;
+
+ /* If we do have to allocate the segment it's RVA must be 64KB aligned. */
+ if ( paSegments[i].cbFile > 0
+ && (paSegments[i].RVA & 0xffff))
+ break;
+ }
+ /** @todo if this is a PE image, we might just try a SEC_IMAGE mapping. It'll work if the host and image machines matches. */
+ if (i == cSegments)
+ {
+ /* WOW! it may work out! Incredible! */
+ SIZE_T ViewSize;
+ LARGE_INTEGER SectionOffset;
+ LARGE_INTEGER MaxiumSize;
+ NTSTATUS Status;
+ PVOID pv;
+
+ MaxiumSize.QuadPart = pRdr->pOps->pfnSize(pRdr);
+ if (MaxiumSize.QuadPart > (LONGLONG)cbTotal)
+ MaxiumSize.QuadPart = cbTotal;
+
+ Status = NtCreateSection(&pPrep->hSection,
+ SECTION_MAP_EXECUTE | SECTION_MAP_READ, /* desired access */
+ NULL, /* object attributes */
+ &MaxiumSize,
+ PAGE_EXECUTE_WRITECOPY, /* page attributes */
+ SEC_COMMIT, /* section attributes */
+ pRdrFile->File);
+ if (!NT_SUCCESS(Status))
+ return (int)Status;
+
+ /*
+ * Determin the base address.
+ */
+ if (fFixed)
+ pPrep->pv = *ppvBase;
+ else
+ {
+ pv = NULL;
+ ViewSize = (KSIZE)cbTotal;
+
+ Status = NtAllocateVirtualMemory(NtCurrentProcess(),
+ &pv,
+ 0, /* ZeroBits */
+ &ViewSize,
+ MEM_RESERVE,
+ PAGE_READONLY);
+ if (NT_SUCCESS(Status))
+ {
+ pPrep->pv = *ppvBase = pv;
+ ViewSize = 0;
+ Status = NtFreeVirtualMemory(NtCurrentProcess(), &pv, &ViewSize, MEM_RELEASE);
+ }
+ if (!NT_SUCCESS(Status))
+ {
+ NtClose(pPrep->hSection);
+ return Status;
+ }
+ }
+
+ /*
+ * Map the segments.
+ */
+ for (i = 0; i < cSegments; i++)
+ {
+ ULONG fPageProt;
+
+ if (paSegments[i].RVA == NIL_KLDRADDR)
+ continue;
+
+ pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
+ if (paSegments[i].cbFile > 0)
+ {
+ SectionOffset.QuadPart = paSegments[i].offFile;
+ ViewSize = paSegments[i].cbFile;
+ fPageProt = krdrFileGetNtMapProt(paSegments[i].enmProt);
+ /* STATUS_MAPPED_ALIGNMENT
+ STATUS_CONFLICTING_ADDRESSES
+ STATUS_INVALID_VIEW_SIZE */
+ Status = NtMapViewOfSection(pPrep->hSection, NtCurrentProcess(),
+ &pv,
+ 0, /* ZeroBits */
+ 0, /* CommitSize */
+ &SectionOffset, /* SectionOffset */
+ &ViewSize,
+ ViewUnmap,
+ MEM_DOS_LIM, /* AllocationType */
+ fPageProt);
+ /* do we have to zero anything? */
+ if ( NT_SUCCESS(Status)
+ && 0/*later*/)
+ {
+ /*ULONG OldPageProt = 0;
+ NtProtectVirtualMemory(NtCurrentProcess(), &pv, &ViewSize, , */
+ }
+ }
+ else
+ {
+ ViewSize = paSegments[i].cbMapped;
+ fPageProt = krdrFileGetNtAllocProt(paSegments[i].enmProt);
+ Status = NtAllocateVirtualMemory(NtCurrentProcess(),
+ &pv,
+ 0, /* ZeroBits */
+ &ViewSize,
+ MEM_COMMIT,
+ fPageProt);
+ }
+ if (!NT_SUCCESS(Status))
+ break;
+ }
+
+ /*
+ * On success, commit the mapping and return.
+ */
+ if (NT_SUCCESS(Status))
+ {
+ pRdrFile->cPreps++;
+ return 0;
+ }
+
+ /* bail out and fall back on the generic code. */
+ while (i-- > 0)
+ {
+ PVOID pv;
+
+ if (paSegments[i].RVA == NIL_KLDRADDR)
+ continue;
+
+ pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
+ if (paSegments[i].cbFile > 0)
+ NtUnmapViewOfSection(NtCurrentProcess(), pv);
+ else
+ NtFreeVirtualMemory(NtCurrentProcess(), &pv, NULL, MEM_RELEASE);
+ }
+ NtClose(pPrep->hSection);
+ }
+ /* else: fall back to the generic code */
+ pPrep->hSection = NULL;
+#endif
+
+ /*
+ * Use the generic map emulation.
+ */
+ pPrep->pv = fFixed ? *ppvBase : NULL;
+ rc = krdrFileGenericMap(pRdr, pPrep, cSegments, paSegments, fFixed);
+ if (!rc)
+ {
+ *ppvBase = pPrep->pv;
+ pRdrFile->cPreps++;
+ }
+
+ return rc;
+}
+
+
+/** Generic implementation of krdrFileMap. */
+static int krdrFileGenericMap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed)
+{
+ int rc;
+ KU32 i;
+
+ /*
+ * Generic mapping code using kHlpPageAlloc(), kHlpPageFree() and kHlpPageProtect().
+ */
+ rc = kHlpPageAlloc(&pPrep->pv, pPrep->cb, KPROT_EXECUTE_READWRITE, fFixed);
+ if (rc)
+ return rc;
+
+ /*
+ * Load the data.
+ */
+ for (i = 0; i < cSegments; i++)
+ {
+ void *pv;
+
+ if ( paSegments[i].RVA == NIL_KLDRADDR
+ || paSegments[i].cbFile <= 0)
+ continue;
+
+ pv = (KU8 *)pPrep->pv + paSegments[i].RVA;
+ rc = pRdr->pOps->pfnRead(pRdr, pv, paSegments[i].cbFile, paSegments[i].offFile);
+ if (rc)
+ break;
+ }
+
+ /*
+ * Set segment protection.
+ */
+ if (!rc)
+ {
+ rc = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */);
+ if (!rc)
+ return 0;
+ krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */);
+ }
+
+ /* bailout */
+ kHlpPageFree(pPrep->pv, pPrep->cb);
+ return rc;
+}
+
+
+/** @copydoc KRDROPS::pfnPageSize */
+static KSIZE krdrFilePageSize(PKRDR pRdr)
+{
+#if K_OS == K_OS_DARWIN
+ return 0x1000; /** @todo find some header somewhere... */
+
+#elif K_OS == K_OS_LINUX
+ return 0x1000; /** @todo find some header somewhere... */
+
+#elif K_OS == K_OS_OS2
+ /* The page size on OS/2 wont change anytime soon. :-) */
+ return 0x1000;
+
+#elif K_OS == K_OS_WINDOWS
+ SYSTEM_INFO SysInfo;
+ GetSystemInfo(&SysInfo);
+ return SysInfo.dwPageSize;
+ /*return SysInfo.dwAllocationGranularity;*/
+#else
+# error "port me"
+#endif
+}
+
+
+/** @copydoc KRDROPS::pfnName */
+static const char *krdrFileName(PKRDR pRdr)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+ return &pRdrFile->szFilename[0];
+}
+
+
+static KIPTR krdrFileNativeFH(PKRDR pRdr)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+#if K_OS == K_OS_DARWIN \
+ || K_OS == K_OS_FREEBSD \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_OS2 \
+ || K_OS == K_OS_SOLARIS \
+ || K_OS == K_OS_WINDOWS
+ return (KIPTR)pRdrFile->File;
+#else
+# error "port me"
+#endif
+}
+
+
+/** @copydoc KRDROPS::pfnTell */
+static KFOFF krdrFileTell(PKRDR pRdr)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+
+ /*
+ * If the offset is undefined, try figure out what it is.
+ */
+ if (pRdrFile->off == -1)
+ {
+#if K_OS == K_OS_DARWIN \
+ || K_OS == K_OS_FREEBSD \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_SOLARIS
+ pRdrFile->off = kHlpSys_lseek(pRdrFile->File, SEEK_CUR, 0);
+ if (pRdrFile->off < 0)
+ pRdrFile->off = -1;
+
+#elif K_OS == K_OS_OS2
+ ULONG ulNew;
+ APIRET rc = DosSetFilePtr(pRdrFile->File, 0, FILE_CURRENT, &ulNew);
+ if (rc)
+ return -1;
+ pRdrFile->off = ulNew;
+
+#elif K_OS == K_OS_WINDOWS
+ LONG offHigh = 0;
+ LONG offLow;
+ int rc;
+
+ SetLastError(0);
+ offLow = SetFilePointer(pRdrFile->File, 0, &offHigh, FILE_CURRENT);
+ rc = GetLastError();
+ if (rc)
+ return -1;
+ pRdrFile->off = ((KFOFF)offHigh << 32) | offLow;
+
+#else
+# error "port me."
+#endif
+ }
+ return pRdrFile->off;
+}
+
+
+/** @copydoc KRDROPS::pfnSize */
+static KFOFF krdrFileSize(PKRDR pRdr)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+ return pRdrFile->cb;
+}
+
+
+/** @copydoc KRDROPS::pfnAllUnmap */
+static int krdrFileAllUnmap(PKRDR pRdr, const void *pvBits)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+
+ /* check for underflow */
+ if (pRdrFile->cMappings <= 0)
+ return KERR_INVALID_PARAMETER;
+
+ /* decrement usage counter, free mapping if no longer in use. */
+ if (!--pRdrFile->cMappings)
+ {
+ kHlpFree(pRdrFile->pvMapping);
+ pRdrFile->pvMapping = NULL;
+ }
+
+ return 0;
+}
+
+
+/** @copydoc KRDROPS::pfnAllMap */
+static int krdrFileAllMap(PKRDR pRdr, const void **ppvBits)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+
+ /*
+ * Do we need to map it?
+ */
+ if (!pRdrFile->pvMapping)
+ {
+ int rc;
+ KFOFF cbFile = pRdrFile->Core.pOps->pfnSize(pRdr);
+ KSIZE cb = (KSIZE)cbFile;
+ if (cb != cbFile)
+ return KERR_NO_MEMORY;
+
+ pRdrFile->pvMapping = kHlpAlloc(cb);
+ if (!pRdrFile->pvMapping)
+ return KERR_NO_MEMORY;
+ rc = pRdrFile->Core.pOps->pfnRead(pRdr, pRdrFile->pvMapping, cb, 0);
+ if (rc)
+ {
+ kHlpFree(pRdrFile->pvMapping);
+ pRdrFile->pvMapping = NULL;
+ return rc;
+ }
+ pRdrFile->cMappings = 0;
+ }
+
+ *ppvBits = pRdrFile->pvMapping;
+ pRdrFile->cMappings++;
+ return 0;
+}
+
+
+/** @copydoc KRDROPS::pfnRead */
+static int krdrFileRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+
+ /*
+ * Do a seek if needed.
+ */
+ if (pRdrFile->off != off)
+ {
+#if K_OS == K_OS_DARWIN \
+ || K_OS == K_OS_FREEBSD \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_SOLARIS
+ pRdrFile->off = kHlpSys_lseek(pRdrFile->File, SEEK_SET, off);
+ if (pRdrFile->off < 0)
+ {
+ int rc = (int)-pRdrFile->off;
+ pRdrFile->off = -1;
+ return -rc;
+ }
+
+#elif K_OS == K_OS_OS2
+ ULONG ulNew;
+ APIRET rc;
+
+ rc = DosSetFilePtr(pRdrFile->File, off, FILE_BEGIN, &ulNew);
+ if (rc)
+ {
+ pRdrFile->off = -1;
+ return rc;
+ }
+
+#elif K_OS == K_OS_WINDOWS
+ LONG offHigh;
+ LONG offLow;
+
+ offHigh = (LONG)(off >> 32);
+ offLow = SetFilePointer(pRdrFile->File, (LONG)off, &offHigh, FILE_BEGIN);
+ if ( offLow != (LONG)off
+ || offHigh != (LONG)(off >> 32))
+ {
+ int rc = GetLastError();
+ if (!rc)
+ rc = KERR_GENERAL_FAILURE;
+ pRdrFile->off = -1;
+ return rc;
+ }
+
+#else
+# error "port me."
+#endif
+ }
+
+ /*
+ * Do the read.
+ */
+#if K_OS == K_OS_DARWIN \
+ || K_OS == K_OS_FREEBSD \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_SOLARIS
+ {
+ KSSIZE cbRead;
+
+ cbRead = kHlpSys_read(pRdrFile->File, pvBuf, cb);
+ if (cbRead != cb)
+ {
+ pRdrFile->off = -1;
+ if (cbRead < 0)
+ return -cbRead;
+ return KERR_GENERAL_FAILURE;
+ }
+ }
+
+#elif K_OS == K_OS_OS2
+ {
+ ULONG cbRead = 0;
+ APIRET rc = DosRead(pRdrFile->File, pvBuf, cb, &cbRead);
+ if (rc)
+ {
+ pRdrFile->off = -1;
+ return rc;
+ }
+ if (cbRead != cb)
+ {
+ pRdrFile->off = -1;
+ return KERR_GENERAL_FAILURE;
+ }
+ }
+
+#elif K_OS == K_OS_WINDOWS
+ {
+ DWORD cbRead = 0;
+ if (!ReadFile(pRdrFile->File, pvBuf, cb, &cbRead, NULL))
+ {
+ int rc = GetLastError();
+ if (!rc)
+ rc = KERR_GENERAL_FAILURE;
+ pRdrFile->off = -1;
+ return rc;
+ }
+ if (cbRead != cb)
+ {
+ pRdrFile->off = -1;
+ return KERR_GENERAL_FAILURE;
+ }
+ }
+
+#else
+# error "port me."
+#endif
+
+ pRdrFile->off = off + cb;
+ return 0;
+}
+
+
+/** @copydoc KRDROPS::pfnDestroy */
+static int krdrFileDestroy(PKRDR pRdr)
+{
+ PKRDRFILE pRdrFile = (PKRDRFILE)pRdr;
+ int rc;
+
+#if K_OS == K_OS_DARWIN \
+ || K_OS == K_OS_FREEBSD \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_SOLARIS
+ rc = kHlpSys_close(pRdrFile->File);
+
+#elif K_OS == K_OS_OS2
+ rc = DosClose(pRdrFile->File);
+
+#elif K_OS == K_OS_WINDOWS
+ rc = 0;
+ if (!CloseHandle(pRdrFile->File))
+ rc = GetLastError();
+
+#else
+# error "port me"
+#endif
+
+ if (pRdrFile->pvMapping)
+ {
+ kHlpFree(pRdrFile->pvMapping);
+ pRdrFile->pvMapping = NULL;
+ }
+
+ kHlpFree(pRdr);
+ return rc;
+}
+
+
+/** @copydoc KRDROPS::pfnCreate */
+static int krdrFileCreate(PPKRDR ppRdr, const char *pszFilename)
+{
+ KSIZE cchFilename;
+ PKRDRFILE pRdrFile;
+
+ /*
+ * Open the file, determin its size and correct filename.
+ */
+#if K_OS == K_OS_DARWIN \
+ || K_OS == K_OS_FREEBSD \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_SOLARIS
+ int File;
+ KFOFF cb;
+ KFOFF rc;
+ char szFilename[1024];
+
+ cchFilename = kHlpStrLen(pszFilename);
+ if (cchFilename >= sizeof(szFilename))
+ return KERR_OUT_OF_RANGE;
+ kHlpMemCopy(szFilename, pszFilename, cchFilename + 1);
+ /** @todo normalize the filename. */
+
+# ifdef O_BINARY
+ File = kHlpSys_open(pszFilename, O_RDONLY | O_BINARY, 0);
+# else
+ File = kHlpSys_open(pszFilename, O_RDONLY, 0);
+# endif
+ if (File < 0)
+ return -File;
+
+ cb = kHlpSys_lseek(File, SEEK_END, 0);
+ rc = kHlpSys_lseek(File, SEEK_SET, 0);
+ if ( cb < 0
+ || rc < 0)
+ {
+ kHlpSys_close(File);
+ return cb < 0 ? -cb : -rc;
+ }
+
+#elif K_OS == K_OS_OS2
+ ULONG ulAction = 0;
+ FILESTATUS3 Info;
+ APIRET rc;
+ HFILE File = 0;
+ KFOFF cb;
+ char szFilename[CCHMAXPATH];
+
+ if ((uintptr_t)pszFilename >= 0x20000000)
+ {
+ char *psz;
+ cchFilename = kHlpStrLen(szFilename);
+ psz = (char *)kHlpAllocA(cchFilename + 1);
+ kHlpMemCopy(psz, pszFilename, cchFilename + 1);
+ pszFilename = psz;
+ }
+ rc = DosOpen((PCSZ)pszFilename, &File, &ulAction, 0, FILE_NORMAL,
+ OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
+ OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY | OPEN_FLAGS_RANDOMSEQUENTIAL,
+ NULL);
+ if (rc)
+ return rc;
+
+ rc = DosQueryPathInfo((PCSZ)pszFilename, FIL_QUERYFULLNAME, szFilename, sizeof(szFilename));
+ if (rc)
+ {
+ DosClose(File);
+ return rc;
+ }
+
+ rc = DosQueryFileInfo(File, FIL_STANDARD, &Info, sizeof(Info));
+ if (rc)
+ {
+ DosClose(File);
+ return rc;
+ }
+ cb = Info.cbFile;
+
+#elif K_OS == K_OS_WINDOWS
+ SECURITY_ATTRIBUTES SecAttr;
+ DWORD High;
+ DWORD Low;
+ int rc;
+ HANDLE File;
+ KFOFF cb;
+ char szFilename[MAX_PATH];
+
+ SecAttr.bInheritHandle = FALSE;
+ SecAttr.lpSecurityDescriptor = NULL;
+ SecAttr.nLength = 0;
+ File = CreateFile(pszFilename, GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, &SecAttr, OPEN_EXISTING, 0, NULL);
+ if (File == INVALID_HANDLE_VALUE)
+ return GetLastError();
+
+ if (!GetFullPathName(pszFilename, sizeof(szFilename), szFilename, NULL))
+ {
+ rc = GetLastError();
+ CloseHandle(File);
+ return rc;
+ }
+
+ SetLastError(0);
+ Low = GetFileSize(File, &High);
+ rc = GetLastError();
+ if (rc)
+ {
+ CloseHandle(File);
+ return rc;
+ }
+ cb = ((KFOFF)High << 32) | Low;
+
+#else
+# error "port me"
+#endif
+
+
+ /*
+ * Allocate the reader instance.
+ */
+ cchFilename = kHlpStrLen(szFilename);
+ pRdrFile = (PKRDRFILE)kHlpAlloc(sizeof(*pRdrFile) + cchFilename);
+ if (!pRdrFile)
+ {
+#if K_OS == K_OS_DARWIN \
+ || K_OS == K_OS_FREEBSD \
+ || K_OS == K_OS_LINUX \
+ || K_OS == K_OS_NETBSD \
+ || K_OS == K_OS_OPENBSD \
+ || K_OS == K_OS_SOLARIS
+ kHlpSys_close(File);
+#elif K_OS == K_OS_OS2
+ DosClose(File);
+#elif K_OS == K_OS_WINDOWS
+ CloseHandle(File);
+#else
+# error "port me"
+#endif
+ return KERR_NO_MEMORY;
+ }
+
+ /*
+ * Initialize it and return successfully.
+ */
+ pRdrFile->Core.u32Magic = KRDR_MAGIC;
+ pRdrFile->Core.pOps = &g_kRdrFileOps;
+ pRdrFile->File = File;
+ pRdrFile->cb = cb;
+ pRdrFile->off = 0;
+ pRdrFile->cPreps = 0;
+ pRdrFile->cMappings = 0;
+ pRdrFile->pvMapping = NULL;
+ kHlpMemCopy(&pRdrFile->szFilename[0], szFilename, cchFilename + 1);
+
+ *ppRdr = &pRdrFile->Core;
+ return 0;
+}
+
diff --git a/src/lib/kStuff/kRdr/kRdrInternal.h b/src/lib/kStuff/kRdr/kRdrInternal.h
new file mode 100644
index 0000000..d8f67db
--- /dev/null
+++ b/src/lib/kStuff/kRdr/kRdrInternal.h
@@ -0,0 +1,122 @@
+/* $Id: kRdrInternal.h 29 2009-07-01 20:30:29Z bird $ */
+/** @file
+ * kRdr - Internal Header.
+ */
+
+/*
+ * Copyright (c) 2006-2007 Knut St. Osmundsen <bird-kStuff-spamix@anduin.net>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef ___kRdrInternal_h___
+#define ___kRdrInternal_h___
+
+#include <k/kHlpAssert.h>
+#include <k/kMagics.h>
+#include <k/kRdrAll.h>
+#include <k/kErrors.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @defgroup grp_kRdrInternal - Internals
+ * @internal
+ * @addtogroup grp_kRdr
+ * @{
+ */
+
+/** @def KRDR_STRICT
+ * If defined the kRdr assertions and other runtime checks will be enabled. */
+#ifdef K_ALL_STRICT
+# undef KRDR_STRICT
+# define KRDR_STRICT
+#endif
+
+/** @name Our Assert macros
+ * @{ */
+#ifdef KRDR_STRICT
+# define kRdrAssert(expr) kHlpAssert(expr)
+# define kRdrAssertReturn(expr, rcRet) kHlpAssertReturn(expr, rcRet)
+# define kRdrAssertMsg(expr, msg) kHlpAssertMsg(expr, msg)
+# define kRdrAssertMsgReturn(expr, msg, rcRet) kHlpAssertMsgReturn(expr, msg, rcRet)
+#else /* !KRDR_STRICT */
+# define kRdrAssert(expr) do { } while (0)
+# define kRdrAssertReturn(expr, rcRet) do { if (!(expr)) return (rcRet); } while (0)
+# define kRdrAssertMsg(expr, msg) do { } while (0)
+# define kRdrAssertMsgReturn(expr, msg, rcRet) do { if (!(expr)) return (rcRet); } while (0)
+#endif /* !KRDR_STRICT */
+
+#define kRdrAssertPtr(ptr) kRdrAssertMsg(K_VALID_PTR(ptr), ("%s = %p\n", #ptr, (ptr)))
+#define kRdrAssertPtrReturn(ptr, rcRet) kRdrAssertMsgReturn(K_VALID_PTR(ptr), ("%s = %p -> %d\n", #ptr, (ptr), (rcRet)), (rcRet))
+#define kRdrAssertPtrNull(ptr) kRdrAssertMsg(!(ptr) || K_VALID_PTR(ptr), ("%s = %p\n", #ptr, (ptr)))
+#define kRdrAssertPtrNullReturn(ptr, rcRet) kRdrAssertMsgReturn(!(ptr) || K_VALID_PTR(ptr), ("%s = %p -> %d\n", #ptr, (ptr), (rcRet)), (rcRet))
+#define kRdrAssertRC(rc) kRdrAssertMsg((rc) == 0, ("%s = %d\n", #rc, (rc)))
+#define kRdrAssertRCReturn(rc, rcRet) kRdrAssertMsgReturn((rc) == 0, ("%s = %d -> %d\n", #rc, (rc), (rcRet)), (rcRet))
+#define kRdrAssertFailed() kRdrAssert(0)
+#define kRdrAssertFailedReturn(rcRet) kRdrAssertReturn(0, (rcRet))
+#define kRdrAssertMsgFailed(msg) kRdrAssertMsg(0, msg)
+#define kRdrAssertMsgFailedReturn(msg, rcRet) kRdrAssertMsgReturn(0, msg, (rcRet))
+/** @} */
+
+/** Return / crash validation of a reader argument. */
+#define KRDR_VALIDATE_EX(pRdr, rc) \
+ do { \
+ if ( (pRdr)->u32Magic != KRDR_MAGIC \
+ || (pRdr)->pOps == NULL \
+ )\
+ { \
+ return (rc); \
+ } \
+ } while (0)
+
+/** Return / crash validation of a reader argument. */
+#define KRDR_VALIDATE(pRdr) \
+ KRDR_VALIDATE_EX(pRdr, KERR_INVALID_PARAMETER)
+
+/** Return / crash validation of a reader argument. */
+#define KRDR_VALIDATE_VOID(pRdr) \
+ do { \
+ if ( !K_VALID_PTR(pRdr) \
+ || (pRdr)->u32Magic != KRDR_MAGIC \
+ || (pRdr)->pOps == NULL \
+ )\
+ { \
+ return; \
+ } \
+ } while (0)
+
+
+/** @name Built-in Providers
+ * @{ */
+extern const KRDROPS g_kRdrFileOps;
+/** @} */
+
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+