VirtualBox

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

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

PATH_MAX requires limits.h

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette