source: trunk/src/lib/kDep.c@ 727

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

Extended the windows fixcase function to also take care of path components with spaces in them. Also, don't stop fixing the path if we were given a shortname.

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