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