1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #if !defined WIN32_LEAN_AND_MEAN
22 # define WIN32_LEAN_AND_MEAN
27 #include <osl/thread.h>
28 #include <osl/file.hxx>
29 #include <osl/process.h>
30 #include <tools/debug.hxx>
31 #include <tools/urlobj.hxx>
32 #include <i18nlangtag/languagetag.hxx>
33 #include <i18nlangtag/mslangid.hxx>
34 #include <unotools/bootstrap.hxx>
35 #include <unotools/lingucfg.hxx>
36 #include <unotools/pathoptions.hxx>
37 #include <rtl/bootstrap.hxx>
38 #include <rtl/ustring.hxx>
39 #include <rtl/string.hxx>
40 #include <rtl/tencinfo.h>
41 #include <linguistic/misc.hxx>
47 #include "lingutil.hxx"
49 #include <sal/macros.h>
51 using namespace ::com::sun::star
;
54 OString
Win_AddLongPathPrefix( const OString
&rPathName
)
56 constexpr OStringLiteral WIN32_LONG_PATH_PREFIX
= "\\\\?\\";
57 if (!rPathName
.match(WIN32_LONG_PATH_PREFIX
)) return WIN32_LONG_PATH_PREFIX
+ rPathName
;
60 #endif //defined(_WIN32)
62 #if defined SYSTEM_DICTS || defined IOS
63 // find old style dictionaries in system directories
64 static void GetOldStyleDicsInDir(
65 OUString
const & aSystemDir
, OUString
const & aFormatName
,
66 std::u16string_view aSystemSuffix
, std::u16string_view aSystemPrefix
,
67 std::set
< OUString
>& aDicLangInUse
,
68 std::vector
< SvtLinguConfigDictionaryEntry
>& aRes
)
70 osl::Directory
aSystemDicts(aSystemDir
);
71 if (aSystemDicts
.open() != osl::FileBase::E_None
)
74 osl::DirectoryItem aItem
;
75 osl::FileStatus
aFileStatus(osl_FileStatus_Mask_FileURL
);
76 while (aSystemDicts
.getNextItem(aItem
) == osl::FileBase::E_None
)
78 aItem
.getFileStatus(aFileStatus
);
79 OUString sPath
= aFileStatus
.getFileURL();
80 if (sPath
.endsWith(aSystemSuffix
))
82 sal_Int32 nStartIndex
= sPath
.lastIndexOf('/') + 1;
83 if (!sPath
.match(aSystemPrefix
, nStartIndex
))
85 OUString sChunk
= sPath
.copy(nStartIndex
+ aSystemPrefix
.size(),
86 sPath
.getLength() - aSystemSuffix
.size() -
87 nStartIndex
- aSystemPrefix
.size());
91 // We prefer (now) to use language tags.
92 // Avoid feeding in the older LANG_REGION scheme to the BCP47
93 // ctor as that triggers use of liblangtag and initializes its
94 // database which we do not want during startup. Convert
96 sChunk
= sChunk
.replace( '_', '-');
98 // There's a known exception to the rule, the dreaded
99 // hu_HU_u8.dic of the myspell-hu package, see
100 // http://packages.debian.org/search?arch=any&searchon=contents&keywords=hu_HU_u8.dic
101 // This was ignored because unknown in the old implementation,
102 // truncate to the known locale and either insert because hu_HU
103 // wasn't encountered yet, or skip because it was. It doesn't
104 // really matter because the proper new-style hu_HU dictionary
105 // will take precedence anyway if installed with a Hungarian
106 // languagepack. Again, this is only to not pull in all
107 // liblangtag and stuff during startup, the result would be
108 // !isValidBcp47() and the dictionary ignored.
109 if (sChunk
== "hu-HU-u8")
112 LanguageTag
aLangTag(sChunk
, true);
113 if (!aLangTag
.isValidBcp47())
116 // Thus we first get the language of the dictionary
117 const OUString
& aLocaleName(aLangTag
.getBcp47());
119 if (aDicLangInUse
.insert(aLocaleName
).second
)
121 // add the dictionary to the resulting vector
122 SvtLinguConfigDictionaryEntry aDicEntry
;
123 aDicEntry
.aLocations
= { sPath
};
124 aDicEntry
.aLocaleNames
= { aLocaleName
};
125 aDicEntry
.aFormatName
= aFormatName
;
126 aRes
.push_back( aDicEntry
);
133 // build list of old style dictionaries (not as extensions) to use.
134 // User installed dictionaries (the ones residing in the user paths)
135 // will get precedence over system installed ones for the same language.
136 std::vector
< SvtLinguConfigDictionaryEntry
> GetOldStyleDics( const char *pDicType
)
138 std::vector
< SvtLinguConfigDictionaryEntry
> aRes
;
143 OUString aFormatName
;
144 OUString aDicExtension
;
145 #if defined SYSTEM_DICTS || defined IOS
147 OUString aSystemPrefix
;
148 OUString aSystemSuffix
;
150 if (strcmp( pDicType
, "DICT" ) == 0)
152 aFormatName
= "DICT_SPELL";
153 aDicExtension
= ".dic";
155 aSystemDir
= DICT_SYSTEM_DIR
;
156 aSystemSuffix
= aDicExtension
;
158 aSystemDir
= "$BRAND_BASE_DIR/share/spell";
159 rtl::Bootstrap::expandMacros(aSystemDir
);
160 aSystemSuffix
= ".dic";
163 else if (strcmp( pDicType
, "HYPH" ) == 0)
165 aFormatName
= "DICT_HYPH";
166 aDicExtension
= ".dic";
168 aSystemDir
= HYPH_SYSTEM_DIR
;
169 aSystemPrefix
= "hyph_";
170 aSystemSuffix
= aDicExtension
;
173 else if (strcmp( pDicType
, "THES" ) == 0)
175 aFormatName
= "DICT_THES";
176 aDicExtension
= ".dat";
178 aSystemDir
= THES_SYSTEM_DIR
;
179 aSystemPrefix
= "th_";
180 aSystemSuffix
= "_v2.dat";
182 aSystemDir
= "$BRAND_BASE_DIR/share/thes";
183 rtl::Bootstrap::expandMacros(aSystemDir
);
184 aSystemPrefix
= "th_";
185 aSystemSuffix
= "_v2.dat";
189 if (aFormatName
.isEmpty() || aDicExtension
.isEmpty())
192 #if defined SYSTEM_DICTS || defined IOS
193 // set of languages to remember the language where it is already
194 // decided to make use of the dictionary.
195 std::set
< OUString
> aDicLangInUse
;
198 // follow the hunspell tool's example and check DICPATH for preferred dictionaries
199 rtl_uString
* pSearchPath
= nullptr;
200 osl_getEnvironment(OUString("DICPATH").pData
, &pSearchPath
);
204 OUString
aSearchPath(pSearchPath
);
205 rtl_uString_release(pSearchPath
);
207 sal_Int32 nIndex
= 0;
210 OUString
aSystem( aSearchPath
.getToken(0, ':', nIndex
) );
215 if (!utl::Bootstrap::getProcessWorkingDir(aCWD
))
217 if (osl::FileBase::getFileURLFromSystemPath(aSystem
, aRelative
)
218 != osl::FileBase::E_None
)
220 if (osl::FileBase::getAbsoluteFileURL(aCWD
, aRelative
, aAbsolute
)
221 != osl::FileBase::E_None
)
224 // GetOldStyleDicsInDir will make sure the dictionary is the right
225 // type based on its prefix, that way hyphen, mythes and regular
226 // dictionaries can live in one directory
227 GetOldStyleDicsInDir(aAbsolute
, aFormatName
, aSystemSuffix
,
228 aSystemPrefix
, aDicLangInUse
, aRes
);
230 while (nIndex
!= -1);
234 // load system directories last so that DICPATH prevails
235 GetOldStyleDicsInDir(aSystemDir
, aFormatName
, aSystemSuffix
, aSystemPrefix
,
236 aDicLangInUse
, aRes
);
242 void MergeNewStyleDicsAndOldStyleDics(
243 std::vector
< SvtLinguConfigDictionaryEntry
> &rNewStyleDics
,
244 const std::vector
< SvtLinguConfigDictionaryEntry
> &rOldStyleDics
)
246 // get list of languages supported by new style dictionaries
247 std::set
< OUString
> aNewStyleLanguages
;
248 for (auto const& newStyleDic
: rNewStyleDics
)
250 const uno::Sequence
< OUString
> aLocaleNames(newStyleDic
.aLocaleNames
);
251 sal_Int32 nLocaleNames
= aLocaleNames
.getLength();
252 for (sal_Int32 k
= 0; k
< nLocaleNames
; ++k
)
254 aNewStyleLanguages
.insert( aLocaleNames
[k
] );
258 // now check all old style dictionaries if they will add a not yet
259 // added language. If so add them to the resulting vector
260 for (auto const& oldStyleDic
: rOldStyleDics
)
262 sal_Int32 nOldStyleDics
= oldStyleDic
.aLocaleNames
.getLength();
264 // old style dics should only have one language listed...
265 DBG_ASSERT( nOldStyleDics
, "old style dictionary with more than one language found!");
266 if (nOldStyleDics
> 0)
268 if (linguistic::LinguIsUnspecified( oldStyleDic
.aLocaleNames
[0]))
270 OSL_FAIL( "old style dictionary with invalid language found!" );
274 // language not yet added?
275 if (aNewStyleLanguages
.find( oldStyleDic
.aLocaleNames
[0] ) == aNewStyleLanguages
.end())
276 rNewStyleDics
.push_back(oldStyleDic
);
280 OSL_FAIL( "old style dictionary with no language found!" );
285 rtl_TextEncoding
getTextEncodingFromCharset(const char* pCharset
)
287 // default result: used to indicate that we failed to get the proper encoding
288 rtl_TextEncoding eRet
= RTL_TEXTENCODING_DONTKNOW
;
292 eRet
= rtl_getTextEncodingFromMimeCharset(pCharset
);
293 if (eRet
== RTL_TEXTENCODING_DONTKNOW
)
294 eRet
= rtl_getTextEncodingFromUnixCharset(pCharset
);
295 if (eRet
== RTL_TEXTENCODING_DONTKNOW
)
297 if (strcmp("ISCII-DEVANAGARI", pCharset
) == 0)
298 eRet
= RTL_TEXTENCODING_ISCII_DEVANAGARI
;
304 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */