Branch data Line data Source code
1 : : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : : /*************************************************************************
3 : : *
4 : : * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 : : *
6 : : * Copyright 2000, 2010 Oracle and/or its affiliates.
7 : : *
8 : : * OpenOffice.org - a multi-platform office productivity suite
9 : : *
10 : : * This file is part of OpenOffice.org.
11 : : *
12 : : * OpenOffice.org is free software: you can redistribute it and/or modify
13 : : * it under the terms of the GNU Lesser General Public License version 3
14 : : * only, as published by the Free Software Foundation.
15 : : *
16 : : * OpenOffice.org is distributed in the hope that it will be useful,
17 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : : * GNU Lesser General Public License version 3 for more details
20 : : * (a copy is included in the LICENSE file that accompanied this code).
21 : : *
22 : : * You should have received a copy of the GNU Lesser General Public License
23 : : * version 3 along with OpenOffice.org. If not, see
24 : : * <http://www.openoffice.org/license.html>
25 : : * for a copy of the LGPLv3 License.
26 : : *
27 : : ************************************************************************/
28 : :
29 : : #include <com/sun/star/uno/Reference.h>
30 : : #include <com/sun/star/linguistic2/XSearchableDictionaryList.hpp>
31 : :
32 : : #include <cppuhelper/factory.hxx> // helper for factories
33 : : #include <com/sun/star/registry/XRegistryKey.hpp>
34 : : #include <i18npool/mslangid.hxx>
35 : : #include <tools/debug.hxx>
36 : : #include <osl/mutex.hxx>
37 : :
38 : : #include <hyphen.h>
39 : : #include <hyphenimp.hxx>
40 : :
41 : : #include <linguistic/hyphdta.hxx>
42 : : #include <rtl/ustring.hxx>
43 : : #include <rtl/ustrbuf.hxx>
44 : : #include <rtl/textenc.h>
45 : :
46 : : #include <linguistic/lngprops.hxx>
47 : : #include <unotools/pathoptions.hxx>
48 : : #include <unotools/useroptions.hxx>
49 : : #include <unotools/lingucfg.hxx>
50 : : #include <osl/file.hxx>
51 : :
52 : : #include "dictmgr.hxx"
53 : :
54 : : #include <stdio.h>
55 : : #include <string.h>
56 : :
57 : : #include <list>
58 : : #include <set>
59 : :
60 : : using namespace utl;
61 : : using namespace osl;
62 : : using namespace com::sun::star;
63 : : using namespace com::sun::star::beans;
64 : : using namespace com::sun::star::lang;
65 : : using namespace com::sun::star::uno;
66 : : using namespace com::sun::star::linguistic2;
67 : : using namespace linguistic;
68 : :
69 : : using ::rtl::OUString;
70 : :
71 : : // values asigned to capitalization types
72 : : #define CAPTYPE_UNKNOWN 0
73 : : #define CAPTYPE_NOCAP 1
74 : : #define CAPTYPE_INITCAP 2
75 : : #define CAPTYPE_ALLCAP 3
76 : : #define CAPTYPE_MIXED 4
77 : :
78 : : // min, max
79 : : #define Max(a,b) (a > b ? a : b)
80 : :
81 : : ///////////////////////////////////////////////////////////////////////////
82 : :
83 : :
84 : 46 : Hyphenator::Hyphenator() :
85 [ + - ][ + - ]: 46 : aEvtListeners ( GetLinguMutex() )
[ + - ]
86 : : {
87 : 46 : bDisposing = sal_False;
88 : 46 : pPropHelper = NULL;
89 : 46 : aDicts = NULL;
90 : 46 : numdict = 0;
91 : 46 : }
92 : :
93 [ + - ][ + - ]: 46 : Hyphenator::~Hyphenator()
94 : : {
95 [ + - ][ + - ]: 46 : if (numdict && aDicts)
96 : : {
97 [ + + ]: 2162 : for (int i=0; i < numdict; ++i)
98 : : {
99 [ + - ][ + - ]: 2116 : delete aDicts[i].apCC;
100 [ + + ]: 2116 : if (aDicts[i].aPtr)
101 [ + - ]: 9 : hnj_hyphen_free(aDicts[i].aPtr);
102 : : }
103 : : }
104 [ + - ][ + + ]: 2162 : delete[] aDicts;
105 : :
106 [ - + ]: 46 : if (pPropHelper)
107 : : {
108 [ # # ]: 0 : pPropHelper->RemoveAsPropListener();
109 [ # # ][ # # ]: 0 : delete pPropHelper;
110 : : }
111 [ - + ]: 92 : }
112 : :
113 : 9 : PropertyHelper_Hyphenation& Hyphenator::GetPropHelper_Impl()
114 : : {
115 [ + - ]: 9 : if (!pPropHelper)
116 : : {
117 [ + - ][ + - ]: 9 : Reference< XPropertySet > xPropSet( GetLinguProperties(), UNO_QUERY );
118 : :
119 [ + - ][ + - ]: 9 : pPropHelper = new PropertyHelper_Hyphenation ((XHyphenator *) this, xPropSet );
[ + - ]
120 [ + - ]: 9 : pPropHelper->AddAsPropListener(); //! after a reference is established
121 : : }
122 : 9 : return *pPropHelper;
123 : : }
124 : :
125 : :
126 : 96 : Sequence< Locale > SAL_CALL Hyphenator::getLocales()
127 : : throw(RuntimeException)
128 : : {
129 [ + - ][ + - ]: 96 : MutexGuard aGuard( GetLinguMutex() );
130 : :
131 : : // this routine should return the locales supported by the installed
132 : : // dictionaries.
133 : :
134 [ + + ]: 96 : if (!numdict)
135 : : {
136 [ + - ]: 46 : SvtLinguConfig aLinguCfg;
137 : :
138 : : // get list of dictionaries-to-use
139 : : // (or better speaking: the list of dictionaries using the
140 : : // new configuration entries).
141 [ + - ]: 46 : std::list< SvtLinguConfigDictionaryEntry > aDics;
142 [ + - ]: 46 : uno::Sequence< rtl::OUString > aFormatList;
143 : : aLinguCfg.GetSupportedDictionaryFormatsFor( A2OU("Hyphenators"),
144 [ + - ][ + - ]: 46 : A2OU("org.openoffice.lingu.LibHnjHyphenator"), aFormatList );
[ + - ]
145 : 46 : sal_Int32 nLen = aFormatList.getLength();
146 [ + + ]: 92 : for (sal_Int32 i = 0; i < nLen; ++i)
147 : : {
148 : : std::vector< SvtLinguConfigDictionaryEntry > aTmpDic(
149 [ + - ][ + - ]: 46 : aLinguCfg.GetActiveDictionariesByFormat( aFormatList[i] ) );
150 [ + - ]: 46 : aDics.insert( aDics.end(), aTmpDic.begin(), aTmpDic.end() );
151 : 46 : }
152 : :
153 : : //!! for compatibility with old dictionaries (the ones not using extensions
154 : : //!! or new configuration entries, but still using the dictionary.lst file)
155 : : //!! Get the list of old style spell checking dictionaries to use...
156 : : std::vector< SvtLinguConfigDictionaryEntry > aOldStyleDics(
157 [ + - ]: 46 : GetOldStyleDics( "HYPH" ) );
158 : :
159 : : // to prefer dictionaries with configuration entries we will only
160 : : // use those old style dictionaries that add a language that
161 : : // is not yet supported by the list od new style dictionaries
162 [ + - ]: 46 : MergeNewStyleDicsAndOldStyleDics( aDics, aOldStyleDics );
163 : :
164 : 46 : numdict = aDics.size();
165 [ + - ]: 46 : if (numdict)
166 : : {
167 : : // get supported locales from the dictionaries-to-use...
168 : 46 : sal_Int32 k = 0;
169 [ + - ]: 46 : std::set< rtl::OUString, lt_rtl_OUString > aLocaleNamesSet;
170 [ + - ]: 46 : std::list< SvtLinguConfigDictionaryEntry >::const_iterator aDictIt;
171 [ + - ][ + - ]: 368 : for (aDictIt = aDics.begin(); aDictIt != aDics.end(); ++aDictIt)
[ + - ][ + - ]
[ + + ]
172 : : {
173 [ + - ][ + - ]: 322 : uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames );
174 : 322 : sal_Int32 nLen2 = aLocaleNames.getLength();
175 [ + + ]: 2438 : for (k = 0; k < nLen2; ++k)
176 : : {
177 [ + - ][ + - ]: 2116 : aLocaleNamesSet.insert( aLocaleNames[k] );
178 : : }
179 [ + - ]: 322 : }
180 : : // ... and add them to the resulting sequence
181 [ + - ]: 46 : aSuppLocales.realloc( aLocaleNamesSet.size() );
182 : 46 : std::set< rtl::OUString, lt_rtl_OUString >::const_iterator aItB;
183 : 46 : k = 0;
184 [ + + ]: 2162 : for (aItB = aLocaleNamesSet.begin(); aItB != aLocaleNamesSet.end(); ++aItB)
185 : : {
186 : : Locale aTmp( MsLangId::convertLanguageToLocale(
187 [ + - ][ + - ]: 2116 : MsLangId::convertIsoStringToLanguage( *aItB )));
188 [ + - ]: 2116 : aSuppLocales[k++] = aTmp;
189 : 2116 : }
190 : :
191 : : //! For each dictionary and each locale we need a seperate entry.
192 : : //! If this results in more than one dictionary per locale than (for now)
193 : : //! it is undefined which dictionary gets used.
194 : : //! In the future the implementation should support using several dictionaries
195 : : //! for one locale.
196 : 46 : numdict = 0;
197 [ + - ][ + - ]: 368 : for (aDictIt = aDics.begin(); aDictIt != aDics.end(); ++aDictIt)
[ + - ][ + - ]
[ + + ]
198 [ + - ]: 322 : numdict = numdict + aDictIt->aLocaleNames.getLength();
199 : :
200 : : // add dictionary information
201 [ + - ][ + + ]: 2162 : aDicts = new HDInfo[numdict];
202 : :
203 : 46 : k = 0;
204 [ + - ][ + - ]: 368 : for (aDictIt = aDics.begin(); aDictIt != aDics.end(); ++aDictIt)
[ + - ][ + - ]
[ + + ]
205 : : {
206 [ + - ]: 644 : if (aDictIt->aLocaleNames.getLength() > 0 &&
[ + - + - ]
[ + - ]
207 [ + - ]: 322 : aDictIt->aLocations.getLength() > 0)
208 : : {
209 [ + - ][ + - ]: 322 : uno::Sequence< rtl::OUString > aLocaleNames( aDictIt->aLocaleNames );
210 : 322 : sal_Int32 nLocales = aLocaleNames.getLength();
211 : :
212 : : // currently only one language per dictionary is supported in the actual implementation...
213 : : // Thus here we work-around this by adding the same dictionary several times.
214 : : // Once for each of it's supported locales.
215 [ + + ]: 2438 : for (sal_Int32 i = 0; i < nLocales; ++i)
216 : : {
217 : 2116 : aDicts[k].aPtr = NULL;
218 : 2116 : aDicts[k].eEnc = RTL_TEXTENCODING_DONTKNOW;
219 : 2116 : aDicts[k].aLoc = MsLangId::convertLanguageToLocale(
220 [ + - ][ + - ]: 2116 : MsLangId::convertIsoStringToLanguage( aDictIt->aLocaleNames[i] ));
[ + - ]
221 [ + - ][ + - ]: 2116 : aDicts[k].apCC = new CharClass( aDicts[k].aLoc );
222 : : // also both files have to be in the same directory and the
223 : : // file names must only differ in the extension (.aff/.dic).
224 : : // Thus we use the first location only and strip the extension part.
225 [ + - ]: 2116 : rtl::OUString aLocation = aDictIt->aLocations[0];
226 : 2116 : sal_Int32 nPos = aLocation.lastIndexOf( '.' );
227 : 2116 : aLocation = aLocation.copy( 0, nPos );
228 : 2116 : aDicts[k].aName = aLocation;
229 : :
230 : 2116 : ++k;
231 [ + - ]: 2438 : }
232 : : }
233 : : }
234 : 46 : DBG_ASSERT( k == numdict, "index mismatch?" );
235 : : }
236 : : else
237 : : {
238 : : /* no dictionary found so register no dictionaries */
239 : 0 : numdict = 0;
240 : 0 : aDicts = NULL;
241 [ # # ]: 0 : aSuppLocales.realloc(0);
242 [ + - ][ + - ]: 46 : }
243 : : }
244 : :
245 [ + - ][ + - ]: 96 : return aSuppLocales;
246 : : }
247 : :
248 : :
249 : :
250 : 33612 : sal_Bool SAL_CALL Hyphenator::hasLocale(const Locale& rLocale)
251 : : throw(RuntimeException)
252 : : {
253 [ + - ][ + - ]: 33612 : MutexGuard aGuard( GetLinguMutex() );
254 : :
255 : 33612 : sal_Bool bRes = sal_False;
256 [ - + ]: 33612 : if (!aSuppLocales.getLength())
257 [ # # ][ # # ]: 0 : getLocales();
258 : :
259 : 33612 : const Locale *pLocale = aSuppLocales.getConstArray();
260 : 33612 : sal_Int32 nLen = aSuppLocales.getLength();
261 [ + - ]: 605016 : for (sal_Int32 i = 0; i < nLen; ++i)
262 : : {
263 [ + + ]: 605016 : if (rLocale == pLocale[i])
264 : : {
265 : 33612 : bRes = sal_True;
266 : 33612 : break;
267 : : }
268 : : }
269 [ + - ]: 33612 : return bRes;
270 : : }
271 : :
272 : :
273 : 33603 : Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const ::rtl::OUString& aWord,
274 : : const ::com::sun::star::lang::Locale& aLocale,
275 : : sal_Int16 nMaxLeading,
276 : : const ::com::sun::star::beans::PropertyValues& aProperties )
277 : : throw (com::sun::star::uno::RuntimeException, com::sun::star::lang::IllegalArgumentException)
278 : : {
279 : 33603 : int nHyphenationPos = -1;
280 : 33603 : int nHyphenationPosAlt = -1;
281 : 33603 : int nHyphenationPosAltHyph = -1;
282 : : int wordlen;
283 : : char *hyphens;
284 : : char *lcword;
285 : 33603 : int k = 0;
286 : :
287 [ + - ]: 33603 : PropertyHelper_Hyphenation& rHelper = GetPropHelper();
288 [ + - ]: 33603 : rHelper.SetTmpPropVals(aProperties);
289 [ + - ]: 33603 : sal_Int16 minTrail = rHelper.GetMinTrailing();
290 [ + - ]: 33603 : sal_Int16 minLead = rHelper.GetMinLeading();
291 [ + - ]: 33603 : sal_Int16 minLen = rHelper.GetMinWordLength();
292 : :
293 : 33603 : HyphenDict *dict = NULL;
294 : 33603 : rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
295 : 33603 : CharClass * pCC = NULL;
296 : :
297 : 33603 : Reference< XHyphenatedWord > xRes;
298 : :
299 : 33603 : k = -1;
300 [ + + ]: 1579341 : for (int j = 0; j < numdict; j++)
301 : : {
302 [ + + ]: 1545738 : if (aLocale == aDicts[j].aLoc)
303 : 33603 : k = j;
304 : : }
305 : :
306 : : // if we have a hyphenation dictionary matching this locale
307 [ + - ]: 33603 : if (k != -1)
308 : : {
309 : : // if this dictinary has not been loaded yet do that
310 [ + + ]: 33603 : if (!aDicts[k].aPtr)
311 : : {
312 [ + - ]: 9 : OUString DictFN = aDicts[k].aName + A2OU(".dic");
313 : 9 : OUString dictpath;
314 : :
315 [ + - ]: 9 : osl::FileBase::getSystemPathFromFileURL( DictFN, dictpath );
316 [ + - ][ + - ]: 9 : OString sTmp( OU2ENC( dictpath, osl_getThreadTextEncoding() ) );
317 : :
318 : : #if defined(WNT)
319 : : // workaround for Windows specifc problem that the
320 : : // path length in calls to 'fopen' is limted to somewhat
321 : : // about 120+ characters which will usually be exceed when
322 : : // using dictionaries as extensions.
323 : : sTmp = Win_GetShortPathName( dictpath );
324 : : #endif
325 : :
326 [ - + ][ + - ]: 9 : if ( ( dict = hnj_hyphen_load ( sTmp.getStr()) ) == NULL )
327 : : {
328 [ # # ][ # # ]: 0 : fprintf(stderr, "Couldn't find file %s\n", OU2ENC(dictpath, osl_getThreadTextEncoding()) );
[ # # ]
329 [ # # ]: 0 : return NULL;
330 : : }
331 : 9 : aDicts[k].aPtr = dict;
332 [ + - ][ - + ]: 9 : aDicts[k].eEnc = getTextEncodingFromCharset(dict->cset);
[ - + ][ + - ]
333 : : }
334 : :
335 : : // other wise hyphenate the word with that dictionary
336 : 33603 : dict = aDicts[k].aPtr;
337 : 33603 : eEnc = aDicts[k].eEnc;
338 : 33603 : pCC = aDicts[k].apCC;
339 : :
340 : : // we don't want to work with a default text encoding since following incorrect
341 : : // results may occur only for specific text and thus may be hard to notice.
342 : : // Thus better always make a clean exit here if the text encoding is in question.
343 : : // Hopefully something not working at all will raise proper attention quickly. ;-)
344 : : DBG_ASSERT( eEnc != RTL_TEXTENCODING_DONTKNOW, "failed to get text encoding! (maybe incorrect encoding string in file)" );
345 [ - + ]: 33603 : if (eEnc == RTL_TEXTENCODING_DONTKNOW)
346 [ # # ]: 0 : return NULL;
347 : :
348 : 33603 : sal_uInt16 ct = CAPTYPE_UNKNOWN;
349 [ + - ]: 33603 : ct = capitalType(aWord, pCC);
350 : :
351 : : // first convert any smart quotes or apostrophes to normal ones
352 [ + - ]: 33603 : OUStringBuffer rBuf(aWord);
353 : 33603 : sal_Int32 nc = rBuf.getLength();
354 : : sal_Unicode ch;
355 [ + + ]: 187245 : for (sal_Int32 ix=0; ix < nc; ix++)
356 : : {
357 : 153642 : ch = rBuf[ix];
358 [ - + ][ + - ]: 153642 : if ((ch == 0x201C) || (ch == 0x201D))
359 : 0 : rBuf[ix] = (sal_Unicode)0x0022;
360 [ + - ][ - + ]: 153642 : if ((ch == 0x2018) || (ch == 0x2019))
361 : 0 : rBuf[ix] = (sal_Unicode)0x0027;
362 : : }
363 [ + - ]: 33603 : OUString nWord(rBuf.makeStringAndClear());
364 : :
365 : : // now convert word to all lowercase for pattern recognition
366 [ + - ]: 33603 : OUString nTerm(makeLowerCase(nWord, pCC));
367 : :
368 : : // now convert word to needed encoding
369 [ + - ]: 33603 : OString encWord(OU2ENC(nTerm,eEnc));
370 : :
371 : 33603 : wordlen = encWord.getLength();
372 [ + - ]: 33603 : lcword = new char[wordlen + 1];
373 [ + - ]: 33603 : hyphens = new char[wordlen + 5];
374 : :
375 : 33603 : char ** rep = NULL; // replacements of discretionary hyphenation
376 : 33603 : int * pos = NULL; // array of [hyphenation point] minus [deletion position]
377 : 33603 : int * cut = NULL; // length of deletions in original word
378 : :
379 : : // copy converted word into simple char buffer
380 : 33603 : strcpy(lcword,encWord.getStr());
381 : :
382 : : // now strip off any ending periods
383 : 33603 : int n = wordlen-1;
384 [ + - ][ - + ]: 33603 : while((n >=0) && (lcword[n] == '.'))
[ - + ]
385 : 0 : n--;
386 : 33603 : n++;
387 [ + - ]: 33603 : if (n > 0)
388 : : {
389 : : const bool bFailed = 0 != hnj_hyphen_hyphenate3( dict, lcword, n, hyphens, NULL,
390 : : &rep, &pos, &cut, minLead, minTrail,
391 : : Max(dict->clhmin, Max(dict->clhmin, 2) + Max(0, minLead - Max(dict->lhmin, 2))),
392 [ + - ]: 33603 : Max(dict->crhmin, Max(dict->crhmin, 2) + Max(0, minTrail - Max(dict->rhmin, 2))) );
393 [ - + ]: 33603 : if (bFailed)
394 : : {
395 : : //whoops something did not work
396 [ # # ]: 0 : delete[] hyphens;
397 [ # # ]: 0 : delete[] lcword;
398 [ # # ]: 0 : if (rep)
399 : : {
400 [ # # ]: 0 : for(int j = 0; j < n; j++)
401 : : {
402 [ # # ]: 0 : if (rep[j]) free(rep[j]);
403 : : }
404 : 0 : free(rep);
405 : : }
406 [ # # ]: 0 : if (pos) free(pos);
407 [ # # ]: 0 : if (cut) free(cut);
408 [ # # ]: 0 : return NULL;
409 : : }
410 : : }
411 : :
412 : : // now backfill hyphens[] for any removed trailing periods
413 [ - + ]: 33603 : for (int c = n; c < wordlen; c++) hyphens[c] = '0';
414 : 33603 : hyphens[wordlen] = '\0';
415 : :
416 [ + - ]: 33603 : sal_Int32 Leading = GetPosInWordToCheck( aWord, nMaxLeading );
417 : :
418 [ + + ]: 187245 : for (sal_Int32 i = 0; i < n; i++)
419 : : {
420 : 153642 : int leftrep = 0;
421 : 153642 : sal_Bool hit = (n >= minLen);
422 [ - + ][ # # ]: 153642 : if (!rep || !rep[i] || (i >= n))
[ # # ]
423 : : {
424 [ + + ][ + + ]: 153642 : hit = hit && (hyphens[i]&1) && (i < Leading);
[ + + ]
425 [ + + ][ + - ]: 153642 : hit = hit && (i >= (minLead-1) );
426 [ + + ][ + - ]: 153642 : hit = hit && ((n - i - 1) >= minTrail);
427 : : }
428 : : else
429 : : {
430 : : // calculate change character length before hyphenation point signed with '='
431 [ # # ][ # # ]: 0 : for (char * c = rep[i]; *c && (*c != '='); c++)
[ # # ]
432 : : {
433 [ # # ]: 0 : if (eEnc == RTL_TEXTENCODING_UTF8)
434 : : {
435 [ # # ]: 0 : if (((unsigned char) *c) >> 6 != 2)
436 : 0 : leftrep++;
437 : : }
438 : : else
439 : 0 : leftrep++;
440 : : }
441 [ # # ][ # # ]: 0 : hit = hit && (hyphens[i]&1) && ((i + leftrep - pos[i]) < Leading);
[ # # ]
442 [ # # ][ # # ]: 0 : hit = hit && ((i + leftrep - pos[i]) >= (minLead-1) );
443 [ # # ][ # # ]: 0 : hit = hit && ((n - i - 1 + sal::static_int_cast< sal_sSize >(strlen(rep[i])) - leftrep - 1) >= minTrail);
444 : : }
445 [ + + ]: 153642 : if (hit)
446 : : {
447 : 282 : nHyphenationPos = i;
448 [ - + ][ # # ]: 282 : if (rep && (i < n) && rep[i])
[ # # ]
449 : : {
450 : 0 : nHyphenationPosAlt = i - pos[i];
451 : 0 : nHyphenationPosAltHyph = i + leftrep - pos[i];
452 : : }
453 : : }
454 : : }
455 : :
456 [ + + ]: 33603 : if (nHyphenationPos == -1)
457 : : {
458 [ + - ]: 33321 : xRes = NULL;
459 : : }
460 : : else
461 : : {
462 [ - + ][ # # ]: 282 : if (rep && rep[nHyphenationPos])
463 : : {
464 : : // remove equal sign
465 : 0 : char * s = rep[nHyphenationPos];
466 : 0 : int eq = 0;
467 [ # # ]: 0 : for (; *s; s++)
468 : : {
469 [ # # ]: 0 : if (*s == '=') eq = 1;
470 [ # # ]: 0 : if (eq) *s = *(s + 1);
471 : : }
472 [ # # ]: 0 : OUString repHyphlow(rep[nHyphenationPos], strlen(rep[nHyphenationPos]), eEnc);
473 : 0 : OUString repHyph;
474 [ # # # ]: 0 : switch (ct)
475 : : {
476 : : case CAPTYPE_ALLCAP:
477 : : {
478 [ # # ]: 0 : repHyph = makeUpperCase(repHyphlow, pCC);
479 : 0 : break;
480 : : }
481 : : case CAPTYPE_INITCAP:
482 : : {
483 [ # # ]: 0 : if (nHyphenationPosAlt == -1)
484 [ # # ]: 0 : repHyph = makeInitCap(repHyphlow, pCC);
485 : : else
486 : 0 : repHyph = repHyphlow;
487 : 0 : break;
488 : : }
489 : : default:
490 : : {
491 : 0 : repHyph = repHyphlow;
492 : 0 : break;
493 : : }
494 : : }
495 : :
496 : : // handle shortening
497 : : sal_Int16 nPos = (sal_Int16) ((nHyphenationPosAltHyph < nHyphenationPos) ?
498 [ # # ]: 0 : nHyphenationPosAltHyph : nHyphenationPos);
499 : : // dicretionary hyphenation
500 [ # # ]: 0 : xRes = HyphenatedWord::CreateHyphenatedWord( aWord, LocaleToLanguage( aLocale ), nPos,
501 : 0 : aWord.replaceAt(nHyphenationPosAlt + 1, cut[nHyphenationPos], repHyph),
502 [ # # ][ # # ]: 0 : (sal_Int16) nHyphenationPosAltHyph);
503 : : }
504 : : else
505 : : {
506 [ + - ]: 282 : xRes = HyphenatedWord::CreateHyphenatedWord( aWord, LocaleToLanguage( aLocale ),
507 [ + - ][ + - ]: 282 : (sal_Int16)nHyphenationPos, aWord, (sal_Int16) nHyphenationPos);
508 : : }
509 : : }
510 : :
511 [ + - ]: 33603 : delete[] lcword;
512 [ + - ]: 33603 : delete[] hyphens;
513 [ - + ]: 33603 : if (rep)
514 : : {
515 [ # # ]: 0 : for(int j = 0; j < n; j++)
516 : : {
517 [ # # ]: 0 : if (rep[j]) free(rep[j]);
518 : : }
519 : 0 : free(rep);
520 : : }
521 [ - + ]: 33603 : if (pos) free(pos);
522 [ - + ]: 33603 : if (cut) free(cut);
523 : 33603 : return xRes;
524 : : }
525 [ # # ]: 33603 : return NULL;
526 : : }
527 : :
528 : :
529 : 0 : Reference < XHyphenatedWord > SAL_CALL Hyphenator::queryAlternativeSpelling(
530 : : const ::rtl::OUString& /*aWord*/,
531 : : const ::com::sun::star::lang::Locale& /*aLocale*/,
532 : : sal_Int16 /*nIndex*/,
533 : : const ::com::sun::star::beans::PropertyValues& /*aProperties*/ )
534 : : throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
535 : : {
536 : : /* alternative spelling isn't supported by tex dictionaries */
537 : : /* XXX: OOo's extended libhjn algorithm can support alternative spellings with extended TeX dic. */
538 : : /* TASK: implement queryAlternativeSpelling() */
539 : 0 : return NULL;
540 : : }
541 : :
542 : 0 : Reference< XPossibleHyphens > SAL_CALL Hyphenator::createPossibleHyphens( const ::rtl::OUString& aWord,
543 : : const ::com::sun::star::lang::Locale& aLocale,
544 : : const ::com::sun::star::beans::PropertyValues& aProperties )
545 : : throw(::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
546 : : {
547 : 0 : PropertyHelper_Hyphenation& rHelper = GetPropHelper();
548 : 0 : rHelper.SetTmpPropVals(aProperties);
549 : 0 : sal_Int16 minTrail = rHelper.GetMinTrailing();
550 : 0 : sal_Int16 minLead = rHelper.GetMinLeading();
551 : 0 : sal_Int16 minLen = rHelper.GetMinWordLength();
552 : :
553 : : //Resolves: fdo#41083 honour MinWordLength in "createPossibleHyphens" as
554 : : //well as "hyphenate"
555 [ # # ]: 0 : if (aWord.getLength() < minLen)
556 : : {
557 [ # # ]: 0 : return PossibleHyphens::CreatePossibleHyphens( aWord, LocaleToLanguage( aLocale ),
558 [ # # ]: 0 : aWord, Sequence< sal_Int16 >() );
559 : : }
560 : :
561 : 0 : int k = -1;
562 [ # # ]: 0 : for (int j = 0; j < numdict; j++)
563 : : {
564 [ # # ]: 0 : if (aLocale == aDicts[j].aLoc) k = j;
565 : : }
566 : :
567 : : // if we have a hyphenation dictionary matching this locale
568 [ # # ]: 0 : if (k != -1)
569 : : {
570 : 0 : HyphenDict *dict = NULL;
571 : : // if this dictioanry has not been loaded yet do that
572 [ # # ]: 0 : if (!aDicts[k].aPtr)
573 : : {
574 [ # # ]: 0 : OUString DictFN = aDicts[k].aName + A2OU(".dic");
575 : 0 : OUString dictpath;
576 : :
577 [ # # ]: 0 : osl::FileBase::getSystemPathFromFileURL( DictFN, dictpath );
578 [ # # ][ # # ]: 0 : OString sTmp( OU2ENC( dictpath, osl_getThreadTextEncoding() ) );
579 : :
580 : : #if defined(WNT)
581 : : // workaround for Windows specifc problem that the
582 : : // path length in calls to 'fopen' is limted to somewhat
583 : : // about 120+ characters which will usually be exceed when
584 : : // using dictionaries as extensions.
585 : : sTmp = Win_GetShortPathName( dictpath );
586 : : #endif
587 : :
588 [ # # ][ # # ]: 0 : if ( ( dict = hnj_hyphen_load ( sTmp.getStr()) ) == NULL )
589 : : {
590 [ # # ][ # # ]: 0 : fprintf(stderr, "Couldn't find file %s and %s\n", sTmp.getStr(), OU2ENC(dictpath, osl_getThreadTextEncoding()) );
[ # # ]
591 [ # # ]: 0 : return NULL;
592 : : }
593 : 0 : aDicts[k].aPtr = dict;
594 [ # # ][ # # ]: 0 : aDicts[k].eEnc = getTextEncodingFromCharset(dict->cset);
[ # # ][ # # ]
595 : : }
596 : :
597 : : // other wise hyphenate the word with that dictionary
598 : 0 : dict = aDicts[k].aPtr;
599 : 0 : rtl_TextEncoding eEnc = aDicts[k].eEnc;
600 : 0 : CharClass* pCC = aDicts[k].apCC;
601 : :
602 : : // we don't want to work with a default text encoding since following incorrect
603 : : // results may occur only for specific text and thus may be hard to notice.
604 : : // Thus better always make a clean exit here if the text encoding is in question.
605 : : // Hopefully something not working at all will raise proper attention quickly. ;-)
606 : : DBG_ASSERT( eEnc != RTL_TEXTENCODING_DONTKNOW, "failed to get text encoding! (maybe incorrect encoding string in file)" );
607 [ # # ]: 0 : if (eEnc == RTL_TEXTENCODING_DONTKNOW)
608 [ # # ]: 0 : return NULL;
609 : :
610 : : // first handle smart quotes both single and double
611 [ # # ]: 0 : OUStringBuffer rBuf(aWord);
612 : 0 : sal_Int32 nc = rBuf.getLength();
613 : : sal_Unicode ch;
614 [ # # ]: 0 : for (sal_Int32 ix=0; ix < nc; ix++)
615 : : {
616 : 0 : ch = rBuf[ix];
617 [ # # ][ # # ]: 0 : if ((ch == 0x201C) || (ch == 0x201D))
618 : 0 : rBuf[ix] = (sal_Unicode)0x0022;
619 [ # # ][ # # ]: 0 : if ((ch == 0x2018) || (ch == 0x2019))
620 : 0 : rBuf[ix] = (sal_Unicode)0x0027;
621 : : }
622 [ # # ]: 0 : OUString nWord(rBuf.makeStringAndClear());
623 : :
624 : : // now convert word to all lowercase for pattern recognition
625 [ # # ]: 0 : OUString nTerm(makeLowerCase(nWord, pCC));
626 : :
627 : : // now convert word to needed encoding
628 [ # # ]: 0 : OString encWord(OU2ENC(nTerm,eEnc));
629 : :
630 : 0 : int wordlen = encWord.getLength();
631 [ # # ]: 0 : char *lcword = new char[wordlen+1];
632 [ # # ]: 0 : char *hyphens = new char[wordlen+5];
633 : 0 : char ** rep = NULL; // replacements of discretionary hyphenation
634 : 0 : int * pos = NULL; // array of [hyphenation point] minus [deletion position]
635 : 0 : int * cut = NULL; // length of deletions in original word
636 : :
637 : : // copy converted word into simple char buffer
638 : 0 : strcpy(lcword,encWord.getStr());
639 : :
640 : : // first remove any trailing periods
641 : 0 : int n = wordlen-1;
642 [ # # ][ # # ]: 0 : while((n >=0) && (lcword[n] == '.'))
[ # # ]
643 : 0 : n--;
644 : 0 : n++;
645 [ # # ]: 0 : if (n > 0)
646 : : {
647 : : const bool bFailed = 0 != hnj_hyphen_hyphenate3(dict, lcword, n, hyphens, NULL,
648 : : &rep, &pos, &cut, minLead, minTrail,
649 : : Max(dict->clhmin, Max(dict->clhmin, 2) + Max(0, minLead - Max(dict->lhmin, 2))),
650 [ # # ]: 0 : Max(dict->crhmin, Max(dict->crhmin, 2) + Max(0, minTrail - Max(dict->rhmin, 2))) );
651 [ # # ]: 0 : if (bFailed)
652 : : {
653 [ # # ]: 0 : delete[] hyphens;
654 [ # # ]: 0 : delete[] lcword;
655 : :
656 [ # # ]: 0 : if (rep)
657 : : {
658 [ # # ]: 0 : for(int j = 0; j < n; j++)
659 : : {
660 [ # # ]: 0 : if (rep[j]) free(rep[j]);
661 : : }
662 : 0 : free(rep);
663 : : }
664 [ # # ]: 0 : if (pos) free(pos);
665 [ # # ]: 0 : if (cut) free(cut);
666 : :
667 [ # # ]: 0 : return NULL;
668 : : }
669 : : }
670 : : // now backfill hyphens[] for any removed periods
671 [ # # ]: 0 : for (int c = n; c < wordlen; c++)
672 : 0 : hyphens[c] = '0';
673 : 0 : hyphens[wordlen] = '\0';
674 : :
675 : 0 : sal_Int16 nHyphCount = 0;
676 : : sal_Int16 i;
677 : :
678 [ # # ]: 0 : for ( i = 0; i < encWord.getLength(); i++)
679 : : {
680 [ # # ][ # # ]: 0 : if (hyphens[i]&1 && (!rep || !rep[i]))
[ # # ]
681 : 0 : nHyphCount++;
682 : : }
683 : :
684 [ # # ]: 0 : Sequence< sal_Int16 > aHyphPos(nHyphCount);
685 [ # # ]: 0 : sal_Int16 *pPos = aHyphPos.getArray();
686 : 0 : OUStringBuffer hyphenatedWordBuffer;
687 : 0 : nHyphCount = 0;
688 : :
689 [ # # ]: 0 : for (i = 0; i < nWord.getLength(); i++)
690 : : {
691 [ # # ]: 0 : hyphenatedWordBuffer.append(aWord[i]);
692 : : // hyphenation position (not alternative)
693 [ # # ][ # # ]: 0 : if (hyphens[i]&1 && (!rep || !rep[i]))
[ # # ]
694 : : {
695 : 0 : pPos[nHyphCount] = i;
696 [ # # ]: 0 : hyphenatedWordBuffer.append(sal_Unicode('='));
697 : 0 : nHyphCount++;
698 : : }
699 : : }
700 : :
701 [ # # ]: 0 : OUString hyphenatedWord = hyphenatedWordBuffer.makeStringAndClear();
702 : :
703 : : Reference< XPossibleHyphens > xRes = PossibleHyphens::CreatePossibleHyphens(
704 [ # # ][ # # ]: 0 : aWord, LocaleToLanguage( aLocale ), hyphenatedWord, aHyphPos);
705 : :
706 [ # # ]: 0 : delete[] hyphens;
707 [ # # ]: 0 : delete[] lcword;
708 : :
709 [ # # ]: 0 : if (rep)
710 : : {
711 [ # # ]: 0 : for(int j = 0; j < n; j++)
712 : : {
713 [ # # ]: 0 : if (rep[j]) free(rep[j]);
714 : : }
715 : 0 : free(rep);
716 : : }
717 [ # # ]: 0 : if (pos) free(pos);
718 [ # # ]: 0 : if (cut) free(cut);
719 : :
720 [ # # ]: 0 : return xRes;
721 : : }
722 : :
723 : 0 : return NULL;
724 : : }
725 : :
726 : 33603 : sal_uInt16 SAL_CALL Hyphenator::capitalType(const OUString& aTerm, CharClass * pCC)
727 : : {
728 : 33603 : sal_Int32 tlen = aTerm.getLength();
729 [ + - ][ + - ]: 33603 : if ((pCC) && (tlen))
730 : : {
731 [ + - ]: 33603 : String aStr(aTerm);
732 : 33603 : sal_Int32 nc = 0;
733 [ + + ]: 187245 : for (xub_StrLen tindex = 0; tindex < tlen; tindex++)
734 : : {
735 [ + - ][ + + ]: 153642 : if (pCC->getCharacterType(aStr,tindex) & ::com::sun::star::i18n::KCharacterType::UPPER)
736 : 30172 : nc++;
737 : : }
738 : :
739 [ + + ]: 33603 : if (nc == 0)
740 : 4386 : return (sal_uInt16) CAPTYPE_NOCAP;
741 [ - + ]: 29217 : if (nc == tlen)
742 : 0 : return (sal_uInt16) CAPTYPE_ALLCAP;
743 [ + + ][ + - ]: 29217 : if ((nc == 1) && (pCC->getCharacterType(aStr,0) & ::com::sun::star::i18n::KCharacterType::UPPER))
[ + + ][ + + ]
744 : 28151 : return (sal_uInt16) CAPTYPE_INITCAP;
745 : :
746 [ + - ]: 33603 : return (sal_uInt16) CAPTYPE_MIXED;
747 : : }
748 : 33603 : return (sal_uInt16) CAPTYPE_UNKNOWN;
749 : : }
750 : :
751 : 33603 : OUString SAL_CALL Hyphenator::makeLowerCase(const OUString& aTerm, CharClass * pCC)
752 : : {
753 [ + - ]: 33603 : if (pCC)
754 : 33603 : return pCC->lowercase(aTerm);
755 : 33603 : return aTerm;
756 : : }
757 : :
758 : 0 : OUString SAL_CALL Hyphenator::makeUpperCase(const OUString& aTerm, CharClass * pCC)
759 : : {
760 [ # # ]: 0 : if (pCC)
761 : 0 : return pCC->uppercase(aTerm);
762 : 0 : return aTerm;
763 : : }
764 : :
765 : :
766 : 0 : OUString SAL_CALL Hyphenator::makeInitCap(const OUString& aTerm, CharClass * pCC)
767 : : {
768 : 0 : sal_Int32 tlen = aTerm.getLength();
769 [ # # ][ # # ]: 0 : if ((pCC) && (tlen))
770 : : {
771 : 0 : OUString bTemp = aTerm.copy(0,1);
772 [ # # ]: 0 : if (tlen > 1)
773 [ # # ][ # # ]: 0 : return ( pCC->uppercase(bTemp, 0, 1) + pCC->lowercase(aTerm,1,(tlen-1)) );
774 : :
775 [ # # ]: 0 : return pCC->uppercase(bTemp, 0, 1);
776 : : }
777 : 0 : return aTerm;
778 : : }
779 : :
780 : :
781 : 46 : Reference< XInterface > SAL_CALL Hyphenator_CreateInstance(
782 : : const Reference< XMultiServiceFactory > & /*rSMgr*/ )
783 : : throw(Exception)
784 : : {
785 [ + - ][ + - ]: 46 : Reference< XInterface > xService = (cppu::OWeakObject*) new Hyphenator;
786 : 46 : return xService;
787 : : }
788 : :
789 : :
790 : 9 : sal_Bool SAL_CALL Hyphenator::addLinguServiceEventListener(
791 : : const Reference< XLinguServiceEventListener >& rxLstnr )
792 : : throw(RuntimeException)
793 : : {
794 [ + - ][ + - ]: 9 : MutexGuard aGuard( GetLinguMutex() );
795 : :
796 : 9 : sal_Bool bRes = sal_False;
797 [ + - ][ + - ]: 9 : if (!bDisposing && rxLstnr.is())
[ + - ]
798 : : {
799 [ + - ][ + - ]: 9 : bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
800 : : }
801 [ + - ]: 9 : return bRes;
802 : : }
803 : :
804 : :
805 : 9 : sal_Bool SAL_CALL Hyphenator::removeLinguServiceEventListener(
806 : : const Reference< XLinguServiceEventListener >& rxLstnr )
807 : : throw(RuntimeException)
808 : : {
809 [ + - ][ + - ]: 9 : MutexGuard aGuard( GetLinguMutex() );
810 : :
811 : 9 : sal_Bool bRes = sal_False;
812 [ - + ][ # # ]: 9 : if (!bDisposing && rxLstnr.is())
[ - + ]
813 : : {
814 [ # # ][ # # ]: 0 : bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
815 : : }
816 [ + - ]: 9 : return bRes;
817 : : }
818 : :
819 : :
820 : 0 : OUString SAL_CALL Hyphenator::getServiceDisplayName( const Locale& /*rLocale*/ )
821 : : throw(RuntimeException)
822 : : {
823 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
824 [ # # ][ # # ]: 0 : return A2OU( "Libhyphen Hyphenator" );
825 : : }
826 : :
827 : :
828 : 0 : void SAL_CALL Hyphenator::initialize( const Sequence< Any >& rArguments )
829 : : throw(Exception, RuntimeException)
830 : : {
831 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
832 : :
833 [ # # ]: 0 : if (!pPropHelper)
834 : : {
835 : 0 : sal_Int32 nLen = rArguments.getLength();
836 [ # # ]: 0 : if (2 == nLen)
837 : : {
838 : 0 : Reference< XPropertySet > xPropSet;
839 [ # # ]: 0 : rArguments.getConstArray()[0] >>= xPropSet;
840 : : //rArguments.getConstArray()[1] >>= xDicList;
841 : :
842 : : //! Pointer allows for access of the non-UNO functions.
843 : : //! And the reference to the UNO-functions while increasing
844 : : //! the ref-count and will implicitly free the memory
845 : : //! when the object is not longer used.
846 [ # # ][ # # ]: 0 : pPropHelper = new PropertyHelper_Hyphenation( (XHyphenator *) this, xPropSet );
[ # # ]
847 [ # # ]: 0 : pPropHelper->AddAsPropListener(); //! after a reference is established
848 : : }
849 : : else {
850 : : OSL_FAIL( "wrong number of arguments in sequence" );
851 : : }
852 [ # # ]: 0 : }
853 : 0 : }
854 : :
855 : :
856 : 46 : void SAL_CALL Hyphenator::dispose()
857 : : throw(RuntimeException)
858 : : {
859 [ + - ][ + - ]: 46 : MutexGuard aGuard( GetLinguMutex() );
860 : :
861 [ + - ]: 46 : if (!bDisposing)
862 : : {
863 : 46 : bDisposing = sal_True;
864 [ + - ][ + - ]: 46 : EventObject aEvtObj( (XHyphenator *) this );
865 [ + - ]: 46 : aEvtListeners.disposeAndClear( aEvtObj );
866 [ + + ]: 46 : if (pPropHelper)
867 : : {
868 [ + - ]: 9 : pPropHelper->RemoveAsPropListener();
869 [ + - ][ + - ]: 9 : delete pPropHelper;
870 : 9 : pPropHelper = NULL;
871 [ + - ]: 46 : }
872 [ + - ]: 46 : }
873 : 46 : }
874 : :
875 : :
876 : 0 : void SAL_CALL Hyphenator::addEventListener( const Reference< XEventListener >& rxListener )
877 : : throw(RuntimeException)
878 : : {
879 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
880 : :
881 [ # # ][ # # ]: 0 : if (!bDisposing && rxListener.is())
[ # # ]
882 [ # # ][ # # ]: 0 : aEvtListeners.addInterface( rxListener );
883 : 0 : }
884 : :
885 : :
886 : 0 : void SAL_CALL Hyphenator::removeEventListener( const Reference< XEventListener >& rxListener )
887 : : throw(RuntimeException)
888 : : {
889 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
890 : :
891 [ # # ][ # # ]: 0 : if (!bDisposing && rxListener.is())
[ # # ]
892 [ # # ][ # # ]: 0 : aEvtListeners.removeInterface( rxListener );
893 : 0 : }
894 : :
895 : :
896 : : ///////////////////////////////////////////////////////////////////////////
897 : : // Service specific part
898 : : //
899 : :
900 : 48 : OUString SAL_CALL Hyphenator::getImplementationName()
901 : : throw(RuntimeException)
902 : : {
903 [ + - ][ + - ]: 48 : MutexGuard aGuard( GetLinguMutex() );
904 : :
905 [ + - ]: 48 : return getImplementationName_Static();
906 : : }
907 : :
908 : :
909 : 0 : sal_Bool SAL_CALL Hyphenator::supportsService( const OUString& ServiceName )
910 : : throw(RuntimeException)
911 : : {
912 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
913 : :
914 [ # # ]: 0 : Sequence< OUString > aSNL = getSupportedServiceNames();
915 : 0 : const OUString * pArray = aSNL.getConstArray();
916 [ # # ]: 0 : for( sal_Int32 i = 0; i < aSNL.getLength(); i++ )
917 [ # # ]: 0 : if( pArray[i] == ServiceName )
918 : 0 : return sal_True;
919 [ # # ][ # # ]: 0 : return sal_False;
920 : : }
921 : :
922 : :
923 : 0 : Sequence< OUString > SAL_CALL Hyphenator::getSupportedServiceNames()
924 : : throw(RuntimeException)
925 : : {
926 [ # # ][ # # ]: 0 : MutexGuard aGuard( GetLinguMutex() );
927 : :
928 [ # # ]: 0 : return getSupportedServiceNames_Static();
929 : : }
930 : :
931 : :
932 : 46 : Sequence< OUString > Hyphenator::getSupportedServiceNames_Static()
933 : : throw()
934 : : {
935 [ + - ][ + - ]: 46 : MutexGuard aGuard( GetLinguMutex() );
936 : :
937 [ + - ]: 46 : Sequence< OUString > aSNS( 1 ); // auch mehr als 1 Service moeglich
938 [ + - ][ + - ]: 46 : aSNS.getArray()[0] = A2OU( SN_HYPHENATOR );
939 [ + - ]: 46 : return aSNS;
940 : : }
941 : :
942 : 46 : void * SAL_CALL Hyphenator_getFactory( const sal_Char * pImplName,
943 : : XMultiServiceFactory * pServiceManager, void * )
944 : : {
945 : 46 : void * pRet = 0;
946 [ + - ]: 46 : if ( !Hyphenator::getImplementationName_Static().compareToAscii( pImplName ) )
947 : : {
948 : : Reference< XSingleServiceFactory > xFactory =
949 : : cppu::createOneInstanceFactory(
950 : : pServiceManager,
951 : : Hyphenator::getImplementationName_Static(),
952 : : Hyphenator_CreateInstance,
953 [ + - ][ + - ]: 46 : Hyphenator::getSupportedServiceNames_Static());
[ + - ]
954 : : // acquire, because we return an interface pointer instead of a reference
955 [ + - ]: 46 : xFactory->acquire();
956 [ + - ]: 46 : pRet = xFactory.get();
957 : : }
958 : 46 : return pRet;
959 : : }
960 : :
961 : :
962 : : ///////////////////////////////////////////////////////////////////////////
963 : :
964 : : #undef CAPTYPE_UNKNOWN
965 : : #undef CAPTYPE_NOCAP
966 : : #undef CAPTYPE_INITCAP
967 : : #undef CAPTYPE_ALLCAP
968 : : #undef CAPTYPE_MIXED
969 : :
970 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|