0030609: Coding - eliminate warnings issued by gcc 8.1.0
[occt.git] / src / Message / Message_MsgFile.cxx
1 // Created on: 2001-04-26
2 // Created by: OCC Team
3 // Copyright (c) 2001-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <Message_MsgFile.hxx>
17
18 #include <NCollection_Buffer.hxx>
19 #include <NCollection_DataMap.hxx>
20 #include <OSD_Environment.hxx>
21 #include <TCollection_AsciiString.hxx>
22 #include <TCollection_ExtendedString.hxx>
23 #include <Standard_Mutex.hxx>
24 #include <OSD_OpenFile.hxx>
25
26 #include <stdlib.h>
27 #include <stdio.h>
28
29 typedef NCollection_DataMap<TCollection_AsciiString,TCollection_ExtendedString> Message_DataMapOfExtendedString;
30
31 static Message_DataMapOfExtendedString& msgsDataMap ()
32 {
33   static Message_DataMapOfExtendedString aDataMap;
34   return aDataMap;
35 }
36
37 // mutex used to prevent concurrent access to message registry
38 static Standard_Mutex theMutex;
39
40 typedef enum
41 {
42   MsgFile_WaitingKeyword,
43   MsgFile_WaitingMessage,
44   MsgFile_WaitingMoreMessage,
45   MsgFile_Indefinite
46 } LoadingState;
47
48 //=======================================================================
49 //function : Message_MsgFile
50 //purpose  : Load file from given directories
51 //           theDirName may be represented as list: "/dirA/dirB /dirA/dirC"
52 //=======================================================================
53
54 Standard_Boolean Message_MsgFile::Load (const Standard_CString theDirName,
55                                         const Standard_CString theFileName)
56 {
57   if ( ! theDirName || ! theFileName ) return Standard_False;
58
59   Standard_Boolean ret = Standard_True;
60   TCollection_AsciiString aDirList (theDirName);
61   //  Try to load from all consecutive directories in list
62   for (int i = 1;; i++)
63   {
64     TCollection_AsciiString aFileName = aDirList.Token (" \t\n", i);
65     if (aFileName.IsEmpty()) break;
66 #ifdef _WIN32
67     aFileName += '\\';
68 #else
69     aFileName += '/';
70 #endif
71     aFileName += theFileName;
72     if ( ! LoadFile (aFileName.ToCString()) ) 
73       ret = Standard_False;
74   }
75   return ret;
76 }
77
78 //=======================================================================
79 //function : getString
80 //purpose  : Takes a TCollection_ExtendedString from Ascii or Unicode
81 //           Strings are left-trimmed; those beginning with '!' are omitted
82 //Called   : from loadFile()
83 //=======================================================================
84
85 template <class _Char> static inline Standard_Boolean
86 getString (_Char *&                     thePtr,
87            TCollection_ExtendedString&  theString,
88            Standard_Integer&            theLeftSpaces)
89 {
90   _Char * anEndPtr = thePtr;
91   _Char * aPtr;
92   Standard_Integer aLeftSpaces;
93
94   do 
95   {
96     //    Skip whitespaces in the beginning of the string
97     aPtr = anEndPtr;
98     aLeftSpaces = 0;
99     for (;;)
100     {
101       _Char aChar = * aPtr;
102       if      (aChar == ' ')  aLeftSpaces++;
103       else if (aChar == '\t') aLeftSpaces += 8;
104       else if (aChar == '\r' || * aPtr == '\n') aLeftSpaces = 0;
105       else break;
106       aPtr++;
107     }
108
109     //    Find the end of the string
110     for (anEndPtr = aPtr; * anEndPtr; anEndPtr++)
111       if (anEndPtr[0] == '\n')
112       {
113         if (anEndPtr[-1] == '\r') anEndPtr--;
114         break;
115       }
116
117   } while (aPtr[0] == '!');
118
119   //    form the result
120   if (aPtr == anEndPtr) return Standard_False;
121   thePtr = anEndPtr;
122   if (*thePtr)
123     *thePtr++ = '\0';
124   theString = TCollection_ExtendedString (aPtr);
125   theLeftSpaces = aLeftSpaces;
126   return Standard_True;
127 }
128
129 //=======================================================================
130 //function : loadFile
131 //purpose  : Static function, fills the DataMap of Messages from Ascii or Unicode
132 //Called   : from LoadFile()
133 //=======================================================================
134
135 template <class _Char> static inline Standard_Boolean loadFile (_Char * theBuffer)
136 {
137   TCollection_AsciiString               aKeyword;
138   TCollection_ExtendedString            aMessage, aString;
139   LoadingState                          aState = MsgFile_WaitingKeyword;
140   _Char                                 * sCurrentString = theBuffer;
141   Standard_Integer                      aLeftSpaces=0, aFirstLeftSpaces = 0;
142
143   //    Take strings one-by-one; comments already screened
144   while (::getString (sCurrentString, aString, aLeftSpaces))
145   {
146     Standard_Boolean isKeyword = (aString.Value(1) == '.');
147     switch (aState)
148     {
149     case MsgFile_WaitingMoreMessage:
150       if (isKeyword)
151         Message_MsgFile::AddMsg (aKeyword, aMessage); // terminate the previous one
152         //      Pass from here to 'case MsgFile_WaitingKeyword'
153       else
154       {
155         //      Add another line to the message already in the buffer 'aMessage'
156         aMessage += '\n';
157         aLeftSpaces -= aFirstLeftSpaces;
158         if (aLeftSpaces > 0) aMessage += TCollection_ExtendedString (aLeftSpaces, ' ');
159         aMessage += aString;
160         break;
161       }
162       Standard_FALLTHROUGH
163     case MsgFile_WaitingMessage:
164       if (isKeyword == Standard_False)
165       {
166         aMessage         = aString;
167         aFirstLeftSpaces = aLeftSpaces;         // remember the starting position
168         aState = MsgFile_WaitingMoreMessage;
169         break;
170       }
171       //      Pass from here to 'case MsgFile_WaitingKeyword'
172       Standard_FALLTHROUGH
173     case MsgFile_WaitingKeyword:
174       if (isKeyword)
175       {
176         // remove the first dot character and all subsequent spaces + right-trim
177         aKeyword = TCollection_AsciiString (aString.Split(1));
178         aKeyword.LeftAdjust();
179         aKeyword.RightAdjust();
180         aState = MsgFile_WaitingMessage;
181       }
182       break;
183     default:
184       break;
185     }
186   }
187   //    Process the last string still remaining in the buffer
188   if (aState == MsgFile_WaitingMoreMessage)
189     Message_MsgFile::AddMsg (aKeyword, aMessage);
190   return Standard_True;
191 }
192
193 //=======================================================================
194 //function : GetFileSize
195 //purpose  : 
196 //=======================================================================
197
198 static Standard_Integer GetFileSize (FILE *theFile)
199 {
200   if ( !theFile ) return -1;
201
202   // get real file size
203   long nRealFileSize = 0;
204   if ( fseek(theFile, 0, SEEK_END) != 0 ) return -1;
205   nRealFileSize = ftell(theFile);
206   if ( fseek(theFile, 0, SEEK_SET) != 0 ) return -1;
207
208   return (Standard_Integer) nRealFileSize;
209 }
210
211 //=======================================================================
212 //function : LoadFile
213 //purpose  : Load the list of messages from a file
214 //=======================================================================
215
216 Standard_Boolean Message_MsgFile::LoadFile (const Standard_CString theFileName)
217 {
218   if (theFileName == NULL || * theFileName == '\0') return Standard_False;
219
220   //    Open the file
221   FILE *anMsgFile = OSD_OpenFile(theFileName,"rb");
222   if (!anMsgFile)
223     return Standard_False;
224
225   const Standard_Integer aFileSize = GetFileSize (anMsgFile);
226   NCollection_Buffer aBuffer(NCollection_BaseAllocator::CommonBaseAllocator());
227   if (aFileSize <= 0 || !aBuffer.Allocate(aFileSize + 2))
228   {
229     fclose (anMsgFile);
230     return Standard_False;
231   }
232
233   char* anMsgBuffer = reinterpret_cast<char*>(aBuffer.ChangeData());
234   const Standard_Integer nbRead =
235     static_cast<Standard_Integer>( fread(anMsgBuffer, 1, aFileSize, anMsgFile) );
236
237   fclose (anMsgFile);
238   if (nbRead != aFileSize)
239     return Standard_False;
240
241   anMsgBuffer[aFileSize] = 0;
242   anMsgBuffer[aFileSize + 1] = 0;
243
244   // Read the messages in the file and append them to the global DataMap
245   Standard_Boolean isLittleEndian = (anMsgBuffer[0] == '\xff' && anMsgBuffer[1] == '\xfe');
246   Standard_Boolean isBigEndian    = (anMsgBuffer[0] == '\xfe' && anMsgBuffer[1] == '\xff');
247   if ( isLittleEndian || isBigEndian )
248   {
249     Standard_ExtCharacter* aUnicodeBuffer =
250       reinterpret_cast<Standard_ExtCharacter*>(&anMsgBuffer[2]);
251     // Convert Unicode representation to order adopted on current platform
252 #if defined(__sparc) && defined(__sun)
253     if ( isLittleEndian ) 
254 #else
255     if ( isBigEndian ) 
256 #endif
257     {
258       // Reverse the bytes throughout the buffer
259       const Standard_ExtCharacter* const anEnd =
260         reinterpret_cast<const Standard_ExtCharacter*>(&anMsgBuffer[aFileSize]);
261
262       for (Standard_ExtCharacter* aPtr = aUnicodeBuffer; aPtr < anEnd; aPtr++)
263       {
264         unsigned short aWord = *aPtr;
265         *aPtr = (aWord & 0x00ff) << 8 | (aWord & 0xff00) >> 8;
266       }
267     }
268     return ::loadFile (aUnicodeBuffer);
269   }
270   else
271     return ::loadFile (anMsgBuffer);
272 }
273
274 //=======================================================================
275 //function : LoadFromEnv
276 //purpose  :
277 //=======================================================================
278 Standard_Boolean Message_MsgFile::LoadFromEnv (const Standard_CString theEnvName,
279                                                const Standard_CString theFileName,
280                                                const Standard_CString theLangExt)
281 {
282   TCollection_AsciiString aLangExt (theLangExt != NULL ? theLangExt : "");
283   if (aLangExt.IsEmpty())
284   {
285     OSD_Environment aLangEnv ("CSF_LANGUAGE");
286     aLangExt = aLangEnv.Value();
287     if (aLangExt.IsEmpty())
288     {
289       aLangExt = "us";
290     }
291   }
292
293   TCollection_AsciiString aFilePath (theFileName);
294   if (theEnvName != NULL
295    && theEnvName[0] != '\0')
296   {
297     OSD_Environment aNameEnv (theEnvName);
298     TCollection_AsciiString aNameEnvStr = aNameEnv.Value();
299     if (!aNameEnvStr.IsEmpty())
300     {
301       if (aNameEnvStr.Value (aNameEnvStr.Length()) != '/')
302       {
303         aFilePath.Insert (1, '/');
304       }
305       aFilePath.Insert (1, aNameEnvStr);
306     }
307   }
308
309   if (aLangExt.Value (1) != '.')
310   {
311     aFilePath.AssignCat ('.');
312   }
313   aFilePath.AssignCat (aLangExt);
314
315   return Message_MsgFile::LoadFile (aFilePath.ToCString());
316 }
317
318 //=======================================================================
319 //function : LoadFromString
320 //purpose  :
321 //=======================================================================
322 Standard_Boolean Message_MsgFile::LoadFromString (const Standard_CString theContent,
323                                                   const Standard_Integer theLength)
324 {
325   Standard_Integer aStringSize = theLength >= 0 ? theLength : (Standard_Integer )strlen (theContent);
326   NCollection_Buffer aBuffer (NCollection_BaseAllocator::CommonBaseAllocator());
327   if (aStringSize <= 0 || !aBuffer.Allocate (aStringSize + 2))
328   {
329     return Standard_False;
330   }
331
332   memcpy (aBuffer.ChangeData(), theContent, aStringSize);
333   aBuffer.ChangeData()[aStringSize + 0] = '\0';
334   aBuffer.ChangeData()[aStringSize + 1] = '\0';
335   char* anMsgBuffer = reinterpret_cast<char*>(aBuffer.ChangeData());
336   return ::loadFile (anMsgBuffer);
337 }
338
339 //=======================================================================
340 //function : AddMsg
341 //purpose  : Add one message to the global table. Fails if the same keyword
342 //           already exists in the table
343 //=======================================================================
344
345 Standard_Boolean Message_MsgFile::AddMsg (const TCollection_AsciiString& theKeyword,
346                                           const TCollection_ExtendedString&  theMessage)
347 {
348   Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
349
350   Standard_Mutex::Sentry aSentry (theMutex);
351   aDataMap.Bind (theKeyword, theMessage);
352   return Standard_True;
353 }
354
355 //=======================================================================
356 //function : getMsg
357 //purpose  : retrieve the message previously defined for the given keyword
358 //=======================================================================
359
360 const TCollection_ExtendedString &Message_MsgFile::Msg (const Standard_CString theKeyword)
361 {
362   TCollection_AsciiString aKey((char*)theKeyword);
363   return Msg (aKey);
364
365
366 //=======================================================================
367 //function : HasMsg
368 //purpose  : 
369 //=======================================================================
370
371 Standard_Boolean Message_MsgFile::HasMsg (const TCollection_AsciiString& theKeyword)
372 {
373   Standard_Mutex::Sentry aSentry (theMutex);
374   return ::msgsDataMap().IsBound (theKeyword);
375 }
376
377 //=======================================================================
378 //function : Msg
379 //purpose  : retrieve the message previously defined for the given keyword
380 //=======================================================================
381
382 const TCollection_ExtendedString &Message_MsgFile::Msg (const TCollection_AsciiString& theKeyword)
383 {
384   // find message in the map
385   Message_DataMapOfExtendedString& aDataMap = ::msgsDataMap();
386   Standard_Mutex::Sentry aSentry (theMutex);
387
388   // if message is not found, generate error message and add it to the map to minimize overhead
389   // on consequent calls with the same key
390   if (! aDataMap.IsBound(theKeyword))
391   {
392     // text of the error message can be itself defined in the map
393     static const TCollection_AsciiString aPrefixCode("Message_Msg_BadKeyword");
394     static const TCollection_ExtendedString aDefPrefix("Unknown message invoked with the keyword ");
395     TCollection_AsciiString aErrorMessage = (aDataMap.IsBound(aPrefixCode) ? aDataMap(aPrefixCode) : aDefPrefix);
396     aErrorMessage += theKeyword;
397     aDataMap.Bind (theKeyword, aErrorMessage); // do not use AddMsg() here to avoid mutex deadlock
398   }
399
400   return aDataMap (theKeyword);
401 }