VirtualBox

source: kBuild/trunk/src/lib/kDep.c@ 942

Last change on this file since 942 was 865, checked in by bird, 18 years ago

win.amd64 fixes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.2 KB
Line 
1/* $Id: kDep.c 865 2007-03-30 01:52:19Z bird $ */
2/** @file
3 *
4 * kDep - Common Dependency Managemnt Code.
5 *
6 * Copyright (c) 2004-2007 knut st. osmundsen <[email protected]>
7 *
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 2 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, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <errno.h>
35#include <ctype.h>
36#include <limits.h>
37#include <sys/stat.h>
38#ifdef __WIN32__
39# include <windows.h>
40#endif
41#if !defined(__WIN32__) && !defined(__OS2__)
42# include <dirent.h>
43#endif
44#ifndef __WIN32__
45# include <unistd.h>
46# include <stdint.h>
47#else
48 typedef unsigned char uint8_t;
49 typedef unsigned short uint16_t;
50 typedef unsigned int uint32_t;
51#endif
52
53#include "kDep.h"
54
55#ifdef NEED_ISBLANK
56# define isblank(ch) ( (unsigned char)(ch) == ' ' || (unsigned char)(ch) == '\t' )
57#endif
58
59#define OFFSETOF(type, member) ( (int)(void *)&( ((type *)(void *)0)->member) )
60
61
62/*******************************************************************************
63* Global Variables *
64*******************************************************************************/
65/** List of dependencies. */
66static PDEP g_pDeps = NULL;
67
68
69/**
70 * Corrects all slashes to unix slashes.
71 *
72 * @returns pszFilename.
73 * @param pszFilename The filename to correct.
74 */
75static char *fixslash(char *pszFilename)
76{
77 char *psz = pszFilename;
78 while ((psz = strchr(psz, '\\')) != NULL)
79 *psz++ = '/';
80 return pszFilename;
81}
82
83
84#ifdef __WIN32__
85/**
86 * Corrects the case of a path and changes any path components containing
87 * spaces with the short name (which can be longer).
88 *
89 * Expects a _fullpath!
90 *
91 * @param pszPath Pointer to the path, both input and output.
92 * The buffer must be at least MAX_PATH in length.
93 */
94static void fixcase(char *pszPath)
95{
96#define my_assert(expr) \
97 do { \
98 if (!(expr)) { \
99 printf("my_assert: %s, file %s, line %d\npszPath=%s\npsz=%s\n", \
100 #expr, __FILE__, __LINE__, pszPath, psz); \
101 __debugbreak(); \
102 exit(1); \
103 } \
104 } while (0)
105
106 char *psz = pszPath;
107 if (*psz == '/' || *psz == '\\')
108 {
109 if (psz[1] == '/' || psz[1] == '\\')
110 {
111 /* UNC */
112 my_assert(psz[1] == '/' || psz[1] == '\\');
113 my_assert(psz[2] != '/' && psz[2] != '\\');
114
115 /* skip server name */
116 psz += 2;
117 while (*psz != '\\' && *psz != '/')
118 {
119 if (!*psz)
120 return;
121 *psz++ = toupper(*psz);
122 }
123
124 /* skip the share name */
125 psz++;
126 my_assert(*psz != '/' && *psz != '\\');
127 while (*psz != '\\' && *psz != '/')
128 {
129 if (!*psz)
130 return;
131 *psz++ = toupper(*psz);
132 }
133 my_assert(*psz == '/' || *psz == '\\');
134 psz++;
135 }
136 else
137 {
138 /* Unix spec */
139 psz++;
140 }
141 }
142 else
143 {
144 /* Drive letter */
145 my_assert(psz[1] == ':');
146 *psz = toupper(*psz);
147 my_assert(psz[0] >= 'A' && psz[0] <= 'Z');
148 my_assert(psz[2] == '/' || psz[2] == '\\');
149 psz += 3;
150 }
151
152 /*
153 * Pointing to the first char after the unc or drive specifier.
154 */
155 while (*psz)
156 {
157 WIN32_FIND_DATA FindFileData;
158 HANDLE hDir;
159 char chSaved0;
160 char chSaved1;
161 char *pszEnd;
162 size_t cch;
163 int iLongNameDiff;
164
165
166 /* find the end of the component. */
167 pszEnd = psz;
168 while (*pszEnd && *pszEnd != '/' && *pszEnd != '\\')
169 pszEnd++;
170 cch = pszEnd - psz;
171
172 /* replace the end with "?\0" */
173 chSaved0 = pszEnd[0];
174 chSaved1 = pszEnd[1];
175 pszEnd[0] = '?';
176 pszEnd[1] = '\0';
177
178 /* find the right filename. */
179 hDir = FindFirstFile(pszPath, &FindFileData);
180 pszEnd[1] = chSaved1;
181 if (!hDir)
182 {
183 pszEnd[0] = chSaved0;
184 return;
185 }
186 pszEnd[0] = '\0';
187 while ( (iLongNameDiff = _stricmp(FindFileData.cFileName, psz))
188 && _stricmp(FindFileData.cAlternateFileName, psz))
189 {
190 if (!FindNextFile(hDir, &FindFileData))
191 {
192 pszEnd[0] = chSaved0;
193 return;
194 }
195 }
196 pszEnd[0] = chSaved0;
197 if (iLongNameDiff || !FindFileData.cAlternateFileName[0] || !memchr(psz, ' ', cch))
198 memcpy(psz, !iLongNameDiff ? FindFileData.cFileName : FindFileData.cAlternateFileName, cch);
199 else
200 {
201 /* replace spacy name with the short name. */
202 const size_t cchAlt = strlen(FindFileData.cAlternateFileName);
203 const size_t cchDelta = cch - cchAlt;
204 my_assert(cchAlt > 0);
205 if (!cchDelta)
206 memcpy(psz, FindFileData.cAlternateFileName, cch);
207 else
208 {
209 size_t cbLeft = strlen(pszEnd) + 1;
210 if ((psz - pszPath) + cbLeft + cchAlt <= _MAX_PATH)
211 {
212 memmove(psz + cchAlt, pszEnd, cbLeft);
213 pszEnd -= cchDelta;
214 memcpy(psz, FindFileData.cAlternateFileName, cchAlt);
215 }
216 else
217 fprintf(stderr, "kDep: case & space fixed filename is growing too long (%d bytes)! '%s'\n",
218 (psz - pszPath) + cbLeft + cchAlt, pszPath);
219 }
220 }
221 my_assert(pszEnd[0] == chSaved0);
222
223 /* advance to the next component */
224 if (!chSaved0)
225 return;
226 psz = pszEnd + 1;
227 my_assert(*psz != '/' && *psz != '\\');
228 }
229#undef my_assert
230}
231
232#elif defined(__OS2__)
233
234/**
235 * Corrects the case of a path.
236 *
237 * @param pszPath Pointer to the path, both input and output.
238 * The buffer must be able to hold one more byte than the string length.
239 */
240static void fixcase(char *pszFilename)
241{
242 return;
243}
244
245#else
246
247/**
248 * Corrects the case of a path.
249 *
250 * @param pszPath Pointer to the path, both input and output.
251 */
252static void fixcase(char *pszFilename)
253{
254 char *psz;
255
256 /*
257 * Skip the root.
258 */
259 psz = pszFilename;
260 while (*psz == '/')
261 psz++;
262
263 /*
264 * Iterate all the components.
265 */
266 while (*psz)
267 {
268 char chSlash;
269 struct stat s;
270 char *pszStart = psz;
271
272 /*
273 * Find the next slash (or end of string) and terminate the string there.
274 */
275 while (*psz != '/' && *psz)
276 *psz++;
277 chSlash = *psz;
278 *psz = '\0';
279
280 /*
281 * Does this part exist?
282 * If not we'll enumerate the directory and search for an case-insensitive match.
283 */
284 if (stat(pszFilename, &s))
285 {
286 struct dirent *pEntry;
287 DIR *pDir;
288 if (pszStart == pszFilename)
289 pDir = opendir(*pszFilename ? pszFilename : ".");
290 else
291 {
292 pszStart[-1] = '\0';
293 pDir = opendir(pszFilename);
294 pszStart[-1] = '/';
295 }
296 if (!pDir)
297 {
298 *psz = chSlash;
299 break; /* giving up, if we fail to open the directory. */
300 }
301
302 while ((pEntry = readdir(pDir)) != NULL)
303 {
304 if (!strcasecmp(pEntry->d_name, pszStart))
305 {
306 strcpy(pszStart, pEntry->d_name);
307 break;
308 }
309 }
310 closedir(pDir);
311 if (!pEntry)
312 {
313 *psz = chSlash;
314 break; /* giving up if not found. */
315 }
316 }
317
318 /* restore the slash and press on. */
319 *psz = chSlash;
320 while (*psz == '/')
321 psz++;
322 }
323
324 return;
325}
326
327
328#endif
329
330
331/**
332 * 'Optimizes' and corrects the dependencies.
333 */
334void depOptimize(int fFixCase)
335{
336 /*
337 * Walk the list correct the names and re-insert them.
338 */
339 PDEP pDepOrg = g_pDeps;
340 PDEP pDep = g_pDeps;
341 g_pDeps = NULL;
342 for (; pDep; pDep = pDep->pNext)
343 {
344#ifdef __WIN32__
345 char szFilename[_MAX_PATH + 1];
346#else
347 char szFilename[PATH_MAX + 1];
348#endif
349 char *pszFilename;
350 struct stat s;
351
352 /*
353 * Skip some fictive names like <built-in> and <command line>.
354 */
355 if ( pDep->szFilename[0] == '<'
356 && pDep->szFilename[pDep->cchFilename - 1] == '>')
357 continue;
358 pszFilename = pDep->szFilename;
359
360#if !defined(__OS2__) && !defined(__WIN32__)
361 /*
362 * Skip any drive letters from compilers running in wine.
363 */
364 if (pszFilename[1] == ':')
365 pszFilename += 2;
366#endif
367
368 /*
369 * The microsoft compilers are notoriously screwing up the casing.
370 * This will screw up kmk (/ GNU Make).
371 */
372 if (fFixCase)
373 {
374#ifdef __WIN32__
375 if (_fullpath(szFilename, pszFilename, sizeof(szFilename)))
376 ;
377 else
378#endif
379 strcpy(szFilename, pszFilename);
380 fixslash(szFilename);
381 fixcase(szFilename);
382 pszFilename = szFilename;
383 }
384
385 /*
386 * Check that the file exists before we start depending on it.
387 */
388 if (stat(pszFilename, &s))
389 {
390 fprintf(stderr, "kDep: Skipping '%s' - %s!\n", szFilename, strerror(errno));
391 continue;
392 }
393
394 /*
395 * Insert the corrected dependency.
396 */
397 depAdd(pszFilename, strlen(pszFilename));
398 }
399
400#if 0 /* waste of time */
401 /*
402 * Free the old ones.
403 */
404 while (pDepOrg)
405 {
406 pDep = pDepOrg;
407 pDepOrg = pDepOrg->pNext;
408 free(pDep);
409 }
410#endif
411}
412
413
414/**
415 * Prints the dependency chain.
416 *
417 * @returns Pointer to the allocated dependency.
418 * @param pOutput Output stream.
419 */
420void depPrint(FILE *pOutput)
421{
422 PDEP pDep;
423 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
424 fprintf(pOutput, " \\\n\t%s", pDep->szFilename);
425 fprintf(pOutput, "\n\n");
426}
427
428
429/**
430 * Prints empty dependency stubs for all dependencies.
431 */
432void depPrintStubs(FILE *pOutput)
433{
434 PDEP pDep;
435 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
436 fprintf(pOutput, "%s:\n\n", pDep->szFilename);
437}
438
439
440/* sdbm:
441 This algorithm was created for sdbm (a public-domain reimplementation of
442 ndbm) database library. it was found to do well in scrambling bits,
443 causing better distribution of the keys and fewer splits. it also happens
444 to be a good general hashing function with good distribution. the actual
445 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
446 is the faster version used in gawk. [there is even a faster, duff-device
447 version] the magic constant 65599 was picked out of thin air while
448 experimenting with different constants, and turns out to be a prime.
449 this is one of the algorithms used in berkeley db (see sleepycat) and
450 elsewhere. */
451static unsigned sdbm(const char *str)
452{
453 unsigned hash = 0;
454 int c;
455
456 while ((c = *(unsigned const char *)str++))
457 hash = c + (hash << 6) + (hash << 16) - hash;
458
459 return hash;
460}
461
462
463/**
464 * Adds a dependency.
465 *
466 * @returns Pointer to the allocated dependency.
467 * @param pszFilename The filename.
468 * @param cchFilename The length of the filename.
469 */
470PDEP depAdd(const char *pszFilename, size_t cchFilename)
471{
472 unsigned uHash = sdbm(pszFilename);
473 PDEP pDep;
474 PDEP pDepPrev;
475
476 /*
477 * Check if we've already got this one.
478 */
479 pDepPrev = NULL;
480 for (pDep = g_pDeps; pDep; pDepPrev = pDep, pDep = pDep->pNext)
481 if ( pDep->uHash == uHash
482 && pDep->cchFilename == cchFilename
483 && !memcmp(pDep->szFilename, pszFilename, cchFilename))
484 return pDep;
485
486 /*
487 * Add it.
488 */
489 pDep = (PDEP)malloc(sizeof(*pDep) + cchFilename);
490 if (!pDep)
491 {
492 fprintf(stderr, "\nOut of memory! (requested %#x bytes)\n\n", sizeof(*pDep) + cchFilename);
493 exit(1);
494 }
495
496 pDep->cchFilename = cchFilename;
497 memcpy(pDep->szFilename, pszFilename, cchFilename + 1);
498 pDep->uHash = uHash;
499
500 if (pDepPrev)
501 {
502 pDep->pNext = pDepPrev->pNext;
503 pDepPrev->pNext = pDep;
504 }
505 else
506 {
507 pDep->pNext = g_pDeps;
508 g_pDeps = pDep;
509 }
510 return pDep;
511}
Note: See TracBrowser for help on using the repository browser.

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