Changeset 69166 in vbox for trunk/src/bldprogs/scmrw.cpp
- Timestamp:
- Oct 23, 2017 3:43:33 PM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/bldprogs/scmrw.cpp
r63568 r69166 39 39 40 40 41 /********************************************************************************************************************************* 42 * Structures and Typedefs * 43 *********************************************************************************************************************************/ 44 /** License types. */ 45 typedef enum SCMLICENSETYPE 46 { 47 kScmLicenseType_Invalid = 0, 48 kScmLicenseType_OseGpl, 49 kScmLicenseType_OseDualGplCddl, 50 kScmLicenseType_VBoxLgpl, 51 kScmLicenseType_Mit, 52 kScmLicenseType_Confidential 53 } SCMLICENSETYPE; 54 55 /** A license. */ 56 typedef struct SCMLICENSETEXT 57 { 58 /** The license type. */ 59 SCMLICENSETYPE enmType; 60 /** The license option. */ 61 SCMLICENSE enmOpt; 62 /** The license text. */ 63 const char *psz; 64 /** The license text length. */ 65 size_t cch; 66 } SCMLICENSETEXT; 67 /** Pointer to a license. */ 68 typedef SCMLICENSETEXT const *PCSCMLICENSETEXT; 69 70 /** 71 * Copyright + license rewriter state. 72 */ 73 typedef struct SCMCOPYRIGHTINFO 74 { 75 /** State. */ 76 PSCMRWSTATE pState; /**< input */ 77 /** The comment style (neede for C/C++). */ 78 SCMCOMMENTSTYLE enmCommentStyle; /**< input */ 79 80 /** @name Common info 81 * @{ */ 82 uint32_t iLineComment; 83 uint32_t cLinesComment; /**< This excludes any external license lines. */ 84 /** @} */ 85 86 /** @name Copyright info 87 * @{ */ 88 uint32_t iLineCopyright; 89 uint32_t uFirstYear; 90 uint32_t uLastYear; 91 bool fWellFormedCopyright; 92 bool fUpToDateCopyright; 93 /** @} */ 94 95 /** @name License info 96 * @{ */ 97 bool fOpenSource; /**< input */ 98 PCSCMLICENSETEXT pExpectedLicense; /**< input */ 99 PCSCMLICENSETEXT paLicenses; /**< input */ 100 SCMLICENSE enmLicenceOpt; /**< input */ 101 uint32_t iLineLicense; 102 uint32_t cLinesLicense; 103 PCSCMLICENSETEXT pCurrentLicense; 104 bool fIsCorrectLicense; 105 bool fWellFormedLicense; 106 bool fExternalLicense; 107 /** @} */ 108 109 } SCMCOPYRIGHTINFO; 110 typedef SCMCOPYRIGHTINFO *PSCMCOPYRIGHTINFO; 111 112 113 /********************************************************************************************************************************* 114 * Global Variables * 115 *********************************************************************************************************************************/ 116 /** --license-ose-gpl */ 117 static const char g_szVBoxOseGpl[] = 118 "This file is part of VirtualBox Open Source Edition (OSE), as\n" 119 "available from http://www.virtualbox.org. This file is free software;\n" 120 "you can redistribute it and/or modify it under the terms of the GNU\n" 121 "General Public License (GPL) as published by the Free Software\n" 122 "Foundation, in version 2 as it comes in the \"COPYING\" file of the\n" 123 "VirtualBox OSE distribution. VirtualBox OSE is distributed in the\n" 124 "hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.\n"; 125 126 /** --license-ose-dual */ 127 static const char g_szVBoxOseDualGplCddl[] = 128 "This file is part of VirtualBox Open Source Edition (OSE), as\n" 129 "available from http://www.virtualbox.org. This file is free software;\n" 130 "you can redistribute it and/or modify it under the terms of the GNU\n" 131 "General Public License (GPL) as published by the Free Software\n" 132 "Foundation, in version 2 as it comes in the \"COPYING\" file of the\n" 133 "VirtualBox OSE distribution. VirtualBox OSE is distributed in the\n" 134 "hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.\n" 135 "\n" 136 "The contents of this file may alternatively be used under the terms\n" 137 "of the Common Development and Distribution License Version 1.0\n" 138 "(CDDL) only, as it comes in the \"COPYING.CDDL\" file of the\n" 139 "VirtualBox OSE distribution, in which case the provisions of the\n" 140 "CDDL are applicable instead of those of the GPL.\n" 141 "\n" 142 "You may elect to license modified versions of this file under the\n" 143 "terms and conditions of either the GPL or the CDDL or both.\n"; 144 145 /** --license-lgpl */ 146 static const char g_szVBoxLgpl[] = 147 "This file is part of a free software library; you can redistribute\n" 148 "it and/or modify it under the terms of the GNU Lesser General\n" 149 "Public License version 2.1 as published by the Free Software\n" 150 "Foundation and shipped in the \"COPYING\" file with this library.\n" 151 "The library is distributed in the hope that it will be useful,\n" 152 "but WITHOUT ANY WARRANTY of any kind.\n" 153 "\n" 154 "Oracle LGPL Disclaimer: For the avoidance of doubt, except that if\n" 155 "any license choice other than GPL or LGPL is available it will\n" 156 "apply instead, Oracle elects to use only the Lesser General Public\n" 157 "License version 2.1 (LGPLv2) at this time for any software where\n" 158 "a choice of LGPL license versions is made available with the\n" 159 "language indicating that LGPLv2 or any later version may be used,\n" 160 "or where a choice of which version of the LGPL is applied is\n" 161 "otherwise unspecified.\n"; 162 163 /** --license-mit 164 * @note This isn't detectable as VirtualBox or Oracle specific. */ 165 static const char g_szMit[] = 166 "Permission is hereby granted, free of charge, to any person\n" 167 "obtaining a copy of this software and associated documentation\n" 168 "files (the \"Software\"), to deal in the Software without\n" 169 "restriction, including without limitation the rights to use,\n" 170 "copy, modify, merge, publish, distribute, sublicense, and/or sell\n" 171 "copies of the Software, and to permit persons to whom the\n" 172 "Software is furnished to do so, subject to the following\n" 173 "conditions:\n" 174 "\n" 175 "The above copyright notice and this permission notice shall be\n" 176 "included in all copies or substantial portions of the Software.\n" 177 "\n" 178 "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\n" 179 "EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\n" 180 "OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\n" 181 "NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\n" 182 "HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\n" 183 "WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n" 184 "FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\n" 185 "OTHER DEALINGS IN THE SOFTWARE.\n"; 186 187 /** Oracle confidential. */ 188 static const char g_szOracleConfidential[] = 189 "Oracle Corporation confidential\n" 190 "All rights reserved\n"; 191 192 /** Licenses to detect when --license-mit isn't used. */ 193 static const SCMLICENSETEXT g_aLicenses[] = 194 { 195 { kScmLicenseType_OseGpl, kScmLicense_OseGpl, RT_STR_TUPLE(g_szVBoxOseGpl)}, 196 { kScmLicenseType_OseDualGplCddl, kScmLicense_OseDualGplCddl, RT_STR_TUPLE(g_szVBoxOseDualGplCddl) }, 197 { kScmLicenseType_VBoxLgpl, kScmLicense_Lgpl, RT_STR_TUPLE(g_szVBoxLgpl)}, 198 { kScmLicenseType_Confidential, kScmLicense_End, RT_STR_TUPLE(g_szOracleConfidential) }, 199 { kScmLicenseType_Invalid, kScmLicense_End, NULL, 0 }, 200 }; 201 202 /** Licenses to detect when --license-mit _is_ used. */ 203 static const SCMLICENSETEXT g_aLicensesWithMit[] = 204 { 205 { kScmLicenseType_OseGpl, kScmLicense_OseGpl, RT_STR_TUPLE(g_szVBoxOseGpl)}, 206 { kScmLicenseType_OseDualGplCddl, kScmLicense_OseDualGplCddl, RT_STR_TUPLE(g_szVBoxOseDualGplCddl) }, 207 { kScmLicenseType_VBoxLgpl, kScmLicense_Lgpl, RT_STR_TUPLE(g_szVBoxLgpl)}, 208 { kScmLicenseType_Mit, kScmLicense_Mit, RT_STR_TUPLE(g_szMit) }, 209 { kScmLicenseType_Confidential, kScmLicense_End, RT_STR_TUPLE(g_szOracleConfidential) }, 210 { kScmLicenseType_Invalid, kScmLicense_End, NULL, 0 }, 211 }; 212 213 /** Copyright holder. */ 214 static const char g_szCopyrightHolder[] = "Oracle Corporation"; 215 216 217 /** Copyright+license comment start for each SCMCOMMENTSTYLE. */ 218 static RTSTRTUPLE const g_aCopyrightCommentStart[] = 219 { 220 RT_STR_TUPLE("<invalid> "), 221 RT_STR_TUPLE("/*"), 222 RT_STR_TUPLE("#"), 223 RT_STR_TUPLE("\"\"\""), 224 RT_STR_TUPLE(";"), 225 RT_STR_TUPLE("REM"), 226 RT_STR_TUPLE("rem"), 227 RT_STR_TUPLE("Rem"), 228 RT_STR_TUPLE("<end>"), 229 }; 230 231 /** Copyright+license line prefix for each SCMCOMMENTSTYLE. */ 232 static RTSTRTUPLE const g_aCopyrightCommentPrefix[] = 233 { 234 RT_STR_TUPLE("<invalid> "), 235 RT_STR_TUPLE(" * "), 236 RT_STR_TUPLE("# "), 237 RT_STR_TUPLE(""), 238 RT_STR_TUPLE("; "), 239 RT_STR_TUPLE("REM "), 240 RT_STR_TUPLE("rem "), 241 RT_STR_TUPLE("Rem "), 242 RT_STR_TUPLE("<end>"), 243 }; 244 245 /** Copyright+license empty line for each SCMCOMMENTSTYLE. */ 246 static RTSTRTUPLE const g_aCopyrightCommentEmpty[] = 247 { 248 RT_STR_TUPLE("<invalid>"), 249 RT_STR_TUPLE(" *"), 250 RT_STR_TUPLE("#"), 251 RT_STR_TUPLE(""), 252 RT_STR_TUPLE(";"), 253 RT_STR_TUPLE("REM"), 254 RT_STR_TUPLE("rem"), 255 RT_STR_TUPLE("Rem"), 256 RT_STR_TUPLE("<end>"), 257 }; 258 259 /** Copyright+license end of comment for each SCMCOMMENTSTYLE. */ 260 static RTSTRTUPLE const g_aCopyrightCommentEnd[] = 261 { 262 RT_STR_TUPLE("<invalid> "), 263 RT_STR_TUPLE(" */"), 264 RT_STR_TUPLE("#"), 265 RT_STR_TUPLE("\"\"\""), 266 RT_STR_TUPLE(";"), 267 RT_STR_TUPLE("REM"), 268 RT_STR_TUPLE("rem"), 269 RT_STR_TUPLE("Rem"), 270 RT_STR_TUPLE("<end>"), 271 }; 272 273 274 /** 275 * Figures out the predominant casing of the "REM" keyword in a batch file. 276 * 277 * @returns Predominant comment style. 278 * @param pIn The file to scan. Will be rewound. 279 */ 280 static SCMCOMMENTSTYLE determinBatchFileCommentStyle(PSCMSTREAM pIn) 281 { 282 /* 283 * Figure out whether it's using upper or lower case REM comments before 284 * doing the work. 285 */ 286 uint32_t cUpper = 0; 287 uint32_t cLower = 0; 288 uint32_t cCamel = 0; 289 SCMEOL enmEol; 290 size_t cchLine; 291 const char *pchLine; 292 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL) 293 { 294 while ( cchLine > 2 295 && RT_C_IS_SPACE(*pchLine)) 296 { 297 pchLine++; 298 cchLine--; 299 } 300 if ( ( cchLine > 3 301 && RT_C_IS_SPACE(pchLine[2])) 302 || cchLine == 3) 303 { 304 if ( pchLine[0] == 'R' 305 && pchLine[1] == 'E' 306 && pchLine[2] == 'M') 307 cUpper++; 308 else if ( pchLine[0] == 'r' 309 && pchLine[1] == 'e' 310 && pchLine[2] == 'm') 311 cLower++; 312 else if ( pchLine[0] == 'R' 313 && pchLine[1] == 'e' 314 && pchLine[2] == 'm') 315 cCamel++; 316 } 317 } 318 319 ScmStreamRewindForReading(pIn); 320 321 if (cLower >= cUpper && cLower >= cCamel) 322 return kScmCommentStyle_Rem_Lower; 323 if (cCamel >= cLower && cCamel >= cUpper) 324 return kScmCommentStyle_Rem_Camel; 325 return kScmCommentStyle_Rem_Upper; 326 } 327 328 41 329 /** 42 330 * Worker for isBlankLine. … … 229 517 int rc2 = ScmSvnSetProperty(pState, "svn:eol-style", pszDesiredSvnEol); 230 518 if (RT_FAILURE(rc2)) 231 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc2); /** @todo propagate the error somehow... */519 ScmError(pState, rc2, "ScmSvnSetProperty: %Rrc\n", rc2); 232 520 } 233 521 if (RT_SUCCESS(rc)) … … 374 662 rc = ScmSvnDelProperty(pState, "svn:executable"); 375 663 if (RT_FAILURE(rc)) 376 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */664 ScmError(pState, rc, "ScmSvnSetProperty: %Rrc\n", rc); 377 665 } 378 666 return false; … … 412 700 rc = ScmSvnSetProperty(pState, "svn:keywords", pszKeywords); 413 701 if (RT_FAILURE(rc)) 414 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */702 ScmError(pState, rc, "ScmSvnSetProperty: %Rrc\n", rc); 415 703 } 416 704 else 417 RTMsgError("RTStrAppend: %Rrc\n", rc); /** @todo error propagation here.. */705 ScmError(pState, rc, "RTStrAppend: %Rrc\n", rc); 418 706 RTStrFree(pszKeywords); 419 707 } … … 423 711 rc = ScmSvnSetProperty(pState, "svn:keywords", "Id Revision"); 424 712 if (RT_FAILURE(rc)) 425 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */713 ScmError(pState, rc, "ScmSvnSetProperty: %Rrc\n", rc); 426 714 } 427 715 else if (RT_SUCCESS(rc)) … … 430 718 return false; 431 719 } 720 721 722 /** 723 * Compares two strings word-by-word, ignoring spaces, punctuation and case. 724 * 725 * Assumes ASCII strings. 726 * 727 * @returns true if they match, false if not. 728 * @param psz1 The first string. This is typically the known one. 729 * @param psz2 The second string. This is typically the unknown one, 730 * which is why we return a next pointer for this one. 731 * @param ppsz2Next Where to return the next part of the 2nd string. If 732 * this is NULL, the whole string must match. 733 */ 734 static bool IsEqualWordByWordIgnoreCase(const char *psz1, const char *psz2, const char **ppsz2Next) 735 { 736 for (;;) 737 { 738 /* Try compare raw strings first. */ 739 char ch1 = *psz1; 740 char ch2 = *psz2; 741 if ( ch1 == ch2 742 || RT_C_TO_LOWER(ch1) == RT_C_TO_LOWER(ch2)) 743 { 744 if (ch1) 745 { 746 psz1++; 747 psz2++; 748 } 749 else 750 return true; 751 } 752 else 753 { 754 /* Try skip spaces an punctuation. */ 755 while ( RT_C_IS_SPACE(ch1) 756 || RT_C_IS_PUNCT(ch1)) 757 ch1 = *++psz1; 758 759 if (ch1 == '\0' && ppsz2Next) 760 { 761 *ppsz2Next = psz2; 762 return true; 763 } 764 765 while ( RT_C_IS_SPACE(ch2) 766 || RT_C_IS_PUNCT(ch2)) 767 ch2 = *++psz2; 768 769 if ( ch1 != ch2 770 && RT_C_TO_LOWER(ch1) != RT_C_TO_LOWER(ch2)) 771 return false; 772 } 773 } 774 } 775 776 777 /** 778 * Counts the number of lines in the given substring. 779 * 780 * @returns The number of lines. 781 * @param psz The start of the substring. 782 * @param cch The length of the substring. 783 */ 784 static uint32_t CountLinesInSubstring(const char *psz, size_t cch) 785 { 786 uint32_t cLines = 0; 787 for (;;) 788 { 789 const char *pszEol = (const char *)memchr(psz, '\n', cch); 790 if (pszEol) 791 cLines++; 792 else 793 return cLines + (*psz != '\0'); 794 cch -= pszEol + 1 - psz; 795 if (!cch) 796 return cLines; 797 psz = pszEol + 1; 798 } 799 } 800 801 802 /** 803 * Comment parser callback for locating copyright and license. 804 */ 805 static DECLCALLBACK(int) 806 rewrite_Copyright_CommentCallback(PCSCMCOMMENTINFO pInfo, const char *pszBody, size_t cchBody, void *pvUser) 807 { 808 PSCMCOPYRIGHTINFO pState = (PSCMCOPYRIGHTINFO)pvUser; 809 Assert(strlen(pszBody) == cchBody); 810 //RTPrintf("--- comment at %u, type %u ---\n%s\n--- end ---\n", pInfo->iLineStart, pInfo->enmType, pszBody); 811 ScmVerbose(pState->pState, 2, 812 "--- comment at %u col %u, %u lines, type %u, %u lines before body, %u lines after body\n", 813 pInfo->iLineStart, pInfo->offStart, pInfo->iLineEnd - pInfo->iLineStart + 1, pInfo->enmType, 814 pInfo->cBlankLinesBefore, pInfo->cBlankLinesAfter); 815 816 uint32_t iLine = pInfo->iLineStart + pInfo->cBlankLinesBefore; 817 818 /* 819 * Since the license statement is typically prefaced by a copyright line. 820 */ 821 bool fFoundCopyright = false; 822 uint32_t cBlankLinesAfterCopyright = 0; 823 if ( pState->iLineCopyright == UINT32_MAX 824 && cchBody > sizeof("Copyright") + sizeof(g_szCopyrightHolder) 825 && RTStrNICmp(pszBody, RT_STR_TUPLE("copyright")) == 0) 826 { 827 const char *pszNextLine = (const char *)memchr(pszBody, '\n', cchBody); 828 829 /* Oracle copyright? */ 830 const char *pszEnd = pszNextLine ? pszNextLine : &pszBody[cchBody]; 831 while (RT_C_IS_SPACE(pszEnd[-1])) 832 pszEnd--; 833 if ( (pszEnd - pszBody) > sizeof(g_szCopyrightHolder) 834 && RTStrNICmp(pszEnd - sizeof(g_szCopyrightHolder) + 1, RT_STR_TUPLE(g_szCopyrightHolder)) == 0) 835 { 836 /* Parse out the year(s). */ 837 const char *psz = pszBody + sizeof("copyright"); 838 while ((uintptr_t)psz < (uintptr_t)pszEnd && !RT_C_IS_DIGIT(*psz)) 839 psz++; 840 if (RT_C_IS_DIGIT(*psz)) 841 { 842 char *pszNext; 843 int rc = RTStrToUInt32Ex(psz, &pszNext, 10, &pState->uFirstYear); 844 if ( RT_SUCCESS(rc) 845 && rc != VWRN_NUMBER_TOO_BIG 846 && rc != VWRN_NEGATIVE_UNSIGNED) 847 { 848 if ( pState->uFirstYear < 1975 849 || pState->uFirstYear > 3000) 850 { 851 ScmError(pState->pState, VERR_OUT_OF_RANGE, "Copyright year is out of range: %u ('%.*s')\n", 852 pState->uFirstYear, pszEnd - pszBody, pszBody); 853 pState->uFirstYear = UINT32_MAX; 854 } 855 856 while (RT_C_IS_SPACE(*pszNext)) 857 pszNext++; 858 if (*pszNext == '-') 859 { 860 do 861 pszNext++; 862 while (RT_C_IS_SPACE(*pszNext)); 863 rc = RTStrToUInt32Ex(pszNext, &pszNext, 10, &pState->uLastYear); 864 if ( RT_SUCCESS(rc) 865 && rc != VWRN_NUMBER_TOO_BIG 866 && rc != VWRN_NEGATIVE_UNSIGNED) 867 { 868 if ( pState->uLastYear < 1975 869 || pState->uLastYear > 3000) 870 { 871 ScmError(pState->pState, VERR_OUT_OF_RANGE, "Second copyright year is out of range: %u ('%.*s')\n", 872 pState->uLastYear, pszEnd - pszBody, pszBody); 873 pState->uLastYear = UINT32_MAX; 874 } 875 else if (pState->uFirstYear > pState->uLastYear) 876 { 877 RTMsgWarning("Copyright years switched(?): '%.*s'\n", pszEnd - pszBody, pszBody); 878 uint32_t iTmp = pState->uLastYear; 879 pState->uLastYear = pState->uFirstYear; 880 pState->uFirstYear = iTmp; 881 } 882 } 883 else 884 { 885 pState->uLastYear = UINT32_MAX; 886 ScmError(pState->pState, RT_SUCCESS(rc) ? -rc : rc, 887 "Failed to parse second copyright year: '%.*s'\n", pszEnd - pszBody, pszBody); 888 } 889 } 890 else if (*pszNext != g_szCopyrightHolder[0]) 891 ScmError(pState->pState, VERR_PARSE_ERROR, 892 "Failed to parse copyright: '%.*s'\n", pszEnd - pszBody, pszBody); 893 else 894 pState->uLastYear = pState->uFirstYear; 895 } 896 else 897 { 898 pState->uFirstYear = UINT32_MAX; 899 ScmError(pState->pState, RT_SUCCESS(rc) ? -rc : rc, 900 "Failed to parse copyright year: '%.*s'\n", pszEnd - pszBody, pszBody); 901 } 902 } 903 904 /* The copyright comment must come before the license. */ 905 if (pState->iLineLicense != UINT32_MAX) 906 ScmError(pState->pState, VERR_WRONG_ORDER, "Copyright (line %u) must come before the license (line %u)!\n", 907 iLine, pState->iLineLicense); 908 909 /* In C/C++ code, this must be a multiline comment. While in python it 910 must be a */ 911 if (pState->enmCommentStyle == kScmCommentStyle_C && pInfo->enmType != kScmCommentType_MultiLine) 912 ScmError(pState->pState, VERR_WRONG_ORDER, "Copyright must appear in a multiline comment (no doxygen stuff)\n"); 913 else if (pState->enmCommentStyle == kScmCommentStyle_Python && pInfo->enmType != kScmCommentType_DocString) 914 ScmError(pState->pState, VERR_WRONG_ORDER, "Copyright must appear in a doc-string\n"); 915 916 /* The copyright must be followed by the license. */ 917 if (!pszNextLine) 918 ScmError(pState->pState, VERR_WRONG_ORDER, "Copyright should be followed by the license text!\n"); 919 920 /* Quit if we've flagged a failure. */ 921 if (RT_FAILURE(pState->pState->rc)) 922 return VERR_CALLBACK_RETURN; 923 924 /* Check if it's well formed and up to date. */ 925 char szWellFormed[256]; 926 size_t cchWellFormed; 927 if (pState->uFirstYear == pState->uLastYear) 928 cchWellFormed = RTStrPrintf(szWellFormed, sizeof(szWellFormed), "Copyright (C) %u %s", 929 pState->uFirstYear, g_szCopyrightHolder); 930 else 931 cchWellFormed = RTStrPrintf(szWellFormed, sizeof(szWellFormed), "Copyright (C) %u-%u %s", 932 pState->uFirstYear, pState->uLastYear, g_szCopyrightHolder); 933 pState->fWellFormedCopyright = cchWellFormed == (uintptr_t)(pszEnd - pszBody) 934 && memcmp(pszBody, szWellFormed, cchWellFormed) == 0; 935 pState->fUpToDateCopyright = pState->uLastYear == g_uYear; 936 pState->iLineCopyright = iLine; 937 938 /* If there wasn't exactly one blank line before the comment, trigger a rewrite. */ 939 if (pInfo->cBlankLinesBefore != 1) 940 pState->fWellFormedCopyright = false; 941 942 /* If the comment doesn't start in column 1, trigger rewrite. */ 943 if (pInfo->offStart != 0) 944 { 945 pState->fWellFormedCopyright = false; 946 /** @todo check that there isn't any code preceeding the comment. */ 947 } 948 949 fFoundCopyright = true; 950 ScmVerbose(pState->pState, 2, "oracle copyright %u-%u: up-to-date=%RTbool well-formed=%RTbool\n", 951 pState->uFirstYear, pState->uLastYear, pState->fUpToDateCopyright, pState->fWellFormedCopyright); 952 } 953 else 954 ScmVerbose(pState->pState, 2, "not oracle copyright: '%.*s'\n", pszEnd - pszBody, pszBody); 955 956 if (!pszNextLine) 957 return VINF_SUCCESS; 958 959 /* Skip the copyright line and any blank lines following it. */ 960 cchBody -= pszNextLine - pszBody + 1; 961 pszBody = pszNextLine + 1; 962 iLine += 1; 963 while (*pszBody == '\n') 964 { 965 pszBody++; 966 cchBody--; 967 iLine++; 968 cBlankLinesAfterCopyright++; 969 } 970 } 971 972 /* 973 * Now try match against the license texts if we haven't already found it. 974 */ 975 if (pState->iLineLicense == UINT32_MAX) 976 { 977 for (PCSCMLICENSETEXT pCur = pState->paLicenses; pCur->cch > 0; pCur++) 978 { 979 const char *pszNext; 980 if ( pCur->cch - 1 <= cchBody 981 && IsEqualWordByWordIgnoreCase(pCur->psz, pszBody, &pszNext)) 982 { 983 while ( RT_C_IS_SPACE(*pszNext) 984 || (RT_C_IS_PUNCT(*pszNext) && *pszNext != '-')) 985 pszNext++; 986 987 uint32_t cDashes = 0; 988 while (*pszNext == '-') 989 cDashes++, pszNext++; 990 bool fExternal = cDashes > 10; 991 992 if ( *pszNext == '\0' 993 || fExternal) 994 { 995 /* In C/C++ code, this must be a multiline comment. While in python it 996 must be a */ 997 if (pState->enmCommentStyle == kScmCommentStyle_C && pInfo->enmType != kScmCommentType_MultiLine) 998 ScmError(pState->pState, VERR_WRONG_ORDER, "License must appear in a multiline comment (no doxygen stuff)\n"); 999 else if (pState->enmCommentStyle == kScmCommentStyle_Python && pInfo->enmType != kScmCommentType_DocString) 1000 ScmError(pState->pState, VERR_WRONG_ORDER, "License must appear in a doc-string\n"); 1001 1002 /* Quit if we've flagged a failure. */ 1003 if (RT_FAILURE(pState->pState->rc)) 1004 return VERR_CALLBACK_RETURN; 1005 1006 /* Record it. */ 1007 pState->iLineLicense = iLine; 1008 pState->cLinesLicense = CountLinesInSubstring(pszBody, pszNext - pszBody); 1009 pState->pCurrentLicense = pCur; 1010 pState->fExternalLicense = fExternal; 1011 pState->fIsCorrectLicense = pState->fOpenSource 1012 ? pCur->enmOpt == pState->enmLicenceOpt 1013 : pCur->enmType == kScmLicenseType_Confidential; 1014 pState->fWellFormedLicense = memcmp(pszBody, pCur->psz, pCur->cch - 1) == 0; 1015 1016 /* If there was more than one blank line between the copyright and the 1017 license text, extend the license text area and force a rewrite of it. */ 1018 if (cBlankLinesAfterCopyright > 1) 1019 { 1020 pState->iLineLicense -= cBlankLinesAfterCopyright - 1; 1021 pState->cLinesLicense += cBlankLinesAfterCopyright - 1; 1022 pState->fWellFormedLicense = false; 1023 } 1024 1025 /* If there was more than one blank line after the license, trigger a rewrite. */ 1026 if (!fExternal && pInfo->cBlankLinesAfter != 1) 1027 pState->fWellFormedLicense = false; 1028 1029 /** @todo Check that the last comment line doesn't have any code on it. */ 1030 /** @todo Check that column 2 contains '*' for C/C++ files. */ 1031 1032 ScmVerbose(pState->pState, 2, 1033 "Found license %d/%d at %u..%u: is-correct=%RTbool well-formed=%RTbool external-part=%RTbool open-source=%RTbool\n", 1034 pCur->enmType, pCur->enmOpt, pState->iLineLicense, pState->iLineLicense + pState->cLinesLicense, 1035 pState->fIsCorrectLicense, pState->fWellFormedLicense, 1036 pState->fExternalLicense, pState->fOpenSource); 1037 1038 if (fFoundCopyright) 1039 { 1040 pState->iLineComment = pInfo->iLineStart; 1041 pState->cLinesComment = (fExternal ? pState->iLineLicense + pState->cLinesLicense : pInfo->iLineEnd + 1) 1042 - pInfo->iLineStart; 1043 } 1044 else 1045 ScmError(pState->pState, VERR_WRONG_ORDER, "License should be preceeded by the copyright!\n"); 1046 if (pState->iLineCopyright != UINT32_MAX) 1047 return VERR_CALLBACK_RETURN; 1048 break; 1049 } 1050 } 1051 } 1052 } 1053 1054 if (fFoundCopyright) 1055 ScmError(pState->pState, VERR_WRONG_ORDER, "Copyright should be followed by the license text!\n"); 1056 return VINF_SUCCESS; 1057 } 1058 1059 1060 /** 1061 * Updates the copyright year and/or license text. 1062 * 1063 * @returns true if modifications were made, false if not. 1064 * @param pState The rewriter state. 1065 * @param pIn The input stream. 1066 * @param pOut The output stream. 1067 * @param pSettings The settings. 1068 * @param enmCommentStyle The comment style used by the file. 1069 */ 1070 static bool rewrite_Copyright_Common(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings, 1071 SCMCOMMENTSTYLE enmCommentStyle) 1072 { 1073 if ( !pSettings->fUpdateCopyrightYear 1074 && pSettings->enmUpdateLicense == kScmLicense_LeaveAlone) 1075 return false; 1076 1077 /* 1078 * Try locate the relevant comments. 1079 */ 1080 SCMCOPYRIGHTINFO Info = 1081 { 1082 /*.pState = */ pState, 1083 /*.enmCommentStyle = */ enmCommentStyle, 1084 1085 /*.iLineComment = */ UINT32_MAX, 1086 /*.cLinesComment = */ 0, 1087 1088 /*.iLineCopyright = */ UINT32_MAX, 1089 /*.uFirstYear = */ UINT32_MAX, 1090 /*.uLastYear = */ UINT32_MAX, 1091 /*.fWellFormedCopyright = */ false, 1092 /*.fUpToDateCopyright = */ false, 1093 1094 /*.fOpenSource = */ true, 1095 /*.pExpectedLicense = */ NULL, 1096 /*.paLicenses = */ pSettings->enmUpdateLicense != kScmLicense_Mit ? &g_aLicenses[0] : &g_aLicensesWithMit[0], 1097 /*.enmLicenceOpt = */ pSettings->enmUpdateLicense, 1098 /*.iLineLicense = */ UINT32_MAX, 1099 /*.cLinesLicense = */ 0, 1100 /*.pCurrentLicense = */ NULL, 1101 /*.fIsCorrectLicense = */ false, 1102 /*.fWellFormedLicense = */ false, 1103 /*.fExternalLicense = */ false, 1104 }; 1105 1106 /* Figure Info.fOpenSource and the desired license: */ 1107 char *pszSyncProcess; 1108 int rc = ScmSvnQueryProperty(pState, "svn:sync-process", &pszSyncProcess); 1109 if (RT_SUCCESS(rc)) 1110 { 1111 Info.fOpenSource = strcmp(RTStrStrip(pszSyncProcess), "export") == 0; 1112 RTStrFree(pszSyncProcess); 1113 } 1114 else if (rc == VERR_NOT_FOUND) 1115 Info.fOpenSource = false; 1116 else 1117 return ScmError(pState, rc, "ScmSvnQueryProperty(svn:sync-process): %Rrc\n", rc); 1118 1119 Info.pExpectedLicense = Info.paLicenses; 1120 if (Info.fOpenSource) 1121 while (Info.pExpectedLicense->enmOpt != pSettings->enmUpdateLicense) 1122 Info.pExpectedLicense++; 1123 else 1124 while (Info.pExpectedLicense->enmType != kScmLicenseType_Confidential) 1125 Info.pExpectedLicense++; 1126 1127 /* Scan the comments. */ 1128 rc = ScmEnumerateComments(pIn, enmCommentStyle, rewrite_Copyright_CommentCallback, &Info); 1129 if ( (rc == VERR_CALLBACK_RETURN || RT_SUCCESS(rc)) 1130 && RT_SUCCESS(pState->rc)) 1131 { 1132 if (Info.iLineCopyright == UINT32_MAX) 1133 ScmError(pState, VERR_NOT_FOUND, "Missing copyright!\n"); 1134 else if (Info.iLineLicense == UINT32_MAX) 1135 ScmError(pState, VERR_NOT_FOUND, "Missing license!\n"); 1136 else 1137 { 1138 /* 1139 * Do we need to make any changes? 1140 */ 1141 bool fUpdateCopyright = !Info.fWellFormedCopyright 1142 || (!Info.fUpToDateCopyright && pSettings->fUpdateCopyrightYear); 1143 bool fUpdateLicense = Info.enmLicenceOpt != kScmLicense_LeaveAlone 1144 && ( !Info.fWellFormedLicense 1145 || !Info.fIsCorrectLicense); 1146 if (fUpdateCopyright || fUpdateLicense) 1147 { 1148 Assert(Info.iLineComment != UINT32_MAX); 1149 Assert(Info.cLinesComment > 0); 1150 1151 /* 1152 * Okay, do the work. 1153 */ 1154 ScmStreamRewindForReading(pIn); 1155 1156 if (pSettings->fUpdateCopyrightYear) 1157 Info.uLastYear = g_uYear; 1158 1159 uint32_t iLine = 0; 1160 SCMEOL enmEol; 1161 size_t cchLine; 1162 const char *pchLine; 1163 while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL) 1164 { 1165 if (iLine == Info.iLineComment) 1166 { 1167 /* Leading blank line. */ 1168 ScmStreamPutLine(pOut, g_aCopyrightCommentStart[enmCommentStyle].psz, 1169 g_aCopyrightCommentStart[enmCommentStyle].cch, enmEol); 1170 1171 /* Write the copyright comment line. */ 1172 ScmStreamWrite(pOut, g_aCopyrightCommentPrefix[enmCommentStyle].psz, 1173 g_aCopyrightCommentPrefix[enmCommentStyle].cch); 1174 1175 char szCopyright[256]; 1176 size_t cchCopyright; 1177 if (Info.uFirstYear == Info.uLastYear) 1178 cchCopyright = RTStrPrintf(szCopyright, sizeof(szCopyright), "Copyright (C) %u %s", 1179 Info.uFirstYear, g_szCopyrightHolder); 1180 else 1181 cchCopyright = RTStrPrintf(szCopyright, sizeof(szCopyright), "Copyright (C) %u-%u %s", 1182 Info.uFirstYear, Info.uLastYear, g_szCopyrightHolder); 1183 1184 ScmStreamWrite(pOut, szCopyright, cchCopyright); 1185 ScmStreamPutEol(pOut, enmEol); 1186 1187 /* Blank line separating the two. */ 1188 ScmStreamPutLine(pOut, g_aCopyrightCommentEmpty[enmCommentStyle].psz, 1189 g_aCopyrightCommentEmpty[enmCommentStyle].cch, enmEol); 1190 1191 /* Write the license text. */ 1192 Assert(Info.pExpectedLicense->psz[Info.pExpectedLicense->cch - 1] == '\n'); 1193 Assert(Info.pExpectedLicense->psz[Info.pExpectedLicense->cch - 2] != '\n'); 1194 const char *psz = Info.pExpectedLicense->psz; 1195 do 1196 { 1197 const char *pszEol = strchr(psz, '\n'); 1198 if (pszEol != psz) 1199 { 1200 ScmStreamWrite(pOut, g_aCopyrightCommentPrefix[enmCommentStyle].psz, 1201 g_aCopyrightCommentPrefix[enmCommentStyle].cch); 1202 ScmStreamWrite(pOut, psz, pszEol - psz); 1203 ScmStreamPutEol(pOut, enmEol); 1204 } 1205 else 1206 ScmStreamPutLine(pOut, g_aCopyrightCommentEmpty[enmCommentStyle].psz, 1207 g_aCopyrightCommentEmpty[enmCommentStyle].cch, enmEol); 1208 psz = pszEol + 1; 1209 } while (*psz != '\0'); 1210 1211 /* Final comment line (picking up the stream status here). */ 1212 if (!Info.fExternalLicense) 1213 rc = ScmStreamPutLine(pOut, g_aCopyrightCommentEnd[enmCommentStyle].psz, 1214 g_aCopyrightCommentEnd[enmCommentStyle].cch, enmEol); 1215 else 1216 rc = ScmStreamGetStatus(pOut); 1217 1218 /* Skip the copyright and license text in the input file. */ 1219 if (RT_SUCCESS(rc)) 1220 { 1221 iLine = Info.iLineComment + Info.cLinesComment; 1222 rc = ScmStreamSeekByLine(pIn, iLine); 1223 } 1224 } 1225 else 1226 { 1227 rc = ScmStreamPutLine(pOut, pchLine, cchLine, enmEol); 1228 iLine++; 1229 } 1230 if (RT_FAILURE(rc)) 1231 return false; 1232 } 1233 return true; 1234 } 1235 } 1236 } 1237 else 1238 ScmError(pState, rc, "ScmEnumerateComments: %Rrc\n", rc); 1239 NOREF(pState); NOREF(pOut); 1240 return false; 1241 } 1242 1243 1244 /** Copyright updater for C-style comments. */ 1245 bool rewrite_Copyright_CstyleComment(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1246 { 1247 return rewrite_Copyright_Common(pState, pIn, pOut, pSettings, kScmCommentStyle_C); 1248 } 1249 1250 /** Copyright updater for hash-prefixed comments. */ 1251 bool rewrite_Copyright_HashComment(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1252 { 1253 return rewrite_Copyright_Common(pState, pIn, pOut, pSettings, kScmCommentStyle_Hash); 1254 } 1255 1256 /** Copyright updater for REM-prefixed comments. */ 1257 bool rewrite_Copyright_RemComment(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1258 { 1259 return rewrite_Copyright_Common(pState, pIn, pOut, pSettings, determinBatchFileCommentStyle(pIn)); 1260 } 1261 1262 /** Copyright updater for python comments. */ 1263 bool rewrite_Copyright_PythonComment(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1264 { 1265 return rewrite_Copyright_Common(pState, pIn, pOut, pSettings, kScmCommentStyle_Python); 1266 } 1267 1268 /** Copyright updater for semicolon-prefixed comments. */ 1269 bool rewrite_Copyright_SemicolonComment(PSCMRWSTATE pState, PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings) 1270 { 1271 return rewrite_Copyright_Common(pState, pIn, pOut, pSettings, kScmCommentStyle_Semicolon); 1272 } 1273 432 1274 433 1275 /**
Note:
See TracChangeset
for help on using the changeset viewer.