VirtualBox

source: vbox/trunk/src/bldprogs/scmrw-kmk.cpp@ 98374

Last change on this file since 98374 was 98374, checked in by vboxsync, 23 months ago

scm: Split the ~2000 lines of .kmk rewriting code out of scmrw.cpp and into a separate file. bugref:10348

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 67.3 KB
Line 
1/* $Id: scmrw-kmk.cpp 98374 2023-02-01 09:48:59Z vboxsync $ */
2/** @file
3 * IPRT Testcase / Tool - Source Code Massager, Makefile.kmk/kup.
4 */
5
6/*
7 * Copyright (C) 2010-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <iprt/assert.h>
33#include <iprt/ctype.h>
34#include <iprt/dir.h>
35#include <iprt/env.h>
36#include <iprt/file.h>
37#include <iprt/err.h>
38#include <iprt/getopt.h>
39#include <iprt/initterm.h>
40#include <iprt/mem.h>
41#include <iprt/message.h>
42#include <iprt/param.h>
43#include <iprt/path.h>
44#include <iprt/process.h>
45#include <iprt/stream.h>
46#include <iprt/string.h>
47
48#include "scm.h"
49
50
51/*********************************************************************************************************************************
52* Structures and Typedefs *
53*********************************************************************************************************************************/
54typedef enum KMKTOKEN
55{
56 kKmkToken_Word = 0,
57 kKmkToken_Comment,
58
59 /* Conditionals: */
60 kKmkToken_ifeq,
61 kKmkToken_ifneq,
62 kKmkToken_if1of,
63 kKmkToken_ifn1of,
64 kKmkToken_ifdef,
65 kKmkToken_ifndef,
66 kKmkToken_if,
67 kKmkToken_else,
68 kKmkToken_endif,
69
70 /* Includes: */
71 kKmkToken_include,
72 kKmkToken_sinclude,
73 kKmkToken_dash_include,
74 kKmkToken_includedep,
75 kKmkToken_includedep_queue,
76 kKmkToken_includedep_flush,
77
78 /* Others: */
79 kKmkToken_define,
80 kKmkToken_endef,
81 kKmkToken_export,
82 kKmkToken_unexport,
83 kKmkToken_local,
84 kKmkToken_override,
85 kKmkToken_undefine
86} KMKTOKEN;
87
88typedef struct KMKPARSER
89{
90 struct
91 {
92 KMKTOKEN enmToken;
93 uint32_t iLine;
94 bool fIgnoreNesting;
95 } aDepth[64];
96 unsigned iDepth;
97 unsigned iActualDepth;
98 bool fInRecipe;
99
100 /** The current line number (for error messages and peeking). */
101 uint32_t iLine;
102 /** The EOL type of the current line. */
103 SCMEOL enmEol;
104 /** The length of the current line. */
105 size_t cchLine;
106 /** Pointer to the start of the current line. */
107 char const *pchLine;
108
109 /** @name Only used for rule/assignment parsing.
110 * @{ */
111 /** Number of continuation lines at current rule/assignment. */
112 uint32_t cLines;
113 /** Characters in continuation lines at current rule/assignment. */
114 size_t cchTotalLine;
115 /** @} */
116
117 /** The SCM rewriter state. */
118 PSCMRWSTATE pState;
119 /** The input stream. */
120 PSCMSTREAM pIn;
121 /** The output stream. */
122 PSCMSTREAM pOut;
123 /** The settings. */
124 PCSCMSETTINGSBASE pSettings;
125 /** Scratch buffer. */
126 char szBuf[4096];
127} KMKPARSER;
128
129
130
131static KMKTOKEN scmKmkIdentifyToken(const char *pchWord, size_t cchWord)
132{
133 static struct { const char *psz; uint32_t cch; KMKTOKEN enmToken; } s_aTokens[] =
134 {
135 { RT_STR_TUPLE("if"), kKmkToken_if },
136 { RT_STR_TUPLE("ifeq"), kKmkToken_ifeq },
137 { RT_STR_TUPLE("ifneq"), kKmkToken_ifneq },
138 { RT_STR_TUPLE("if1of"), kKmkToken_if1of },
139 { RT_STR_TUPLE("ifn1of"), kKmkToken_ifn1of },
140 { RT_STR_TUPLE("ifdef"), kKmkToken_ifdef },
141 { RT_STR_TUPLE("ifndef"), kKmkToken_ifndef },
142 { RT_STR_TUPLE("else"), kKmkToken_else },
143 { RT_STR_TUPLE("endif"), kKmkToken_endif },
144 { RT_STR_TUPLE("include"), kKmkToken_include },
145 { RT_STR_TUPLE("sinclude"), kKmkToken_sinclude },
146 { RT_STR_TUPLE("-include"), kKmkToken_dash_include },
147 { RT_STR_TUPLE("includedep"), kKmkToken_includedep },
148 { RT_STR_TUPLE("includedep-queue"), kKmkToken_includedep_queue },
149 { RT_STR_TUPLE("includedep-flush"), kKmkToken_includedep_flush },
150 { RT_STR_TUPLE("define"), kKmkToken_define },
151 { RT_STR_TUPLE("endef"), kKmkToken_endef },
152 { RT_STR_TUPLE("export"), kKmkToken_export },
153 { RT_STR_TUPLE("unexport"), kKmkToken_unexport },
154 { RT_STR_TUPLE("local"), kKmkToken_local },
155 { RT_STR_TUPLE("override"), kKmkToken_override },
156 { RT_STR_TUPLE("undefine"), kKmkToken_undefine },
157 };
158 char chFirst = *pchWord;
159 if ( chFirst == 'i'
160 || chFirst == 'e'
161 || chFirst == 'd'
162 || chFirst == 's'
163 || chFirst == '-'
164 || chFirst == 'u'
165 || chFirst == 'l'
166 || chFirst == 'o')
167 {
168 for (size_t i = 0; i < RT_ELEMENTS(s_aTokens); i++)
169 if ( s_aTokens[i].cch == cchWord
170 && *s_aTokens[i].psz == chFirst
171 && memcmp(s_aTokens[i].psz, pchWord, cchWord) == 0)
172 return s_aTokens[i].enmToken;
173 }
174#ifdef VBOX_STRICT
175 else
176 for (size_t i = 0; i < RT_ELEMENTS(s_aTokens); i++)
177 Assert(chFirst != *s_aTokens[i].psz);
178#endif
179
180 if (chFirst == '#')
181 return kKmkToken_Comment;
182 return kKmkToken_Word;
183}
184
185
186/**
187 * Gives up on the current line, copying it as it and requesting manual repair.
188 */
189static bool scmKmkGiveUp(KMKPARSER *pParser, const char *pszFormat, ...)
190{
191 va_list va;
192 va_start(va, pszFormat);
193 ScmFixManually(pParser->pState, "%u: %N\n", pParser->iLine, pszFormat, &va);
194 va_end(va);
195
196 ScmStreamPutLine(pParser->pOut, pParser->pchLine, pParser->cchLine, pParser->enmEol);
197 return false;
198}
199
200
201static bool scmKmkIsLineWithContinuationSlow(const char *pchLine, size_t cchLine)
202{
203 size_t cchSlashes = 1;
204 cchLine--;
205 while (cchSlashes < cchLine && pchLine[cchLine - cchSlashes - 1] == '\\')
206 cchSlashes++;
207 return RT_BOOL(cchSlashes & 1);
208}
209
210
211DECLINLINE(bool) scmKmkIsLineWithContinuation(const char *pchLine, size_t cchLine)
212{
213 if (cchLine == 0 || pchLine[cchLine - 1] != '\\')
214 return false;
215 return scmKmkIsLineWithContinuationSlow(pchLine, cchLine);
216}
217
218
219/**
220 * Finds the length of a line where line continuation is in play.
221 *
222 * @returns Length from start of current line to the final unescaped EOL.
223 * @param pParser The KMK parser state.
224 * @param pcLine Where to return the number of lines. Optional.
225 * @param pcchMaxLeadWord Where to return the max lead word length on
226 * subsequent lines. Used to help balance multi-line
227 * 'if' statements (imperfect). Optional.
228 */
229static size_t scmKmkLineContinuationPeek(KMKPARSER *pParser, uint32_t *pcLines, size_t *pcchMaxLeadWord)
230{
231 size_t const offSaved = ScmStreamTell(pParser->pIn);
232 uint32_t cLines = 1;
233 size_t cchMaxLeadWord = 0;
234 const char *pchLine = pParser->pchLine;
235 size_t cchLine = pParser->cchLine;
236 SCMEOL enmEol;
237 for (;;)
238 {
239 /* Return if no line continuation (or end of stream): */
240 if ( cchLine == 0
241 || !scmKmkIsLineWithContinuation(pchLine, cchLine)
242 || ScmStreamIsEndOfStream(pParser->pIn))
243 {
244 ScmStreamSeekAbsolute(pParser->pIn, offSaved);
245 if (pcLines)
246 *pcLines = cLines;
247 if (pcchMaxLeadWord)
248 *pcchMaxLeadWord = cchMaxLeadWord;
249 return (size_t)(pchLine - pParser->pchLine) + cchLine;
250 }
251
252 /* Get the next line: */
253 pchLine = ScmStreamGetLine(pParser->pIn, &cchLine, &enmEol);
254 cLines++;
255
256 /* Check the length of the first word if requested: */
257 if (pcchMaxLeadWord)
258 {
259 size_t offLine = 0;
260 while (offLine < cchLine && RT_C_IS_BLANK(pchLine[offLine]))
261 offLine++;
262
263 size_t const offStartWord = offLine;
264 while (offLine < cchLine && !RT_C_IS_BLANK(pchLine[offLine]))
265 offLine++;
266
267 if (offLine - offStartWord > cchMaxLeadWord)
268 cchMaxLeadWord = offLine - offStartWord;
269 }
270 }
271}
272
273
274static bool scmKmkPushNesting(KMKPARSER *pParser, KMKTOKEN enmToken)
275{
276 uint32_t iDepth = pParser->iDepth;
277 if (iDepth + 1 >= RT_ELEMENTS(pParser->aDepth))
278 {
279 ScmError(pParser->pState, VERR_ASN1_TOO_DEEPLY_NESTED /*?*/, "%u: Too deep if/define nesting!\n", pParser->iLine);
280 return false;
281 }
282
283 pParser->aDepth[iDepth].enmToken = enmToken;
284 pParser->aDepth[iDepth].iLine = pParser->iLine;
285 pParser->aDepth[iDepth].fIgnoreNesting = false;
286 pParser->iDepth = iDepth + 1;
287 pParser->iActualDepth += 1;
288 return true;
289}
290
291
292/**
293 * Skips a string stopping at @a chStop1 or @a chStop2, taking $() and ${} into
294 * account.
295 */
296static size_t scmKmkSkipExpString(const char *pchLine, size_t cchLine, size_t off, char chStop1, char chStop2 = '\0')
297{
298 unsigned iExpDepth = 0;
299 char ch;
300 while ( off < cchLine
301 && (ch = pchLine[off])
302 && ( (ch != chStop1 && ch != chStop2)
303 || iExpDepth > 0))
304 {
305 off++;
306 if (ch == '$')
307 {
308 ch = pchLine[off];
309 if (ch == '(' || ch == '{')
310 {
311 iExpDepth++;
312 off++;
313 }
314 }
315 else if ((ch == ')' || ch == '}') && iExpDepth > 0)
316 iExpDepth--;
317 }
318 return off;
319}
320
321
322/** Context for scmKmkWordLength. */
323typedef enum
324{
325 /** Target file or assignment.
326 * Separators: space, '=', ':' */
327 kKmkWordCtx_TargetFileOrAssignment,
328 /** Target file.
329 * Separators: space, ':' */
330 kKmkWordCtx_TargetFile,
331 /** Dependency file or (target variable) assignment.
332 * Separators: space, '=', ':', '|' */
333 kKmkWordCtx_DepFileOrAssignment,
334 /** Dependency file.
335 * Separators: space, '|' */
336 kKmkWordCtx_DepFile
337} KMKWORDCTX;
338
339/**
340 * Finds the length of the word (file) @a offStart.
341 *
342 * @returns Length of word starting at @a offStart. Zero if there is whitespace
343 * at given offset or it's beyond the end of the line (both cases will
344 * assert).
345 * @param pchLine The line.
346 * @param cchLine The line length.
347 * @param offStart Offset to the start of the word.
348 */
349static size_t scmKmkWordLength(const char *pchLine, size_t cchLine, size_t offStart, KMKWORDCTX enmCtx)
350{
351 AssertReturn(offStart < cchLine && !RT_C_IS_BLANK(pchLine[offStart]), 0);
352 size_t off = offStart;
353 while (off < cchLine)
354 {
355 char ch = pchLine[off];
356 if (RT_C_IS_BLANK(ch))
357 break;
358
359 if (ch == ':')
360 {
361 /*
362 * Check for plain driver letter, omitting the archive member variant.
363 */
364 if (off - offStart != 1 || !RT_C_IS_ALPHA(pchLine[off - 1]))
365 {
366 if (off == offStart)
367 {
368 /* We need to check for single and double colon rules as well as
369 simple and immediate assignments here. */
370 off++;
371 if (pchLine[off] == ':')
372 {
373 off++;
374 if (pchLine[off] == '=')
375 {
376 if (enmCtx == kKmkWordCtx_TargetFileOrAssignment || enmCtx == kKmkWordCtx_DepFileOrAssignment)
377 return 3; /* ::= - immediate assignment. */
378 off++;
379 }
380 else if (enmCtx != kKmkWordCtx_DepFile)
381 return 2; /* :: - double colon rule */
382 }
383 else if (pchLine[off] == '=')
384 {
385 if (enmCtx == kKmkWordCtx_TargetFileOrAssignment || enmCtx == kKmkWordCtx_DepFileOrAssignment)
386 return 2; /* := - simple assignment. */
387 off++;
388 }
389 else if (enmCtx != kKmkWordCtx_DepFile)
390 return 1; /* : - regular rule. */
391 continue;
392 }
393 /* ':' is a separator except in DepFile context. */
394 else if (enmCtx != kKmkWordCtx_DepFile)
395 return off - offStart;
396 }
397 }
398 else if (ch == '=')
399 {
400 /*
401 * Assignment. We check for the previous character too so we'll catch
402 * append, prepend and conditional assignments. Simple and immediate
403 * assignments are handled above.
404 */
405 if ( enmCtx == kKmkWordCtx_TargetFileOrAssignment
406 || enmCtx == kKmkWordCtx_DepFileOrAssignment)
407 {
408 if (off > offStart)
409 {
410 ch = pchLine[off - 1];
411 if (ch == '?' || ch == '+' || ch == '>')
412 off = off - 1 == offStart
413 ? off + 2 /* return '+=', '?=', '<=' */
414 : off - 1; /* up to '+=', '?=', '<=' */
415 else
416 Assert(ch != ':'); /* handled above */
417 }
418 else
419 off++; /* '=' */
420 return off - offStart;
421 }
422 }
423 else if (ch == '|')
424 {
425 /*
426 * This is rather straight forward.
427 */
428 if (enmCtx == kKmkWordCtx_DepFileOrAssignment || enmCtx == kKmkWordCtx_DepFile)
429 {
430 if (off == offStart)
431 return 1;
432 return off - offStart;
433 }
434 }
435 off++;
436 }
437 return off - offStart;
438}
439
440
441static bool scmKmkTailComment(KMKPARSER *pParser, const char *pchLine, size_t cchLine, size_t offSrc, char **ppszDst)
442{
443 /* Wind back offSrc to the first blank space (not all callers can do this). */
444 Assert(offSrc <= cchLine);
445 while (offSrc > 0 && RT_C_IS_SPACE(pchLine[offSrc - 1]))
446 offSrc--;
447 size_t const offSrcStart = offSrc;
448
449 /* Skip blanks. */
450 while (offSrc < cchLine && RT_C_IS_SPACE(pchLine[offSrc]))
451 offSrc++;
452 if (offSrc >= cchLine)
453 return true;
454
455 /* Is it a comment? */
456 char *pszDst = *ppszDst;
457 if (pchLine[offSrc] == '#')
458 {
459 /* Try preserve the start column number. */
460/** @todo tabs */
461 size_t const offDst = pszDst - pParser->szBuf;
462 if (offDst < offSrc)
463 {
464 memset(pszDst, ' ', offSrc - offDst);
465 pszDst += offSrc - offDst;
466 }
467 else if (offSrc != offSrcStart)
468 *pszDst++ = ' ';
469
470 *ppszDst = pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], cchLine - offSrc);
471 return false; /*dummy*/
472 }
473
474 /* Complain and copy out the text unmodified. */
475 ScmError(pParser->pState, VERR_PARSE_ERROR, "%u:%u: Expected comment, found: %.*s",
476 pParser->iLine, offSrc, cchLine - offSrc, &pchLine[offSrc]);
477 *ppszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], cchLine - offSrcStart);
478 return false; /*dummy*/
479}
480
481
482/**
483 * Deals with: ifeq, ifneq, if1of and ifn1of
484 *
485 * @returns dummy (false) to facility return + call.
486 */
487static bool scmKmkHandleIfParentheses(KMKPARSER *pParser, size_t offToken, KMKTOKEN enmToken, size_t cchToken, bool fElse)
488{
489 const char * const pchLine = pParser->pchLine;
490 size_t const cchLine = pParser->cchLine;
491 uint32_t const cchIndent = pParser->iActualDepth
492 - (fElse && pParser->iDepth > 0 && !pParser->aDepth[pParser->iDepth].fIgnoreNesting);
493
494 /*
495 * Push it onto the stack. All these nestings are relevant.
496 */
497 if (!scmKmkPushNesting(pParser, enmToken))
498 return false;
499
500 /*
501 * We do not allow line continuation for these.
502 */
503 if (scmKmkIsLineWithContinuation(pchLine, cchLine))
504 return scmKmkGiveUp(pParser, "Line continuation not allowed with '%.*s' directive.", cchToken, &pchLine[offToken]);
505
506 /*
507 * We stage the modified line in the buffer, so check that the line isn't
508 * too long (it seriously should be).
509 */
510 if (cchLine + cchIndent + 32 > sizeof(pParser->szBuf))
511 return scmKmkGiveUp(pParser, "Line too long for a '%.*s' directive: %u chars", cchToken, &pchLine[offToken], cchLine);
512 char *pszDst = pParser->szBuf;
513
514 /*
515 * Emit indent and initial token.
516 */
517 memset(pszDst, ' ', cchIndent);
518 pszDst += cchIndent;
519
520 if (fElse)
521 pszDst = (char *)mempcpy(pszDst, RT_STR_TUPLE("else "));
522
523 memcpy(pszDst, &pchLine[offToken], cchToken);
524 pszDst += cchToken;
525
526 size_t offSrc = offToken + cchToken;
527
528 /*
529 * There shall be exactly one space between the token and the opening parenthesis.
530 */
531 if (pchLine[offSrc] == ' ' && pchLine[offSrc + 1] == '(')
532 offSrc += 2;
533 else
534 {
535 while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc]))
536 offSrc++;
537 if (pchLine[offSrc] != '(')
538 return scmKmkGiveUp(pParser, "Expected '(' to follow '%.*s'", cchToken, &pchLine[offToken]);
539 offSrc++;
540 }
541 *pszDst++ = ' ';
542 *pszDst++ = '(';
543
544 /*
545 * Skip spaces after the opening parenthesis.
546 */
547 while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc]))
548 offSrc++;
549
550 /*
551 * Work up to the ',' separator. It shall likewise not be preceeded by any spaces.
552 * Need to take $(func 1,2,3) calls into account here, so we trac () and {} while
553 * skipping ahead.
554 */
555 if (pchLine[offSrc] != ',')
556 {
557 size_t const offSrcStart = offSrc;
558 offSrc = scmKmkSkipExpString(pchLine, cchLine, offSrc, ',');
559 if (pchLine[offSrc] != ',')
560 return scmKmkGiveUp(pParser, "Expected ',' somewhere after '%.*s('", cchToken, &pchLine[offToken]);
561
562 size_t cchCopy = offSrc - offSrcStart;
563 while (cchCopy > 0 && RT_C_IS_BLANK(pchLine[offSrcStart + cchCopy - 1]))
564 cchCopy--;
565
566 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], cchCopy);
567 }
568 /* 'if1of(, stuff)' does not make sense in committed code: */
569 else if (enmToken == kKmkToken_if1of || enmToken == kKmkToken_ifn1of)
570 return scmKmkGiveUp(pParser, "Left set cannot be empty for '%.*s'", cchToken, &pchLine[offToken]);
571 offSrc++;
572 *pszDst++ = ',';
573
574 /*
575 * For if1of and ifn1of we require a space after the comma, whereas ifeq and
576 * ifneq shall not have any blanks. This is to help tell them apart.
577 */
578 if (enmToken == kKmkToken_if1of || enmToken == kKmkToken_ifn1of)
579 {
580 *pszDst++ = ' ';
581 if (pchLine[offSrc] == ' ')
582 offSrc++;
583 }
584 while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc]))
585 offSrc++;
586
587 if (pchLine[offSrc] != ')')
588 {
589 size_t const offSrcStart = offSrc;
590 offSrc = scmKmkSkipExpString(pchLine, cchLine, offSrc, ')');
591 if (pchLine[offSrc] != ')')
592 return scmKmkGiveUp(pParser, "No closing parenthesis for '%.*s'?", cchToken, &pchLine[offToken]);
593
594 size_t cchCopy = offSrc - offSrcStart;
595 while (cchCopy > 0 && RT_C_IS_BLANK(pchLine[offSrcStart + cchCopy - 1]))
596 cchCopy--;
597
598 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], cchCopy);
599 }
600 /* 'if1of(stuff, )' does not make sense in committed code: */
601 else if (enmToken == kKmkToken_if1of || enmToken == kKmkToken_ifn1of)
602 return scmKmkGiveUp(pParser, "Right set cannot be empty for '%.*s'", cchToken, &pchLine[offToken]);
603 offSrc++;
604 *pszDst++ = ')';
605
606 /*
607 * Handle comment.
608 */
609 if (offSrc < cchLine)
610 scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst);
611
612 /*
613 * Done.
614 */
615 *pszDst = '\0';
616 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
617 return false; /* dummy */
618}
619
620
621/**
622 * Deals with: if, ifdef and ifndef
623 *
624 * @returns dummy (false) to facility return + call.
625 */
626static bool scmKmkHandleIfSpace(KMKPARSER *pParser, size_t offToken, KMKTOKEN enmToken, size_t cchToken, bool fElse)
627{
628 const char *pchLine = pParser->pchLine;
629 size_t cchLine = pParser->cchLine;
630 uint32_t const cchIndent = pParser->iActualDepth
631 - (fElse && pParser->iDepth > 0 && !pParser->aDepth[pParser->iDepth].fIgnoreNesting);
632
633 /*
634 * Push it onto the stack.
635 *
636 * For ifndef we ignore the outmost ifndef in non-Makefile.kmk files, if
637 * the define matches the typical pattern for a file blocker.
638 */
639 if (!fElse)
640 {
641 if (!scmKmkPushNesting(pParser, enmToken))
642 return false;
643 }
644 else
645 {
646 pParser->aDepth[pParser->iDepth - 1].enmToken = enmToken;
647 pParser->aDepth[pParser->iDepth - 1].iLine = pParser->iLine;
648 }
649 bool fIgnoredNesting = false;
650 if (enmToken == kKmkToken_ifndef)
651 {
652 /** @todo */
653 }
654
655 /*
656 * We do not allow line continuation for these.
657 */
658 uint32_t cLines = 1;
659 size_t cchMaxLeadWord = 0;
660 size_t cchTotalLine = cchLine;
661 if (scmKmkIsLineWithContinuation(pchLine, cchLine))
662 {
663 if (enmToken != kKmkToken_if)
664 return scmKmkGiveUp(pParser, "Line continuation not allowed with '%.*s' directive.", cchToken, &pchLine[offToken]);
665 cchTotalLine = scmKmkLineContinuationPeek(pParser, &cLines, &cchMaxLeadWord);
666 }
667
668 /*
669 * We stage the modified line in the buffer, so check that the line isn't
670 * too long (plain if can be long, but not ifndef/ifdef).
671 */
672 if (cchTotalLine + pParser->iActualDepth + 32 > sizeof(pParser->szBuf))
673 return scmKmkGiveUp(pParser, "Line too long for a '%.*s' directive: %u chars",
674 cchToken, &pchLine[offToken], cchTotalLine);
675 char *pszDst = pParser->szBuf;
676
677 /*
678 * Emit indent and initial token.
679 */
680 memset(pszDst, ' ', cchIndent);
681 pszDst += cchIndent;
682
683 if (fElse)
684 pszDst = (char *)mempcpy(pszDst, RT_STR_TUPLE("else "));
685
686 memcpy(pszDst, &pchLine[offToken], cchToken);
687 pszDst += cchToken;
688
689 size_t offSrc = offToken + cchToken;
690
691 /*
692 * ifndef/ifdef shall have exactly one space. For 'if' we allow up to 4, but
693 * we'll deal with that further down.
694 */
695 size_t cchSpaces = 0;
696 while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc]))
697 {
698 cchSpaces++;
699 offSrc++;
700 }
701 if (cchSpaces == 0)
702 return scmKmkGiveUp(pParser, "Nothing following '%.*s' or bogus line continuation?", cchToken, &pchLine[offToken]);
703 *pszDst++ = ' ';
704
705 /*
706 * For ifdef and ifndef there now comes a single word.
707 */
708 if (enmToken != kKmkToken_if)
709 {
710 size_t const offSrcStart = offSrc;
711 offSrc = scmKmkSkipExpString(pchLine, cchLine, offSrc, ' ', '\t'); /** @todo probably not entirely correct */
712 if (offSrc == offSrcStart)
713 return scmKmkGiveUp(pParser, "No word following '%.*s'?", cchToken, &pchLine[offToken]);
714
715 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], offSrc - offSrcStart);
716 }
717 /*
718 * While for 'if' things are more complicated, especially if it spans more
719 * than one line.
720 */
721 else if (cLines <= 1)
722 {
723 /* Single line expression: Just assume the expression goes up to the
724 EOL or comment hash. Strip and copy as-is for now. */
725 const char *pchSrcHash = (const char *)memchr(&pchLine[offSrc], '#', cchLine - offSrc);
726 size_t cchExpr = pchSrcHash ? pchSrcHash - &pchLine[offSrc] : cchLine - offSrc;
727 while (cchExpr > 0 && RT_C_IS_BLANK(pchLine[offSrc + cchExpr - 1]))
728 cchExpr--;
729
730 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], cchExpr);
731 offSrc += cchExpr;
732 }
733 else
734 {
735 /* Multi line expression: We normalize leading whitespace using
736 cchMaxLeadWord for now. Expression on line 2+ are indented by two
737 extra characters, because we'd otherwise be puttin the operator on
738 the same level as the 'if', which would be confusing. Thus:
739
740 if expr1
741 + expr2
742 endif
743
744 if expr1
745 || expr2
746 endif
747
748 if expr3
749 vtg expr4
750 endif
751
752 We do '#' / EOL handling for the final line the same way as above.
753
754 Later we should add the ability to rework the expression properly,
755 making sure new lines starts with operators and such. */
756 /** @todo Implement simples expression parser and indenter, possibly also
757 * removing unnecessary parentheses. Can be shared with C/C++. */
758 if (cchMaxLeadWord > 3)
759 return scmKmkGiveUp(pParser,
760 "Bogus multi-line 'if' expression! Extra lines must start with operator (cchMaxLeadWord=%u).",
761 cchMaxLeadWord);
762 memset(pszDst, ' ', cchMaxLeadWord);
763 pszDst += cchMaxLeadWord;
764
765 size_t cchSrcContIndent = offToken + 2;
766 for (uint32_t iSubLine = 0; iSubLine < cLines - 1; iSubLine++)
767 {
768 /* Trim the line. */
769 size_t offSrcEnd = cchLine;
770 Assert(pchLine[offSrcEnd - 1] == '\\');
771 offSrcEnd--;
772
773 if (pchLine[offSrcEnd - 1] == '\\')
774 return scmKmkGiveUp(pParser, "Escaped '\\' before line continuation in 'if' expression is not allowed!");
775
776 while (offSrcEnd > offSrc && RT_C_IS_BLANK(pchLine[offSrcEnd - 1]))
777 offSrcEnd--;
778
779 /* Comments with line continuation is not allowed in commited makefiles. */
780 if (offSrc < offSrcEnd && memchr(&pchLine[offSrc], '#', cchLine - offSrc) != NULL)
781 return scmKmkGiveUp(pParser, "Comment in multi-line 'if' expression is not allowed to start before the final line!");
782
783 /* Output it. */
784 if (offSrc < offSrcEnd)
785 {
786 if (iSubLine > 0 && offSrc > cchSrcContIndent)
787 {
788 memset(pszDst, ' ', offSrc - cchSrcContIndent);
789 pszDst += offSrc - cchSrcContIndent;
790 }
791 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], offSrcEnd - offSrc);
792 *pszDst++ = ' ';
793 }
794 else if (iSubLine == 0)
795 return scmKmkGiveUp(pParser, "Expected expression after 'if', not line continuation!");
796 *pszDst++ = '\\';
797 *pszDst = '\0';
798 size_t cchDst = (size_t)(pszDst - pParser->szBuf);
799 ScmStreamPutLine(pParser->pOut, pParser->szBuf, cchDst, pParser->enmEol);
800
801 /*
802 * Fetch the next line and start processing it.
803 */
804 pParser->pchLine = pchLine = ScmStreamGetLine(pParser->pIn, &pParser->cchLine, &pParser->enmEol);
805 if (!pchLine)
806 {
807 ScmError(pParser->pState, VERR_INTERNAL_ERROR_3, "ScmStreamGetLine unexpectedly returned NULL!");
808 return false;
809 }
810 cchLine = pParser->cchLine;
811 pParser->iLine++;
812
813 /* Skip leading whitespace and adjust the source continuation indent: */
814 offSrc = 0;
815 while (offSrc < cchLine && RT_C_IS_SPACE(pchLine[offSrc]))
816 offSrc++;
817 /** @todo tabs */
818
819 if (iSubLine == 0)
820 cchSrcContIndent = offSrc;
821
822 /* Initial indent: */
823 pszDst = pParser->szBuf;
824 memset(pszDst, ' ', cchIndent + 2);
825 pszDst += cchIndent + 2;
826 }
827
828 /* Output the expression on the final line. */
829 const char *pchSrcHash = (const char *)memchr(&pchLine[offSrc], '#', cchLine - offSrc);
830 size_t cchExpr = pchSrcHash ? pchSrcHash - &pchLine[offSrc] : cchLine - offSrc;
831 while (cchExpr > 0 && RT_C_IS_BLANK(pchLine[offSrc + cchExpr - 1]))
832 cchExpr--;
833
834 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], cchExpr);
835 offSrc += cchExpr;
836 }
837
838
839 /*
840 * Handle comment.
841 *
842 * Here we check for the "scm:ignore-nesting" directive that makes us not
843 * add indentation for this directive. We do this on the destination buffer
844 * as that can be zero terminated and is therefore usable with strstr.
845 */
846 if (offSrc >= cchLine)
847 *pszDst = '\0';
848 else
849 {
850 char * const pszDstSrc = pszDst;
851 scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst);
852 *pszDst = '\0';
853
854 /* Check for special comment making us ignore the nesting. We do this in the
855
856 */
857 if (!fIgnoredNesting && strstr(pszDstSrc, "scm:ignore-nesting") != NULL)
858 {
859 pParser->aDepth[pParser->iDepth - 1].fIgnoreNesting = true;
860 pParser->iActualDepth--;
861 }
862 }
863
864 /*
865 * Done.
866 */
867 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
868 return false; /* dummy */
869}
870
871
872/**
873 * Deals with: else
874 *
875 * @returns dummy (false) to facility return + call.
876 */
877static bool scmKmkHandleElse(KMKPARSER *pParser, size_t offToken)
878{
879 const char * const pchLine = pParser->pchLine;
880 size_t const cchLine = pParser->cchLine;
881
882 if (pParser->iDepth < 1)
883 return scmKmkGiveUp(pParser, "Lone 'else'");
884 uint32_t const cchIndent = pParser->iActualDepth - !pParser->aDepth[pParser->iDepth].fIgnoreNesting;
885
886 /*
887 * Look past the else and check if there any ifxxx token following it.
888 */
889 size_t offSrc = offToken + 4;
890 while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc]))
891 offSrc++;
892 if (offSrc < cchLine)
893 {
894 size_t cchWord = 0;
895 while (offSrc + cchWord < cchLine && RT_C_IS_ALNUM(pchLine[offSrc + cchWord]))
896 cchWord++;
897 if (cchWord)
898 {
899 KMKTOKEN enmToken = scmKmkIdentifyToken(&pchLine[offSrc], cchWord);
900 switch (enmToken)
901 {
902 case kKmkToken_ifeq:
903 case kKmkToken_ifneq:
904 case kKmkToken_if1of:
905 case kKmkToken_ifn1of:
906 return scmKmkHandleIfParentheses(pParser, offSrc, enmToken, cchWord, true /*fElse*/);
907
908 case kKmkToken_ifdef:
909 case kKmkToken_ifndef:
910 case kKmkToken_if:
911 return scmKmkHandleIfSpace(pParser, offSrc, enmToken, cchWord, true /*fElse*/);
912
913 default:
914 break;
915 }
916 }
917 }
918
919 /*
920 * We do not allow line continuation for these.
921 */
922 if (scmKmkIsLineWithContinuation(pchLine, cchLine))
923 return scmKmkGiveUp(pParser, "Line continuation not allowed with 'else' directive.");
924
925 /*
926 * We stage the modified line in the buffer, so check that the line isn't
927 * too long (it seriously should be).
928 */
929 if (cchLine + cchIndent + 32 > sizeof(pParser->szBuf))
930 return scmKmkGiveUp(pParser, "Line too long for a 'else' directive: %u chars", cchLine);
931 char *pszDst = pParser->szBuf;
932
933 /*
934 * Emit indent and initial token.
935 */
936 memset(pszDst, ' ', cchIndent);
937 pszDst = (char *)mempcpy(&pszDst[cchIndent], RT_STR_TUPLE("else"));
938
939 offSrc = offToken + 4;
940
941 /*
942 * Handle comment.
943 */
944 if (offSrc < cchLine)
945 scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst);
946
947 /*
948 * Done.
949 */
950 *pszDst = '\0';
951 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
952 return false; /* dummy */
953}
954
955
956/**
957 * Deals with: endif
958 *
959 * @returns dummy (false) to facility return + call.
960 */
961static bool scmKmkHandleEndif(KMKPARSER *pParser, size_t offToken)
962{
963 const char * const pchLine = pParser->pchLine;
964 size_t const cchLine = pParser->cchLine;
965
966 /*
967 * Pop a nesting.
968 */
969 if (pParser->iDepth < 1)
970 return scmKmkGiveUp(pParser, "Lone 'endif'");
971 uint32_t iDepth = pParser->iDepth - 1;
972 pParser->iDepth = iDepth;
973 if (!pParser->aDepth[iDepth].fIgnoreNesting)
974 {
975 AssertStmt(pParser->iActualDepth > 0, pParser->iActualDepth++);
976 pParser->iActualDepth -= 1;
977 }
978 uint32_t const cchIndent = pParser->iActualDepth;
979
980 /*
981 * We do not allow line continuation for these.
982 */
983 if (scmKmkIsLineWithContinuation(pchLine, cchLine))
984 return scmKmkGiveUp(pParser, "Line continuation not allowed with 'endif' directive.");
985
986 /*
987 * We stage the modified line in the buffer, so check that the line isn't
988 * too long (it seriously should be).
989 */
990 if (cchLine + cchIndent + 32 > sizeof(pParser->szBuf))
991 return scmKmkGiveUp(pParser, "Line too long for a 'else' directive: %u chars", cchLine);
992 char *pszDst = pParser->szBuf;
993
994 /*
995 * Emit indent and initial token.
996 */
997 memset(pszDst, ' ', cchIndent);
998 pszDst = (char *)mempcpy(&pszDst[cchIndent], RT_STR_TUPLE("endif"));
999
1000 size_t offSrc = offToken + 5;
1001
1002 /*
1003 * Handle comment.
1004 */
1005 if (offSrc < cchLine)
1006 scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst);
1007
1008 /*
1009 * Done.
1010 */
1011 *pszDst = '\0';
1012 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
1013 return false; /* dummy */
1014}
1015
1016
1017/**
1018 * Passing thru any line continuation lines following the current one.
1019 */
1020static bool scmKmkPassThruLineContinuationLines(KMKPARSER *pParser)
1021{
1022 while (scmKmkIsLineWithContinuation(pParser->pchLine, pParser->cchLine))
1023 {
1024 pParser->pchLine = ScmStreamGetLine(pParser->pIn, &pParser->cchLine, &pParser->enmEol);
1025 if (!pParser->pchLine)
1026 break;
1027 ScmStreamPutLine(pParser->pOut, pParser->pchLine, pParser->cchLine, pParser->enmEol);
1028 }
1029 return false; /* dummy */
1030}
1031
1032
1033/**
1034 * For dealing with a directive w/o special formatting rules (yet).
1035 *
1036 * @returns dummy (false) to facility return + call.
1037 */
1038static bool scmKmkHandleSimple(KMKPARSER *pParser, size_t offToken, bool fIndentIt = true)
1039{
1040 const char *pchLine = pParser->pchLine;
1041 size_t cchLine = pParser->cchLine;
1042 uint32_t const cchIndent = fIndentIt ? pParser->iActualDepth : 0;
1043
1044 /*
1045 * Just reindent the statement.
1046 */
1047 ScmStreamWrite(pParser->pOut, g_szSpaces, cchIndent);
1048 ScmStreamWrite(pParser->pOut, &pchLine[offToken], cchLine - offToken);
1049 ScmStreamPutEol(pParser->pOut, pParser->enmEol);
1050
1051 /*
1052 * Check for line continuation and output concatenated lines.
1053 */
1054 scmKmkPassThruLineContinuationLines(pParser);
1055 return false; /* dummy */
1056}
1057
1058
1059static bool scmKmkHandleDefine(KMKPARSER *pParser, size_t offToken)
1060{
1061 /* Assignments takes us out of recipe mode. */
1062 pParser->fInRecipe = false;
1063
1064 return scmKmkHandleSimple(pParser, offToken);
1065}
1066
1067
1068static bool scmKmkHandleEndef(KMKPARSER *pParser, size_t offToken)
1069{
1070 /* Leaving a define resets the recipt mode. */
1071 pParser->fInRecipe = false;
1072
1073 return scmKmkHandleSimple(pParser, offToken);
1074}
1075
1076
1077typedef enum KMKASSIGNTYPE
1078{
1079 kKmkAssignType_Recursive,
1080 kKmkAssignType_Conditional,
1081 kKmkAssignType_Appending,
1082 kKmkAssignType_Prepending,
1083 kKmkAssignType_Simple,
1084 kKmkAssignType_Immediate
1085} KMKASSIGNTYPE;
1086
1087
1088/**
1089 * @returns dummy (false) to facility return + call.
1090 */
1091static bool scmKmkHandleAssignment2(KMKPARSER *pParser, size_t offVarStart, size_t offVarEnd, KMKASSIGNTYPE enmType,
1092 size_t offAssignOp, unsigned fFlags)
1093{
1094 unsigned const cchIndent = pParser->iActualDepth;
1095 const char *pchLine = pParser->pchLine;
1096 size_t cchLine = pParser->cchLine;
1097 uint32_t const cLines = pParser->cLines;
1098 uint32_t iSubLine = 0;
1099
1100 RT_NOREF(fFlags);
1101 Assert(offVarStart < cchLine);
1102 Assert(offVarEnd <= cchLine);
1103 Assert(offVarStart < offVarEnd);
1104 Assert(!RT_C_IS_SPACE(pchLine[offVarStart]));
1105 Assert(!RT_C_IS_SPACE(pchLine[offVarEnd - 1]));
1106
1107 /* Assignments takes us out of recipe mode. */
1108 pParser->fInRecipe = false;
1109
1110 /* This is too much hazzle to deal with. */
1111 if (cLines > 0 && pchLine[cchLine - 2] == '\\')
1112 return scmKmkGiveUp(pParser, "Escaped slashes at end of line not allowed. Insert space before line continuation slash!");
1113 if (cchLine + 64 > sizeof(pParser->szBuf))
1114 return scmKmkGiveUp(pParser, "Line too long!");
1115
1116 /*
1117 * Indent and output the variable name.
1118 */
1119 char *pszDst = pParser->szBuf;
1120 memset(pszDst, ' ', cchIndent);
1121 pszDst += cchIndent;
1122 pszDst = (char *)mempcpy(pszDst, &pchLine[offVarStart], offVarEnd - offVarStart);
1123
1124 /*
1125 * Try preserve the assignment operator position, but make sure we've got a
1126 * space in front of it.
1127 */
1128 if (offAssignOp < cchLine)
1129 {
1130 size_t offDst = (size_t)(pszDst - pParser->szBuf);
1131 size_t offEffAssignOp = ScmCalcSpacesForSrcSpan(pchLine, 0, offAssignOp, pParser->pSettings);
1132 if (offDst < offEffAssignOp)
1133 {
1134 size_t cchSpacesToWrite = offEffAssignOp - offDst;
1135 memset(pszDst, ' ', cchSpacesToWrite);
1136 pszDst += cchSpacesToWrite;
1137 }
1138 else
1139 *pszDst++ = ' ';
1140 }
1141 else
1142 {
1143 /* Pull up the assignment operator to the variable line. */
1144 *pszDst++ = ' ';
1145
1146 /* Eat up lines till we hit the operator. */
1147 while (offAssignOp < cchLine)
1148 {
1149 const char * const pchPrevLine = pchLine;
1150 Assert(iSubLine + 1 < cLines);
1151 pParser->pchLine = pchLine = ScmStreamGetLine(pParser->pIn, &pParser->cchLine, &pParser->enmEol);
1152 AssertReturn(pchLine, false /*dummy*/);
1153 cchLine = pParser->cchLine;
1154 iSubLine++;
1155 if (iSubLine + 1 < cLines && pchLine[cchLine - 2] == '\\')
1156 return scmKmkGiveUp(pParser, "Escaped slashes at end of line not allowed. Insert space before line continuation slash!");
1157
1158 /* Adjust offAssignOp: */
1159 offAssignOp -= (uintptr_t)pchLine - (uintptr_t)pchPrevLine;
1160 Assert(offAssignOp < ~(size_t)0 / 2);
1161 }
1162
1163 if ((size_t)(pszDst - pParser->szBuf) > sizeof(pParser->szBuf))
1164 return scmKmkGiveUp(pParser, "Line too long!");
1165 }
1166
1167 /*
1168 * Emit the operator.
1169 */
1170 size_t offLine = offAssignOp;
1171 switch (enmType)
1172 {
1173 default:
1174 AssertReleaseFailed();
1175 RT_FALL_THRU();
1176 case kKmkAssignType_Recursive:
1177 *pszDst++ = '=';
1178 Assert(pchLine[offLine] == '=');
1179 offLine++;
1180 break;
1181 case kKmkAssignType_Conditional:
1182 *pszDst++ = '?';
1183 *pszDst++ = '=';
1184 Assert(pchLine[offLine] == '?'); Assert(pchLine[offLine + 1] == '=');
1185 offLine += 2;
1186 break;
1187 case kKmkAssignType_Appending:
1188 *pszDst++ = '+';
1189 *pszDst++ = '=';
1190 Assert(pchLine[offLine] == '+'); Assert(pchLine[offLine + 1] == '=');
1191 offLine += 2;
1192 break;
1193 case kKmkAssignType_Prepending:
1194 *pszDst++ = '>';
1195 *pszDst++ = '=';
1196 Assert(pchLine[offLine] == '>'); Assert(pchLine[offLine + 1] == '=');
1197 offLine += 2;
1198 break;
1199 case kKmkAssignType_Immediate:
1200 *pszDst++ = ':';
1201 Assert(pchLine[offLine] == ':');
1202 offLine++;
1203 RT_FALL_THRU();
1204 case kKmkAssignType_Simple:
1205 *pszDst++ = ':';
1206 *pszDst++ = '=';
1207 Assert(pchLine[offLine] == ':'); Assert(pchLine[offLine + 1] == '=');
1208 offLine += 2;
1209 break;
1210 }
1211
1212 /*
1213 * Skip space till we hit the value or comment.
1214 */
1215 while (offLine < cchLine && RT_C_IS_SPACE(pchLine[offLine]))
1216 offLine++;
1217
1218/** @todo this block can probably be merged into the final loop below. */
1219 unsigned cPendingEols = 0;
1220 while (iSubLine + 1 < cLines && offLine + 1 == cchLine && pchLine[offLine] == '\\')
1221 {
1222 pParser->pchLine = pchLine = ScmStreamGetLine(pParser->pIn, &pParser->cchLine, &pParser->enmEol);
1223 AssertReturn(pchLine, false /*dummy*/);
1224 cchLine = pParser->cchLine;
1225 iSubLine++;
1226 if (iSubLine + 1 < cLines && pchLine[cchLine - 2] == '\\')
1227 {
1228 *pszDst++ = ' ';
1229 *pszDst++ = '\\';
1230 *pszDst = '\0';
1231 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
1232 return scmKmkGiveUp(pParser, "Escaped slashes at end of line not allowed. Insert space before line continuation slash!");
1233 }
1234 cPendingEols = 1;
1235
1236 /* Skip indent/whitespace. */
1237 offLine = 0;
1238 while (offLine < cchLine && RT_C_IS_SPACE(pchLine[offLine]))
1239 offLine++;
1240 }
1241
1242 /*
1243 * Okay, we've gotten to the value / comment part.
1244 */
1245 for (;;)
1246 {
1247 /*
1248 * The end? Flush what we've got.
1249 */
1250 if (offLine == cchLine)
1251 {
1252 Assert(iSubLine + 1 == cLines);
1253 *pszDst = '\0';
1254 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
1255 return false; /* dummy */
1256 }
1257
1258 /*
1259 * Output any non-comment stuff, stripping off newlines.
1260 */
1261 const char *pchHash = (const char *)memchr(&pchLine[offLine], '#', cchLine - offLine);
1262 if (pchHash != &pchLine[offLine])
1263 {
1264 /* Add space or flush pending EOLs. */
1265 if (!cPendingEols)
1266 *pszDst++ = ' ';
1267 else
1268 {
1269 cPendingEols = RT_MIN(2, cPendingEols); /* reduce to two, i.e. only one empty separator line */
1270 do
1271 {
1272 *pszDst++ = ' ';
1273 *pszDst++ = '\\';
1274 *pszDst = '\0';
1275 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
1276
1277 pszDst = pParser->szBuf;
1278 memset(pszDst, ' ', cchIndent);
1279 pszDst += cchIndent;
1280 *pszDst++ = '\t';
1281 cPendingEols--;
1282 } while (cPendingEols > 0);
1283 }
1284
1285 /* Strip backwards. */
1286 size_t const offValueEnd2 = pchHash ? (size_t)(pchHash - pchLine) : cchLine - (iSubLine + 1 < cLines);
1287 size_t offValueEnd = offValueEnd2;
1288 while (offValueEnd > offLine && RT_C_IS_BLANK(pchLine[offValueEnd - 1]))
1289 offValueEnd--;
1290 Assert(offValueEnd > offLine);
1291
1292 /* Append the value part we found. */
1293 pszDst = (char *)mempcpy(pszDst, &pchLine[offLine], offValueEnd - offLine);
1294 offLine = offValueEnd2;
1295 }
1296
1297 /*
1298 * If we found a comment hash, emit it and whatever follows just as-is w/o
1299 * any particular reformatting. Comments within a variable definition are
1300 * usually to disable portitions of a property like _DEFS or _SOURCES.
1301 */
1302 if (pchHash != NULL)
1303 {
1304 if (cPendingEols == 0)
1305 scmKmkTailComment(pParser, pchLine, cchLine, offLine, &pszDst);
1306 size_t const cchDst = (size_t)(pszDst - pParser->szBuf);
1307 *pszDst = '\0';
1308 ScmStreamPutLine(pParser->pOut, pParser->szBuf, cchDst, pParser->enmEol);
1309
1310 if (cPendingEols > 1)
1311 ScmStreamPutEol(pParser->pOut, pParser->enmEol);
1312
1313 if (cPendingEols > 0)
1314 ScmStreamPutLine(pParser->pOut, pchLine, cchLine, pParser->enmEol);
1315 scmKmkPassThruLineContinuationLines(pParser);
1316 return false; /* dummy */
1317 }
1318
1319 /*
1320 * Fetch another line, if we've got one.
1321 */
1322 if (iSubLine + 1 >= cLines)
1323 Assert(offLine == cchLine);
1324 else
1325 {
1326 Assert(offLine + 1 == cchLine);
1327 while (iSubLine + 1 < cLines && offLine + 1 == cchLine && pchLine[offLine] == '\\')
1328 {
1329 pParser->pchLine = pchLine = ScmStreamGetLine(pParser->pIn, &pParser->cchLine, &pParser->enmEol);
1330 AssertReturn(pchLine, false /*dummy*/);
1331 cchLine = pParser->cchLine;
1332 iSubLine++;
1333 if (iSubLine + 1 < cLines && pchLine[cchLine - 2] == '\\')
1334 {
1335 *pszDst++ = ' ';
1336 *pszDst++ = '\\';
1337 *pszDst = '\0';
1338 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol);
1339 if (cPendingEols > 1)
1340 ScmError(pParser->pState, VERR_NOT_SUPPORTED, "oops #1: Manually fix the next issue after reverting edits!");
1341 return scmKmkGiveUp(pParser, "Escaped slashes at end of line not allowed. Insert space before line continuation slash!");
1342 }
1343 cPendingEols++;
1344
1345 /* Deal with indent/whitespace. */
1346 offLine = 0;
1347 while (offLine < cchLine && RT_C_IS_SPACE(pchLine[offLine]))
1348 offLine++;
1349 }
1350 }
1351 }
1352}
1353
1354
1355/**
1356 * A rule.
1357 *
1358 * This is a bit involved. Sigh.
1359 *
1360 * @returns dummy (false) to facility return + call.
1361 */
1362static bool scmKmkHandleRule(KMKPARSER *pParser, size_t offFirstWord, bool fDoubleColon, size_t offColon)
1363{
1364 SCMSTREAM *pOut = pParser->pOut;
1365 unsigned const cchIndent = pParser->iActualDepth;
1366 const char *pchLine = pParser->pchLine;
1367 size_t cchLine = pParser->cchLine;
1368 Assert(offFirstWord < cchLine);
1369 uint32_t const cLines = pParser->cLines;
1370 uint32_t iSubLine = 0;
1371
1372 /* Following this, we'll be in recipe-mode. */
1373 pParser->fInRecipe = true;
1374
1375 /* This is too much hazzle to deal with. */
1376 if (cLines > 0 && pchLine[cchLine - 2] == '\\')
1377 return scmKmkGiveUp(pParser, "Escaped slashes at end of line not allowed. Insert space before line continuation slash!");
1378
1379 /* Too special case. */
1380 if (offColon <= offFirstWord)
1381 return scmKmkGiveUp(pParser, "Missing target file before colon!");
1382
1383 /*
1384 * Indent it.
1385 */
1386 ScmStreamWrite(pOut, g_szSpaces, cchIndent);
1387 size_t offLine = offFirstWord;
1388
1389 /*
1390 * Process word by word past the colon, taking new lines into account.
1391 *
1392 */
1393 KMKWORDCTX enmCtx = kKmkWordCtx_TargetFileOrAssignment;
1394 bool fPendingEol = false;
1395 for (;;)
1396 {
1397 /*
1398 * Output the next word.
1399 */
1400 size_t cchWord = scmKmkWordLength(pchLine, cchLine, offLine, enmCtx);
1401 Assert(offLine + cchWord <= offColon);
1402 ScmStreamWrite(pOut, &pchLine[offLine], cchWord);
1403 offLine += cchWord;
1404
1405 /* Skip whitespace (if any). */
1406 while (offLine < cchLine && RT_C_IS_SPACE(pchLine[offLine]))
1407 offLine++;
1408
1409 /* Have we reached the colon already? */
1410 if (offLine >= offColon)
1411 {
1412 Assert(pchLine[offLine] == ':');
1413 Assert(!fDoubleColon || pchLine[offLine + 1] == ':');
1414 offLine += fDoubleColon ? 2 : 1;
1415
1416 ScmStreamPutCh(pOut, ':');
1417 if (fDoubleColon)
1418 ScmStreamPutCh(pOut, ':');
1419 break;
1420 }
1421
1422 /* Deal with new line and emit indentation. */
1423 if (offLine + 1 == cchLine && pchLine[offLine] == '\\')
1424 {
1425 /* Get the next input line. */
1426 for (;;)
1427 {
1428 const char * const pchPrevLine = pchLine;
1429 Assert(iSubLine + 1 < cLines);
1430 pParser->pchLine = pchLine = ScmStreamGetLine(pParser->pIn, &pParser->cchLine, &pParser->enmEol);
1431 AssertReturn(pchLine, false /*dummy*/);
1432 cchLine = pParser->cchLine;
1433 iSubLine++;
1434 if (iSubLine + 1 < cLines && pchLine[cchLine - 2] == '\\')
1435 return scmKmkGiveUp(pParser, "Escaped slashes at end of line not allowed. Insert space before line continuation slash!");
1436
1437 /* Adjust offColon: */
1438 offColon -= (uintptr_t)pchLine - (uintptr_t)pchPrevLine;
1439 Assert(offColon < ~(size_t)0 / 2);
1440
1441 /* Skip leading spaces. */
1442 offLine = 0;
1443 while (offLine < cchLine && RT_C_IS_SPACE(pchLine[offLine]))
1444 offLine++;
1445
1446 /* Just drop empty lines. */
1447 if (offLine + 1 == cchLine && pchLine[offLine] == '\\')
1448 continue;
1449
1450 /* Complete the current line and emit indent, unless we reached the colon: */
1451 if (offLine >= offColon)
1452 {
1453 Assert(pchLine[offLine] == ':');
1454 Assert(!fDoubleColon || pchLine[offLine + 1] == ':');
1455 offLine += fDoubleColon ? 2 : 1;
1456
1457 ScmStreamPutCh(pOut, ':');
1458 if (fDoubleColon)
1459 ScmStreamPutCh(pOut, ':');
1460
1461 fPendingEol = true;
1462 break;
1463 }
1464 ScmStreamWrite(pOut, RT_STR_TUPLE(" \\"));
1465 ScmStreamPutEol(pOut, pParser->enmEol);
1466 ScmStreamWrite(pOut, g_szSpaces, cchIndent);
1467 }
1468 if (offLine >= offColon)
1469 break;
1470 }
1471 else
1472 ScmStreamPutCh(pOut, ' ');
1473 enmCtx = kKmkWordCtx_TargetFile;
1474 }
1475
1476 /*
1477 * We're immediately past the colon now, so eat whitespace and newlines and
1478 * whatever till we get to a solid word.
1479 */
1480 /* Skip spaces - there should be exactly one. */
1481 while (offLine < cchLine && RT_C_IS_SPACE(pchLine[offLine]))
1482 offLine++;
1483
1484 /* Deal with new lines: */
1485 while (offLine + 1 == cchLine && pchLine[offLine] == '\\')
1486 {
1487 fPendingEol = true;
1488
1489 Assert(iSubLine + 1 < cLines);
1490 pParser->pchLine = pchLine = ScmStreamGetLine(pParser->pIn, &pParser->cchLine, &pParser->enmEol);
1491 AssertReturn(pchLine, false /*dummy*/);
1492 cchLine = pParser->cchLine;
1493 iSubLine++;
1494 if (iSubLine + 1 < cLines && pchLine[cchLine - 2] == '\\')
1495 return scmKmkGiveUp(pParser, "Escaped slashes at end of line not allowed. Insert space before line continuation slash!");
1496
1497 /* Skip leading spaces. */
1498 offLine = 0;
1499 while (offLine < cchLine && RT_C_IS_SPACE(pchLine[offLine]))
1500 offLine++;
1501
1502 /* Just drop empty lines. */
1503 if (offLine + 1 == cchLine && pchLine[offLine] == '\\')
1504 continue;
1505 }
1506
1507 /*
1508 * Special case: No dependencies.
1509 */
1510 if (offLine == cchLine && iSubLine >= cLines)
1511 {
1512 ScmStreamPutEol(pOut, pParser->enmEol);
1513 return false /*dummy*/;
1514 }
1515
1516 /*
1517 * Work the dependencies word for word. Indent in spaces + two tabs.
1518 * (Pattern rules will also end up here, but we'll just ignore that for now.)
1519 */
1520 enmCtx = kKmkWordCtx_DepFileOrAssignment;
1521 for (;;)
1522 {
1523 /* Indent the next word. */
1524 if (!fPendingEol)
1525 ScmStreamPutCh(pOut, ' ');
1526 else
1527 {
1528 ScmStreamWrite(pOut, RT_STR_TUPLE(" \\"));
1529 ScmStreamPutEol(pOut, pParser->enmEol);
1530 ScmStreamWrite(pOut, g_szSpaces, cchIndent);
1531 ScmStreamWrite(pOut, RT_STR_TUPLE("\t\t"));
1532 fPendingEol = false;
1533 }
1534
1535 /* Get the next word and output it. */
1536 size_t cchWord = scmKmkWordLength(pchLine, cchLine, offLine, enmCtx);
1537 Assert(offLine + cchWord <= cchLine);
1538
1539 ScmStreamWrite(pOut, &pchLine[offLine], cchWord);
1540 offLine += cchWord;
1541
1542 /* Skip whitespace (if any). */
1543 size_t cchSpaces = 0;
1544 while (offLine < cchLine && RT_C_IS_SPACE(pchLine[offLine]))
1545 {
1546 cchSpaces++;
1547 offLine++;
1548 }
1549
1550 /* Deal with new line and emit indentation. */
1551 if (iSubLine + 1 < cLines && offLine + 1 == cchLine && pchLine[offLine] == '\\')
1552 {
1553 /* Get the next input line. */
1554 unsigned cEmptyLines = 0;
1555 for (;;)
1556 {
1557 Assert(iSubLine + 1 < cLines);
1558 pParser->pchLine = pchLine = ScmStreamGetLine(pParser->pIn, &pParser->cchLine, &pParser->enmEol);
1559 AssertReturn(pchLine, false /*dummy*/);
1560 cchLine = pParser->cchLine;
1561 iSubLine++;
1562 if (iSubLine + 1 < cLines && pchLine[cchLine - 2] == '\\')
1563 return scmKmkGiveUp(pParser, "Escaped slashes at end of line not allowed. Insert space before line continuation slash!");
1564
1565 /* Skip leading spaces. */
1566 offLine = 0;
1567 while (offLine < cchLine && RT_C_IS_SPACE(pchLine[offLine]))
1568 offLine++;
1569
1570 /* Just drop empty lines, we'll re-add one of them afterward if we find more dependencies. */
1571 if (offLine + 1 == cchLine && pchLine[offLine] == '\\')
1572 {
1573 cEmptyLines++;
1574 continue;
1575 }
1576
1577 fPendingEol = true;
1578 break;
1579 }
1580 }
1581
1582 if (offLine >= cchLine)
1583 {
1584 /* End of input. */
1585/** @todo deal with comments */
1586 Assert(iSubLine + 1 == cLines);
1587 ScmStreamPutEol(pOut, pParser->enmEol);
1588 return false; /* dummmy */
1589 }
1590 enmCtx = kKmkWordCtx_DepFile;
1591 }
1592}
1593
1594
1595/**
1596 * Checks if the (extended) line is a variable assignment.
1597 *
1598 * We scan past line continuation stuff here as the assignment operator could be
1599 * on the next line, even if that's very unlikely it is recommened by the coding
1600 * guide lines if the line needs to be split. Fortunately, though, the caller
1601 * already removes empty empty leading lines, so we only have to consider the
1602 * line continuation issue if no '=' was found on the first line.
1603 *
1604 * @returns Modified or not.
1605 * @param pParser The parser.
1606 * @param cLines Number of lines to consider.
1607 * @param cchTotalLine Total length of all the lines to consider.
1608 * @param offWord Where the first word of the line starts.
1609 * @param pfIsAssignment Where to return whether this is an assignment or
1610 * not.
1611 */
1612static bool scmKmkHandleAssignmentOrRule(KMKPARSER *pParser, size_t offWord)
1613{
1614 const char *pchLine = pParser->pchLine;
1615 size_t const cchTotalLine = pParser->cchTotalLine;
1616
1617 /*
1618 * Scan words till we find ':' or '='.
1619 */
1620 uint32_t iWord = 0;
1621 size_t offCurWord = offWord;
1622 size_t offEndPrev = 0;
1623 size_t offLine = offWord;
1624 while (offLine < cchTotalLine)
1625 {
1626 char ch = pchLine[offLine++];
1627 if (ch == '$')
1628 {
1629 /*
1630 * Skip variable expansion.
1631 */
1632 char const chOpen = pchLine[offLine++];
1633 if (chOpen == '(' || chOpen == '{')
1634 {
1635 char const chClose = chOpen == '(' ? ')' : '}';
1636 unsigned cDepth = 1;
1637 while (offLine < cchTotalLine)
1638 {
1639 ch = pchLine[offLine++];
1640 if (ch == chOpen)
1641 cDepth++;
1642 else if (ch == chClose)
1643 if (!--cDepth)
1644 break;
1645 }
1646 }
1647 /* else: $x or $$, so just skip the next character. */
1648 }
1649 else if (RT_C_IS_SPACE(ch))
1650 {
1651 /*
1652 * End of word. Skip whitespace till the next word starts.
1653 */
1654 offEndPrev = offLine - 1;
1655 Assert(offLine != offWord);
1656 while (offLine < cchTotalLine)
1657 {
1658 ch = pchLine[offLine];
1659 if (RT_C_IS_SPACE(ch))
1660 offLine++;
1661 else if (ch == '\\' && (pchLine[offLine] == '\r' || pchLine[offLine] == '\n'))
1662 offLine += 2;
1663 else
1664 break;
1665 }
1666 offCurWord = offLine;
1667 iWord++;
1668
1669 /*
1670 * To simplify the assignment operator checks, we just check the
1671 * start of the 2nd word when we're here.
1672 */
1673 if (iWord == 1 && offLine < cchTotalLine)
1674 {
1675 ch = pchLine[offLine];
1676 if (ch == '=')
1677 return scmKmkHandleAssignment2(pParser, offWord, offEndPrev, kKmkAssignType_Recursive, offLine, 0);
1678 if (offLine + 1 < cchTotalLine && pchLine[offLine + 1] == '=')
1679 {
1680 if (ch == ':')
1681 return scmKmkHandleAssignment2(pParser, offWord, offEndPrev, kKmkAssignType_Simple, offLine, 0);
1682 if (ch == '+')
1683 return scmKmkHandleAssignment2(pParser, offWord, offEndPrev, kKmkAssignType_Appending, offLine, 0);
1684 if (ch == '>')
1685 return scmKmkHandleAssignment2(pParser, offWord, offEndPrev, kKmkAssignType_Prepending, offLine, 0);
1686 if (ch == '?')
1687 return scmKmkHandleAssignment2(pParser, offWord, offEndPrev, kKmkAssignType_Conditional, offLine, 0);
1688 }
1689 else if ( ch == ':'
1690 && pchLine[offLine + 1] == ':'
1691 && pchLine[offLine + 2] == '=')
1692 return scmKmkHandleAssignment2(pParser, offWord, offEndPrev, kKmkAssignType_Immediate, offLine, 0);
1693
1694 /* Check for rule while we're here. */
1695 if (ch == ':')
1696 return scmKmkHandleRule(pParser, offWord, pchLine[offLine + 1] == ':', offLine);
1697 }
1698 }
1699 /*
1700 * If '=' is found in the first word it's an assignment.
1701 */
1702 else if (ch == '=')
1703 {
1704 if (iWord == 0)
1705 {
1706 KMKASSIGNTYPE enmType = kKmkAssignType_Recursive;
1707 ch = pchLine[offLine - 2];
1708 if (ch == '+')
1709 enmType = kKmkAssignType_Appending;
1710 else if (ch == '?')
1711 enmType = kKmkAssignType_Conditional;
1712 else if (ch == '>')
1713 enmType = kKmkAssignType_Prepending;
1714 else
1715 Assert(ch != ':');
1716 return scmKmkHandleAssignment2(pParser, offWord, offLine - 1, enmType, offLine - 1, 0);
1717 }
1718 }
1719 /*
1720 * When ':' is found it can mean a drive letter, a rule or in the
1721 * first word a simple or immediate assignment.
1722 */
1723 else if (ch == ':')
1724 {
1725 /* Check for drive letters (we ignore the archive form): */
1726 if (offLine - offWord == 2 && RT_C_IS_ALPHA(pchLine[offLine - 2]))
1727 { /* ignore */ }
1728 else
1729 {
1730 /* Simple or immediate assignment? */
1731 ch = pchLine[offLine];
1732 if (iWord == 0)
1733 {
1734 if (ch == '=')
1735 return scmKmkHandleAssignment2(pParser, offWord, offLine - 1, kKmkAssignType_Simple, offLine - 1, 0);
1736 if (ch == ':' && pchLine[offLine + 1] == '=')
1737 return scmKmkHandleAssignment2(pParser, offWord, offLine - 1, kKmkAssignType_Immediate, offLine - 1, 0);
1738 }
1739
1740 /* Okay, it's a rule then. */
1741 return scmKmkHandleRule(pParser, offWord, ch == ':', offLine - 1);
1742 }
1743 }
1744 }
1745
1746 /*
1747 * If we didn't find anything, output it as-as.
1748 * We use scmKmkHandleSimple in a special way to do this.
1749 */
1750 ScmVerbose(pParser->pState, 1, "debug: %u: Unable to make sense of this line!", pParser->iLine);
1751 return scmKmkHandleSimple(pParser, 0 /*offToken*/, false /*fIndentIt*/);
1752}
1753
1754
1755static bool scmKmkHandleAssignKeyword(KMKPARSER *pParser, size_t offToken, KMKTOKEN enmToken, size_t cchWord,
1756 bool fMustBeAssignment)
1757{
1758 /* Assignments takes us out of recipe mode. */
1759 pParser->fInRecipe = false;
1760
1761 RT_NOREF(pParser, offToken, enmToken, cchWord, fMustBeAssignment);
1762 return scmKmkHandleSimple(pParser, offToken);
1763}
1764
1765
1766/**
1767 * Rewrite a kBuild makefile.
1768 *
1769 * @returns kScmMaybeModified or kScmUnmodified.
1770 * @param pIn The input stream.
1771 * @param pOut The output stream.
1772 * @param pSettings The settings.
1773 *
1774 * @todo
1775 *
1776 * Ideas for Makefile.kmk and Config.kmk:
1777 * - sort if1of/ifn1of sets.
1778 * - line continuation slashes should only be preceded by one space.
1779 */
1780SCMREWRITERRES rewrite_Makefile_kmk(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
1781{
1782 if (!pSettings->fStandarizeKmk)
1783 return kScmUnmodified;
1784
1785 /*
1786 * Parser state.
1787 */
1788 KMKPARSER Parser;
1789 Parser.iDepth = 0;
1790 Parser.iActualDepth = 0;
1791 Parser.fInRecipe = false;
1792 Parser.iLine = 0;
1793 Parser.pState = pState;
1794 Parser.pIn = pIn;
1795 Parser.pOut = pOut;
1796 Parser.pSettings = pSettings;
1797
1798 /*
1799 * Iterate the file.
1800 */
1801 const char *pchLine;
1802 while ((Parser.pchLine = pchLine = ScmStreamGetLine(pIn, &Parser.cchLine, &Parser.enmEol)) != NULL)
1803 {
1804 size_t cchLine = Parser.cchLine;
1805 Parser.iLine++;
1806
1807 /*
1808 * If we're in the command part of a recipe, anything starting with a
1809 * tab is considered another command for the recipe.
1810 */
1811 if (Parser.fInRecipe && *pchLine == '\t')
1812 {
1813 /* Do we do anything here? */
1814 }
1815 else
1816 {
1817 /*
1818 * Skip leading whitespace and check for directives (simplified).
1819 *
1820 * This is simplified in the sense that GNU make first checks for variable
1821 * assignments, so that directive can be used as variable names. We don't
1822 * want that, so we do the variable assignment check later.
1823 */
1824 size_t offLine = 0;
1825 while (offLine < cchLine && RT_C_IS_BLANK(pchLine[offLine]))
1826 offLine++;
1827
1828 /* Find end of word (if any): */
1829 size_t cchWord = 0;
1830 while ( offLine + cchWord < cchLine
1831 && ( RT_C_IS_ALNUM(pchLine[offLine + cchWord])
1832 || pchLine[offLine + cchWord] == '-'))
1833 cchWord++;
1834 if (cchWord > 0)
1835 {
1836 /* If the line is just a line continuation slash, simply remove it
1837 (this also makes the parsing a lot easier). */
1838 if (cchWord == 1 && offLine == cchLine - 1 && pchLine[cchLine] == '\\')
1839 continue;
1840
1841 /* Unlike the GNU make parser, we won't recognize 'if' or any other
1842 directives as variable names, so we can */
1843 KMKTOKEN enmToken = scmKmkIdentifyToken(&pchLine[offLine], cchWord);
1844 switch (enmToken)
1845 {
1846 case kKmkToken_ifeq:
1847 case kKmkToken_ifneq:
1848 case kKmkToken_if1of:
1849 case kKmkToken_ifn1of:
1850 scmKmkHandleIfParentheses(&Parser, offLine, enmToken, cchWord, false /*fElse*/);
1851 continue;
1852
1853 case kKmkToken_ifdef:
1854 case kKmkToken_ifndef:
1855 case kKmkToken_if:
1856 scmKmkHandleIfSpace(&Parser, offLine, enmToken, cchWord, false /*fElse*/);
1857 continue;
1858
1859 case kKmkToken_else:
1860 scmKmkHandleElse(&Parser, offLine);
1861 continue;
1862
1863 case kKmkToken_endif:
1864 scmKmkHandleEndif(&Parser, offLine);
1865 continue;
1866
1867 /* Includes: */
1868 case kKmkToken_include:
1869 case kKmkToken_sinclude:
1870 case kKmkToken_dash_include:
1871 case kKmkToken_includedep:
1872 case kKmkToken_includedep_queue:
1873 case kKmkToken_includedep_flush:
1874 scmKmkHandleSimple(&Parser, offLine);
1875 continue;
1876
1877 /* Others: */
1878 case kKmkToken_define:
1879 scmKmkHandleDefine(&Parser, offLine);
1880 continue;
1881 case kKmkToken_endef:
1882 scmKmkHandleEndef(&Parser, offLine);
1883 continue;
1884
1885 case kKmkToken_override:
1886 case kKmkToken_local:
1887 scmKmkHandleAssignKeyword(&Parser, offLine, enmToken, cchWord, true /*fMustBeAssignment*/);
1888 continue;
1889
1890 case kKmkToken_export:
1891 scmKmkHandleAssignKeyword(&Parser, offLine, enmToken, cchWord, false /*fMustBeAssignment*/);
1892 continue;
1893
1894 case kKmkToken_unexport:
1895 case kKmkToken_undefine:
1896 scmKmkHandleSimple(&Parser, offLine);
1897 break;
1898
1899 case kKmkToken_Comment:
1900 break;
1901
1902 /*
1903 * Check if it's perhaps an variable assignment or start of a rule.
1904 * We'll do this in a very simple fashion.
1905 */
1906 case kKmkToken_Word:
1907 {
1908 Parser.cLines = 1;
1909 Parser.cchTotalLine = cchLine;
1910 if (scmKmkIsLineWithContinuation(pchLine, cchLine))
1911 Parser.cchTotalLine = scmKmkLineContinuationPeek(&Parser, &Parser.cLines, NULL);
1912 scmKmkHandleAssignmentOrRule(&Parser, offLine);
1913 continue;
1914 }
1915 }
1916 }
1917 }
1918
1919 /*
1920 * Pass it thru as-is with line continuation.
1921 */
1922 while (scmKmkIsLineWithContinuation(pchLine, cchLine))
1923 {
1924 ScmStreamPutLine(pOut, pchLine, cchLine, Parser.enmEol);
1925 Parser.pchLine = pchLine = ScmStreamGetLine(pIn, &Parser.cchLine, &Parser.enmEol);
1926 if (!pchLine)
1927 break;
1928 cchLine = Parser.cchLine;
1929 }
1930 if (pchLine)
1931 ScmStreamPutLine(pOut, pchLine, cchLine, Parser.enmEol);
1932 }
1933
1934 return kScmMaybeModified; /* Make the caller check */
1935}
1936
1937
1938/**
1939 * Makefile.kup are empty files, enforce this.
1940 *
1941 * @returns true if modifications were made, false if not.
1942 * @param pIn The input stream.
1943 * @param pOut The output stream.
1944 * @param pSettings The settings.
1945 */
1946SCMREWRITERRES rewrite_Makefile_kup(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
1947{
1948 RT_NOREF2(pOut, pSettings);
1949
1950 /* These files should be zero bytes. */
1951 if (pIn->cb == 0)
1952 return kScmUnmodified;
1953 ScmVerbose(pState, 2, " * Truncated file to zero bytes\n");
1954 return kScmModified;
1955}
1956
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