Changeset 98319 in vbox
- Timestamp:
- Jan 26, 2023 3:31:55 PM (2 years ago)
- svn:sync-xref-src-repo-rev:
- 155538
- Location:
- trunk/src/bldprogs
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/bldprogs/scm.cpp
r98113 r98319 105 105 SCMOPT_NO_RC_USE, 106 106 SCMOPT_UNRESTRICTED_RC_USE, 107 SCMOPT_STANDARIZE_KMK, 108 SCMOPT_NO_STANDARIZE_KMK, 107 109 SCMOPT_UPDATE_COPYRIGHT_YEAR, 108 110 SCMOPT_NO_UPDATE_COPYRIGHT_YEAR, … … 215 217 /* .fNoASMMemPageUse = */ false, 216 218 /* .fOnlyHrcVrcInsteadOfRc */ false, 219 /* .fStandarizeKmk */ false, 217 220 /* .fUpdateCopyrightYear = */ false, 218 221 /* .fExternalCopyright = */ false, … … 274 277 { "--no-rc-use", SCMOPT_NO_RC_USE, RTGETOPT_REQ_NOTHING }, 275 278 { "--unrestricted-rc-use", SCMOPT_UNRESTRICTED_RC_USE, RTGETOPT_REQ_NOTHING }, 279 { "--standarize-kmk", SCMOPT_STANDARIZE_KMK, RTGETOPT_REQ_NOTHING }, 280 { "--no-standarize-kmk", SCMOPT_NO_STANDARIZE_KMK, RTGETOPT_REQ_NOTHING }, 276 281 { "--update-copyright-year", SCMOPT_UPDATE_COPYRIGHT_YEAR, RTGETOPT_REQ_NOTHING }, 277 282 { "--no-update-copyright-year", SCMOPT_NO_UPDATE_COPYRIGHT_YEAR, RTGETOPT_REQ_NOTHING }, … … 1226 1231 return VINF_SUCCESS; 1227 1232 1233 case SCMOPT_STANDARIZE_KMK: 1234 pSettings->fStandarizeKmk = true; 1235 return VINF_SUCCESS; 1236 case SCMOPT_NO_STANDARIZE_KMK: 1237 pSettings->fStandarizeKmk = false; 1238 return VINF_SUCCESS; 1239 1228 1240 case SCMOPT_UPDATE_COPYRIGHT_YEAR: 1229 1241 pSettings->fUpdateCopyrightYear = true; … … 2222 2234 bool ScmFixManually(PSCMRWSTATE pState, const char *pszFormat, ...) 2223 2235 { 2236 va_list va; 2237 va_start(va, pszFormat); 2238 ScmFixManuallyV(pState, pszFormat, va); 2239 va_end(va); 2240 return false; 2241 } 2242 2243 2244 /** 2245 * Prints message indicating that something requires manual fixing. 2246 * 2247 * @returns false 2248 * @param pState The rewrite state. Optional. 2249 * @param rc The error code. 2250 * @param pszFormat The message format string. 2251 * @param va Format arguments. 2252 */ 2253 bool ScmFixManuallyV(PSCMRWSTATE pState, const char *pszFormat, va_list va) 2254 { 2224 2255 pState->fNeedsManualRepair = true; 2225 2256 … … 2229 2260 pState->fFirst = true; 2230 2261 } 2231 va_list va ;2232 va_ start(va, pszFormat);2233 RTPrintf("%s: error/fixme: %s: %N", g_szProgName, pState->pszFilename, pszFormat, &va );2234 va_end(va );2262 va_list vaCopy; 2263 va_copy(vaCopy, va); 2264 RTPrintf("%s: error/fixme: %s: %N", g_szProgName, pState->pszFilename, pszFormat, &vaCopy); 2265 va_end(vaCopy); 2235 2266 2236 2267 return false; … … 2961 2992 g_Defaults.fOnlyHrcVrcInsteadOfRc); 2962 2993 break; 2994 case SCMOPT_STANDARIZE_KMK: 2995 RTPrintf(" Clean up kmk files (the makefile-kmk action). Default: %RTbool\n", g_Defaults.fStandarizeKmk); 2996 break; 2963 2997 case SCMOPT_UPDATE_COPYRIGHT_YEAR: 2964 2998 RTPrintf(" Update the copyright year. Default: %RTbool\n", g_Defaults.fUpdateCopyrightYear); -
trunk/src/bldprogs/scm.h
r98103 r98319 377 377 /** No rc declarations allowed, only hrc or vrc depending on the result type. */ 378 378 bool fOnlyHrcVrcInsteadOfRc; 379 380 /** Whether to standarize kmk makefiles. */ 381 bool fStandarizeKmk; 379 382 380 383 /** Update the copyright year. */ … … 479 482 bool ScmError(PSCMRWSTATE pState, int rc, const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(3, 4); 480 483 bool ScmFixManually(PSCMRWSTATE pState, const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(2, 3); 484 bool ScmFixManuallyV(PSCMRWSTATE pState, const char *pszFormat, va_list va) RT_IPRT_FORMAT_ATTR(2, 0); 481 485 482 486 extern const char g_szTabSpaces[16+1]; -
trunk/src/bldprogs/scmrw.cpp
r98103 r98319 1211 1211 return false; 1212 1212 } 1213 1214 1215 1216 /********************************************************************************************************************************* 1217 * Copyright & License * 1218 *********************************************************************************************************************************/ 1213 1219 1214 1220 /** … … 2154 2160 2155 2161 2162 2163 /********************************************************************************************************************************* 2164 * kBuild Makefiles * 2165 *********************************************************************************************************************************/ 2166 2156 2167 /** 2157 2168 * Makefile.kup are empty files, enforce this. … … 2173 2184 } 2174 2185 2186 typedef enum KMKTOKEN 2187 { 2188 kKmkToken_Word = 0, 2189 2190 /* Conditionals: */ 2191 kKmkToken_ifeq, 2192 kKmkToken_ifneq, 2193 kKmkToken_if1of, 2194 kKmkToken_ifn1of, 2195 kKmkToken_ifdef, 2196 kKmkToken_ifndef, 2197 kKmkToken_if, 2198 kKmkToken_else, 2199 kKmkToken_endif, 2200 2201 /* Includes: */ 2202 kKmkToken_include, 2203 kKmkToken_sinclude, 2204 kKmkToken_dash_include, 2205 kKmkToken_includedep, 2206 kKmkToken_includedep_queue, 2207 kKmkToken_includedep_flush, 2208 2209 /* Others: */ 2210 kKmkToken_define, 2211 kKmkToken_endef, 2212 kKmkToken_export, 2213 kKmkToken_unexport, 2214 kKmkToken_local, 2215 kKmkToken_override 2216 } KMKTOKEN; 2217 2218 typedef struct KMKPARSER 2219 { 2220 struct 2221 { 2222 KMKTOKEN enmToken; 2223 uint32_t iLine; 2224 bool fIgnoreNesting; 2225 } aDepth[64]; 2226 unsigned iDepth; 2227 unsigned iActualDepth; 2228 bool fInReceipt; 2229 2230 /** The current line number (for error messages and peeking). */ 2231 uint32_t iLine; 2232 /** The EOL type of the current line. */ 2233 SCMEOL enmEol; 2234 /** The length of the current line. */ 2235 size_t cchLine; 2236 /** Pointer to the start of the current line. */ 2237 char const *pchLine; 2238 2239 /** The SCM rewriter state. */ 2240 PSCMRWSTATE pState; 2241 /** The input stream. */ 2242 PSCMSTREAM pIn; 2243 /** The output stream. */ 2244 PSCMSTREAM pOut; 2245 /** The settings. */ 2246 PCSCMSETTINGSBASE pSettings; 2247 /** Scratch buffer. */ 2248 char szBuf[4096]; 2249 } KMKPARSER; 2250 2251 static KMKTOKEN scmKmkIdentifyToken(const char *pchWord, size_t cchWord) 2252 { 2253 static struct { const char *psz; uint32_t cch; KMKTOKEN enmToken; } s_aTokens[] = 2254 { 2255 { RT_STR_TUPLE("if"), kKmkToken_if }, 2256 { RT_STR_TUPLE("ifeq"), kKmkToken_ifeq }, 2257 { RT_STR_TUPLE("ifneq"), kKmkToken_ifneq }, 2258 { RT_STR_TUPLE("if1of"), kKmkToken_if1of }, 2259 { RT_STR_TUPLE("ifn1of"), kKmkToken_ifn1of }, 2260 { RT_STR_TUPLE("ifdef"), kKmkToken_ifdef }, 2261 { RT_STR_TUPLE("ifndef"), kKmkToken_ifndef }, 2262 { RT_STR_TUPLE("else"), kKmkToken_else }, 2263 { RT_STR_TUPLE("endif"), kKmkToken_endif }, 2264 { RT_STR_TUPLE("include"), kKmkToken_include }, 2265 { RT_STR_TUPLE("sinclude"), kKmkToken_sinclude }, 2266 { RT_STR_TUPLE("-include"), kKmkToken_dash_include }, 2267 { RT_STR_TUPLE("includedep"), kKmkToken_includedep }, 2268 { RT_STR_TUPLE("includedep-queue"), kKmkToken_includedep_queue }, 2269 { RT_STR_TUPLE("includedep-flush"), kKmkToken_includedep_flush }, 2270 { RT_STR_TUPLE("define"), kKmkToken_define }, 2271 { RT_STR_TUPLE("endef"), kKmkToken_endef }, 2272 { RT_STR_TUPLE("export"), kKmkToken_export }, 2273 { RT_STR_TUPLE("unexport"), kKmkToken_unexport }, 2274 { RT_STR_TUPLE("local"), kKmkToken_local }, 2275 { RT_STR_TUPLE("override"), kKmkToken_override }, 2276 }; 2277 char chFirst = *pchWord; 2278 if ( chFirst == 'i' 2279 || chFirst == 'e' 2280 || chFirst == 'd' 2281 || chFirst == 's' 2282 || chFirst == '-' 2283 || chFirst == 'u' 2284 || chFirst == 'l' 2285 || chFirst == 'o') 2286 { 2287 for (size_t i = 0; i < RT_ELEMENTS(s_aTokens); i++) 2288 if ( s_aTokens[i].cch == cchWord 2289 && *s_aTokens[i].psz == chFirst 2290 && memcmp(s_aTokens[i].psz, pchWord, cchWord) == 0) 2291 return s_aTokens[i].enmToken; 2292 } 2293 #ifdef VBOX_STRICT 2294 else 2295 for (size_t i = 0; i < RT_ELEMENTS(s_aTokens); i++) 2296 Assert(chFirst != *s_aTokens[i].psz); 2297 #endif 2298 2299 return kKmkToken_Word; 2300 } 2301 2302 2303 /** 2304 * Gives up on the current line, copying it as it and requesting manual repair. 2305 */ 2306 static bool scmKmkGiveUp(KMKPARSER *pParser, const char *pszFormat, ...) 2307 { 2308 va_list va; 2309 va_start(va, pszFormat); 2310 ScmFixManually(pParser->pState, "%u: %N\n", pParser->iLine, pszFormat, &va); 2311 va_end(va); 2312 2313 ScmStreamPutLine(pParser->pOut, pParser->pchLine, pParser->cchLine, pParser->enmEol); 2314 return false; 2315 } 2316 2317 2318 static bool scmKmkIsLineWithContinuationSlow(const char *pchLine, size_t cchLine) 2319 { 2320 size_t cchSlashes = 1; 2321 cchLine--; 2322 while (cchSlashes < cchLine && pchLine[cchLine - cchSlashes - 1] == '\\') 2323 cchSlashes++; 2324 return RT_BOOL(cchSlashes & 1); 2325 } 2326 2327 2328 DECLINLINE(bool) scmKmkIsLineWithContinuation(const char *pchLine, size_t cchLine) 2329 { 2330 if (cchLine == 0 || pchLine[cchLine - 1] != '\\') 2331 return false; 2332 return scmKmkIsLineWithContinuationSlow(pchLine, cchLine); 2333 } 2334 2335 2336 /** 2337 * Finds the length of a line where line continuation is in play. 2338 * 2339 * @returns Length from start of current line to the final unescaped EOL. 2340 * @param pParser The KMK parser state. 2341 * @param pcLine Where to return the number of lines. Optional. 2342 * @param pcchMaxLeadWord Where to return the max lead word length on 2343 * subsequent lines. Used to help balance multi-line 2344 * 'if' statements (imperfect). Optional. 2345 */ 2346 static size_t scmKmkLineContinuationPeek(KMKPARSER *pParser, uint32_t *pcLines, size_t *pcchMaxLeadWord) 2347 { 2348 size_t const offSaved = ScmStreamTell(pParser->pIn); 2349 uint32_t cLines = 1; 2350 size_t cchMaxLeadWord = 0; 2351 const char *pchLine = pParser->pchLine; 2352 size_t cchLine = pParser->cchLine; 2353 SCMEOL enmEol; 2354 for (;;) 2355 { 2356 /* Return if no line continuation (or end of stream): */ 2357 if ( cchLine == 0 2358 || !scmKmkIsLineWithContinuation(pchLine, cchLine) 2359 || ScmStreamIsEndOfStream(pParser->pIn)) 2360 { 2361 ScmStreamSeekAbsolute(pParser->pIn, offSaved); 2362 if (pcLines) 2363 *pcLines = cLines; 2364 if (pcchMaxLeadWord) 2365 *pcchMaxLeadWord = cchMaxLeadWord; 2366 return (size_t)(pchLine - pParser->pchLine) + cchLine; 2367 } 2368 2369 /* Get the next line: */ 2370 pchLine = ScmStreamGetLine(pParser->pIn, &cchLine, &enmEol); 2371 cLines++; 2372 2373 /* Check the length of the first word if requested: */ 2374 if (pcchMaxLeadWord) 2375 { 2376 size_t offLine = 0; 2377 while (offLine < cchLine && RT_C_IS_BLANK(pchLine[offLine])) 2378 offLine++; 2379 2380 size_t const offStartWord = offLine; 2381 while (offLine < cchLine && !RT_C_IS_BLANK(pchLine[offLine])) 2382 offLine++; 2383 2384 if (offLine - offStartWord > cchMaxLeadWord) 2385 cchMaxLeadWord = offLine - offStartWord; 2386 } 2387 } 2388 } 2389 2390 2391 static bool scmKmkPushNesting(KMKPARSER *pParser, KMKTOKEN enmToken) 2392 { 2393 uint32_t iDepth = pParser->iDepth; 2394 if (iDepth + 1 >= RT_ELEMENTS(pParser->aDepth)) 2395 return ScmError(pParser->pState, VERR_ASN1_TOO_DEEPLY_NESTED /*?*/, "%u: Too deep if/define nesting!\n", pParser->iLine); 2396 2397 pParser->aDepth[iDepth].enmToken = enmToken; 2398 pParser->aDepth[iDepth].iLine = pParser->iLine; 2399 pParser->aDepth[iDepth].fIgnoreNesting = false; 2400 pParser->iDepth = iDepth + 1; 2401 pParser->iActualDepth += 1; 2402 return true; 2403 } 2404 2405 2406 /** 2407 * Skips a string stopping at @a chStop1 or @a chStop2, taking $() and ${} into 2408 * account. 2409 */ 2410 static size_t scmKmkSkipExpString(const char *pchLine, size_t cchLine, size_t off, char chStop1, char chStop2 = '\0') 2411 { 2412 unsigned iExpDepth = 0; 2413 char ch; 2414 while ( off < cchLine 2415 && (ch = pchLine[off]) 2416 && ( (ch != chStop1 && ch != chStop2) 2417 || iExpDepth > 0)) 2418 { 2419 off++; 2420 if (ch == '$') 2421 { 2422 ch = pchLine[off]; 2423 if (ch == '(' || ch == '{') 2424 { 2425 iExpDepth++; 2426 off++; 2427 } 2428 } 2429 else if ((ch == ')' || ch == '}') && iExpDepth > 0) 2430 iExpDepth--; 2431 } 2432 return off; 2433 } 2434 2435 2436 static bool scmKmkTailComment(KMKPARSER *pParser, const char *pchLine, size_t cchLine, size_t offSrc, char **ppszDst) 2437 { 2438 size_t const offSrcStart = offSrc; 2439 2440 /* Skip blanks. */ 2441 while (offSrc < cchLine && RT_C_IS_SPACE(pchLine[offSrc])) 2442 offSrc++; 2443 if (offSrc >= cchLine) 2444 return true; 2445 2446 /* Is it a comment? */ 2447 char *pszDst = *ppszDst; 2448 if (pchLine[offSrc] == '#') 2449 { 2450 /* Try preserve the start column number. */ 2451 size_t const offDst = pszDst - pParser->szBuf; 2452 if (offDst < offSrc) 2453 { 2454 memset(pszDst, ' ', offSrc - offDst); 2455 pszDst += offSrc - offDst; 2456 } 2457 else if (offSrc != offSrcStart) 2458 *pszDst++ = ' '; 2459 2460 *ppszDst = pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], cchLine - offSrc); 2461 return cchLine - offSrcStart != (size_t)(pszDst - &pParser->szBuf[offDst]) 2462 || memcmp(&pParser->szBuf[offDst], &pchLine[offSrcStart], cchLine - offSrcStart) != 0; 2463 } 2464 2465 /* Complain and copy out the text unmodified. */ 2466 ScmError(pParser->pState, VERR_PARSE_ERROR, "%u:%u: Expected comment, found: %.*s", 2467 pParser->iLine, offSrc, cchLine - offSrc, &pchLine[offSrc]); 2468 *ppszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], cchLine - offSrcStart); 2469 return false; 2470 } 2471 2472 2473 /** 2474 * Deals with: ifeq, ifneq, if1of and ifn1of 2475 */ 2476 static bool scmKmkHandleIfParentheses(KMKPARSER *pParser, size_t offToken, KMKTOKEN enmToken, size_t cchToken, bool fElse) 2477 { 2478 const char * const pchLine = pParser->pchLine; 2479 size_t const cchLine = pParser->cchLine; 2480 uint32_t const cchIndent = pParser->iActualDepth 2481 - (fElse && pParser->iDepth > 0 && !pParser->aDepth[pParser->iDepth].fIgnoreNesting); 2482 2483 /* 2484 * Push it onto the stack. All these nestings are relevant. 2485 */ 2486 if (!scmKmkPushNesting(pParser, enmToken)) 2487 return false; 2488 2489 /* 2490 * We do not allow line continuation for these. 2491 */ 2492 if (scmKmkIsLineWithContinuation(pchLine, cchLine)) 2493 return scmKmkGiveUp(pParser, "Line continuation not allowed with '%.*s' directive.", cchToken, &pchLine[offToken]); 2494 2495 /* 2496 * We stage the modified line in the buffer, so check that the line isn't 2497 * too long (it seriously should be). 2498 */ 2499 if (cchLine + cchIndent + 32 > sizeof(pParser->szBuf)) 2500 return scmKmkGiveUp(pParser, "Line too long for a '%.*s' directive: %u chars", cchToken, &pchLine[offToken], cchLine); 2501 char *pszDst = pParser->szBuf; 2502 2503 /* 2504 * Emit indent and initial token. 2505 */ 2506 memset(pszDst, ' ', cchIndent); 2507 pszDst += cchIndent; 2508 2509 if (fElse) 2510 pszDst = (char *)mempcpy(pszDst, RT_STR_TUPLE("else ")); 2511 2512 memcpy(pszDst, &pchLine[offToken], cchToken); 2513 pszDst += cchToken; 2514 2515 size_t offSrc = offToken + cchToken; 2516 bool fModified = offSrc != (size_t)(pszDst - &pParser->szBuf[0]) 2517 || memcmp(pchLine, pszDst, offSrc) != 0; 2518 2519 /* 2520 * There shall be exactly one space between the token and the opening parenthesis. 2521 */ 2522 if (pchLine[offSrc] == ' ' && pchLine[offSrc + 1] == '(') 2523 offSrc += 2; 2524 else 2525 { 2526 fModified = true; 2527 while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc])) 2528 offSrc++; 2529 if (pchLine[offSrc] != '(') 2530 return scmKmkGiveUp(pParser, "Expected '(' to follow '%.*s'", cchToken, &pchLine[offToken]); 2531 offSrc++; 2532 } 2533 *pszDst++ = ' '; 2534 *pszDst++ = '('; 2535 2536 /* 2537 * There shall be no blanks after the opening parenthesis. 2538 */ 2539 if (RT_C_IS_SPACE(pchLine[offSrc])) 2540 { 2541 fModified = true; 2542 while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc])) 2543 offSrc++; 2544 } 2545 2546 /* 2547 * Work up to the ',' separator. It shall likewise not be preceeded by any spaces. 2548 * Need to take $(func 1,2,3) calls into account here, so we trac () and {} while 2549 * skipping ahead. 2550 */ 2551 if (pchLine[offSrc] != ',') 2552 { 2553 size_t const offSrcStart = offSrc; 2554 offSrc = scmKmkSkipExpString(pchLine, cchLine, offSrc, ','); 2555 if (pchLine[offSrc] != ',') 2556 return scmKmkGiveUp(pParser, "Expected ',' somewhere after '%.*s('", cchToken, &pchLine[offToken]); 2557 2558 size_t cchCopy = offSrc - offSrcStart; 2559 while (cchCopy > 0 && RT_C_IS_BLANK(pchLine[offSrcStart + cchCopy - 1])) 2560 { 2561 fModified = true; 2562 cchCopy--; 2563 } 2564 2565 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], cchCopy); 2566 } 2567 /* 'if1of(, stuff)' does not make sense in committed code: */ 2568 else if (enmToken == kKmkToken_if1of || enmToken == kKmkToken_ifn1of) 2569 return scmKmkGiveUp(pParser, "Left set cannot be empty for '%.*s'", cchToken, &pchLine[offToken]); 2570 offSrc++; 2571 *pszDst++ = ','; 2572 2573 /* 2574 * For if1of and ifn1of we require a space after the comma, whereas ifeq and 2575 * ifneq shall not have any blanks. This is to help tell them apart. 2576 */ 2577 if (enmToken == kKmkToken_if1of || enmToken == kKmkToken_ifn1of) 2578 { 2579 *pszDst++ = ' '; 2580 if (pchLine[offSrc] == ' ') 2581 offSrc++; 2582 } 2583 while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc])) 2584 { 2585 fModified = true; 2586 offSrc++; 2587 } 2588 2589 if (pchLine[offSrc] != ')') 2590 { 2591 size_t const offSrcStart = offSrc; 2592 offSrc = scmKmkSkipExpString(pchLine, cchLine, offSrc, ')'); 2593 if (pchLine[offSrc] != ')') 2594 return scmKmkGiveUp(pParser, "No closing parenthesis for '%.*s'?", cchToken, &pchLine[offToken]); 2595 2596 size_t cchCopy = offSrc - offSrcStart; 2597 while (cchCopy > 0 && RT_C_IS_BLANK(pchLine[offSrcStart + cchCopy - 1])) 2598 { 2599 fModified = true; 2600 cchCopy--; 2601 } 2602 2603 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], cchCopy); 2604 } 2605 /* 'if1of(stuff, )' does not make sense in committed code: */ 2606 else if (enmToken == kKmkToken_if1of || enmToken == kKmkToken_ifn1of) 2607 return scmKmkGiveUp(pParser, "Right set cannot be empty for '%.*s'", cchToken, &pchLine[offToken]); 2608 offSrc++; 2609 *pszDst++ = ')'; 2610 2611 /* 2612 * Handle comment. 2613 */ 2614 if (offSrc < cchLine) 2615 fModified |= scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst); 2616 2617 /* 2618 * Done. 2619 */ 2620 *pszDst = '\0'; 2621 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol); 2622 return fModified; 2623 } 2624 2625 2626 /** 2627 * Deals with: if, ifdef and ifndef 2628 */ 2629 static bool scmKmkHandleIfSpace(KMKPARSER *pParser, size_t offToken, KMKTOKEN enmToken, size_t cchToken, bool fElse) 2630 { 2631 const char *pchLine = pParser->pchLine; 2632 size_t cchLine = pParser->cchLine; 2633 uint32_t const cchIndent = pParser->iActualDepth 2634 - (fElse && pParser->iDepth > 0 && !pParser->aDepth[pParser->iDepth].fIgnoreNesting); 2635 2636 /* 2637 * Push it onto the stack. 2638 * 2639 * For ifndef we ignore the outmost ifndef in non-Makefile.kmk files, if 2640 * the define matches the typical pattern for a file blocker. 2641 */ 2642 if (!fElse) 2643 { 2644 if (!scmKmkPushNesting(pParser, enmToken)) 2645 return false; 2646 } 2647 else 2648 { 2649 pParser->aDepth[pParser->iDepth - 1].enmToken = enmToken; 2650 pParser->aDepth[pParser->iDepth - 1].iLine = pParser->iLine; 2651 } 2652 bool fIgnoredNesting = false; 2653 if (enmToken == kKmkToken_ifndef) 2654 { 2655 /** @todo */ 2656 } 2657 2658 /* 2659 * We do not allow line continuation for these. 2660 */ 2661 uint32_t cLines = 1; 2662 size_t cchMaxLeadWord = 0; 2663 size_t cchLineTotal = cchLine; 2664 if (scmKmkIsLineWithContinuation(pchLine, cchLine)) 2665 { 2666 if (enmToken != kKmkToken_if) 2667 return scmKmkGiveUp(pParser, "Line continuation not allowed with '%.*s' directive.", cchToken, &pchLine[offToken]); 2668 cchLineTotal = scmKmkLineContinuationPeek(pParser, &cLines, &cchMaxLeadWord); 2669 } 2670 2671 /* 2672 * We stage the modified line in the buffer, so check that the line isn't 2673 * too long (plain if can be long, but not ifndef/ifdef). 2674 */ 2675 if (cchLineTotal + pParser->iActualDepth + 32 > sizeof(pParser->szBuf)) 2676 return scmKmkGiveUp(pParser, "Line too long for a '%.*s' directive: %u chars", 2677 cchToken, &pchLine[offToken], cchLineTotal); 2678 char *pszDst = pParser->szBuf; 2679 2680 /* 2681 * Emit indent and initial token. 2682 */ 2683 memset(pszDst, ' ', cchIndent); 2684 pszDst += cchIndent; 2685 2686 if (fElse) 2687 pszDst = (char *)mempcpy(pszDst, RT_STR_TUPLE("else ")); 2688 2689 memcpy(pszDst, &pchLine[offToken], cchToken); 2690 pszDst += cchToken; 2691 2692 size_t offSrc = offToken + cchToken; 2693 bool fModified = offSrc != (size_t)(pszDst - &pParser->szBuf[0]) 2694 || memcmp(pchLine, pszDst, offSrc) != 0; 2695 2696 /* 2697 * ifndef/ifdef shall have exactly one space. For 'if' we allow up to 4, but 2698 * we'll deal with that further down. 2699 */ 2700 size_t cchSpaces = 0; 2701 while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc])) 2702 { 2703 fModified |= pchLine[offSrc] != ' '; 2704 cchSpaces++; 2705 offSrc++; 2706 } 2707 if (cchSpaces == 0) 2708 return scmKmkGiveUp(pParser, "Nothing following '%.*s' or bogus line continuation?", cchToken, &pchLine[offToken]); 2709 *pszDst++ = ' '; 2710 2711 /* 2712 * For ifdef and ifndef there now comes a single word. 2713 */ 2714 if (enmToken != kKmkToken_if) 2715 { 2716 fModified |= cchSpaces != 1; 2717 2718 size_t const offSrcStart = offSrc; 2719 offSrc = scmKmkSkipExpString(pchLine, cchLine, offSrc, ' ', '\t'); /** @todo probably not entirely correct */ 2720 if (offSrc == offSrcStart) 2721 return scmKmkGiveUp(pParser, "No word following '%.*s'?", cchToken, &pchLine[offToken]); 2722 2723 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], offSrc - offSrcStart); 2724 } 2725 /* 2726 * While for 'if' things are more complicated, especially if it spans more 2727 * than one line. 2728 */ 2729 else if (cLines <= 1) 2730 { 2731 /* Single line expression: Just assume the expression goes up to the 2732 EOL or comment hash. Strip and copy as-is for now. */ 2733 fModified |= cchSpaces != 1; 2734 2735 const char *pchSrcHash = (const char *)memchr(&pchLine[offSrc], '#', cchLine - offSrc); 2736 size_t cchExpr = pchSrcHash ? pchSrcHash - &pchLine[offSrc] : cchLine - offSrc; 2737 while (cchExpr > 0 && RT_C_IS_BLANK(pchLine[offSrc + cchExpr - 1])) 2738 cchExpr--; 2739 2740 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], cchExpr); 2741 offSrc += cchExpr; 2742 } 2743 else 2744 { 2745 /* Multi line expression: We normalize leading whitespace using 2746 cchMaxLeadWord for now. Expression on line 2+ are indented by two 2747 extra characters, because we'd otherwise be puttin the operator on 2748 the same level as the 'if', which would be confusing. Thus: 2749 2750 if expr1 2751 + expr2 2752 endif 2753 2754 if expr1 2755 || expr2 2756 endif 2757 2758 if expr3 2759 vtg expr4 2760 endif 2761 2762 We do '#' / EOL handling for the final line the same way as above. 2763 2764 Later we should add the ability to rework the expression properly, 2765 making sure new lines starts with operators and such. */ 2766 /** @todo Implement simples expression parser and indenter, possibly also 2767 * removing unnecessary parentheses. Can be shared with C/C++. */ 2768 if (cchMaxLeadWord > 3) 2769 return scmKmkGiveUp(pParser, 2770 "Bogus multi-line 'if' expression! Extra lines must start with operator (cchMaxLeadWord=%u).", 2771 cchMaxLeadWord); 2772 fModified |= cchSpaces != cchMaxLeadWord + 1; 2773 memset(pszDst, ' ', cchMaxLeadWord); 2774 pszDst += cchMaxLeadWord; 2775 2776 size_t cchSrcContIndent = offToken + 2; 2777 for (uint32_t iSubLine = 0; iSubLine < cLines - 1; iSubLine++) 2778 { 2779 /* Trim the line. */ 2780 size_t offSrcEnd = cchLine; 2781 Assert(pchLine[offSrcEnd - 1] == '\\'); 2782 offSrcEnd--; 2783 2784 if (pchLine[offSrcEnd - 1] == '\\') 2785 return scmKmkGiveUp(pParser, "Escaped '\\' before line continuation in 'if' expression is not allowed!"); 2786 2787 while (offSrcEnd > offSrc && RT_C_IS_BLANK(pchLine[offSrcEnd - 1])) 2788 offSrcEnd--; 2789 2790 /* Comments with line continuation is not allowed in commited makefiles. */ 2791 if (offSrc < offSrcEnd && memchr(&pchLine[offSrc], '#', cchLine - offSrc) != NULL) 2792 return scmKmkGiveUp(pParser, "Comment in multi-line 'if' expression is not allowed to start before the final line!"); 2793 2794 /* Output it. */ 2795 if (offSrc < offSrcEnd) 2796 { 2797 if (iSubLine > 0 && offSrc > cchSrcContIndent) 2798 { 2799 memset(pszDst, ' ', offSrc - cchSrcContIndent); 2800 pszDst += offSrc - cchSrcContIndent; 2801 } 2802 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], offSrcEnd - offSrc); 2803 *pszDst++ = ' '; 2804 } 2805 else if (iSubLine == 0) 2806 return scmKmkGiveUp(pParser, "Expected expression after 'if', not line continuation!"); 2807 *pszDst++ = '\\'; 2808 *pszDst = '\0'; 2809 size_t cchDst = (size_t)(pszDst - pParser->szBuf); 2810 fModified |= cchDst != cchLine 2811 || memcmp(pParser->szBuf, pchLine, cchLine) != 0; 2812 ScmStreamPutLine(pParser->pOut, pParser->szBuf, cchDst, pParser->enmEol); 2813 2814 /* 2815 * Fetch the next line and start processing it. 2816 */ 2817 pParser->pchLine = pchLine = ScmStreamGetLine(pParser->pIn, &pParser->cchLine, &pParser->enmEol); 2818 if (!pchLine) 2819 return ScmError(pParser->pState, VERR_INTERNAL_ERROR_3, "ScmStreamGetLine unexpectedly returned NULL!"); 2820 cchLine = pParser->cchLine; 2821 pParser->iLine++; 2822 2823 /* Skip leading whitespace and adjust the source continuation indent: */ 2824 offSrc = 0; 2825 while (offSrc < cchLine && RT_C_IS_SPACE(pchLine[offSrc])) 2826 offSrc++; 2827 /** @todo tabs */ 2828 2829 if (iSubLine == 0) 2830 cchSrcContIndent = offSrc; 2831 2832 /* Initial indent: */ 2833 pszDst = pParser->szBuf; 2834 memset(pszDst, ' ', cchIndent + 2); 2835 pszDst += cchIndent + 2; 2836 } 2837 2838 /* Output the expression on the final line. */ 2839 const char *pchSrcHash = (const char *)memchr(&pchLine[offSrc], '#', cchLine - offSrc); 2840 size_t cchExpr = pchSrcHash ? pchSrcHash - &pchLine[offSrc] : cchLine - offSrc; 2841 while (cchExpr > 0 && RT_C_IS_BLANK(pchLine[offSrc + cchExpr - 1])) 2842 cchExpr--; 2843 2844 pszDst = (char *)mempcpy(pszDst, &pchLine[offSrc], cchExpr); 2845 offSrc += cchExpr; 2846 } 2847 2848 2849 /* 2850 * Handle comment. 2851 * 2852 * Here we check for the "scm:ignore-nesting" directive that makes us not 2853 * add indentation for this directive. We do this on the destination buffer 2854 * as that can be zero terminated and is therefore usable with strstr. 2855 */ 2856 if (offSrc >= cchLine) 2857 *pszDst = '\0'; 2858 else 2859 { 2860 char * const pszDstSrc = pszDst; 2861 fModified |= scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst); 2862 *pszDst = '\0'; 2863 2864 /* Check for special comment making us ignore the nesting. We do this in the 2865 2866 */ 2867 if (!fIgnoredNesting && strstr(pszDstSrc, "scm:ignore-nesting") != NULL) 2868 { 2869 pParser->aDepth[pParser->iDepth - 1].fIgnoreNesting = true; 2870 pParser->iActualDepth--; 2871 } 2872 } 2873 2874 /* 2875 * Done. 2876 */ 2877 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol); 2878 return fModified; 2879 } 2880 2881 2882 /** 2883 * Deals with: else 2884 */ 2885 static bool scmKmkHandleElse(KMKPARSER *pParser, size_t offToken) 2886 { 2887 const char * const pchLine = pParser->pchLine; 2888 size_t const cchLine = pParser->cchLine; 2889 2890 if (pParser->iDepth < 1) 2891 return scmKmkGiveUp(pParser, "Lone 'else'"); 2892 uint32_t const cchIndent = pParser->iActualDepth - !pParser->aDepth[pParser->iDepth].fIgnoreNesting; 2893 2894 /* 2895 * Look past the else and check if there any ifxxx token following it. 2896 */ 2897 size_t offSrc = offToken + 4; 2898 while (offSrc < cchLine && RT_C_IS_BLANK(pchLine[offSrc])) 2899 offSrc++; 2900 if (offSrc < cchLine) 2901 { 2902 size_t cchWord = 0; 2903 while (offSrc + cchWord < cchLine && RT_C_IS_ALNUM(pchLine[offSrc + cchWord])) 2904 cchWord++; 2905 if (cchWord) 2906 { 2907 KMKTOKEN enmToken = scmKmkIdentifyToken(&pchLine[offSrc], cchWord); 2908 switch (enmToken) 2909 { 2910 case kKmkToken_ifeq: 2911 case kKmkToken_ifneq: 2912 case kKmkToken_if1of: 2913 case kKmkToken_ifn1of: 2914 return scmKmkHandleIfParentheses(pParser, offSrc, enmToken, cchWord, true /*fElse*/); 2915 2916 case kKmkToken_ifdef: 2917 case kKmkToken_ifndef: 2918 case kKmkToken_if: 2919 return scmKmkHandleIfSpace(pParser, offSrc, enmToken, cchWord, true /*fElse*/); 2920 2921 default: 2922 break; 2923 } 2924 } 2925 } 2926 2927 /* 2928 * We do not allow line continuation for these. 2929 */ 2930 if (scmKmkIsLineWithContinuation(pchLine, cchLine)) 2931 return scmKmkGiveUp(pParser, "Line continuation not allowed with 'else' directive."); 2932 2933 /* 2934 * We stage the modified line in the buffer, so check that the line isn't 2935 * too long (it seriously should be). 2936 */ 2937 if (cchLine + cchIndent + 32 > sizeof(pParser->szBuf)) 2938 return scmKmkGiveUp(pParser, "Line too long for a 'else' directive: %u chars", cchLine); 2939 char *pszDst = pParser->szBuf; 2940 2941 /* 2942 * Emit indent and initial token. 2943 */ 2944 memset(pszDst, ' ', cchIndent); 2945 pszDst = (char *)mempcpy(&pszDst[cchIndent], RT_STR_TUPLE("else")); 2946 2947 offSrc = offToken + 4; 2948 bool fModified = offSrc != (size_t)(pszDst - &pParser->szBuf[0]) 2949 || memcmp(pchLine, pszDst, offSrc) != 0; 2950 2951 /* 2952 * Handle comment. 2953 */ 2954 if (offSrc < cchLine) 2955 fModified |= scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst); 2956 2957 /* 2958 * Done. 2959 */ 2960 *pszDst = '\0'; 2961 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol); 2962 return fModified; 2963 } 2964 2965 2966 /** 2967 * Deals with: endif 2968 */ 2969 static bool scmKmkHandleEndif(KMKPARSER *pParser, size_t offToken) 2970 { 2971 const char * const pchLine = pParser->pchLine; 2972 size_t const cchLine = pParser->cchLine; 2973 2974 /* 2975 * Pop a nesting. 2976 */ 2977 if (pParser->iDepth < 1) 2978 return scmKmkGiveUp(pParser, "Lone 'endif'"); 2979 uint32_t iDepth = pParser->iDepth - 1; 2980 pParser->iDepth = iDepth; 2981 if (!pParser->aDepth[iDepth].fIgnoreNesting) 2982 { 2983 AssertStmt(pParser->iActualDepth > 0, pParser->iActualDepth++); 2984 pParser->iActualDepth -= 1; 2985 } 2986 uint32_t const cchIndent = pParser->iActualDepth; 2987 2988 /* 2989 * We do not allow line continuation for these. 2990 */ 2991 if (scmKmkIsLineWithContinuation(pchLine, cchLine)) 2992 return scmKmkGiveUp(pParser, "Line continuation not allowed with 'endif' directive."); 2993 2994 /* 2995 * We stage the modified line in the buffer, so check that the line isn't 2996 * too long (it seriously should be). 2997 */ 2998 if (cchLine + cchIndent + 32 > sizeof(pParser->szBuf)) 2999 return scmKmkGiveUp(pParser, "Line too long for a 'else' directive: %u chars", cchLine); 3000 char *pszDst = pParser->szBuf; 3001 3002 /* 3003 * Emit indent and initial token. 3004 */ 3005 memset(pszDst, ' ', cchIndent); 3006 pszDst = (char *)mempcpy(&pszDst[cchIndent], RT_STR_TUPLE("endif")); 3007 3008 size_t offSrc = offToken + 5; 3009 bool fModified = offSrc != (size_t)(pszDst - &pParser->szBuf[0]) 3010 || memcmp(pchLine, pszDst, offSrc) != 0; 3011 3012 /* 3013 * Handle comment. 3014 */ 3015 if (offSrc < cchLine) 3016 fModified |= scmKmkTailComment(pParser, pchLine, cchLine, offSrc, &pszDst); 3017 3018 /* 3019 * Done. 3020 */ 3021 *pszDst = '\0'; 3022 ScmStreamPutLine(pParser->pOut, pParser->szBuf, pszDst - pParser->szBuf, pParser->enmEol); 3023 return fModified; 3024 } 3025 3026 2175 3027 /** 2176 3028 * Rewrite a kBuild makefile. … … 2189 3041 bool rewrite_Makefile_kmk(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 2190 3042 { 2191 RT_NOREF4(pState, pIn, pOut, pSettings); 2192 return false; 2193 } 2194 3043 if (!pSettings->fStandarizeKmk) 3044 return false; 3045 3046 /* 3047 * Parser state. 3048 */ 3049 KMKPARSER Parser; 3050 Parser.iDepth = 0; 3051 Parser.iActualDepth = 0; 3052 Parser.fInReceipt = false; 3053 Parser.iLine = 0; 3054 Parser.pState = pState; 3055 Parser.pIn = pIn; 3056 Parser.pOut = pOut; 3057 Parser.pSettings = pSettings; 3058 3059 /* 3060 * Iterate the file. 3061 */ 3062 bool fModified = false; 3063 const char *pchLine; 3064 while ((Parser.pchLine = pchLine = ScmStreamGetLine(pIn, &Parser.cchLine, &Parser.enmEol)) != NULL) 3065 { 3066 Parser.iLine++; 3067 size_t const cchLine = Parser.cchLine; 3068 3069 /* 3070 * Skip leading whitespace and check for directives (simplified). 3071 */ 3072 size_t offLine = 0; 3073 while (offLine < cchLine && RT_C_IS_BLANK(pchLine[offLine])) 3074 offLine++; 3075 3076 /* Find end of word (if any): */ 3077 size_t cchWord = 0; 3078 while (offLine + cchWord < cchLine && (RT_C_IS_ALNUM(pchLine[offLine + cchWord]) || pchLine[offLine + cchWord] == '-')) 3079 cchWord++; 3080 if (cchWord > 0) 3081 { 3082 KMKTOKEN enmToken = scmKmkIdentifyToken(&pchLine[offLine], cchWord); 3083 switch (enmToken) 3084 { 3085 case kKmkToken_ifeq: 3086 case kKmkToken_ifneq: 3087 case kKmkToken_if1of: 3088 case kKmkToken_ifn1of: 3089 fModified |= scmKmkHandleIfParentheses(&Parser, offLine, enmToken, cchWord, false /*fElse*/); 3090 continue; 3091 3092 case kKmkToken_ifdef: 3093 case kKmkToken_ifndef: 3094 case kKmkToken_if: 3095 fModified |= scmKmkHandleIfSpace(&Parser, offLine, enmToken, cchWord, false /*fElse*/); 3096 continue; 3097 3098 case kKmkToken_else: 3099 fModified |= scmKmkHandleElse(&Parser, offLine); 3100 continue; 3101 3102 case kKmkToken_endif: 3103 fModified |= scmKmkHandleEndif(&Parser, offLine); 3104 continue; 3105 3106 /* Includes: */ 3107 case kKmkToken_include: 3108 case kKmkToken_sinclude: 3109 case kKmkToken_dash_include: 3110 case kKmkToken_includedep: 3111 case kKmkToken_includedep_queue: 3112 case kKmkToken_includedep_flush: 3113 break; 3114 3115 /* Others: */ 3116 case kKmkToken_define: 3117 case kKmkToken_endef: 3118 case kKmkToken_export: 3119 case kKmkToken_unexport: 3120 case kKmkToken_local: 3121 case kKmkToken_override: 3122 break; 3123 3124 case kKmkToken_Word: 3125 break; 3126 } 3127 } 3128 3129 /* Pass it thru as-is: */ 3130 ScmStreamPutLine(pOut, pchLine, cchLine, Parser.enmEol); 3131 } 3132 3133 return fModified; 3134 } 3135 3136 3137 3138 /********************************************************************************************************************************* 3139 * Flower Box Section Markers * 3140 *********************************************************************************************************************************/ 2195 3141 2196 3142 static bool isFlowerBoxSectionMarker(PSCMSTREAM pIn, const char *pchLine, size_t cchLine, uint32_t cchWidth, -
trunk/src/bldprogs/scmstream.cpp
r98103 r98319 917 917 memcpy(pvBuf, &pStream->pch[pStream->off], cbToRead); 918 918 return ScmStreamSeekAbsolute(pStream, pStream->off + cbToRead); 919 } 920 921 922 /** 923 * Checks if we're at the end of the stream. 924 * 925 * @returns true if end of stream, false if not. 926 * @param pStream The stream. Must be in read mode. 927 */ 928 bool ScmStreamIsEndOfStream(PSCMSTREAM pStream) 929 { 930 AssertReturn(!pStream->fWriteOrRead, false); 931 return pStream->off >= pStream->cb 932 || RT_FAILURE(pStream->rc); 919 933 } 920 934 -
trunk/src/bldprogs/scmstream.h
r98103 r98319 129 129 unsigned ScmStreamPeekCh(PSCMSTREAM pStream); 130 130 int ScmStreamRead(PSCMSTREAM pStream, void *pvBuf, size_t cbToRead); 131 bool ScmStreamIsEndOfStream(PSCMSTREAM pStream); 131 132 bool ScmStreamIsWhiteLine(PSCMSTREAM pStream, size_t iLine); 132 133 SCMEOL ScmStreamGetEol(PSCMSTREAM pStream);
Note:
See TracChangeset
for help on using the changeset viewer.