#pragma implementation #include #include #include #include #include #include #include #include #include #include #include "internal.h" #if defined (unix) #include #define O_BINARY 0 static const char *translat_readall(int fd, int size) { return (const char*)mmap(0,size,PROT_READ,MAP_SHARED,fd,0); } #else #if defined (UNIX) // Just to test the DOS/Windows mode in Unix. #include #define O_BINARY 0 #else #include #endif static const char *translat_readall(int fd, int size) { char *ptm = (char*)malloc(size); if (ptm != NULL){ if (read(fd,ptm,size)!=size){ fprintf (stderr,"Can't read messages database\n"); } }else{ fprintf (stderr,"Can't allocate memory for messages\n"); } close (fd); return ptm; } #endif #include "translat.h" static TRANSLATE_SYSTEM_REQ *first = NULL; PUBLIC TRANSLATE_SYSTEM_REQ::TRANSLATE_SYSTEM_REQ( const char *_sysname, const char **&_global_var, // Will point to an array // of message (char*) int _nb_expected, int _version) : global_var (_global_var) { nb_expected = _nb_expected; version = _version; /* #Specification: translate / dictionnary request A dictionnary request is done simply by declaring a static variable of type TRANSLATE_SYSTEM_REQ. We generally defined this variable in the source module which is always used within a system. Sometime, there is no such a module but indeed a few module (A library) which are candidate to be included in every program which use the librairy. It is possible to declare several static TRANSLATE_SYSTEM_REQ, one in every module we need. These request should use obviously the same name and the same global variable. Special macros are automagically defined by msgscan to insure consistency. */ TRANSLATE_SYSTEM_REQ *pt = first; while (pt != NULL){ if (strcmp(pt->sysname,_sysname)==0)break; pt = pt->next; } if (pt == NULL){ next = first; first = this; sysname = _sysname; } } static char *pterr = NULL; #define MAXERR_SIZE 2000 /* Print an error message and increment the err counter */ static void translat_err (const char *msg, ...) { va_list list; va_start (list,msg); pterr += vsnprintf (pterr,MAXERR_SIZE,msg,list); va_end (list); } static void translat_alloc ( BDICT_SYSTEM *tbsys, int nbsys, const char *base_file, // Start of mmap file const char *ptm) // start of indexes { /* The static variable "first" points to a linked list of TRANSLATE_SYSTEM_REQ. Each member corresponds to a dictionary to load. If we find a given dictionary in tbsys, then we remove the TRANSLATE_SYSTEM_REQ object from the linked list. Ultimatly, first wiil point to NULL. This strategy allows various things -One program may be composed of several dictionaries (one per directory generally) -One program may spread its dictionaries in several message files. This is especially useful when you are using various libraries where each one supplies its own message file. translat_alloc will be called several times. -One program (linuxconf for one) may have its own message file and each module provides also its message file. */ TRANSLATE_SYSTEM_REQ **preq = &first; while (*preq != NULL){ unsigned offset = 0; BDICT_SYSTEM *ptsys = tbsys; int i; TRANSLATE_SYSTEM_REQ *req = *preq; bool found = false; for (i=0; inbmsg; if (strcmp(req->sysname,ptsys->name)==0){ if (req->version > ptsys->version){ translat_err ("sub-dictionnary %s: Invalid version %d < %d\n" ,req->sysname,ptsys->version,req->version); }else if (req->nb_expected > ptsys->nbmsg){ translat_err ("sub-dictionnary %s: Not enough messages %d < %d\n" ,req->sysname,ptsys->nbmsg,req->nb_expected); }else{ req->global_var = (const char**)malloc(nbmsg*sizeof(char*)); const char **ptvar = req->global_var; unsigned *ptoff = (unsigned*)(ptm + offset); for (int m=0; mname,m,base_file,*ptvar); } // We remove the dictionary from the linked list *preq = req->next; found = true; } break; }else{ offset += nbmsg * sizeof(unsigned); } } if (!found) preq = &(req->next); } } /* Read a BDICT_HEADER in a binary compatible way on all architecture Return the pointer at the end of BDICT_HEADER in the buffer */ static const char *translat_readheader ( const char *ptm, BDICT_HEADER &hd) { unsigned short *val = (unsigned short*)ptm; hd.magic = ntohs (val[0]); hd.version = ntohs (val[1]); hd.nbsys = ntohs (val[2]); // printf ("magic = %x version = %x nbsys = %x\n",hd.magic,hd.version,hd.nbsys); return ptm+6; } /* Read a BDICT_SYSTEM in a binary compatible way on all architecture Return the pointer at the end of BDICT_SYSTEM in the buffer */ static const char *translat_readsys ( const char *ptm, BDICT_SYSTEM &sys) { strcpy (sys.name,ptm); ptm += sizeof(sys.name); unsigned short *val = (unsigned short*)ptm; sys.version = ntohs (val[0]); sys.nbmsg = ntohs (val[1]); return ptm+4; } /* Load the message dictionnary. Terminate the application if any error. */ static int translat_loaderr ( const char *basepath, // Directory holding dictionnaries const char *basename, // base name of the dictionnary // the language selection will supply // the extension. const char *lang, // Suffix for the language char errmsg[MAXERR_SIZE]) // Will contain the error message { int ret = -1; errmsg[0] = '\0'; pterr = errmsg; char path[PATH_MAX]; snprintf (path,sizeof(path)-1,"%s.%s/%s.%s",basepath,lang,basename,lang); int fd = open (path,O_BINARY|O_RDONLY); if (fd == -1){ translat_err ("Can't load dictionnary file %s (%s)\n" ,path,strerror (errno)); }else{ struct stat st; stat (path,&st); const char *ptm = translat_readall (fd,st.st_size); if (ptm != NULL){ const char *start_ptm = ptm;; BDICT_HEADER hd; ptm = translat_readheader (ptm,hd); if (hd.magic != BDICT_MAGIC){ translat_err ("Invalid dictionnary magic word: %s\n",path); }else if (hd.version != BDICT_VERSION){ translat_err ("Invalid dictionnary version: %s\n",path); }else{ BDICT_SYSTEM tbsys[hd.nbsys]; for (int i=0; i errmsg ? -1 : 0; } return ret; } /* Load the message dictionnary. Terminate the application if any error. */ void translat_load ( const char *basepath, // Directory holding dictionnaries const char *basename, // base name of the dictionnary // the language selection will supply // the extension. const char *lang) { char errmsg[MAXERR_SIZE]; if (translat_loaderr (basepath,basename,lang,errmsg)==-1){ fprintf (stderr,"%s",errmsg); exit (-1); } } /* Load the message dictionnary. Terminate the application if any error. */ void translat_load ( const char *basepath, // Default directory holding dictionnaries const char *envdirvar, // Environment variable to override basepath const char *basename, // base name of the dictionnary // the language selection will supply // the extension. const char *envlangvar, // Environment variable to override the // language const char *deflang) // Default language { /* #Specification: messages dictionnary / location / strategy The application specify the location and the default language for the message dictionnary. This is supplied as a basepath and a basename. The effective path of the dictionnary is # basepath/basename.lang # An environnement variable let you override the base path and another environnement variable let you override the language. For the linuxconf project, the environnement variables are for example. # LINUXCONF_DICT: base path LINUXCONF_LANG: language extension # The basepath may be overriden only if "geteuid() == getuid()" for security reason. */ if (geteuid() == getuid()){ char *en = getenv (envdirvar); if (en != NULL) basepath = en; } const char *lang = deflang; const char *lang_env = getenv(envlangvar); if (lang_env != NULL && strlen(lang_env)<=5) lang = lang_env; char errmsg[MAXERR_SIZE]; if (translat_loaderr (basepath,basename,lang,errmsg)==-1){ if (strcmp(lang,"eng")!=0){ bool showerr = getenv ("SHOWDICERR")!=NULL; if (showerr){ fprintf (stderr,"%s",errmsg); fprintf (stderr,"Switching to english\n"); } translat_load (basepath,basename,"eng"); }else{ fprintf (stderr,"%s",errmsg); exit(-1); } } } /* Make sure that all message dictionaries are loaded */ void translat_checkmissing() { TRANSLATE_SYSTEM_REQ *req = first; while (req != NULL){ fprintf (stderr,"Unknown dictionnary %s\n",req->sysname); req = req->next; } if (first != NULL) exit(-1); }