summaryrefslogtreecommitdiffstats
path: root/src/kmk/kmkbuiltin/kDepIDB.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kmk/kmkbuiltin/kDepIDB.c')
-rw-r--r--src/kmk/kmkbuiltin/kDepIDB.c860
1 files changed, 860 insertions, 0 deletions
diff --git a/src/kmk/kmkbuiltin/kDepIDB.c b/src/kmk/kmkbuiltin/kDepIDB.c
new file mode 100644
index 0000000..24cadc1
--- /dev/null
+++ b/src/kmk/kmkbuiltin/kDepIDB.c
@@ -0,0 +1,860 @@
+/* $Id: kDepIDB.c 3315 2020-03-31 01:12:19Z bird $ */
+/** @file
+ * kDepIDB - Extract dependency information from a MS Visual C++ .idb file.
+ */
+
+/*
+ * Copyright (c) 2007-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
+ *
+ * This file is part of kBuild.
+ *
+ * kBuild is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * kBuild is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kBuild. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#ifdef HAVE_ALLOCA_H
+# include <alloca.h>
+#endif
+#if !defined(_MSC_VER)
+# include <unistd.h>
+#else
+# include <io.h>
+#endif
+#include "k/kDefs.h"
+#include "k/kTypes.h"
+#include "kDep.h"
+#include "err.h"
+#include "kmkbuiltin.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/*#define DEBUG*/
+#ifdef DEBUG
+# define dprintf(a) printf a
+# define dump(pb, cb, offBase) depHexDump(pb,cb,offBase)
+#else
+# define dprintf(a) do {} while (0)
+# define dump(pb, cb, offBase) do {} while (0)
+#endif
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct KDEPIDBGLOBALS
+{
+ PKMKBUILTINCTX pCtx;
+ DEPGLOBALS Core;
+} KDEPIDBGLOBALS;
+typedef KDEPIDBGLOBALS *PKDEPIDBGLOBALS;
+
+
+/**
+ * Scans a stream (chunk of data really) for dependencies.
+ *
+ * @returns 0 on success.
+ * @returns !0 on failure.
+ * @param pThis The kDepIDB instance.
+ * @param pbStream The stream bits.
+ * @param cbStream The size of the stream.
+ * @param pszPrefix The dependency prefix.
+ * @param cchPrefix The size of the prefix.
+ */
+static int ScanStream(PKDEPIDBGLOBALS pThis, KU8 *pbStream, size_t cbStream, const char *pszPrefix, size_t cchPrefix)
+{
+ const KU8 *pbCur = pbStream;
+ size_t cbLeft = cbStream;
+ register char chFirst = *pszPrefix;
+ while (cbLeft > cchPrefix + 2)
+ {
+ if ( *pbCur != chFirst
+ || memcmp(pbCur, pszPrefix, cchPrefix))
+ {
+ pbCur++;
+ cbLeft--;
+ }
+ else
+ {
+ size_t cchDep;
+ pbCur += cchPrefix;
+ cchDep = strlen((const char *)pbCur);
+ depAdd(&pThis->Core, (const char *) pbCur, cchDep);
+ dprintf(("%05x: '%s'\n", pbCur - pbStream, pbCur));
+
+ pbCur += cchDep;
+ cbLeft -= cchDep + cchPrefix;
+ }
+ }
+
+ return 0;
+}
+
+
+/*/////////////////////////////////////////////////////////////////////////////
+//
+//
+// P D B 7 . 0
+//
+//
+/////////////////////////////////////////////////////////////////////////////*/
+
+/** A PDB 7.0 Page number. */
+typedef KU32 PDB70PAGE;
+/** Pointer to a PDB 7.0 Page number. */
+typedef PDB70PAGE *PPDB70PAGE;
+
+/**
+ * A PDB 7.0 stream.
+ */
+typedef struct PDB70STREAM
+{
+ /** The size of the stream. */
+ KU32 cbStream;
+} PDB70STREAM, *PPDB70STREAM;
+
+
+/** The PDB 7.00 signature. */
+#define PDB_SIGNATURE_700 "Microsoft C/C++ MSF 7.00\r\n\x1A" "DS\0\0"
+/**
+ * The PDB 7.0 header.
+ */
+typedef struct PDB70HDR
+{
+ /** The signature string. */
+ KU8 szSignature[sizeof(PDB_SIGNATURE_700)];
+ /** The page size. */
+ KU32 cbPage;
+ /** The start page. */
+ PDB70PAGE iStartPage;
+ /** The number of pages in the file. */
+ PDB70PAGE cPages;
+ /** The root stream directory. */
+ KU32 cbRoot;
+ /** Unknown function, always 0. */
+ KU32 u32Reserved;
+ /** The page index of the root page table. */
+ PDB70PAGE iRootPages;
+} PDB70HDR, *PPDB70HDR;
+
+/**
+ * The PDB 7.0 root directory.
+ */
+typedef struct PDB70ROOT
+{
+ /** The number of streams */
+ KU32 cStreams;
+ /** Array of streams. */
+ PDB70STREAM aStreams[1];
+ /* KU32 aiPages[] */
+} PDB70ROOT, *PPDB70ROOT;
+
+/**
+ * The PDB 7.0 name stream (#1) header.
+ */
+typedef struct PDB70NAMES
+{
+ /** The structure version. */
+ KU32 Version;
+ /** Timestamp. */
+ KU32 TimeStamp;
+ /** Unknown. */
+ KU32 Unknown1;
+ /** GUID. */
+ KU32 u32Guid[4];
+ /** The size of the following name table. */
+ KU32 cbNames;
+ /** The name table. */
+ char szzNames[1];
+} PDB70NAMES, *PPDB70NAMES;
+
+/** The version / magic of the names structure. */
+#define PDB70NAMES_VERSION 20000404
+
+
+static int Pdb70ValidateHeader(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr, size_t cbFile)
+{
+ if (pHdr->cbPage * pHdr->cPages != cbFile)
+ return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - cbPage * cPages != cbFile.");
+ if (pHdr->iStartPage >= pHdr->cPages && pHdr->iStartPage <= 0)
+ return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - iStartPage=%u cPages=%u.",
+ pHdr->iStartPage, pHdr->cPages);
+ if (pHdr->iRootPages >= pHdr->cPages && pHdr->iRootPages <= 0)
+ return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - iRootPages=%u cPage=%u.",
+ pHdr->iStartPage, pHdr->cPages);
+ return 0;
+}
+
+#ifdef DEBUG
+static size_t Pdb70Align(PPDB70HDR pHdr, size_t cb)
+{
+ if (cb == ~(KU32)0 || !cb)
+ return 0;
+ return ((cb + pHdr->cbPage - 1) / pHdr->cbPage) * pHdr->cbPage;
+}
+#endif /* DEBUG */
+
+static size_t Pdb70Pages(PPDB70HDR pHdr, size_t cb)
+{
+ if (cb == ~(KU32)0 || !cb)
+ return 0;
+ return (cb + pHdr->cbPage - 1) / pHdr->cbPage;
+}
+
+static void *Pdb70AllocAndRead(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr, size_t cb, PPDB70PAGE paiPageMap)
+{
+ const size_t cbPage = pHdr->cbPage;
+ size_t cPages = Pdb70Pages(pHdr, cb);
+ KU8 *pbBuf = malloc(cPages * cbPage + 1);
+ if (pbBuf)
+ {
+ size_t iPage = 0;
+ while (iPage < cPages)
+ {
+ size_t off = paiPageMap[iPage];
+ if (off < pHdr->cPages)
+ {
+ off *= cbPage;
+ memcpy(pbBuf + iPage * cbPage, (KU8 *)pHdr + off, cbPage);
+ dump(pbBuf + iPage * cbPage, iPage + 1 < cPages ? cbPage : cb % cbPage, off);
+ }
+ else
+ {
+ warnx(pThis->pCtx, "warning: Invalid page index %u (max %u)!\n", (unsigned)off, pHdr->cPages);
+ memset(pbBuf + iPage * cbPage, 0, cbPage);
+ }
+
+ iPage++;
+ }
+ pbBuf[cPages * cbPage] = '\0';
+ }
+ else
+ {
+ errx(pThis->pCtx, 1, "failed to allocate %lu bytes", (unsigned long)(cPages * cbPage + 1));
+ return NULL;
+ }
+ return pbBuf;
+}
+
+static PPDB70ROOT Pdb70AllocAndReadRoot(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr)
+{
+ /*
+ * The tricky bit here is to find the right length. Really?
+ * (Todo: Check if we can just use the stream #0 size..)
+ */
+ PPDB70PAGE piPageMap = (KU32 *)((KU8 *)pHdr + pHdr->iRootPages * pHdr->cbPage);
+ PPDB70ROOT pRoot = Pdb70AllocAndRead(pThis, pHdr, pHdr->cbRoot, piPageMap);
+ if (pRoot)
+ {
+#if 1
+ /* This stuff is probably unnecessary: */
+ /* size = stream header + array of stream. */
+ size_t cb = K_OFFSETOF(PDB70ROOT, aStreams[pRoot->cStreams]);
+ free(pRoot);
+ pRoot = Pdb70AllocAndRead(pThis, pHdr, cb, piPageMap);
+ if (pRoot)
+ {
+ /* size += page tables. */
+ unsigned iStream = pRoot->cStreams;
+ while (iStream-- > 0)
+ if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
+ cb += Pdb70Pages(pHdr, pRoot->aStreams[iStream].cbStream) * sizeof(PDB70PAGE);
+ free(pRoot);
+ pRoot = Pdb70AllocAndRead(pThis, pHdr, cb, piPageMap);
+ if (pRoot)
+ {
+ /* validate? */
+ return pRoot;
+ }
+ }
+#else
+ /* validate? */
+ return pRoot;
+#endif
+ }
+ return NULL;
+}
+
+static void *Pdb70AllocAndReadStream(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr, PPDB70ROOT pRoot, unsigned iStream, size_t *pcbStream)
+{
+ const size_t cbStream = pRoot->aStreams[iStream].cbStream;
+ PPDB70PAGE paiPageMap;
+ if ( iStream >= pRoot->cStreams
+ || cbStream == ~(KU32)0)
+ {
+ errx(pThis->pCtx, 1, "Invalid stream %d", iStream);
+ return NULL;
+ }
+
+ paiPageMap = (PPDB70PAGE)&pRoot->aStreams[pRoot->cStreams];
+ while (iStream-- > 0)
+ if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
+ paiPageMap += Pdb70Pages(pHdr, pRoot->aStreams[iStream].cbStream);
+
+ if (pcbStream)
+ *pcbStream = cbStream;
+ return Pdb70AllocAndRead(pThis, pHdr, cbStream, paiPageMap);
+}
+
+static int Pdb70Process(PKDEPIDBGLOBALS pThis, KU8 *pbFile, size_t cbFile)
+{
+ PPDB70HDR pHdr = (PPDB70HDR)pbFile;
+ PPDB70ROOT pRoot;
+ PPDB70NAMES pNames;
+ size_t cbStream = 0;
+ unsigned fDone = 0;
+ unsigned iStream;
+ int rc = 0;
+ dprintf(("pdb70\n"));
+
+ /*
+ * Validate the header and read the root stream.
+ */
+ if (Pdb70ValidateHeader(pThis, pHdr, cbFile))
+ return 1;
+ pRoot = Pdb70AllocAndReadRoot(pThis, pHdr);
+ if (!pRoot)
+ return 1;
+
+ /*
+ * The names we want are usually all found in the 'Names' stream, that is #1.
+ */
+ dprintf(("Reading the names stream....\n"));
+ pNames = Pdb70AllocAndReadStream(pThis, pHdr, pRoot, 1, &cbStream);
+ if (pNames)
+ {
+ dprintf(("Names: Version=%u cbNames=%u (%#x)\n", pNames->Version, pNames->cbNames, pNames->cbNames));
+ if ( pNames->Version == PDB70NAMES_VERSION
+ && pNames->cbNames > 32
+ && pNames->cbNames + K_OFFSETOF(PDB70NAMES, szzNames) <= pRoot->aStreams[1].cbStream)
+ {
+ /*
+ * Iterate the names and add the /mr/inversedeps/ ones to the dependency list.
+ */
+ const char *psz = &pNames->szzNames[0];
+ size_t cb = pNames->cbNames;
+ size_t off = 0;
+ dprintf(("0x0000 #0: %6d bytes [root / toc]\n", pRoot->aStreams[0].cbStream));
+ for (iStream = 1; cb > 0; iStream++)
+ {
+ int fAdded = 0;
+ size_t cch = strlen(psz);
+ if ( cch >= sizeof("/mr/inversedeps/")
+ && !memcmp(psz, "/mr/inversedeps/", sizeof("/mr/inversedeps/") - 1))
+ {
+ depAdd(&pThis->Core, psz + sizeof("/mr/inversedeps/") - 1, cch - (sizeof("/mr/inversedeps/") - 1));
+ fAdded = 1;
+ }
+ dprintf(("%#06x #%d: %6d bytes %s%s\n", off, iStream,
+ iStream < pRoot->cStreams ? pRoot->aStreams[iStream].cbStream : -1,
+ psz, fAdded ? " [dep]" : ""));
+ (void)fAdded;
+
+ /* next */
+ if (cch >= cb)
+ {
+ dprintf(("warning! cch=%d cb=%d\n", cch, cb));
+ cch = cb - 1;
+ }
+ cb -= cch + 1;
+ psz += cch + 1;
+ off += cch + 1;
+ }
+ rc = 0;
+ fDone = 1;
+ }
+ else
+ dprintf(("Unknown version or bad size: Version=%u cbNames=%d cbStream=%d\n",
+ pNames->Version, pNames->cbNames, cbStream));
+ free(pNames);
+ }
+
+ if (!fDone)
+ {
+ /*
+ * Iterate the streams in the root and scan their content for
+ * dependencies.
+ */
+ rc = 0;
+ for (iStream = 0; iStream < pRoot->cStreams && !rc; iStream++)
+ {
+ KU8 *pbStream;
+ if ( pRoot->aStreams[iStream].cbStream == ~(KU32)0
+ || !pRoot->aStreams[iStream].cbStream)
+ continue;
+ dprintf(("Stream #%d: %#x bytes (%#x aligned)\n", iStream, pRoot->aStreams[iStream].cbStream,
+ Pdb70Align(pHdr, pRoot->aStreams[iStream].cbStream)));
+ pbStream = (KU8 *)Pdb70AllocAndReadStream(pThis, pHdr, pRoot, iStream, &cbStream);
+ if (pbStream)
+ {
+ rc = ScanStream(pThis, pbStream, cbStream, "/mr/inversedeps/", sizeof("/mr/inversedeps/") - 1);
+ free(pbStream);
+ }
+ else
+ rc = 1;
+ }
+ }
+
+ free(pRoot);
+ return rc;
+}
+
+
+
+/*/////////////////////////////////////////////////////////////////////////////
+//
+//
+// P D B 2 . 0
+//
+//
+/////////////////////////////////////////////////////////////////////////////*/
+
+
+/** A PDB 2.0 Page number. */
+typedef KU16 PDB20PAGE;
+/** Pointer to a PDB 2.0 Page number. */
+typedef PDB20PAGE *PPDB20PAGE;
+
+/**
+ * A PDB 2.0 stream.
+ */
+typedef struct PDB20STREAM
+{
+ /** The size of the stream. */
+ KU32 cbStream;
+ /** Some unknown value. */
+ KU32 u32Unknown;
+} PDB20STREAM, *PPDB20STREAM;
+
+/** The PDB 2.00 signature. */
+#define PDB_SIGNATURE_200 "Microsoft C/C++ program database 2.00\r\n\x1A" "JG\0"
+/**
+ * The PDB 2.0 header.
+ */
+typedef struct PDB20HDR
+{
+ /** The signature string. */
+ KU8 szSignature[sizeof(PDB_SIGNATURE_200)];
+ /** The page size. */
+ KU32 cbPage;
+ /** The start page - whatever that is... */
+ PDB20PAGE iStartPage;
+ /** The number of pages in the file. */
+ PDB20PAGE cPages;
+ /** The root stream directory. */
+ PDB20STREAM RootStream;
+ /** The root page table. */
+ PDB20PAGE aiRootPageMap[1];
+} PDB20HDR, *PPDB20HDR;
+
+/**
+ * The PDB 2.0 root directory.
+ */
+typedef struct PDB20ROOT
+{
+ /** The number of streams */
+ KU16 cStreams;
+ /** Reserved or high part of cStreams. */
+ KU16 u16Reserved;
+ /** Array of streams. */
+ PDB20STREAM aStreams[1];
+} PDB20ROOT, *PPDB20ROOT;
+
+
+static int Pdb20ValidateHeader(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr, size_t cbFile)
+{
+ if (pHdr->cbPage * pHdr->cPages != cbFile)
+ return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - cbPage * cPages != cbFile.");
+ if (pHdr->iStartPage >= pHdr->cPages && pHdr->iStartPage <= 0)
+ return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - cbPage * cPages != cbFile.");
+ return 0;
+}
+
+static size_t Pdb20Pages(PPDB20HDR pHdr, size_t cb)
+{
+ if (cb == ~(KU32)0 || !cb)
+ return 0;
+ return (cb + pHdr->cbPage - 1) / pHdr->cbPage;
+}
+
+static void *Pdb20AllocAndRead(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr, size_t cb, PPDB20PAGE paiPageMap)
+{
+ size_t cPages = Pdb20Pages(pHdr, cb);
+ KU8 *pbBuf = malloc(cPages * pHdr->cbPage + 1);
+ if (pbBuf)
+ {
+ size_t iPage = 0;
+ while (iPage < cPages)
+ {
+ size_t off = paiPageMap[iPage];
+ off *= pHdr->cbPage;
+ memcpy(pbBuf + iPage * pHdr->cbPage, (KU8 *)pHdr + off, pHdr->cbPage);
+ iPage++;
+ }
+ pbBuf[cPages * pHdr->cbPage] = '\0';
+ }
+ else
+ errx(pThis->pCtx, 1, "failed to allocate %lu bytes", (unsigned long)(cPages * pHdr->cbPage + 1));
+ return pbBuf;
+}
+
+static PPDB20ROOT Pdb20AllocAndReadRoot(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr)
+{
+ /*
+ * The tricky bit here is to find the right length.
+ * (Todo: Check if we can just use the stream size..)
+ */
+ PPDB20ROOT pRoot = Pdb20AllocAndRead(pThis, pHdr, sizeof(*pRoot), &pHdr->aiRootPageMap[0]);
+ if (pRoot)
+ {
+ /* size = stream header + array of stream. */
+ size_t cb = K_OFFSETOF(PDB20ROOT, aStreams[pRoot->cStreams]);
+ free(pRoot);
+ pRoot = Pdb20AllocAndRead(pThis, pHdr, cb, &pHdr->aiRootPageMap[0]);
+ if (pRoot)
+ {
+ /* size += page tables. */
+ unsigned iStream = pRoot->cStreams;
+ while (iStream-- > 0)
+ if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
+ cb += Pdb20Pages(pHdr, pRoot->aStreams[iStream].cbStream) * sizeof(PDB20PAGE);
+ free(pRoot);
+ pRoot = Pdb20AllocAndRead(pThis, pHdr, cb, &pHdr->aiRootPageMap[0]);
+ if (pRoot)
+ {
+ /* validate? */
+ return pRoot;
+ }
+ }
+ }
+ return NULL;
+
+}
+
+static void *Pdb20AllocAndReadStream(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr, PPDB20ROOT pRoot, unsigned iStream, size_t *pcbStream)
+{
+ size_t cbStream = pRoot->aStreams[iStream].cbStream;
+ PPDB20PAGE paiPageMap;
+ if ( iStream >= pRoot->cStreams
+ || cbStream == ~(KU32)0)
+ {
+ errx(pThis->pCtx, 1, "Invalid stream %d", iStream);
+ return NULL;
+ }
+
+ paiPageMap = (PPDB20PAGE)&pRoot->aStreams[pRoot->cStreams];
+ while (iStream-- > 0)
+ if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
+ paiPageMap += Pdb20Pages(pHdr, pRoot->aStreams[iStream].cbStream);
+
+ if (pcbStream)
+ *pcbStream = cbStream;
+ return Pdb20AllocAndRead(pThis, pHdr, cbStream, paiPageMap);
+}
+
+static int Pdb20Process(PKDEPIDBGLOBALS pThis, KU8 *pbFile, size_t cbFile)
+{
+ PPDB20HDR pHdr = (PPDB20HDR)pbFile;
+ PPDB20ROOT pRoot;
+ unsigned iStream;
+ int rc = 0;
+
+ /*
+ * Validate the header and read the root stream.
+ */
+ if (Pdb20ValidateHeader(pThis, pHdr, cbFile))
+ return 1;
+ pRoot = Pdb20AllocAndReadRoot(pThis, pHdr);
+ if (!pRoot)
+ return 1;
+
+ /*
+ * Iterate the streams in the root and scan their content for
+ * dependencies.
+ */
+ rc = 0;
+ for (iStream = 0; iStream < pRoot->cStreams && !rc; iStream++)
+ {
+ KU8 *pbStream;
+ if (pRoot->aStreams[iStream].cbStream == ~(KU32)0)
+ continue;
+ pbStream = (KU8 *)Pdb20AllocAndReadStream(pThis, pHdr, pRoot, iStream, NULL);
+ if (pbStream)
+ {
+ rc = ScanStream(pThis, pbStream, pRoot->aStreams[iStream].cbStream, "/ipm/header/", sizeof("/ipm/header/") - 1);
+ free(pbStream);
+ }
+ else
+ rc = 1;
+ }
+
+ free(pRoot);
+ return rc;
+}
+
+
+/**
+ * Make an attempt at parsing a Visual C++ IDB file.
+ */
+static int ProcessIDB(PKDEPIDBGLOBALS pThis, FILE *pInput)
+{
+ size_t cbFile;
+ KU8 *pbFile;
+ void *pvOpaque;
+ int rc = 0;
+
+ /*
+ * Read the file into memory.
+ */
+ pbFile = (KU8 *)depReadFileIntoMemory(pInput, &cbFile, &pvOpaque);
+ if (!pbFile)
+ return 1;
+
+ /*
+ * Figure out which parser to use.
+ */
+ if (!memcmp(pbFile, PDB_SIGNATURE_700, sizeof(PDB_SIGNATURE_700)))
+ rc = Pdb70Process(pThis, pbFile, cbFile);
+ else if (!memcmp(pbFile, PDB_SIGNATURE_200, sizeof(PDB_SIGNATURE_200)))
+ rc = Pdb20Process(pThis, pbFile, cbFile);
+ else
+ rc = errx(pThis->pCtx, 1, "Doesn't recognize the header of the Visual C++ IDB file.");
+
+ depFreeFileMemory(pbFile, pvOpaque);
+ return rc;
+}
+
+
+static void kDepIDBUsage(PKMKBUILTINCTX pCtx, int fIsErr)
+{
+ kmk_builtin_ctx_printf(pCtx, fIsErr,
+ "usage: %s -o <output> -t <target> [-fqs] <vc idb-file>\n"
+ " or: %s --help\n"
+ " or: %s --version\n",
+ pCtx->pszProgName, pCtx->pszProgName, pCtx->pszProgName);
+}
+
+
+int kmk_builtin_kDepIDB(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx)
+{
+ int i;
+ KDEPIDBGLOBALS This;
+
+ /* Arguments. */
+ FILE *pOutput = NULL;
+ const char *pszOutput = NULL;
+ FILE *pInput = NULL;
+ const char *pszTarget = NULL;
+ int fStubs = 0;
+ int fFixCase = 0;
+ /* Argument parsing. */
+ int fInput = 0; /* set when we've found input argument. */
+ int fQuiet = 0;
+
+ /* Init the instance data. */
+ This.pCtx = pCtx;
+
+ /*
+ * Parse arguments.
+ */
+ if (argc <= 1)
+ {
+ kDepIDBUsage(pCtx, 0);
+ return 1;
+ }
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ const char *psz = &argv[i][1];
+ if (*psz == '-')
+ {
+ if (!strcmp(psz, "-quiet"))
+ psz = "q";
+ else if (!strcmp(psz, "-help"))
+ psz = "?";
+ else if (!strcmp(psz, "-version"))
+ psz = "V";
+ }
+
+ switch (*psz)
+ {
+ /*
+ * Output file.
+ */
+ case 'o':
+ {
+ pszOutput = &argv[i][2];
+ if (pOutput)
+ return errx(pCtx, 2, "only one output file!");
+ if (!*pszOutput)
+ {
+ if (++i >= argc)
+ return errx(pCtx, 2, "The '-o' argument is missing the filename.");
+ pszOutput = argv[i];
+ }
+ if (pszOutput[0] == '-' && !pszOutput[1])
+ pOutput = stdout;
+ else
+ pOutput = fopen(pszOutput, "w" KMK_FOPEN_NO_INHERIT_MODE);
+ if (!pOutput)
+ return err(pCtx, 1, "Failed to create output file '%s'", pszOutput);
+ break;
+ }
+
+ /*
+ * Target name.
+ */
+ case 't':
+ {
+ if (pszTarget)
+ return errx(pCtx, 2, "only one target!");
+ pszTarget = &argv[i][2];
+ if (!*pszTarget)
+ {
+ if (++i >= argc)
+ return errx(pCtx, 2, "The '-t' argument is missing the target name.");
+ pszTarget = argv[i];
+ }
+ break;
+ }
+
+ /*
+ * Fix case.
+ */
+ case 'f':
+ {
+ fFixCase = 1;
+ break;
+ }
+
+ /*
+ * Quiet.
+ */
+ case 'q':
+ {
+ fQuiet = 1;
+ break;
+ }
+
+ /*
+ * Generate stubs.
+ */
+ case 's':
+ {
+ fStubs = 1;
+ break;
+ }
+
+ /*
+ * The mandatory version & help.
+ */
+ case '?':
+ kDepIDBUsage(pCtx, 0);
+ return 0;
+ case 'V':
+ case 'v':
+ return kbuild_version(pCtx->pszProgName);
+
+ /*
+ * Invalid argument.
+ */
+ default:
+ errx(pCtx, 2, "Invalid argument '%s.'", argv[i]);
+ kDepIDBUsage(pCtx, 1);
+ return 2;
+ }
+ }
+ else
+ {
+ pInput = fopen(argv[i], "rb" KMK_FOPEN_NO_INHERIT_MODE);
+ if (!pInput)
+ return err(pCtx, 1, "Failed to open input file '%s'", argv[i]);
+ fInput = 1;
+ }
+
+ /*
+ * End of the line?
+ */
+ if (fInput)
+ {
+ if (++i < argc)
+ return errx(pCtx, 2, "No arguments shall follow the input spec.");
+ break;
+ }
+ }
+
+ /*
+ * Got all we require?
+ */
+ if (!pInput)
+ return errx(pCtx, 2, "No input!");
+ if (!pOutput)
+ return errx(pCtx, 2, "No output!");
+ if (!pszTarget)
+ return errx(pCtx, 2, "No target!");
+
+ /*
+ * Do the parsing.
+ */
+ depInit(&This.Core);
+ i = ProcessIDB(&This, pInput);
+ fclose(pInput);
+
+ /*
+ * Write the dependecy file.
+ */
+ if (!i)
+ {
+ depOptimize(&This.Core, fFixCase, fQuiet, NULL /*pszIgnoredExt*/);
+ depPrintTargetWithDeps(&This.Core, pOutput, pszTarget, 1 /*fEscapeTarget*/);
+ if (fStubs)
+ depPrintStubs(&This.Core, pOutput);
+ }
+
+ /*
+ * Close the output, delete output on failure.
+ */
+ if (!i && ferror(pOutput))
+ i = errx(pCtx, 1, "Error writing to '%s'.", pszOutput);
+ fclose(pOutput);
+ if (i)
+ {
+ if (unlink(pszOutput))
+ warnx(pCtx, "warning: failed to remove output file '%s' on failure.", pszOutput);
+ }
+
+ depCleanup(&This.Core);
+ return i;
+}
+
+#ifdef KMK_BUILTIN_STANDALONE
+int main(int argc, char **argv, char **envp)
+{
+ KMKBUILTINCTX Ctx = { "kmk_kDepIDB", NULL };
+ return kmk_builtin_kDepIDB(argc, argv, envp, &Ctx);
+}
+#endif
+