VirtualBox

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

Last change on this file since 684 was 407, checked in by bird, 19 years ago

fixslash for everyone!

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