/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
struct PredefinedPathVariables
{ // Predefined variables supported by substitute variables
LanguageType m_eLanguageType; // Language type of Office
OUString m_FixedVar[ PREDEFVAR_COUNT ]; // Variable value access by PreDefVariable
OUString m_FixedVarNames[ PREDEFVAR_COUNT ]; // Variable name access by PreDefVariable
};
// Special case (transient) values can change during runtime! // Don't store them in the pre defined struct static OUString GetWorkPath(); static OUString GetWorkVariableValue(); static OUString GetPathVariableValue();
VarNameToIndexMap m_aPreDefVarMap; // Mapping from pre-def variable names to enum for array access
PredefinedPathVariables m_aPreDefVars; // All predefined variables
std::vector<ReSubstFixedVarOrder> m_aReSubstFixedVarOrder; // To speed up resubstitution fixed variables (order for lookup)
};
// Init the predefined/fixed variable to index hash map for ( int i = 0; i < PREDEFVAR_COUNT; i++ )
{ // Store variable name into struct of predefined/fixed variables
m_aPreDefVars.m_FixedVarNames[i] = aFixedVarTable[i].pVarName;
// Sort predefined/fixed variable to path length for ( int i = 0; i < PREDEFVAR_COUNT; i++ )
{ if (( i != PREDEFVAR_WORKDIRURL ) && ( i != PREDEFVAR_PATH ))
{ // Special path variables, don't include into automatic resubstitution search! // $(workdirurl) is not allowed to resubstitute! This variable is the value of path settings entry // and it could be possible that it will be resubstituted by itself!! // Example: WORK_PATH=c:\test, $(workdirurl)=WORK_PATH => WORK_PATH=$(workdirurl) and this cannot be substituted!
ReSubstFixedVarOrder aFixedVar;
aFixedVar.eVariable = PreDefVariable(i);
aFixedVar.nVarValueLength = m_aPreDefVars.m_FixedVar[static_cast<sal_Int32>(aFixedVar.eVariable)].getLength();
m_aReSubstFixedVarOrder.push_back( aFixedVar );
}
}
sort(m_aReSubstFixedVarOrder.begin(),m_aReSubstFixedVarOrder.end());
}
// static
OUString SubstitutePathVariables::GetWorkPath()
{
OUString aWorkPath;
css::uno::Reference< css::container::XHierarchicalNameAccess > xPaths(officecfg::Office::Paths::Paths::get(), css::uno::UNO_QUERY_THROW); if (!(xPaths->getByHierarchicalName(u"['Work']/WritePath"_ustr) >>= aWorkPath)) // fallback in case config layer does not return a usable work dir value.
aWorkPath = GetWorkVariableValue();
return aWorkPath;
}
// static
OUString SubstitutePathVariables::GetWorkVariableValue()
{
OUString aWorkPath;
std::optional<OUString> x(officecfg::Office::Paths::Variables::Work::get()); if (!x)
{ // fallback to $HOME in case platform dependent config layer does not return // a usable work dir value.
osl::Security aSecurity;
aSecurity.getHomeDir( aWorkPath );
} else
aWorkPath = *x; return aWorkPath;
}
// Use vector with strings to detect endless recursions!
std::vector< OUString > aEndlessRecursiveDetector;
// Search for first occurrence of "$(...".
sal_Int32 nDepth = 0; bool bSubstitutionCompleted = false;
sal_Int32 nPosition = aWorkText.indexOf( "$(" );
sal_Int32 nLength = 0; // = count of letters from "$(" to ")" in string bool bVarNotSubstituted = false;
// Have we found any variable like "$(...)"? if ( nPosition != -1 )
{ // Yes; Get length of found variable. // If no ")" was found - nLength is set to 0 by default! see before.
sal_Int32 nEndPosition = aWorkText.indexOf( ')', nPosition ); if ( nEndPosition != -1 )
nLength = nEndPosition - nPosition + 1;
}
// Is there something to replace ? bool bWorkRetrieved = false; bool bWorkDirURLRetrieved = false; while (nDepth < nMaxRecursiveDepth)
{ while ( ( nPosition != -1 ) && ( nLength > 3 ) ) // "$(" ")"
{ // YES; Get the next variable for replace.
sal_Int32 nReplaceLength = 0;
OUString aReplacement;
OUString aSubString = aWorkText.copy( nPosition, nLength );
// Path variables are not case sensitive!
OUString aSubVarString = aSubString.toAsciiLowerCase();
VarNameToIndexMap::const_iterator pNTOIIter = m_aPreDefVarMap.find( aSubVarString ); if ( pNTOIIter != m_aPreDefVarMap.end() )
{ // Fixed/Predefined variable found
PreDefVariable nIndex = pNTOIIter->second;
// Determine variable value and length from array/table if ( nIndex == PREDEFVAR_WORK && !bWorkRetrieved )
{ // Transient value, retrieve it again
m_aPreDefVars.m_FixedVar[ nIndex ] = GetWorkVariableValue();
bWorkRetrieved = true;
} elseif ( nIndex == PREDEFVAR_WORKDIRURL && !bWorkDirURLRetrieved )
{ // Transient value, retrieve it again
m_aPreDefVars.m_FixedVar[ nIndex ] = GetWorkPath();
bWorkDirURLRetrieved = true;
}
// Check preconditions to substitute path variables. // 1. A path variable can only be substituted if it follows a ';'! // 2. It's located exactly at the start of the string being substituted! if (( aFixedVarTable[ int( nIndex ) ].bAbsPath && (( nPosition == 0 ) || (( nPosition > 0 ) && ( aWorkText[nPosition-1] == ';')))) ||
( !aFixedVarTable[ int( nIndex ) ].bAbsPath ))
{
aReplacement = m_aPreDefVars.m_FixedVar[ nIndex ];
nReplaceLength = nLength;
}
}
// Have we found something to replace? if ( nReplaceLength > 0 )
{ // Yes ... then do it.
aWorkText = aWorkText.replaceAt( nPosition, nReplaceLength, aReplacement );
} else
{ // Variable not known
bVarNotSubstituted = true;
nPosition += nLength;
}
// Step after replaced text! If no text was replaced (unknown variable!), // length of aReplacement is 0 ... and we don't step then.
nPosition += aReplacement.getLength();
// We must control index in string before call something at OUString! // The OUString-implementation don't do it for us :-( but the result is not defined otherwise. if ( nPosition + 1 > aWorkText.getLength() )
{ // Position is out of range. Break loop!
nPosition = -1;
nLength = 0;
} else
{ // Else; Position is valid. Search for next variable to replace.
nPosition = aWorkText.indexOf( "$(", nPosition ); // Have we found any variable like "$(...)"? if ( nPosition != -1 )
{ // Yes; Get length of found variable. If no ")" was found - nLength must set to 0!
nLength = 0;
sal_Int32 nEndPosition = aWorkText.indexOf( ')', nPosition ); if ( nEndPosition != -1 )
nLength = nEndPosition - nPosition + 1;
}
}
}
nPosition = aWorkText.indexOf( "$(" ); if ( nPosition == -1 )
{
bSubstitutionCompleted = true; break; // All variables are substituted
} else
{ // Check for recursion const sal_uInt32 nCount = aEndlessRecursiveDetector.size(); for ( sal_uInt32 i=0; i < nCount; i++ )
{ if ( aEndlessRecursiveDetector[i] == aWorkText )
{ if ( bVarNotSubstituted ) break; // Not all variables could be substituted! else
{
nDepth = nMaxRecursiveDepth; break; // Recursion detected!
}
}
}
INetURLObject aUrl( rURL ); if ( !aUrl.HasError() )
aURL = aUrl.GetMainURL( INetURLObject::DecodeMechanism::NONE ); else
{ // Convert a system path to a UCB compliant URL before resubstitution
OUString aTemp; if ( osl::FileBase::getFileURLFromSystemPath( rURL, aTemp ) == osl::FileBase::E_None )
{
aURL = INetURLObject( aTemp ).GetMainURL( INetURLObject::DecodeMechanism::NONE ); if( aURL.isEmpty() ) return rURL;
} else
{ // rURL is not a valid URL nor a osl system path. Give up and return error! return rURL;
}
}
// Get transient predefined path variable $(work) value before starting resubstitution
m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue();
for (;;)
{ bool bVariableFound = false;
for (autoconst & i: m_aReSubstFixedVarOrder)
{
OUString aValue = m_aPreDefVars.m_FixedVar[i.eVariable];
sal_Int32 nPos = aURL.indexOf( aValue ); if ( nPos >= 0 )
{ bool bMatch = true; if ( !aFixedVarTable[i.eVariable].bAbsPath )
{ // Special path variables as they can occur in the middle of a path. Only match if they // describe a whole directory and not only a substring of a directory! // (Ideally, all substitutions should stick to syntactical // boundaries within the given URL, like not covering only // part of a URL path segment; however, at least when saving // an Impress document, one URL that is passed in is of the // form <file:///.../share/palette%3Bfile:///.../user/ // config/standard.sob>, re-substituted to // <$(inst)/share/palette%3B$(user)/config/standard.sob>.) const sal_Unicode* pStr = aURL.getStr();
// This method support both request schemes "$("<varname>")" or "<varname>".
OUString const & SubstitutePathVariables::impl_getSubstituteVariableValue( constOUString& rVariable )
{
OUString aVariable;
sal_Int32 nPos = rVariable.indexOf( "$(" ); if ( nPos == -1 )
{ // Prepare variable name before hash map access
aVariable = "$(" + rVariable + ")";
}
// Get inspath and userpath from bootstrap mechanism in every case as file URL
::utl::Bootstrap::PathStatus aState;
OUString sVal;
aState = utl::Bootstrap::locateUserData( sVal ); //There can be the valid case that there is no user installation. //TODO: Is that still the case? (With OOo 3.4, "unopkg sync" was run as part // of the setup. Then no user installation was required.) //Therefore we do not assert here. // It's not possible to detect when an empty value would actually be used. // (note: getenv is a hack to detect if we're running in a unit test) // Also, it's okay to have an empty user installation path in case of LOK if (aState == ::utl::Bootstrap::PATH_EXISTS || getenv("SRC_ROOT") ||
(comphelper::LibreOfficeKit::isActive() && aState == ::utl::Bootstrap::PATH_VALID))
{
m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ] = sVal;
}
// Set $(inst), $(instpath), $(insturl)
m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ] = m_aPreDefVars.m_FixedVar[PREDEFVAR_BRANDBASEURL];
m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTURL ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ];
m_aPreDefVars.m_FixedVar[ PREDEFVAR_INST ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ]; // New variable of hierarchy service (#i32656#)
m_aPreDefVars.m_FixedVar[ PREDEFVAR_BASEINSTURL ]= m_aPreDefVars.m_FixedVar[ PREDEFVAR_INSTPATH ];
// Set $(user), $(userpath), $(userurl)
m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERURL ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ];
m_aPreDefVars.m_FixedVar[ PREDEFVAR_USER ] = m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ]; // New variable of hierarchy service (#i32656#)
m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERDATAURL ]= m_aPreDefVars.m_FixedVar[ PREDEFVAR_USERPATH ];
// Detect the language type of the current office
m_aPreDefVars.m_eLanguageType = LANGUAGE_ENGLISH_US;
OUString aLocaleStr( utl::ConfigManager::getUILocale() );
m_aPreDefVars.m_eLanguageType = LanguageTag::convertToLanguageTypeWithFallback( aLocaleStr ); // We used to have an else branch here with a SAL_WARN, but that // always fired in some unit tests when this code was built with // debug=t, so it seems fairly pointless, especially as // m_aPreDefVars.m_eLanguageType has been initialized to a // default value above anyway.
// Set $(vlang)
m_aPreDefVars.m_FixedVar[ PREDEFVAR_VLANG ] = aLocaleStr;
// Set $(langid)
m_aPreDefVars.m_FixedVar[ PREDEFVAR_LANGID ] = OUString::number( static_cast<sal_uInt16>(m_aPreDefVars.m_eLanguageType) );
// Set the other pre defined path variables // Set $(work)
m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORK ] = GetWorkVariableValue();
m_aPreDefVars.m_FixedVar[ PREDEFVAR_HOME ] = GetHomeVariableValue();
// Set $(workdirurl) this is the value of the path PATH_WORK which doesn't make sense // anymore because the path settings service has this value! It can deliver this value more // quickly than the substitution service!
m_aPreDefVars.m_FixedVar[ PREDEFVAR_WORKDIRURL ] = GetWorkPath();
// Set $(path) variable
m_aPreDefVars.m_FixedVar[ PREDEFVAR_PATH ] = GetPathVariableValue();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.