source: trunk/src/kmk/kmkbuiltin/kDepIDB.c

Last change on this file was 3315, checked in by bird, 5 years ago

lib/kDep+users: Escape spaces and such in dependency files (experimental).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 25.3 KB
RevLine 
[393]1/* $Id: kDepIDB.c 3315 2020-03-31 01:12:19Z bird $ */
2/** @file
3 * kDepIDB - Extract dependency information from a MS Visual C++ .idb file.
[2019]4 */
5
6/*
[2413]7 * Copyright (c) 2007-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
[393]8 *
9 * This file is part of kBuild.
10 *
11 * kBuild is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
[2019]13 * the Free Software Foundation; either version 3 of the License, or
[393]14 * (at your option) any later version.
15 *
16 * kBuild is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
[2019]22 * along with kBuild. If not, see <http://www.gnu.org/licenses/>
[393]23 *
24 */
25
[3167]26
27/*********************************************************************************************************************************
28* Header Files *
29*********************************************************************************************************************************/
[2113]30#include "config.h"
[393]31#include <stdio.h>
32#include <stdlib.h>
[860]33#include <stddef.h>
[393]34#include <string.h>
35#include <errno.h>
36#include <ctype.h>
[2121]37#ifdef HAVE_ALLOCA_H
38# include <alloca.h>
39#endif
[1162]40#if !defined(_MSC_VER)
[2115]41# include <unistd.h>
[393]42#else
[1165]43# include <io.h>
[393]44#endif
[2856]45#include "k/kDefs.h"
46#include "k/kTypes.h"
47#include "kDep.h"
[3192]48#include "err.h"
[1162]49#include "kmkbuiltin.h"
[393]50
51
[3167]52/*********************************************************************************************************************************
53* Defined Constants And Macros *
54*********************************************************************************************************************************/
[866]55/*#define DEBUG*/
[398]56#ifdef DEBUG
[860]57# define dprintf(a) printf a
[2263]58# define dump(pb, cb, offBase) depHexDump(pb,cb,offBase)
[398]59#else
[860]60# define dprintf(a) do {} while (0)
61# define dump(pb, cb, offBase) do {} while (0)
[713]62#endif
[393]63
[3167]64/*********************************************************************************************************************************
65* Structures and Typedefs *
66*********************************************************************************************************************************/
67typedef struct KDEPIDBGLOBALS
68{
[3192]69 PKMKBUILTINCTX pCtx;
[3167]70 DEPGLOBALS Core;
71} KDEPIDBGLOBALS;
72typedef KDEPIDBGLOBALS *PKDEPIDBGLOBALS;
[393]73
74
75/**
76 * Scans a stream (chunk of data really) for dependencies.
[713]77 *
[393]78 * @returns 0 on success.
79 * @returns !0 on failure.
[3167]80 * @param pThis The kDepIDB instance.
[393]81 * @param pbStream The stream bits.
82 * @param cbStream The size of the stream.
83 * @param pszPrefix The dependency prefix.
84 * @param cchPrefix The size of the prefix.
85 */
[3167]86static int ScanStream(PKDEPIDBGLOBALS pThis, KU8 *pbStream, size_t cbStream, const char *pszPrefix, size_t cchPrefix)
[393]87{
[2263]88 const KU8 *pbCur = pbStream;
[398]89 size_t cbLeft = cbStream;
90 register char chFirst = *pszPrefix;
91 while (cbLeft > cchPrefix + 2)
[393]92 {
[398]93 if ( *pbCur != chFirst
94 || memcmp(pbCur, pszPrefix, cchPrefix))
[393]95 {
[398]96 pbCur++;
97 cbLeft--;
[393]98 }
99 else
100 {
101 size_t cchDep;
[398]102 pbCur += cchPrefix;
[1329]103 cchDep = strlen((const char *)pbCur);
[3167]104 depAdd(&pThis->Core, (const char *) pbCur, cchDep);
[398]105 dprintf(("%05x: '%s'\n", pbCur - pbStream, pbCur));
[393]106
[398]107 pbCur += cchDep;
108 cbLeft -= cchDep + cchPrefix;
[393]109 }
110 }
111
112 return 0;
113}
114
115
[2115]116/*/////////////////////////////////////////////////////////////////////////////
[713]117//
118//
[393]119// P D B 7 . 0
[713]120//
121//
[2115]122/////////////////////////////////////////////////////////////////////////////*/
[393]123
124/** A PDB 7.0 Page number. */
[2263]125typedef KU32 PDB70PAGE;
[393]126/** Pointer to a PDB 7.0 Page number. */
127typedef PDB70PAGE *PPDB70PAGE;
128
129/**
130 * A PDB 7.0 stream.
131 */
132typedef struct PDB70STREAM
133{
134 /** The size of the stream. */
[2263]135 KU32 cbStream;
[393]136} PDB70STREAM, *PPDB70STREAM;
137
138
139/** The PDB 7.00 signature. */
140#define PDB_SIGNATURE_700 "Microsoft C/C++ MSF 7.00\r\n\x1A" "DS\0\0"
141/**
142 * The PDB 7.0 header.
143 */
144typedef struct PDB70HDR
145{
146 /** The signature string. */
[2263]147 KU8 szSignature[sizeof(PDB_SIGNATURE_700)];
[393]148 /** The page size. */
[2263]149 KU32 cbPage;
[393]150 /** The start page. */
151 PDB70PAGE iStartPage;
152 /** The number of pages in the file. */
153 PDB70PAGE cPages;
154 /** The root stream directory. */
[2263]155 KU32 cbRoot;
[393]156 /** Unknown function, always 0. */
[2263]157 KU32 u32Reserved;
[393]158 /** The page index of the root page table. */
159 PDB70PAGE iRootPages;
160} PDB70HDR, *PPDB70HDR;
161
162/**
163 * The PDB 7.0 root directory.
164 */
165typedef struct PDB70ROOT
166{
167 /** The number of streams */
[2263]168 KU32 cStreams;
[393]169 /** Array of streams. */
170 PDB70STREAM aStreams[1];
[2263]171 /* KU32 aiPages[] */
[393]172} PDB70ROOT, *PPDB70ROOT;
173
[860]174/**
175 * The PDB 7.0 name stream (#1) header.
176 */
177typedef struct PDB70NAMES
178{
179 /** The structure version. */
[2263]180 KU32 Version;
[860]181 /** Timestamp. */
[2263]182 KU32 TimeStamp;
[860]183 /** Unknown. */
[2263]184 KU32 Unknown1;
[860]185 /** GUID. */
[2263]186 KU32 u32Guid[4];
[860]187 /** The size of the following name table. */
[2263]188 KU32 cbNames;
[860]189 /** The name table. */
190 char szzNames[1];
191} PDB70NAMES, *PPDB70NAMES;
[393]192
[860]193/** The version / magic of the names structure. */
194#define PDB70NAMES_VERSION 20000404
[393]195
[860]196
[3167]197static int Pdb70ValidateHeader(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr, size_t cbFile)
[393]198{
199 if (pHdr->cbPage * pHdr->cPages != cbFile)
[3192]200 return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - cbPage * cPages != cbFile.");
[393]201 if (pHdr->iStartPage >= pHdr->cPages && pHdr->iStartPage <= 0)
[3192]202 return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - iStartPage=%u cPages=%u.",
203 pHdr->iStartPage, pHdr->cPages);
[398]204 if (pHdr->iRootPages >= pHdr->cPages && pHdr->iRootPages <= 0)
[3192]205 return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - iRootPages=%u cPage=%u.",
206 pHdr->iStartPage, pHdr->cPages);
[393]207 return 0;
208}
209
[2115]210#ifdef DEBUG
[398]211static size_t Pdb70Align(PPDB70HDR pHdr, size_t cb)
212{
[2263]213 if (cb == ~(KU32)0 || !cb)
[398]214 return 0;
215 return ((cb + pHdr->cbPage - 1) / pHdr->cbPage) * pHdr->cbPage;
216}
[2115]217#endif /* DEBUG */
[398]218
[393]219static size_t Pdb70Pages(PPDB70HDR pHdr, size_t cb)
220{
[2263]221 if (cb == ~(KU32)0 || !cb)
[393]222 return 0;
223 return (cb + pHdr->cbPage - 1) / pHdr->cbPage;
224}
225
[3167]226static void *Pdb70AllocAndRead(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr, size_t cb, PPDB70PAGE paiPageMap)
[393]227{
[398]228 const size_t cbPage = pHdr->cbPage;
229 size_t cPages = Pdb70Pages(pHdr, cb);
[2263]230 KU8 *pbBuf = malloc(cPages * cbPage + 1);
[393]231 if (pbBuf)
232 {
233 size_t iPage = 0;
234 while (iPage < cPages)
235 {
236 size_t off = paiPageMap[iPage];
[398]237 if (off < pHdr->cPages)
238 {
239 off *= cbPage;
[2263]240 memcpy(pbBuf + iPage * cbPage, (KU8 *)pHdr + off, cbPage);
[860]241 dump(pbBuf + iPage * cbPage, iPage + 1 < cPages ? cbPage : cb % cbPage, off);
[398]242 }
243 else
244 {
[3192]245 warnx(pThis->pCtx, "warning: Invalid page index %u (max %u)!\n", (unsigned)off, pHdr->cPages);
[398]246 memset(pbBuf + iPage * cbPage, 0, cbPage);
247 }
248
[393]249 iPage++;
250 }
[398]251 pbBuf[cPages * cbPage] = '\0';
[393]252 }
253 else
[3192]254 {
255 errx(pThis->pCtx, 1, "failed to allocate %lu bytes", (unsigned long)(cPages * cbPage + 1));
256 return NULL;
257 }
[393]258 return pbBuf;
259}
260
[3167]261static PPDB70ROOT Pdb70AllocAndReadRoot(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr)
[393]262{
263 /*
[860]264 * The tricky bit here is to find the right length. Really?
265 * (Todo: Check if we can just use the stream #0 size..)
[393]266 */
[2263]267 PPDB70PAGE piPageMap = (KU32 *)((KU8 *)pHdr + pHdr->iRootPages * pHdr->cbPage);
[3167]268 PPDB70ROOT pRoot = Pdb70AllocAndRead(pThis, pHdr, pHdr->cbRoot, piPageMap);
[393]269 if (pRoot)
270 {
[860]271#if 1
[393]272 /* This stuff is probably unnecessary: */
273 /* size = stream header + array of stream. */
[2263]274 size_t cb = K_OFFSETOF(PDB70ROOT, aStreams[pRoot->cStreams]);
[393]275 free(pRoot);
[3167]276 pRoot = Pdb70AllocAndRead(pThis, pHdr, cb, piPageMap);
[393]277 if (pRoot)
278 {
279 /* size += page tables. */
280 unsigned iStream = pRoot->cStreams;
281 while (iStream-- > 0)
[2263]282 if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
[393]283 cb += Pdb70Pages(pHdr, pRoot->aStreams[iStream].cbStream) * sizeof(PDB70PAGE);
284 free(pRoot);
[3167]285 pRoot = Pdb70AllocAndRead(pThis, pHdr, cb, piPageMap);
[393]286 if (pRoot)
287 {
288 /* validate? */
289 return pRoot;
290 }
291 }
[860]292#else
293 /* validate? */
294 return pRoot;
[1162]295#endif
[393]296 }
297 return NULL;
298}
299
[3167]300static void *Pdb70AllocAndReadStream(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr, PPDB70ROOT pRoot, unsigned iStream, size_t *pcbStream)
[393]301{
[860]302 const size_t cbStream = pRoot->aStreams[iStream].cbStream;
303 PPDB70PAGE paiPageMap;
[393]304 if ( iStream >= pRoot->cStreams
[2263]305 || cbStream == ~(KU32)0)
[393]306 {
[3192]307 errx(pThis->pCtx, 1, "Invalid stream %d", iStream);
[393]308 return NULL;
309 }
310
311 paiPageMap = (PPDB70PAGE)&pRoot->aStreams[pRoot->cStreams];
312 while (iStream-- > 0)
[2263]313 if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
[398]314 paiPageMap += Pdb70Pages(pHdr, pRoot->aStreams[iStream].cbStream);
[393]315
316 if (pcbStream)
317 *pcbStream = cbStream;
[3167]318 return Pdb70AllocAndRead(pThis, pHdr, cbStream, paiPageMap);
[393]319}
320
[3167]321static int Pdb70Process(PKDEPIDBGLOBALS pThis, KU8 *pbFile, size_t cbFile)
[393]322{
323 PPDB70HDR pHdr = (PPDB70HDR)pbFile;
324 PPDB70ROOT pRoot;
[860]325 PPDB70NAMES pNames;
[2115]326 size_t cbStream = 0;
[860]327 unsigned fDone = 0;
[393]328 unsigned iStream;
329 int rc = 0;
[398]330 dprintf(("pdb70\n"));
[393]331
332 /*
333 * Validate the header and read the root stream.
334 */
[3167]335 if (Pdb70ValidateHeader(pThis, pHdr, cbFile))
[393]336 return 1;
[3167]337 pRoot = Pdb70AllocAndReadRoot(pThis, pHdr);
[393]338 if (!pRoot)
339 return 1;
340
341 /*
[860]342 * The names we want are usually all found in the 'Names' stream, that is #1.
[393]343 */
[860]344 dprintf(("Reading the names stream....\n"));
[3167]345 pNames = Pdb70AllocAndReadStream(pThis, pHdr, pRoot, 1, &cbStream);
[860]346 if (pNames)
[393]347 {
[860]348 dprintf(("Names: Version=%u cbNames=%u (%#x)\n", pNames->Version, pNames->cbNames, pNames->cbNames));
349 if ( pNames->Version == PDB70NAMES_VERSION
350 && pNames->cbNames > 32
[2263]351 && pNames->cbNames + K_OFFSETOF(PDB70NAMES, szzNames) <= pRoot->aStreams[1].cbStream)
[393]352 {
[860]353 /*
354 * Iterate the names and add the /mr/inversedeps/ ones to the dependency list.
355 */
356 const char *psz = &pNames->szzNames[0];
357 size_t cb = pNames->cbNames;
358 size_t off = 0;
359 dprintf(("0x0000 #0: %6d bytes [root / toc]\n", pRoot->aStreams[0].cbStream));
360 for (iStream = 1; cb > 0; iStream++)
361 {
362 int fAdded = 0;
363 size_t cch = strlen(psz);
364 if ( cch >= sizeof("/mr/inversedeps/")
365 && !memcmp(psz, "/mr/inversedeps/", sizeof("/mr/inversedeps/") - 1))
366 {
[3167]367 depAdd(&pThis->Core, psz + sizeof("/mr/inversedeps/") - 1, cch - (sizeof("/mr/inversedeps/") - 1));
[860]368 fAdded = 1;
369 }
[1162]370 dprintf(("%#06x #%d: %6d bytes %s%s\n", off, iStream,
371 iStream < pRoot->cStreams ? pRoot->aStreams[iStream].cbStream : -1,
372 psz, fAdded ? " [dep]" : ""));
[860]373 (void)fAdded;
374
375 /* next */
376 if (cch >= cb)
377 {
378 dprintf(("warning! cch=%d cb=%d\n", cch, cb));
379 cch = cb - 1;
380 }
381 cb -= cch + 1;
382 psz += cch + 1;
383 off += cch + 1;
384 }
385 rc = 0;
386 fDone = 1;
[393]387 }
388 else
[1162]389 dprintf(("Unknown version or bad size: Version=%u cbNames=%d cbStream=%d\n",
[860]390 pNames->Version, pNames->cbNames, cbStream));
391 free(pNames);
[393]392 }
393
[860]394 if (!fDone)
395 {
396 /*
397 * Iterate the streams in the root and scan their content for
398 * dependencies.
399 */
400 rc = 0;
401 for (iStream = 0; iStream < pRoot->cStreams && !rc; iStream++)
402 {
[2263]403 KU8 *pbStream;
404 if ( pRoot->aStreams[iStream].cbStream == ~(KU32)0
[860]405 || !pRoot->aStreams[iStream].cbStream)
406 continue;
[1162]407 dprintf(("Stream #%d: %#x bytes (%#x aligned)\n", iStream, pRoot->aStreams[iStream].cbStream,
[860]408 Pdb70Align(pHdr, pRoot->aStreams[iStream].cbStream)));
[3167]409 pbStream = (KU8 *)Pdb70AllocAndReadStream(pThis, pHdr, pRoot, iStream, &cbStream);
[860]410 if (pbStream)
411 {
[3167]412 rc = ScanStream(pThis, pbStream, cbStream, "/mr/inversedeps/", sizeof("/mr/inversedeps/") - 1);
[860]413 free(pbStream);
414 }
415 else
416 rc = 1;
417 }
418 }
419
[393]420 free(pRoot);
421 return rc;
422}
423
424
425
[2115]426/*/////////////////////////////////////////////////////////////////////////////
[713]427//
428//
[393]429// P D B 2 . 0
[713]430//
431//
[2115]432/////////////////////////////////////////////////////////////////////////////*/
[393]433
434
435/** A PDB 2.0 Page number. */
[2263]436typedef KU16 PDB20PAGE;
[393]437/** Pointer to a PDB 2.0 Page number. */
438typedef PDB20PAGE *PPDB20PAGE;
439
440/**
441 * A PDB 2.0 stream.
442 */
443typedef struct PDB20STREAM
444{
445 /** The size of the stream. */
[2263]446 KU32 cbStream;
[393]447 /** Some unknown value. */
[2263]448 KU32 u32Unknown;
[393]449} PDB20STREAM, *PPDB20STREAM;
450
451/** The PDB 2.00 signature. */
452#define PDB_SIGNATURE_200 "Microsoft C/C++ program database 2.00\r\n\x1A" "JG\0"
453/**
454 * The PDB 2.0 header.
455 */
456typedef struct PDB20HDR
457{
458 /** The signature string. */
[2263]459 KU8 szSignature[sizeof(PDB_SIGNATURE_200)];
[393]460 /** The page size. */
[2263]461 KU32 cbPage;
[393]462 /** The start page - whatever that is... */
463 PDB20PAGE iStartPage;
464 /** The number of pages in the file. */
465 PDB20PAGE cPages;
466 /** The root stream directory. */
467 PDB20STREAM RootStream;
468 /** The root page table. */
469 PDB20PAGE aiRootPageMap[1];
470} PDB20HDR, *PPDB20HDR;
471
472/**
473 * The PDB 2.0 root directory.
474 */
475typedef struct PDB20ROOT
476{
477 /** The number of streams */
[2263]478 KU16 cStreams;
[393]479 /** Reserved or high part of cStreams. */
[2263]480 KU16 u16Reserved;
[393]481 /** Array of streams. */
482 PDB20STREAM aStreams[1];
483} PDB20ROOT, *PPDB20ROOT;
484
485
[3167]486static int Pdb20ValidateHeader(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr, size_t cbFile)
[393]487{
488 if (pHdr->cbPage * pHdr->cPages != cbFile)
[3192]489 return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - cbPage * cPages != cbFile.");
[393]490 if (pHdr->iStartPage >= pHdr->cPages && pHdr->iStartPage <= 0)
[3192]491 return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - cbPage * cPages != cbFile.");
[393]492 return 0;
493}
494
495static size_t Pdb20Pages(PPDB20HDR pHdr, size_t cb)
496{
[2263]497 if (cb == ~(KU32)0 || !cb)
[393]498 return 0;
499 return (cb + pHdr->cbPage - 1) / pHdr->cbPage;
500}
501
[3167]502static void *Pdb20AllocAndRead(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr, size_t cb, PPDB20PAGE paiPageMap)
[393]503{
504 size_t cPages = Pdb20Pages(pHdr, cb);
[2263]505 KU8 *pbBuf = malloc(cPages * pHdr->cbPage + 1);
[393]506 if (pbBuf)
507 {
508 size_t iPage = 0;
509 while (iPage < cPages)
510 {
511 size_t off = paiPageMap[iPage];
512 off *= pHdr->cbPage;
[2263]513 memcpy(pbBuf + iPage * pHdr->cbPage, (KU8 *)pHdr + off, pHdr->cbPage);
[393]514 iPage++;
515 }
516 pbBuf[cPages * pHdr->cbPage] = '\0';
517 }
518 else
[3192]519 errx(pThis->pCtx, 1, "failed to allocate %lu bytes", (unsigned long)(cPages * pHdr->cbPage + 1));
[393]520 return pbBuf;
521}
522
[3167]523static PPDB20ROOT Pdb20AllocAndReadRoot(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr)
[393]524{
525 /*
526 * The tricky bit here is to find the right length.
527 * (Todo: Check if we can just use the stream size..)
528 */
[3167]529 PPDB20ROOT pRoot = Pdb20AllocAndRead(pThis, pHdr, sizeof(*pRoot), &pHdr->aiRootPageMap[0]);
[393]530 if (pRoot)
531 {
532 /* size = stream header + array of stream. */
[2263]533 size_t cb = K_OFFSETOF(PDB20ROOT, aStreams[pRoot->cStreams]);
[393]534 free(pRoot);
[3167]535 pRoot = Pdb20AllocAndRead(pThis, pHdr, cb, &pHdr->aiRootPageMap[0]);
[393]536 if (pRoot)
537 {
538 /* size += page tables. */
539 unsigned iStream = pRoot->cStreams;
540 while (iStream-- > 0)
[2263]541 if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
[393]542 cb += Pdb20Pages(pHdr, pRoot->aStreams[iStream].cbStream) * sizeof(PDB20PAGE);
543 free(pRoot);
[3167]544 pRoot = Pdb20AllocAndRead(pThis, pHdr, cb, &pHdr->aiRootPageMap[0]);
[393]545 if (pRoot)
546 {
547 /* validate? */
548 return pRoot;
549 }
550 }
551 }
552 return NULL;
553
554}
555
[3167]556static void *Pdb20AllocAndReadStream(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr, PPDB20ROOT pRoot, unsigned iStream, size_t *pcbStream)
[393]557{
558 size_t cbStream = pRoot->aStreams[iStream].cbStream;
559 PPDB20PAGE paiPageMap;
560 if ( iStream >= pRoot->cStreams
[2263]561 || cbStream == ~(KU32)0)
[393]562 {
[3192]563 errx(pThis->pCtx, 1, "Invalid stream %d", iStream);
[393]564 return NULL;
565 }
566
567 paiPageMap = (PPDB20PAGE)&pRoot->aStreams[pRoot->cStreams];
568 while (iStream-- > 0)
[2263]569 if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
[393]570 paiPageMap += Pdb20Pages(pHdr, pRoot->aStreams[iStream].cbStream);
571
572 if (pcbStream)
573 *pcbStream = cbStream;
[3167]574 return Pdb20AllocAndRead(pThis, pHdr, cbStream, paiPageMap);
[393]575}
576
[3167]577static int Pdb20Process(PKDEPIDBGLOBALS pThis, KU8 *pbFile, size_t cbFile)
[393]578{
579 PPDB20HDR pHdr = (PPDB20HDR)pbFile;
580 PPDB20ROOT pRoot;
581 unsigned iStream;
582 int rc = 0;
583
584 /*
585 * Validate the header and read the root stream.
586 */
[3167]587 if (Pdb20ValidateHeader(pThis, pHdr, cbFile))
[393]588 return 1;
[3167]589 pRoot = Pdb20AllocAndReadRoot(pThis, pHdr);
[393]590 if (!pRoot)
591 return 1;
592
593 /*
[713]594 * Iterate the streams in the root and scan their content for
[393]595 * dependencies.
596 */
597 rc = 0;
598 for (iStream = 0; iStream < pRoot->cStreams && !rc; iStream++)
599 {
[2263]600 KU8 *pbStream;
601 if (pRoot->aStreams[iStream].cbStream == ~(KU32)0)
[393]602 continue;
[3167]603 pbStream = (KU8 *)Pdb20AllocAndReadStream(pThis, pHdr, pRoot, iStream, NULL);
[393]604 if (pbStream)
605 {
[3167]606 rc = ScanStream(pThis, pbStream, pRoot->aStreams[iStream].cbStream, "/ipm/header/", sizeof("/ipm/header/") - 1);
[393]607 free(pbStream);
608 }
609 else
610 rc = 1;
611 }
612
613 free(pRoot);
614 return rc;
615}
616
617
618/**
619 * Make an attempt at parsing a Visual C++ IDB file.
620 */
[3167]621static int ProcessIDB(PKDEPIDBGLOBALS pThis, FILE *pInput)
[393]622{
[1329]623 size_t cbFile;
[2263]624 KU8 *pbFile;
625 void *pvOpaque;
[1329]626 int rc = 0;
[393]627
628 /*
629 * Read the file into memory.
630 */
[2263]631 pbFile = (KU8 *)depReadFileIntoMemory(pInput, &cbFile, &pvOpaque);
[393]632 if (!pbFile)
633 return 1;
634
635 /*
636 * Figure out which parser to use.
637 */
638 if (!memcmp(pbFile, PDB_SIGNATURE_700, sizeof(PDB_SIGNATURE_700)))
[3167]639 rc = Pdb70Process(pThis, pbFile, cbFile);
[393]640 else if (!memcmp(pbFile, PDB_SIGNATURE_200, sizeof(PDB_SIGNATURE_200)))
[3167]641 rc = Pdb20Process(pThis, pbFile, cbFile);
[393]642 else
[3192]643 rc = errx(pThis->pCtx, 1, "Doesn't recognize the header of the Visual C++ IDB file.");
[393]644
[2263]645 depFreeFileMemory(pbFile, pvOpaque);
[393]646 return rc;
647}
648
[713]649
[3192]650static void kDepIDBUsage(PKMKBUILTINCTX pCtx, int fIsErr)
[393]651{
[3192]652 kmk_builtin_ctx_printf(pCtx, fIsErr,
653 "usage: %s -o <output> -t <target> [-fqs] <vc idb-file>\n"
654 " or: %s --help\n"
655 " or: %s --version\n",
656 pCtx->pszProgName, pCtx->pszProgName, pCtx->pszProgName);
[393]657}
658
659
[3192]660int kmk_builtin_kDepIDB(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx)
[393]661{
[3167]662 int i;
663 KDEPIDBGLOBALS This;
[393]664
665 /* Arguments. */
666 FILE *pOutput = NULL;
667 const char *pszOutput = NULL;
668 FILE *pInput = NULL;
669 const char *pszTarget = NULL;
670 int fStubs = 0;
671 int fFixCase = 0;
672 /* Argument parsing. */
673 int fInput = 0; /* set when we've found input argument. */
[2104]674 int fQuiet = 0;
[393]675
[3167]676 /* Init the instance data. */
[3192]677 This.pCtx = pCtx;
[393]678
679 /*
680 * Parse arguments.
681 */
682 if (argc <= 1)
683 {
[3192]684 kDepIDBUsage(pCtx, 0);
[393]685 return 1;
686 }
687 for (i = 1; i < argc; i++)
688 {
689 if (argv[i][0] == '-')
690 {
[1183]691 const char *psz = &argv[i][1];
692 if (*psz == '-')
[393]693 {
[2104]694 if (!strcmp(psz, "-quiet"))
695 psz = "q";
696 else if (!strcmp(psz, "-help"))
[1183]697 psz = "?";
698 else if (!strcmp(psz, "-version"))
[2104]699 psz = "V";
[1183]700 }
701
702 switch (*psz)
703 {
[393]704 /*
705 * Output file.
706 */
707 case 'o':
708 {
709 pszOutput = &argv[i][2];
710 if (pOutput)
[3192]711 return errx(pCtx, 2, "only one output file!");
[393]712 if (!*pszOutput)
713 {
714 if (++i >= argc)
[3192]715 return errx(pCtx, 2, "The '-o' argument is missing the filename.");
[393]716 pszOutput = argv[i];
717 }
718 if (pszOutput[0] == '-' && !pszOutput[1])
719 pOutput = stdout;
720 else
[3159]721 pOutput = fopen(pszOutput, "w" KMK_FOPEN_NO_INHERIT_MODE);
[393]722 if (!pOutput)
[3192]723 return err(pCtx, 1, "Failed to create output file '%s'", pszOutput);
[393]724 break;
725 }
726
727 /*
728 * Target name.
729 */
730 case 't':
731 {
732 if (pszTarget)
[3192]733 return errx(pCtx, 2, "only one target!");
[393]734 pszTarget = &argv[i][2];
735 if (!*pszTarget)
736 {
737 if (++i >= argc)
[3192]738 return errx(pCtx, 2, "The '-t' argument is missing the target name.");
[393]739 pszTarget = argv[i];
740 }
741 break;
742 }
743
744 /*
745 * Fix case.
746 */
747 case 'f':
748 {
749 fFixCase = 1;
750 break;
751 }
752
753 /*
[2104]754 * Quiet.
755 */
756 case 'q':
757 {
758 fQuiet = 1;
759 break;
760 }
761
762 /*
[393]763 * Generate stubs.
764 */
765 case 's':
766 {
767 fStubs = 1;
768 break;
769 }
770
771 /*
[1183]772 * The mandatory version & help.
773 */
774 case '?':
[3192]775 kDepIDBUsage(pCtx, 0);
[1183]776 return 0;
[2104]777 case 'V':
[1183]778 case 'v':
[3192]779 return kbuild_version(pCtx->pszProgName);
[1183]780
781 /*
[393]782 * Invalid argument.
783 */
784 default:
[3192]785 errx(pCtx, 2, "Invalid argument '%s.'", argv[i]);
786 kDepIDBUsage(pCtx, 1);
787 return 2;
[393]788 }
789 }
790 else
791 {
[3159]792 pInput = fopen(argv[i], "rb" KMK_FOPEN_NO_INHERIT_MODE);
[393]793 if (!pInput)
[3192]794 return err(pCtx, 1, "Failed to open input file '%s'", argv[i]);
[393]795 fInput = 1;
796 }
797
798 /*
799 * End of the line?
800 */
801 if (fInput)
802 {
803 if (++i < argc)
[3192]804 return errx(pCtx, 2, "No arguments shall follow the input spec.");
[393]805 break;
806 }
807 }
808
809 /*
810 * Got all we require?
811 */
812 if (!pInput)
[3192]813 return errx(pCtx, 2, "No input!");
[393]814 if (!pOutput)
[3192]815 return errx(pCtx, 2, "No output!");
[393]816 if (!pszTarget)
[3192]817 return errx(pCtx, 2, "No target!");
[393]818
819 /*
820 * Do the parsing.
821 */
[3167]822 depInit(&This.Core);
823 i = ProcessIDB(&This, pInput);
[1269]824 fclose(pInput);
[393]825
826 /*
827 * Write the dependecy file.
828 */
829 if (!i)
830 {
[3167]831 depOptimize(&This.Core, fFixCase, fQuiet, NULL /*pszIgnoredExt*/);
[3315]832 depPrintTargetWithDeps(&This.Core, pOutput, pszTarget, 1 /*fEscapeTarget*/);
[393]833 if (fStubs)
[3167]834 depPrintStubs(&This.Core, pOutput);
[393]835 }
836
837 /*
838 * Close the output, delete output on failure.
839 */
840 if (!i && ferror(pOutput))
[3192]841 i = errx(pCtx, 1, "Error writing to '%s'.", pszOutput);
[393]842 fclose(pOutput);
843 if (i)
844 {
845 if (unlink(pszOutput))
[3192]846 warnx(pCtx, "warning: failed to remove output file '%s' on failure.", pszOutput);
[393]847 }
848
[3167]849 depCleanup(&This.Core);
[393]850 return i;
851}
852
[3192]853#ifdef KMK_BUILTIN_STANDALONE
854int main(int argc, char **argv, char **envp)
855{
856 KMKBUILTINCTX Ctx = { "kmk_kDepIDB", NULL };
857 return kmk_builtin_kDepIDB(argc, argv, envp, &Ctx);
858}
859#endif
860
Note: See TracBrowser for help on using the repository browser.