123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399(**************************************************************************)(* ocaml-gettext: a library to translate messages *)(* *)(* Copyright (C) 2003-2008 Sylvain Le Gall <sylvain@le-gall.net> *)(* *)(* This library is free software; you can redistribute it and/or *)(* modify it under the terms of the GNU Lesser General Public *)(* License as published by the Free Software Foundation; either *)(* version 2.1 of the License, or (at your option) any later version; *)(* with the OCaml static compilation exception. *)(* *)(* This library is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *)(* Lesser General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public *)(* License along with this library; if not, write to the Free Software *)(* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *)(* USA *)(**************************************************************************)openGettextUtils(**
@author Sylvain Le Gall
*)openGettextTypesopenGettextMo_int32letmo_sig_be=int32_of_byte(0x95,0x04,0x12,0xde)letmo_sig_le=int32_of_byte(0xde,0x12,0x04,0x95)letcheck_mo_headerchnhdr=letoffset_min=Int32.of_int28inletoffset_max=Int32.of_int(in_channel_lengthchn)inletrange_offsetstart_bound=letend_bound=Int32.addstart_bound(Int32.mul(Int32.predhdr.number_of_strings)(Int32.of_int8))in((offset_min,offset_max),(start_bound,end_bound))inletval_in_range(start_bound,end_bound)value=Int32.comparestart_boundvalue<=0&&Int32.comparevalueend_bound<=0in(* check_* function return true in case of problem *)letcheck_overlapstart_bound1start_bound2=let_,(_,end_bound1)=range_offsetstart_bound1inlet_,(_,end_bound2)=range_offsetstart_bound2inval_in_range(start_bound1,end_bound1)start_bound2||val_in_range(start_bound1,end_bound1)end_bound2||val_in_range(start_bound2,end_bound2)start_bound1||val_in_range(start_bound2,end_bound2)end_bound1inletcheck_range_offsetstart_bound=letfile,(start_bound,end_bound)=range_offsetstart_boundinnot(val_in_rangefilestart_bound&&val_in_rangefileend_bound)inifInt32.comparehdr.number_of_stringsInt32.zero<0thenraiseMoInvalidHeaderNegativeStringselseifcheck_range_offsethdr.offset_table_stringsthenraise(MoInvalidHeaderTableStringOutOfBound(fst(range_offsethdr.offset_table_strings),snd(range_offsethdr.offset_table_strings)))elseifcheck_range_offsethdr.offset_table_translationthenraise(MoInvalidHeaderTableTranslationOutOfBound(fst(range_offsethdr.offset_table_translation),snd(range_offsethdr.offset_table_translation)))elseifcheck_overlaphdr.offset_table_translationhdr.offset_table_stringsthenraise(MoInvalidHeaderTableTranslationStringOverlap(snd(range_offsethdr.offset_table_translation),snd(range_offsethdr.offset_table_strings)))(* We don't care of hashing table, since we don't use it *)elsehdrletinput_mo_headerchn=letendianess=letmagic_number=seek_inchn0;input_int32chnBigEndianinifmagic_number=mo_sig_bethenBigEndianelseifmagic_number=mo_sig_lethenLittleEndianelseraiseMoInvalidFileinletseek_and_inputx=seek_inchnx;input_int32chnendianessincheck_mo_headerchn{endianess;file_format_revision=seek_and_input4;number_of_strings=seek_and_input8;offset_table_strings=seek_and_input12;offset_table_translation=seek_and_input16;size_of_hashing_table=seek_and_input20;offset_of_hashing_table=seek_and_input24;}letoutput_mo_headerchnhdr=letoutput=output_int32chnhdr.endianessin(* magic_number : be is the native way to
* specify it, it will be translated through
* the output_int32*)outputmo_sig_be;outputhdr.file_format_revision;outputhdr.number_of_strings;outputhdr.offset_table_strings;outputhdr.offset_table_translation;outputhdr.size_of_hashing_table;outputhdr.offset_of_hashing_tableletstring_of_mo_headermo_header=letbuff=Buffer.create256inPrintf.bprintfbuff"File format revision : %ld\n"mo_header.file_format_revision;Printf.bprintfbuff"Number of string : %ld\n"mo_header.number_of_strings;Printf.bprintfbuff"Offset of table with original strings : %lx\n"mo_header.offset_table_strings;Printf.bprintfbuff"Offset of table with translation strings : %lx\n"mo_header.offset_table_translation;Printf.bprintfbuff"Size of hashing table : %lx\n"mo_header.size_of_hashing_table;Printf.bprintfbuff"Offset of hashing table : %lx\n"mo_header.offset_of_hashing_table;Buffer.contentsbuffletinput_mo_untranslated_failsafechnmo_headernumber=ifnumber<Int32.to_intmo_header.number_of_stringsthenletoffset_pair=Int32.to_intmo_header.offset_table_strings+(number*8)inletstr=tryseek_inchnoffset_pair;input_int32_pair_stringchnmo_header.endianesswithEnd_of_file->raise(MoInvalidStringOutOfBound(in_channel_lengthchn,offset_pair))insplit_pluralstrelseraise(MoInvalidStringOutOfBound(Int32.to_intmo_header.number_of_strings,number))letinput_mo_translated_failsafechnmo_headernumber=ifnumber<Int32.to_intmo_header.number_of_stringsthenletoffset_pair=Int32.to_intmo_header.offset_table_translation+(number*8)inletstr=tryseek_inchnoffset_pair;input_int32_pair_stringchnmo_header.endianesswithEnd_of_file->raise(MoInvalidTranslationOutOfBound(in_channel_lengthchn,offset_pair))insplit_pluralstrelseraise(MoInvalidStringOutOfBound(Int32.to_intmo_header.number_of_strings,number))letinput_mo_translationfailsafechnmo_headernumber=letuntranslated=input_mo_untranslatedfailsafechnmo_headernumberinlettranslated=input_mo_translatedfailsafechnmo_headernumberinmatchuntranslatedwith|[id]->Singular(id,String.concat"\000"translated)|[id;id_plural]->Plural(id,id_plural,translated)|id::id_plural::tl->fail_or_continuefailsafe(MoJunk(id,tl))(Plural(id,id_plural,translated))|[]->fail_or_continuefailsafeMoEmptyEntry(Singular("",""))letget_translated_valuefailsafetranslationplural_number=match(translation,plural_number)with|Singular(_,str),0->str|Singular(_,str),x->fail_or_continuefailsafe(MoInvalidTranslationSingular(str,x))str|Plural(str,str_plural,[]),x->ifx=0thenstrelsestr_plural|Plural(_,_,lst),xwhenx<List.lengthlst->List.nthlstx|Plural(_,_,lst),x->fail_or_continuefailsafe(MoInvalidTranslationPlural(lst,x))List.nthlst0letgermanic_plural(* The germanic default *)n=ifn=1then1else0letinput_mo_informationsfailsafechnmo_header=(* La position de "" est forcément 0 *)letempty_translation=get_translated_valuefailsafe(input_mo_translationfailsafechnmo_header0)0inletfield_value=letlexbuf=Lexing.from_stringempty_translationintryGettextMo_parser.mainGettextMo_lexer.token_field_namelexbufwithParsing.Parse_error|Failure_->fail_or_continuefailsafe(MoInvalidOptions(lexbuf,empty_translation))[]inletnplurals,fun_plural_forms=tryletfield_plural_forms=List.assoc"Plural-Forms"field_valueinletlexbuf=Lexing.from_stringfield_plural_formsintryGettextMo_parser.plural_formsGettextMo_lexer.token_field_plural_valuelexbufwithParsing.Parse_error|Failure_->fail_or_continuefailsafe(MoInvalidPlurals(lexbuf,field_plural_forms))(2,germanic_plural)withNot_found->(2,germanic_plural)inlet_content_type,content_type_charset=letgettext_content=("text/plain",GettextConfig.default_codeset)intryletfield_content_type=List.assoc"Content-Type"field_valueinletlexbuf=Lexing.from_stringfield_content_typeintryGettextMo_parser.content_typeGettextMo_lexer.token_field_content_typelexbufwithParsing.Parse_error|Failure_->fail_or_continuefailsafe(MoInvalidContentType(lexbuf,field_content_type))gettext_contentwithNot_found->gettext_contentinletextract_field_stringname=trySome(List.assocnamefield_value)withNot_found->Nonein{project_id_version=extract_field_string"Project-Id-Version";report_msgid_bugs_to=extract_field_string"Report-Msgid-Bugs-To";pot_creation_date=extract_field_string"POT-Creation-Date";po_revision_date=extract_field_string"PO-Revision-Date";last_translator=extract_field_string"Last-Translator";language_tream=extract_field_string"Language-Team";mime_version=extract_field_string"MIME-Version";content_type=extract_field_string"Content-Type";content_transfer_encoding=extract_field_string"Content-Transfer-Encoding";plural_forms=extract_field_string"Plural-Forms";content_type_charset;nplurals;fun_plural_forms;}letstring_of_mo_informations?(compute_plurals=(0,3))mo_translation=letbuff=Buffer.create1024inletp=Printf.bprintfinletextract_stringx=matchxwithSomes->s|None->""inpbuff"Project-Id-Version : %s\n"(extract_stringmo_translation.project_id_version);pbuff"Report-Msgid-Bugs-To : %s\n"(extract_stringmo_translation.report_msgid_bugs_to);pbuff"POT-Creation-Date : %s\n"(extract_stringmo_translation.pot_creation_date);pbuff"PO-Revision-Date : %s\n"(extract_stringmo_translation.po_revision_date);pbuff"Last-Translator : %s\n"(extract_stringmo_translation.last_translator);pbuff"Language-Team : %s\n"(extract_stringmo_translation.language_tream);pbuff"MIME-Version : %s\n"(extract_stringmo_translation.mime_version);pbuff"Content-Type : %s\n"(extract_stringmo_translation.content_type);pbuff"Plurals-Forms : %s\n"(extract_stringmo_translation.plural_forms);pbuff"Content-Transfer-Encoding : %s\n"(extract_stringmo_translation.content_transfer_encoding);pbuff"Content-Type-Charset : %s\n"mo_translation.content_type_charset;pbuff"NPlurals : %d\n"mo_translation.nplurals;pbuff"Fun plural : ";(leta,b=compute_pluralsinfori=atobdopbuff"%d -> %d ; "i(mo_translation.fun_plural_formsi)done);pbuff"\n";Buffer.contentsbuffletoutput_mo?(endianess=LittleEndian)chnlst=(* There could have potential issue with alignment, but it seems to be fixed
* at 1 in gettext-0.14.1/gettext-tools/configure.ac, so there is no probleme
* *)letnull_terminatedlst=List.map(funstr->str^"\000")lstinletcompute_tablestart_poslst=letcompute_lengthlst=List.mapString.lengthlstinletcompute_offset(current_pos,lst_pos)length=(* Remove 1 since we have NULL terminated strings *)(current_pos+length,(length-1,current_pos)::lst_pos)inletfinal_pos,lst_rev=List.fold_leftcompute_offset(start_pos,[])(compute_lengthlst)in(final_pos,List.revlst_rev)inletno_empty_lst=(* Avoid using empty translated string *)List.filter(function|Singular(_,"")->false|Plural(_,_,lst)whenString.concat""lst=""->false|_->true)lstinletsorted_lst=letcompare_entryentry1entry2=letvalue_of_entryentry=matchentrywithSingular(id,_)->id|Plural(id,_,_)->idinString.compare(value_of_entryentry1)(value_of_entryentry2)inList.sortcompare_entryno_empty_lstinletuntranslated=letto_stringentry=matchentrywith|Singular(id,_)->id|Plural(id,id_plural,_)->id^"\000"^id_pluralinnull_terminated(List.mapto_stringsorted_lst)inlettranslated=letto_stringentry=matchentrywith|Singular(_,str)->str|Plural(_,_,lst)->String.concat"\000"lstinnull_terminated(List.mapto_stringsorted_lst)inletgN=List.lengthsorted_lstinletgO=28(* Size of the header *)inletgT=gO+(8*gN)inletgS=0(* Hashtable is not implemented, since algorithm is not public -- documented *)inletgH=gT+(8*gN)inletfinal_untranslated,untranslated_table=compute_table(gH+(gS*4))untranslatedinlet_,translated_table=compute_tablefinal_untranslatedtranslatedinletheader={endianess;file_format_revision=Int32.zero;number_of_strings=Int32.of_intgN;offset_table_strings=Int32.of_intgO;offset_table_translation=Int32.of_intgT;size_of_hashing_table=Int32.of_intgS;offset_of_hashing_table=Int32.of_intgH;}inoutput_mo_headerchnheader;List.iter(List.iter(fun(a,b)->output_int32_pairchnendianess(Int32.of_inta,Int32.of_intb)))[untranslated_table;translated_table];List.iter(output_stringchn)untranslated;List.iter(output_stringchn)translatedletfold_mofailsafefinitfl_mo=letchn=open_in_binfl_moinletres=try(* Processing of the file *)letmo_header=input_mo_headerchninletinformations=input_mo_informationsfailsafechnmo_headerinletfun_plural_forms=informations.GettextTypes.fun_plural_formsinletrecfold_mo_auxaccui=ifi<Int32.to_intmo_header.number_of_stringsthenletnew_translation=input_mo_translationfailsafechnmo_headeriinletnew_accu=fnew_translationaccuinfold_mo_auxnew_accu(i+1)elseaccuinlettranslations=fold_mo_auxinit0in(translations,fun_plural_forms)withSys_error_->fail_or_continuefailsafe(MoCannotOpenFilefl_mo)(init,germanic_plural)inclose_inchn;res