VirtualBox

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

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

fix slashes on unix too!

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