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

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

Initial coding. (missing exec but who cares)

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