Changeset 98380 in vbox
- Timestamp:
- Feb 1, 2023 12:40:48 PM (2 years ago)
- svn:sync-xref-src-repo-rev:
- 155624
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/bldprogs/scmrw-kmk.cpp
r98374 r98380 52 52 * Structures and Typedefs * 53 53 *********************************************************************************************************************************/ 54 typedef enum KMKASSIGNTYPE 55 { 56 kKmkAssignType_Recursive, 57 kKmkAssignType_Conditional, 58 kKmkAssignType_Appending, 59 kKmkAssignType_Prepending, 60 kKmkAssignType_Simple, 61 kKmkAssignType_Immediate 62 } KMKASSIGNTYPE; 63 64 /** Context for scmKmkWordLength. */ 65 typedef enum 66 { 67 /** Target file or assignment. 68 * Separators: space, '=', ':' */ 69 kKmkWordCtx_TargetFileOrAssignment, 70 /** Target file. 71 * Separators: space, ':' */ 72 kKmkWordCtx_TargetFile, 73 /** Dependency file or (target variable) assignment. 74 * Separators: space, '=', ':', '|' */ 75 kKmkWordCtx_DepFileOrAssignment, 76 /** Dependency file. 77 * Separators: space, '|' */ 78 kKmkWordCtx_DepFile 79 } KMKWORDCTX; 80 81 typedef struct KMKWORDSTATE 82 { 83 uint16_t uDepth; 84 char chOpen; 85 } KMKWORDSTATE; 86 54 87 typedef enum KMKTOKEN 55 88 { … … 91 124 { 92 125 KMKTOKEN enmToken; 93 uint32_t iLine;94 126 bool fIgnoreNesting; 127 size_t iLine; 95 128 } aDepth[64]; 96 129 unsigned iDepth; … … 98 131 bool fInRecipe; 99 132 100 /** The current line number (for error messages and peeking). */101 uint32_t iLine;102 133 /** The EOL type of the current line. */ 103 134 SCMEOL enmEol; … … 185 216 186 217 /** 218 * Modifies the fInRecipe state variable, logging changes in verbose mode. 219 */ 220 static void scmKmkSetInRecipe(KMKPARSER *pParser, bool fInRecipe) 221 { 222 if (pParser->fInRecipe != fInRecipe) 223 ScmVerbose(pParser->pState, 4, "%u: debug: %s\n", 224 ScmStreamTellLine(pParser->pIn), fInRecipe ? "in-recipe" : "not-in-recipe"); 225 pParser->fInRecipe = fInRecipe; 226 } 227 228 229 /** 187 230 * Gives up on the current line, copying it as it and requesting manual repair. 188 231 */ … … 191 234 va_list va; 192 235 va_start(va, pszFormat); 193 ScmFixManually(pParser->pState, "%u: %N\n", pParser->iLine, pszFormat, &va);236 ScmFixManually(pParser->pState, "%u: %N\n", ScmStreamTellLine(pParser->pIn), pszFormat, &va); 194 237 va_end(va); 195 238 … … 277 320 if (iDepth + 1 >= RT_ELEMENTS(pParser->aDepth)) 278 321 { 279 ScmError(pParser->pState, VERR_ASN1_TOO_DEEPLY_NESTED /*?*/, "%u: Too deep if/define nesting!\n", pParser->iLine); 322 ScmError(pParser->pState, VERR_ASN1_TOO_DEEPLY_NESTED /*?*/, 323 "%u: Too deep if/define nesting!\n", ScmStreamTellLine(pParser->pIn)); 280 324 return false; 281 325 } 282 326 283 327 pParser->aDepth[iDepth].enmToken = enmToken; 284 pParser->aDepth[iDepth].iLine = pParser->iLine;328 pParser->aDepth[iDepth].iLine = ScmStreamTellLine(pParser->pIn); 285 329 pParser->aDepth[iDepth].fIgnoreNesting = false; 286 330 pParser->iDepth = iDepth + 1; 287 331 pParser->iActualDepth += 1; 332 ScmVerbose(pParser->pState, 5, "%u: debug: nesting %u (token %u)\n", pParser->aDepth[iDepth].iLine, iDepth + 1, enmToken); 288 333 return true; 289 334 } … … 320 365 321 366 322 /** Context for scmKmkWordLength. */323 typedef enum324 {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_DepFile337 } KMKWORDCTX;338 339 367 /** 340 368 * Finds the length of the word (file) @a offStart. 369 * 370 * This only takes one line into account, so variable expansions (function 371 * calls) spanning multiple lines will be handled as one word per line with help 372 * from @a pState. This allows the caller to properly continutation intend the 373 * additional lines. 341 374 * 342 375 * @returns Length of word starting at @a offStart. Zero if there is whitespace … … 346 379 * @param cchLine The line length. 347 380 * @param offStart Offset to the start of the word. 381 * @param pState Where multiline variable expansion is tracked. 348 382 */ 349 static size_t scmKmkWordLength(const char *pchLine, size_t cchLine, size_t offStart, KMKWORDCTX enmCtx )383 static size_t scmKmkWordLength(const char *pchLine, size_t cchLine, size_t offStart, KMKWORDCTX enmCtx, KMKWORDSTATE *pState) 350 384 { 351 385 AssertReturn(offStart < cchLine && !RT_C_IS_BLANK(pchLine[offStart]), 0); 386 387 /* 388 * Drop any line continuation slash from the line length, so we don't count 389 * it into the word length. Also, any spaces preceeding it (for multiline 390 * variable function expansion). ASSUMES no trailing slash escaping. 391 */ 392 if (cchLine > 0 && pchLine[cchLine - 1] == '\\') 393 do 394 cchLine--; 395 while (cchLine > offStart && RT_C_IS_SPACE(pchLine[cchLine - 1])); 396 397 /* 398 * If we were inside a variable function expansion, continue till we reach the end. 399 * This kind of duplicates the code below. 400 */ 352 401 size_t off = offStart; 402 if (pState->uDepth > 0) 403 { 404 Assert(pState->chOpen == '(' || pState->chOpen == '{'); 405 char const chOpen = pState->chOpen; 406 char const chClose = chOpen == '(' ? ')' : '}'; 407 unsigned uDepth = pState->uDepth; 408 for (;;) 409 { 410 char ch; 411 if (off < cchLine) 412 ch = pchLine[off++]; 413 else /* Reached the end while still inside the expansion. */ 414 { 415 pState->chOpen = chOpen; 416 pState->uDepth = (uint16_t)uDepth; 417 return cchLine - offStart; 418 } 419 if (ch == chOpen) 420 uDepth++; 421 else if (ch == chClose && --uDepth == 0) 422 break; 423 } 424 pState->uDepth = 0; 425 pState->chOpen = 0; 426 } 427 428 /* 429 * Process till we find blank or end of the line. 430 */ 353 431 while (off < cchLine) 354 432 { … … 357 435 break; 358 436 359 if (ch == ':') 437 if (ch == '$') 438 { 439 /* 440 * Skip variable expansion. 441 */ 442 if (off + 2 >= cchLine) 443 return cchLine - offStart; 444 char const chOpen = pchLine[++off]; 445 if (chOpen == '(' || chOpen == '{') 446 { 447 char const chClose = chOpen == '(' ? ')' : '}'; 448 unsigned uDepth = 1; 449 off++; 450 for (;;) 451 { 452 if (off < cchLine) 453 ch = pchLine[off++]; 454 else /* Reached the end while inside the expansion. */ 455 { 456 pState->chOpen = chOpen; 457 pState->uDepth = (uint16_t)uDepth; 458 return cchLine - offStart; 459 } 460 if (ch == chOpen) 461 uDepth++; 462 else if (ch == chClose && --uDepth == 0) 463 break; 464 } 465 continue; 466 } 467 /* else: $X */ 468 } 469 else if (ch == ':') 360 470 { 361 471 /* … … 474 584 /* Complain and copy out the text unmodified. */ 475 585 ScmError(pParser->pState, VERR_PARSE_ERROR, "%u:%u: Expected comment, found: %.*s", 476 pParser->iLine, offSrc, cchLine - offSrc, &pchLine[offSrc]);586 ScmStreamTellLine(pParser->pIn), offSrc, cchLine - offSrc, &pchLine[offSrc]); 477 587 *ppszDst = (char *)mempcpy(pszDst, &pchLine[offSrcStart], cchLine - offSrcStart); 478 588 return false; /*dummy*/ … … 495 605 * Push it onto the stack. All these nestings are relevant. 496 606 */ 497 if (!scmKmkPushNesting(pParser, enmToken)) 498 return false; 607 if (!fElse) 608 { 609 if (!scmKmkPushNesting(pParser, enmToken)) 610 return false; 611 } 612 else 613 { 614 pParser->aDepth[pParser->iDepth - 1].enmToken = enmToken; 615 pParser->aDepth[pParser->iDepth - 1].iLine = ScmStreamTellLine(pParser->pIn); 616 } 499 617 500 618 /* … … 637 755 * the define matches the typical pattern for a file blocker. 638 756 */ 757 bool fIgnoredNesting = false; 639 758 if (!fElse) 640 759 { 641 760 if (!scmKmkPushNesting(pParser, enmToken)) 642 761 return false; 762 if (enmToken == kKmkToken_ifndef) 763 { 764 /** @todo */ 765 } 643 766 } 644 767 else 645 768 { 646 769 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 */ 770 pParser->aDepth[pParser->iDepth - 1].iLine = ScmStreamTellLine(pParser->pIn); 653 771 } 654 772 … … 809 927 } 810 928 cchLine = pParser->cchLine; 811 pParser->iLine++;812 929 813 930 /* Skip leading whitespace and adjust the source continuation indent: */ … … 852 969 *pszDst = '\0'; 853 970 854 /* Check for special comment making us ignore the nesting. We do this in the855 856 */971 /* Check for special comment making us ignore the nesting. We do this 972 on the destination buffer since it's zero terminated allowing normal 973 strstr use. */ 857 974 if (!fIgnoredNesting && strstr(pszDstSrc, "scm:ignore-nesting") != NULL) 858 975 { 859 976 pParser->aDepth[pParser->iDepth - 1].fIgnoreNesting = true; 860 977 pParser->iActualDepth--; 978 ScmVerbose(pParser->pState, 5, "%u: debug: ignoring nesting - actual depth: %u\n", 979 pParser->aDepth[pParser->iDepth - 1].iLine, pParser->iActualDepth); 861 980 } 862 981 } … … 977 1096 } 978 1097 uint32_t const cchIndent = pParser->iActualDepth; 1098 ScmVerbose(pParser->pState, 5, "%u: debug: unnesting %u/%u (endif)\n", 1099 ScmStreamTellLine(pParser->pIn), iDepth, pParser->iActualDepth); 979 1100 980 1101 /* … … 1059 1180 static bool scmKmkHandleDefine(KMKPARSER *pParser, size_t offToken) 1060 1181 { 1061 /* Assignments takes us out of recipe mode. */ 1062 pParser->fInRecipe = false; 1182 /* Hack Alert! Start out parsing the define in recipe mode. 1183 1184 Technically, we shouldn't evaluate the content of a define till it's 1185 used. However, we ASSUME they are either makefile code snippets or 1186 recipe templates. */ 1187 scmKmkSetInRecipe(pParser, true); 1063 1188 1064 1189 return scmKmkHandleSimple(pParser, offToken); … … 1069 1194 { 1070 1195 /* Leaving a define resets the recipt mode. */ 1071 pParser->fInRecipe = false;1196 scmKmkSetInRecipe(pParser, false); 1072 1197 1073 1198 return scmKmkHandleSimple(pParser, offToken); … … 1075 1200 1076 1201 1077 typedef enum KMKASSIGNTYPE 1078 { 1079 kKmkAssignType_Recursive, 1080 kKmkAssignType_Conditional, 1081 kKmkAssignType_Appending, 1082 kKmkAssignType_Prepending, 1083 kKmkAssignType_Simple, 1084 kKmkAssignType_Immediate 1085 } KMKASSIGNTYPE; 1086 1202 /** 1203 * Checks for escaped trailing slashes on a line, giving up and asking the 1204 * developer to fix those manually. 1205 * 1206 * @returns true if we gave up. false if no escaped slashed and we didn't. 1207 */ 1208 static bool scmKmkGiveUpIfTrailingEscapedSlashed(KMKPARSER *pParser, const char *pchLine, size_t cchLine) 1209 { 1210 if (cchLine > 2 && pchLine[cchLine - 2] == '\\' && pchLine[cchLine - 1] == '\\') 1211 { 1212 scmKmkGiveUp(pParser, "Escaped slashes at end of line not allowed. Insert space before line continuation slash!"); 1213 return true; 1214 } 1215 return false; 1216 } 1087 1217 1088 1218 /** … … 1106 1236 1107 1237 /* Assignments takes us out of recipe mode. */ 1108 pParser->fInRecipe = false; 1238 ScmVerbose(pParser->pState, 6, "%u: debug: assignment\n", ScmStreamTellLine(pParser->pIn)); 1239 scmKmkSetInRecipe(pParser, false); 1109 1240 1110 1241 /* 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!");1242 if (cLines > 1 && scmKmkGiveUpIfTrailingEscapedSlashed(pParser, pchLine, cchLine)) 1243 return false; 1113 1244 if (cchLine + 64 > sizeof(pParser->szBuf)) 1114 1245 return scmKmkGiveUp(pParser, "Line too long!"); … … 1153 1284 cchLine = pParser->cchLine; 1154 1285 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!");1286 if (iSubLine + 1 < cLines && scmKmkGiveUpIfTrailingEscapedSlashed(pParser, pchLine, cchLine)) 1287 return false; 1157 1288 1158 1289 /* Adjust offAssignOp: */ … … 1192 1323 break; 1193 1324 case kKmkAssignType_Prepending: 1194 *pszDst++ = ' >';1325 *pszDst++ = '<'; 1195 1326 *pszDst++ = '='; 1196 Assert(pchLine[offLine] == ' >'); Assert(pchLine[offLine + 1] == '=');1327 Assert(pchLine[offLine] == '<'); Assert(pchLine[offLine + 1] == '='); 1197 1328 offLine += 2; 1198 1329 break; … … 1371 1502 1372 1503 /* Following this, we'll be in recipe-mode. */ 1373 pParser->fInRecipe = true; 1504 ScmVerbose(pParser->pState, 4, "%u: debug: start rule\n", ScmStreamTellLine(pParser->pIn)); 1505 scmKmkSetInRecipe(pParser, true); 1374 1506 1375 1507 /* 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!");1508 if (cLines > 0 && scmKmkGiveUpIfTrailingEscapedSlashed(pParser, pchLine, cchLine)) 1509 return false; 1378 1510 1379 1511 /* Too special case. */ … … 1389 1521 /* 1390 1522 * Process word by word past the colon, taking new lines into account. 1391 * 1392 */1393 KMKWORDCTX enmCtx = kKmkWordCtx_TargetFileOrAssignment;1394 bool fPendingEol = false;1523 */ 1524 KMKWORDSTATE WordState = { 0, 0 }; 1525 KMKWORDCTX enmCtx = kKmkWordCtx_TargetFileOrAssignment; 1526 bool fPendingEol = false; 1395 1527 for (;;) 1396 1528 { … … 1398 1530 * Output the next word. 1399 1531 */ 1400 size_t cchWord = scmKmkWordLength(pchLine, cchLine, offLine, enmCtx );1532 size_t cchWord = scmKmkWordLength(pchLine, cchLine, offLine, enmCtx, &WordState); 1401 1533 Assert(offLine + cchWord <= offColon); 1402 1534 ScmStreamWrite(pOut, &pchLine[offLine], cchWord); … … 1432 1564 cchLine = pParser->cchLine; 1433 1565 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!");1566 if (iSubLine + 1 < cLines && scmKmkGiveUpIfTrailingEscapedSlashed(pParser, pchLine, cchLine)) 1567 return false; 1436 1568 1437 1569 /* Adjust offColon: */ … … 1460 1592 1461 1593 fPendingEol = true; 1462 break;1463 1594 } 1464 ScmStreamWrite(pOut, RT_STR_TUPLE(" \\")); 1465 ScmStreamPutEol(pOut, pParser->enmEol); 1466 ScmStreamWrite(pOut, g_szSpaces, cchIndent); 1595 else 1596 { 1597 ScmStreamWrite(pOut, RT_STR_TUPLE(" \\")); 1598 ScmStreamPutEol(pOut, pParser->enmEol); 1599 ScmStreamWrite(pOut, g_szSpaces, cchIndent); 1600 } 1601 break; 1467 1602 } 1468 1603 if (offLine >= offColon) … … 1476 1611 /* 1477 1612 * We're immediately past the colon now, so eat whitespace and newlines and 1478 * whatever till we get to a solid word .1613 * whatever till we get to a solid word or the end of the line. 1479 1614 */ 1480 1615 /* Skip spaces - there should be exactly one. */ … … 1492 1627 cchLine = pParser->cchLine; 1493 1628 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!");1629 if (iSubLine + 1 < cLines && scmKmkGiveUpIfTrailingEscapedSlashed(pParser, pchLine, cchLine)) 1630 return false; 1496 1631 1497 1632 /* Skip leading spaces. */ … … 1508 1643 * Special case: No dependencies. 1509 1644 */ 1510 if (offLine == cchLine && iSubLine >= cLines)1645 if (offLine == cchLine && iSubLine + 1 >= cLines) 1511 1646 { 1512 1647 ScmStreamPutEol(pOut, pParser->enmEol); … … 1534 1669 1535 1670 /* Get the next word and output it. */ 1536 size_t cchWord = scmKmkWordLength(pchLine, cchLine, offLine, enmCtx );1671 size_t cchWord = scmKmkWordLength(pchLine, cchLine, offLine, enmCtx, &WordState); 1537 1672 Assert(offLine + cchWord <= cchLine); 1538 1673 … … 1560 1695 cchLine = pParser->cchLine; 1561 1696 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!");1697 if (iSubLine + 1 < cLines && scmKmkGiveUpIfTrailingEscapedSlashed(pParser, pchLine, cchLine)) 1698 return false; 1564 1699 1565 1700 /* Skip leading spaces. */ … … 1682 1817 if (ch == '+') 1683 1818 return scmKmkHandleAssignment2(pParser, offWord, offEndPrev, kKmkAssignType_Appending, offLine, 0); 1684 if (ch == ' >')1819 if (ch == '<') 1685 1820 return scmKmkHandleAssignment2(pParser, offWord, offEndPrev, kKmkAssignType_Prepending, offLine, 0); 1686 1821 if (ch == '?') … … 1710 1845 else if (ch == '?') 1711 1846 enmType = kKmkAssignType_Conditional; 1712 else if (ch == ' >')1847 else if (ch == '<') 1713 1848 enmType = kKmkAssignType_Prepending; 1714 1849 else … … 1745 1880 1746 1881 /* 1882 * Check if this is a $(error ) or similar function call line. 1883 */ 1884 if ( pchLine[offWord] == '$' 1885 && pchLine[offWord + 1] == '(') 1886 { 1887 size_t const cchLine = pParser->cchLine; 1888 size_t offEnd = offWord + 2; 1889 char ch = '\0'; 1890 while (offEnd < cchLine && (RT_C_IS_LOWER(ch = pchLine[offEnd]) || RT_C_IS_DIGIT(ch) || ch == '-')) 1891 offEnd++; 1892 if (offEnd >= cchLine || RT_C_IS_SPACE(ch) || (offEnd == cchLine - 1 && ch == '\\')) 1893 { 1894 static const RTSTRTUPLE s_aAllowedFunctions[] = 1895 { 1896 { RT_STR_TUPLE("info") }, 1897 { RT_STR_TUPLE("error") }, 1898 { RT_STR_TUPLE("warning") }, 1899 { RT_STR_TUPLE("eval") }, 1900 { RT_STR_TUPLE("set-umask") }, 1901 }; 1902 size_t cchFunc = offEnd - offWord - 2; 1903 for (size_t i = 0; i < RT_ELEMENTS(s_aAllowedFunctions); i++) 1904 if ( cchFunc == s_aAllowedFunctions[i].cch 1905 && memcmp(&pchLine[offWord + 2], s_aAllowedFunctions[i].psz, cchFunc) == 0) 1906 return scmKmkHandleSimple(pParser, offWord); 1907 } 1908 } 1909 1910 /* 1747 1911 * If we didn't find anything, output it as-as. 1748 1912 * We use scmKmkHandleSimple in a special way to do this. 1749 1913 */ 1750 ScmVerbose(pParser->pState, 1, " debug: %u: Unable to make sense of this line!", pParser->iLine);1914 ScmVerbose(pParser->pState, 1, "%u: debug: Unable to make sense of this line!\n", ScmStreamTellLine(pParser->pIn)); 1751 1915 return scmKmkHandleSimple(pParser, 0 /*offToken*/, false /*fIndentIt*/); 1752 1916 } … … 1757 1921 { 1758 1922 /* Assignments takes us out of recipe mode. */ 1759 pParser->fInRecipe = false;1923 scmKmkSetInRecipe(pParser, false); 1760 1924 1761 1925 RT_NOREF(pParser, offToken, enmToken, cchWord, fMustBeAssignment); … … 1790 1954 Parser.iActualDepth = 0; 1791 1955 Parser.fInRecipe = false; 1792 Parser.iLine = 0;1793 1956 Parser.pState = pState; 1794 1957 Parser.pIn = pIn; … … 1803 1966 { 1804 1967 size_t cchLine = Parser.cchLine; 1805 Parser.iLine++;1806 1968 1807 1969 /* … … 1826 1988 offLine++; 1827 1989 1828 /* Find end of word (if any) : */1990 /* Find end of word (if any) - only looking for keywords here: */ 1829 1991 size_t cchWord = 0; 1830 1992 while ( offLine + cchWord < cchLine … … 1915 2077 } 1916 2078 } 2079 /* 2080 * Not keyword, check for assignment, rule or comment: 2081 */ 2082 else if (offLine < cchLine) 2083 { 2084 if (pchLine[offLine] != '#') 2085 { 2086 Parser.cLines = 1; 2087 Parser.cchTotalLine = cchLine; 2088 if (scmKmkIsLineWithContinuation(pchLine, cchLine)) 2089 Parser.cchTotalLine = scmKmkLineContinuationPeek(&Parser, &Parser.cLines, NULL); 2090 scmKmkHandleAssignmentOrRule(&Parser, offLine); 2091 continue; 2092 } 2093 /** @todo process comments. */ 2094 } 1917 2095 } 1918 2096
Note:
See TracChangeset
for help on using the changeset viewer.