source: trunk/src/kDepPre/kDepPre.c@ 256

Last change on this file since 256 was 256, checked in by bird, 20 years ago

..

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 KB
Line 
1/* $Id: kDepPre.c 256 2005-04-21 01:46:39Z 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
32#ifdef HAVE_FGETC_UNLOCKED
33# define FGETC(s) getc_unlocked(s)
34#else
35# define FGETC(s) fgetc(s)
36#endif
37
38
39
40
41/*******************************************************************************
42* Structures and Typedefs *
43*******************************************************************************/
44/** A dependency. */
45typedef struct DEP
46{
47 /** Next dependency in the list. */
48 struct DEP *pNext;
49 /** The filename hash. */
50 unsigned uHash;
51 /** The length of the filename. */
52 size_t cchFilename;
53 /** The filename. */
54 char szFilename[4];
55} DEP, *PDEP;
56
57
58/*******************************************************************************
59* Global Variables *
60*******************************************************************************/
61/** List of dependencies. */
62static PDEP g_pDeps = NULL;
63
64
65/**
66 * Prints the dependency chain.
67 *
68 * @returns Pointer to the allocated dependency.
69 * @param pOutput Output stream.
70 */
71static void depPrint(FILE *pOutput)
72{
73 PDEP pDep = g_pDeps;
74 for (pDep = g_pDeps; pDep; pDep = pDep->pNext)
75 {
76 /*
77 * Skip some fictive names like <built-in> and <command line>.
78 */
79 if ( pDep->szFilename[0] == '<'
80 && pDep->szFilename[pDep->cchFilename - 1] == '>')
81 continue;
82
83 fprintf(pOutput, " \\\n\t%s", pDep->szFilename);
84 }
85 fprintf(pOutput, "\n\n");
86}
87
88
89/* sdbm:
90 This algorithm was created for sdbm (a public-domain reimplementation of
91 ndbm) database library. it was found to do well in scrambling bits,
92 causing better distribution of the keys and fewer splits. it also happens
93 to be a good general hashing function with good distribution. the actual
94 function is hash(i) = hash(i - 1) * 65599 + str[i]; what is included below
95 is the faster version used in gawk. [there is even a faster, duff-device
96 version] the magic constant 65599 was picked out of thin air while
97 experimenting with different constants, and turns out to be a prime.
98 this is one of the algorithms used in berkeley db (see sleepycat) and
99 elsewhere. */
100static unsigned sdbm(const char *str)
101{
102 const char *pszStart;
103 unsigned hash = 0;
104 int c;
105
106 while ((c = *(unsigned const char *)str++))
107 hash = c + (hash << 6) + (hash << 16) - hash;
108
109 return hash;
110}
111
112
113/**
114 * Adds a dependency.
115 *
116 * @returns Pointer to the allocated dependency.
117 * @param pszFilename The filename.
118 * @param cchFilename The length of the filename.
119 */
120static PDEP depAdd(const char *pszFilename, size_t cchFilename)
121{
122 unsigned uHash = sdbm(pszFilename);
123 PDEP pDep;
124 PDEP pDepPrev;
125
126 /*
127 * Check if we've already got this one.
128 */
129 pDepPrev = NULL;
130 for (pDep = g_pDeps; pDep; pDepPrev = pDep, pDep = pDep->pNext)
131 if ( pDep->uHash == uHash
132 && pDep->cchFilename == cchFilename
133 && !memcmp(pDep->szFilename, pszFilename, cchFilename))
134 return pDep;
135
136 /*
137 * Add it.
138 */
139 pDep = malloc(sizeof(*pDep) + cchFilename);
140 if (!pDep)
141 {
142 fprintf(stderr, "\nOut of memory! (requested %#x bytes)\n\n", sizeof(*pDep) + cchFilename);
143 exit(1);
144 }
145
146 pDep->cchFilename = cchFilename;
147 memcpy(pDep->szFilename, pszFilename, cchFilename + 1);
148 pDep->uHash = uHash;
149
150 if (pDepPrev)
151 {
152 pDep->pNext = pDepPrev->pNext;
153 pDepPrev->pNext = pDep;
154 }
155 else
156 {
157 pDep->pNext = g_pDeps;
158 g_pDeps = pDep;
159 }
160 return pDep;
161}
162
163
164/**
165 * Parses the output from a preprocessor of a C-style language.
166 *
167 * @returns 0 on success.
168 * @returns 1 or other approriate exit code on failure.
169 * @param pInput Input stream. (probably not seekable)
170 */
171static int ParseCPrecompiler(FILE *pInput)
172{
173 enum
174 {
175 C_DISCOVER = 0,
176 C_SKIP_LINE,
177 C_PARSE_FILENAME,
178 C_EOF
179 } enmMode = C_DISCOVER;
180 PDEP pDep = NULL;
181 int ch;
182 char szBuf[8192];
183
184 for (;;)
185 {
186 switch (enmMode)
187 {
188 /*
189 * Start of line, need to look for '#[[:space]]*line <num> "file"' and '# <num> "file"'.
190 */
191 case C_DISCOVER:
192 /* first find '#' */
193 while ((ch = FGETC(pInput)) != EOF)
194 if (!isblank(ch))
195 break;
196 if (ch == '#')
197 {
198 /* skip spaces */
199 while ((ch = FGETC(pInput)) != EOF)
200 if (!isblank(ch))
201 break;
202
203 /* check for "line" */
204 if (ch == 'l')
205 {
206 if ( (ch = FGETC(pInput)) == 'i'
207 && (ch = FGETC(pInput)) == 'n'
208 && (ch = FGETC(pInput)) == 'e')
209 {
210 ch = FGETC(pInput);
211 if (isblank(ch))
212 {
213 /* skip spaces */
214 while ((ch = FGETC(pInput)) != EOF)
215 if (!isblank(ch))
216 break;
217 }
218 else
219 ch = 'x';
220 }
221 else
222 ch = 'x';
223 }
224
225 /* line number */
226 if (ch >= '0' && ch <= '9')
227 {
228 /* skip the number following spaces */
229 while ((ch = FGETC(pInput)) != EOF)
230 if (!isxdigit(ch))
231 break;
232 if (isblank(ch))
233 {
234 while ((ch = FGETC(pInput)) != EOF)
235 if (!isblank(ch))
236 break;
237 /* quoted filename */
238 if (ch == '"')
239 {
240 enmMode = C_PARSE_FILENAME;
241 break;
242 }
243 }
244 }
245 }
246 enmMode = C_SKIP_LINE;
247 break;
248
249 /*
250 * Skip past the end of the current line.
251 */
252 case C_SKIP_LINE:
253# if 0
254 for (;;)
255 {
256 if (!fgets(szBuf, sizeof(szBuf), pInput))
257 {
258 enmMode = C_EOF;
259 break;
260 }
261 ch = szBuf[strlen(szBuf) - 1];
262 if ( ch == '\r'
263 || ch == '\n')
264 break;
265 }
266# else
267 while ((ch = FGETC(pInput)) != EOF)
268 if ( ch == '\r'
269 || ch == '\n')
270 break;
271# endif
272 enmMode = C_DISCOVER;
273 break;
274
275 /*
276 * Parse the filename.
277 */
278 case C_PARSE_FILENAME:
279 {
280 /* retreive and unescape the filename. */
281 char *psz = &szBuf[0];
282 while ( (ch = FGETC(pInput)) != EOF
283 && psz < &szBuf[sizeof(szBuf) - 1])
284 {
285 if (ch == '\\')
286 {
287 ch = FGETC(pInput);
288 switch (ch)
289 {
290 case '\\': ch = '/'; break;
291 case 't': ch = '\t'; break;
292 case 'r': ch = '\r'; break;
293 case 'n': ch = '\n'; break;
294 case 'b': ch = '\b'; break;
295 default:
296 fprintf(stderr, "warning: unknown escape char '%c'\n", ch);
297 continue;
298
299 }
300 *psz++ = ch == '\\' ? '/' : ch;
301 }
302 else if (ch != '"')
303 *psz++ = ch;
304 else
305 {
306 size_t cchFilename = psz - &szBuf[0];
307 *psz = '\0';
308 /* compare with current dep, add & switch on mismatch. */
309 if ( !pDep
310 || pDep->cchFilename != cchFilename
311 || memcmp(pDep->szFilename, szBuf, cchFilename))
312 pDep = depAdd(szBuf, cchFilename);
313 break;
314 }
315 }
316 enmMode = C_SKIP_LINE;
317 break;
318 }
319
320 /*
321 * Handle EOF.
322 */
323 case C_EOF:
324 if (feof(pInput))
325 return 0;
326 enmMode = C_DISCOVER;
327 break;
328 }
329 if (ch == EOF)
330 enmMode = C_EOF;
331 }
332
333 return 0;
334}
335
336
337static void usage(const char *argv0)
338{
339 printf("syntax: %s [-l=c] -o <output> -t <target> <-e <cmdline> | - | <filename>\n", argv0);
340}
341
342int main(int argc, char *argv[])
343{
344 int i;
345
346 /* Arguments. */
347 int iExec = 0;
348 FILE *pOutput = NULL;
349 const char *pszOutput = NULL;
350 FILE *pInput = NULL;
351 const char *pszTarget = NULL;
352 /* Argument parsing. */
353 int fInput = 0; /* set when we've found input argument. */
354
355 /*
356 * Parse arguments.
357 */
358 if (argc <= 1)
359 {
360 usage(argv[0]);
361 return 1;
362 }
363 for (i = 1; i < argc; i++)
364 {
365 if (argv[i][0] == '-')
366 {
367 switch (argv[i][1])
368 {
369 /*
370 * Output file.
371 */
372 case 'o':
373 {
374 pszOutput = &argv[i][2];
375 if (pOutput)
376 {
377 fprintf(stderr, "%s: syntax error: only one output file!\n", argv[0]);
378 return 1;
379 }
380 if (!*pszOutput)
381 {
382 if (++i >= argc)
383 {
384 fprintf(stderr, "%s: syntax error: The '-o' argument is missing the filename.\n", argv[0]);
385 return 1;
386 }
387 pszOutput = argv[i];
388 }
389 pOutput = fopen(pszOutput, "w");
390 if (!pOutput)
391 {
392 fprintf(stderr, "%s: error: Failed to create output file '%s'.\n", argv[0], pszOutput);
393 return 1;
394 }
395 break;
396 }
397
398 /*
399 * Language spec.
400 */
401 case 'l':
402 {
403 const char *psz = &argv[i][2];
404 if (*psz == '=')
405 psz++;
406 if (!strcmp(psz, "c"))
407 ;
408 else
409 {
410 fprintf(stderr, "%s: error: The '%s' language is not supported.\n", argv[0], psz);
411 return 1;
412 }
413 break;
414 }
415
416 /*
417 * Target name.
418 */
419 case 't':
420 {
421 if (pszTarget)
422 {
423 fprintf(stderr, "%s: syntax error: only one target!\n", argv[0]);
424 return 1;
425 }
426 pszTarget = &argv[i][2];
427 if (!*pszTarget)
428 {
429 if (++i >= argc)
430 {
431 fprintf(stderr, "%s: syntax error: The '-t' argument is missing the target name.\n", argv[0]);
432 return 1;
433 }
434 pszTarget = argv[i];
435 }
436 break;
437 }
438
439 /*
440 * Exec.
441 */
442 case 'e':
443 {
444 if (++i >= argc)
445 {
446 fprintf(stderr, "%s: syntax error: The '-e' argument is missing the command.\n", argv[0]);
447 return 1;
448 }
449 iExec = i;
450 i = argc - 1;
451 break;
452 }
453
454
455 /*
456 * Pipe input.
457 */
458 case '\0':
459 {
460 pInput = stdin;
461 fInput = 1;
462 break;
463 }
464
465 /*
466 * Invalid argument.
467 */
468 default:
469 fprintf(stderr, "%s: syntax error: Invalid argument '%s'.\n", argv[0], argv[i]);
470 usage(argv[0]);
471 return 1;
472 }
473 }
474 else
475 {
476 pInput = fopen(argv[i], "r");
477 if (!pOutput)
478 {
479 fprintf(stderr, "%s: error: Failed to open input file '%s'.\n", argv[0], argv[i]);
480 return 1;
481 }
482 fInput = 1;
483 }
484
485 /*
486 * End of the line?
487 */
488 if (fInput)
489 {
490 if (++i < argc)
491 {
492 fprintf(stderr, "%s: syntax error: No arguments shall follow the input spec.\n", argv[0]);
493 return 1;
494 }
495 break;
496 }
497 }
498
499 /*
500 * Got all we require?
501 */
502 if (!pInput && iExec <= 0)
503 {
504 fprintf(stderr, "%s: syntax error: No input!\n", argv[0]);
505 return 1;
506 }
507 if (!pOutput)
508 {
509 fprintf(stderr, "%s: syntax error: No input!\n", argv[0]);
510 return 1;
511 }
512 if (!pszTarget)
513 {
514 fprintf(stderr, "%s: syntax error: No target!\n", argv[0]);
515 return 1;
516 }
517
518 /*
519 * Spawn process?
520 */
521 if (iExec > 0)
522 {
523 fprintf(stderr, "%s: -e is not yet implemented!\n", argv[0]);
524 return 1;
525 }
526
527 /*
528 * Do the parsing.
529 */
530 i = ParseCPrecompiler(pInput);
531
532 /*
533 * Reap child.
534 */
535 if (iExec > 0)
536 {
537 // later
538 }
539
540 /*
541 * Write the dependecy file.
542 */
543 if (!i)
544 {
545 fprintf(pOutput, "%s:", pszTarget);
546 depPrint(pOutput);
547 }
548
549 /*
550 * Close the output, delete output on failure.
551 */
552 if (!i && ferror(pOutput))
553 {
554 i = 1;
555 fprintf(stderr, "%s: error: Error writing to '%s'.\n", argv[0], pszOutput);
556 }
557 fclose(pOutput);
558 if (i)
559 {
560 if (unlink(pszOutput))
561 fprintf(stderr, "%s: warning: failed to remove output file '%s' on failure.\n", argv[0], pszOutput);
562 }
563
564 return i;
565}
Note: See TracBrowser for help on using the repository browser.