VirtualBox

source: kBuild/trunk/src/kDepPre/kDepPre.c@ 343

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

missing unlink proto, cast malloc result to make g++ happy.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.1 KB
Line 
1/* $Id: kDepPre.c 343 2005-12-11 04:02:21Z bird $ */
2/** @file
3 *
4 * kDepPre - Dependency Generator using Precompiler output.
5 *
6 * Copyright (c) 2005 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#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <errno.h>
32#include <ctype.h>
33#include <limits.h>
34#include <sys/stat.h>
35#ifdef __WIN32__
36# include <windows.h>
37#endif
38#if !defined(__WIN32__) && !defined(__OS2__)
39# include <dirent.h>
40#endif
41#ifndef __WIN32__
42# include <unistd.h>
43#endif
44
45#ifdef HAVE_FGETC_UNLOCKED
46# define FGETC(s) getc_unlocked(s)
47#else
48# define FGETC(s) fgetc(s)
49#endif
50
51#ifdef NEED_ISBLANK
52# define isblank(ch) ( (unsigned char)(ch) == ' ' || (unsigned char)(ch) == '\t' )
53#endif
54
55
56
57
58/*******************************************************************************
59* Structures and Typedefs *
60*******************************************************************************/
61/** A dependency. */
62typedef struct DEP
63{
64 /** Next dependency in the list. */
65 struct DEP *pNext;
66 /** The filename hash. */
67 unsigned uHash;
68 /** The length of the filename. */
69 size_t cchFilename;
70 /** The filename. */
71 char szFilename[4];
72} DEP, *PDEP;
73
74
75/*******************************************************************************
76* Global Variables *
77*******************************************************************************/
78/** List of dependencies. */
79static PDEP g_pDeps = NULL;
80
81
82/*******************************************************************************
83* Internal Functions *
84*******************************************************************************/
85static PDEP depAdd(const char *pszFilename, size_t cchFilename);
86static void depOptimize(int fFixCase);
87static void depPrint(FILE *pOutput);
88static void depPrintStubs(FILE *pOutput);
89
90
91#ifdef __WIN32__
92/**
93 * Corrects the case of a path.
94 * Expects a fullpath!
95 *
96 * @param pszPath Pointer to the path, both input and output.
97 * The buffer must be able to hold one more byte than the string length.
98 */
99void fixcase(char *pszPath)
100{
101#define my_assert(expr) \
102 do { \
103 if (!(expr)) { \
104 printf("my_assert: %s, file %s, line %d\npszPath=%s\npsz=%s\n", \
105 #expr, __FILE__, __LINE__, pszPath, psz); \
106 __asm { __asm int 3 } \
107 exit(1); \
108 } \
109 } while (0)
110
111 char *psz = pszPath;
112 if (*psz == '/' || *psz == '\\')
113 {
114 if (psz[1] == '/' || psz[1] == '\\')
115 {
116 /* UNC */
117 my_assert(psz[1] == '/' || psz[1] == '\\');
118 my_assert(psz[2] != '/' && psz[2] != '\\');
119
120 /* skip server name */
121 psz += 2;
122 while (*psz != '\\' && *psz != '/')
123 {
124 if (!*psz)
125 return;
126 *psz++ = toupper(*psz);
127 }
128
129 /* skip the share name */
130 psz++;
131 my_assert(*psz != '/' && *psz != '\\');
132 while (*psz != '\\' && *psz != '/')
133 {
134 if (!*psz)
135 return;
136 *psz++ = toupper(*psz);
137 }
138 my_assert(*psz == '/' || *psz == '\\');
139 psz++;
140 }
141 else
142 {
143 /* Unix spec */
144 psz++;
145 }
146 }
147 else
148 {
149 /* Drive letter */
150 my_assert(psz[1] == ':');
151 *psz = toupper(*psz);
152 my_assert(psz[0] >= 'A' && psz[0] <= 'Z');
153 my_assert(psz[2] == '/' || psz[2] == '\\');
154 psz += 3;
155 }
156
157 /*
158 * Pointing to the first char after the unc or drive specifier.
159 */
160 while (*psz)
161 {
162 WIN32_FIND_DATA FindFileData;
163 HANDLE hDir;
164 char chSaved0;
165 char chSaved1;
166 char *pszEnd;
167
168
169 /* find the end of the component. */
170 pszEnd = psz;
171 while (*pszEnd && *pszEnd != '/' && *pszEnd != '\\')
172 pszEnd++;
173
174 /* replace the end with "?\0" */
175 chSaved0 = pszEnd[0];
176 chSaved1 = pszEnd[1];
177 pszEnd[0] = '?';
178 pszEnd[1] = '\0';
179
180 /* find the right filename. */
181 hDir = FindFirstFile(pszPath, &FindFileData);
182 pszEnd[1] = chSaved1;
183 if (!hDir)
184 {
185 pszEnd[0] = chSaved0;
186 return;
187 }
188 pszEnd[0] = '\0';
189 while (stricmp(FindFileData.cFileName, psz))
190 {
191 if (!FindNextFile(hDir, &FindFileData))
192 {
193 pszEnd[0] = chSaved0;
194 return;
195 }
196 }
197 strcpy(psz, FindFileData.cFileName);
198 pszEnd[0] = chSaved0;
199
200 /* advance to the next component */
201 if (!chSaved0)
202 return;
203 psz = pszEnd + 1;
204 my_assert(*psz != '/' && *psz != '\\');
205 }
206#undef my_assert
207}
208
209/**
210 * Corrects all slashes to unix slashes.
211 *
212 * @returns pszFilename.
213 * @param pszFilename The filename to correct.
214 */
215char *fixslash(char *pszFilename)
216{
217 char *psz = pszFilename;
218 while ((psz = strchr(psz, '\\')) != NULL)
219 *psz++ = '/';
220 return pszFilename;
221}
222
223#elif defined(__OS2__)
224
225/**
226 * Corrects the case of a path.
227 *
228 * @param pszPath Pointer to the path, both input and output.
229 * The buffer must be able to hold one more byte than the string length.
230 */
231void fixcase(char *pszFilename)
232{
233 return;
234}
235
236#else
237
238/**
239 * Corrects the case of a path.
240 *
241 * @param pszPath Pointer to the path, both input and output.
242 */
243void fixcase(char *pszFilename)
244{
245 char *psz;
246
247 /*
248 * Skip the root.
249 */
250 psz = pszFilename;
251 while (*psz == '/')
252 psz++;
253
254 /*
255 * Iterate all the components.
256 */
257 while (*psz)
258 {
259 char chSlash;
260 struct stat s;
261 char *pszStart = psz;
262
263 /*
264 * Find the next slash (or end of string) and terminate the string there.
265 */
266 while (*psz != '/' && *psz)
267 *psz++;
268 chSlash = *psz;
269 *psz = '\0';
270
271 /*
272 * Does this part exist?
273 * If not we'll enumerate the directory and search for an case-insensitive match.
274 */
275 if (stat(pszFilename, &s))
276 {
277 struct dirent *pEntry;
278 DIR *pDir;
279 if (pszStart == pszFilename)
280 pDir = opendir(*pszFilename ? pszFilename : ".");
281 else
282 {
283 pszStart[-1] = '\0';
284 pDir = opendir(pszFilename);
285 pszStart[-1] = '/';
286 }
287 if (!pDir)
288 {
289 *psz = chSlash;
290 break; /* giving up, if we fail to open the directory. */
291 }
292
293 while ((pEntry = readdir(pDir)) != NULL)
294 {
295 if (!strcasecmp(pEntry->d_name, pszStart))
296 {
297 strcpy(pszStart, pEntry->d_name);
298 break;
299 }
300 }
301 closedir(pDir);
302 if (!pEntry)
303 {
304 *psz = chSlash;
305 break; /* giving up if not found. */
306 }
307 }
308
309 /* restore the slash and press on. */
310 *psz = chSlash;
311 while (*psz == '/')
312 psz++;
313 }
314
315 return;
316}
317
318
319#endif
320
321
322/**
323 * 'Optimizes' and corrects the dependencies.
324 */
325static void depOptimize(int fFixCase)
326{
327 /*
328 * Walk the list correct the names and re-insert them.
329 */
330 PDEP pDepOrg = g_pDeps;
331 PDEP pDep = g_pDeps;
332 g_pDeps = NULL;
333 for (; pDep; pDep = pDep->pNext)
334 {
335#ifdef __WIN32__
336 char szFilename[_MAX_PATH + 1];
337#else
338 char szFilename[PATH_MAX + 1];
339#endif
340 char *pszFilename;
341 struct stat s;
342
343 /*
344 * Skip some fictive names like <built-in> and <command line>.
345 */
346 if ( pDep->szFilename[0] == '<'
347 && pDep->szFilename[pDep->cchFilename - 1] == '>')
348 continue;
349 pszFilename = pDep->szFilename;
350
351#if !defined(__OS2__) && !defined(__WIN32__)
352 /*
353 * Skip any drive letters from compilers running in wine.
354 */
355 if (pszFilename[1] == ':')
356 pszFilename += 2;
357#endif
358
359 /*
360 * The microsoft compilers are notoriously screwing up the casing.
361 * This will screw up kmk (/ GNU Make).
362 */
363 if (fFixCase)
364 {
365#ifdef __WIN32__
366 if (_fullpath(szFilename, pszFilename, sizeof(szFilename)))
367 fixslash(szFilename);
368 else
369#endif
370 strcpy(szFilename, pszFilename);
371 fixcase(szFilename);
372 pszFilename = szFilename;
373 }
374
375 /*
376 * Check that the file exists before we start depending on it.
377 */
378 if (stat(pszFilename, &s))
379 {
380 fprintf(stderr, "kDepPre: Skipping '%s' - %s!\n", szFilename, strerror(errno));
381 continue;
382 }
383
384 /*
385 * Insert the corrected dependency.
386 */
387 depAdd(pszFilename, strlen(pszFilename));
388 }
389
390#if 0 /* waste of time */
391 /*
392 * Free the old ones.
393 */
394 while (pDepOrg)
395 {
396 pDep = pDepOrg;
397 pDepOrg = pDepOrg->pNext;
398 free(pDep);
399 }
400#endif
401}
402
403
404/**
405 * Prints the dependency chain.
406 *
407 * @returns Pointer to the allocated dependency.
408 * @param pOutput Output stream.
409 */
410static void depPrint(FILE *pOutput)
411{
412 PDEP pDep;
413 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
414 fprintf(pOutput, " \\\n\t%s", pDep->szFilename);
415 fprintf(pOutput, "\n\n");
416}
417
418
419/**
420 * Prints empty dependency stubs for all dependencies.
421 */
422static void depPrintStubs(FILE *pOutput)
423{
424 PDEP pDep;
425 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
426 fprintf(pOutput, "%s:\n\n", pDep->szFilename);
427}
428
429
430/* sdbm:
431 This algorithm was created for sdbm (a public-domain reimplementation of
432 ndbm) database library. it was found to do well in scrambling bits,
433 causing better distribution of the keys and fewer splits. it also happens
434 to be a good general hashing function with good distribution. the actual
435 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
436 is the faster version used in gawk. [there is even a faster, duff-device
437 version] the magic constant 65599 was picked out of thin air while
438 experimenting with different constants, and turns out to be a prime.
439 this is one of the algorithms used in berkeley db (see sleepycat) and
440 elsewhere. */
441static unsigned sdbm(const char *str)
442{
443 unsigned hash = 0;
444 int c;
445
446 while ((c = *(unsigned const char *)str++))
447 hash = c + (hash << 6) + (hash << 16) - hash;
448
449 return hash;
450}
451
452
453/**
454 * Adds a dependency.
455 *
456 * @returns Pointer to the allocated dependency.
457 * @param pszFilename The filename.
458 * @param cchFilename The length of the filename.
459 */
460static PDEP depAdd(const char *pszFilename, size_t cchFilename)
461{
462 unsigned uHash = sdbm(pszFilename);
463 PDEP pDep;
464 PDEP pDepPrev;
465
466 /*
467 * Check if we've already got this one.
468 */
469 pDepPrev = NULL;
470 for (pDep = g_pDeps; pDep; pDepPrev = pDep, pDep = pDep->pNext)
471 if ( pDep->uHash == uHash
472 && pDep->cchFilename == cchFilename
473 && !memcmp(pDep->szFilename, pszFilename, cchFilename))
474 return pDep;
475
476 /*
477 * Add it.
478 */
479 pDep = (PDEP)malloc(sizeof(*pDep) + cchFilename);
480 if (!pDep)
481 {
482 fprintf(stderr, "\nOut of memory! (requested %#x bytes)\n\n", sizeof(*pDep) + cchFilename);
483 exit(1);
484 }
485
486 pDep->cchFilename = cchFilename;
487 memcpy(pDep->szFilename, pszFilename, cchFilename + 1);
488 pDep->uHash = uHash;
489
490 if (pDepPrev)
491 {
492 pDep->pNext = pDepPrev->pNext;
493 pDepPrev->pNext = pDep;
494 }
495 else
496 {
497 pDep->pNext = g_pDeps;
498 g_pDeps = pDep;
499 }
500 return pDep;
501}
502
503
504/**
505 * Parses the output from a preprocessor of a C-style language.
506 *
507 * @returns 0 on success.
508 * @returns 1 or other approriate exit code on failure.
509 * @param pInput Input stream. (probably not seekable)
510 */
511static int ParseCPrecompiler(FILE *pInput)
512{
513 enum
514 {
515 C_DISCOVER = 0,
516 C_SKIP_LINE,
517 C_PARSE_FILENAME,
518 C_EOF
519 } enmMode = C_DISCOVER;
520 PDEP pDep = NULL;
521 int ch;
522 char szBuf[8192];
523
524 for (;;)
525 {
526 switch (enmMode)
527 {
528 /*
529 * Start of line, need to look for '#[[:space]]*line <num> "file"' and '# <num> "file"'.
530 */
531 case C_DISCOVER:
532 /* first find '#' */
533 while ((ch = FGETC(pInput)) != EOF)
534 if (!isblank(ch))
535 break;
536 if (ch == '#')
537 {
538 /* skip spaces */
539 while ((ch = FGETC(pInput)) != EOF)
540 if (!isblank(ch))
541 break;
542
543 /* check for "line" */
544 if (ch == 'l')
545 {
546 if ( (ch = FGETC(pInput)) == 'i'
547 && (ch = FGETC(pInput)) == 'n'
548 && (ch = FGETC(pInput)) == 'e')
549 {
550 ch = FGETC(pInput);
551 if (isblank(ch))
552 {
553 /* skip spaces */
554 while ((ch = FGETC(pInput)) != EOF)
555 if (!isblank(ch))
556 break;
557 }
558 else
559 ch = 'x';
560 }
561 else
562 ch = 'x';
563 }
564
565 /* line number */
566 if (ch >= '0' && ch <= '9')
567 {
568 /* skip the number following spaces */
569 while ((ch = FGETC(pInput)) != EOF)
570 if (!isxdigit(ch))
571 break;
572 if (isblank(ch))
573 {
574 while ((ch = FGETC(pInput)) != EOF)
575 if (!isblank(ch))
576 break;
577 /* quoted filename */
578 if (ch == '"')
579 {
580 enmMode = C_PARSE_FILENAME;
581 break;
582 }
583 }
584 }
585 }
586 enmMode = C_SKIP_LINE;
587 break;
588
589 /*
590 * Skip past the end of the current line.
591 */
592 case C_SKIP_LINE:
593 do
594 {
595 if ( ch == '\r'
596 || ch == '\n')
597 break;
598 } while ((ch = FGETC(pInput)) != EOF);
599 enmMode = C_DISCOVER;
600 break;
601
602 /*
603 * Parse the filename.
604 */
605 case C_PARSE_FILENAME:
606 {
607 /* retreive and unescape the filename. */
608 char *psz = &szBuf[0];
609 while ( (ch = FGETC(pInput)) != EOF
610 && psz < &szBuf[sizeof(szBuf) - 1])
611 {
612 if (ch == '\\')
613 {
614 ch = FGETC(pInput);
615 switch (ch)
616 {
617 case '\\': ch = '/'; break;
618 case 't': ch = '\t'; break;
619 case 'r': ch = '\r'; break;
620 case 'n': ch = '\n'; break;
621 case 'b': ch = '\b'; break;
622 default:
623 fprintf(stderr, "warning: unknown escape char '%c'\n", ch);
624 continue;
625
626 }
627 *psz++ = ch == '\\' ? '/' : ch;
628 }
629 else if (ch != '"')
630 *psz++ = ch;
631 else
632 {
633 size_t cchFilename = psz - &szBuf[0];
634 *psz = '\0';
635 /* compare with current dep, add & switch on mismatch. */
636 if ( !pDep
637 || pDep->cchFilename != cchFilename
638 || memcmp(pDep->szFilename, szBuf, cchFilename))
639 pDep = depAdd(szBuf, cchFilename);
640 break;
641 }
642 }
643 enmMode = C_SKIP_LINE;
644 break;
645 }
646
647 /*
648 * Handle EOF.
649 */
650 case C_EOF:
651 if (feof(pInput))
652 return 0;
653 enmMode = C_DISCOVER;
654 break;
655 }
656 if (ch == EOF)
657 enmMode = C_EOF;
658 }
659
660 return 0;
661}
662
663
664static void usage(const char *argv0)
665{
666 printf("syntax: %s [-l=c] -o <output> -t <target> [-f] [-s] < - | <filename> | -e <cmdline> >\n", argv0);
667}
668
669int main(int argc, char *argv[])
670{
671 int i;
672
673 /* Arguments. */
674 int iExec = 0;
675 FILE *pOutput = NULL;
676 const char *pszOutput = NULL;
677 FILE *pInput = NULL;
678 const char *pszTarget = NULL;
679 int fStubs = 0;
680 int fFixCase = 0;
681 /* Argument parsing. */
682 int fInput = 0; /* set when we've found input argument. */
683
684 /*
685 * Parse arguments.
686 */
687 if (argc <= 1)
688 {
689 usage(argv[0]);
690 return 1;
691 }
692 for (i = 1; i < argc; i++)
693 {
694 if (argv[i][0] == '-')
695 {
696 switch (argv[i][1])
697 {
698 /*
699 * Output file.
700 */
701 case 'o':
702 {
703 pszOutput = &argv[i][2];
704 if (pOutput)
705 {
706 fprintf(stderr, "%s: syntax error: only one output file!\n", argv[0]);
707 return 1;
708 }
709 if (!*pszOutput)
710 {
711 if (++i >= argc)
712 {
713 fprintf(stderr, "%s: syntax error: The '-o' argument is missing the filename.\n", argv[0]);
714 return 1;
715 }
716 pszOutput = argv[i];
717 }
718 if (pszOutput[0] == '-' && !pszOutput[1])
719 pOutput = stdout;
720 else
721 pOutput = fopen(pszOutput, "w");
722 if (!pOutput)
723 {
724 fprintf(stderr, "%s: error: Failed to create output file '%s'.\n", argv[0], pszOutput);
725 return 1;
726 }
727 break;
728 }
729
730 /*
731 * Language spec.
732 */
733 case 'l':
734 {
735 const char *psz = &argv[i][2];
736 if (*psz == '=')
737 psz++;
738 if (!strcmp(psz, "c"))
739 ;
740 else
741 {
742 fprintf(stderr, "%s: error: The '%s' language is not supported.\n", argv[0], psz);
743 return 1;
744 }
745 break;
746 }
747
748 /*
749 * Target name.
750 */
751 case 't':
752 {
753 if (pszTarget)
754 {
755 fprintf(stderr, "%s: syntax error: only one target!\n", argv[0]);
756 return 1;
757 }
758 pszTarget = &argv[i][2];
759 if (!*pszTarget)
760 {
761 if (++i >= argc)
762 {
763 fprintf(stderr, "%s: syntax error: The '-t' argument is missing the target name.\n", argv[0]);
764 return 1;
765 }
766 pszTarget = argv[i];
767 }
768 break;
769 }
770
771 /*
772 * Exec.
773 */
774 case 'e':
775 {
776 if (++i >= argc)
777 {
778 fprintf(stderr, "%s: syntax error: The '-e' argument is missing the command.\n", argv[0]);
779 return 1;
780 }
781 iExec = i;
782 i = argc - 1;
783 break;
784 }
785
786 /*
787 * Pipe input.
788 */
789 case '\0':
790 {
791 pInput = stdin;
792 fInput = 1;
793 break;
794 }
795
796 /*
797 * Fix case.
798 */
799 case 'f':
800 {
801 fFixCase = 1;
802 break;
803 }
804
805 /*
806 * Generate stubs.
807 */
808 case 's':
809 {
810 fStubs = 1;
811 break;
812 }
813
814 /*
815 * Invalid argument.
816 */
817 default:
818 fprintf(stderr, "%s: syntax error: Invalid argument '%s'.\n", argv[0], argv[i]);
819 usage(argv[0]);
820 return 1;
821 }
822 }
823 else
824 {
825 pInput = fopen(argv[i], "r");
826 if (!pInput)
827 {
828 fprintf(stderr, "%s: error: Failed to open input file '%s'.\n", argv[0], argv[i]);
829 return 1;
830 }
831 fInput = 1;
832 }
833
834 /*
835 * End of the line?
836 */
837 if (fInput)
838 {
839 if (++i < argc)
840 {
841 fprintf(stderr, "%s: syntax error: No arguments shall follow the input spec.\n", argv[0]);
842 return 1;
843 }
844 break;
845 }
846 }
847
848 /*
849 * Got all we require?
850 */
851 if (!pInput && iExec <= 0)
852 {
853 fprintf(stderr, "%s: syntax error: No input!\n", argv[0]);
854 return 1;
855 }
856 if (!pOutput)
857 {
858 fprintf(stderr, "%s: syntax error: No output!\n", argv[0]);
859 return 1;
860 }
861 if (!pszTarget)
862 {
863 fprintf(stderr, "%s: syntax error: No target!\n", argv[0]);
864 return 1;
865 }
866
867 /*
868 * Spawn process?
869 */
870 if (iExec > 0)
871 {
872 fprintf(stderr, "%s: -e is not yet implemented!\n", argv[0]);
873 return 1;
874 }
875
876 /*
877 * Do the parsing.
878 */
879 i = ParseCPrecompiler(pInput);
880
881 /*
882 * Reap child.
883 */
884 if (iExec > 0)
885 {
886 // later
887 }
888
889 /*
890 * Write the dependecy file.
891 */
892 if (!i)
893 {
894 depOptimize(fFixCase);
895 fprintf(pOutput, "%s:", pszTarget);
896 depPrint(pOutput);
897 if (fStubs)
898 depPrintStubs(pOutput);
899 }
900
901 /*
902 * Close the output, delete output on failure.
903 */
904 if (!i && ferror(pOutput))
905 {
906 i = 1;
907 fprintf(stderr, "%s: error: Error writing to '%s'.\n", argv[0], pszOutput);
908 }
909 fclose(pOutput);
910 if (i)
911 {
912 if (unlink(pszOutput))
913 fprintf(stderr, "%s: warning: failed to remove output file '%s' on failure.\n", argv[0], pszOutput);
914 }
915
916 return i;
917}
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