#include #include #include #include #include #include #include "internal.h" // This table indicates the correspondance for the first // string and second string of the MSG_B macro. static char *tblang; #ifdef UNIX #include #define MAXIMUM_PATH PATH_MAX #else #define MAXIMUM_PATH 128 #endif struct READINFO{ int noline; char buf[1000]; FILE *fin; }; /* Print an error message in a popup Stubs to avoid linking the world */ void xconf_error (const char *msg, ...) { va_list list; va_start (list,msg); vfprintf (stderr,msg,list); va_end (list); } struct { const char *fname; int noline; int nberr; int showname; int showline; // First error for a context }err; /* Record basic info about errors */ static void msgscan_errset (const char *fname, int noline) { if (err.fname == NULL || strcmp(err.fname,fname)!=0){ err.showname = 1; } err.fname = fname; err.noline = noline; err.showline = 1; } /* Display an error message with context */ static void msgscan_err (const char *ctl, ...) { va_list list; va_start (list,ctl); if (err.showname){ fprintf (stderr,"ERR: In file %s\n",err.fname); err.showname = 0; } if (err.showline){ fprintf (stderr,"\t\tstarting on line %d\n",err.noline); err.showline = 0; } fprintf (stderr,"\t\t\t"); vfprintf (stderr,ctl,list); va_end (list); } static int msgscan_read (READINFO &inf, int signal_err) { int ret = fgets_cont (inf.buf,sizeof(inf.buf)-1,inf.fin); inf.noline++; if (ret == -1 && signal_err){ msgscan_err ("EOF while parsing\n"); } return ret; } static char *scan_copyid (const char * pt, char id[]) { pt = str_skip (pt); char *ptid = id; while (isalpha(*pt) || isdigit(*pt) || *pt == '_'){ *ptid++ = *pt++; } *ptid = '\0'; return (char*)pt; } static int scan_parseid (char * &pt, char id[]) { int ret = -1; pt = scan_copyid (pt,id); if (id[0] != '\0'){ if (isdigit(id[0])){ msgscan_err ("Invalid id %s\n",id); }else{ ret = 0; } } return ret; } /* Extract a single string from a C source. */ static int scan_parse1str (char * &pt, SSTRING &s) { int ret = -1; pt = str_skip (pt); if (*pt != '"'){ msgscan_err ("Expected an openning \"\n"); }else{ pt++; char *start = pt; while (1){ if (*pt == '\0'){ msgscan_err ("Expected an ending \"\n"); break; }else if (*pt == '"'){ ret = 0; *pt++ = '\0'; s.append (start); break; }else if (*pt == '\\'){ pt++; if (*pt != '\0') pt++; }else{ pt++; } } } return ret; } /* Extract a string (or sequence) from a C source. A string may be made of several strings concatenated and potentially spreaded on several lines. */ static int scan_parsestr ( char * &pt, SSTRING &s, READINFO &inf) { int ret = -1; s.setfrom (""); while (1){ pt = str_skip (pt); if (*pt == '\0'){ if (msgscan_read(inf,1)==-1){ break; } pt = inf.buf; }else if (*pt != '"'){ if (ret == -1){ msgscan_err ("Expected an openning \"\n"); } break; }else if (scan_parse1str(pt,s)==-1){ ret = -1; break; }else{ // One string was seen, looking for others ret = 0; } } return ret; } /* Parse a MSG_x() macro to extract the ID and the strings Return the pointer after the closing parenthese. */ static char *scan_parse( char *pt, TR_STRINGS &tr, READINFO &inf, int nbsreq) // Number of strings expected { /* #Specification: translation / message / encoding in source Messages are created in C (C++) source files and extracted with a utility (msgscan). A message is defined like this # // This is a message without translation MSG_U(msgid,"message's text") // This is a message with a proposed translation MSG_B(msgid,"message's text","Texte du message") # msgid is a unique identifier generally composed of letters and number. It must respected lexical convention for macros (as it will be #defined) */ pt = str_skip (pt); int last_nberr = err.nberr; if (pt[0] == '('){ char id[100]; if (scan_parseid(++pt,id)!=-1){ SSTRING tbs[2]; int nbs = 0; while (1){ pt = str_skip (pt); if (pt[0] == '\0'){ // We must refill the buffer if (msgscan_read (inf,1)==-1){ break; } pt = inf.buf; }else if (*pt == ')'){ pt++; break; }else if (*pt != ','){ msgscan_err ("expected comma after id\n"); break; }else if (nbs == nbsreq){ msgscan_err ("Too many strings\n"); break; }else if (scan_parsestr(++pt,tbs[nbs],inf)==0){ nbs++; }else{ break; } } if (err.nberr == last_nberr){ if (nbs != nbsreq){ msgscan_err ("Not enough strings supplied\n"); }else{ TR_STRING *t = tr.getitem(id); if (t == NULL){ t = new TR_STRING (id); tr.add (t); } if (t->was_changed()){ msgscan_err ("Duplicate translation id %s\n" ,id); const char *ori = t->getorigin(); msgscan_err (" It was defined in %s\n",ori); msgscan_err (" Previous definition was \"%s\"\n" ,t->getmsg(tblang[0])); msgscan_err (" New definition is \"%s\"\n",tbs[0].get()); }else{ t->setorigin (err.fname); for (int i=0; isettranslation (tblang[i] ,tbs[i].get()); } } } } } }else{ msgscan_err("Expected (\n"); } if (err.nberr != last_nberr){ // We skip this line to avoid falling always on the same // error. while (*pt != '\0') pt++; } return pt; } /* Locate all messages in a source file and update the dictionary. Return -1 if any error */ int scan_one (const char *fname, TR_STRINGS &tr) { int ret = -1; READINFO inf; inf.noline = 0; inf.fin = vfopen (fname,"r"); if (inf.fin == NULL){ fprintf (stderr,"Can't open file %s (%s)\n",fname ,strerror(errno)); }else{ ret = 0; while (msgscan_read (inf,0)!=-1){ char *pt = inf.buf; // Maybe more than one message on the line while (1){ pt = strstr (pt,"MSG"); if (pt != NULL){ char verb[1000]; pt = scan_copyid (pt,verb); if (strcmp(verb,"MSG_U")==0 || strcmp(verb,"P_MSG_U")==0){ msgscan_errset (fname,inf.noline); pt = scan_parse (pt,tr,inf,1); }else if (strcmp(verb,"MSG_B")==0 || strcmp(verb,"P_MSG_B")==0){ msgscan_errset (fname,inf.noline); pt = scan_parse (pt,tr,inf,2); } }else{ break; } } } } return ret; } #if 0 int scan_vone (const char *fname, TR_STRINGS &tr) { int ret = 0; char *tb[1000]; int nb = vdir_getlistp (fname,tb,1000); for (int i=0; i