VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/kDepIDB.c@ 3387

Last change on this file since 3387 was 3315, checked in by bird, 4 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
Line 
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.
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 "err.h"
49#include "kmkbuiltin.h"
50
51
52/*********************************************************************************************************************************
53* Defined Constants And Macros *
54*********************************************************************************************************************************/
55/*#define DEBUG*/
56#ifdef DEBUG
57# define dprintf(a) printf a
58# define dump(pb, cb, offBase) depHexDump(pb,cb,offBase)
59#else
60# define dprintf(a) do {} while (0)
61# define dump(pb, cb, offBase) do {} while (0)
62#endif
63
64/*********************************************************************************************************************************
65* Structures and Typedefs *
66*********************************************************************************************************************************/
67typedef struct KDEPIDBGLOBALS
68{
69 PKMKBUILTINCTX pCtx;
70 DEPGLOBALS Core;
71} KDEPIDBGLOBALS;
72typedef KDEPIDBGLOBALS *PKDEPIDBGLOBALS;
73
74
75/**
76 * Scans a stream (chunk of data really) for dependencies.
77 *
78 * @returns 0 on success.
79 * @returns !0 on failure.
80 * @param pThis The kDepIDB instance.
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 */
86static int ScanStream(PKDEPIDBGLOBALS pThis, KU8 *pbStream, size_t cbStream, const char *pszPrefix, size_t cchPrefix)
87{
88 const KU8 *pbCur = pbStream;
89 size_t cbLeft = cbStream;
90 register char chFirst = *pszPrefix;
91 while (cbLeft > cchPrefix + 2)
92 {
93 if ( *pbCur != chFirst
94 || memcmp(pbCur, pszPrefix, cchPrefix))
95 {
96 pbCur++;
97 cbLeft--;
98 }
99 else
100 {
101 size_t cchDep;
102 pbCur += cchPrefix;
103 cchDep = strlen((const char *)pbCur);
104 depAdd(&pThis->Core, (const char *) pbCur, cchDep);
105 dprintf(("%05x: '%s'\n", pbCur - pbStream, pbCur));
106
107 pbCur += cchDep;
108 cbLeft -= cchDep + cchPrefix;
109 }
110 }
111
112 return 0;
113}
114
115
116/*/////////////////////////////////////////////////////////////////////////////
117//
118//
119// P D B 7 . 0
120//
121//
122/////////////////////////////////////////////////////////////////////////////*/
123
124/** A PDB 7.0 Page number. */
125typedef KU32 PDB70PAGE;
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. */
135 KU32 cbStream;
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. */
147 KU8 szSignature[sizeof(PDB_SIGNATURE_700)];
148 /** The page size. */
149 KU32 cbPage;
150 /** The start page. */
151 PDB70PAGE iStartPage;
152 /** The number of pages in the file. */
153 PDB70PAGE cPages;
154 /** The root stream directory. */
155 KU32 cbRoot;
156 /** Unknown function, always 0. */
157 KU32 u32Reserved;
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 */
168 KU32 cStreams;
169 /** Array of streams. */
170 PDB70STREAM aStreams[1];
171 /* KU32 aiPages[] */
172} PDB70ROOT, *PPDB70ROOT;
173
174/**
175 * The PDB 7.0 name stream (#1) header.
176 */
177typedef struct PDB70NAMES
178{
179 /** The structure version. */
180 KU32 Version;
181 /** Timestamp. */
182 KU32 TimeStamp;
183 /** Unknown. */
184 KU32 Unknown1;
185 /** GUID. */
186 KU32 u32Guid[4];
187 /** The size of the following name table. */
188 KU32 cbNames;
189 /** The name table. */
190 char szzNames[1];
191} PDB70NAMES, *PPDB70NAMES;
192
193/** The version / magic of the names structure. */
194#define PDB70NAMES_VERSION 20000404
195
196
197static int Pdb70ValidateHeader(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr, size_t cbFile)
198{
199 if (pHdr->cbPage * pHdr->cPages != cbFile)
200 return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - cbPage * cPages != cbFile.");
201 if (pHdr->iStartPage >= pHdr->cPages && pHdr->iStartPage <= 0)
202 return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - iStartPage=%u cPages=%u.",
203 pHdr->iStartPage, pHdr->cPages);
204 if (pHdr->iRootPages >= pHdr->cPages && pHdr->iRootPages <= 0)
205 return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - iRootPages=%u cPage=%u.",
206 pHdr->iStartPage, pHdr->cPages);
207 return 0;
208}
209
210#ifdef DEBUG
211static size_t Pdb70Align(PPDB70HDR pHdr, size_t cb)
212{
213 if (cb == ~(KU32)0 || !cb)
214 return 0;
215 return ((cb + pHdr->cbPage - 1) / pHdr->cbPage) * pHdr->cbPage;
216}
217#endif /* DEBUG */
218
219static size_t Pdb70Pages(PPDB70HDR pHdr, size_t cb)
220{
221 if (cb == ~(KU32)0 || !cb)
222 return 0;
223 return (cb + pHdr->cbPage - 1) / pHdr->cbPage;
224}
225
226static void *Pdb70AllocAndRead(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr, size_t cb, PPDB70PAGE paiPageMap)
227{
228 const size_t cbPage = pHdr->cbPage;
229 size_t cPages = Pdb70Pages(pHdr, cb);
230 KU8 *pbBuf = malloc(cPages * cbPage + 1);
231 if (pbBuf)
232 {
233 size_t iPage = 0;
234 while (iPage < cPages)
235 {
236 size_t off = paiPageMap[iPage];
237 if (off < pHdr->cPages)
238 {
239 off *= cbPage;
240 memcpy(pbBuf + iPage * cbPage, (KU8 *)pHdr + off, cbPage);
241 dump(pbBuf + iPage * cbPage, iPage + 1 < cPages ? cbPage : cb % cbPage, off);
242 }
243 else
244 {
245 warnx(pThis->pCtx, "warning: Invalid page index %u (max %u)!\n", (unsigned)off, pHdr->cPages);
246 memset(pbBuf + iPage * cbPage, 0, cbPage);
247 }
248
249 iPage++;
250 }
251 pbBuf[cPages * cbPage] = '\0';
252 }
253 else
254 {
255 errx(pThis->pCtx, 1, "failed to allocate %lu bytes", (unsigned long)(cPages * cbPage + 1));
256 return NULL;
257 }
258 return pbBuf;
259}
260
261static PPDB70ROOT Pdb70AllocAndReadRoot(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr)
262{
263 /*
264 * The tricky bit here is to find the right length. Really?
265 * (Todo: Check if we can just use the stream #0 size..)
266 */
267 PPDB70PAGE piPageMap = (KU32 *)((KU8 *)pHdr + pHdr->iRootPages * pHdr->cbPage);
268 PPDB70ROOT pRoot = Pdb70AllocAndRead(pThis, pHdr, pHdr->cbRoot, piPageMap);
269 if (pRoot)
270 {
271#if 1
272 /* This stuff is probably unnecessary: */
273 /* size = stream header + array of stream. */
274 size_t cb = K_OFFSETOF(PDB70ROOT, aStreams[pRoot->cStreams]);
275 free(pRoot);
276 pRoot = Pdb70AllocAndRead(pThis, pHdr, cb, piPageMap);
277 if (pRoot)
278 {
279 /* size += page tables. */
280 unsigned iStream = pRoot->cStreams;
281 while (iStream-- > 0)
282 if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
283 cb += Pdb70Pages(pHdr, pRoot->aStreams[iStream].cbStream) * sizeof(PDB70PAGE);
284 free(pRoot);
285 pRoot = Pdb70AllocAndRead(pThis, pHdr, cb, piPageMap);
286 if (pRoot)
287 {
288 /* validate? */
289 return pRoot;
290 }
291 }
292#else
293 /* validate? */
294 return pRoot;
295#endif
296 }
297 return NULL;
298}
299
300static void *Pdb70AllocAndReadStream(PKDEPIDBGLOBALS pThis, PPDB70HDR pHdr, PPDB70ROOT pRoot, unsigned iStream, size_t *pcbStream)
301{
302 const size_t cbStream = pRoot->aStreams[iStream].cbStream;
303 PPDB70PAGE paiPageMap;
304 if ( iStream >= pRoot->cStreams
305 || cbStream == ~(KU32)0)
306 {
307 errx(pThis->pCtx, 1, "Invalid stream %d", iStream);
308 return NULL;
309 }
310
311 paiPageMap = (PPDB70PAGE)&pRoot->aStreams[pRoot->cStreams];
312 while (iStream-- > 0)
313 if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
314 paiPageMap += Pdb70Pages(pHdr, pRoot->aStreams[iStream].cbStream);
315
316 if (pcbStream)
317 *pcbStream = cbStream;
318 return Pdb70AllocAndRead(pThis, pHdr, cbStream, paiPageMap);
319}
320
321static int Pdb70Process(PKDEPIDBGLOBALS pThis, KU8 *pbFile, size_t cbFile)
322{
323 PPDB70HDR pHdr = (PPDB70HDR)pbFile;
324 PPDB70ROOT pRoot;
325 PPDB70NAMES pNames;
326 size_t cbStream = 0;
327 unsigned fDone = 0;
328 unsigned iStream;
329 int rc = 0;
330 dprintf(("pdb70\n"));
331
332 /*
333 * Validate the header and read the root stream.
334 */
335 if (Pdb70ValidateHeader(pThis, pHdr, cbFile))
336 return 1;
337 pRoot = Pdb70AllocAndReadRoot(pThis, pHdr);
338 if (!pRoot)
339 return 1;
340
341 /*
342 * The names we want are usually all found in the 'Names' stream, that is #1.
343 */
344 dprintf(("Reading the names stream....\n"));
345 pNames = Pdb70AllocAndReadStream(pThis, pHdr, pRoot, 1, &cbStream);
346 if (pNames)
347 {
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
351 && pNames->cbNames + K_OFFSETOF(PDB70NAMES, szzNames) <= pRoot->aStreams[1].cbStream)
352 {
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 {
367 depAdd(&pThis->Core, psz + sizeof("/mr/inversedeps/") - 1, cch - (sizeof("/mr/inversedeps/") - 1));
368 fAdded = 1;
369 }
370 dprintf(("%#06x #%d: %6d bytes %s%s\n", off, iStream,
371 iStream < pRoot->cStreams ? pRoot->aStreams[iStream].cbStream : -1,
372 psz, fAdded ? " [dep]" : ""));
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;
387 }
388 else
389 dprintf(("Unknown version or bad size: Version=%u cbNames=%d cbStream=%d\n",
390 pNames->Version, pNames->cbNames, cbStream));
391 free(pNames);
392 }
393
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 {
403 KU8 *pbStream;
404 if ( pRoot->aStreams[iStream].cbStream == ~(KU32)0
405 || !pRoot->aStreams[iStream].cbStream)
406 continue;
407 dprintf(("Stream #%d: %#x bytes (%#x aligned)\n", iStream, pRoot->aStreams[iStream].cbStream,
408 Pdb70Align(pHdr, pRoot->aStreams[iStream].cbStream)));
409 pbStream = (KU8 *)Pdb70AllocAndReadStream(pThis, pHdr, pRoot, iStream, &cbStream);
410 if (pbStream)
411 {
412 rc = ScanStream(pThis, pbStream, cbStream, "/mr/inversedeps/", sizeof("/mr/inversedeps/") - 1);
413 free(pbStream);
414 }
415 else
416 rc = 1;
417 }
418 }
419
420 free(pRoot);
421 return rc;
422}
423
424
425
426/*/////////////////////////////////////////////////////////////////////////////
427//
428//
429// P D B 2 . 0
430//
431//
432/////////////////////////////////////////////////////////////////////////////*/
433
434
435/** A PDB 2.0 Page number. */
436typedef KU16 PDB20PAGE;
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. */
446 KU32 cbStream;
447 /** Some unknown value. */
448 KU32 u32Unknown;
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. */
459 KU8 szSignature[sizeof(PDB_SIGNATURE_200)];
460 /** The page size. */
461 KU32 cbPage;
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 */
478 KU16 cStreams;
479 /** Reserved or high part of cStreams. */
480 KU16 u16Reserved;
481 /** Array of streams. */
482 PDB20STREAM aStreams[1];
483} PDB20ROOT, *PPDB20ROOT;
484
485
486static int Pdb20ValidateHeader(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr, size_t cbFile)
487{
488 if (pHdr->cbPage * pHdr->cPages != cbFile)
489 return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - cbPage * cPages != cbFile.");
490 if (pHdr->iStartPage >= pHdr->cPages && pHdr->iStartPage <= 0)
491 return errx(pThis->pCtx, 1, "Bad PDB 2.0 header - cbPage * cPages != cbFile.");
492 return 0;
493}
494
495static size_t Pdb20Pages(PPDB20HDR pHdr, size_t cb)
496{
497 if (cb == ~(KU32)0 || !cb)
498 return 0;
499 return (cb + pHdr->cbPage - 1) / pHdr->cbPage;
500}
501
502static void *Pdb20AllocAndRead(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr, size_t cb, PPDB20PAGE paiPageMap)
503{
504 size_t cPages = Pdb20Pages(pHdr, cb);
505 KU8 *pbBuf = malloc(cPages * pHdr->cbPage + 1);
506 if (pbBuf)
507 {
508 size_t iPage = 0;
509 while (iPage < cPages)
510 {
511 size_t off = paiPageMap[iPage];
512 off *= pHdr->cbPage;
513 memcpy(pbBuf + iPage * pHdr->cbPage, (KU8 *)pHdr + off, pHdr->cbPage);
514 iPage++;
515 }
516 pbBuf[cPages * pHdr->cbPage] = '\0';
517 }
518 else
519 errx(pThis->pCtx, 1, "failed to allocate %lu bytes", (unsigned long)(cPages * pHdr->cbPage + 1));
520 return pbBuf;
521}
522
523static PPDB20ROOT Pdb20AllocAndReadRoot(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr)
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 */
529 PPDB20ROOT pRoot = Pdb20AllocAndRead(pThis, pHdr, sizeof(*pRoot), &pHdr->aiRootPageMap[0]);
530 if (pRoot)
531 {
532 /* size = stream header + array of stream. */
533 size_t cb = K_OFFSETOF(PDB20ROOT, aStreams[pRoot->cStreams]);
534 free(pRoot);
535 pRoot = Pdb20AllocAndRead(pThis, pHdr, cb, &pHdr->aiRootPageMap[0]);
536 if (pRoot)
537 {
538 /* size += page tables. */
539 unsigned iStream = pRoot->cStreams;
540 while (iStream-- > 0)
541 if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
542 cb += Pdb20Pages(pHdr, pRoot->aStreams[iStream].cbStream) * sizeof(PDB20PAGE);
543 free(pRoot);
544 pRoot = Pdb20AllocAndRead(pThis, pHdr, cb, &pHdr->aiRootPageMap[0]);
545 if (pRoot)
546 {
547 /* validate? */
548 return pRoot;
549 }
550 }
551 }
552 return NULL;
553
554}
555
556static void *Pdb20AllocAndReadStream(PKDEPIDBGLOBALS pThis, PPDB20HDR pHdr, PPDB20ROOT pRoot, unsigned iStream, size_t *pcbStream)
557{
558 size_t cbStream = pRoot->aStreams[iStream].cbStream;
559 PPDB20PAGE paiPageMap;
560 if ( iStream >= pRoot->cStreams
561 || cbStream == ~(KU32)0)
562 {
563 errx(pThis->pCtx, 1, "Invalid stream %d", iStream);
564 return NULL;
565 }
566
567 paiPageMap = (PPDB20PAGE)&pRoot->aStreams[pRoot->cStreams];
568 while (iStream-- > 0)
569 if (pRoot->aStreams[iStream].cbStream != ~(KU32)0)
570 paiPageMap += Pdb20Pages(pHdr, pRoot->aStreams[iStream].cbStream);
571
572 if (pcbStream)
573 *pcbStream = cbStream;
574 return Pdb20AllocAndRead(pThis, pHdr, cbStream, paiPageMap);
575}
576
577static int Pdb20Process(PKDEPIDBGLOBALS pThis, KU8 *pbFile, size_t cbFile)
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 */
587 if (Pdb20ValidateHeader(pThis, pHdr, cbFile))
588 return 1;
589 pRoot = Pdb20AllocAndReadRoot(pThis, pHdr);
590 if (!pRoot)
591 return 1;
592
593 /*
594 * Iterate the streams in the root and scan their content for
595 * dependencies.
596 */
597 rc = 0;
598 for (iStream = 0; iStream < pRoot->cStreams && !rc; iStream++)
599 {
600 KU8 *pbStream;
601 if (pRoot->aStreams[iStream].cbStream == ~(KU32)0)
602 continue;
603 pbStream = (KU8 *)Pdb20AllocAndReadStream(pThis, pHdr, pRoot, iStream, NULL);
604 if (pbStream)
605 {
606 rc = ScanStream(pThis, pbStream, pRoot->aStreams[iStream].cbStream, "/ipm/header/", sizeof("/ipm/header/") - 1);
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 */
621static int ProcessIDB(PKDEPIDBGLOBALS pThis, FILE *pInput)
622{
623 size_t cbFile;
624 KU8 *pbFile;
625 void *pvOpaque;
626 int rc = 0;
627
628 /*
629 * Read the file into memory.
630 */
631 pbFile = (KU8 *)depReadFileIntoMemory(pInput, &cbFile, &pvOpaque);
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)))
639 rc = Pdb70Process(pThis, pbFile, cbFile);
640 else if (!memcmp(pbFile, PDB_SIGNATURE_200, sizeof(PDB_SIGNATURE_200)))
641 rc = Pdb20Process(pThis, pbFile, cbFile);
642 else
643 rc = errx(pThis->pCtx, 1, "Doesn't recognize the header of the Visual C++ IDB file.");
644
645 depFreeFileMemory(pbFile, pvOpaque);
646 return rc;
647}
648
649
650static void kDepIDBUsage(PKMKBUILTINCTX pCtx, int fIsErr)
651{
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);
657}
658
659
660int kmk_builtin_kDepIDB(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx)
661{
662 int i;
663 KDEPIDBGLOBALS This;
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. */
674 int fQuiet = 0;
675
676 /* Init the instance data. */
677 This.pCtx = pCtx;
678
679 /*
680 * Parse arguments.
681 */
682 if (argc <= 1)
683 {
684 kDepIDBUsage(pCtx, 0);
685 return 1;
686 }
687 for (i = 1; i < argc; i++)
688 {
689 if (argv[i][0] == '-')
690 {
691 const char *psz = &argv[i][1];
692 if (*psz == '-')
693 {
694 if (!strcmp(psz, "-quiet"))
695 psz = "q";
696 else if (!strcmp(psz, "-help"))
697 psz = "?";
698 else if (!strcmp(psz, "-version"))
699 psz = "V";
700 }
701
702 switch (*psz)
703 {
704 /*
705 * Output file.
706 */
707 case 'o':
708 {
709 pszOutput = &argv[i][2];
710 if (pOutput)
711 return errx(pCtx, 2, "only one output file!");
712 if (!*pszOutput)
713 {
714 if (++i >= argc)
715 return errx(pCtx, 2, "The '-o' argument is missing the filename.");
716 pszOutput = argv[i];
717 }
718 if (pszOutput[0] == '-' && !pszOutput[1])
719 pOutput = stdout;
720 else
721 pOutput = fopen(pszOutput, "w" KMK_FOPEN_NO_INHERIT_MODE);
722 if (!pOutput)
723 return err(pCtx, 1, "Failed to create output file '%s'", pszOutput);
724 break;
725 }
726
727 /*
728 * Target name.
729 */
730 case 't':
731 {
732 if (pszTarget)
733 return errx(pCtx, 2, "only one target!");
734 pszTarget = &argv[i][2];
735 if (!*pszTarget)
736 {
737 if (++i >= argc)
738 return errx(pCtx, 2, "The '-t' argument is missing the target name.");
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 /*
754 * Quiet.
755 */
756 case 'q':
757 {
758 fQuiet = 1;
759 break;
760 }
761
762 /*
763 * Generate stubs.
764 */
765 case 's':
766 {
767 fStubs = 1;
768 break;
769 }
770
771 /*
772 * The mandatory version & help.
773 */
774 case '?':
775 kDepIDBUsage(pCtx, 0);
776 return 0;
777 case 'V':
778 case 'v':
779 return kbuild_version(pCtx->pszProgName);
780
781 /*
782 * Invalid argument.
783 */
784 default:
785 errx(pCtx, 2, "Invalid argument '%s.'", argv[i]);
786 kDepIDBUsage(pCtx, 1);
787 return 2;
788 }
789 }
790 else
791 {
792 pInput = fopen(argv[i], "rb" KMK_FOPEN_NO_INHERIT_MODE);
793 if (!pInput)
794 return err(pCtx, 1, "Failed to open input file '%s'", argv[i]);
795 fInput = 1;
796 }
797
798 /*
799 * End of the line?
800 */
801 if (fInput)
802 {
803 if (++i < argc)
804 return errx(pCtx, 2, "No arguments shall follow the input spec.");
805 break;
806 }
807 }
808
809 /*
810 * Got all we require?
811 */
812 if (!pInput)
813 return errx(pCtx, 2, "No input!");
814 if (!pOutput)
815 return errx(pCtx, 2, "No output!");
816 if (!pszTarget)
817 return errx(pCtx, 2, "No target!");
818
819 /*
820 * Do the parsing.
821 */
822 depInit(&This.Core);
823 i = ProcessIDB(&This, pInput);
824 fclose(pInput);
825
826 /*
827 * Write the dependecy file.
828 */
829 if (!i)
830 {
831 depOptimize(&This.Core, fFixCase, fQuiet, NULL /*pszIgnoredExt*/);
832 depPrintTargetWithDeps(&This.Core, pOutput, pszTarget, 1 /*fEscapeTarget*/);
833 if (fStubs)
834 depPrintStubs(&This.Core, pOutput);
835 }
836
837 /*
838 * Close the output, delete output on failure.
839 */
840 if (!i && ferror(pOutput))
841 i = errx(pCtx, 1, "Error writing to '%s'.", pszOutput);
842 fclose(pOutput);
843 if (i)
844 {
845 if (unlink(pszOutput))
846 warnx(pCtx, "warning: failed to remove output file '%s' on failure.", pszOutput);
847 }
848
849 depCleanup(&This.Core);
850 return i;
851}
852
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.

© 2023 Oracle
ContactPrivacy policyTerms of Use