99 function export_to_file_handle( $fh ) { |
99 function export_to_file_handle( $fh ) { |
100 $entries = array_filter( $this->entries, array( $this, 'is_entry_good_for_export' ) ); |
100 $entries = array_filter( $this->entries, array( $this, 'is_entry_good_for_export' ) ); |
101 ksort( $entries ); |
101 ksort( $entries ); |
102 $magic = 0x950412de; |
102 $magic = 0x950412de; |
103 $revision = 0; |
103 $revision = 0; |
104 $total = count( $entries ) + 1; // all the headers are one entry |
104 $total = count( $entries ) + 1; // All the headers are one entry. |
105 $originals_lenghts_addr = 28; |
105 $originals_lenghts_addr = 28; |
106 $translations_lenghts_addr = $originals_lenghts_addr + 8 * $total; |
106 $translations_lenghts_addr = $originals_lenghts_addr + 8 * $total; |
107 $size_of_hash = 0; |
107 $size_of_hash = 0; |
108 $hash_addr = $translations_lenghts_addr + 8 * $total; |
108 $hash_addr = $translations_lenghts_addr + 8 * $total; |
109 $current_addr = $hash_addr; |
109 $current_addr = $hash_addr; |
120 $hash_addr |
120 $hash_addr |
121 ) |
121 ) |
122 ); |
122 ); |
123 fseek( $fh, $originals_lenghts_addr ); |
123 fseek( $fh, $originals_lenghts_addr ); |
124 |
124 |
125 // headers' msgid is an empty string |
125 // Headers' msgid is an empty string. |
126 fwrite( $fh, pack( 'VV', 0, $current_addr ) ); |
126 fwrite( $fh, pack( 'VV', 0, $current_addr ) ); |
127 $current_addr++; |
127 $current_addr++; |
128 $originals_table = "\0"; |
128 $originals_table = "\0"; |
129 |
129 |
130 $reader = new POMO_Reader(); |
130 $reader = new POMO_Reader(); |
131 |
131 |
132 foreach ( $entries as $entry ) { |
132 foreach ( $entries as $entry ) { |
133 $originals_table .= $this->export_original( $entry ) . "\0"; |
133 $originals_table .= $this->export_original( $entry ) . "\0"; |
134 $length = $reader->strlen( $this->export_original( $entry ) ); |
134 $length = $reader->strlen( $this->export_original( $entry ) ); |
135 fwrite( $fh, pack( 'VV', $length, $current_addr ) ); |
135 fwrite( $fh, pack( 'VV', $length, $current_addr ) ); |
136 $current_addr += $length + 1; // account for the NULL byte after |
136 $current_addr += $length + 1; // Account for the NULL byte after. |
137 } |
137 } |
138 |
138 |
139 $exported_headers = $this->export_headers(); |
139 $exported_headers = $this->export_headers(); |
140 fwrite( $fh, pack( 'VV', $reader->strlen( $exported_headers ), $current_addr ) ); |
140 fwrite( $fh, pack( 'VV', $reader->strlen( $exported_headers ), $current_addr ) ); |
141 $current_addr += strlen( $exported_headers ) + 1; |
141 $current_addr += strlen( $exported_headers ) + 1; |
156 /** |
156 /** |
157 * @param Translation_Entry $entry |
157 * @param Translation_Entry $entry |
158 * @return string |
158 * @return string |
159 */ |
159 */ |
160 function export_original( $entry ) { |
160 function export_original( $entry ) { |
161 //TODO: warnings for control characters |
161 // TODO: Warnings for control characters. |
162 $exported = $entry->singular; |
162 $exported = $entry->singular; |
163 if ( $entry->is_plural ) { |
163 if ( $entry->is_plural ) { |
164 $exported .= "\0" . $entry->plural; |
164 $exported .= "\0" . $entry->plural; |
165 } |
165 } |
166 if ( $entry->context ) { |
166 if ( $entry->context ) { |
192 /** |
192 /** |
193 * @param int $magic |
193 * @param int $magic |
194 * @return string|false |
194 * @return string|false |
195 */ |
195 */ |
196 function get_byteorder( $magic ) { |
196 function get_byteorder( $magic ) { |
197 // The magic is 0x950412de |
197 // The magic is 0x950412de. |
198 |
198 |
199 // bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565 |
199 // bug in PHP 5.0.2, see https://savannah.nongnu.org/bugs/?func=detailitem&item_id=10565 |
200 $magic_little = (int) - 1794895138; |
200 $magic_little = (int) - 1794895138; |
201 $magic_little_64 = (int) 2500072158; |
201 $magic_little_64 = (int) 2500072158; |
202 // 0xde120495 |
202 // 0xde120495 |
219 if ( false === $endian_string ) { |
219 if ( false === $endian_string ) { |
220 return false; |
220 return false; |
221 } |
221 } |
222 $reader->setEndian( $endian_string ); |
222 $reader->setEndian( $endian_string ); |
223 |
223 |
224 $endian = ( 'big' == $endian_string ) ? 'N' : 'V'; |
224 $endian = ( 'big' === $endian_string ) ? 'N' : 'V'; |
225 |
225 |
226 $header = $reader->read( 24 ); |
226 $header = $reader->read( 24 ); |
227 if ( $reader->strlen( $header ) != 24 ) { |
227 if ( $reader->strlen( $header ) != 24 ) { |
228 return false; |
228 return false; |
229 } |
229 } |
230 |
230 |
231 // parse header |
231 // Parse header. |
232 $header = unpack( "{$endian}revision/{$endian}total/{$endian}originals_lenghts_addr/{$endian}translations_lenghts_addr/{$endian}hash_length/{$endian}hash_addr", $header ); |
232 $header = unpack( "{$endian}revision/{$endian}total/{$endian}originals_lenghts_addr/{$endian}translations_lenghts_addr/{$endian}hash_length/{$endian}hash_addr", $header ); |
233 if ( ! is_array( $header ) ) { |
233 if ( ! is_array( $header ) ) { |
234 return false; |
234 return false; |
235 } |
235 } |
236 |
236 |
237 // support revision 0 of MO format specs, only |
237 // Support revision 0 of MO format specs, only. |
238 if ( $header['revision'] != 0 ) { |
238 if ( 0 != $header['revision'] ) { |
239 return false; |
239 return false; |
240 } |
240 } |
241 |
241 |
242 // seek to data blocks |
242 // Seek to data blocks. |
243 $reader->seekto( $header['originals_lenghts_addr'] ); |
243 $reader->seekto( $header['originals_lenghts_addr'] ); |
244 |
244 |
245 // read originals' indices |
245 // Read originals' indices. |
246 $originals_lengths_length = $header['translations_lenghts_addr'] - $header['originals_lenghts_addr']; |
246 $originals_lengths_length = $header['translations_lenghts_addr'] - $header['originals_lenghts_addr']; |
247 if ( $originals_lengths_length != $header['total'] * 8 ) { |
247 if ( $originals_lengths_length != $header['total'] * 8 ) { |
248 return false; |
248 return false; |
249 } |
249 } |
250 |
250 |
251 $originals = $reader->read( $originals_lengths_length ); |
251 $originals = $reader->read( $originals_lengths_length ); |
252 if ( $reader->strlen( $originals ) != $originals_lengths_length ) { |
252 if ( $reader->strlen( $originals ) != $originals_lengths_length ) { |
253 return false; |
253 return false; |
254 } |
254 } |
255 |
255 |
256 // read translations' indices |
256 // Read translations' indices. |
257 $translations_lenghts_length = $header['hash_addr'] - $header['translations_lenghts_addr']; |
257 $translations_lenghts_length = $header['hash_addr'] - $header['translations_lenghts_addr']; |
258 if ( $translations_lenghts_length != $header['total'] * 8 ) { |
258 if ( $translations_lenghts_length != $header['total'] * 8 ) { |
259 return false; |
259 return false; |
260 } |
260 } |
261 |
261 |
262 $translations = $reader->read( $translations_lenghts_length ); |
262 $translations = $reader->read( $translations_lenghts_length ); |
263 if ( $reader->strlen( $translations ) != $translations_lenghts_length ) { |
263 if ( $reader->strlen( $translations ) != $translations_lenghts_length ) { |
264 return false; |
264 return false; |
265 } |
265 } |
266 |
266 |
267 // transform raw data into set of indices |
267 // Transform raw data into set of indices. |
268 $originals = $reader->str_split( $originals, 8 ); |
268 $originals = $reader->str_split( $originals, 8 ); |
269 $translations = $reader->str_split( $translations, 8 ); |
269 $translations = $reader->str_split( $translations, 8 ); |
270 |
270 |
271 // skip hash table |
271 // Skip hash table. |
272 $strings_addr = $header['hash_addr'] + $header['hash_length'] * 4; |
272 $strings_addr = $header['hash_addr'] + $header['hash_length'] * 4; |
273 |
273 |
274 $reader->seekto( $strings_addr ); |
274 $reader->seekto( $strings_addr ); |
275 |
275 |
276 $strings = $reader->read_all(); |
276 $strings = $reader->read_all(); |
281 $t = unpack( "{$endian}length/{$endian}pos", $translations[ $i ] ); |
281 $t = unpack( "{$endian}length/{$endian}pos", $translations[ $i ] ); |
282 if ( ! $o || ! $t ) { |
282 if ( ! $o || ! $t ) { |
283 return false; |
283 return false; |
284 } |
284 } |
285 |
285 |
286 // adjust offset due to reading strings to separate space before |
286 // Adjust offset due to reading strings to separate space before. |
287 $o['pos'] -= $strings_addr; |
287 $o['pos'] -= $strings_addr; |
288 $t['pos'] -= $strings_addr; |
288 $t['pos'] -= $strings_addr; |
289 |
289 |
290 $original = $reader->substr( $strings, $o['pos'], $o['length'] ); |
290 $original = $reader->substr( $strings, $o['pos'], $o['length'] ); |
291 $translation = $reader->substr( $strings, $t['pos'], $t['length'] ); |
291 $translation = $reader->substr( $strings, $t['pos'], $t['length'] ); |
317 $parts = explode( "\4", $original ); |
317 $parts = explode( "\4", $original ); |
318 if ( isset( $parts[1] ) ) { |
318 if ( isset( $parts[1] ) ) { |
319 $original = $parts[1]; |
319 $original = $parts[1]; |
320 $entry->context = $parts[0]; |
320 $entry->context = $parts[0]; |
321 } |
321 } |
322 // look for plural original |
322 // Look for plural original. |
323 $parts = explode( "\0", $original ); |
323 $parts = explode( "\0", $original ); |
324 $entry->singular = $parts[0]; |
324 $entry->singular = $parts[0]; |
325 if ( isset( $parts[1] ) ) { |
325 if ( isset( $parts[1] ) ) { |
326 $entry->is_plural = true; |
326 $entry->is_plural = true; |
327 $entry->plural = $parts[1]; |
327 $entry->plural = $parts[1]; |
328 } |
328 } |
329 // plural translations are also separated by \0 |
329 // Plural translations are also separated by \0. |
330 $entry->translations = explode( "\0", $translation ); |
330 $entry->translations = explode( "\0", $translation ); |
331 return $entry; |
331 return $entry; |
332 } |
332 } |
333 |
333 |
334 /** |
334 /** |