VirtualBox

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

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

fixslash for all but OS2.

  • 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 406 2006-01-15 00:53:31Z 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#elif defined(__OS2__)
188
189/**
190 * Corrects the case of a path.
191 *
192 * @param pszPath Pointer to the path, both input and output.
193 * The buffer must be able to hold one more byte than the string length.
194 */
195static void fixcase(char *pszFilename)
196{
197 return;
198}
199
200#else
201
202/**
203 * Corrects the case of a path.
204 *
205 * @param pszPath Pointer to the path, both input and output.
206 */
207static void fixcase(char *pszFilename)
208{
209 char *psz;
210
211 /*
212 * Skip the root.
213 */
214 psz = pszFilename;
215 while (*psz == '/')
216 psz++;
217
218 /*
219 * Iterate all the components.
220 */
221 while (*psz)
222 {
223 char chSlash;
224 struct stat s;
225 char *pszStart = psz;
226
227 /*
228 * Find the next slash (or end of string) and terminate the string there.
229 */
230 while (*psz != '/' && *psz)
231 *psz++;
232 chSlash = *psz;
233 *psz = '\0';
234
235 /*
236 * Does this part exist?
237 * If not we'll enumerate the directory and search for an case-insensitive match.
238 */
239 if (stat(pszFilename, &s))
240 {
241 struct dirent *pEntry;
242 DIR *pDir;
243 if (pszStart == pszFilename)
244 pDir = opendir(*pszFilename ? pszFilename : ".");
245 else
246 {
247 pszStart[-1] = '\0';
248 pDir = opendir(pszFilename);
249 pszStart[-1] = '/';
250 }
251 if (!pDir)
252 {
253 *psz = chSlash;
254 break; /* giving up, if we fail to open the directory. */
255 }
256
257 while ((pEntry = readdir(pDir)) != NULL)
258 {
259 if (!strcasecmp(pEntry->d_name, pszStart))
260 {
261 strcpy(pszStart, pEntry->d_name);
262 break;
263 }
264 }
265 closedir(pDir);
266 if (!pEntry)
267 {
268 *psz = chSlash;
269 break; /* giving up if not found. */
270 }
271 }
272
273 /* restore the slash and press on. */
274 *psz = chSlash;
275 while (*psz == '/')
276 psz++;
277 }
278
279 return;
280}
281
282
283#endif
284
285
286/**
287 * 'Optimizes' and corrects the dependencies.
288 */
289void depOptimize(int fFixCase)
290{
291 /*
292 * Walk the list correct the names and re-insert them.
293 */
294 PDEP pDepOrg = g_pDeps;
295 PDEP pDep = g_pDeps;
296 g_pDeps = NULL;
297 for (; pDep; pDep = pDep->pNext)
298 {
299#ifdef __WIN32__
300 char szFilename[_MAX_PATH + 1];
301#else
302 char szFilename[PATH_MAX + 1];
303#endif
304 char *pszFilename;
305 struct stat s;
306
307 /*
308 * Skip some fictive names like <built-in> and <command line>.
309 */
310 if ( pDep->szFilename[0] == '<'
311 && pDep->szFilename[pDep->cchFilename - 1] == '>')
312 continue;
313 pszFilename = pDep->szFilename;
314
315#if !defined(__OS2__) && !defined(__WIN32__)
316 /*
317 * Skip any drive letters from compilers running in wine.
318 */
319 if (pszFilename[1] == ':')
320 pszFilename += 2;
321#endif
322
323 /*
324 * The microsoft compilers are notoriously screwing up the casing.
325 * This will screw up kmk (/ GNU Make).
326 */
327 if (fFixCase)
328 {
329#ifdef __WIN32__
330 if (_fullpath(szFilename, pszFilename, sizeof(szFilename)))
331 ;
332 else
333#endif
334 strcpy(szFilename, pszFilename);
335 fixslash(szFilename);
336 fixcase(szFilename);
337 pszFilename = szFilename;
338 }
339
340 /*
341 * Check that the file exists before we start depending on it.
342 */
343 if (stat(pszFilename, &s))
344 {
345 fprintf(stderr, "kDep: Skipping '%s' - %s!\n", szFilename, strerror(errno));
346 continue;
347 }
348
349 /*
350 * Insert the corrected dependency.
351 */
352 depAdd(pszFilename, strlen(pszFilename));
353 }
354
355#if 0 /* waste of time */
356 /*
357 * Free the old ones.
358 */
359 while (pDepOrg)
360 {
361 pDep = pDepOrg;
362 pDepOrg = pDepOrg->pNext;
363 free(pDep);
364 }
365#endif
366}
367
368
369#ifndef __OS2__
370/**
371 * Corrects all slashes to unix slashes.
372 *
373 * @returns pszFilename.
374 * @param pszFilename The filename to correct.
375 */
376static char *fixslash(char *pszFilename)
377{
378 char *psz = pszFilename;
379 while ((psz = strchr(psz, '\\')) != NULL)
380 *psz++ = '/';
381 return pszFilename;
382}
383#endif
384
385
386/**
387 * Prints the dependency chain.
388 *
389 * @returns Pointer to the allocated dependency.
390 * @param pOutput Output stream.
391 */
392void depPrint(FILE *pOutput)
393{
394 PDEP pDep;
395 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
396 fprintf(pOutput, " \\\n\t%s", pDep->szFilename);
397 fprintf(pOutput, "\n\n");
398}
399
400
401/**
402 * Prints empty dependency stubs for all dependencies.
403 */
404void depPrintStubs(FILE *pOutput)
405{
406 PDEP pDep;
407 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
408 fprintf(pOutput, "%s:\n\n", pDep->szFilename);
409}
410
411
412/* sdbm:
413 This algorithm was created for sdbm (a public-domain reimplementation of
414 ndbm) database library. it was found to do well in scrambling bits,
415 causing better distribution of the keys and fewer splits. it also happens
416 to be a good general hashing function with good distribution. the actual
417 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
418 is the faster version used in gawk. [there is even a faster, duff-device
419 version] the magic constant 65599 was picked out of thin air while
420 experimenting with different constants, and turns out to be a prime.
421 this is one of the algorithms used in berkeley db (see sleepycat) and
422 elsewhere. */
423static unsigned sdbm(const char *str)
424{
425 unsigned hash = 0;
426 int c;
427
428 while ((c = *(unsigned const char *)str++))
429 hash = c + (hash << 6) + (hash << 16) - hash;
430
431 return hash;
432}
433
434
435/**
436 * Adds a dependency.
437 *
438 * @returns Pointer to the allocated dependency.
439 * @param pszFilename The filename.
440 * @param cchFilename The length of the filename.
441 */
442PDEP depAdd(const char *pszFilename, size_t cchFilename)
443{
444 unsigned uHash = sdbm(pszFilename);
445 PDEP pDep;
446 PDEP pDepPrev;
447
448 /*
449 * Check if we've already got this one.
450 */
451 pDepPrev = NULL;
452 for (pDep = g_pDeps; pDep; pDepPrev = pDep, pDep = pDep->pNext)
453 if ( pDep->uHash == uHash
454 && pDep->cchFilename == cchFilename
455 && !memcmp(pDep->szFilename, pszFilename, cchFilename))
456 return pDep;
457
458 /*
459 * Add it.
460 */
461 pDep = (PDEP)malloc(sizeof(*pDep) + cchFilename);
462 if (!pDep)
463 {
464 fprintf(stderr, "\nOut of memory! (requested %#x bytes)\n\n", sizeof(*pDep) + cchFilename);
465 exit(1);
466 }
467
468 pDep->cchFilename = cchFilename;
469 memcpy(pDep->szFilename, pszFilename, cchFilename + 1);
470 pDep->uHash = uHash;
471
472 if (pDepPrev)
473 {
474 pDep->pNext = pDepPrev->pNext;
475 pDepPrev->pNext = pDep;
476 }
477 else
478 {
479 pDep->pNext = g_pDeps;
480 g_pDeps = pDep;
481 }
482 return pDep;
483}
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