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

Last change on this file since 3167 was 3167, checked in by bird, 7 years ago

kDep*: no globals; dir-nt-bird.c: only main thread

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