Changeset 76451 in vbox
- Timestamp:
- Dec 25, 2018 1:40:08 AM (6 years ago)
- svn:sync-xref-src-repo-rev:
- 127752
- Location:
- trunk/src/bldprogs
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/bldprogs/scm.cpp
r72568 r76451 77 77 SCMOPT_FIX_TODOS, 78 78 SCMOPT_NO_FIX_TODOS, 79 SCMOPT_FIX_ERR_H, 80 SCMOPT_NO_FIX_ERR_H, 79 81 SCMOPT_UPDATE_COPYRIGHT_YEAR, 80 82 SCMOPT_NO_UPDATE_COPYRIGHT_YEAR, … … 174 176 /* .cMinBlankLinesBeforeFlowerBoxMakers = */ 2, 175 177 /* .fFixTodos = */ true, 178 /* .fFixErrH = */ false, ///@todo true, 176 179 /* .fUpdateCopyrightYear = */ false, 177 180 /* .fExternalCopyright = */ false, … … 214 217 { "--fix-todos", SCMOPT_FIX_TODOS, RTGETOPT_REQ_NOTHING }, 215 218 { "--no-fix-todos", SCMOPT_NO_FIX_TODOS, RTGETOPT_REQ_NOTHING }, 219 { "--fix-err-h", SCMOPT_FIX_ERR_H, RTGETOPT_REQ_NOTHING }, 220 { "--no-fix-err-h", SCMOPT_NO_FIX_ERR_H, RTGETOPT_REQ_NOTHING }, 216 221 { "--update-copyright-year", SCMOPT_UPDATE_COPYRIGHT_YEAR, RTGETOPT_REQ_NOTHING }, 217 222 { "--no-update-copyright-year", SCMOPT_NO_UPDATE_COPYRIGHT_YEAR, RTGETOPT_REQ_NOTHING }, … … 285 290 SCM_REWRITER_CFG(g_FixFlowerBoxMarkers, "fix-flower-boxes", rewrite_FixFlowerBoxMarkers); 286 291 SCM_REWRITER_CFG(g_Fix_C_and_CPP_Todos, "fix-c-todos", rewrite_Fix_C_and_CPP_Todos); 292 SCM_REWRITER_CFG(g_Fix_Err_H, "fix-err-h", rewrite_Fix_Err_H); 287 293 SCM_REWRITER_CFG(g_C_and_CPP, "c-and-cpp", rewrite_C_and_CPP); 288 294 … … 313 319 &g_FixFlowerBoxMarkers, 314 320 &g_Fix_C_and_CPP_Todos, 321 &g_Fix_Err_H, 315 322 &g_C_and_CPP, 316 323 }; … … 359 366 &g_FixFlowerBoxMarkers, 360 367 &g_Fix_C_and_CPP_Todos, 368 &g_Fix_Err_H, 361 369 &g_C_and_CPP 362 370 }; … … 1003 1011 case SCMOPT_NO_FIX_TODOS: 1004 1012 pSettings->fFixTodos = false; 1013 return VINF_SUCCESS; 1014 1015 case SCMOPT_FIX_ERR_H: 1016 pSettings->fFixErrH = true; 1017 return VINF_SUCCESS; 1018 case SCMOPT_NO_FIX_ERR_H: 1019 pSettings->fFixErrH = false; 1005 1020 return VINF_SUCCESS; 1006 1021 … … 2662 2677 RTPrintf(" Fix @todo statements so doxygen sees them. Default: %RTbool\n", g_Defaults.fFixTodos); 2663 2678 break; 2679 case SCMOPT_FIX_ERR_H: 2680 RTPrintf(" Fix err.h/errcore.h usage. Default: %RTbool\n", g_Defaults.fFixErrH); 2681 break; 2664 2682 case SCMOPT_UPDATE_COPYRIGHT_YEAR: 2665 2683 RTPrintf(" Update the copyright year. Default: %RTbool\n", g_Defaults.fUpdateCopyrightYear); -
trunk/src/bldprogs/scm.h
r70834 r76451 149 149 150 150 int ScmEnumerateComments(PSCMSTREAM pIn, SCMCOMMENTSTYLE enmCommentStyle, PFNSCMCOMMENTENUMERATOR pfnCallback, void *pvUser); 151 152 153 /** 154 * Include directive type. 155 */ 156 typedef enum SCMINCLUDEDIR 157 { 158 kScmIncludeDir_Invalid = 0, /**< Constomary invalid enum value. */ 159 kScmIncludeDir_Quoted, /**< \#include \"filename.h\" */ 160 kScmIncludeDir_Bracketed, /**< \#include \<filename.h\> */ 161 kScmIncludeDir_Macro, /**< \#include MACRO_H */ 162 kScmIncludeDir_End /**< End of valid enum values. */ 163 } SCMINCLUDEDIR; 164 165 SCMINCLUDEDIR ScmMaybeParseCIncludeLine(PSCMRWSTATE pState, const char *pchLine, size_t cchLine, 166 const char **ppchFilename, size_t *pcchFilename); 167 168 /** 169 * Checks if the given character is a valid C identifier lead character. 170 * 171 * @returns true / false. 172 * @param ch The character to inspect. 173 * @sa vbcppIsCIdentifierLeadChar 174 */ 175 DECLINLINE(bool) ScmIsCIdentifierLeadChar(char ch) 176 { 177 return RT_C_IS_ALPHA(ch) 178 || ch == '_'; 179 } 180 181 182 /** 183 * Checks if the given character is a valid C identifier character. 184 * 185 * @returns true / false. 186 * @param ch The character to inspect. 187 * @sa vbcppIsCIdentifierChar 188 */ 189 DECLINLINE(bool) scmIsCIdentifierChar(char ch) 190 { 191 return RT_C_IS_ALNUM(ch) 192 || ch == '_'; 193 } 194 151 195 152 196 /** @} */ … … 215 259 FNSCMREWRITER rewrite_FixFlowerBoxMarkers; 216 260 FNSCMREWRITER rewrite_Fix_C_and_CPP_Todos; 261 FNSCMREWRITER rewrite_Fix_Err_H; 217 262 FNSCMREWRITER rewrite_C_and_CPP; 218 263 … … 288 333 /** Whether to fix C/C++ todos. */ 289 334 bool fFixTodos; 335 /** Whether to fix C/C++ err.h/errcore.h usage. */ 336 bool fFixErrH; 290 337 291 338 /** Update the copyright year. */ -
trunk/src/bldprogs/scmrw.cpp
r73075 r76451 535 535 */ 536 536 return isBlankLineSlow(pchLine, cchLine); 537 } 538 539 540 /** 541 * Checks if there are @a cch blanks at @a pch. 542 * 543 * @returns true if span of @a cch blanks, false if not. 544 * @param pch The start of the span to check. 545 * @param cch The length of the span. 546 */ 547 DECLINLINE(bool) isSpanOfBlanks(const char *pch, size_t cch) 548 { 549 while (cch-- > 0) 550 { 551 char const ch = *pch++; 552 if (!RT_C_IS_BLANK(ch)) 553 return false; 554 } 555 return true; 537 556 } 538 557 … … 2299 2318 2300 2319 /** 2301 * Flower box marker comments in C and C++ code.2320 * Doxygen todos in C and C++ code. 2302 2321 * 2303 2322 * @returns true if modifications were made, false if not. 2323 * @param pState The rewriter state. 2304 2324 * @param pIn The input stream. 2305 2325 * @param pOut The output stream. … … 2385 2405 } 2386 2406 2407 2408 2409 /** 2410 * Tries to parse a C/C++ preprocessor include directive. 2411 * 2412 * This is resonably forgiving and expects sane input. 2413 * 2414 * @retval kScmIncludeDir_Invalid if not a valid include directive. 2415 * @retval kScmIncludeDir_Quoted 2416 * @retval kScmIncludeDir_Bracketed 2417 * @retval kScmIncludeDir_Macro 2418 * 2419 * @param pState The rewriter state (for repording malformed 2420 * directives). 2421 * @param pchLine The line to try parse as an include statement. 2422 * @param cchLine The line length. 2423 * @param ppchFilename Where to return the pointer to the filename part. 2424 * @param pcchFilename Where to return the length of the filename. 2425 */ 2426 SCMINCLUDEDIR ScmMaybeParseCIncludeLine(PSCMRWSTATE pState, const char *pchLine, size_t cchLine, 2427 const char **ppchFilename, size_t *pcchFilename) 2428 { 2429 /* Skip leading spaces: */ 2430 while (cchLine > 0 && RT_C_IS_BLANK(*pchLine)) 2431 cchLine--, pchLine++; 2432 2433 /* Check for '#': */ 2434 if (cchLine > 0 && *pchLine == '#') 2435 { 2436 cchLine--; 2437 pchLine++; 2438 2439 /* Skip spaces after '#' (optional): */ 2440 while (cchLine > 0 && RT_C_IS_BLANK(*pchLine)) 2441 cchLine--, pchLine++; 2442 2443 /* Check for 'include': */ 2444 static char const s_szInclude[] = "include"; 2445 if ( cchLine >= sizeof(s_szInclude) 2446 && memcmp(pchLine, RT_STR_TUPLE(s_szInclude)) == 0) 2447 { 2448 cchLine -= sizeof(s_szInclude) - 1; 2449 pchLine += sizeof(s_szInclude) - 1; 2450 2451 /* Skip spaces after 'include' word (optional): */ 2452 while (cchLine > 0 && RT_C_IS_BLANK(*pchLine)) 2453 cchLine--, pchLine++; 2454 if (cchLine > 0) 2455 { 2456 /* Quoted or bracketed? */ 2457 char const chFirst = *pchLine; 2458 if (chFirst == '"' || chFirst == '<') 2459 { 2460 cchLine--; 2461 pchLine++; 2462 const char *pchEnd = (const char *)memchr(pchLine, chFirst == '"' ? '"' : '>', cchLine); 2463 if (pchEnd) 2464 { 2465 if (ppchFilename) 2466 *ppchFilename = pchLine; 2467 if (pcchFilename) 2468 *pcchFilename = pchEnd - pchLine; 2469 return chFirst == '"' ? kScmIncludeDir_Quoted : kScmIncludeDir_Bracketed; 2470 } 2471 ScmError(pState, VERR_PARSE_ERROR, "Unbalanced #include filename %s: %.*s\n", 2472 chFirst == '"' ? "quotes" : "brackets" , cchLine, pchLine); 2473 } 2474 /* C prepreprocessor macro? */ 2475 else if (ScmIsCIdentifierLeadChar(chFirst)) 2476 { 2477 size_t cchFilename = 1; 2478 while ( cchFilename < cchLine 2479 && scmIsCIdentifierChar(pchLine[cchFilename])) 2480 cchFilename++; 2481 if (ppchFilename) 2482 *ppchFilename = pchLine; 2483 if (pcchFilename) 2484 *pcchFilename = cchFilename; 2485 return kScmIncludeDir_Macro; 2486 } 2487 else 2488 ScmError(pState, VERR_PARSE_ERROR, "Malformed #include filename part: %.*s\n", cchLine, pchLine); 2489 } 2490 else 2491 ScmError(pState, VERR_PARSE_ERROR, "Missing #include filename!\n"); 2492 } 2493 } 2494 2495 if (ppchFilename) 2496 *ppchFilename = NULL; 2497 if (pcchFilename) 2498 *pcchFilename = 0; 2499 return kScmIncludeDir_Invalid; 2500 } 2501 2502 2503 /** 2504 * Fix err.h/errcore.h usage. 2505 * 2506 * @returns true if modifications were made, false if not. 2507 * @param pIn The input stream. 2508 * @param pOut The output stream. 2509 * @param pSettings The settings. 2510 */ 2511 bool rewrite_Fix_Err_H(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 2512 { 2513 if (!pSettings->fFixErrH) 2514 return false; 2515 2516 static struct 2517 { 2518 const char *pszHeader; 2519 unsigned cchHeader; 2520 int iLevel; 2521 } const s_aHeaders[] 2522 { 2523 { RT_STR_TUPLE("iprt/errcore.h"), 1 }, 2524 { RT_STR_TUPLE("iprt/err.h"), 2 }, 2525 { RT_STR_TUPLE("VBox/err.h"), 3 }, 2526 }; 2527 static RTSTRTUPLE const g_aLevel1Statuses[] = /* Note! Keep in sync with errcore.h content! */ 2528 { 2529 RT_STR_TUPLE("VINF_SUCCESS"), 2530 RT_STR_TUPLE("VERR_GENERAL_FAILURE"), 2531 RT_STR_TUPLE("VERR_INVALID_PARAMETER"), 2532 RT_STR_TUPLE("VWRN_INVALID_PARAMETER"), 2533 RT_STR_TUPLE("VERR_INVALID_MAGIC"), 2534 RT_STR_TUPLE("VWRN_INVALID_MAGIC"), 2535 RT_STR_TUPLE("VERR_INVALID_HANDLE"), 2536 RT_STR_TUPLE("VWRN_INVALID_HANDLE"), 2537 RT_STR_TUPLE("VERR_INVALID_POINTER"), 2538 RT_STR_TUPLE("VERR_NO_MEMORY"), 2539 RT_STR_TUPLE("VERR_PERMISSION_DENIED"), 2540 RT_STR_TUPLE("VINF_PERMISSION_DENIED"), 2541 RT_STR_TUPLE("VERR_VERSION_MISMATCH"), 2542 RT_STR_TUPLE("VERR_NOT_IMPLEMENTED"), 2543 RT_STR_TUPLE("VERR_INVALID_FLAGS"), 2544 RT_STR_TUPLE("VERR_WRONG_ORDER"), 2545 RT_STR_TUPLE("VERR_INVALID_FUNCTION"), 2546 RT_STR_TUPLE("VERR_NOT_SUPPORTED"), 2547 RT_STR_TUPLE("VINF_NOT_SUPPORTED"), 2548 RT_STR_TUPLE("VERR_ACCESS_DENIED"), 2549 RT_STR_TUPLE("VERR_INTERRUPTED"), 2550 RT_STR_TUPLE("VINF_INTERRUPTED"), 2551 RT_STR_TUPLE("VERR_TIMEOUT"), 2552 RT_STR_TUPLE("VINF_TIMEOUT"), 2553 RT_STR_TUPLE("VERR_BUFFER_OVERFLOW"), 2554 RT_STR_TUPLE("VINF_BUFFER_OVERFLOW"), 2555 RT_STR_TUPLE("VERR_TOO_MUCH_DATA"), 2556 RT_STR_TUPLE("VERR_TRY_AGAIN"), 2557 RT_STR_TUPLE("VINF_TRY_AGAIN"), 2558 RT_STR_TUPLE("VERR_PARSE_ERROR"), 2559 RT_STR_TUPLE("VERR_OUT_OF_RANGE"), 2560 RT_STR_TUPLE("VERR_NUMBER_TOO_BIG"), 2561 RT_STR_TUPLE("VWRN_NUMBER_TOO_BIG"), 2562 RT_STR_TUPLE("VERR_CANCELLED"), 2563 RT_STR_TUPLE("VERR_TRAILING_CHARS"), 2564 RT_STR_TUPLE("VWRN_TRAILING_CHARS"), 2565 RT_STR_TUPLE("VERR_TRAILING_SPACES"), 2566 RT_STR_TUPLE("VWRN_TRAILING_SPACES"), 2567 RT_STR_TUPLE("VERR_NOT_FOUND"), 2568 RT_STR_TUPLE("VWRN_NOT_FOUND"), 2569 RT_STR_TUPLE("VERR_INVALID_STATE"), 2570 RT_STR_TUPLE("VWRN_INVALID_STATE"), 2571 RT_STR_TUPLE("VERR_OUT_OF_RESOURCES"), 2572 RT_STR_TUPLE("VWRN_OUT_OF_RESOURCES"), 2573 RT_STR_TUPLE("VERR_END_OF_STRING"), 2574 RT_STR_TUPLE("VERR_CALLBACK_RETURN"), 2575 RT_STR_TUPLE("VINF_CALLBACK_RETURN"), 2576 RT_STR_TUPLE("VERR_DUPLICATE"), 2577 RT_STR_TUPLE("VERR_MISSING"), 2578 RT_STR_TUPLE("VERR_BUFFER_UNDERFLOW"), 2579 RT_STR_TUPLE("VINF_BUFFER_UNDERFLOW"), 2580 RT_STR_TUPLE("VERR_NOT_AVAILABLE"), 2581 RT_STR_TUPLE("VERR_MISMATCH"), 2582 RT_STR_TUPLE("VERR_WRONG_TYPE"), 2583 RT_STR_TUPLE("VWRN_WRONG_TYPE"), 2584 RT_STR_TUPLE("VERR_WRONG_PARAMETER_COUNT"), 2585 RT_STR_TUPLE("VERR_WRONG_PARAMETER_TYPE"), 2586 RT_STR_TUPLE("VERR_INVALID_CLIENT_ID"), 2587 RT_STR_TUPLE("VERR_INVALID_SESSION_ID"), 2588 RT_STR_TUPLE("VERR_INCOMPATIBLE_CONFIG"), 2589 RT_STR_TUPLE("VERR_INTERNAL_ERROR"), 2590 RT_STR_TUPLE("VINF_GETOPT_NOT_OPTION"), 2591 RT_STR_TUPLE("VERR_GETOPT_UNKNOWN_OPTION"), 2592 }; 2593 2594 /* 2595 * First pass: Scout #include err.h/errcore.h locations and usage. 2596 * 2597 * Note! This isn't entirely optimal since it's also parsing comments and 2598 * strings, not just code. However it does a decent job for now. 2599 */ 2600 int iIncludeLevel = 0; 2601 int iUsageLevel = 0; 2602 uint32_t iLine = 0; 2603 SCMEOL enmEol; 2604 size_t cchLine; 2605 const char *pchLine; 2606 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL) 2607 { 2608 iLine++; 2609 if (cchLine < 6) 2610 continue; 2611 2612 /* 2613 * Look for #includes. 2614 */ 2615 const char *pchHash = (const char *)memchr(pchLine, '#', cchLine); 2616 if ( pchHash 2617 && isSpanOfBlanks(pchLine, pchHash - pchLine)) 2618 { 2619 const char *pchFilename; 2620 size_t cchFilename; 2621 SCMINCLUDEDIR enmIncDir = ScmMaybeParseCIncludeLine(pState, pchLine, cchLine, &pchFilename, &cchFilename); 2622 if ( enmIncDir == kScmIncludeDir_Bracketed 2623 || enmIncDir == kScmIncludeDir_Quoted) 2624 { 2625 unsigned i = RT_ELEMENTS(s_aHeaders); 2626 while (i-- > 0) 2627 if ( s_aHeaders[i].cchHeader == cchFilename 2628 && RTStrNICmpAscii(pchFilename, s_aHeaders[i].pszHeader, cchFilename) == 0) 2629 { 2630 if (iIncludeLevel < s_aHeaders[i].iLevel) 2631 iIncludeLevel = s_aHeaders[i].iLevel; 2632 break; 2633 } 2634 2635 /* Special hack for error info. */ 2636 if (cchFilename == sizeof("errmsgdata.h") - 1 && memcmp(pchFilename, RT_STR_TUPLE("errmsgdata.h")) == 0) 2637 iUsageLevel = 3; 2638 continue; 2639 } 2640 } 2641 /* 2642 * Look for VERR_, VWRN_, VINF_ prefixed identifiers in the current line. 2643 */ 2644 const char *pchHit = (const char *)memchr(pchLine, 'V', cchLine); 2645 if (pchHit) 2646 { 2647 const char *pchLeft = pchLine; 2648 size_t cchLeft = cchLine; 2649 do 2650 { 2651 size_t cchLeftHit = &pchLeft[cchLeft] - pchHit; 2652 if (cchLeftHit < 6) 2653 break; 2654 if ( pchHit[4] == '_' 2655 && ( pchHit == pchLine 2656 || !scmIsCIdentifierChar(pchHit[-1])) 2657 && ( (pchHit[1] == 'E' && pchHit[2] == 'R' && pchHit[3] == 'R') 2658 || (pchHit[1] == 'W' && pchHit[2] == 'R' && pchHit[3] == 'N') 2659 || (pchHit[1] == 'I' && pchHit[2] == 'N' && pchHit[3] == 'F') ) ) 2660 { 2661 size_t cchIdentifier = 5; 2662 while (cchIdentifier < cchLeftHit && scmIsCIdentifierChar(pchHit[cchIdentifier])) 2663 cchIdentifier++; 2664 ScmVerbose(pState, 4, "--- status code at %u col %zu: %.*s\n", 2665 iLine, pchHit - pchLine, cchIdentifier, pchHit); 2666 2667 if (iUsageLevel <= 1) 2668 { 2669 iUsageLevel = 2; 2670 for (unsigned i = 0; i < RT_ELEMENTS(g_aLevel1Statuses); i++) 2671 if ( cchIdentifier == g_aLevel1Statuses[i].cch 2672 && memcmp(pchHit, g_aLevel1Statuses[i].psz, cchIdentifier) == 0) 2673 { 2674 iUsageLevel = 1; 2675 break; 2676 } 2677 } 2678 2679 pchLeft = pchHit + cchIdentifier; 2680 cchLeft = cchLeftHit - cchIdentifier; 2681 } 2682 else 2683 { 2684 pchLeft = pchHit + 1; 2685 cchLeft = cchLeftHit - 1; 2686 } 2687 pchHit = (const char *)memchr(pchLeft, 'V', cchLeft); 2688 } while (pchHit != NULL); 2689 } 2690 } 2691 ScmVerbose(pState, 3, "--- iIncludeLevel=%d iUsageLevel=%d\n", iIncludeLevel, iUsageLevel); 2692 2693 /* 2694 * Second pass: Change err.h to errcore.h if we detected a need for change. 2695 */ 2696 if (iIncludeLevel <= iUsageLevel) 2697 return false; 2698 2699 unsigned cChanges = 0; 2700 ScmStreamRewindForReading(pIn); 2701 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL) 2702 { 2703 /* 2704 * Look for #includes to modify. 2705 */ 2706 if (cchLine >= 6) 2707 { 2708 const char *pchHash = (const char *)memchr(pchLine, '#', cchLine); 2709 if ( pchHash 2710 && isSpanOfBlanks(pchLine, pchHash - pchLine)) 2711 { 2712 const char *pchFilename; 2713 size_t cchFilename; 2714 SCMINCLUDEDIR enmIncDir = ScmMaybeParseCIncludeLine(pState, pchLine, cchLine, &pchFilename, &cchFilename); 2715 if ( enmIncDir == kScmIncludeDir_Bracketed 2716 || enmIncDir == kScmIncludeDir_Quoted) 2717 { 2718 unsigned i = RT_ELEMENTS(s_aHeaders); 2719 while (i-- > 0) 2720 if ( s_aHeaders[i].cchHeader == cchFilename 2721 && RTStrNICmpAscii(pchFilename, s_aHeaders[i].pszHeader, cchFilename) == 0) 2722 { 2723 ScmStreamWrite(pOut, pchLine, pchFilename - pchLine - 1); 2724 ScmStreamWrite(pOut, RT_STR_TUPLE("<iprt/errcore.h>")); 2725 size_t cchTrailing = &pchLine[cchLine] - &pchFilename[cchFilename + 1]; 2726 if (cchTrailing > 0) 2727 ScmStreamWrite(pOut, &pchFilename[cchFilename + 1], cchTrailing); 2728 ScmStreamPutEol(pOut, enmEol); 2729 cChanges++; 2730 pchLine = NULL; 2731 break; 2732 } 2733 if (!pchLine) 2734 continue; 2735 } 2736 } 2737 } 2738 2739 int rc = ScmStreamPutLine(pOut, pchLine, cchLine, enmEol); 2740 if (RT_FAILURE(rc)) 2741 return false; 2742 } 2743 ScmVerbose(pState, 2, " * Converted %zu err.h/errcore.h include statements.\n", cChanges); 2744 return true; 2745 } 2387 2746 2388 2747 /** -
trunk/src/bldprogs/scmstream.cpp
r69500 r76451 1280 1280 const char *pchLine = ScmStreamGetLine(pSrc, &cchLine, &enmEol); 1281 1281 if (!pchLine) 1282 return pDst->rc = (RT_FAILURE(pSrc->rc) ? pSrc->rc : VERR_EOF);1282 return pDst->rc = RT_FAILURE(pSrc->rc) ? pSrc->rc : VERR_EOF; 1283 1283 1284 1284 int rc = ScmStreamPutLine(pDst, pchLine, cchLine, enmEol);
Note:
See TracChangeset
for help on using the changeset viewer.