VirtualBox

Changeset 26477 in vbox


Ignore:
Timestamp:
Feb 13, 2010 3:40:00 AM (15 years ago)
Author:
vboxsync
Message:

scm: More flexible settings.

File:
1 edited

Legend:

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

    r26359 r26477  
    4848
    4949/*******************************************************************************
    50 *   Internal Functions                                                         *
     50*   Defined Constants And Macros                                               *
    5151*******************************************************************************/
     52/** The name of the settings files. */
     53#define SCM_SETTINGS_FILENAME           ".scm-settings"
     54
     55
     56/*******************************************************************************
     57*   Structures and Typedefs                                                    *
     58*******************************************************************************/
     59/** Pointer to const massager settings. */
     60typedef struct SCMSETTINGSBASE const *PCSCMSETTINGSBASE;
     61
    5262/** End of line marker type. */
    5363typedef enum SCMEOL
     
    122132 *
    123133 * @returns true if any changes were made, false if not.
    124  * @param   pIn         The input stream.
    125  * @param   pOut        The output stream.
    126  */
    127 typedef bool (*PFNSCMREWRITER)(PSCMSTREAM pIn, PSCMSTREAM pOut);
     134 * @param   pIn                 The input stream.
     135 * @param   pOut                The output stream.
     136 * @param   pSettings           The settings.
     137 */
     138typedef bool (*PFNSCMREWRITER)(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings);
    128139
    129140
     
    163174    /** Whether to print special characters in human readable form or not. */
    164175    bool            fSpecialChars;
     176    /** The tab size. */
     177    size_t          cchTab;
    165178    /** Where to push the diff. */
    166179    PRTSTREAM       pDiff;
     
    169182typedef SCMDIFFSTATE *PSCMDIFFSTATE;
    170183
     184/**
     185 * Source Code Massager Settings.
     186 */
     187typedef struct SCMSETTINGSBASE
     188{
     189    bool            fConvertEol;
     190    bool            fConvertTabs;
     191    bool            fForceFinalEol;
     192    bool            fForceTrailingLine;
     193    bool            fStripTrailingBlanks;
     194    bool            fStripTrailingLines;
     195    unsigned        cchTab;
     196} SCMSETTINGSBASE;
     197/** Pointer to massager settings. */
     198typedef SCMSETTINGSBASE *PSCMSETTINGSBASE;
     199
     200/**
     201 * Option identifiers.
     202 *
     203 * @note    The first chunk, down to SCMOPT_TAB_SIZE, are alternately set &
     204 *          clear.  So, the option setting a flag (boolean) will have an even
     205 *          number and the one clearing it will have an odd number.
     206 * @note    Down to SCMOPT_LAST_SETTINGS corresponds exactly to SCMSETTINGSBASE.
     207 */
     208typedef enum SCMOPT
     209{
     210    SCMOPT_CONVERT_EOL = 10000,
     211    SCMOPT_NO_CONVERT_EOL,
     212    SCMOPT_CONVERT_TABS,
     213    SCMOPT_NO_CONVERT_TABS,
     214    SCMOPT_FORCE_FINAL_EOL,
     215    SCMOPT_NO_FORCE_FINAL_EOL,
     216    SCMOPT_FORCE_TRAILING_LINE,
     217    SCMOPT_NO_FORCE_TRAILING_LINE,
     218    SCMOPT_STRIP_TRAILING_BLANKS,
     219    SCMOPT_NO_STRIP_TRAILING_BLANKS,
     220    SCMOPT_STRIP_TRAILING_LINES,
     221    SCMOPT_NO_STRIP_TRAILING_LINES,
     222    SCMOPT_TAB_SIZE,
     223    SCMOPT_LAST_SETTINGS = SCMOPT_TAB_SIZE,
     224    //
     225    SCMOPT_DIFF_IGNORE_EOL,
     226    SCMOPT_DIFF_NO_IGNORE_EOL,
     227    SCMOPT_DIFF_IGNORE_SPACE,
     228    SCMOPT_DIFF_NO_IGNORE_SPACE,
     229    SCMOPT_DIFF_IGNORE_LEADING_SPACE,
     230    SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE,
     231    SCMOPT_DIFF_IGNORE_TRAILING_SPACE,
     232    SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE,
     233    SCMOPT_DIFF_SPECIAL_CHARS,
     234    SCMOPT_DIFF_NO_SPECIAL_CHARS,
     235    SCMOPT_END
     236} SCMOPT;
     237
     238
     239/**
     240 * File/dir pattern + options.
     241 */
     242typedef struct SCMPATRNOPTPAIR
     243{
     244    char *pszPattern;
     245    char *pszOptions;
     246} SCMPATRNOPTPAIR;
     247/** Pointer to a pattern + option pair. */
     248typedef SCMPATRNOPTPAIR *PSCMPATRNOPTPAIR;
     249
     250
     251/** Pointer to a settings set. */
     252typedef struct SCMSETTINGS *PSCMSETTINGS;
     253/**
     254 * Settings set.
     255 *
     256 * This structure is constructed from the command line arguments or any
     257 * .scm-settings file found in a directory we recurse into.  When recusing in
     258 * and out of a directory, we push and pop a settings set for it.
     259 *
     260 * The .scm-settings file has two kinds of setttings, first there are the
     261 * unqualified base settings and then there are the settings which applies to a
     262 * set of files or directories.  The former are lines with command line options.
     263 * For the latter, the options are preceeded by a string pattern and a colon.
     264 * The pattern specifies which files (and/or directories) the options applies
     265 * to.
     266 *
     267 * We parse the base options into the Base member and put the others into the
     268 * paPairs array.
     269 */
     270typedef struct SCMSETTINGS
     271{
     272    /** Pointer to the setting file below us in the stack. */
     273    PSCMSETTINGS        pDown;
     274    /** Pointer to the setting file above us in the stack. */
     275    PSCMSETTINGS        pUp;
     276    /** File/dir patterns and their options. */
     277    PSCMPATRNOPTPAIR    paPairs;
     278    /** The number of entires in paPairs. */
     279    uint32_t            cPairs;
     280    /** The base settings that was read out of the file. */
     281    SCMSETTINGSBASE     Base;
     282} SCMSETTINGS;
     283/** Pointer to a const settings set. */
     284typedef SCMSETTINGS const *PCSCMSETTINGS;
     285
     286
    171287
    172288/*******************************************************************************
    173289*   Internal Functions                                                         *
    174290*******************************************************************************/
    175 static bool rewrite_StripTrailingBlanks(PSCMSTREAM pIn, PSCMSTREAM pOut);
    176 static bool rewrite_ExpandTabs(PSCMSTREAM pIn, PSCMSTREAM pOut);
    177 static bool rewrite_ForceNativeEol(PSCMSTREAM pIn, PSCMSTREAM pOut);
    178 static bool rewrite_ForceLF(PSCMSTREAM pIn, PSCMSTREAM pOut);
    179 static bool rewrite_ForceCRLF(PSCMSTREAM pIn, PSCMSTREAM pOut);
    180 static bool rewrite_AdjustTrailingLines(PSCMSTREAM pIn, PSCMSTREAM pOut);
    181 static bool rewrite_Makefile_kup(PSCMSTREAM pIn, PSCMSTREAM pOut);
    182 static bool rewrite_Makefile_kmk(PSCMSTREAM pIn, PSCMSTREAM pOut);
    183 static bool rewrite_C_and_CPP(PSCMSTREAM pIn, PSCMSTREAM pOut);
     291static bool rewrite_StripTrailingBlanks(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings);
     292static bool rewrite_ExpandTabs(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings);
     293static bool rewrite_ForceNativeEol(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings);
     294static bool rewrite_ForceLF(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings);
     295static bool rewrite_ForceCRLF(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings);
     296static bool rewrite_AdjustTrailingLines(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings);
     297static bool rewrite_Makefile_kup(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings);
     298static bool rewrite_Makefile_kmk(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings);
     299static bool rewrite_C_and_CPP(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings);
    184300
    185301
     
    189305static const char   g_szProgName[]          = "scm";
    190306static const char  *g_pszChangedSuff        = "";
     307static const char   g_szTabSpaces[16+1]     = "                ";
    191308static bool         g_fDryRun               = true;
    192 static bool         g_fStripTrailingBlanks  = true;
    193 static bool         g_fStripTrailingLines   = true;
    194 static bool         g_fForceFinalEol        = true;
    195 static bool         g_fForceTrailingLine    = false;
    196 static bool         g_fConvertTabs          = true;
    197 static unsigned     g_cchTab                = 8;
    198 static const char   g_szTabSpaces[16+1]     = "                ";
    199 static bool         g_fConvertEol           = true;
    200309static bool         g_fDiffSpecialChars     = true;
    201310static bool         g_fDiffIgnoreEol        = false;
     
    203312static bool         g_fDiffIgnoreTrailingWS = false;
    204313static int          g_iVerbosity            = 2;//99; //0;
    205 static const char  *g_pszFileFilter         = "*";
     314
     315/** The global settings. */
     316static SCMSETTINGSBASE const g_Defaults =
     317{
     318    /* .fStripTrailingBlanks = */   true,
     319    /* .fStripTrailingLines = */    true,
     320    /* .fForceFinalEol = */         true,
     321    /* .fForceTrailingLine = */     false,
     322    /* .fConvertTabs = */           true,
     323    /* .cchTab = */                 8,
     324    /* .fConvertEol = */            true
     325};
     326
     327/** Option definitions for the base settings. */
     328static RTGETOPTDEF  g_aScmOpts[] =
     329{
     330    { "--convert-eol",                      SCMOPT_CONVERT_EOL,                     RTGETOPT_REQ_NOTHING },
     331    { "--no-convert-eol",                   SCMOPT_NO_CONVERT_EOL,                  RTGETOPT_REQ_NOTHING },
     332    { "--convert-tabs",                     SCMOPT_CONVERT_TABS,                    RTGETOPT_REQ_NOTHING },
     333    { "--no-convert-tabs",                  SCMOPT_NO_CONVERT_TABS,                 RTGETOPT_REQ_NOTHING },
     334    { "--force-final-eol",                  SCMOPT_FORCE_FINAL_EOL,                 RTGETOPT_REQ_NOTHING },
     335    { "--no-force-final-eol",               SCMOPT_NO_FORCE_FINAL_EOL,              RTGETOPT_REQ_NOTHING },
     336    { "--force-trailing-line",              SCMOPT_FORCE_TRAILING_LINE,             RTGETOPT_REQ_NOTHING },
     337    { "--no-force-trailing-line",           SCMOPT_NO_FORCE_TRAILING_LINE,          RTGETOPT_REQ_NOTHING },
     338    { "--strip-trailing-blanks",            SCMOPT_STRIP_TRAILING_BLANKS,           RTGETOPT_REQ_NOTHING },
     339    { "--no-strip-trailing-blanks",         SCMOPT_NO_STRIP_TRAILING_BLANKS,        RTGETOPT_REQ_NOTHING },
     340    { "--strip-trailing-lines",             SCMOPT_STRIP_TRAILING_LINES,            RTGETOPT_REQ_NOTHING },
     341    { "--strip-no-trailing-lines",          SCMOPT_NO_STRIP_TRAILING_LINES,         RTGETOPT_REQ_NOTHING },
     342    { "--tab-size",                         SCMOPT_TAB_SIZE,                        RTGETOPT_REQ_UINT8   },
     343};
     344
     345/** Consider files matching the following patterns (base names only). */
     346static const char  *g_pszFileFilter         = NULL;
     347/** Filter out files matching the following patterns.  This is applied to the
     348 *  base names as well as the absolute paths. */
    206349static const char  *g_pszFileFilterOut      =
    207350    "*.exe|"
     
    214357    "/dummy/."
    215358;
     359/** Consider directories matching the following patterns (base names only) */
    216360static const char  *g_pszDirFilter          = NULL;
     361/** Filter out directories matching the following patterns.  This is applied
     362 *  to base names as well as the aboslute paths.  All absolute paths ends with a
     363 *  slash and dot ("/.").  */
    217364static const char  *g_pszDirFilterOut       =
    218365    // generic
    219366    ".svn|"
     367    ".hg|"
     368    ".git|"
    220369    "CVS|"
    221370    // root
     
    11851334                    offVir += pchTab - pchStart;
    11861335
    1187                     size_t cchTab = g_cchTab - offVir % g_cchTab;
     1336                    size_t cchTab = pState->cchTab - offVir % pState->cchTab;
    11881337                    switch (cchTab)
    11891338                    {
     
    14861635 * @param   fSpecialChars       Whether to print special chars in a human
    14871636 *                              readable form or not.
     1637 * @param   cchTab              The tab size.
    14881638 * @param   pDiff               Where to write the diff.
    14891639 */
    14901640size_t ScmDiffStreams(const char *pszFilename, PSCMSTREAM pLeft, PSCMSTREAM pRight, bool fIgnoreEol,
    1491                       bool fIgnoreLeadingWhite, bool fIgnoreTrailingWhite, bool fSpecialChars, PRTSTREAM pDiff)
     1641                      bool fIgnoreLeadingWhite, bool fIgnoreTrailingWhite, bool fSpecialChars,
     1642                      size_t cchTab, PRTSTREAM pDiff)
    14921643{
    14931644#ifdef RT_STRICT
     
    15081659    State.fIgnoreTrailingWhite  = fIgnoreTrailingWhite;
    15091660    State.fSpecialChars         = fSpecialChars;
     1661    State.cchTab                = cchTab;
    15101662    State.pDiff                 = pDiff;
    15111663
     
    15551707
    15561708
     1709/* -=-=-=-=-=- settings -=-=-=-=-=- */
     1710
     1711/**
     1712 * Init a settings structure with settings from @a pSrc.
     1713 *
     1714 * @returns IPRT status code
     1715 * @param   pSettings           The settings.
     1716 * @param   pSrc                The source settings.
     1717 */
     1718static int scmSettingsBaseInitAndCopy(PSCMSETTINGSBASE pSettings, PCSCMSETTINGSBASE pSrc)
     1719{
     1720    *pSettings = *pSrc;
     1721    /* No dynamically allocated stuff yet. */
     1722    return VINF_SUCCESS;
     1723}
     1724
     1725/**
     1726 * Init a settings structure.
     1727 *
     1728 * @returns IPRT status code
     1729 * @param   pSettings           The settings.
     1730 */
     1731static int scmSettingsBaseInit(PSCMSETTINGSBASE pSettings)
     1732{
     1733    return scmSettingsBaseInitAndCopy(pSettings, &g_Defaults);
     1734}
     1735
     1736/**
     1737 * Deletes the settings, i.e. free any dynamically allocated content.
     1738 *
     1739 * @param   pSettings           The settings.
     1740 */
     1741static void scmSettingsBaseDelete(PSCMSETTINGSBASE pSettings)
     1742{
     1743    if (pSettings)
     1744    {
     1745        Assert(pSettings->cchTab != ~(unsigned)0);
     1746        pSettings->cchTab = ~(unsigned)0;
     1747        /* No dynamically allocated stuff yet. */
     1748    }
     1749}
     1750
     1751/**
     1752 * Processes a RTGetOpt result.
     1753 *
     1754 * @retval  VINF_SUCCESS if handled.
     1755 * @retval  VERR_OUT_OF_RANGE if the option value was out of range.
     1756 * @retval  VERR_GETOPT_UNKNOWN_OPTION if the option was not recognized.
     1757 *
     1758 * @param   pSettings           The settings to change.
     1759 * @param   rc                  The RTGetOpt return value.
     1760 * @param   pValueUnion         The RTGetOpt value union.
     1761 */
     1762static int scmSettingsBaseHandleOpt(PSCMSETTINGSBASE pSettings, int rc, PRTGETOPTUNION pValueUnion)
     1763{
     1764    switch (rc)
     1765    {
     1766        case SCMOPT_CONVERT_EOL:
     1767            pSettings->fConvertEol = true;
     1768            return VINF_SUCCESS;
     1769        case SCMOPT_NO_CONVERT_EOL:
     1770            pSettings->fConvertEol = false;
     1771            return VINF_SUCCESS;
     1772
     1773        case SCMOPT_CONVERT_TABS:
     1774            pSettings->fConvertTabs = true;
     1775            return VINF_SUCCESS;
     1776        case SCMOPT_NO_CONVERT_TABS:
     1777            pSettings->fConvertTabs = false;
     1778            return VINF_SUCCESS;
     1779
     1780        case SCMOPT_FORCE_FINAL_EOL:
     1781            pSettings->fForceFinalEol = true;
     1782            return VINF_SUCCESS;
     1783        case SCMOPT_NO_FORCE_FINAL_EOL:
     1784            pSettings->fForceFinalEol = false;
     1785            return VINF_SUCCESS;
     1786
     1787        case SCMOPT_FORCE_TRAILING_LINE:
     1788            pSettings->fForceTrailingLine = true;
     1789            return VINF_SUCCESS;
     1790        case SCMOPT_NO_FORCE_TRAILING_LINE:
     1791            pSettings->fForceTrailingLine = false;
     1792            return VINF_SUCCESS;
     1793
     1794        case SCMOPT_STRIP_TRAILING_BLANKS:
     1795            pSettings->fStripTrailingBlanks = true;
     1796            return VINF_SUCCESS;
     1797        case SCMOPT_NO_STRIP_TRAILING_BLANKS:
     1798            pSettings->fStripTrailingBlanks = false;
     1799            return VINF_SUCCESS;
     1800
     1801        case SCMOPT_STRIP_TRAILING_LINES:
     1802            pSettings->fStripTrailingLines = true;
     1803            return VINF_SUCCESS;
     1804        case SCMOPT_NO_STRIP_TRAILING_LINES:
     1805            pSettings->fStripTrailingLines = false;
     1806            return VINF_SUCCESS;
     1807
     1808        case SCMOPT_TAB_SIZE:
     1809            if (   pValueUnion->u8 < 1
     1810                || pValueUnion->u8 >= RT_ELEMENTS(g_szTabSpaces))
     1811            {
     1812                RTMsgError("Invalid tab size: %u - must be in {1..%u}\n",
     1813                           pValueUnion->u8, RT_ELEMENTS(g_szTabSpaces) - 1);
     1814                return VERR_OUT_OF_RANGE;
     1815            }
     1816            pSettings->cchTab = pValueUnion->u8;
     1817            return VINF_SUCCESS;
     1818
     1819        default:
     1820            return VERR_GETOPT_UNKNOWN_OPTION;
     1821    }
     1822}
     1823
     1824/**
     1825 * Parses an option string.
     1826 *
     1827 * @returns IPRT status code.
     1828 * @param   pBase               The base settings structure to apply the options
     1829 *                              to.
     1830 * @param   pszOptions          The options to parse.
     1831 */
     1832static int scmSettingsBaseParseString(PSCMSETTINGSBASE pBase, const char *pszLine)
     1833{
     1834    int    cArgs;
     1835    char **papszArgs;
     1836    int rc = RTGetOptArgvFromString(&papszArgs, &cArgs, pszLine, NULL);
     1837    if (RT_SUCCESS(rc))
     1838    {
     1839        RTGETOPTUNION   ValueUnion;
     1840        RTGETOPTSTATE   GetOptState;
     1841        rc = RTGetOptInit(&GetOptState, cArgs, papszArgs, &g_aScmOpts[0], RT_ELEMENTS(g_aScmOpts), 0, 0 /*fFlags*/);
     1842        if (RT_SUCCESS(rc))
     1843        {
     1844            while ((rc = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
     1845            {
     1846                rc = scmSettingsBaseHandleOpt(pBase, rc, &ValueUnion);
     1847                if (RT_FAILURE(rc))
     1848                    break;
     1849            }
     1850        }
     1851        RTGetOptArgvFree(papszArgs);
     1852    }
     1853
     1854    return rc;
     1855}
     1856
     1857/**
     1858 * Parses an unterminated option string.
     1859 *
     1860 * @returns IPRT status code.
     1861 * @param   pBase               The base settings structure to apply the options
     1862 *                              to.
     1863 * @param   pchLine             The line.
     1864 * @param   cchLine             The line length.
     1865 */
     1866static int scmSettingsBaseParseStringN(PSCMSETTINGSBASE pBase, const char *pchLine, size_t cchLine)
     1867{
     1868    char *pszLine = RTStrDupN(pchLine, cchLine);
     1869    if (!pszLine)
     1870        return VERR_NO_MEMORY;
     1871    int rc = scmSettingsBaseParseString(pBase, pszLine);
     1872    RTStrFree(pszLine);
     1873    return rc;
     1874}
     1875
     1876/**
     1877 * Verifies the options string.
     1878 *
     1879 * @returns IPRT status code.
     1880 * @param   pszOptions          The options to verify .
     1881 */
     1882static int scmSettingsBaseVerifyString(const char *pszOptions)
     1883{
     1884    SCMSETTINGSBASE Base;
     1885    int rc = scmSettingsBaseInit(&Base);
     1886    if (RT_SUCCESS(rc))
     1887    {
     1888        rc = scmSettingsBaseParseString(&Base, pszOptions);
     1889        scmSettingsBaseDelete(&Base);
     1890    }
     1891    return rc;
     1892}
     1893
     1894/**
     1895 * Loads settings found in editor and SCM settings directives within the
     1896 * document (@a pStream).
     1897 *
     1898 * @returns IPRT status code.
     1899 * @param   pBase               The settings base to load settings into.
     1900 * @param   pStream             The stream to scan for settings directives.
     1901 */
     1902static int scmSettingsBaseLoadFromDocument(PSCMSETTINGSBASE pBase, PSCMSTREAM pStream)
     1903{
     1904    /** @todo Editor and SCM settings directives in documents.  */
     1905    return VINF_SUCCESS;
     1906}
     1907
     1908/**
     1909 * Creates a new settings file struct, cloning @a pSettings.
     1910 *
     1911 * @returns IPRT status code.
     1912 * @param   ppSettings          Where to return the new struct.
     1913 * @param   pSettingsBase       The settings to inherit from.
     1914 */
     1915static int scmSettingsCreate(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pSettingsBase)
     1916{
     1917    PSCMSETTINGS pSettings = (PSCMSETTINGS)RTMemAlloc(sizeof(*pSettings));
     1918    if (!pSettings)
     1919        return VERR_NO_MEMORY;
     1920    int rc = scmSettingsBaseInitAndCopy(&pSettings->Base, pSettingsBase);
     1921    if (RT_SUCCESS(rc))
     1922    {
     1923        pSettings->pDown   = NULL;
     1924        pSettings->pUp     = NULL;
     1925        pSettings->paPairs = NULL;
     1926        pSettings->cPairs  = 0;
     1927        *ppSettings = pSettings;
     1928        return VINF_SUCCESS;
     1929    }
     1930    RTMemFree(pSettings);
     1931    return rc;
     1932}
     1933
     1934/**
     1935 * Destroys a settings structure.
     1936 *
     1937 * @param   pSettings           The settgins structure to destroy.  NULL is OK.
     1938 */
     1939static void scmSettingsDestroy(PSCMSETTINGS pSettings)
     1940{
     1941    if (pSettings)
     1942    {
     1943        scmSettingsBaseDelete(&pSettings->Base);
     1944        for (size_t i = 0; i < pSettings->cPairs; i++)
     1945        {
     1946            RTStrFree(pSettings->paPairs[i].pszPattern);
     1947            RTStrFree(pSettings->paPairs[i].pszOptions);
     1948            pSettings->paPairs[i].pszPattern = NULL;
     1949            pSettings->paPairs[i].pszOptions = NULL;
     1950        }
     1951        RTMemFree(pSettings->paPairs);
     1952        pSettings->paPairs = NULL;
     1953        RTMemFree(pSettings);
     1954    }
     1955}
     1956
     1957/**
     1958 * Adds a pattern/options pair to the settings structure.
     1959 *
     1960 * @returns IPRT status code.
     1961 * @param   pSettings           The settings.
     1962 * @param   pchLine             The line containing the unparsed pair.
     1963 * @param   cchLine             The length of the line.
     1964 */
     1965static int scmSettingsAddPair(PSCMSETTINGS pSettings, const char *pchLine, size_t cchLine)
     1966{
     1967    /*
     1968     * Split the string.
     1969     */
     1970    const char *pchOptions = (const char *)memchr(pchLine, ':', cchLine);
     1971    if (!pchOptions)
     1972        return VERR_INVALID_PARAMETER;
     1973    size_t cchPattern = pchOptions - pchLine - 1;
     1974    size_t cchOptions = cchLine - cchPattern - 1 - 1;
     1975    pchOptions++;
     1976
     1977    /* strip spaces everywhere */
     1978    while (cchPattern > 0 && RT_C_IS_SPACE(pchLine[cchPattern - 1]))
     1979        cchPattern--;
     1980    while (cchPattern > 0 && RT_C_IS_SPACE(*pchLine))
     1981        cchPattern--, pchLine++;
     1982
     1983    while (cchOptions > 0 && RT_C_IS_SPACE(pchOptions[cchOptions - 1]))
     1984        cchOptions--;
     1985    while (cchOptions > 0 && RT_C_IS_SPACE(*pchOptions))
     1986        cchOptions--, cchOptions++;
     1987
     1988    /* Quietly ignore empty patterns and empty options. */
     1989    if (!cchOptions || !cchPattern)
     1990        return VINF_SUCCESS;
     1991
     1992    /*
     1993     * Add the pair and verify the option string.
     1994     */
     1995    uint32_t iPair = pSettings->cPairs;
     1996    if ((iPair % 32) == 0)
     1997    {
     1998        void *pvNew = RTMemRealloc(pSettings->paPairs, (iPair + 32) * sizeof(pSettings->paPairs[0]));
     1999        if (!pvNew)
     2000            return VERR_NO_MEMORY;
     2001        pSettings->paPairs = (PSCMPATRNOPTPAIR)pvNew;
     2002    }
     2003
     2004    pSettings->paPairs[iPair].pszPattern = RTStrDupN(pchLine, cchPattern);
     2005    pSettings->paPairs[iPair].pszOptions = RTStrDupN(pchOptions, cchOptions);
     2006    int rc;
     2007    if (   pSettings->paPairs[iPair].pszPattern
     2008        && pSettings->paPairs[iPair].pszOptions)
     2009        rc = scmSettingsBaseVerifyString(pSettings->paPairs[iPair].pszOptions);
     2010    else
     2011        rc = VERR_NO_MEMORY;
     2012    if (RT_SUCCESS(rc))
     2013        pSettings->cPairs = iPair + 1;
     2014    else
     2015    {
     2016        RTStrFree(pSettings->paPairs[iPair].pszPattern);
     2017        RTStrFree(pSettings->paPairs[iPair].pszOptions);
     2018    }
     2019    return rc;
     2020}
     2021
     2022/**
     2023 * Loads in the settings from @a pszFilename.
     2024 *
     2025 * @returns IPRT status code.
     2026 * @param   pSettings           Where to load the settings file.
     2027 * @param   pszFilename         The file to load.
     2028 */
     2029static int scmSettingsLoadFile(PSCMSETTINGS pSettings, const char *pszFilename)
     2030{
     2031    SCMSTREAM Stream;
     2032    int rc = ScmStreamInitForReading(&Stream, pszFilename);
     2033    if (RT_FAILURE(rc))
     2034    {
     2035        RTMsgError("%s: ScmStreamInitForReading -> %Rrc\n", pszFilename, rc);
     2036        return rc;
     2037    }
     2038
     2039    SCMEOL      enmEol;
     2040    const char *pchLine;
     2041    size_t      cchLine;
     2042    while ((pchLine = ScmStreamGetLine(&Stream, &cchLine, &enmEol)) != NULL)
     2043    {
     2044        /* Ignore leading spaces. */
     2045        while (cchLine > 0 && RT_C_IS_SPACE(*pchLine))
     2046            pchLine++, cchLine--;
     2047
     2048        /* Ignore empty lines and comment lines. */
     2049        if (cchLine < 1 || *pchLine == '#')
     2050            continue;
     2051
     2052        /* What kind of line is it? */
     2053        const char *pchColon = (const char *)memchr(pchLine, ':', cchLine);
     2054        if (pchColon)
     2055            rc = scmSettingsAddPair(pSettings, pchLine, cchLine);
     2056        else
     2057            rc = scmSettingsBaseParseStringN(&pSettings->Base, pchLine, cchLine);
     2058        if (RT_FAILURE(rc))
     2059        {
     2060            RTMsgError("%s:%d: %Rrc\n", pszFilename, ScmStreamTellLine(&Stream), rc);
     2061            break;
     2062        }
     2063    }
     2064
     2065    if (RT_SUCCESS(rc))
     2066    {
     2067        rc = ScmStreamGetStatus(&Stream);
     2068        if (RT_FAILURE(rc))
     2069            RTMsgError("%s: ScmStreamGetStatus- > %Rrc\n", pszFilename, rc);
     2070    }
     2071
     2072    ScmStreamDelete(&Stream);
     2073    return rc;
     2074}
     2075
     2076/**
     2077 * Parse the specified settings file creating a new settings struct from it.
     2078 *
     2079 * @returns IPRT status code
     2080 * @param   ppSettings          Where to return the new settings.
     2081 * @param   pszFilename         The file to parse.
     2082 * @param   pSettingsBase       The base settings we inherit from.
     2083 */
     2084static int scmSettingsCreateFromFile(PSCMSETTINGS *ppSettings, const char *pszFilename, PCSCMSETTINGSBASE pSettingsBase)
     2085{
     2086    PSCMSETTINGS pSettings;
     2087    int rc = scmSettingsCreate(&pSettings, pSettingsBase);
     2088    if (RT_SUCCESS(rc))
     2089    {
     2090        rc = scmSettingsLoadFile(pSettings, pszFilename);
     2091        if (RT_SUCCESS(rc))
     2092        {
     2093            *ppSettings = pSettings;
     2094            return VINF_SUCCESS;
     2095        }
     2096
     2097        scmSettingsDestroy(pSettings);
     2098    }
     2099    *ppSettings = NULL;
     2100    return rc;
     2101}
     2102
     2103
     2104/**
     2105 * Create an initial settings structure when starting processing a new file or
     2106 * directory.
     2107 *
     2108 * This will look for .scm-settings files from the root and down to the
     2109 * specified directory, combining them into the returned settings structure.
     2110 *
     2111 * @returns IPRT status code.
     2112 * @param   ppSettings          Where to return the pointer to the top stack
     2113 *                              object.
     2114 * @param   pBaseSettings       The base settings we inherit from (globals
     2115 *                              typically).
     2116 * @param   pszPath             The absolute path to the new directory or file.
     2117 */
     2118static int scmSettingsCreateForPath(PSCMSETTINGS *ppSettings, PCSCMSETTINGSBASE pBaseSettings, const char *pszPath)
     2119{
     2120    /*
     2121     * We'll be working with a stack copy of the path.
     2122     */
     2123    char    szFile[RTPATH_MAX];
     2124    size_t  cchDir = strlen(pszPath);
     2125    if (cchDir >= sizeof(szFile) - sizeof(SCM_SETTINGS_FILENAME))
     2126        return VERR_FILENAME_TOO_LONG;
     2127
     2128    /*
     2129     * Create the bottom-most settings.
     2130     */
     2131    PSCMSETTINGS pSettings;
     2132    int rc = scmSettingsCreate(&pSettings, pBaseSettings);
     2133    if (RT_FAILURE(rc))
     2134        return rc;
     2135
     2136    /*
     2137     * Enumerate the path components from the root and down. Load any setting
     2138     * files we find.
     2139     */
     2140    size_t cComponents = RTPathCountComponents(pszPath);
     2141    for (size_t i = 1; i < cComponents; i++)
     2142    {
     2143        rc = RTPathCopyComponents(szFile, sizeof(szFile), pszPath, i);
     2144        if (RT_SUCCESS(rc))
     2145            rc = RTPathAppend(szFile, sizeof(szFile), SCM_SETTINGS_FILENAME);
     2146        if (RT_FAILURE(rc))
     2147            break;
     2148        if (RTFileExists(szFile))
     2149        {
     2150            rc = scmSettingsLoadFile(pSettings, szFile);
     2151            if (RT_FAILURE(rc))
     2152                break;
     2153        }
     2154    }
     2155
     2156    if (RT_SUCCESS(rc))
     2157        *ppSettings = pSettings;
     2158    else
     2159        scmSettingsDestroy(pSettings);
     2160    return rc;
     2161}
     2162
     2163/**
     2164 * Pushes a new settings set onto the stack.
     2165 *
     2166 * @param   ppSettingsStack     The pointer to the pointer to the top stack
     2167 *                              element.  This will be used as input and output.
     2168 * @param   pSettings           The settings to push onto the stack.
     2169 */
     2170static void scmSettingsStackPush(PSCMSETTINGS *ppSettingsStack, PSCMSETTINGS pSettings)
     2171{
     2172    PSCMSETTINGS pOld = *ppSettingsStack;
     2173    pSettings->pDown  = pOld;
     2174    pSettings->pUp    = NULL;
     2175    if (pOld)
     2176        pOld->pUp = pSettings;
     2177    *ppSettingsStack = pSettings;
     2178}
     2179
     2180/**
     2181 * Pushes the settings of the specified directory onto the stack.
     2182 *
     2183 * We will load any .scm-settings in the directory.  A stack entry is added even
     2184 * if no settings file was found.
     2185 *
     2186 * @returns IPRT status code.
     2187 * @param   ppSettingsStack     The pointer to the pointer to the top stack
     2188 *                              element.  This will be used as input and output.
     2189 * @param   pszDir              The directory to do this for.
     2190 */
     2191static int scmSettingsStackPushDir(PSCMSETTINGS *ppSettingsStack, const char *pszDir)
     2192{
     2193    char szFile[RTPATH_MAX];
     2194    int rc = RTPathJoin(szFile, sizeof(szFile), pszDir, SCM_SETTINGS_FILENAME);
     2195    if (RT_SUCCESS(rc))
     2196    {
     2197        PSCMSETTINGS pSettings;
     2198        rc = scmSettingsCreate(&pSettings, &(*ppSettingsStack)->Base);
     2199        if (RT_SUCCESS(rc))
     2200        {
     2201            if (RTFileExists(szFile))
     2202                rc = scmSettingsLoadFile(pSettings, szFile);
     2203            if (RT_SUCCESS(rc))
     2204            {
     2205                scmSettingsStackPush(ppSettingsStack, pSettings);
     2206                return VINF_SUCCESS;
     2207            }
     2208
     2209            scmSettingsDestroy(pSettings);
     2210        }
     2211    }
     2212    return rc;
     2213}
     2214
     2215
     2216/**
     2217 * Pops a settings set off the stack.
     2218 *
     2219 * @returns The popped setttings.
     2220 * @param   ppSettingsStack     The pointer to the pointer to the top stack
     2221 *                              element.  This will be used as input and output.
     2222 */
     2223static PSCMSETTINGS scmSettingsStackPop(PSCMSETTINGS *ppSettingsStack)
     2224{
     2225    PSCMSETTINGS pRet = *ppSettingsStack;
     2226    PSCMSETTINGS pNew = pRet ? pRet->pDown : NULL;
     2227    *ppSettingsStack = pNew;
     2228    if (pNew)
     2229        pNew->pUp    = NULL;
     2230    if (pRet)
     2231    {
     2232        pRet->pUp    = NULL;
     2233        pRet->pDown  = NULL;
     2234    }
     2235    return pRet;
     2236}
     2237
     2238/**
     2239 * Pops and destroys the top entry of the stack.
     2240 *
     2241 * @param   ppSettingsStack     The pointer to the pointer to the top stack
     2242 *                              element.  This will be used as input and output.
     2243 */
     2244static void scmSettingsStackPopAndDestroy(PSCMSETTINGS *ppSettingsStack)
     2245{
     2246    scmSettingsDestroy(scmSettingsStackPop(ppSettingsStack));
     2247}
     2248
     2249/**
     2250 * Constructs the base settings for the specified file name.
     2251 *
     2252 * @returns IPRT status code.
     2253 * @param   pSettingsStack      The top element on the settings stack.
     2254 * @param   pszFilename         The file name.
     2255 * @param   pszBasename         The base name (pointer within @a pszFilename).
     2256 * @param   cchBasename         The length of the base name.  (For passing to
     2257 *                              RTStrSimplePatternMultiMatch.)
     2258 * @param   pBase               Base settings to initialize.
     2259 */
     2260static int scmSettingsStackMakeFileBase(PCSCMSETTINGS pSettingsStack, const char *pszFilename,
     2261                                        const char *pszBasename, size_t cchBasename, PSCMSETTINGSBASE pBase)
     2262{
     2263    int rc = scmSettingsBaseInitAndCopy(pBase, &pSettingsStack->Base);
     2264    if (RT_SUCCESS(rc))
     2265    {
     2266        /* find the bottom entry in the stack. */
     2267        PCSCMSETTINGS pCur = pSettingsStack;
     2268        while (pCur->pDown)
     2269            pCur = pCur->pDown;
     2270
     2271        /* Work our way up thru the stack and look for matching pairs. */
     2272        while (pCur)
     2273        {
     2274            size_t const cPairs = pCur->cPairs;
     2275            if (cPairs)
     2276            {
     2277                for (size_t i = 0; i < cPairs; i++)
     2278                    if (   RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
     2279                                                        pszBasename,  cchBasename, NULL)
     2280                        || RTStrSimplePatternMultiMatch(pCur->paPairs[i].pszPattern, RTSTR_MAX,
     2281                                                        pszFilename,  RTSTR_MAX, NULL))
     2282                    {
     2283                        rc = scmSettingsBaseParseString(pBase, pCur->paPairs[i].pszOptions);
     2284                        if (RT_FAILURE(rc))
     2285                            break;
     2286                    }
     2287                if (RT_FAILURE(rc))
     2288                    break;
     2289            }
     2290
     2291            /* advance */
     2292            pCur = pCur->pUp;
     2293        }
     2294    }
     2295    if (RT_FAILURE(rc))
     2296        scmSettingsBaseDelete(pBase);
     2297    return rc;
     2298}
     2299
    15572300
    15582301/* -=-=-=-=-=- misc -=-=-=-=-=- */
     
    15882331 * @param   pIn                 The input stream.
    15892332 * @param   pOut                The output stream.
    1590  */
    1591 static bool rewrite_StripTrailingBlanks(PSCMSTREAM pIn, PSCMSTREAM pOut)
    1592 {
    1593     if (!g_fStripTrailingBlanks)
     2333 * @param   pSettings           The settings.
     2334 */
     2335static bool rewrite_StripTrailingBlanks(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
     2336{
     2337    if (!pSettings->fStripTrailingBlanks)
    15942338        return false;
    15952339
     
    16262370 * @param   pIn                 The input stream.
    16272371 * @param   pOut                The output stream.
    1628  */
    1629 static bool rewrite_ExpandTabs(PSCMSTREAM pIn, PSCMSTREAM pOut)
    1630 {
    1631     if (!g_fConvertTabs)
     2372 * @param   pSettings           The settings.
     2373 */
     2374static bool rewrite_ExpandTabs(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
     2375{
     2376    if (!pSettings->fConvertTabs)
    16322377        return false;
    16332378
    1634     bool        fModified = false;
    1635     SCMEOL      enmEol;
    1636     size_t      cchLine;
    1637     const char *pchLine;
     2379    size_t const    cchTab = pSettings->cchTab;
     2380    bool            fModified = false;
     2381    SCMEOL          enmEol;
     2382    size_t          cchLine;
     2383    const char     *pchLine;
    16382384    while ((pchLine = ScmStreamGetLine(pIn, &cchLine, &enmEol)) != NULL)
    16392385    {
     
    16522398                ScmStreamWrite(pOut, pchChunk, cchChunk);
    16532399
    1654                 size_t  cchToTab = g_cchTab - offTab % g_cchTab;
     2400                size_t  cchToTab = cchTab - offTab % cchTab;
    16552401                ScmStreamWrite(pOut, g_szTabSpaces, cchToTab);
    16562402                offTab += cchToTab;
     
    16822428 * @param   pIn                 The input stream.
    16832429 * @param   pOut                The output stream.
     2430 * @param   pSettings           The settings.
    16842431 * @param   enmDesiredEol       The desired end of line indicator type.
    16852432 */
    1686 static bool rewrite_ForceEol(PSCMSTREAM pIn, PSCMSTREAM pOut, SCMEOL enmDesiredEol)
    1687 {
    1688     if (!g_fConvertEol)
     2433static bool rewrite_ForceEol(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings, SCMEOL enmDesiredEol)
     2434{
     2435    if (!pSettings->fConvertEol)
    16892436        return false;
    16902437
     
    17162463 * @param   pIn                 The input stream.
    17172464 * @param   pOut                The output stream.
    1718  */
    1719 static bool rewrite_ForceNativeEol(PSCMSTREAM pIn, PSCMSTREAM pOut)
     2465 * @param   pSettings           The settings.
     2466 */
     2467static bool rewrite_ForceNativeEol(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
    17202468{
    17212469#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    1722     return rewrite_ForceEol(pIn, pOut, SCMEOL_CRLF);
     2470    return rewrite_ForceEol(pIn, pOut, pSettings, SCMEOL_CRLF);
    17232471#else
    1724     return rewrite_ForceEol(pIn, pOut, SCMEOL_LF);
     2472    return rewrite_ForceEol(pIn, pOut, pSettings, SCMEOL_LF);
    17252473#endif
    17262474}
     
    17322480 * @param   pIn                 The input stream.
    17332481 * @param   pOut                The output stream.
    1734  */
    1735 static bool rewrite_ForceLF(PSCMSTREAM pIn, PSCMSTREAM pOut)
    1736 {
    1737     return rewrite_ForceEol(pIn, pOut, SCMEOL_LF);
     2482 * @param   pSettings           The settings.
     2483 */
     2484static bool rewrite_ForceLF(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
     2485{
     2486    return rewrite_ForceEol(pIn, pOut, pSettings, SCMEOL_LF);
    17382487}
    17392488
     
    17442493 * @param   pIn                 The input stream.
    17452494 * @param   pOut                The output stream.
    1746  */
    1747 static bool rewrite_ForceCRLF(PSCMSTREAM pIn, PSCMSTREAM pOut)
    1748 {
    1749     return rewrite_ForceEol(pIn, pOut, SCMEOL_CRLF);
     2495 * @param   pSettings           The settings.
     2496 */
     2497static bool rewrite_ForceCRLF(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
     2498{
     2499    return rewrite_ForceEol(pIn, pOut, pSettings, SCMEOL_CRLF);
    17502500}
    17512501
     
    17572507 * @param   pIn                 The input stream.
    17582508 * @param   pOut                The output stream.
     2509 * @param   pSettings           The settings.
    17592510 *
    17602511 * @remarks ASSUMES trailing white space has been removed already.
    17612512 */
    1762 static bool rewrite_AdjustTrailingLines(PSCMSTREAM pIn, PSCMSTREAM pOut)
    1763 {
    1764     if (!g_fStripTrailingLines && !g_fForceTrailingLine && !g_fForceFinalEol)
     2513static bool rewrite_AdjustTrailingLines(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
     2514{
     2515    if (   !pSettings->fStripTrailingLines
     2516        && !pSettings->fForceTrailingLine
     2517        && !pSettings->fForceFinalEol)
    17652518        return false;
    17662519
     
    17742527    size_t cLinesNew = cLines;
    17752528
    1776     if (   g_fStripTrailingLines
     2529    if (   pSettings->fStripTrailingLines
    17772530        && ScmStreamIsWhiteLine(pIn, cLinesNew - 1))
    17782531    {
     
    17822535    }
    17832536
    1784     if (    g_fForceTrailingLine
     2537    if (    pSettings->fForceTrailingLine
    17852538        && !ScmStreamIsWhiteLine(pIn, cLinesNew - 1))
    17862539        cLinesNew++;
    17872540
    1788     bool fFixMissingEol = g_fForceFinalEol
     2541    bool fFixMissingEol = pSettings->fForceFinalEol
    17892542                       && ScmStreamGetEolByLine(pIn, cLinesNew - 1) == SCMEOL_NONE;
    17902543
     
    18042557            ScmStreamPutLine(pOut, "", 0, ScmStreamGetEol(pIn));
    18052558    }
     2559    /* Fix missing EOL if required. */
    18062560    else if (fFixMissingEol)
    18072561    {
     
    18222576 * @param   pIn                 The input stream.
    18232577 * @param   pOut                The output stream.
    1824  */
    1825 static bool rewrite_Makefile_kup(PSCMSTREAM pIn, PSCMSTREAM pOut)
     2578 * @param   pSettings           The settings.
     2579 */
     2580static bool rewrite_Makefile_kup(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
    18262581{
    18272582    /* These files should be zero bytes. */
     
    18382593 * @param   pIn                 The input stream.
    18392594 * @param   pOut                The output stream.
     2595 * @param   pSettings           The settings.
    18402596 *
    18412597 * @todo
     
    18452601 *      - line continuation slashes should only be preceeded by one space.
    18462602 */
    1847 static bool rewrite_Makefile_kmk(PSCMSTREAM pIn, PSCMSTREAM pOut)
     2603static bool rewrite_Makefile_kmk(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
    18482604{
    18492605    return false;
     
    18562612 * @param   pIn                 The input stream.
    18572613 * @param   pOut                The output stream.
     2614 * @param   pSettings           The settings.
    18582615 *
    18592616 * @todo
     
    18792636 *      - space between functions.
    18802637 */
    1881 static bool rewrite_C_and_CPP(PSCMSTREAM pIn, PSCMSTREAM pOut)
    1882 {
     2638static bool rewrite_C_and_CPP(PSCMSTREAM pIn, PSCMSTREAM pOut, PCSCMSETTINGSBASE pSettings)
     2639{
     2640
    18832641    return false;
    18842642}
     
    18942652 * @param   cchBasename         The length of the base name.  (For passing to
    18952653 *                              RTStrSimplePatternMultiMatch.)
    1896  */
    1897 static int scmProcessFile(const char *pszFilename, const char *pszBasename, size_t cchBasename)
     2654 * @param   pBaseSettings       The base settings to use.  It's OK to modify
     2655 *                              these.
     2656 */
     2657static int scmProcessFileInner(const char *pszFilename, const char *pszBasename, size_t cchBasename,
     2658                               PSCMSETTINGSBASE pBaseSettings)
    18982659{
    18992660    /*
     
    19462707
    19472708        /*
    1948          * Create two more streams for output and push the text thru all the
    1949          * rewriters, switching the two streams around when something is
    1950          * actually rewritten.  Stream1 remains unchanged.
     2709         * Gather SCM and editor settings from the stream.
    19512710         */
    1952         SCMSTREAM Stream2;
    1953         rc = ScmStreamInitForWriting(&Stream2, &Stream1);
     2711        rc = scmSettingsBaseLoadFromDocument(pBaseSettings, &Stream1);
    19542712        if (RT_SUCCESS(rc))
    19552713        {
    1956             SCMSTREAM Stream3;
    1957             rc = ScmStreamInitForWriting(&Stream3, &Stream1);
     2714            ScmStreamRewindForReading(&Stream1);
     2715
     2716            /*
     2717             * Create two more streams for output and push the text thru all the
     2718             * rewriters, switching the two streams around when something is
     2719             * actually rewritten.  Stream1 remains unchanged.
     2720             */
     2721            SCMSTREAM Stream2;
     2722            rc = ScmStreamInitForWriting(&Stream2, &Stream1);
    19582723            if (RT_SUCCESS(rc))
    19592724            {
    1960                 bool        fModified = false;
    1961                 PSCMSTREAM  pIn       = &Stream1;
    1962                 PSCMSTREAM  pOut      = &Stream2;
    1963                 for (size_t iRw = 0; iRw < pCfg->cRewriters; iRw++)
     2725                SCMSTREAM Stream3;
     2726                rc = ScmStreamInitForWriting(&Stream3, &Stream1);
     2727                if (RT_SUCCESS(rc))
    19642728                {
    1965                     bool fRc = pCfg->papfnRewriter[iRw](pIn, pOut);
    1966                     if (fRc)
     2729                    bool        fModified = false;
     2730                    PSCMSTREAM  pIn       = &Stream1;
     2731                    PSCMSTREAM  pOut      = &Stream2;
     2732                    for (size_t iRw = 0; iRw < pCfg->cRewriters; iRw++)
    19672733                    {
    1968                         PSCMSTREAM pTmp = pOut;
    1969                         pOut = pIn == &Stream1 ? &Stream3 : pIn;
    1970                         pIn  = pTmp;
    1971                         fModified = true;
     2734                        bool fRc = pCfg->papfnRewriter[iRw](pIn, pOut, pBaseSettings);
     2735                        if (fRc)
     2736                        {
     2737                            PSCMSTREAM pTmp = pOut;
     2738                            pOut = pIn == &Stream1 ? &Stream3 : pIn;
     2739                            pIn  = pTmp;
     2740                            fModified = true;
     2741                        }
     2742                        ScmStreamRewindForReading(pIn);
     2743                        ScmStreamRewindForWriting(pOut);
    19722744                    }
    1973                     ScmStreamRewindForReading(pIn);
    1974                     ScmStreamRewindForWriting(pOut);
    1975                 }
    1976 
    1977                 /*
    1978                  * If rewritten, write it back to disk.
    1979                  */
    1980                 if (fModified)
    1981                 {
    1982                     if (!g_fDryRun)
     2745
     2746                    /*
     2747                     * If rewritten, write it back to disk.
     2748                     */
     2749                    if (fModified)
    19832750                    {
    1984                         ScmVerbose(1, "Writing modified file to \"%s%s\"\n", pszFilename, g_pszChangedSuff);
    1985                         rc = ScmStreamWriteToFile(pIn, "%s%s", pszFilename, g_pszChangedSuff);
    1986                         if (RT_FAILURE(rc))
    1987                             RTMsgError("Error writing '%s%s': %Rrc\n", pszFilename, g_pszChangedSuff, rc);
     2751                        if (!g_fDryRun)
     2752                        {
     2753                            ScmVerbose(1, "Writing modified file to \"%s%s\"\n", pszFilename, g_pszChangedSuff);
     2754                            rc = ScmStreamWriteToFile(pIn, "%s%s", pszFilename, g_pszChangedSuff);
     2755                            if (RT_FAILURE(rc))
     2756                                RTMsgError("Error writing '%s%s': %Rrc\n", pszFilename, g_pszChangedSuff, rc);
     2757                        }
     2758                        else
     2759                        {
     2760                            ScmVerbose(1, "Would have modified file \"%s\"\n", pszFilename);
     2761                            ScmDiffStreams(pszFilename, &Stream1, pIn, g_fDiffIgnoreEol, g_fDiffIgnoreLeadingWS,
     2762                                           g_fDiffIgnoreTrailingWS, g_fDiffSpecialChars, pBaseSettings->cchTab, g_pStdOut);
     2763                        }
    19882764                    }
    19892765                    else
    1990                     {
    1991                         ScmVerbose(1, "Would have modified file \"%s\"\n", pszFilename);
    1992                         ScmDiffStreams(pszFilename, &Stream1, pIn, g_fDiffIgnoreEol, g_fDiffIgnoreLeadingWS,
    1993                                        g_fDiffIgnoreTrailingWS, g_fDiffSpecialChars, g_pStdOut);
    1994                     }
     2766                        ScmVerbose(2, "Unchanged \"%s\"\n", pszFilename);
     2767
     2768                    ScmStreamDelete(&Stream3);
    19952769                }
    19962770                else
    1997                     ScmVerbose(2, "Unchanged \"%s\"\n", pszFilename);
    1998 
    1999                 ScmStreamDelete(&Stream3);
     2771                    RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
     2772                ScmStreamDelete(&Stream2);
    20002773            }
    20012774            else
    20022775                RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
    2003             ScmStreamDelete(&Stream2);
    20042776        }
    20052777        else
    2006             RTMsgError("Failed to init stream for writing: %Rrc\n", rc);
     2778            RTMsgError("scmSettingsBaseLoadFromDocument: %Rrc\n", rc);
    20072779    }
    20082780    else
     
    20122784    return rc;
    20132785}
     2786
     2787/**
     2788 * Processes a file.
     2789 *
     2790 * This is just a wrapper for scmProcessFileInner for avoid wasting stack in the
     2791 * directory recursion method.
     2792 *
     2793 * @returns IPRT status code.
     2794 * @param   pszFilename         The file name.
     2795 * @param   pszBasename         The base name (pointer within @a pszFilename).
     2796 * @param   cchBasename         The length of the base name.  (For passing to
     2797 *                              RTStrSimplePatternMultiMatch.)
     2798 * @param   pSettingsStack      The settings stack (pointer to the top element).
     2799 */
     2800static int scmProcessFile(const char *pszFilename, const char *pszBasename, size_t cchBasename,
     2801                          PSCMSETTINGS pSettingsStack)
     2802{
     2803    SCMSETTINGSBASE Base;
     2804    int rc = scmSettingsStackMakeFileBase(pSettingsStack, pszFilename, pszBasename, cchBasename, &Base);
     2805    if (RT_SUCCESS(rc))
     2806    {
     2807        rc = scmProcessFileInner(pszFilename, pszBasename, cchBasename, &Base);
     2808        scmSettingsBaseDelete(&Base);
     2809    }
     2810    return rc;
     2811}
     2812
    20142813
    20152814/**
     
    20432842 * @param   pEntry              Directory entry buffer.  This is also passed
    20442843 *                              along when recursing to save stack space.
     2844 * @param   pSettingsStack      The settings stack (pointer to the top element).
    20452845 * @param   iRecursion          The recursion depth.  This is used to restrict
    20462846 *                              the recursions.
    20472847 */
    2048 static int scmProcessDirTreeRecursion(char *pszBuf, size_t cchDir, PRTDIRENTRY pEntry, unsigned iRecursion)
     2848static int scmProcessDirTreeRecursion(char *pszBuf, size_t cchDir, PRTDIRENTRY pEntry,
     2849                                      PSCMSETTINGS pSettingsStack, unsigned iRecursion)
    20492850{
    20502851    Assert(cchDir > 1 && pszBuf[cchDir - 1] == '.');
     
    21012902        /* Process the file or directory, skip the rest. */
    21022903        if (enmType == RTDIRENTRYTYPE_FILE)
    2103             rc = scmProcessFile(pszBuf, pEntry->szName, pEntry->cbName);
     2904            rc = scmProcessFile(pszBuf, pEntry->szName, pEntry->cbName, pSettingsStack);
    21042905        else if (enmType == RTDIRENTRYTYPE_DIRECTORY)
    21052906        {
     
    21252926               )
    21262927            {
    2127                 rc = scmProcessDirTreeRecursion(pszBuf, cchSubDir, pEntry, iRecursion + 1);
     2928                rc = scmSettingsStackPushDir(&pSettingsStack, pszBuf);
     2929                if (RT_SUCCESS(rc))
     2930                {
     2931                    rc = scmProcessDirTreeRecursion(pszBuf, cchSubDir, pEntry, pSettingsStack, iRecursion + 1);
     2932                    scmSettingsStackPopAndDestroy(&pSettingsStack);
     2933                }
    21282934            }
    21292935        }
     
    21432949 *                              a RTPATH_MAX sized buffer.
    21442950 */
    2145 static int scmProcessDirTree(char *pszDir)
     2951static int scmProcessDirTree(char *pszDir, PSCMSETTINGS pSettingsStack)
    21462952{
    21472953    /*
     
    21522958    {
    21532959        RTDIRENTRY Entry;
    2154         rc = scmProcessDirTreeRecursion(pszDir, strlen(pszDir), &Entry, 0);
     2960        rc = scmProcessDirTreeRecursion(pszDir, strlen(pszDir), &Entry, pSettingsStack, 0);
    21552961    }
    21562962    else
     
    21642970 *
    21652971 * @returns IPRT status code
    2166  * @param   pszSomething    What we found in the commad line arguments.
    2167  */
    2168 static int scmProcessSomething(const char *pszSomething)
     2972 * @param   pszSomething        What we found in the commad line arguments.
     2973 * @param   pSettingsStack      The settings stack (pointer to the top element).
     2974 */
     2975static int scmProcessSomething(const char *pszSomething, PSCMSETTINGS pSettingsStack)
    21692976{
    21702977    char szBuf[RTPATH_MAX];
     
    21722979    if (RT_SUCCESS(rc))
    21732980    {
    2174         if (RTFileExists(szBuf))
     2981        PSCMSETTINGS pSettings;
     2982        rc = scmSettingsCreateForPath(&pSettings, &pSettingsStack->Base, szBuf);
     2983        if (RT_SUCCESS(rc))
    21752984        {
    2176             const char *pszBasename = RTPathFilename(szBuf);
    2177             if (pszBasename)
     2985            scmSettingsStackPush(&pSettingsStack, pSettings);
     2986
     2987            if (RTFileExists(szBuf))
    21782988            {
    2179                 size_t cchBasename = strlen(pszBasename);
    2180                 rc = scmProcessFile(szBuf, pszBasename, cchBasename);
     2989                const char *pszBasename = RTPathFilename(szBuf);
     2990                if (pszBasename)
     2991                {
     2992                    size_t cchBasename = strlen(pszBasename);
     2993                    rc = scmProcessFile(szBuf, pszBasename, cchBasename, pSettingsStack);
     2994                }
     2995                else
     2996                {
     2997                    RTMsgError("RTPathFilename: NULL\n");
     2998                    rc = VERR_IS_A_DIRECTORY;
     2999                }
    21813000            }
    21823001            else
    2183             {
    2184                 RTMsgError("RTPathFilename: NULL\n");
    2185                 rc = VERR_IS_A_DIRECTORY;
    2186             }
     3002                rc = scmProcessDirTree(szBuf, pSettingsStack);
     3003
     3004            PSCMSETTINGS pPopped = scmSettingsStackPop(&pSettingsStack);
     3005            Assert(pPopped == pSettings);
     3006            scmSettingsDestroy(pSettings);
    21873007        }
    21883008        else
    2189             rc = scmProcessDirTree(szBuf);
     3009            RTMsgError("scmSettingsInitStack: %Rrc\n", rc);
    21903010    }
    21913011    else
     
    22013021
    22023022    /*
     3023     * Init the settings.
     3024     */
     3025    PSCMSETTINGS pSettings;
     3026    rc = scmSettingsCreate(&pSettings, &g_Defaults);
     3027    if (RT_FAILURE(rc))
     3028    {
     3029        RTMsgError("scmSettingsCreate: %Rrc\n", rc);
     3030        return 1;
     3031    }
     3032
     3033    /*
    22033034     * Parse arguments and process input in order (because this is the only
    22043035     * thing that works at the moment).
    22053036     */
    2206     enum SCMOPT
    2207     {
    2208         SCMOPT_DIFF_IGNORE_EOL = 10000,
    2209         SCMOPT_DIFF_NO_IGNORE_EOL,
    2210         SCMOPT_DIFF_IGNORE_SPACE,
    2211         SCMOPT_DIFF_NO_IGNORE_SPACE,
    2212         SCMOPT_DIFF_IGNORE_LEADING_SPACE,
    2213         SCMOPT_DIFF_NO_IGNORE_LEADING_SPACE,
    2214         SCMOPT_DIFF_IGNORE_TRAILING_SPACE,
    2215         SCMOPT_DIFF_NO_IGNORE_TRAILING_SPACE,
    2216         SCMOPT_DIFF_SPECIAL_CHARS,
    2217         SCMOPT_DIFF_NO_SPECIAL_CHARS,
    2218         SCMOPT_STRIP_TRAILING_LINES,
    2219         SCMOPT_NO_STRIP_TRAILING_LINES,
    2220         SCMOPT_FORCE_FINAL_EOL,
    2221         SCMOPT_NO_FORCE_FINAL_EOL,
    2222         SCMOPT_FORCE_TRAILING_LINE,
    2223         SCMOPT_NO_FORCE_TRAILING_LINE,
    2224         SCMOPT_LAST
    2225     };
    2226     static const RTGETOPTDEF s_aOpts[] =
    2227     {
    2228         { "--strip-trailing-blanks",            'b',                                    RTGETOPT_REQ_NOTHING },
    2229         { "--no-strip-trailing-blanks",         'B',                                    RTGETOPT_REQ_NOTHING },
    2230         { "--convert-tabs",                     'c',                                    RTGETOPT_REQ_NOTHING },
    2231         { "--no-convert-tabs",                  'C',                                    RTGETOPT_REQ_NOTHING },
     3037    static RTGETOPTDEF s_aOpts[16 + RT_ELEMENTS(g_aScmOpts)] =
     3038    {
     3039        { "--dry-run",                          'd',                                    RTGETOPT_REQ_NOTHING },
     3040        { "--real-run",                         'D',                                    RTGETOPT_REQ_NOTHING },
     3041        { "--file-filter",                      'f',                                    RTGETOPT_REQ_STRING  },
     3042        { "--help",                             'h',                                    RTGETOPT_REQ_NOTHING },
     3043        { "--quiet",                            'q',                                    RTGETOPT_REQ_NOTHING },
     3044        { "--verbose",                          'v',                                    RTGETOPT_REQ_NOTHING },
     3045        { "--version",                          'V',                                    RTGETOPT_REQ_NOTHING },
    22323046        { "--diff-ignore-eol",                  SCMOPT_DIFF_IGNORE_EOL,                 RTGETOPT_REQ_NOTHING },
    22333047        { "--diff-no-ignore-eol",               SCMOPT_DIFF_NO_IGNORE_EOL,              RTGETOPT_REQ_NOTHING },
     
    22403054        { "--diff-special-chars",               SCMOPT_DIFF_SPECIAL_CHARS,              RTGETOPT_REQ_NOTHING },
    22413055        { "--diff-no-special-chars",            SCMOPT_DIFF_NO_SPECIAL_CHARS,           RTGETOPT_REQ_NOTHING },
    2242         { "--dry-run",                          'd',                                    RTGETOPT_REQ_NOTHING },
    2243         { "--real-run",                         'D',                                    RTGETOPT_REQ_NOTHING },
    2244         { "--strip-trailing-lines",             SCMOPT_STRIP_TRAILING_LINES,            RTGETOPT_REQ_NOTHING },
    2245         { "--strip-no-trailing-lines",          SCMOPT_NO_STRIP_TRAILING_LINES,         RTGETOPT_REQ_NOTHING },
    2246         { "--force-final-eol",                  SCMOPT_FORCE_FINAL_EOL,                 RTGETOPT_REQ_NOTHING },
    2247         { "--no-force-final-eol",               SCMOPT_NO_FORCE_FINAL_EOL,              RTGETOPT_REQ_NOTHING },
    2248         { "--force-trailing-line",              SCMOPT_FORCE_TRAILING_LINE,             RTGETOPT_REQ_NOTHING },
    2249         { "--no-force-trailing-line",           SCMOPT_NO_FORCE_TRAILING_LINE,          RTGETOPT_REQ_NOTHING },
    2250         { "--convert-eol",                      'e',                                    RTGETOPT_REQ_NOTHING },
    2251         { "--no-convert-eol",                   'E',                                    RTGETOPT_REQ_NOTHING },
    2252         { "--file-filter",                      'f',                                    RTGETOPT_REQ_STRING  },
    2253         { "--help",                             'h',                                    RTGETOPT_REQ_NOTHING },
    2254         { "--quiet",                            'q',                                    RTGETOPT_REQ_NOTHING },
    2255         { "--tab-size",                         't',                                    RTGETOPT_REQ_UINT8   },
    2256         { "--verbose",                          'v',                                    RTGETOPT_REQ_NOTHING },
    22573056    };
     3057    memcpy(&s_aOpts[RT_ELEMENTS(s_aOpts) - RT_ELEMENTS(g_aScmOpts)], &g_aScmOpts[0], sizeof(g_aScmOpts));
    22583058
    22593059    RTGETOPTUNION   ValueUnion;
     
    22673067        switch (rc)
    22683068        {
    2269             case 'b':
    2270                 g_fStripTrailingBlanks = true;
    2271                 break;
    2272 
    2273             case 'B':
    2274                 g_fStripTrailingBlanks = false;
    2275                 break;
    2276 
    2277             case 'c':
    2278                 g_fConvertTabs = true;
    2279                 break;
    2280 
    2281             case 'C':
    2282                 g_fConvertTabs = false;
    2283                 break;
    2284 
    22853069            case 'd':
    22863070                g_fDryRun = true;
     
    22893073            case 'D':
    22903074                g_fDryRun = false;
    2291                 break;
    2292 
    2293             case 'e':
    2294                 g_fConvertEol = true;
    2295                 break;
    2296 
    2297             case 'E':
    2298                 g_fConvertEol = false;
    22993075                break;
    23003076
     
    23223098                break;
    23233099
    2324             case 't':
    2325                 if (   ValueUnion.u8 < 1
    2326                     || ValueUnion.u8 >= RT_ELEMENTS(g_szTabSpaces))
    2327                 {
    2328                     RTMsgError("Invalid tab size: %u - must be in {1..%u}\n",
    2329                                ValueUnion.u8, RT_ELEMENTS(g_szTabSpaces) - 1);
    2330                     return 2;
    2331                 }
    2332                 g_cchTab = ValueUnion.u8;
    2333                 break;
    2334 
    23353100            case 'v':
    23363101                g_iVerbosity++;
    23373102                break;
     3103
     3104            case 'V':
     3105            {
     3106                /* The following is assuming that svn does it's job here. */
     3107                static const char s_szRev[] = "$Revision$";
     3108                const char *psz = RTStrStripL(strchr(s_szRev, ' '));
     3109                RTPrintf("r%.*s\n", strchr(psz, ' ') - psz, psz);
     3110                return 0;
     3111            }
    23383112
    23393113            case SCMOPT_DIFF_IGNORE_EOL:
     
    23703144            case SCMOPT_DIFF_NO_SPECIAL_CHARS:
    23713145                g_fDiffSpecialChars = false;
    2372                 break;
    2373 
    2374             case SCMOPT_STRIP_TRAILING_LINES:
    2375                 g_fStripTrailingLines = true;
    2376                 break;
    2377             case SCMOPT_NO_STRIP_TRAILING_LINES:
    2378                 g_fStripTrailingLines = false;
    2379                 break;
    2380 
    2381             case SCMOPT_FORCE_FINAL_EOL:
    2382                 g_fForceFinalEol = true;
    2383                 break;
    2384             case SCMOPT_NO_FORCE_FINAL_EOL:
    2385                 g_fForceFinalEol = false;
    2386                 break;
    2387 
    2388             case SCMOPT_FORCE_TRAILING_LINE:
    2389                 g_fForceTrailingLine = true;
    2390                 break;
    2391             case SCMOPT_NO_FORCE_TRAILING_LINE:
    2392                 g_fForceTrailingLine = false;
    23933146                break;
    23943147
     
    24123165                    cProcessed++;
    24133166                }
    2414                 rc = scmProcessSomething(ValueUnion.psz);
     3167                rc = scmProcessSomething(ValueUnion.psz, pSettings);
    24153168                if (RT_FAILURE(rc))
    24163169                    return rc;
     
    24193172
    24203173            default:
     3174            {
     3175                int rc2 = scmSettingsBaseHandleOpt(&pSettings->Base, rc, &ValueUnion);
     3176                if (RT_SUCCESS(rc2))
     3177                    break;
     3178                if (rc2 != VERR_GETOPT_UNKNOWN_OPTION)
     3179                    return 2;
    24213180                return RTGetOptPrintError(rc, &ValueUnion);
     3181            }
    24223182        }
    24233183    }
    24243184
     3185    scmSettingsDestroy(pSettings);
    24253186    return 0;
    24263187}
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