1 //C-  -*- C++ -*-
2 //C- -------------------------------------------------------------------
3 //C- DjVuLibre-3.5
4 //C- Copyright (c) 2002  Leon Bottou and Yann Le Cun.
5 //C- Copyright (c) 2001  AT&T
6 //C-
7 //C- This software is subject to, and may be distributed under, the
8 //C- GNU General Public License, either Version 2 of the license,
9 //C- or (at your option) any later version. The license should have
10 //C- accompanied the software or you may obtain a copy of the license
11 //C- from the Free Software Foundation at http://www.fsf.org .
12 //C-
13 //C- This program is distributed in the hope that it will be useful,
14 //C- but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //C- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //C- GNU General Public License for more details.
17 //C-
18 //C- DjVuLibre-3.5 is derived from the DjVu(r) Reference Library from
19 //C- Lizardtech Software.  Lizardtech Software has authorized us to
20 //C- replace the original DjVu(r) Reference Library notice by the following
21 //C- text (see doc/lizard2002.djvu and doc/lizardtech2007.djvu):
22 //C-
23 //C-  ------------------------------------------------------------------
24 //C- | DjVu (r) Reference Library (v. 3.5)
25 //C- | Copyright (c) 1999-2001 LizardTech, Inc. All Rights Reserved.
26 //C- | The DjVu Reference Library is protected by U.S. Pat. No.
27 //C- | 6,058,214 and patents pending.
28 //C- |
29 //C- | This software is subject to, and may be distributed under, the
30 //C- | GNU General Public License, either Version 2 of the license,
31 //C- | or (at your option) any later version. The license should have
32 //C- | accompanied the software or you may obtain a copy of the license
33 //C- | from the Free Software Foundation at http://www.fsf.org .
34 //C- |
35 //C- | The computer code originally released by LizardTech under this
36 //C- | license and unmodified by other parties is deemed "the LIZARDTECH
37 //C- | ORIGINAL CODE."  Subject to any third party intellectual property
38 //C- | claims, LizardTech grants recipient a worldwide, royalty-free,
39 //C- | non-exclusive license to make, use, sell, or otherwise dispose of
40 //C- | the LIZARDTECH ORIGINAL CODE or of programs derived from the
41 //C- | LIZARDTECH ORIGINAL CODE in compliance with the terms of the GNU
42 //C- | General Public License.   This grant only confers the right to
43 //C- | infringe patent claims underlying the LIZARDTECH ORIGINAL CODE to
44 //C- | the extent such infringement is reasonably necessary to enable
45 //C- | recipient to make, have made, practice, sell, or otherwise dispose
46 //C- | of the LIZARDTECH ORIGINAL CODE (or portions thereof) and not to
47 //C- | any greater extent that may be necessary to utilize further
48 //C- | modifications or combinations.
49 //C- |
50 //C- | The LIZARDTECH ORIGINAL CODE is provided "AS IS" WITHOUT WARRANTY
51 //C- | OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
52 //C- | TO ANY WARRANTY OF NON-INFRINGEMENT, OR ANY IMPLIED WARRANTY OF
53 //C- | MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
54 //C- +------------------------------------------------------------------
55 
56 #ifdef HAVE_CONFIG_H
57 # include "config.h"
58 #endif
59 #if NEED_GNUG_PRAGMAS
60 # pragma implementation
61 #endif
62 
63 // From: Leon Bottou, 1/31/2002
64 // All these I18N XML messages are Lizardtech innovations.
65 // For DjvuLibre, I changed the path extraction logic
66 // and added support for non I18N messages.
67 
68 #include "DjVuMessageLite.h"
69 #include "GOS.h"
70 #include "XMLTags.h"
71 #include "ByteStream.h"
72 #include "GURL.h"
73 #include "debug.h"
74 #include <ctype.h>
75 #include <string.h>
76 #include <stddef.h>
77 #include <stdlib.h>
78 #ifdef _WIN32
79 #include <tchar.h>
80 #include <windows.h>
81 #include <winreg.h>
82 #endif
83 #ifdef UNIX
84 #include <unistd.h>
85 #include <pwd.h>
86 #include <sys/types.h>
87 #endif
88 #include <locale.h>
89 
90 
91 #ifdef HAVE_NAMESPACES
92 namespace DJVU {
93 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
94 }
95 #endif
96 #endif
97 
98 
99 const DjVuMessageLite& (*DjVuMessageLite::create)(void) =
100   DjVuMessageLite::create_lite;
101 
102 static const char *failed_to_parse_XML=
103   ERR_MSG("DjVuMessage.failed_to_parse_XML");
104 static const char *unrecognized=
105   ERR_MSG("DjVuMessage.Unrecognized");
106 static const char *uparameter=
107   ERR_MSG("DjVuMessage.Parameter");
108 #ifdef LIZARDTECH_1_800_NUMBER
109 static const char unrecognized_default[] =
110   "** Unrecognized DjVu Message: [Contact LizardTech at "
111   LIZARDTECH_1_800_NUMBER " \n"
112   "\t** Message name:  %1!s!";
113 #else
114 static const char unrecognized_default[] =
115   "** Unrecognized DjVu Message:\n"
116   "\t** Message name:  %1!s!";
117 #endif
118 static const char uparameter_default[] =
119   "\t   Parameter: %1!s!";
120 static const char failed_to_parse_XML_default[]=
121   "Failed to parse XML message file:&#10;&#09;&apos;%1!s!&apos;.";
122 
123 static const char namestring[]="name";
124 static const char valuestring[]="value";
125 static const char numberstring[]="number";
126 static const char bodystring[]="BODY";
127 static const char messagestring[]="MESSAGE";
128 
129 GPList<ByteStream> &
getByteStream(void)130 DjVuMessageLite::getByteStream(void)
131 {
132   static GPList<ByteStream> gbs;
133   return gbs;
134 }
135 
136 GP<DjVuMessageLite> &
getDjVuMessageLite(void)137 DjVuMessageLite::getDjVuMessageLite(void)
138 {
139   static GP<DjVuMessageLite> message;
140   return message;
141 }
142 
143 void
AddByteStreamLater(const GP<ByteStream> & bs)144 DjVuMessageLite::AddByteStreamLater(const GP<ByteStream> &bs)
145 {
146   getByteStream().append(bs);
147 }
148 
149 //  There is only object of class DjVuMessage in a program, and here it is:
150 //DjVuMessage  DjVuMsg;
151 const DjVuMessageLite &
create_lite(void)152 DjVuMessageLite::create_lite(void)
153 {
154   GP<DjVuMessageLite> &static_message=getDjVuMessageLite();
155   if(!static_message)
156   {
157     static_message=new DjVuMessageLite;
158   }
159   DjVuMessageLite &m=*static_message;
160   GPList<ByteStream> &bs = getByteStream();
161   for(GPosition pos;(pos=bs);bs.del(pos))
162   {
163     m.AddByteStream(bs[pos]);
164   }
165   return m;
166 }
167 
168 // Constructor
DjVuMessageLite(void)169 DjVuMessageLite::DjVuMessageLite( void ) {}
170 
171 // Destructor
~DjVuMessageLite()172 DjVuMessageLite::~DjVuMessageLite( ) {}
173 
174 
175 void
perror(const GUTF8String & MessageList)176 DjVuMessageLite::perror( const GUTF8String & MessageList )
177 {
178   DjVuPrintErrorUTF8("%s\n",(const char *)DjVuMessageLite::LookUpUTF8(MessageList));
179 }
180 
181 
182 //  Expands message lists by looking up the message IDs and inserting
183 //  arguments into the retrieved messages.
184 //  N.B. The resulting string may be encoded in UTF-8 format (ISO 10646-1 Annex R)
185 //       and SHOULD NOT BE ASSUMED TO BE ASCII.
186 GUTF8String
LookUp(const GUTF8String & MessageList) const187 DjVuMessageLite::LookUp( const GUTF8String & MessageList ) const
188 {
189 //  DEBUG_MSG( "========== DjVuMessageLite::LookUp ==========\n" <<
190 //             MessageList <<
191 //             "\n========== DjVuMessageLite::LookUp ==========\n" );
192   GUTF8String result;                       // Result string; begins empty
193   if(errors.length())
194   {
195     const GUTF8String err1(errors);
196     (const_cast<GUTF8String &>(errors)).empty();
197     result=LookUp(err1)+"\n";
198   }
199 
200   int start = 0;                            // Beginning of next message
201   int end = MessageList.length();           // End of the message string
202 
203   //  Isolate single messages and process them
204   while( start < end )
205   {
206     if( MessageList[start] == '\n' )
207     {
208       result += MessageList[start++];       // move the newline to the result
209                                             // and advance to the next message
210     }
211     else
212     {
213       //  Find the end of the next message and process it
214       int next_ending = MessageList.search((unsigned long)'\n', start);
215       if( next_ending < 0 )
216         next_ending = end;
217       result += LookUpSingle( MessageList.substr(start, next_ending-start) );
218       //  Advance to the next message
219       start = next_ending;
220     }
221   }
222 
223   //  All done
224   return result;
225 }
226 
227 
228 // Expands a single message and inserts the arguments. Single_Message
229 // contains no separators (newlines), but includes all the parameters
230 // separated by tabs.
231 GUTF8String
LookUpSingle(const GUTF8String & Single_Message) const232 DjVuMessageLite::LookUpSingle( const GUTF8String &Single_Message ) const
233 {
234 #if HAS_CTRL_C_IN_ERR_MSG
235   if (Single_Message[0] != '\003')
236     return Single_Message;
237 #endif
238   //  Isolate the message ID and get the corresponding message text
239   int ending_posn = Single_Message.contains("\t\v");
240   if( ending_posn < 0 )
241     ending_posn = Single_Message.length();
242   GUTF8String msg_text;
243   GUTF8String msg_number;
244   const GUTF8String message=Single_Message.substr(0,ending_posn);
245   LookUpID( message, msg_text, msg_number );
246 
247   //  Check whether we found anything
248   if( !msg_text.length())
249   {
250     if(message == unrecognized)
251     {
252       msg_text = unrecognized_default;
253     }else if(message == uparameter)
254     {
255       msg_text = uparameter_default;
256     }else if(message == failed_to_parse_XML)
257     {
258       msg_text = failed_to_parse_XML_default;
259     }else
260     {
261       return LookUpSingle(unrecognized + ("\t" + Single_Message));
262     }
263   }
264 
265   //  Insert the parameters (if any)
266   unsigned int param_num = 0;
267   while( (unsigned int)ending_posn < Single_Message.length() )
268   {
269     GUTF8String arg;
270     const int start_posn = ending_posn+1;
271     if(Single_Message[ending_posn] == '\v')
272     {
273       ending_posn=Single_Message.length();
274       arg=LookUpSingle(Single_Message.substr(start_posn,ending_posn));
275     }else
276     {
277       ending_posn = Single_Message.contains("\v\t",start_posn);
278       if( ending_posn < 0 )
279         ending_posn = Single_Message.length();
280       arg=Single_Message.substr(start_posn, ending_posn-start_posn);
281     }
282     InsertArg( msg_text, ++param_num, arg);
283   }
284   //  Insert the message number
285   InsertArg( msg_text, 0, msg_number );
286 
287   return msg_text;
288 }
289 
290 
291 // Looks up the msgID in the file of messages and returns a pointer to
292 // the beginning of the translated message, if found; and an empty string
293 // otherwise.
294 void
LookUpID(const GUTF8String & xmsgID,GUTF8String & message_text,GUTF8String & message_number) const295 DjVuMessageLite::LookUpID( const GUTF8String &xmsgID,
296                        GUTF8String &message_text,
297                        GUTF8String &message_number ) const
298 {
299   if(!Map.isempty())
300   {
301     GUTF8String msgID = xmsgID;
302 #if HAS_CTRL_C_IN_ERR_MSG
303     int start = 0;
304     while (msgID[start] == '\003')
305       start ++;
306     if (start > 0)
307       msgID = msgID.substr(start, msgID.length() - start);
308 #endif
309     GPosition pos=Map.contains(msgID);
310     if(pos)
311     {
312       const GP<lt_XMLTags> tag=Map[pos];
313       GPosition valuepos=tag->get_args().contains(valuestring);
314       if(valuepos)
315       {
316         message_text=tag->get_args()[valuepos];
317       }else
318       {
319         const GUTF8String raw(tag->get_raw());
320         const int start_line=raw.search((unsigned long)'\n',0);
321 
322         const int start_text=raw.nextNonSpace(0);
323         const int end_text=raw.firstEndSpace(0);
324         if(start_line<0 || start_text<0 || start_text < start_line)
325         {
326           message_text=raw.substr(0,end_text).fromEscaped();
327         }else
328         {
329           message_text=raw.substr(start_line+1,end_text-start_line-1).fromEscaped();
330         }
331       }
332       GPosition numberpos=tag->get_args().contains(numberstring);
333       if(numberpos)
334       {
335         message_number=tag->get_args()[numberpos];
336       }
337     }
338   }
339 }
340 
341 
342 // Insert a string into the message text. Will insert into any field
343 // description.  Except for an ArgId of zero (message number), if the ArgId
344 // is not found, the routine adds a line with the parameter so information
345 // will not be lost.
346 void
InsertArg(GUTF8String & message,const int ArgId,const GUTF8String & arg) const347 DjVuMessageLite::InsertArg( GUTF8String &message,
348   const int ArgId, const GUTF8String &arg ) const
349 {
350     // argument target string
351   const GUTF8String target= "%"+GUTF8String(ArgId)+"!";
352     // location of target string
353   int format_start = message.search( (const char *)target );
354   if( format_start >= 0 )
355   {
356     do
357     {
358       const int n=format_start+target.length()+1;
359       const int format_end=message.search((unsigned long)'!',n);
360       if(format_end > format_start)
361       {
362         const int len=1+format_end-n;
363         if(len && isascii(message[n-1]))
364         {
365           GUTF8String narg;
366           GUTF8String format="%"+message.substr(n-1,len);
367           switch(format[len])
368           {
369             case 'd':
370             case 'i':
371               narg.format((const char *)format,arg.toInt());
372               break;
373             case 'u':
374             case 'o':
375             case 'x':
376             case 'X':
377               narg.format((const char *)format,(unsigned int)arg.toInt());
378               break;
379             case 'f':
380             case 'g':
381             case 'e':
382               {
383                 int endpos;
384                 narg.format((const char *)format, arg.toDouble(0,endpos));
385                 if( endpos < 0 )
386                   narg = arg;
387               }
388               break;
389             default:
390               narg.format((const char *)format,(const char *)arg);
391               break;
392           }
393           message = message.substr( 0, format_start )+narg
394             +message.substr( format_end+1, -1 );
395         }else
396         {
397           message = message.substr( 0, format_start )+arg
398             +message.substr( format_end+1, -1 );
399         }
400       }
401       format_start=message.search((const char*)target, format_start+arg.length());
402     } while(format_start >= 0);
403   }
404   else
405   {
406     //  Not found, fake it
407     if( ArgId != 0 )
408     {
409       message += "\n"+LookUpSingle(uparameter+("\t"+arg));
410     }
411   }
412 }
413 
414 
415 //  A C function to perform a message lookup. Arguments are a buffer to received the
416 //  translated message, a buffer size (bytes), and a message_list. The translated
417 //  result is returned in msg_buffer encoded in UTF-8. In case of error, msg_buffer is
418 //  empty (i.e., msg_buffer[0] == '\0').
419 void
DjVuMessageLite_LookUp(char * msg_buffer,const unsigned int buffer_size,const char * message)420 DjVuMessageLite_LookUp( char *msg_buffer, const unsigned int buffer_size, const char *message )
421 {
422   GUTF8String converted = DjVuMessageLite::LookUpUTF8( message );
423   if( converted.length() >= buffer_size )
424     msg_buffer[0] = '\0';
425   else
426     strcpy( msg_buffer, converted );
427 }
428 
429 void
AddByteStream(const GP<ByteStream> & bs)430 DjVuMessageLite::AddByteStream(const GP<ByteStream> &bs)
431 {
432   const GP<lt_XMLTags> gtags(lt_XMLTags::create(bs));
433   lt_XMLTags &tags=*gtags;
434   GPList<lt_XMLTags> Bodies=tags.get_Tags(bodystring);
435   if(! Bodies.isempty())
436   {
437     lt_XMLTags::get_Maps(messagestring,namestring,Bodies,Map);
438   }
439 }
440 
441 
442 
443 #ifdef HAVE_NAMESPACES
444 }
445 # ifndef NOT_USING_DJVU_NAMESPACE
446 using namespace DJVU;
447 # endif
448 #endif
449 
450 void
451 DjVuWriteError( const char *message )
452 {
453   G_TRY {
454     GP<ByteStream> errout = ByteStream::get_stderr();
455     if (errout)
456       {
457         const GUTF8String external = DjVuMessageLite::LookUpUTF8( message );
458         errout->writestring(external+"\n");
459       }
460     // Need to catch all exceptions because these might be
461     // called from an outer exception handler (with prejudice)
462   } G_CATCH_ALL { } G_ENDCATCH;
463 }
464 
465 void
466 DjVuWriteMessage( const char *message )
467 {
468   G_TRY {
469     GP<ByteStream> strout = ByteStream::get_stdout();
470     if (strout)
471       {
472         const GUTF8String external = DjVuMessageLite::LookUpUTF8( message );
473         strout->writestring(external+"\n");
474       }
475     // Need to catch all exceptions because these might be
476     // called from an outer exception handler (with prejudice)
477   } G_CATCH_ALL { } G_ENDCATCH;
478 }
479