From 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:21:29 +0200 Subject: Adding upstream version 1:0.1.9998svn3589+dfsg. Signed-off-by: Daniel Baumann --- src/kmk/kmkbuiltin/kDepIDB.c | 860 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 860 insertions(+) create mode 100644 src/kmk/kmkbuiltin/kDepIDB.c (limited to 'src/kmk/kmkbuiltin/kDepIDB.c') 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 + * + * 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 + * + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "config.h" +#include +#include +#include +#include +#include +#include +#ifdef HAVE_ALLOCA_H +# include +#endif +#if !defined(_MSC_VER) +# include +#else +# include +#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 -t [-fqs] \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 + -- cgit v1.2.3