VirtualBox

source: kBuild/trunk/src/lib/kDep.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: 14.8 KB
Line 
1/* $Id: kDep.c 3167 2018-03-20 21:47:25Z bird $ */
2/** @file
3 * kDep - Common Dependency Managemnt Code.
4 */
5
6/*
7 * Copyright (c) 2004-2013 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included
17 * in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 * IN THE SOFTWARE.
26 *
27 * Alternatively, the content of this file may be used under the terms of the
28 * GPL version 2 or later, or LGPL version 2.1 or later.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#ifdef KMK /* For when it gets compiled and linked into kmk. */
36# include "makeint.h"
37#endif
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <errno.h>
42#include <ctype.h>
43#include <limits.h>
44#include <sys/stat.h>
45#include "k/kDefs.h"
46#include "k/kTypes.h"
47#if K_OS == K_OS_WINDOWS
48# define USE_WIN_MMAP
49# include <io.h>
50# include <Windows.h>
51# include "nt_fullpath.h"
52# include "nt/ntstat.h"
53#else
54# include <dirent.h>
55# include <unistd.h>
56# include <stdint.h>
57#endif
58
59#include "kDep.h"
60
61#ifdef KWORKER
62extern int kwFsPathExists(const char *pszPath);
63#endif
64
65
66/*********************************************************************************************************************************
67* Defined Constants And Macros *
68*********************************************************************************************************************************/
69/* For the GNU/hurd weirdo. */
70#if !defined(PATH_MAX) && !defined(_MAX_PATH)
71# define PATH_MAX 4096
72#endif
73
74
75/**
76 * Initializes the dep instance.
77 *
78 * @param pThis The dep instance to init.
79 */
80void depInit(PDEPGLOBALS pThis)
81{
82 pThis->pDeps = NULL;
83}
84
85
86/**
87 * Cleans up the dep instance (frees resources).
88 *
89 * @param pThis The dep instance to cleanup.
90 */
91void depCleanup(PDEPGLOBALS pThis)
92{
93 PDEP pDep = pThis->pDeps;
94 pThis->pDeps = NULL;
95 while (pDep)
96 {
97 PDEP pFree = pDep;
98 pDep = pDep->pNext;
99 free(pFree);
100 }
101}
102
103
104/**
105 * Corrects all slashes to unix slashes.
106 *
107 * @returns pszFilename.
108 * @param pszFilename The filename to correct.
109 */
110static char *fixslash(char *pszFilename)
111{
112 char *psz = pszFilename;
113 while ((psz = strchr(psz, '\\')) != NULL)
114 *psz++ = '/';
115 return pszFilename;
116}
117
118
119#if K_OS == K_OS_OS2
120
121/**
122 * Corrects the case of a path.
123 *
124 * @param pszPath Pointer to the path, both input and output.
125 * The buffer must be able to hold one more byte than the string length.
126 */
127static void fixcase(char *pszFilename)
128{
129 return;
130}
131
132#elif K_OS != K_OS_WINDOWS
133
134/**
135 * Corrects the case of a path.
136 *
137 * @param pszPath Pointer to the path, both input and output.
138 */
139static void fixcase(char *pszFilename)
140{
141 char *psz;
142
143 /*
144 * Skip the root.
145 */
146 psz = pszFilename;
147 while (*psz == '/')
148 psz++;
149
150 /*
151 * Iterate all the components.
152 */
153 while (*psz)
154 {
155 char chSlash;
156 struct stat s;
157 char *pszStart = psz;
158
159 /*
160 * Find the next slash (or end of string) and terminate the string there.
161 */
162 while (*psz != '/' && *psz)
163 psz++;
164 chSlash = *psz;
165 *psz = '\0';
166
167 /*
168 * Does this part exist?
169 * If not we'll enumerate the directory and search for an case-insensitive match.
170 */
171 if (stat(pszFilename, &s))
172 {
173 struct dirent *pEntry;
174 DIR *pDir;
175 if (pszStart == pszFilename)
176 pDir = opendir(*pszFilename ? pszFilename : ".");
177 else
178 {
179 pszStart[-1] = '\0';
180 pDir = opendir(pszFilename);
181 pszStart[-1] = '/';
182 }
183 if (!pDir)
184 {
185 *psz = chSlash;
186 break; /* giving up, if we fail to open the directory. */
187 }
188
189 while ((pEntry = readdir(pDir)) != NULL)
190 {
191 if (!strcasecmp(pEntry->d_name, pszStart))
192 {
193 strcpy(pszStart, pEntry->d_name);
194 break;
195 }
196 }
197 closedir(pDir);
198 if (!pEntry)
199 {
200 *psz = chSlash;
201 break; /* giving up if not found. */
202 }
203 }
204
205 /* restore the slash and press on. */
206 *psz = chSlash;
207 while (*psz == '/')
208 psz++;
209 }
210
211 return;
212}
213
214#endif /* !OS/2 && !Windows */
215
216
217/**
218 * 'Optimizes' and corrects the dependencies.
219 */
220void depOptimize(PDEPGLOBALS pThis, int fFixCase, int fQuiet, const char *pszIgnoredExt)
221{
222 /*
223 * Walk the list correct the names and re-insert them.
224 */
225 size_t cchIgnoredExt = pszIgnoredExt ? strlen(pszIgnoredExt) : 0;
226 PDEP pDepOrg = pThis->pDeps;
227 PDEP pDep = pThis->pDeps;
228 pThis->pDeps = NULL;
229 for (; pDep; pDep = pDep->pNext)
230 {
231#ifndef PATH_MAX
232 char szFilename[_MAX_PATH + 1];
233#else
234 char szFilename[PATH_MAX + 1];
235#endif
236 char *pszFilename;
237#if !defined(KWORKER) && !defined(KMK)
238 struct stat s;
239#endif
240
241 /*
242 * Skip some fictive names like <built-in> and <command line>.
243 */
244 if ( pDep->szFilename[0] == '<'
245 && pDep->szFilename[pDep->cchFilename - 1] == '>')
246 continue;
247 pszFilename = pDep->szFilename;
248
249 /*
250 * Skip pszIgnoredExt if given.
251 */
252 if ( pszIgnoredExt
253 && pDep->cchFilename > cchIgnoredExt
254 && memcmp(&pDep->szFilename[pDep->cchFilename - cchIgnoredExt], pszIgnoredExt, cchIgnoredExt) == 0)
255 continue;
256
257#if K_OS != K_OS_OS2 && K_OS != K_OS_WINDOWS
258 /*
259 * Skip any drive letters from compilers running in wine.
260 */
261 if (pszFilename[1] == ':')
262 pszFilename += 2;
263#endif
264
265 /*
266 * The microsoft compilers are notoriously screwing up the casing.
267 * This will screw up kmk (/ GNU Make).
268 */
269 if (fFixCase)
270 {
271#if K_OS == K_OS_WINDOWS
272 nt_fullpath_cached(pszFilename, szFilename, sizeof(szFilename));
273 fixslash(szFilename);
274#else
275 strcpy(szFilename, pszFilename);
276 fixslash(szFilename);
277 fixcase(szFilename);
278#endif
279 pszFilename = szFilename;
280 }
281
282 /*
283 * Check that the file exists before we start depending on it.
284 */
285#ifdef KWORKER
286 if (!kwFsPathExists(pszFilename))
287#elif defined(KMK)
288 if (!file_exists_p(pszFilename))
289#elif K_OS == K_OS_WINDOWS
290 if (birdStatModTimeOnly(pszFilename, &s.st_mtim, 1 /*fFollowLink*/) != 0)
291#else
292 if (stat(pszFilename, &s) != 0)
293#endif
294 {
295 if ( !fQuiet
296 || errno != ENOENT
297 || ( pszFilename[0] != '/'
298 && pszFilename[0] != '\\'
299 && ( !isalpha(pszFilename[0])
300 || pszFilename[1] != ':'
301 || ( pszFilename[2] != '/'
302 && pszFilename[2] != '\\')))
303 )
304 fprintf(stderr, "kDep: Skipping '%s' - %s!\n", pszFilename, strerror(errno));
305 continue;
306 }
307
308 /*
309 * Insert the corrected dependency.
310 */
311 depAdd(pThis, pszFilename, strlen(pszFilename));
312 }
313
314 /*
315 * Free the old ones.
316 */
317 while (pDepOrg)
318 {
319 pDep = pDepOrg;
320 pDepOrg = pDepOrg->pNext;
321 free(pDep);
322 }
323}
324
325
326/**
327 * Prints the dependency chain.
328 *
329 * @param pThis The 'dep' instance.
330 * @param pOutput Output stream.
331 */
332void depPrint(PDEPGLOBALS pThis, FILE *pOutput)
333{
334 PDEP pDep;
335 for (pDep = pThis->pDeps; pDep; pDep = pDep->pNext)
336 fprintf(pOutput, " \\\n\t%s", pDep->szFilename);
337 fprintf(pOutput, "\n\n");
338}
339
340
341/**
342 * Prints empty dependency stubs for all dependencies.
343 *
344 * @param pThis The 'dep' instance.
345 * @param pOutput Output stream.
346 */
347void depPrintStubs(PDEPGLOBALS pThis, FILE *pOutput)
348{
349 PDEP pDep;
350 for (pDep = pThis->pDeps; pDep; pDep = pDep->pNext)
351 fprintf(pOutput, "%s:\n\n", pDep->szFilename);
352}
353
354
355/* sdbm:
356 This algorithm was created for sdbm (a public-domain reimplementation of
357 ndbm) database library. it was found to do well in scrambling bits,
358 causing better distribution of the keys and fewer splits. it also happens
359 to be a good general hashing function with good distribution. the actual
360 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
361 is the faster version used in gawk. [there is even a faster, duff-device
362 version] the magic constant 65599 was picked out of thin air while
363 experimenting with different constants, and turns out to be a prime.
364 this is one of the algorithms used in berkeley db (see sleepycat) and
365 elsewhere. */
366static unsigned sdbm(const char *str, size_t size)
367{
368 unsigned hash = 0;
369 int c;
370
371 while (size-- > 0 && (c = *(unsigned const char *)str++))
372 hash = c + (hash << 6) + (hash << 16) - hash;
373
374 return hash;
375}
376
377
378/**
379 * Adds a dependency.
380 *
381 * @returns Pointer to the allocated dependency.
382 * @param pThis The 'dep' instance.
383 * @param pszFilename The filename. Does not need to be terminated.
384 * @param cchFilename The length of the filename.
385 */
386PDEP depAdd(PDEPGLOBALS pThis, const char *pszFilename, size_t cchFilename)
387{
388 unsigned uHash = sdbm(pszFilename, cchFilename);
389 PDEP pDep;
390 PDEP pDepPrev;
391
392 /*
393 * Check if we've already got this one.
394 */
395 pDepPrev = NULL;
396 for (pDep = pThis->pDeps; pDep; pDepPrev = pDep, pDep = pDep->pNext)
397 if ( pDep->uHash == uHash
398 && pDep->cchFilename == cchFilename
399 && !memcmp(pDep->szFilename, pszFilename, cchFilename))
400 return pDep;
401
402 /*
403 * Add it.
404 */
405 pDep = (PDEP)malloc(sizeof(*pDep) + cchFilename);
406 if (!pDep)
407 {
408 fprintf(stderr, "\nOut of memory! (requested %lx bytes)\n\n",
409 (unsigned long)(sizeof(*pDep) + cchFilename));
410 exit(1);
411 }
412
413 pDep->cchFilename = cchFilename;
414 memcpy(pDep->szFilename, pszFilename, cchFilename);
415 pDep->szFilename[cchFilename] = '\0';
416 pDep->uHash = uHash;
417
418 if (pDepPrev)
419 {
420 pDep->pNext = pDepPrev->pNext;
421 pDepPrev->pNext = pDep;
422 }
423 else
424 {
425 pDep->pNext = pThis->pDeps;
426 pThis->pDeps = pDep;
427 }
428 return pDep;
429}
430
431
432/**
433 * Performs a hexdump.
434 */
435void depHexDump(const KU8 *pb, size_t cb, size_t offBase)
436{
437 const unsigned cchWidth = 16;
438 size_t off = 0;
439 while (off < cb)
440 {
441 unsigned i;
442 printf("%s%0*lx %04lx:", off ? "\n" : "", (int)sizeof(pb) * 2,
443 (unsigned long)offBase + (unsigned long)off, (unsigned long)off);
444 for (i = 0; i < cchWidth && off + i < cb ; i++)
445 printf(off + i < cb ? !(i & 7) && i ? "-%02x" : " %02x" : " ", pb[i]);
446
447 while (i++ < cchWidth)
448 printf(" ");
449 printf(" ");
450
451 for (i = 0; i < cchWidth && off + i < cb; i++)
452 {
453 const KU8 u8 = pb[i];
454 printf("%c", u8 < 127 && u8 >= 32 ? u8 : '.');
455 }
456 off += cchWidth;
457 pb += cchWidth;
458 }
459 printf("\n");
460}
461
462
463/**
464 * Reads the file specified by the pInput file stream into memory.
465 *
466 * @returns The address of the memory mapping on success. This must be
467 * freed by calling depFreeFileMemory.
468 *
469 * @param pInput The file stream to load or map into memory.
470 * @param pcbFile Where to return the mapping (file) size.
471 * @param ppvOpaque Opaque data when mapping, otherwise NULL.
472 */
473void *depReadFileIntoMemory(FILE *pInput, size_t *pcbFile, void **ppvOpaque)
474{
475 void *pvFile;
476 long cbFile;
477
478 /*
479 * Figure out file size.
480 */
481#if defined(_MSC_VER)
482 cbFile = _filelength(fileno(pInput));
483 if (cbFile < 0)
484#else
485 if ( fseek(pInput, 0, SEEK_END) < 0
486 || (cbFile = ftell(pInput)) < 0
487 || fseek(pInput, 0, SEEK_SET))
488#endif
489 {
490 fprintf(stderr, "kDep: error: Failed to determin file size.\n");
491 return NULL;
492 }
493 if (pcbFile)
494 *pcbFile = cbFile;
495
496 /*
497 * Try mmap first.
498 */
499#ifdef USE_WIN_MMAP
500 {
501 HANDLE hMapObj = CreateFileMapping((HANDLE)_get_osfhandle(fileno(pInput)),
502 NULL, PAGE_READONLY, 0, cbFile, NULL);
503 if (hMapObj != NULL)
504 {
505 pvFile = MapViewOfFile(hMapObj, FILE_MAP_READ, 0, 0, cbFile);
506 if (pvFile)
507 {
508 *ppvOpaque = hMapObj;
509 return pvFile;
510 }
511 fprintf(stderr, "kDep: warning: MapViewOfFile failed, %d.\n", GetLastError());
512 CloseHandle(hMapObj);
513 }
514 else
515 fprintf(stderr, "kDep: warning: CreateFileMapping failed, %d.\n", GetLastError());
516 }
517
518#endif
519
520 /*
521 * Allocate memory and read the file.
522 */
523 pvFile = malloc(cbFile + 1);
524 if (pvFile)
525 {
526 if (fread(pvFile, cbFile, 1, pInput))
527 {
528 ((KU8 *)pvFile)[cbFile] = '\0';
529 *ppvOpaque = NULL;
530 return pvFile;
531 }
532 fprintf(stderr, "kDep: error: Failed to read %ld bytes.\n", cbFile);
533 free(pvFile);
534 }
535 else
536 fprintf(stderr, "kDep: error: Failed to allocate %ld bytes (file mapping).\n", cbFile);
537 return NULL;
538}
539
540
541/**
542 * Free resources allocated by depReadFileIntoMemory.
543 *
544 * @param pvFile The address of the memory mapping.
545 * @param pvOpaque The opaque value returned together with the mapping.
546 */
547void depFreeFileMemory(void *pvFile, void *pvOpaque)
548{
549#if defined(USE_WIN_MMAP)
550 if (pvOpaque)
551 {
552 UnmapViewOfFile(pvFile);
553 CloseHandle(pvOpaque);
554 return;
555 }
556#endif
557 free(pvFile);
558}
559
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette