VirtualBox

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

Last change on this file since 3315 was 3315, checked in by bird, 5 years ago

lib/kDep+users: Escape spaces and such in dependency files (experimental).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.2 KB
Line 
1/* $Id: kDepPre.c 3315 2020-03-31 01:12:19Z bird $ */
2/** @file
3 * kDepPre - Dependency Generator using Precompiler output.
4 */
5
6/*
7 * Copyright (c) 2005-2010 knut st. osmundsen <[email protected]>
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 3 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, see <http://www.gnu.org/licenses/>
23 *
24 */
25
26/*******************************************************************************
27* Header Files *
28*******************************************************************************/
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <ctype.h>
33#ifdef _MSC_VER
34# include <io.h>
35#else
36# include <unistd.h>
37#endif
38#include "kDep.h"
39
40#ifdef HAVE_FGETC_UNLOCKED
41# define FGETC(s) getc_unlocked(s)
42#else
43# define FGETC(s) fgetc(s)
44#endif
45
46#ifdef NEED_ISBLANK
47# define isblank(ch) ( (unsigned char)(ch) == ' ' || (unsigned char)(ch) == '\t' )
48#endif
49
50
51
52
53/**
54 * Parses the output from a preprocessor of a C-style language.
55 *
56 * @returns 0 on success.
57 * @returns 1 or other approriate exit code on failure.
58 * @param pThis Pointer to the 'dep' instance.
59 * @param pInput Input stream. (probably not seekable)
60 */
61static int ParseCPrecompiler(PDEPGLOBALS pThis, FILE *pInput)
62{
63 enum
64 {
65 C_DISCOVER = 0,
66 C_SKIP_LINE,
67 C_PARSE_FILENAME,
68 C_EOF
69 } enmMode = C_DISCOVER;
70 PDEP pDep = NULL;
71 int ch = 0;
72 char szBuf[8192];
73
74 for (;;)
75 {
76 switch (enmMode)
77 {
78 /*
79 * Start of line, need to look for '#[[:space]]*line <num> "file"' and '# <num> "file"'.
80 */
81 case C_DISCOVER:
82 /* first find '#' */
83 while ((ch = FGETC(pInput)) != EOF)
84 if (!isblank(ch))
85 break;
86 if (ch == '#')
87 {
88 /* skip spaces */
89 while ((ch = FGETC(pInput)) != EOF)
90 if (!isblank(ch))
91 break;
92
93 /* check for "line" */
94 if (ch == 'l')
95 {
96 if ( (ch = FGETC(pInput)) == 'i'
97 && (ch = FGETC(pInput)) == 'n'
98 && (ch = FGETC(pInput)) == 'e')
99 {
100 ch = FGETC(pInput);
101 if (isblank(ch))
102 {
103 /* skip spaces */
104 while ((ch = FGETC(pInput)) != EOF)
105 if (!isblank(ch))
106 break;
107 }
108 else
109 ch = 'x';
110 }
111 else
112 ch = 'x';
113 }
114
115 /* line number */
116 if (ch >= '0' && ch <= '9')
117 {
118 /* skip the number following spaces */
119 while ((ch = FGETC(pInput)) != EOF)
120 if (!isxdigit(ch))
121 break;
122 if (isblank(ch))
123 {
124 while ((ch = FGETC(pInput)) != EOF)
125 if (!isblank(ch))
126 break;
127 /* quoted filename */
128 if (ch == '"')
129 {
130 enmMode = C_PARSE_FILENAME;
131 break;
132 }
133 }
134 }
135 }
136 enmMode = C_SKIP_LINE;
137 break;
138
139 /*
140 * Skip past the end of the current line.
141 */
142 case C_SKIP_LINE:
143 do
144 {
145 if ( ch == '\r'
146 || ch == '\n')
147 break;
148 } while ((ch = FGETC(pInput)) != EOF);
149 enmMode = C_DISCOVER;
150 break;
151
152 /*
153 * Parse the filename.
154 */
155 case C_PARSE_FILENAME:
156 {
157 /* retreive and unescape the filename. */
158 char *psz = &szBuf[0];
159 while ( (ch = FGETC(pInput)) != EOF
160 && psz < &szBuf[sizeof(szBuf) - 1])
161 {
162 if (ch == '\\')
163 {
164 ch = FGETC(pInput);
165 switch (ch)
166 {
167 case '\\': ch = '/'; break;
168 case 't': ch = '\t'; break;
169 case 'r': ch = '\r'; break;
170 case 'n': ch = '\n'; break;
171 case 'b': ch = '\b'; break;
172 default:
173 fprintf(stderr, "warning: unknown escape char '%c'\n", ch);
174 continue;
175
176 }
177 *psz++ = ch == '\\' ? '/' : ch;
178 }
179 else if (ch != '"')
180 *psz++ = ch;
181 else
182 {
183 size_t cchFilename = psz - &szBuf[0];
184 *psz = '\0';
185 /* compare with current dep, add & switch on mismatch. */
186 if ( !pDep
187 || pDep->cchFilename != cchFilename
188 || memcmp(pDep->szFilename, szBuf, cchFilename))
189 pDep = depAdd(pThis, szBuf, cchFilename);
190 break;
191 }
192 }
193 enmMode = C_SKIP_LINE;
194 break;
195 }
196
197 /*
198 * Handle EOF.
199 */
200 case C_EOF:
201 if (feof(pInput))
202 return 0;
203 enmMode = C_DISCOVER;
204 break;
205 }
206 if (ch == EOF)
207 enmMode = C_EOF;
208 }
209
210 return 0;
211}
212
213
214static int usage(FILE *pOut, const char *argv0)
215{
216 fprintf(pOut,
217 "usage: %s [-l=c] -o <output> -t <target> [-f] [-s] < - | <filename> | -e <cmdline> >\n"
218 " or: %s --help\n"
219 " or: %s --version\n",
220 argv0, argv0, argv0);
221 return 1;
222}
223
224
225int main(int argc, char *argv[])
226{
227 int i;
228 DEPGLOBALS This;
229
230 /* Arguments. */
231 int iExec = 0;
232 FILE *pOutput = NULL;
233 const char *pszOutput = NULL;
234 FILE *pInput = NULL;
235 const char *pszTarget = NULL;
236 int fStubs = 0;
237 int fFixCase = 0;
238 /* Argument parsing. */
239 int fInput = 0; /* set when we've found input argument. */
240
241 /*
242 * Parse arguments.
243 */
244 if (argc <= 1)
245 return usage(stderr, argv[0]);
246 for (i = 1; i < argc; i++)
247 {
248 if (argv[i][0] == '-')
249 {
250 const char *psz = &argv[i][1];
251 if (*psz == '-')
252 {
253 if (!strcmp(psz, "-help"))
254 psz = "h";
255 else if (!strcmp(psz, "-version"))
256 psz = "V";
257 }
258
259 switch (*psz)
260 {
261 /*
262 * Output file.
263 */
264 case 'o':
265 {
266 pszOutput = &argv[i][2];
267 if (pOutput)
268 {
269 fprintf(stderr, "%s: syntax error: only one output file!\n", argv[0]);
270 return 1;
271 }
272 if (!*pszOutput)
273 {
274 if (++i >= argc)
275 {
276 fprintf(stderr, "%s: syntax error: The '-o' argument is missing the filename.\n", argv[0]);
277 return 1;
278 }
279 pszOutput = argv[i];
280 }
281 if (pszOutput[0] == '-' && !pszOutput[1])
282 pOutput = stdout;
283 else
284 pOutput = fopen(pszOutput, "w");
285 if (!pOutput)
286 {
287 fprintf(stderr, "%s: error: Failed to create output file '%s'.\n", argv[0], pszOutput);
288 return 1;
289 }
290 break;
291 }
292
293 /*
294 * Language spec.
295 */
296 case 'l':
297 {
298 const char *pszValue = &argv[i][2];
299 if (*pszValue == '=')
300 pszValue++;
301 if (!strcmp(pszValue, "c"))
302 ;
303 else
304 {
305 fprintf(stderr, "%s: error: The '%s' language is not supported.\n", argv[0], pszValue);
306 return 1;
307 }
308 break;
309 }
310
311 /*
312 * Target name.
313 */
314 case 't':
315 {
316 if (pszTarget)
317 {
318 fprintf(stderr, "%s: syntax error: only one target!\n", argv[0]);
319 return 1;
320 }
321 pszTarget = &argv[i][2];
322 if (!*pszTarget)
323 {
324 if (++i >= argc)
325 {
326 fprintf(stderr, "%s: syntax error: The '-t' argument is missing the target name.\n", argv[0]);
327 return 1;
328 }
329 pszTarget = argv[i];
330 }
331 break;
332 }
333
334 /*
335 * Exec.
336 */
337 case 'e':
338 {
339 if (++i >= argc)
340 {
341 fprintf(stderr, "%s: syntax error: The '-e' argument is missing the command.\n", argv[0]);
342 return 1;
343 }
344 iExec = i;
345 i = argc - 1;
346 break;
347 }
348
349 /*
350 * Pipe input.
351 */
352 case '\0':
353 {
354 pInput = stdin;
355 fInput = 1;
356 break;
357 }
358
359 /*
360 * Fix case.
361 */
362 case 'f':
363 {
364 fFixCase = 1;
365 break;
366 }
367
368 /*
369 * Generate stubs.
370 */
371 case 's':
372 {
373 fStubs = 1;
374 break;
375 }
376
377 /*
378 * The obligatory help and version.
379 */
380 case 'h':
381 usage(stdout, argv[0]);
382 return 0;
383
384 case 'V':
385 printf("kDepPre - kBuild version %d.%d.%d\n"
386 "Copyright (C) 2005-2008 knut st. osmundsen\n",
387 KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH);
388 return 0;
389
390 /*
391 * Invalid argument.
392 */
393 default:
394 fprintf(stderr, "%s: syntax error: Invalid argument '%s'.\n", argv[0], argv[i]);
395 return usage(stderr, argv[0]);
396 }
397 }
398 else
399 {
400 pInput = fopen(argv[i], "r");
401 if (!pInput)
402 {
403 fprintf(stderr, "%s: error: Failed to open input file '%s'.\n", argv[0], argv[i]);
404 return 1;
405 }
406 fInput = 1;
407 }
408
409 /*
410 * End of the line?
411 */
412 if (fInput)
413 {
414 if (++i < argc)
415 {
416 fprintf(stderr, "%s: syntax error: No arguments shall follow the input spec.\n", argv[0]);
417 return 1;
418 }
419 break;
420 }
421 }
422
423 /*
424 * Got all we require?
425 */
426 if (!pInput && iExec <= 0)
427 {
428 fprintf(stderr, "%s: syntax error: No input!\n", argv[0]);
429 return 1;
430 }
431 if (!pOutput)
432 {
433 fprintf(stderr, "%s: syntax error: No output!\n", argv[0]);
434 return 1;
435 }
436 if (!pszTarget)
437 {
438 fprintf(stderr, "%s: syntax error: No target!\n", argv[0]);
439 return 1;
440 }
441
442 /*
443 * Spawn process?
444 */
445 if (iExec > 0)
446 {
447 fprintf(stderr, "%s: -e is not yet implemented!\n", argv[0]);
448 return 1;
449 }
450
451 /*
452 * Do the parsing.
453 */
454 depInit(&This);
455 i = ParseCPrecompiler(&This, pInput);
456
457 /*
458 * Reap child.
459 */
460 if (iExec > 0)
461 {
462 /* later */
463 }
464
465 /*
466 * Write the dependecy file.
467 */
468 if (!i)
469 {
470 depOptimize(&This, fFixCase, 0 /* fQuiet */, NULL /*pszIgnoredExt*/);
471 depPrintTargetWithDeps(&This, pOutput, pszTarget, 1 /*fEscapeTarget*/);
472 if (fStubs)
473 depPrintStubs(&This, pOutput);
474 }
475
476 /*
477 * Close the output, delete output on failure.
478 */
479 if (!i && ferror(pOutput))
480 {
481 i = 1;
482 fprintf(stderr, "%s: error: Error writing to '%s'.\n", argv[0], pszOutput);
483 }
484 fclose(pOutput);
485 if (i)
486 {
487 if (unlink(pszOutput))
488 fprintf(stderr, "%s: warning: failed to remove output file '%s' on failure.\n", argv[0], pszOutput);
489 }
490
491 depCleanup(&This);
492
493 return i;
494}
495
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