00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057 #ifdef HAVE_CONFIG_H
00058 # include "config.h"
00059 #endif
00060 #if NEED_GNUG_PRAGMAS
00061 # pragma implementation
00062 #endif
00063
00064
00065
00066
00067 #include "DjVuMessage.h"
00068 #include "GOS.h"
00069 #include "XMLTags.h"
00070 #include "ByteStream.h"
00071 #include "GURL.h"
00072 #include "debug.h"
00073 #include <ctype.h>
00074 #include <string.h>
00075 #include <stdlib.h>
00076 #ifdef WIN32
00077 # include <tchar.h>
00078 # include <atlbase.h>
00079 # include <windows.h>
00080 # include <winreg.h>
00081 #endif
00082 #ifdef UNIX
00083 # include <unistd.h>
00084 # include <pwd.h>
00085 # include <sys/types.h>
00086 #endif
00087 #include <locale.h>
00088 #ifndef LC_MESSAGES
00089 # define LC_MESSAGES LC_ALL
00090 #endif
00091
00092
00093 #ifdef HAVE_NAMESPACES
00094 namespace DJVU {
00095 # ifdef NOT_DEFINED // Just to fool emacs c++ mode
00096 }
00097 #endif
00098 #endif
00099
00100 GUTF8String &
00101 DjVuMessage::programname(void)
00102 {
00103 static GUTF8String xprogramname;
00104 use_language();
00105 return xprogramname;
00106 }
00107
00108 static const char namestring[]="name";
00109 static const char srcstring[]="src";
00110
00111 static const char *failed_to_parse_XML=ERR_MSG("DjVuMessage.failed_to_parse_XML");
00112 static const char bodystring[]="BODY";
00113 static const char languagestring[]="LANGUAGE";
00114 static const char headstring[]="HEAD";
00115 static const char includestring[]="INCLUDE";
00116 static const char messagestring[]="MESSAGE";
00117 static const char localestring[]="locale";
00118
00119
00120
00121 static const char opensourcedir[]="osi";
00122 #ifdef AUTOCONF
00123 static const char DjVuDataDir[] = DIR_DATADIR "/djvu";
00124 static const char ModuleDjVuDir[] ="share/djvu";
00125 #else
00126 static const char ModuleDjVuDir[] ="profiles";
00127 #endif
00128 static const char LocalDjVuDir[] =".DjVu";
00129 #ifdef LT_DEFAULT_PREFIX
00130 static const char DjVuPrefixDir[] = LT_DEFAULT_PREFIX "/profiles";
00131 #endif
00132 #ifndef NDEBUG
00133 static const char DebugModuleDjVuDir[] ="../TOPDIR/SRCDIR/profiles";
00134 #endif
00135 #ifdef WIN32
00136 static const char RootDjVuDir[] ="C:/Program Files/LizardTech/Profiles";
00137 static const TCHAR registrypath[]= TEXT("Software\\LizardTech\\DjVu\\Profile Path");
00138 #else
00139 static const char RootDjVuDir[] ="/etc/DjVu/";
00140 #endif
00141
00142 static const char DjVuEnv[] = "DJVU_CONFIG_DIR";
00143
00144
00145 static const char MessageFile[]="messages.xml";
00146 static const char LanguageFile[]="languages.xml";
00147
00148 #ifdef WIN32
00149 static GURL
00150 RegOpenReadConfig ( HKEY hParentKey )
00151 {
00152 GURL retval;
00153
00154 LPCTSTR path = registrypath;
00155
00156 HKEY hKey = 0;
00157
00158 if (RegOpenKeyEx(hParentKey, path, 0,
00159 KEY_READ, &hKey) == ERROR_SUCCESS )
00160 {
00161 TCHAR path[1024];
00162
00163 TCHAR *szPathValue = path;
00164 LPCTSTR lpszEntry = (LPCTSTR &)TEXT("");
00165 DWORD dwCount = (sizeof(path)/sizeof(TCHAR))-1;
00166 DWORD dwType;
00167
00168 LONG lResult = RegQueryValueEx(hKey, lpszEntry, NULL,
00169 &dwType, (LPBYTE) szPathValue, &dwCount);
00170
00171 RegCloseKey(hKey);
00172
00173 if ((lResult == ERROR_SUCCESS))
00174 {
00175 szPathValue[dwCount] = 0;
00176 USES_CONVERSION;
00177 retval=GURL::Filename::Native(T2CA(path));
00178 }
00179 }
00180
00181 return retval;
00182 }
00183
00184 static GURL
00185 GetModulePath( void )
00186 {
00187 const GUTF8String cwd(GOS::cwd());
00188 TCHAR path[1024];
00189 DWORD dwCount = (sizeof(path)/sizeof(TCHAR))-1;
00190 GetModuleFileName(0, path, dwCount);
00191 USES_CONVERSION;
00192 GURL retval=GURL::Filename::Native(T2CA(path)).base();
00193 GOS::cwd(cwd);
00194 return retval;
00195 }
00196 #elif defined(UNIX)
00197
00198 static GList<GURL>
00199 parsePATH(void)
00200 {
00201 GList<GURL> retval;
00202 const char *path=getenv("PATH");
00203 if(path)
00204 {
00205 GNativeString p(path);
00206 int from=0;
00207 for(int to;(to=p.search(':',from))>0;from=to+1)
00208 {
00209 if(to > from)
00210 {
00211 retval.append(GURL::Filename::Native(p.substr(from,to-from)));
00212 }
00213 }
00214 if((from+1)<(int)p.length())
00215 {
00216 retval.append(GURL::Filename::Native(p.substr(from,-1)));
00217 }
00218 }
00219 return retval;
00220 }
00221
00222 static GURL
00223 GetModulePath( void )
00224 {
00225 GURL retval;
00226 GUTF8String &xprogramname=DjVuMessage::programname();
00227 if(xprogramname.length())
00228 {
00229 if(xprogramname[1]=='/'
00230 ||!xprogramname.cmp("../",3)
00231 ||!xprogramname.cmp("./",2))
00232 {
00233 retval=GURL::Filename::UTF8(xprogramname);
00234 }
00235 if(retval.is_empty() || !retval.is_file())
00236 {
00237 GList<GURL> paths(parsePATH());
00238 GMap<GUTF8String,void *> pathMAP;
00239 for(GPosition pos=paths;pos;++pos)
00240 {
00241 retval=GURL::UTF8(xprogramname,paths[pos]);
00242 const GUTF8String path(retval.get_string());
00243 if(!pathMAP.contains(path))
00244 {
00245 if(retval.is_file())
00246 break;
00247 pathMAP[path]=0;
00248 }
00249 }
00250 }
00251 if (! retval.is_empty() )
00252 retval = retval.follow_symlinks();
00253 if (! retval.is_empty() )
00254 retval = retval.base();
00255 }
00256 return retval;
00257 }
00258 #endif
00259
00260 static void
00261 appendPath(const GURL &url,
00262 GMap<GUTF8String,void *> &map,
00263 GList<GURL> &list)
00264 {
00265 if( !url.is_empty()
00266 && !map.contains(url.get_string()) && url.is_dir() )
00267 {
00268 map[url.get_string()]=0;
00269 list.append(url);
00270 }
00271 }
00272
00273 GList<GURL>
00274 DjVuMessage::GetProfilePaths(void)
00275 {
00276 static bool first=true;
00277 static GList<GURL> realpaths;
00278 if(first)
00279 {
00280 first=false;
00281 GMap<GUTF8String,void *> pathsmap;
00282 GList<GURL> paths;
00283 GURL path;
00284 const GUTF8String envp(GOS::getenv(DjVuEnv));
00285 if(envp.length())
00286 appendPath(GURL::Filename::UTF8(envp),pathsmap,paths);
00287 #if defined(WIN32) || defined(UNIX)
00288 GURL mpath(GetModulePath());
00289 if(!mpath.is_empty() && mpath.is_dir())
00290 {
00291 #if defined(UNIX) && !defined(AUTOCONF) && !defined(NDEBUG)
00292 appendPath(GURL::UTF8(DebugModuleDjVuDir,mpath),pathsmap,paths);
00293 #endif
00294 appendPath(mpath,pathsmap,paths);
00295 mpath=mpath.base();
00296 appendPath(GURL::UTF8(ModuleDjVuDir,mpath),pathsmap,paths);
00297 mpath=mpath.base();
00298 appendPath(GURL::UTF8(ModuleDjVuDir,mpath),pathsmap,paths);
00299 }
00300 #endif
00301 #if defined(AUTOCONF)
00302 GURL dpath = GURL::Filename::UTF8(DjVuDataDir);
00303 appendPath(dpath,pathsmap,paths);
00304 #endif
00305 #ifdef WIN32
00306 appendPath(RegOpenReadConfig(HKEY_CURRENT_USER),pathsmap,paths);
00307 appendPath(RegOpenReadConfig(HKEY_LOCAL_MACHINE),pathsmap,paths);
00308 #else
00309 GUTF8String home=GOS::getenv("HOME");
00310 # if HAVE_GETPWUID
00311 if (! home.length()) {
00312 struct passwd *pw=0;
00313 if ((pw = getpwuid(getuid())))
00314 home=GNativeString(pw->pw_dir);
00315 }
00316 # endif
00317 if (home.length()) {
00318 GURL hpath = GURL::UTF8(LocalDjVuDir,GURL::Filename::UTF8(home));
00319 appendPath(hpath,pathsmap,paths);
00320 }
00321 #endif
00322 #ifdef LT_DEFAULT_PREFIX
00323 appendPath(GURL::Filename::UTF8(DjVuPrefixDir),pathsmap,paths);
00324 #endif
00325 appendPath(GURL::Filename::UTF8(RootDjVuDir),pathsmap,paths);
00326 pathsmap.empty();
00327
00328 GPosition pos;
00329 GList< GMap<GUTF8String,GP<lt_XMLTags> > > localemaps;
00330 for(pos=paths;pos;++pos)
00331 {
00332 path=GURL::UTF8(LanguageFile,paths[pos]);
00333 if(path.is_file())
00334 {
00335 const GP<lt_XMLTags> xml(lt_XMLTags::create(ByteStream::create(path,"rb")));
00336 const GPList<lt_XMLTags> Body(xml->get_Tags(bodystring));
00337 GPosition pos=Body;
00338 if(!pos || (pos != Body.lastpos()))
00339 {
00340 G_THROW( ERR_MSG("XMLAnno.extra_body") );
00341 }
00342 const GP<lt_XMLTags> GBody(Body[pos]);
00343 if(!GBody)
00344 {
00345 G_THROW( ERR_MSG("XMLAnno.no_body") );
00346 }
00347 GMap<GUTF8String,GP<lt_XMLTags> > localemap;
00348 lt_XMLTags::get_Maps(languagestring,localestring,Body,localemap);
00349 localemaps.append(localemap);
00350 }
00351 }
00352 GList<GURL> localepaths;
00353 GList<GURL> osilocalepaths;
00354
00355
00356 GUTF8String defaultlocale = getenv("LANGUAGE");
00357 if (! defaultlocale)
00358 {
00359 const GUTF8String oldlocale(setlocale(LC_MESSAGES,0));
00360 defaultlocale = setlocale(LC_MESSAGES,"");
00361 setlocale(LC_MESSAGES,(const char *)oldlocale);
00362 }
00363
00364 for(int loop=0; loop<2; loop++)
00365 {
00366 static const char sepchars[]=" _.@";
00367 const char *p=sepchars+sizeof(sepchars)-1;
00368 do
00369 {
00370 int sepcharpos=p[0]?defaultlocale.search(p[0]):defaultlocale.length();
00371 if(sepcharpos > 0)
00372 {
00373 const GUTF8String sublocale(defaultlocale,sepcharpos);
00374 const GUTF8String downcasesublocale("downcase^"+sublocale.downcase());
00375 for(pos=localemaps;pos;++pos)
00376 {
00377 const GMap<GUTF8String,GP<lt_XMLTags> > &localemap=localemaps[pos];
00378 GPosition pos=localemap.contains(sublocale);
00379 if(!pos)
00380 pos=localemap.contains(downcasesublocale);
00381 if(pos)
00382 {
00383 const GMap<GUTF8String,GUTF8String>&args
00384 = localemap[pos]->get_args();
00385 pos = args.contains(srcstring);
00386 if (pos)
00387 {
00388 const GUTF8String src(args[pos]);
00389 for(pos=paths;pos;++pos)
00390 {
00391 path=GURL::UTF8(src,paths[pos]);
00392 if(path.is_dir())
00393 localepaths.append(path);
00394 path=GURL::UTF8(GUTF8String(opensourcedir)+"/"+src,paths[pos]);
00395 if(path.is_dir())
00396 osilocalepaths.append(path);
00397 }
00398 }
00399
00400 p=sepchars;
00401 break;
00402 }
00403 }
00404 if(!pos)
00405 {
00406 for(pos=paths;pos;++pos)
00407 {
00408 path=GURL::UTF8(sublocale,paths[pos]);
00409 if(path.is_dir())
00410 {
00411 localepaths.append(path);
00412 }
00413 path=GURL::UTF8(GUTF8String(opensourcedir)+"/"+sublocale,paths[pos]);
00414 if(path.is_dir())
00415 {
00416 osilocalepaths.append(path);
00417 }
00418 }
00419 }
00420 }
00421 } while(p-- != sepchars);
00422 if((GPosition) localepaths)
00423 break;
00424 defaultlocale="C";
00425 }
00426 for(pos=localepaths;pos;++pos)
00427 appendPath(localepaths[pos],pathsmap,realpaths);
00428 for(pos=paths;pos;++pos)
00429 appendPath(paths[pos],pathsmap,realpaths);
00430 for(pos=osilocalepaths;pos;++pos)
00431 appendPath(osilocalepaths[pos],pathsmap,realpaths);
00432 for(pos=paths;pos;++pos)
00433 {
00434 path=GURL::UTF8(opensourcedir,paths[pos]);
00435 appendPath(path,pathsmap,realpaths);
00436 }
00437 }
00438 return realpaths;
00439 }
00440
00441 static GUTF8String
00442 getbodies(
00443 GList<GURL> &paths,
00444 const GUTF8String &MessageFileName,
00445 GPList<lt_XMLTags> &body,
00446 GMap<GUTF8String, void *> & map )
00447 {
00448 GUTF8String errors;
00449 bool isdone=false;
00450 GPosition firstpathpos=paths;
00451 for(GPosition pathpos=firstpathpos;!isdone && pathpos;++pathpos)
00452 {
00453 const GURL::UTF8 url(MessageFileName,paths[pathpos]);
00454 if(url.is_file())
00455 {
00456 map[MessageFileName]=0;
00457 GP<lt_XMLTags> gtags;
00458 {
00459 GP<ByteStream> bs=ByteStream::create(url,"rb");
00460 G_TRY
00461 {
00462 gtags=lt_XMLTags::create(bs);
00463 }
00464 G_CATCH(ex)
00465 {
00466 GUTF8String mesg(failed_to_parse_XML+("\t"+url.get_string()));
00467 if(errors.length())
00468 {
00469 errors+="\n"+mesg;
00470 }else
00471 {
00472 errors=mesg;
00473 }
00474 errors+="\n"+GUTF8String(ex.get_cause());
00475 }
00476 G_ENDCATCH;
00477 }
00478 if(gtags)
00479 {
00480 lt_XMLTags &tags=*gtags;
00481 GPList<lt_XMLTags> Bodies=tags.get_Tags(bodystring);
00482 if(! Bodies.isempty())
00483 {
00484 isdone=true;
00485 for(GPosition pos=Bodies;pos;++pos)
00486 {
00487 body.append(Bodies[pos]);
00488 }
00489 }
00490 GPList<lt_XMLTags> Head=tags.get_Tags(headstring);
00491 if(! Head.isempty())
00492 {
00493 isdone=true;
00494 GMap<GUTF8String, GP<lt_XMLTags> > includes;
00495 lt_XMLTags::get_Maps(includestring,namestring,Head,includes);
00496 for(GPosition pos=includes;pos;++pos)
00497 {
00498 const GUTF8String file=includes.key(pos);
00499 if(! map.contains(file))
00500 {
00501 GList<GURL> xpaths;
00502 xpaths.append(url.base());
00503 const GUTF8String err2(getbodies(xpaths,file,body,map));
00504 if(err2.length())
00505 {
00506 if(errors.length())
00507 {
00508 errors+="\n"+err2;
00509 }else
00510 {
00511 errors=err2;
00512 }
00513 }
00514 }
00515 }
00516 }
00517 }
00518 }
00519 }
00520 return errors;
00521 }
00522
00523 static GUTF8String
00524 parse(GMap<GUTF8String,GP<lt_XMLTags> > &retval)
00525 {
00526 GUTF8String errors;
00527 GPList<lt_XMLTags> body;
00528 {
00529 GList<GURL> paths=DjVuMessage::GetProfilePaths();
00530 GMap<GUTF8String, void *> map;
00531 GUTF8String m(MessageFile);
00532 errors=getbodies(paths,m,body,map);
00533 }
00534 if(! body.isempty())
00535 {
00536 lt_XMLTags::get_Maps(messagestring,namestring,body,retval);
00537 }
00538 return errors;
00539 }
00540
00541
00542 const DjVuMessageLite &
00543 DjVuMessage::create_full(void)
00544 {
00545 GP<DjVuMessageLite> &static_message=getDjVuMessageLite();
00546 if(!static_message)
00547 {
00548 DjVuMessage *mesg=new DjVuMessage;
00549 static_message=mesg;
00550 mesg->init();
00551 }
00552 return DjVuMessageLite::create_lite();
00553 }
00554
00555 void
00556 DjVuMessage::set_programname(const GUTF8String &xprogramname)
00557 {
00558 programname()=xprogramname;
00559 DjVuMessageLite::create=create_full;
00560 }
00561
00562 void
00563 DjVuMessage::use_language(void)
00564 {
00565 DjVuMessageLite::create=create_full;
00566 }
00567
00568
00569
00570 DjVuMessage::DjVuMessage( void ) {}
00571
00572 void
00573 DjVuMessage::init(void)
00574 {
00575 errors=parse(Map);
00576 }
00577
00578
00579 DjVuMessage::~DjVuMessage( )
00580 {
00581 }
00582
00583
00584
00585
00586
00587
00588 void
00589 DjVuMessageLookUpNative(
00590 char *msg_buffer, const unsigned int buffer_size, const char *message)
00591 {
00592 const GNativeString converted(DjVuMessage::LookUpNative( message ));
00593 if( converted.length() >= buffer_size )
00594 msg_buffer[0] = '\0';
00595 else
00596 strcpy( msg_buffer, converted );
00597 }
00598
00599
00600
00601
00602
00603 void
00604 DjVuMessageLookUpUTF8(
00605 char *msg_buffer, const unsigned int buffer_size, const char *message)
00606 {
00607 const GUTF8String converted(DjVuMessage::LookUpUTF8( message ));
00608 if( converted.length() >= buffer_size )
00609 msg_buffer[0] = '\0';
00610 else
00611 strcpy( msg_buffer, converted );
00612 }
00613
00614
00615
00616 #ifdef HAVE_NAMESPACES
00617 }
00618 # ifndef NOT_USING_DJVU_NAMESPACE
00619 using namespace DJVU;
00620 # endif
00621 #endif
00622
00623 void
00624 DjVuFormatErrorUTF8( const char *fmt, ... )
00625 {
00626 va_list args;
00627 va_start(args, fmt);
00628 const GUTF8String message(fmt,args);
00629 DjVuWriteError( message );
00630 }
00631
00632 void
00633 DjVuFormatErrorNative( const char *fmt, ... )
00634 {
00635 va_list args;
00636 va_start(args, fmt);
00637 const GNativeString message(fmt,args);
00638 DjVuWriteError( message );
00639 }
00640
00641 const char *
00642 djvu_programname(const char *xprogramname)
00643 {
00644 if(xprogramname)
00645 DjVuMessage::programname()=GNativeString(xprogramname);
00646 return DjVuMessage::programname();
00647 }