VirtualBox

Changeset 69166 in vbox for trunk/src/bldprogs/scmrw.cpp


Ignore:
Timestamp:
Oct 23, 2017 3:43:33 PM (7 years ago)
Author:
vboxsync
Message:

scm: License and copyright updating.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/bldprogs/scmrw.cpp

    r63568 r69166  
    3939
    4040
     41/*********************************************************************************************************************************
     42*   Structures and Typedefs                                                                                                      *
     43*********************************************************************************************************************************/
     44/** License types. */
     45typedef 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. */
     56typedef 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. */
     68typedef SCMLICENSETEXT const *PCSCMLICENSETEXT;
     69
     70/**
     71 * Copyright + license rewriter state.
     72 */
     73typedef 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;
     110typedef SCMCOPYRIGHTINFO *PSCMCOPYRIGHTINFO;
     111
     112
     113/*********************************************************************************************************************************
     114*   Global Variables                                                                                                             *
     115*********************************************************************************************************************************/
     116/** --license-ose-gpl */
     117static 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 */
     127static 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 */
     146static 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.  */
     165static 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. */
     188static 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. */
     193static 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. */
     203static 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. */
     214static const char g_szCopyrightHolder[] = "Oracle Corporation";
     215
     216
     217/** Copyright+license comment start for each SCMCOMMENTSTYLE. */
     218static 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. */
     232static 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. */
     246static 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. */
     260static 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 */
     280static 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
    41329/**
    42330 * Worker for isBlankLine.
     
    229517            int rc2 = ScmSvnSetProperty(pState, "svn:eol-style", pszDesiredSvnEol);
    230518            if (RT_FAILURE(rc2))
    231                 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc2); /** @todo propagate the error somehow... */
     519                ScmError(pState, rc2, "ScmSvnSetProperty: %Rrc\n", rc2);
    232520        }
    233521        if (RT_SUCCESS(rc))
     
    374662        rc = ScmSvnDelProperty(pState, "svn:executable");
    375663        if (RT_FAILURE(rc))
    376             RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */
     664            ScmError(pState, rc, "ScmSvnSetProperty: %Rrc\n", rc);
    377665    }
    378666    return false;
     
    412700            rc = ScmSvnSetProperty(pState, "svn:keywords", pszKeywords);
    413701            if (RT_FAILURE(rc))
    414                 RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */
     702                ScmError(pState, rc, "ScmSvnSetProperty: %Rrc\n", rc);
    415703        }
    416704        else
    417             RTMsgError("RTStrAppend: %Rrc\n", rc); /** @todo error propagation here.. */
     705            ScmError(pState, rc, "RTStrAppend: %Rrc\n", rc);
    418706        RTStrFree(pszKeywords);
    419707    }
     
    423711        rc = ScmSvnSetProperty(pState, "svn:keywords", "Id Revision");
    424712        if (RT_FAILURE(rc))
    425             RTMsgError("ScmSvnSetProperty: %Rrc\n", rc); /** @todo error propagation here.. */
     713            ScmError(pState, rc, "ScmSvnSetProperty: %Rrc\n", rc);
    426714    }
    427715    else if (RT_SUCCESS(rc))
     
    430718    return false;
    431719}
     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 */
     734static 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 */
     784static 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 */
     805static DECLCALLBACK(int)
     806rewrite_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 */
     1070static 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.   */
     1245bool 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.   */
     1251bool 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.   */
     1257bool 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.   */
     1263bool 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.   */
     1269bool 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
    4321274
    4331275/**
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette