blob: d55303b99b5727dd94bbcf8899655366d10a06ac [file] [log] [blame]
Josh Coalsoncae58732002-05-04 17:30:32 +00001/* libFLAC - Free Lossless Audio Codec library
2 * Copyright (C) 2001,2002 Josh Coalson
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 */
19
20#include <errno.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24
25#if defined _MSC_VER || defined __MINGW32__
26#include <sys/utime.h> /* for utime() */
27#include <io.h> /* for chmod() */
28#else
29#include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */
30#include <utime.h> /* for utime() */
31#include <unistd.h> /* for chown(), unlink() */
32#endif
33#include <sys/stat.h> /* for stat(), maybe chmod() */
34
35#include "FLAC/assert.h"
36#include "FLAC/metadata.h"
37#include "FLAC/file_decoder.h"
38
39#ifdef max
40#undef max
41#endif
42#define max(a,b) ((a)>(b)?(a):(b))
43#ifdef min
44#undef min
45#endif
46#define min(a,b) ((a)<(b)?(a):(b))
47
48
49/****************************************************************************
50 *
51 * Local function declarations
52 *
53 ***************************************************************************/
54
55/* this one should probably not go in the public interface: */
56static void FLAC__metadata_object_delete_data_(FLAC__StreamMetaData *object);
57
58static void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes);
59static void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes);
60static void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes);
61static FLAC__uint32 unpack_uint32_(FLAC__byte *b, unsigned bytes);
62static FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, unsigned bytes);
63static FLAC__uint64 unpack_uint64_(FLAC__byte *b, unsigned bytes);
64
65static FLAC__bool read_metadata_block_header_(FLAC__MetaData_SimpleIterator *iterator);
66static FLAC__bool read_metadata_block_data_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block);
67static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_streaminfo_(FILE *file, FLAC__StreamMetaData_StreamInfo *block);
68static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_padding_(FILE *file, FLAC__StreamMetaData_Padding *block, unsigned block_length);
69static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_application_(FILE *file, FLAC__StreamMetaData_Application *block, unsigned block_length);
70static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_seektable_(FILE *file, FLAC__StreamMetaData_SeekTable *block, unsigned block_length);
71static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_(FILE *file, FLAC__StreamMetaData_VorbisComment_Entry *entry);
72static FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_(FILE *file, FLAC__StreamMetaData_VorbisComment *block);
73
74static FLAC__bool write_metadata_block_header_(FILE *file, FLAC__MetaData_SimpleIteratorStatus *status, const FLAC__StreamMetaData *block);
75static FLAC__bool write_metadata_block_data_(FILE *file, FLAC__MetaData_SimpleIteratorStatus *status, const FLAC__StreamMetaData *block);
76static FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_streaminfo_(FILE *file, const FLAC__StreamMetaData_StreamInfo *block);
77static FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_padding_(FILE *file, const FLAC__StreamMetaData_Padding *block, unsigned block_length);
78static FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_application_(FILE *file, const FLAC__StreamMetaData_Application *block, unsigned block_length);
79static FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_seektable_(FILE *file, const FLAC__StreamMetaData_SeekTable *block);
80static FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_vorbis_comment_(FILE *file, const FLAC__StreamMetaData_VorbisComment *block);
81static FLAC__bool write_metadata_block_stationary_(FLAC__MetaData_SimpleIterator *iterator, const FLAC__StreamMetaData *block);
82static FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, unsigned padding_length, FLAC__bool padding_is_last);
83static FLAC__bool rewrite_whole_file_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, FLAC__bool append);
84
85static FLAC__bool chain_rewrite_chain_(FLAC__MetaData_Chain *chain);
86static FLAC__bool chain_rewrite_file_(FLAC__MetaData_Chain *chain, const char *tempfile_path_prefix);
87
88static void simple_iterator_push_(FLAC__MetaData_SimpleIterator *iterator);
89static FLAC__bool simple_iterator_pop_(FLAC__MetaData_SimpleIterator *iterator);
90
91/* return 0 if OK, 1 if read error, 2 if not a FLAC file */
92static unsigned seek_to_first_metadata_block_(FILE *f);
93
94static FLAC__bool simple_iterator_copy_file_prefix_(FLAC__MetaData_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append);
Josh Coalson6b02a752002-05-10 06:42:02 +000095static FLAC__bool simple_iterator_copy_file_postfix_(FLAC__MetaData_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, long fixup_is_last_flag_offset, FLAC__bool backup);
Josh Coalsoncae58732002-05-04 17:30:32 +000096
97static FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, unsigned bytes/*@@@ 4G limit*/, FLAC__MetaData_SimpleIteratorStatus *status);
98static FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__MetaData_SimpleIteratorStatus *status);
99
100static FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__MetaData_SimpleIteratorStatus *status);
101static FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__MetaData_SimpleIteratorStatus *status);
102static void cleanup_tempfile_(FILE **tempfile, char **tempfilename);
103
104static FLAC__bool get_file_stats_(const char *filename, struct stat *stats);
105static void set_file_stats_(const char *filename, struct stat *stats);
106
107static FLAC__MetaData_ChainStatus get_equivalent_status_(FLAC__MetaData_SimpleIteratorStatus status);
108
109
110/****************************************************************************
111 *
112 * Level 0 implementation
113 *
114 ***************************************************************************/
115
116static FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data);
117static void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data);
118static void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
119
120FLAC__bool FLAC__metadata_get_streaminfo(const char *filename, FLAC__StreamMetaData_StreamInfo *streaminfo)
121{
122 FLAC__FileDecoder *decoder = FLAC__file_decoder_new();
123
124 if(0 == decoder)
125 return false;
126
127 FLAC__file_decoder_set_md5_checking(decoder, false);
128 FLAC__file_decoder_set_filename(decoder, filename);
129 FLAC__file_decoder_set_write_callback(decoder, write_callback_);
130 FLAC__file_decoder_set_metadata_callback(decoder, metadata_callback_);
131 FLAC__file_decoder_set_error_callback(decoder, error_callback_);
132 FLAC__file_decoder_set_client_data(decoder, &streaminfo);
133
134 if(FLAC__file_decoder_init(decoder) != FLAC__FILE_DECODER_OK)
135 return false;
136
137 if(!FLAC__file_decoder_process_metadata(decoder))
138 return false;
139
Josh Coalson51ee4652002-05-09 05:46:23 +0000140 FLAC__file_decoder_finish(decoder);
Josh Coalsoncae58732002-05-04 17:30:32 +0000141 FLAC__file_decoder_delete(decoder);
142
143 return 0 != streaminfo; /* the metadata_callback_() will set streaminfo to 0 on an error */
144}
145
146FLAC__StreamDecoderWriteStatus write_callback_(const FLAC__FileDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *buffer[], void *client_data)
147{
148 (void)decoder, (void)frame, (void)buffer, (void)client_data;
149
150 return FLAC__STREAM_DECODER_WRITE_CONTINUE;
151}
152
153void metadata_callback_(const FLAC__FileDecoder *decoder, const FLAC__StreamMetaData *metadata, void *client_data)
154{
155 FLAC__StreamMetaData_StreamInfo **streaminfo = (FLAC__StreamMetaData_StreamInfo **)client_data;
156 (void)decoder;
157
158 if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO && 0 != *streaminfo)
159 **streaminfo = metadata->data.stream_info;
160}
161
162void error_callback_(const FLAC__FileDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
163{
164 FLAC__StreamMetaData_StreamInfo **streaminfo = (FLAC__StreamMetaData_StreamInfo **)client_data;
165 (void)decoder;
166
167 if(status != FLAC__STREAM_DECODER_ERROR_LOST_SYNC)
168 *streaminfo = 0;
169}
170
171
172/****************************************************************************
173 *
174 * Level 1 implementation
175 *
176 ***************************************************************************/
177
178#define SIMPLE_ITERATOR_MAX_PUSH_DEPTH (1+4)
179/* 1 for initial offset, +4 for our own personal use */
180
181struct FLAC__MetaData_SimpleIterator {
182 FILE *file;
183 char *filename, *tempfile_path_prefix;
184 struct stat stats;
185 FLAC__bool has_stats;
186 FLAC__bool is_writable;
187 FLAC__MetaData_SimpleIteratorStatus status;
188 /*@@@ 2G limits here because of the offset type: */
189 long offset[SIMPLE_ITERATOR_MAX_PUSH_DEPTH];
190 long first_offset; /* this is the offset to the STREAMINFO block */
191 unsigned depth;
192 /* this is the metadata block header of the current block we are pointing to: */
193 FLAC__bool is_last;
194 FLAC__MetaDataType type;
195 unsigned length;
196};
197
198const char *FLAC__MetaData_SimpleIteratorStatusString[] = {
199 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK",
200 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT",
201 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE",
202 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE",
203 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE",
Josh Coalsona7b673c2002-05-25 02:13:38 +0000204 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA",
Josh Coalsoncae58732002-05-04 17:30:32 +0000205 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR",
206 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR",
207 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR",
208 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR",
209 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR",
210 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR",
211 "FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR"
212};
213
214
215FLAC__MetaData_SimpleIterator *FLAC__metadata_simple_iterator_new()
216{
217 FLAC__MetaData_SimpleIterator *iterator = malloc(sizeof(FLAC__MetaData_SimpleIterator));
218
219 if(0 != iterator) {
220 iterator->file = 0;
221 iterator->filename = 0;
222 iterator->tempfile_path_prefix = 0;
223 iterator->has_stats = false;
224 iterator->is_writable = false;
225 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
226 iterator->first_offset = iterator->offset[0] = -1;
227 iterator->depth = 0;
228 }
229
230 return iterator;
231}
232
233static void simple_iterator_free_guts_(FLAC__MetaData_SimpleIterator *iterator)
234{
235 FLAC__ASSERT(0 != iterator);
236
237 if(0 != iterator->file) {
238 fclose(iterator->file);
239 iterator->file = 0;
240 if(iterator->has_stats)
241 set_file_stats_(iterator->filename, &iterator->stats);
242 }
243 if(0 != iterator->filename) {
244 free(iterator->filename);
245 iterator->filename = 0;
246 }
247 if(0 != iterator->tempfile_path_prefix) {
248 free(iterator->tempfile_path_prefix);
249 iterator->tempfile_path_prefix = 0;
250 }
251}
252
253void FLAC__metadata_simple_iterator_delete(FLAC__MetaData_SimpleIterator *iterator)
254{
255 FLAC__ASSERT(0 != iterator);
256
257 simple_iterator_free_guts_(iterator);
258 free(iterator);
259}
260
261FLAC__MetaData_SimpleIteratorStatus FLAC__metadata_simple_iterator_status(FLAC__MetaData_SimpleIterator *iterator)
262{
263 FLAC__MetaData_SimpleIteratorStatus status;
264
265 FLAC__ASSERT(0 != iterator);
266
267 status = iterator->status;
268 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
269 return status;
270}
271
272static FLAC__bool simple_iterator_prime_input_(FLAC__MetaData_SimpleIterator *iterator)
273{
274 unsigned ret;
275
276 FLAC__ASSERT(0 != iterator);
277
278 iterator->is_writable = false;
279
280 if(0 == (iterator->file = fopen(iterator->filename, "r+b"))) {
281 if(errno == EACCES) {
282 if(0 == (iterator->file = fopen(iterator->filename, "rb"))) {
283 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE;
284 return false;
285 }
286 }
287 else {
288 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE;
289 return false;
290 }
291 }
292 else {
293 iterator->is_writable = true;
294 }
295
296 ret = seek_to_first_metadata_block_(iterator->file);
297 switch(ret) {
298 case 0:
299 iterator->depth = 0;
300 iterator->first_offset = iterator->offset[iterator->depth] = ftell(iterator->file);
301 return read_metadata_block_header_(iterator);
302 case 1:
303 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
304 return false;
305 case 2:
306 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE;
307 return false;
308 default:
309 FLAC__ASSERT(0);
310 return false;
311 }
312}
313
314#if 0
315@@@ If we decide to finish implementing this, put this comment back in metadata.h
316/*
317 * The 'tempfile_path_prefix' allows you to specify a directory where
318 * tempfiles should go. Remember that if your metadata edits cause the
319 * FLAC file to grow, the entire file will have to be rewritten. If
320 * 'tempfile_path_prefix' is NULL, the temp file will be written in the
321 * same directory as the original FLAC file. This makes replacing the
322 * original with the tempfile fast but requires extra space in the same
323 * partition for the tempfile. If space is a problem, you can pass a
324 * directory name belonging to a different partition in
325 * 'tempfile_path_prefix'. Note that you should use the forward slash
326 * '/' as the directory separator. A trailing slash is not needed; it
327 * will be added automatically.
328 */
329FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__MetaData_SimpleIterator *iterator, const char *filename, FLAC__bool preserve_file_stats, const char *tempfile_path_prefix);
330#endif
331
332FLAC__bool FLAC__metadata_simple_iterator_init(FLAC__MetaData_SimpleIterator *iterator, const char *filename, FLAC__bool preserve_file_stats)
333{
Josh Coalson72943092002-05-22 05:35:43 +0000334 const char *tempfile_path_prefix = 0; /*@@@ search for comments near 'rename(...)' for what it will take to finish implementing this */
Josh Coalsoncae58732002-05-04 17:30:32 +0000335
336 FLAC__ASSERT(0 != iterator);
337 FLAC__ASSERT(0 != filename);
338
339 simple_iterator_free_guts_(iterator);
340
341 if(preserve_file_stats)
342 iterator->has_stats = get_file_stats_(filename, &iterator->stats);
343
344 if(0 == (iterator->filename = strdup(filename))) {
345 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
346 return false;
347 }
348 if(0 != tempfile_path_prefix && 0 == (iterator->tempfile_path_prefix = strdup(tempfile_path_prefix))) {
349 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
350 return false;
351 }
352
353 return simple_iterator_prime_input_(iterator);
354}
355
356FLAC__bool FLAC__metadata_simple_iterator_is_writable(const FLAC__MetaData_SimpleIterator *iterator)
357{
358 FLAC__ASSERT(0 != iterator);
359 FLAC__ASSERT(0 != iterator->file);
360
361 return iterator->is_writable;
362}
363
364FLAC__bool FLAC__metadata_simple_iterator_next(FLAC__MetaData_SimpleIterator *iterator)
365{
366 FLAC__ASSERT(0 != iterator);
367 FLAC__ASSERT(0 != iterator->file);
368
369 if(iterator->is_last)
370 return false;
371
372 if(0 != fseek(iterator->file, iterator->length, SEEK_CUR)) {
373 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
374 return false;
375 }
376
377 iterator->offset[iterator->depth] = ftell(iterator->file);
378
379 return read_metadata_block_header_(iterator);
380}
381
382FLAC__bool FLAC__metadata_simple_iterator_prev(FLAC__MetaData_SimpleIterator *iterator)
383{
384 long this_offset;
385
386 FLAC__ASSERT(0 != iterator);
387 FLAC__ASSERT(0 != iterator->file);
388
389 if(iterator->offset[iterator->depth] == iterator->first_offset)
390 return false;
391
392 if(0 != fseek(iterator->file, iterator->first_offset, SEEK_SET)) {
393 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
394 return false;
395 }
396 this_offset = iterator->first_offset;
397 if(!read_metadata_block_header_(iterator))
398 return false;
399
400 /* we ignore any error from ftell() and catch it in fseek() */
401 while(ftell(iterator->file) + (long)iterator->length < iterator->offset[iterator->depth]) {
402 if(0 != fseek(iterator->file, iterator->length, SEEK_CUR)) {
403 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
404 return false;
405 }
406 this_offset = ftell(iterator->file);
407 if(!read_metadata_block_header_(iterator))
408 return false;
409 }
410
411 iterator->offset[iterator->depth] = this_offset;
412
413 return true;
414}
415
416FLAC__MetaDataType FLAC__metadata_simple_iterator_get_block_type(const FLAC__MetaData_SimpleIterator *iterator)
417{
418 FLAC__ASSERT(0 != iterator);
419 FLAC__ASSERT(0 != iterator->file);
420
421 return iterator->type;
422}
423
424FLAC__StreamMetaData *FLAC__metadata_simple_iterator_get_block(FLAC__MetaData_SimpleIterator *iterator)
425{
426 FLAC__StreamMetaData *block = FLAC__metadata_object_new(iterator->type);
427
428 FLAC__ASSERT(0 != iterator);
429 FLAC__ASSERT(0 != iterator->file);
430
431 if(0 != block) {
432 block->is_last = iterator->is_last;
433 block->length = iterator->length;
434
435 if(!read_metadata_block_data_(iterator, block)) {
436 FLAC__metadata_object_delete(block);
437 return 0;
438 }
439
440 /* back up to the beginning of the block data to stay consistent */
Josh Coalsoneb643a22002-05-20 05:58:50 +0000441 if(0 != fseek(iterator->file, iterator->offset[iterator->depth] + FLAC__STREAM_METADATA_HEADER_LENGTH, SEEK_SET)) {
Josh Coalsoncae58732002-05-04 17:30:32 +0000442 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
443 FLAC__metadata_object_delete(block);
444 return 0;
445 }
446 }
447 else
448 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
449
450 return block;
451}
452
453FLAC__bool FLAC__metadata_simple_iterator_set_block(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, FLAC__bool use_padding)
454{
Josh Coalson51ee4652002-05-09 05:46:23 +0000455 FLAC__ASSERT_DECLARATION(long debug_target_offset = iterator->offset[iterator->depth];)
456 FLAC__bool ret;
457
Josh Coalsoncae58732002-05-04 17:30:32 +0000458 FLAC__ASSERT(0 != iterator);
459 FLAC__ASSERT(0 != iterator->file);
460
461 if(!iterator->is_writable) {
462 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE;
463 return false;
464 }
465
466 if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO || block->type == FLAC__METADATA_TYPE_STREAMINFO) {
467 if(iterator->type != block->type) {
468 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT;
469 return false;
470 }
471 }
472
473 block->is_last = iterator->is_last;
Josh Coalsoncae58732002-05-04 17:30:32 +0000474
475 if(iterator->length == block->length)
476 return write_metadata_block_stationary_(iterator, block);
477 else if(iterator->length > block->length) {
Josh Coalsoneb643a22002-05-20 05:58:50 +0000478 if(use_padding && iterator->length >= FLAC__STREAM_METADATA_HEADER_LENGTH + block->length) {
479 ret = write_metadata_block_stationary_with_padding_(iterator, block, iterator->length - FLAC__STREAM_METADATA_HEADER_LENGTH - block->length, block->is_last);
Josh Coalson51ee4652002-05-09 05:46:23 +0000480 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
Josh Coalson72943092002-05-22 05:35:43 +0000481 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + (long)FLAC__STREAM_METADATA_HEADER_LENGTH);
Josh Coalson51ee4652002-05-09 05:46:23 +0000482 return ret;
483 }
484 else {
485 ret = rewrite_whole_file_(iterator, block, /*append=*/false);
Josh Coalson51ee4652002-05-09 05:46:23 +0000486 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
Josh Coalson72943092002-05-22 05:35:43 +0000487 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + (long)FLAC__STREAM_METADATA_HEADER_LENGTH);
Josh Coalson51ee4652002-05-09 05:46:23 +0000488 return ret;
489 }
Josh Coalsoncae58732002-05-04 17:30:32 +0000490 }
491 else /* iterator->length < block->length */ {
492 unsigned padding_leftover = 0;
493 FLAC__bool padding_is_last = false;
494 if(use_padding) {
495 /* first see if we can even use padding */
496 if(iterator->is_last) {
497 use_padding = false;
498 }
499 else {
Josh Coalson51ee4652002-05-09 05:46:23 +0000500 const unsigned extra_padding_bytes_required = block->length - iterator->length;
Josh Coalsoncae58732002-05-04 17:30:32 +0000501 simple_iterator_push_(iterator);
502 if(!FLAC__metadata_simple_iterator_next(iterator)) {
503 (void)simple_iterator_pop_(iterator);
504 return false;
505 }
506 if(iterator->type != FLAC__METADATA_TYPE_PADDING) {
507 use_padding = false;
508 }
509 else {
Josh Coalsoneb643a22002-05-20 05:58:50 +0000510 if(FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length == extra_padding_bytes_required) {
Josh Coalsoncae58732002-05-04 17:30:32 +0000511 padding_leftover = 0;
512 block->is_last = iterator->is_last;
513 }
Josh Coalson51ee4652002-05-09 05:46:23 +0000514 else if(iterator->length < extra_padding_bytes_required)
Josh Coalsoncae58732002-05-04 17:30:32 +0000515 use_padding = false;
516 else {
Josh Coalsoneb643a22002-05-20 05:58:50 +0000517 padding_leftover = FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length - extra_padding_bytes_required;
Josh Coalsoncae58732002-05-04 17:30:32 +0000518 padding_is_last = iterator->is_last;
519 block->is_last = false;
520 }
521 }
522 if(!simple_iterator_pop_(iterator))
523 return false;
524 }
525 }
526 if(use_padding) {
Josh Coalson51ee4652002-05-09 05:46:23 +0000527 if(padding_leftover == 0) {
528 ret = write_metadata_block_stationary_(iterator, block);
Josh Coalson51ee4652002-05-09 05:46:23 +0000529 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
Josh Coalson72943092002-05-22 05:35:43 +0000530 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + (long)FLAC__STREAM_METADATA_HEADER_LENGTH);
Josh Coalson51ee4652002-05-09 05:46:23 +0000531 return ret;
532 }
533 else {
Josh Coalsoneb643a22002-05-20 05:58:50 +0000534 FLAC__ASSERT(padding_leftover >= FLAC__STREAM_METADATA_HEADER_LENGTH);
535 ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last);
Josh Coalson51ee4652002-05-09 05:46:23 +0000536 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
Josh Coalson72943092002-05-22 05:35:43 +0000537 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + (long)FLAC__STREAM_METADATA_HEADER_LENGTH);
Josh Coalson51ee4652002-05-09 05:46:23 +0000538 return ret;
539 }
Josh Coalsoncae58732002-05-04 17:30:32 +0000540 }
541 else {
Josh Coalson51ee4652002-05-09 05:46:23 +0000542 ret = rewrite_whole_file_(iterator, block, /*append=*/false);
Josh Coalson51ee4652002-05-09 05:46:23 +0000543 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
Josh Coalson72943092002-05-22 05:35:43 +0000544 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + (long)FLAC__STREAM_METADATA_HEADER_LENGTH);
Josh Coalson51ee4652002-05-09 05:46:23 +0000545 return ret;
Josh Coalsoncae58732002-05-04 17:30:32 +0000546 }
547 }
548}
549
550FLAC__bool FLAC__metadata_simple_iterator_insert_block_after(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, FLAC__bool use_padding)
551{
552 unsigned padding_leftover = 0;
553 FLAC__bool padding_is_last = false;
554
Josh Coalsoneb643a22002-05-20 05:58:50 +0000555 FLAC__ASSERT_DECLARATION(long debug_target_offset = iterator->offset[iterator->depth] + FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length;)
Josh Coalson51ee4652002-05-09 05:46:23 +0000556 FLAC__bool ret;
557
Josh Coalsoncae58732002-05-04 17:30:32 +0000558 FLAC__ASSERT(0 != iterator);
559 FLAC__ASSERT(0 != iterator->file);
560
561 if(!iterator->is_writable)
562 return false;
563
564 if(block->type == FLAC__METADATA_TYPE_STREAMINFO) {
565 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT;
566 return false;
567 }
568
Josh Coalson6b02a752002-05-10 06:42:02 +0000569 block->is_last = iterator->is_last;
Josh Coalson51ee4652002-05-09 05:46:23 +0000570
Josh Coalsoncae58732002-05-04 17:30:32 +0000571 if(use_padding) {
572 /* first see if we can even use padding */
573 if(iterator->is_last) {
574 use_padding = false;
575 }
576 else {
577 simple_iterator_push_(iterator);
578 if(!FLAC__metadata_simple_iterator_next(iterator)) {
579 (void)simple_iterator_pop_(iterator);
580 return false;
581 }
582 if(iterator->type != FLAC__METADATA_TYPE_PADDING) {
583 use_padding = false;
584 }
585 else {
Josh Coalsoncae58732002-05-04 17:30:32 +0000586 if(iterator->length == block->length) {
587 padding_leftover = 0;
588 block->is_last = iterator->is_last;
589 }
Josh Coalsoneb643a22002-05-20 05:58:50 +0000590 else if(iterator->length < FLAC__STREAM_METADATA_HEADER_LENGTH + block->length)
Josh Coalsoncae58732002-05-04 17:30:32 +0000591 use_padding = false;
592 else {
Josh Coalson6b02a752002-05-10 06:42:02 +0000593 padding_leftover = iterator->length - block->length;
Josh Coalsoncae58732002-05-04 17:30:32 +0000594 padding_is_last = iterator->is_last;
595 block->is_last = false;
596 }
597 }
598 if(!simple_iterator_pop_(iterator))
599 return false;
600 }
601 }
602 if(use_padding) {
603 /* move to the next block, which is suitable padding */
604 if(!FLAC__metadata_simple_iterator_next(iterator))
605 return false;
Josh Coalson51ee4652002-05-09 05:46:23 +0000606 if(padding_leftover == 0) {
607 ret = write_metadata_block_stationary_(iterator, block);
Josh Coalson51ee4652002-05-09 05:46:23 +0000608 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
Josh Coalson72943092002-05-22 05:35:43 +0000609 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + (long)FLAC__STREAM_METADATA_HEADER_LENGTH);
Josh Coalson51ee4652002-05-09 05:46:23 +0000610 return ret;
611 }
612 else {
Josh Coalsoneb643a22002-05-20 05:58:50 +0000613 FLAC__ASSERT(padding_leftover >= FLAC__STREAM_METADATA_HEADER_LENGTH);
614 ret = write_metadata_block_stationary_with_padding_(iterator, block, padding_leftover - FLAC__STREAM_METADATA_HEADER_LENGTH, padding_is_last);
Josh Coalson51ee4652002-05-09 05:46:23 +0000615 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
Josh Coalson72943092002-05-22 05:35:43 +0000616 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + (long)FLAC__STREAM_METADATA_HEADER_LENGTH);
Josh Coalson51ee4652002-05-09 05:46:23 +0000617 return ret;
618 }
Josh Coalsoncae58732002-05-04 17:30:32 +0000619 }
620 else {
Josh Coalson51ee4652002-05-09 05:46:23 +0000621 ret = rewrite_whole_file_(iterator, block, /*append=*/true);
Josh Coalson51ee4652002-05-09 05:46:23 +0000622 FLAC__ASSERT(iterator->offset[iterator->depth] == debug_target_offset);
Josh Coalson72943092002-05-22 05:35:43 +0000623 FLAC__ASSERT(ftell(iterator->file) == debug_target_offset + (long)FLAC__STREAM_METADATA_HEADER_LENGTH);
Josh Coalson51ee4652002-05-09 05:46:23 +0000624 return ret;
Josh Coalsoncae58732002-05-04 17:30:32 +0000625 }
626}
627
Josh Coalsoncae58732002-05-04 17:30:32 +0000628FLAC__bool FLAC__metadata_simple_iterator_delete_block(FLAC__MetaData_SimpleIterator *iterator, FLAC__bool use_padding)
629{
Josh Coalson51ee4652002-05-09 05:46:23 +0000630 FLAC__ASSERT_DECLARATION(long debug_target_offset = iterator->offset[iterator->depth];)
631 FLAC__bool ret;
632
Josh Coalsoncae58732002-05-04 17:30:32 +0000633 if(iterator->type == FLAC__METADATA_TYPE_STREAMINFO) {
634 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT;
635 return false;
636 }
637
638 if(use_padding) {
639 FLAC__StreamMetaData *padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
640 if(0 == padding) {
641 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
642 return false;
643 }
644 padding->length = iterator->length;
645 if(!FLAC__metadata_simple_iterator_set_block(iterator, padding, false)) {
646 FLAC__metadata_object_delete(padding);
647 return false;
648 }
649 FLAC__metadata_object_delete(padding);
Josh Coalson6b02a752002-05-10 06:42:02 +0000650 if(!FLAC__metadata_simple_iterator_prev(iterator))
651 return false;
Josh Coalson72943092002-05-22 05:35:43 +0000652 FLAC__ASSERT(iterator->offset[iterator->depth] + (long)FLAC__STREAM_METADATA_HEADER_LENGTH + (long)iterator->length == debug_target_offset);
Josh Coalson51ee4652002-05-09 05:46:23 +0000653 FLAC__ASSERT(ftell(iterator->file) + (long)iterator->length == debug_target_offset);
Josh Coalsoncae58732002-05-04 17:30:32 +0000654 return true;
655 }
656 else {
Josh Coalson51ee4652002-05-09 05:46:23 +0000657 ret = rewrite_whole_file_(iterator, 0, /*append=*/false);
Josh Coalson72943092002-05-22 05:35:43 +0000658 FLAC__ASSERT(iterator->offset[iterator->depth] + (long)FLAC__STREAM_METADATA_HEADER_LENGTH + (long)iterator->length == debug_target_offset);
Josh Coalson51ee4652002-05-09 05:46:23 +0000659 FLAC__ASSERT(ftell(iterator->file) + (long)iterator->length == debug_target_offset);
660 return ret;
Josh Coalsoncae58732002-05-04 17:30:32 +0000661 }
662}
663
664
665
666/****************************************************************************
667 *
668 * Level 2 implementation
669 *
670 ***************************************************************************/
671
672
673typedef struct FLAC__MetaData_Node {
674 FLAC__StreamMetaData *data;
675 struct FLAC__MetaData_Node *prev, *next;
676} FLAC__MetaData_Node;
677
678struct FLAC__MetaData_Chain {
679 char *filename;
680 FLAC__MetaData_Node *head;
681 FLAC__MetaData_Node *tail;
682 unsigned nodes;
683 FLAC__MetaData_ChainStatus status;
684 long first_offset, last_offset; /*@@@ 2G limit */
685 /*
686 * This is the length of the chain initially read from the FLAC file.
687 * it is used to compare against the current_length to decide whether
688 * or not the whole file has to be rewritten.
689 */
690 unsigned initial_length; /*@@@ 4G limit */
691 unsigned current_length; /*@@@ 4G limit */
692};
693
694struct FLAC__MetaData_Iterator {
695 FLAC__MetaData_Chain *chain;
696 FLAC__MetaData_Node *current;
697};
698
699const char *FLAC__MetaData_ChainStatusString[] = {
700 "FLAC__METADATA_CHAIN_STATUS_OK",
701 "FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT",
702 "FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE",
703 "FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE",
704 "FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE",
Josh Coalsona7b673c2002-05-25 02:13:38 +0000705 "FLAC__METADATA_CHAIN_STATUS_BAD_METADATA",
Josh Coalsoncae58732002-05-04 17:30:32 +0000706 "FLAC__METADATA_CHAIN_STATUS_READ_ERROR",
707 "FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR",
708 "FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR",
709 "FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR",
710 "FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR",
711 "FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR",
712 "FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR"
713};
714
715
716static FLAC__MetaData_Node *node_new_()
717{
718 FLAC__MetaData_Node *node = (FLAC__MetaData_Node*)malloc(sizeof(FLAC__MetaData_Node));
719 if(0 != node)
720 memset(node, 0, sizeof(FLAC__MetaData_Node));
721 return node;
722}
723
724static void node_delete_(FLAC__MetaData_Node *node)
725{
726 FLAC__ASSERT(0 != node);
727 if(0 != node->data)
728 FLAC__metadata_object_delete(node->data);
729 free(node);
730}
731
732static void chain_append_node_(FLAC__MetaData_Chain *chain, FLAC__MetaData_Node *node)
733{
734 FLAC__ASSERT(0 != chain);
735 FLAC__ASSERT(0 != node);
Josh Coalson6b02a752002-05-10 06:42:02 +0000736 FLAC__ASSERT(0 != node->data);
Josh Coalsoncae58732002-05-04 17:30:32 +0000737
Josh Coalson6b02a752002-05-10 06:42:02 +0000738 node->next = node->prev = 0;
Josh Coalsoncae58732002-05-04 17:30:32 +0000739 node->data->is_last = true;
740 if(0 != chain->tail)
741 chain->tail->data->is_last = false;
742
743 if(0 == chain->head)
744 chain->head = node;
745 else {
Josh Coalson6b02a752002-05-10 06:42:02 +0000746 FLAC__ASSERT(0 != chain->tail);
Josh Coalsoncae58732002-05-04 17:30:32 +0000747 chain->tail->next = node;
748 node->prev = chain->tail;
749 }
750 chain->tail = node;
751 chain->nodes++;
Josh Coalsoneb643a22002-05-20 05:58:50 +0000752 chain->current_length += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length);
Josh Coalsoncae58732002-05-04 17:30:32 +0000753}
754
755static void chain_remove_node_(FLAC__MetaData_Chain *chain, FLAC__MetaData_Node *node)
756{
757 FLAC__ASSERT(0 != chain);
758 FLAC__ASSERT(0 != node);
759
760 if(node == chain->head)
761 chain->head = node->next;
762 else
763 node->prev->next = node->next;
764
765 if(node == chain->tail)
766 chain->tail = node->prev;
767 else
768 node->next->prev = node->prev;
769
Josh Coalson6b02a752002-05-10 06:42:02 +0000770 if(0 != chain->tail)
771 chain->tail->data->is_last = true;
772
Josh Coalsoncae58732002-05-04 17:30:32 +0000773 chain->nodes--;
Josh Coalsoneb643a22002-05-20 05:58:50 +0000774 chain->current_length -= (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length);
Josh Coalsoncae58732002-05-04 17:30:32 +0000775}
776
777static void chain_delete_node_(FLAC__MetaData_Chain *chain, FLAC__MetaData_Node *node)
778{
779 chain_remove_node_(chain, node);
780 node_delete_(node);
781}
782
783static void iterator_insert_node_(FLAC__MetaData_Iterator *iterator, FLAC__MetaData_Node *node)
784{
785 FLAC__ASSERT(0 != node);
Josh Coalson6b02a752002-05-10 06:42:02 +0000786 FLAC__ASSERT(0 != node->data);
Josh Coalsoncae58732002-05-04 17:30:32 +0000787 FLAC__ASSERT(0 != iterator);
788 FLAC__ASSERT(0 != iterator->current);
789 FLAC__ASSERT(0 != iterator->chain);
790 FLAC__ASSERT(0 != iterator->chain->head);
791 FLAC__ASSERT(0 != iterator->chain->tail);
792
Josh Coalson6b02a752002-05-10 06:42:02 +0000793 node->data->is_last = false;
Josh Coalson2745fe72002-05-11 05:31:56 +0000794
Josh Coalsoncae58732002-05-04 17:30:32 +0000795 node->prev = iterator->current->prev;
Josh Coalson2745fe72002-05-11 05:31:56 +0000796 node->next = iterator->current;
797
Josh Coalsoncae58732002-05-04 17:30:32 +0000798 if(0 == node->prev)
799 iterator->chain->head = node;
800 else
801 node->prev->next = node;
802
Josh Coalsoncae58732002-05-04 17:30:32 +0000803 iterator->current->prev = node;
804
805 iterator->chain->nodes++;
Josh Coalsoneb643a22002-05-20 05:58:50 +0000806 iterator->chain->current_length += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length);
Josh Coalsoncae58732002-05-04 17:30:32 +0000807}
808
809static void iterator_insert_node_after_(FLAC__MetaData_Iterator *iterator, FLAC__MetaData_Node *node)
810{
811 FLAC__ASSERT(0 != node);
Josh Coalson6b02a752002-05-10 06:42:02 +0000812 FLAC__ASSERT(0 != node->data);
Josh Coalsoncae58732002-05-04 17:30:32 +0000813 FLAC__ASSERT(0 != iterator);
814 FLAC__ASSERT(0 != iterator->current);
815 FLAC__ASSERT(0 != iterator->chain);
816 FLAC__ASSERT(0 != iterator->chain->head);
817 FLAC__ASSERT(0 != iterator->chain->tail);
818
819 iterator->current->data->is_last = false;
820
821 node->prev = iterator->current;
Josh Coalsoncae58732002-05-04 17:30:32 +0000822 node->next = iterator->current->next;
Josh Coalson2745fe72002-05-11 05:31:56 +0000823
Josh Coalsoncae58732002-05-04 17:30:32 +0000824 if(0 == node->next)
825 iterator->chain->tail = node;
826 else
827 node->next->prev = node;
828
Josh Coalson2745fe72002-05-11 05:31:56 +0000829 node->prev->next = node;
830
Josh Coalsoncae58732002-05-04 17:30:32 +0000831 iterator->chain->tail->data->is_last = true;
832
833 iterator->chain->nodes++;
Josh Coalsoneb643a22002-05-20 05:58:50 +0000834 iterator->chain->current_length += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length);
Josh Coalsoncae58732002-05-04 17:30:32 +0000835}
836
Josh Coalson2745fe72002-05-11 05:31:56 +0000837/* return true iff node and node->next are both padding */
838static FLAC__bool chain_merge_adjacent_padding_(FLAC__MetaData_Chain *chain, FLAC__MetaData_Node *node)
839{
840 if(node->data->type == FLAC__METADATA_TYPE_PADDING && 0 != node->next && node->next->data->type == FLAC__METADATA_TYPE_PADDING) {
Josh Coalsoneb643a22002-05-20 05:58:50 +0000841 const unsigned growth = FLAC__STREAM_METADATA_HEADER_LENGTH + node->next->data->length;
Josh Coalson2745fe72002-05-11 05:31:56 +0000842 node->data->length += growth;
843 chain->current_length += growth;
844
845 chain_delete_node_(chain, node->next);
846 return true;
847 }
848 else
849 return false;
850}
851
Josh Coalsoncae58732002-05-04 17:30:32 +0000852FLAC__MetaData_Chain *FLAC__metadata_chain_new()
853{
854 FLAC__MetaData_Chain *chain = malloc(sizeof(FLAC__MetaData_Chain));
855
856 if(0 != chain) {
857 chain->filename = 0;
858 chain->head = chain->tail = 0;
859 chain->nodes = 0;
860 chain->status = FLAC__METADATA_CHAIN_STATUS_OK;
861 chain->initial_length = chain->current_length = 0;
862 }
863
864 return chain;
865}
866
867void FLAC__metadata_chain_delete(FLAC__MetaData_Chain *chain)
868{
869 FLAC__MetaData_Node *node, *next;
870
871 FLAC__ASSERT(0 != chain);
872
873 for(node = chain->head; node; ) {
874 next = node->next;
875 node_delete_(node);
876 node = next;
877 }
878
879 if(0 != chain->filename)
880 free(chain->filename);
881
882 free(chain);
883}
884
885FLAC__MetaData_ChainStatus FLAC__metadata_chain_status(FLAC__MetaData_Chain *chain)
886{
887 FLAC__MetaData_ChainStatus status;
888
889 FLAC__ASSERT(0 != chain);
890
891 status = chain->status;
892 chain->status = FLAC__METADATA_CHAIN_STATUS_OK;
893 return status;
894}
895
896FLAC__bool FLAC__metadata_chain_read(FLAC__MetaData_Chain *chain, const char *filename)
897{
898 FLAC__MetaData_SimpleIterator *iterator;
899 FLAC__MetaData_Node *node;
900
901 FLAC__ASSERT(0 != chain);
902 FLAC__ASSERT(0 != filename);
903
904 if(0 == (chain->filename = strdup(filename))) {
905 chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
906 return false;
907 }
908
909 if(0 == (iterator = FLAC__metadata_simple_iterator_new())) {
910 chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
911 return false;
912 }
913
914 if(!FLAC__metadata_simple_iterator_init(iterator, filename, /*preserve_file_stats=*/false)) {
915 chain->status = get_equivalent_status_(iterator->status);
916 return false;
917 }
918
Josh Coalson2745fe72002-05-11 05:31:56 +0000919 chain->first_offset = iterator->offset[iterator->depth];
920
Josh Coalsoncae58732002-05-04 17:30:32 +0000921 do {
922 node = node_new_();
923 if(0 == node) {
924 chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
925 return false;
926 }
927 node->data = FLAC__metadata_simple_iterator_get_block(iterator);
928 if(0 == node->data) {
929 node_delete_(node);
930 chain->status = get_equivalent_status_(iterator->status);
931 return false;
932 }
933 chain_append_node_(chain, node);
934 } while(FLAC__metadata_simple_iterator_next(iterator));
935
936 if(!iterator->is_last || iterator->status != FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK) {
937 chain->status = get_equivalent_status_(iterator->status);
938 return false;
939 }
940
Josh Coalson2745fe72002-05-11 05:31:56 +0000941 chain->last_offset = ftell(iterator->file) + iterator->length;
Josh Coalsoncae58732002-05-04 17:30:32 +0000942 FLAC__metadata_simple_iterator_delete(iterator);
943
944 chain->initial_length = chain->current_length;
945 return true;
946}
947
Josh Coalsoncae58732002-05-04 17:30:32 +0000948FLAC__bool FLAC__metadata_chain_write(FLAC__MetaData_Chain *chain, FLAC__bool use_padding, FLAC__bool preserve_file_stats)
949{
950 struct stat stats;
951 const char *tempfile_path_prefix = 0;
952
Josh Coalson2745fe72002-05-11 05:31:56 +0000953 if(use_padding) {
Josh Coalson2745fe72002-05-11 05:31:56 +0000954 if(chain->current_length < chain->initial_length && chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) {
955 const unsigned delta = chain->initial_length - chain->current_length;
956 chain->tail->data->length += delta;
957 chain->current_length += delta;
958 FLAC__ASSERT(chain->current_length == chain->initial_length);
Josh Coalsoncae58732002-05-04 17:30:32 +0000959 }
Josh Coalsoneb643a22002-05-20 05:58:50 +0000960 else if(chain->current_length + FLAC__STREAM_METADATA_HEADER_LENGTH <= chain->initial_length) {
Josh Coalson2745fe72002-05-11 05:31:56 +0000961 FLAC__StreamMetaData *padding;
962 FLAC__MetaData_Node *node;
963 if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING))) {
964 chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
965 return false;
966 }
Josh Coalsoneb643a22002-05-20 05:58:50 +0000967 padding->length = chain->initial_length - (FLAC__STREAM_METADATA_HEADER_LENGTH + chain->current_length);
Josh Coalson2745fe72002-05-11 05:31:56 +0000968 if(0 == (node = node_new_())) {
969 FLAC__metadata_object_delete(padding);
970 chain->status = FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
971 return false;
972 }
973 node->data = padding;
974 chain_append_node_(chain, node);
975 FLAC__ASSERT(chain->current_length == chain->initial_length);
Josh Coalsoncae58732002-05-04 17:30:32 +0000976 }
Josh Coalson2745fe72002-05-11 05:31:56 +0000977 else if(chain->current_length > chain->initial_length) {
978 const unsigned delta = chain->current_length - chain->initial_length;
979 if(chain->tail->data->type == FLAC__METADATA_TYPE_PADDING) {
Josh Coalsoneb643a22002-05-20 05:58:50 +0000980 if(chain->tail->data->length + FLAC__STREAM_METADATA_HEADER_LENGTH == delta) {
Josh Coalson2745fe72002-05-11 05:31:56 +0000981 chain_delete_node_(chain, chain->tail);
982 FLAC__ASSERT(chain->current_length == chain->initial_length);
983 }
Josh Coalsone2ba10f2002-05-14 06:03:17 +0000984 else if(chain->tail->data->length >= delta) {
Josh Coalson2745fe72002-05-11 05:31:56 +0000985 chain->tail->data->length -= delta;
986 chain->current_length -= delta;
987 FLAC__ASSERT(chain->current_length == chain->initial_length);
988 }
989 }
990 }
Josh Coalsoncae58732002-05-04 17:30:32 +0000991 }
992
993 if(preserve_file_stats)
994 get_file_stats_(chain->filename, &stats);
995
996 if(chain->current_length == chain->initial_length) {
997 if(!chain_rewrite_chain_(chain))
998 return false;
999 }
1000 else {
1001 if(!chain_rewrite_file_(chain, tempfile_path_prefix))
1002 return false;
1003 }
1004
1005 if(preserve_file_stats)
1006 set_file_stats_(chain->filename, &stats);
1007
Josh Coalsone2ba10f2002-05-14 06:03:17 +00001008 /* recompute lengths and offsets if necessary */
1009 if(chain->initial_length != chain->current_length) {
1010 const FLAC__MetaData_Node *node;
1011 chain->initial_length = chain->current_length;
1012 chain->last_offset = chain->first_offset;
1013 for(node = chain->head; node; node = node->next)
Josh Coalsoneb643a22002-05-20 05:58:50 +00001014 chain->last_offset += (FLAC__STREAM_METADATA_HEADER_LENGTH + node->data->length);
Josh Coalsone2ba10f2002-05-14 06:03:17 +00001015 }
1016
Josh Coalsoncae58732002-05-04 17:30:32 +00001017 return true;
1018}
1019
1020void FLAC__metadata_chain_merge_padding(FLAC__MetaData_Chain *chain)
1021{
1022 FLAC__MetaData_Node *node;
1023
1024 FLAC__ASSERT(0 != chain);
1025
1026 for(node = chain->head; node; ) {
Josh Coalson2745fe72002-05-11 05:31:56 +00001027 if(!chain_merge_adjacent_padding_(chain, node))
Josh Coalsoncae58732002-05-04 17:30:32 +00001028 node = node->next;
Josh Coalsoncae58732002-05-04 17:30:32 +00001029 }
Josh Coalsoncae58732002-05-04 17:30:32 +00001030}
1031
1032void FLAC__metadata_chain_sort_padding(FLAC__MetaData_Chain *chain)
1033{
1034 FLAC__MetaData_Node *node, *save;
1035 unsigned i;
1036
1037 FLAC__ASSERT(0 != chain);
1038
1039 /*
1040 * Don't try and be too smart... this simple algo is good enough for
1041 * the small number of nodes that we deal with.
1042 */
1043 for(i = 0, node = chain->head; i < chain->nodes; i++) {
Josh Coalsoncae58732002-05-04 17:30:32 +00001044 if(node->data->type == FLAC__METADATA_TYPE_PADDING) {
1045 save = node->next;
1046 chain_remove_node_(chain, node);
1047 chain_append_node_(chain, node);
1048 node = save;
1049 }
1050 else {
1051 node = node->next;
1052 }
1053 }
Josh Coalsoncae58732002-05-04 17:30:32 +00001054
1055 FLAC__metadata_chain_merge_padding(chain);
1056}
1057
1058
1059FLAC__MetaData_Iterator *FLAC__metadata_iterator_new()
1060{
1061 FLAC__MetaData_Iterator *iterator = malloc(sizeof(FLAC__MetaData_Iterator));
1062
1063 if(0 != iterator) {
1064 iterator->current = 0;
1065 iterator->chain = 0;
1066 }
1067
1068 return iterator;
1069}
1070
1071void FLAC__metadata_iterator_delete(FLAC__MetaData_Iterator *iterator)
1072{
1073 FLAC__ASSERT(0 != iterator);
1074
1075 free(iterator);
1076}
1077
1078/*
1079 * Initialize the iterator to point to the first metadata block in the
1080 * given chain.
1081 */
1082void FLAC__metadata_iterator_init(FLAC__MetaData_Iterator *iterator, FLAC__MetaData_Chain *chain)
1083{
1084 FLAC__ASSERT(0 != iterator);
1085 FLAC__ASSERT(0 != chain);
1086 FLAC__ASSERT(0 != chain->head);
1087
1088 iterator->chain = chain;
1089 iterator->current = chain->head;
1090}
1091
1092FLAC__bool FLAC__metadata_iterator_next(FLAC__MetaData_Iterator *iterator)
1093{
1094 FLAC__ASSERT(0 != iterator);
1095
1096 if(0 == iterator->current || 0 == iterator->current->next)
1097 return false;
1098
1099 iterator->current = iterator->current->next;
1100 return true;
1101}
1102
1103FLAC__bool FLAC__metadata_iterator_prev(FLAC__MetaData_Iterator *iterator)
1104{
1105 FLAC__ASSERT(0 != iterator);
1106
1107 if(0 == iterator->current || 0 == iterator->current->prev)
1108 return false;
1109
1110 iterator->current = iterator->current->prev;
1111 return true;
1112}
1113
1114FLAC__MetaDataType FLAC__metadata_iterator_get_block_type(const FLAC__MetaData_Iterator *iterator)
1115{
1116 FLAC__ASSERT(0 != iterator);
1117 FLAC__ASSERT(0 != iterator->current);
Josh Coalson2745fe72002-05-11 05:31:56 +00001118 FLAC__ASSERT(0 != iterator->current->data);
Josh Coalsoncae58732002-05-04 17:30:32 +00001119
1120 return iterator->current->data->type;
1121}
1122
1123FLAC__StreamMetaData *FLAC__metadata_iterator_get_block(FLAC__MetaData_Iterator *iterator)
1124{
1125 FLAC__ASSERT(0 != iterator);
1126 FLAC__ASSERT(0 != iterator->current);
1127
1128 return iterator->current->data;
1129}
1130
Josh Coalson2745fe72002-05-11 05:31:56 +00001131FLAC__bool FLAC__metadata_iterator_set_block(FLAC__MetaData_Iterator *iterator, FLAC__StreamMetaData *block)
1132{
1133 return FLAC__metadata_iterator_delete_block(iterator, false) && FLAC__metadata_iterator_insert_block_after(iterator, block);
1134}
1135
Josh Coalsoncae58732002-05-04 17:30:32 +00001136FLAC__bool FLAC__metadata_iterator_delete_block(FLAC__MetaData_Iterator *iterator, FLAC__bool replace_with_padding)
1137{
1138 FLAC__MetaData_Node *save;
1139
1140 FLAC__ASSERT(0 != iterator);
1141 FLAC__ASSERT(0 != iterator->current);
1142
1143 if(0 == iterator->current->prev) {
1144 FLAC__ASSERT(iterator->current->data->type == FLAC__METADATA_TYPE_STREAMINFO);
1145 return false;
1146 }
1147
1148 save = iterator->current->prev;
1149
1150 if(replace_with_padding) {
1151 FLAC__metadata_object_delete_data_(iterator->current->data);
1152 iterator->current->data->type = FLAC__METADATA_TYPE_PADDING;
1153 }
1154 else {
1155 chain_delete_node_(iterator->chain, iterator->current);
1156 }
1157
1158 iterator->current = save;
1159 return true;
1160}
1161
1162FLAC__bool FLAC__metadata_iterator_insert_block_before(FLAC__MetaData_Iterator *iterator, FLAC__StreamMetaData *block)
1163{
1164 FLAC__MetaData_Node *node;
1165
1166 FLAC__ASSERT(0 != iterator);
1167 FLAC__ASSERT(0 != iterator->current);
1168
1169 if(block->type == FLAC__METADATA_TYPE_STREAMINFO)
1170 return false;
1171
1172 if(0 == iterator->current->prev) {
1173 FLAC__ASSERT(iterator->current->data->type == FLAC__METADATA_TYPE_STREAMINFO);
1174 return false;
1175 }
1176
1177 if(0 == (node = node_new_()))
1178 return false;
1179
1180 node->data = block;
1181 iterator_insert_node_(iterator, node);
1182 iterator->current = node;
1183 return true;
1184}
1185
1186FLAC__bool FLAC__metadata_iterator_insert_block_after(FLAC__MetaData_Iterator *iterator, FLAC__StreamMetaData *block)
1187{
1188 FLAC__MetaData_Node *node;
1189
1190 FLAC__ASSERT(0 != iterator);
1191 FLAC__ASSERT(0 != iterator->current);
1192
1193 if(block->type == FLAC__METADATA_TYPE_STREAMINFO)
1194 return false;
1195
1196 if(0 == (node = node_new_()))
1197 return false;
1198
1199 node->data = block;
1200 iterator_insert_node_after_(iterator, node);
1201 iterator->current = node;
1202 return true;
1203}
1204
1205
1206/****************************************************************************
1207 *
1208 * Metadata object routines
1209 *
1210 ***************************************************************************/
1211
1212FLAC__StreamMetaData *FLAC__metadata_object_new(FLAC__MetaDataType type)
1213{
1214 FLAC__StreamMetaData *object = malloc(sizeof(FLAC__StreamMetaData));
1215 if(0 != object) {
1216 memset(object, 0, sizeof(FLAC__StreamMetaData));
1217 object->is_last = false;
1218 object->type = type;
1219 switch(type) {
1220 case FLAC__METADATA_TYPE_STREAMINFO:
1221 object->length = FLAC__STREAM_METADATA_STREAMINFO_LENGTH;
1222 break;
1223 case FLAC__METADATA_TYPE_PADDING:
1224 break;
1225 case FLAC__METADATA_TYPE_APPLICATION:
1226 object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
1227 break;
1228 case FLAC__METADATA_TYPE_SEEKTABLE:
1229 break;
1230 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
1231 object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN + FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
1232 break;
1233 default:
1234 FLAC__ASSERT(0);
1235 }
1236 }
1237
1238 return object;
1239}
1240
1241static FLAC__bool copy_bytes_(FLAC__byte **to, const FLAC__byte *from, unsigned bytes)
1242{
Josh Coalson72943092002-05-22 05:35:43 +00001243 if(bytes > 0 && 0 != from) {
1244 if(0 == (*to = malloc(bytes)))
1245 return false;
1246 memcpy(*to, from, bytes);
1247 }
1248 else {
1249 FLAC__ASSERT(0 == from);
1250 FLAC__ASSERT(bytes == 0);
1251 *to = 0;
1252 }
Josh Coalsoncae58732002-05-04 17:30:32 +00001253 return true;
1254}
1255
1256static FLAC__bool copy_vcentry_(FLAC__StreamMetaData_VorbisComment_Entry *to, const FLAC__StreamMetaData_VorbisComment_Entry *from)
1257{
1258 to->length = from->length;
1259 if(0 == from->entry) {
Josh Coalson72943092002-05-22 05:35:43 +00001260 FLAC__ASSERT(from->length == 0);
Josh Coalsoncae58732002-05-04 17:30:32 +00001261 to->entry = 0;
1262 }
1263 else {
Josh Coalson72943092002-05-22 05:35:43 +00001264 FLAC__ASSERT(from->length > 0);
Josh Coalsoncae58732002-05-04 17:30:32 +00001265 if(0 == (to->entry = malloc(from->length)))
1266 return false;
1267 memcpy(to->entry, from->entry, from->length);
1268 }
1269 return true;
1270}
1271
1272FLAC__StreamMetaData *FLAC__metadata_object_copy(const FLAC__StreamMetaData *object)
1273{
1274 FLAC__StreamMetaData *to;
1275 unsigned i;
1276
1277 FLAC__ASSERT(0 != object);
1278
1279 if(0 != (to = FLAC__metadata_object_new(object->type))) {
1280 to->is_last = object->is_last;
1281 to->type = object->type;
1282 to->length = object->length;
1283 switch(to->type) {
1284 case FLAC__METADATA_TYPE_STREAMINFO:
1285 memcpy(&to->data.stream_info, &object->data.stream_info, sizeof(FLAC__StreamMetaData_StreamInfo));
1286 break;
1287 case FLAC__METADATA_TYPE_PADDING:
1288 break;
1289 case FLAC__METADATA_TYPE_APPLICATION:
1290 memcpy(&to->data.application.id, &object->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8);
Josh Coalson72943092002-05-22 05:35:43 +00001291 if(!copy_bytes_(&to->data.application.data, object->data.application.data, object->length - FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) {
1292 FLAC__metadata_object_delete(to);
1293 return 0;
Josh Coalsoncae58732002-05-04 17:30:32 +00001294 }
1295 break;
1296 case FLAC__METADATA_TYPE_SEEKTABLE:
1297 to->data.seek_table.num_points = object->data.seek_table.num_points;
Josh Coalson72943092002-05-22 05:35:43 +00001298 if(!copy_bytes_((FLAC__byte**)&to->data.seek_table.points, (FLAC__byte*)object->data.seek_table.points, object->data.seek_table.num_points * sizeof(FLAC__StreamMetaData_SeekPoint))) {
1299 FLAC__metadata_object_delete(to);
1300 return 0;
Josh Coalsoncae58732002-05-04 17:30:32 +00001301 }
1302 break;
1303 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
1304 if(!copy_vcentry_(&to->data.vorbis_comment.vendor_string, &object->data.vorbis_comment.vendor_string)) {
1305 FLAC__metadata_object_delete(to);
1306 return 0;
1307 }
1308 to->data.vorbis_comment.num_comments = object->data.vorbis_comment.num_comments;
Josh Coalson72943092002-05-22 05:35:43 +00001309 if(object->data.vorbis_comment.num_comments == 0) {
1310 FLAC__ASSERT(0 == object->data.vorbis_comment.comments);
1311 to->data.vorbis_comment.comments = 0;
1312 }
1313 else {
1314 FLAC__ASSERT(0 != object->data.vorbis_comment.comments);
1315 if(0 == (to->data.vorbis_comment.comments = malloc(object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry*)))) {
1316 FLAC__metadata_object_delete(to);
1317 return 0;
1318 }
Josh Coalsoncae58732002-05-04 17:30:32 +00001319 }
1320 /* Need to do this to set the pointers inside the comments to 0.
1321 * In case of an error in the following loop, the object will be
1322 * deleted and we don't want the destructor freeing uninitialized
1323 * pointers.
1324 */
Josh Coalson72943092002-05-22 05:35:43 +00001325 if(0 != to->data.vorbis_comment.comments)
1326 memset(to->data.vorbis_comment.comments, 0, object->data.vorbis_comment.num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry*));
Josh Coalsoncae58732002-05-04 17:30:32 +00001327 for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
1328 if(!copy_vcentry_(to->data.vorbis_comment.comments+i, object->data.vorbis_comment.comments+i)) {
1329 FLAC__metadata_object_delete(to);
1330 return 0;
1331 }
1332 }
1333 break;
1334 default:
1335 FLAC__ASSERT(0);
1336 }
1337 }
1338
1339 return to;
1340}
1341
1342void FLAC__metadata_object_delete_data_(FLAC__StreamMetaData *object)
1343{
1344 unsigned i;
1345
1346 FLAC__ASSERT(0 != object);
1347
1348 switch(object->type) {
1349 case FLAC__METADATA_TYPE_STREAMINFO:
1350 case FLAC__METADATA_TYPE_PADDING:
1351 break;
1352 case FLAC__METADATA_TYPE_APPLICATION:
1353 if(0 != object->data.application.data)
1354 free(object->data.application.data);
1355 break;
1356 case FLAC__METADATA_TYPE_SEEKTABLE:
1357 if(0 != object->data.seek_table.points)
1358 free(object->data.seek_table.points);
1359 break;
1360 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
1361 if(0 != object->data.vorbis_comment.vendor_string.entry)
1362 free(object->data.vorbis_comment.vendor_string.entry);
1363 for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
1364 if(0 != object->data.vorbis_comment.comments[i].entry)
1365 free(object->data.vorbis_comment.comments[i].entry);
1366 }
Josh Coalsonfad2e612002-05-07 05:33:10 +00001367 if(0 != object->data.vorbis_comment.comments)
1368 free(object->data.vorbis_comment.comments);
Josh Coalsoncae58732002-05-04 17:30:32 +00001369 break;
1370 default:
1371 FLAC__ASSERT(0);
1372 }
1373}
1374
1375void FLAC__metadata_object_delete(FLAC__StreamMetaData *object)
1376{
1377 FLAC__metadata_object_delete_data_(object);
1378 free(object);
1379}
1380
Josh Coalsoncae58732002-05-04 17:30:32 +00001381FLAC__bool FLAC__metadata_object_application_set_data(FLAC__StreamMetaData *object, FLAC__byte *data, unsigned length, FLAC__bool copy)
1382{
1383 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_APPLICATION);
Josh Coalson72943092002-05-22 05:35:43 +00001384 FLAC__ASSERT((0 != data && length > 0) || (0 == data && length == 0 && copy == false));
Josh Coalsoncae58732002-05-04 17:30:32 +00001385
1386 if(0 != object->data.application.data)
1387 free(object->data.application.data);
1388
1389 if(copy) {
1390 if(!copy_bytes_(&object->data.application.data, data, length))
1391 return false;
1392 }
1393 else {
1394 object->data.application.data = data;
1395 }
1396 object->length = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8 + length;
1397 return true;
1398}
1399
1400FLAC__StreamMetaData_SeekPoint *FLAC__metadata_object_seekpoint_array_new(unsigned num_points)
1401{
Josh Coalson72943092002-05-22 05:35:43 +00001402 FLAC__StreamMetaData_SeekPoint *object_array;
1403
1404 FLAC__ASSERT(num_points > 0);
1405
1406 object_array = malloc(num_points * sizeof(FLAC__StreamMetaData_SeekPoint));
Josh Coalsoncae58732002-05-04 17:30:32 +00001407
1408 if(0 != object_array)
1409 memset(object_array, 0, num_points * sizeof(FLAC__StreamMetaData_SeekPoint));
1410
1411 return object_array;
1412}
1413
1414FLAC__StreamMetaData_SeekPoint *FLAC__metadata_object_seekpoint_array_copy(const FLAC__StreamMetaData_SeekPoint *object_array, unsigned num_points)
1415{
1416 FLAC__StreamMetaData_SeekPoint *return_array;
1417
1418 FLAC__ASSERT(0 != object_array);
Josh Coalson72943092002-05-22 05:35:43 +00001419 FLAC__ASSERT(num_points > 0);
Josh Coalsoncae58732002-05-04 17:30:32 +00001420
1421 return_array = FLAC__metadata_object_seekpoint_array_new(num_points);
1422
1423 if(0 != return_array)
1424 memcpy(return_array, object_array, num_points * sizeof(FLAC__StreamMetaData_SeekPoint));
1425
1426 return return_array;
1427}
1428
1429void FLAC__metadata_object_seekpoint_array_delete(FLAC__StreamMetaData_SeekPoint *object_array)
1430{
1431 FLAC__ASSERT(0 != object_array);
1432
1433 free(object_array);
1434}
1435
1436FLAC__bool FLAC__metadata_object_seekpoint_array_resize(FLAC__StreamMetaData_SeekPoint **object_array, unsigned old_num_points, unsigned new_num_points)
1437{
1438 FLAC__ASSERT(0 != object_array);
1439
1440 if(0 == *object_array) {
1441 FLAC__ASSERT(old_num_points == 0);
Josh Coalson72943092002-05-22 05:35:43 +00001442 if(0 == new_num_points)
1443 return true;
1444 else
1445 return 0 != (*object_array = FLAC__metadata_object_seekpoint_array_new(new_num_points));
Josh Coalsoncae58732002-05-04 17:30:32 +00001446 }
1447 else {
1448 const unsigned old_size = old_num_points * sizeof(FLAC__StreamMetaData_SeekPoint);
1449 const unsigned new_size = new_num_points * sizeof(FLAC__StreamMetaData_SeekPoint);
1450
Josh Coalson72943092002-05-22 05:35:43 +00001451 FLAC__ASSERT(old_num_points > 0);
1452
1453 if(new_size == 0)
1454 *object_array = 0;
1455 else if(0 == (*object_array = realloc(*object_array, new_size)))
Josh Coalsoncae58732002-05-04 17:30:32 +00001456 return false;
1457
1458 if(new_size > old_size)
1459 memset(*object_array + old_num_points, 0, new_size - old_size);
1460
1461 return true;
1462 }
1463}
1464
1465FLAC__bool FLAC__metadata_object_seektable_set_points(FLAC__StreamMetaData *object, FLAC__StreamMetaData_SeekPoint *points, unsigned num_points, FLAC__bool copy)
1466{
1467 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_SEEKTABLE);
Josh Coalson72943092002-05-22 05:35:43 +00001468 FLAC__ASSERT((0 != points && num_points > 0) || (0 == points && num_points == 0 && copy == false));
Josh Coalsoncae58732002-05-04 17:30:32 +00001469
1470 object->data.seek_table.num_points = num_points;
1471
1472 if(0 != object->data.seek_table.points)
1473 FLAC__metadata_object_seekpoint_array_delete(object->data.seek_table.points);
1474
1475 if(copy) {
1476 if(0 == (object->data.seek_table.points = FLAC__metadata_object_seekpoint_array_copy(points, num_points)))
1477 return false;
1478 }
1479 else {
1480 object->data.seek_table.points = points;
1481 }
1482 object->length = num_points * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
1483 return true;
1484}
1485
1486FLAC__StreamMetaData_VorbisComment_Entry *FLAC__metadata_object_vorbiscomment_entry_array_new(unsigned num_comments)
1487{
Josh Coalson72943092002-05-22 05:35:43 +00001488 FLAC__StreamMetaData_VorbisComment_Entry *object_array;
1489
1490 FLAC__ASSERT(num_comments > 0);
1491
1492 object_array = malloc(num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry));
Josh Coalsoncae58732002-05-04 17:30:32 +00001493
1494 if(0 != object_array)
1495 memset(object_array, 0, num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry));
1496
1497 return object_array;
1498}
1499
1500FLAC__StreamMetaData_VorbisComment_Entry *FLAC__metadata_object_vorbiscomment_entry_array_copy(const FLAC__StreamMetaData_VorbisComment_Entry *object_array, unsigned num_comments)
1501{
1502 FLAC__StreamMetaData_VorbisComment_Entry *return_array;
1503
1504 FLAC__ASSERT(0 != object_array);
Josh Coalson72943092002-05-22 05:35:43 +00001505 FLAC__ASSERT(num_comments > 0);
Josh Coalsoncae58732002-05-04 17:30:32 +00001506
1507 return_array = FLAC__metadata_object_vorbiscomment_entry_array_new(num_comments);
1508
1509 if(0 != return_array) {
1510 unsigned i;
1511 for(i = 0; i < num_comments; i++) {
1512 return_array[i].length = object_array[i].length;
Josh Coalson72943092002-05-22 05:35:43 +00001513 if(object_array[i].length == 0)
1514 return_array[i].entry = 0;
1515 else if(!copy_bytes_(&(return_array[i].entry), object_array[i].entry, object_array[i].length)) {
Josh Coalsoncae58732002-05-04 17:30:32 +00001516 FLAC__metadata_object_vorbiscomment_entry_array_delete(return_array, num_comments);
1517 return 0;
1518 }
1519 }
1520 }
1521
1522 return return_array;
1523}
1524
1525void FLAC__metadata_object_vorbiscomment_entry_array_delete(FLAC__StreamMetaData_VorbisComment_Entry *object_array, unsigned num_comments)
1526{
1527 unsigned i;
1528
Josh Coalson72943092002-05-22 05:35:43 +00001529 FLAC__ASSERT(0 != object_array && num_comments > 0);
Josh Coalsoncae58732002-05-04 17:30:32 +00001530
1531 for(i = 0; i < num_comments; i++)
1532 if(0 != object_array[i].entry)
1533 free(object_array[i].entry);
1534
Josh Coalson72943092002-05-22 05:35:43 +00001535 if(0 != object_array)
1536 free(object_array);
Josh Coalsoncae58732002-05-04 17:30:32 +00001537}
1538
1539FLAC__bool FLAC__metadata_object_vorbiscomment_entry_array_resize(FLAC__StreamMetaData_VorbisComment_Entry **object_array, unsigned old_num_comments, unsigned new_num_comments)
1540{
1541 FLAC__ASSERT(0 != object_array);
1542
1543 if(0 == *object_array) {
1544 FLAC__ASSERT(old_num_comments == 0);
Josh Coalson72943092002-05-22 05:35:43 +00001545 if(0 == new_num_comments)
1546 return true;
1547 else
1548 return 0 != (*object_array = FLAC__metadata_object_vorbiscomment_entry_array_new(new_num_comments));
Josh Coalsoncae58732002-05-04 17:30:32 +00001549 }
1550 else {
1551 const unsigned old_size = old_num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry);
1552 const unsigned new_size = new_num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry);
1553
Josh Coalson72943092002-05-22 05:35:43 +00001554 FLAC__ASSERT(old_num_comments > 0);
1555
Josh Coalsoncae58732002-05-04 17:30:32 +00001556 /* if shrinking, free the truncated entries */
1557 if(new_num_comments < old_num_comments) {
1558 unsigned i;
1559 for(i = new_num_comments; i < old_num_comments; i++)
Josh Coalson72943092002-05-22 05:35:43 +00001560 if(0 != (*object_array)[i].entry)
1561 free((*object_array)[i].entry);
Josh Coalsoncae58732002-05-04 17:30:32 +00001562 }
1563
Josh Coalson72943092002-05-22 05:35:43 +00001564 if(new_size == 0)
1565 *object_array = 0;
1566 else if(0 == (*object_array = realloc(*object_array, new_size)))
Josh Coalsoncae58732002-05-04 17:30:32 +00001567 return false;
1568
1569 /* if growing, zero all the length/pointers of new elements */
1570 if(new_size > old_size)
1571 memset(*object_array + old_num_comments, 0, new_size - old_size);
1572
1573 return true;
1574 }
1575}
1576
1577FLAC__bool FLAC__metadata_object_vorbiscomment_set_vendor_string(FLAC__StreamMetaData *object, FLAC__byte *entry, unsigned length, FLAC__bool copy)
1578{
1579 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
Josh Coalson72943092002-05-22 05:35:43 +00001580 FLAC__ASSERT((0 != entry && length > 0) || (0 == entry && length == 0 && copy == false));
Josh Coalsoncae58732002-05-04 17:30:32 +00001581
1582 if(0 != object->data.vorbis_comment.vendor_string.entry)
1583 free(object->data.vorbis_comment.vendor_string.entry);
1584
1585 object->length -= object->data.vorbis_comment.vendor_string.length;
1586
1587 if(copy) {
1588 if(!copy_bytes_(&object->data.vorbis_comment.vendor_string.entry, entry, length)) {
1589 object->data.vorbis_comment.vendor_string.length = 0;
1590 return false;
1591 }
1592 }
1593 else {
1594 object->data.vorbis_comment.vendor_string.entry = entry;
1595 }
1596
1597 object->data.vorbis_comment.vendor_string.length = length;
1598 object->length += object->data.vorbis_comment.vendor_string.length;
1599
1600 return true;
1601}
1602
1603FLAC__bool FLAC__metadata_object_vorbiscomment_set_comments(FLAC__StreamMetaData *object, FLAC__StreamMetaData_VorbisComment_Entry *comments, unsigned num_comments, FLAC__bool copy)
1604{
1605 unsigned i;
1606
1607 FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT);
Josh Coalson72943092002-05-22 05:35:43 +00001608 FLAC__ASSERT((0 != comments && num_comments > 0) || (0 == comments && num_comments == 0 && copy == false));
Josh Coalsoncae58732002-05-04 17:30:32 +00001609
1610 object->data.vorbis_comment.num_comments = num_comments;
1611
1612 if(0 != object->data.vorbis_comment.comments)
1613 FLAC__metadata_object_vorbiscomment_entry_array_delete(object->data.vorbis_comment.comments, num_comments);
1614
1615 if(copy) {
1616 if(0 == (object->data.vorbis_comment.comments = FLAC__metadata_object_vorbiscomment_entry_array_copy(comments, num_comments)))
1617 return false;
1618 }
1619 else {
1620 object->data.vorbis_comment.comments = comments;
1621 }
1622 object->length = num_comments * FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
1623
1624 /* calculate the new length */
1625 object->length = (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN) / 8;
1626 object->length += object->data.vorbis_comment.vendor_string.length;
1627 object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8;
1628 for(i = 0; i < object->data.vorbis_comment.num_comments; i++) {
1629 object->length += (FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8);
1630 object->length += object->data.vorbis_comment.comments[i].length;
1631 }
1632
1633 return true;
1634}
1635
1636
1637/****************************************************************************
1638 *
1639 * Local function definitions
1640 *
1641 ***************************************************************************/
1642
1643void pack_uint32_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes)
1644{
1645 unsigned i;
1646
1647 b += bytes;
1648
1649 for(i = 0; i < bytes; i++) {
Josh Coalsoneb643a22002-05-20 05:58:50 +00001650 *(--b) = (FLAC__byte)(val & 0xff);
Josh Coalsoncae58732002-05-04 17:30:32 +00001651 val >>= 8;
1652 }
1653}
1654
1655void pack_uint32_little_endian_(FLAC__uint32 val, FLAC__byte *b, unsigned bytes)
1656{
1657 unsigned i;
1658
1659 for(i = 0; i < bytes; i++) {
Josh Coalsoneb643a22002-05-20 05:58:50 +00001660 *(b++) = (FLAC__byte)(val & 0xff);
Josh Coalsoncae58732002-05-04 17:30:32 +00001661 val >>= 8;
1662 }
1663}
1664
1665void pack_uint64_(FLAC__uint64 val, FLAC__byte *b, unsigned bytes)
1666{
1667 unsigned i;
1668
1669 b += bytes;
1670
1671 for(i = 0; i < bytes; i++) {
Josh Coalson72943092002-05-22 05:35:43 +00001672 *(--b) = (FLAC__byte)(val & 0xff);
Josh Coalsoncae58732002-05-04 17:30:32 +00001673 val >>= 8;
1674 }
1675}
1676
1677FLAC__uint32 unpack_uint32_(FLAC__byte *b, unsigned bytes)
1678{
1679 FLAC__uint32 ret = 0;
1680 unsigned i;
1681
1682 for(i = 0; i < bytes; i++)
1683 ret = (ret << 8) | (FLAC__uint32)(*b++);
1684
1685 return ret;
1686}
1687
1688FLAC__uint32 unpack_uint32_little_endian_(FLAC__byte *b, unsigned bytes)
1689{
1690 FLAC__uint32 ret = 0;
1691 unsigned i;
1692
1693 b += bytes;
1694
1695 for(i = 0; i < bytes; i++)
1696 ret = (ret << 8) | (FLAC__uint32)(*--b);
1697
1698 return ret;
1699}
1700
1701FLAC__uint64 unpack_uint64_(FLAC__byte *b, unsigned bytes)
1702{
1703 FLAC__uint64 ret = 0;
1704 unsigned i;
1705
1706 for(i = 0; i < bytes; i++)
1707 ret = (ret << 8) | (FLAC__uint64)(*b++);
1708
1709 return ret;
1710}
1711
1712FLAC__bool read_metadata_block_header_(FLAC__MetaData_SimpleIterator *iterator)
1713{
Josh Coalsoneb643a22002-05-20 05:58:50 +00001714 FLAC__byte raw_header[FLAC__STREAM_METADATA_HEADER_LENGTH];
Josh Coalsoncae58732002-05-04 17:30:32 +00001715
1716 FLAC__ASSERT(0 != iterator);
1717 FLAC__ASSERT(0 != iterator->file);
1718
Josh Coalsoneb643a22002-05-20 05:58:50 +00001719 if(fread(raw_header, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, iterator->file) != FLAC__STREAM_METADATA_HEADER_LENGTH) {
Josh Coalsoncae58732002-05-04 17:30:32 +00001720 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1721 return false;
1722 }
1723
1724 iterator->is_last = raw_header[0] & 0x80? true : false;
1725 iterator->type = (FLAC__MetaDataType)(raw_header[0] & 0x7f);
1726 iterator->length = unpack_uint32_(raw_header + 1, 3);
1727
Josh Coalsona7b673c2002-05-25 02:13:38 +00001728 /* do some checking */
1729 if(iterator->type > FLAC__METADATA_TYPE_VORBIS_COMMENT) {
1730 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA;
1731 return false;
1732 }
1733
Josh Coalsoncae58732002-05-04 17:30:32 +00001734 return true;
1735}
1736
1737FLAC__bool read_metadata_block_data_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block)
1738{
1739 FLAC__ASSERT(0 != iterator);
1740 FLAC__ASSERT(0 != iterator->file);
1741
1742 switch(block->type) {
1743 case FLAC__METADATA_TYPE_STREAMINFO:
1744 iterator->status = read_metadata_block_data_streaminfo_(iterator->file, &block->data.stream_info);
1745 break;
1746 case FLAC__METADATA_TYPE_PADDING:
1747 iterator->status = read_metadata_block_data_padding_(iterator->file, &block->data.padding, block->length);
1748 break;
1749 case FLAC__METADATA_TYPE_APPLICATION:
1750 iterator->status = read_metadata_block_data_application_(iterator->file, &block->data.application, block->length);
1751 break;
1752 case FLAC__METADATA_TYPE_SEEKTABLE:
1753 iterator->status = read_metadata_block_data_seektable_(iterator->file, &block->data.seek_table, block->length);
1754 break;
1755 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
1756 iterator->status = read_metadata_block_data_vorbis_comment_(iterator->file, &block->data.vorbis_comment);
1757 break;
1758 default:
1759 FLAC__ASSERT(0);
1760 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR;
1761 }
1762
1763 return (iterator->status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK);
1764}
1765
1766FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_streaminfo_(FILE *file, FLAC__StreamMetaData_StreamInfo *block)
1767{
1768 FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH], *b;
1769
1770 FLAC__ASSERT(0 != file);
1771
1772 if(fread(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, file) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH)
1773 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1774
1775 b = buffer;
1776
1777 /* @@@ we are using hardcoded numbers for simplicity but we should
1778 * probably eventually write a bit-level unpacker and use the
1779 * _STREAMINFO_ constants.
1780 */
1781 block->min_blocksize = unpack_uint32_(b, 2); b += 2;
1782 block->max_blocksize = unpack_uint32_(b, 2); b += 2;
1783 block->min_framesize = unpack_uint32_(b, 3); b += 3;
1784 block->max_framesize = unpack_uint32_(b, 3); b += 3;
1785 block->sample_rate = (unpack_uint32_(b, 2) << 4) | ((unsigned)(b[2] & 0xf0) >> 4);
1786 block->channels = (unsigned)((b[2] & 0x0e) >> 1) + 1;
1787 block->bits_per_sample = ((((unsigned)(b[2] & 0x01)) << 1) | (((unsigned)(b[3] & 0xf0)) >> 4)) + 1;
1788 block->total_samples = (((FLAC__uint64)(b[3] & 0x0f)) << 32) | unpack_uint64_(b+4, 4);
1789 memcpy(block->md5sum, b+8, 16);
1790
1791 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1792}
1793
1794
1795FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_padding_(FILE *file, FLAC__StreamMetaData_Padding *block, unsigned block_length)
1796{
1797 FLAC__ASSERT(0 != file);
1798
1799 (void)block; /* nothing to do; we don't care about reading the padding bytes */
1800
1801 if(0 != fseek(file, block_length, SEEK_CUR))
1802 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
1803
1804 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1805}
1806
1807FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_application_(FILE *file, FLAC__StreamMetaData_Application *block, unsigned block_length)
1808{
1809 const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
1810
1811 FLAC__ASSERT(0 != file);
1812
1813 if(fread(block->id, 1, id_bytes, file) != id_bytes)
1814 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1815
1816 block_length -= id_bytes;
1817
Josh Coalson72943092002-05-22 05:35:43 +00001818 if(block_length == 0) {
1819 block->data = 0;
1820 }
1821 else {
1822 if(0 == (block->data = malloc(block_length)))
1823 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
Josh Coalsoncae58732002-05-04 17:30:32 +00001824
Josh Coalson72943092002-05-22 05:35:43 +00001825 if(fread(block->data, 1, block_length, file) != block_length)
1826 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1827 }
Josh Coalsoncae58732002-05-04 17:30:32 +00001828
1829 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1830}
1831
1832FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_seektable_(FILE *file, FLAC__StreamMetaData_SeekTable *block, unsigned block_length)
1833{
1834 unsigned i;
1835 FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH];
1836
1837 FLAC__ASSERT(0 != file);
1838 FLAC__ASSERT(block_length % FLAC__STREAM_METADATA_SEEKPOINT_LENGTH == 0);
1839
1840 block->num_points = block_length / FLAC__STREAM_METADATA_SEEKPOINT_LENGTH;
1841
Josh Coalson72943092002-05-22 05:35:43 +00001842 if(block->num_points == 0)
1843 block->points = 0;
1844 else if(0 == (block->points = malloc(block->num_points * sizeof(FLAC__StreamMetaData_SeekPoint))))
Josh Coalsoncae58732002-05-04 17:30:32 +00001845 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
1846
1847 for(i = 0; i < block->num_points; i++) {
1848 if(fread(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, file) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)
1849 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1850 /*@@@ some MAGIC NUMBERs here */
1851 block->points[i].sample_number = unpack_uint64_(buffer, 8);
1852 block->points[i].stream_offset = unpack_uint64_(buffer+8, 8);
1853 block->points[i].frame_samples = unpack_uint32_(buffer+16, 2);
1854 }
1855
1856 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1857}
1858
1859FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_entry_(FILE *file, FLAC__StreamMetaData_VorbisComment_Entry *entry)
1860{
1861 const unsigned entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8;
1862 FLAC__byte buffer[FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8];
1863
1864 FLAC__ASSERT(0 != file);
1865
1866 if(fread(buffer, 1, entry_length_len, file) != entry_length_len)
1867 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
Josh Coalsoneb643a22002-05-20 05:58:50 +00001868 entry->length = unpack_uint32_little_endian_(buffer, entry_length_len);
Josh Coalsoncae58732002-05-04 17:30:32 +00001869
Josh Coalson72943092002-05-22 05:35:43 +00001870 if(entry->length == 0) {
1871 entry->entry = 0;
1872 }
1873 else {
1874 if(0 == (entry->entry = malloc(entry->length)))
1875 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
Josh Coalsoncae58732002-05-04 17:30:32 +00001876
Josh Coalson72943092002-05-22 05:35:43 +00001877 if(fread(entry->entry, 1, entry->length, file) != entry->length)
1878 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1879 }
Josh Coalsoncae58732002-05-04 17:30:32 +00001880
1881 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1882}
1883
1884FLAC__MetaData_SimpleIteratorStatus read_metadata_block_data_vorbis_comment_(FILE *file, FLAC__StreamMetaData_VorbisComment *block)
1885{
1886 unsigned i;
1887 FLAC__MetaData_SimpleIteratorStatus status;
1888 const unsigned num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8;
1889 FLAC__byte buffer[FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8];
1890
1891 FLAC__ASSERT(0 != file);
1892
1893 if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_vorbis_comment_entry_(file, &(block->vendor_string))))
1894 return status;
1895
1896 if(fread(buffer, 1, num_comments_len, file) != num_comments_len)
1897 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
1898 block->num_comments = unpack_uint32_little_endian_(buffer, num_comments_len);
1899
Josh Coalson72943092002-05-22 05:35:43 +00001900 if(block->num_comments == 0) {
1901 block->comments = 0;
1902 }
1903 else if(0 == (block->comments = malloc(block->num_comments * sizeof(FLAC__StreamMetaData_VorbisComment_Entry))))
Josh Coalsoncae58732002-05-04 17:30:32 +00001904 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
1905
1906 for(i = 0; i < block->num_comments; i++) {
1907 if(FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK != (status = read_metadata_block_data_vorbis_comment_entry_(file, block->comments + i)))
1908 return status;
1909 }
1910
1911 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1912}
1913
1914FLAC__bool write_metadata_block_header_(FILE *file, FLAC__MetaData_SimpleIteratorStatus *status, const FLAC__StreamMetaData *block)
1915{
Josh Coalsoneb643a22002-05-20 05:58:50 +00001916 FLAC__byte buffer[FLAC__STREAM_METADATA_HEADER_LENGTH];
Josh Coalsoncae58732002-05-04 17:30:32 +00001917
1918 FLAC__ASSERT(0 != file);
1919 FLAC__ASSERT(0 != status);
1920 FLAC__ASSERT(block->length < (1u << FLAC__STREAM_METADATA_LENGTH_LEN));
1921
1922 buffer[0] = (block->is_last? 0x80 : 0) | (FLAC__byte)block->type;
1923 pack_uint32_(block->length, buffer + 1, 3);
1924
Josh Coalsoneb643a22002-05-20 05:58:50 +00001925 if(fwrite(buffer, 1, FLAC__STREAM_METADATA_HEADER_LENGTH, file) != FLAC__STREAM_METADATA_HEADER_LENGTH) {
Josh Coalsoncae58732002-05-04 17:30:32 +00001926 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
1927 return false;
1928 }
1929
1930 return true;
1931}
1932
1933FLAC__bool write_metadata_block_data_(FILE *file, FLAC__MetaData_SimpleIteratorStatus *status, const FLAC__StreamMetaData *block)
1934{
1935 FLAC__ASSERT(0 != file);
1936 FLAC__ASSERT(0 != status);
1937
1938 switch(block->type) {
1939 case FLAC__METADATA_TYPE_STREAMINFO:
1940 *status = write_metadata_block_data_streaminfo_(file, &block->data.stream_info);
1941 break;
1942 case FLAC__METADATA_TYPE_PADDING:
1943 *status = write_metadata_block_data_padding_(file, &block->data.padding, block->length);
1944 break;
1945 case FLAC__METADATA_TYPE_APPLICATION:
1946 *status = write_metadata_block_data_application_(file, &block->data.application, block->length);
1947 break;
1948 case FLAC__METADATA_TYPE_SEEKTABLE:
1949 *status = write_metadata_block_data_seektable_(file, &block->data.seek_table);
1950 break;
1951 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
1952 *status = write_metadata_block_data_vorbis_comment_(file, &block->data.vorbis_comment);
1953 break;
1954 default:
1955 FLAC__ASSERT(0);
1956 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR;
1957 }
1958 return (*status == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK);
1959}
1960
1961FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_streaminfo_(FILE *file, const FLAC__StreamMetaData_StreamInfo *block)
1962{
1963 FLAC__byte buffer[FLAC__STREAM_METADATA_STREAMINFO_LENGTH];
1964 const unsigned channels1 = block->channels - 1;
1965 const unsigned bps1 = block->bits_per_sample - 1;
1966
1967 FLAC__ASSERT(0 != file);
1968
1969 /* @@@ we are using hardcoded numbers for simplicity but we should
1970 * probably eventually write a bit-level packer and use the
1971 * _STREAMINFO_ constants.
1972 */
1973 pack_uint32_(block->min_blocksize, buffer, 2);
1974 pack_uint32_(block->max_blocksize, buffer+2, 2);
1975 pack_uint32_(block->min_framesize, buffer+4, 3);
1976 pack_uint32_(block->max_framesize, buffer+7, 3);
1977 buffer[10] = (block->sample_rate >> 12) & 0xff;
1978 buffer[11] = (block->sample_rate >> 4) & 0xff;
1979 buffer[12] = ((block->sample_rate & 0x0f) << 4) | (channels1 << 1) | (bps1 >> 4);
1980 buffer[13] = ((bps1 & 0x0f) << 4) | ((block->total_samples >> 32) & 0x0f);
1981 pack_uint32_((FLAC__uint32)block->total_samples, buffer+14, 4);
1982 memcpy(buffer+18, block->md5sum, 16);
1983
1984 if(fwrite(buffer, 1, FLAC__STREAM_METADATA_STREAMINFO_LENGTH, file) != FLAC__STREAM_METADATA_STREAMINFO_LENGTH)
1985 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
1986
1987 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
1988}
1989
1990FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_padding_(FILE *file, const FLAC__StreamMetaData_Padding *block, unsigned block_length)
1991{
1992 unsigned i, n = block_length;
1993 FLAC__byte buffer[1024];
1994
1995 FLAC__ASSERT(0 != file);
1996
1997 (void)block;
1998
1999 memset(buffer, 0, 1024);
2000
2001 for(i = 0; i < n/1024; i++)
2002 if(fwrite(buffer, 1, 1024, file) != 1024)
2003 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2004
2005 n %= 1024;
2006
2007 if(fwrite(buffer, 1, n, file) != n)
2008 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2009
2010 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
2011}
2012
2013FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_application_(FILE *file, const FLAC__StreamMetaData_Application *block, unsigned block_length)
2014{
2015 const unsigned id_bytes = FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8;
2016
2017 FLAC__ASSERT(0 != file);
2018
2019 if(fwrite(block->id, 1, id_bytes, file) != id_bytes)
2020 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2021
2022 block_length -= id_bytes;
2023
2024 if(fwrite(block->data, 1, block_length, file) != block_length)
2025 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2026
2027 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
2028}
2029
2030FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_seektable_(FILE *file, const FLAC__StreamMetaData_SeekTable *block)
2031{
2032 unsigned i;
2033 FLAC__byte buffer[FLAC__STREAM_METADATA_SEEKPOINT_LENGTH];
2034
2035 FLAC__ASSERT(0 != file);
2036
2037 for(i = 0; i < block->num_points; i++) {
2038 /*@@@ some MAGIC NUMBERs here */
2039 pack_uint64_(block->points[i].sample_number, buffer, 8);
2040 pack_uint64_(block->points[i].stream_offset, buffer+8, 8);
2041 pack_uint32_(block->points[i].frame_samples, buffer+16, 2);
2042 if(fwrite(buffer, 1, FLAC__STREAM_METADATA_SEEKPOINT_LENGTH, file) != FLAC__STREAM_METADATA_SEEKPOINT_LENGTH)
2043 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2044 }
2045
2046 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
2047}
2048
2049FLAC__MetaData_SimpleIteratorStatus write_metadata_block_data_vorbis_comment_(FILE *file, const FLAC__StreamMetaData_VorbisComment *block)
2050{
2051 unsigned i;
2052 const unsigned entry_length_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN / 8;
2053 const unsigned num_comments_len = FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN / 8;
2054 FLAC__byte buffer[max(FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN, FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN) / 8];
2055
2056 FLAC__ASSERT(0 != file);
2057
2058 pack_uint32_little_endian_(block->vendor_string.length, buffer, entry_length_len);
2059 if(fwrite(buffer, 1, entry_length_len, file) != entry_length_len)
2060 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2061 if(fwrite(block->vendor_string.entry, 1, block->vendor_string.length, file) != block->vendor_string.length)
2062 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2063
2064 pack_uint32_little_endian_(block->num_comments, buffer, num_comments_len);
2065 if(fwrite(buffer, 1, num_comments_len, file) != num_comments_len)
2066 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2067
2068 for(i = 0; i < block->num_comments; i++) {
2069 pack_uint32_little_endian_(block->comments[i].length, buffer, entry_length_len);
2070 if(fwrite(buffer, 1, entry_length_len, file) != entry_length_len)
2071 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2072 if(fwrite(block->comments[i].entry, 1, block->comments[i].length, file) != block->comments[i].length)
2073 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2074 }
2075
2076 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK;
2077}
2078
2079FLAC__bool write_metadata_block_stationary_(FLAC__MetaData_SimpleIterator *iterator, const FLAC__StreamMetaData *block)
2080{
2081 if(0 != fseek(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
2082 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2083 return false;
2084 }
2085
2086 if(!write_metadata_block_header_(iterator->file, &iterator->status, block))
2087 return false;
2088
2089 if(!write_metadata_block_data_(iterator->file, &iterator->status, block))
2090 return false;
2091
2092 if(0 != fseek(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
2093 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2094 return false;
2095 }
2096
2097 return read_metadata_block_header_(iterator);
2098}
2099
2100FLAC__bool write_metadata_block_stationary_with_padding_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, unsigned padding_length, FLAC__bool padding_is_last)
2101{
2102 FLAC__StreamMetaData *padding;
2103
2104 if(0 != fseek(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
2105 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2106 return false;
2107 }
2108
2109 block->is_last = false;
2110
2111 if(!write_metadata_block_header_(iterator->file, &iterator->status, block))
2112 return false;
2113
2114 if(!write_metadata_block_data_(iterator->file, &iterator->status, block))
2115 return false;
2116
2117 if(0 == (padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING)))
2118 return FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
2119
2120 padding->is_last = padding_is_last;
2121 padding->length = padding_length;
2122
2123 if(!write_metadata_block_header_(iterator->file, &iterator->status, padding)) {
2124 FLAC__metadata_object_delete(padding);
2125 return false;
2126 }
2127
2128 if(!write_metadata_block_data_(iterator->file, &iterator->status, padding)) {
2129 FLAC__metadata_object_delete(padding);
2130 return false;
2131 }
2132
2133 FLAC__metadata_object_delete(padding);
2134
2135 if(0 != fseek(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
2136 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2137 return false;
2138 }
2139
2140 return read_metadata_block_header_(iterator);
2141}
2142
2143FLAC__bool rewrite_whole_file_(FLAC__MetaData_SimpleIterator *iterator, FLAC__StreamMetaData *block, FLAC__bool append)
2144{
2145 FILE *tempfile;
2146 char *tempfilename;
Josh Coalson6b02a752002-05-10 06:42:02 +00002147 int fixup_is_last_code = 0; /* 0 => no need to change any is_last flags */
2148 long fixup_is_last_flag_offset = -1;
Josh Coalsoncae58732002-05-04 17:30:32 +00002149
Josh Coalson51ee4652002-05-09 05:46:23 +00002150 FLAC__ASSERT(0 != block || append == false);
2151
Josh Coalson6b02a752002-05-10 06:42:02 +00002152 if(iterator->is_last) {
2153 if(append) {
2154 fixup_is_last_code = 1; /* 1 => clear the is_last flag at the following offset */
2155 fixup_is_last_flag_offset = iterator->offset[iterator->depth];
2156 }
2157 else if(0 == block) {
2158 simple_iterator_push_(iterator);
2159 if(!FLAC__metadata_simple_iterator_prev(iterator)) {
2160 (void)simple_iterator_pop_(iterator);
2161 return false;
2162 }
2163 fixup_is_last_code = -1; /* -1 => set the is_last the flag at the following offset */
2164 fixup_is_last_flag_offset = iterator->offset[iterator->depth];
2165 if(!simple_iterator_pop_(iterator))
2166 return false;
2167 }
2168 }
2169
Josh Coalsoncae58732002-05-04 17:30:32 +00002170 if(!simple_iterator_copy_file_prefix_(iterator, &tempfile, &tempfilename, append))
2171 return false;
2172
2173 if(0 != block) {
2174 if(!write_metadata_block_header_(tempfile, &iterator->status, block)) {
2175 cleanup_tempfile_(&tempfile, &tempfilename);
2176 return false;
2177 }
2178
2179 if(!write_metadata_block_data_(tempfile, &iterator->status, block)) {
2180 cleanup_tempfile_(&tempfile, &tempfilename);
2181 return false;
2182 }
2183 }
2184
Josh Coalson6b02a752002-05-10 06:42:02 +00002185 if(!simple_iterator_copy_file_postfix_(iterator, &tempfile, &tempfilename, fixup_is_last_code, fixup_is_last_flag_offset, block==0))
Josh Coalsoncae58732002-05-04 17:30:32 +00002186 return false;
2187
Josh Coalson51ee4652002-05-09 05:46:23 +00002188 if(append)
2189 return FLAC__metadata_simple_iterator_next(iterator);
2190
Josh Coalsoncae58732002-05-04 17:30:32 +00002191 return true;
2192}
2193
2194FLAC__bool chain_rewrite_chain_(FLAC__MetaData_Chain *chain)
2195{
2196 FILE *f;
2197 FLAC__MetaData_Node *node;
2198 FLAC__MetaData_SimpleIteratorStatus status;
2199
2200 FLAC__ASSERT(0 != chain);
2201 FLAC__ASSERT(0 != chain->filename);
2202 FLAC__ASSERT(0 != chain->head);
2203
2204 if(0 == (f = fopen(chain->filename, "r+b"))) {
2205 chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
2206 return false;
2207 }
2208 if(0 != fseek(f, chain->first_offset, SEEK_SET)) {
2209 chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
2210 return false;
2211 }
2212
2213 for(node = chain->head; node; node = node->next) {
2214 if(!write_metadata_block_header_(f, &status, node->data)) {
2215 chain->status = get_equivalent_status_(status);
2216 return false;
2217 }
2218 if(!write_metadata_block_data_(f, &status, node->data)) {
2219 chain->status = get_equivalent_status_(status);
2220 return false;
2221 }
2222 }
2223
2224 /*FLAC__ASSERT(fflush(), ftell() == chain->last_offset);*/
2225
2226 (void)fclose(f);
2227
2228 return true;
2229}
2230
2231FLAC__bool chain_rewrite_file_(FLAC__MetaData_Chain *chain, const char *tempfile_path_prefix)
2232{
2233 FILE *f, *tempfile;
2234 char *tempfilename;
2235 FLAC__MetaData_SimpleIteratorStatus status;
2236 const FLAC__MetaData_Node *node;
2237
2238 FLAC__ASSERT(0 != chain);
2239 FLAC__ASSERT(0 != chain->filename);
2240 FLAC__ASSERT(0 != chain->head);
2241
2242 /* copy the file prefix (data up to first metadata block */
2243 if(0 == (f = fopen(chain->filename, "rb"))) {
2244 chain->status = FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
2245 return false;
2246 }
2247 if(!open_tempfile_(chain->filename, tempfile_path_prefix, &tempfile, &tempfilename, &status)) {
2248 chain->status = get_equivalent_status_(status);
2249 cleanup_tempfile_(&tempfile, &tempfilename);
2250 return false;
2251 }
2252 if(!copy_n_bytes_from_file_(f, tempfile, chain->first_offset, &status)) {
2253 chain->status = get_equivalent_status_(status);
2254 cleanup_tempfile_(&tempfile, &tempfilename);
2255 return false;
2256 }
2257
2258 /* write the metadata */
2259 for(node = chain->head; node; node = node->next) {
2260 if(!write_metadata_block_header_(tempfile, &status, node->data)) {
2261 chain->status = get_equivalent_status_(status);
2262 return false;
2263 }
2264 if(!write_metadata_block_data_(tempfile, &status, node->data)) {
2265 chain->status = get_equivalent_status_(status);
2266 return false;
2267 }
2268 }
2269 /*FLAC__ASSERT(fflush(), ftell() == chain->last_offset);*/
2270
2271 /* copy the file postfix (everything after the metadata) */
2272 if(0 != fseek(f, chain->last_offset, SEEK_SET)) {
2273 cleanup_tempfile_(&tempfile, &tempfilename);
2274 chain->status = FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
2275 return false;
2276 }
2277 if(!copy_remaining_bytes_from_file_(f, tempfile, &status)) {
2278 cleanup_tempfile_(&tempfile, &tempfilename);
2279 chain->status = get_equivalent_status_(status);
2280 return false;
2281 }
2282
2283 /* move the tempfile on top of the original */
2284 (void)fclose(f);
2285 if(!transport_tempfile_(chain->filename, &tempfile, &tempfilename, &status))
2286 return false;
2287
2288 return true;
2289}
2290
2291void simple_iterator_push_(FLAC__MetaData_SimpleIterator *iterator)
2292{
2293 FLAC__ASSERT(iterator->depth+1 < SIMPLE_ITERATOR_MAX_PUSH_DEPTH);
2294 iterator->offset[iterator->depth+1] = iterator->offset[iterator->depth];
2295 iterator->depth++;
2296}
2297
2298FLAC__bool simple_iterator_pop_(FLAC__MetaData_SimpleIterator *iterator)
2299{
2300 FLAC__ASSERT(iterator->depth > 0);
2301 iterator->depth--;
2302 if(0 != fseek(iterator->file, iterator->offset[iterator->depth], SEEK_SET)) {
2303 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2304 return false;
2305 }
2306
2307 return read_metadata_block_header_(iterator);
2308}
2309
2310unsigned seek_to_first_metadata_block_(FILE *f)
2311{
Josh Coalsona7b673c2002-05-25 02:13:38 +00002312 FLAC__byte buffer[4];
2313 size_t n;
2314 unsigned i;
Josh Coalsoncae58732002-05-04 17:30:32 +00002315
2316 FLAC__ASSERT(0 != f);
Josh Coalsona7b673c2002-05-25 02:13:38 +00002317 FLAC__ASSERT(FLAC__STREAM_SYNC_LENGTH == 4);
Josh Coalsoncae58732002-05-04 17:30:32 +00002318
Josh Coalsona7b673c2002-05-25 02:13:38 +00002319 /* skip any id3v2 tag */
2320 errno = 0;
2321 n = fread(buffer, 1, 4, f);
2322 if(errno)
Josh Coalsoncae58732002-05-04 17:30:32 +00002323 return 1;
Josh Coalsona7b673c2002-05-25 02:13:38 +00002324 else if(n != 4)
2325 return 2;
2326 else if(0 == memcmp(buffer, "ID3", 3)) {
2327 unsigned tag_length = 0;
2328
2329 /* skip to the tag length */
2330 if(fseek(f, 2, SEEK_CUR) < 0)
2331 return 1;
2332
2333 /* read the length */
2334 for(i = 0; i < 4; i++) {
2335 if(fread(buffer, 1, 1, f) < 1 || buffer[0] & 0x80)
2336 return 1;
2337 tag_length <<= 7;
2338 tag_length |= (buffer[0] & 0x7f);
2339 }
2340
2341 /* skip the rest of the tag */
2342 if(fseek(f, tag_length, SEEK_CUR) < 0)
2343 return 1;
2344
2345 /* read the stream sync code */
2346 errno = 0;
2347 n = fread(buffer, 1, 4, f);
2348 if(errno)
2349 return 1;
2350 else if(n != 4)
2351 return 2;
Josh Coalsoncae58732002-05-04 17:30:32 +00002352 }
Josh Coalsona7b673c2002-05-25 02:13:38 +00002353
2354 /* check for the fLaC signature */
2355 if(0 == memcmp(FLAC__STREAM_SYNC_STRING, buffer, FLAC__STREAM_SYNC_LENGTH))
2356 return 0;
2357 else
2358 return 2;
Josh Coalsoncae58732002-05-04 17:30:32 +00002359}
2360
2361FLAC__bool simple_iterator_copy_file_prefix_(FLAC__MetaData_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, FLAC__bool append)
2362{
Josh Coalson72943092002-05-22 05:35:43 +00002363 const long offset_end = append? iterator->offset[iterator->depth] + (long)FLAC__STREAM_METADATA_HEADER_LENGTH + (long)iterator->length : iterator->offset[iterator->depth];
Josh Coalsoncae58732002-05-04 17:30:32 +00002364
2365 if(0 != fseek(iterator->file, 0, SEEK_SET)) {
2366 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2367 return false;
2368 }
2369 if(!open_tempfile_(iterator->filename, iterator->tempfile_path_prefix, tempfile, tempfilename, &iterator->status)) {
2370 cleanup_tempfile_(tempfile, tempfilename);
2371 return false;
2372 }
2373 if(!copy_n_bytes_from_file_(iterator->file, *tempfile, offset_end, &iterator->status)) {
2374 cleanup_tempfile_(tempfile, tempfilename);
2375 return false;
2376 }
2377
2378 return true;
2379}
2380
Josh Coalson6b02a752002-05-10 06:42:02 +00002381FLAC__bool simple_iterator_copy_file_postfix_(FLAC__MetaData_SimpleIterator *iterator, FILE **tempfile, char **tempfilename, int fixup_is_last_code, long fixup_is_last_flag_offset, FLAC__bool backup)
Josh Coalsoncae58732002-05-04 17:30:32 +00002382{
2383 long save_offset = iterator->offset[iterator->depth]; /*@@@ 2G limit */
2384 FLAC__ASSERT(0 != *tempfile);
2385
Josh Coalsoneb643a22002-05-20 05:58:50 +00002386 if(0 != fseek(iterator->file, save_offset + FLAC__STREAM_METADATA_HEADER_LENGTH + iterator->length, SEEK_SET)) {
Josh Coalsoncae58732002-05-04 17:30:32 +00002387 cleanup_tempfile_(tempfile, tempfilename);
2388 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2389 return false;
2390 }
2391 if(!copy_remaining_bytes_from_file_(iterator->file, *tempfile, &iterator->status)) {
2392 cleanup_tempfile_(tempfile, tempfilename);
2393 return false;
2394 }
2395
Josh Coalson6b02a752002-05-10 06:42:02 +00002396 if(fixup_is_last_code != 0) {
2397 /*
2398 * if code == 1, it means a block was appended to the end so
2399 * we have to clear the is_last flag of the previous block
2400 * if code == -1, it means the last block was deleted so
2401 * we have to set the is_last flag of the previous block
2402 */
Josh Coalson51ee4652002-05-09 05:46:23 +00002403 /*@@@ MAGIC NUMBERs here; we know the is_last flag is the high bit of the byte at this location */
Josh Coalsoncae58732002-05-04 17:30:32 +00002404 FLAC__byte x;
2405 if(0 != fseek(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) {
2406 cleanup_tempfile_(tempfile, tempfilename);
2407 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2408 return false;
2409 }
2410 if(fread(&x, 1, 1, *tempfile) != 1) {
2411 cleanup_tempfile_(tempfile, tempfilename);
2412 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
2413 return false;
2414 }
Josh Coalson6b02a752002-05-10 06:42:02 +00002415 if(fixup_is_last_code > 0) {
2416 FLAC__ASSERT(x & 0x80);
2417 x &= 0x7f;
2418 }
2419 else {
2420 FLAC__ASSERT(!(x & 0x80));
2421 x |= 0x80;
2422 }
Josh Coalsoncae58732002-05-04 17:30:32 +00002423 if(0 != fseek(*tempfile, fixup_is_last_flag_offset, SEEK_SET)) {
2424 cleanup_tempfile_(tempfile, tempfilename);
2425 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR;
2426 return false;
2427 }
2428 if(fwrite(&x, 1, 1, *tempfile) != 1) {
2429 cleanup_tempfile_(tempfile, tempfilename);
2430 iterator->status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2431 return false;
2432 }
2433 }
2434
2435 (void)fclose(iterator->file);
2436
2437 if(!transport_tempfile_(iterator->filename, tempfile, tempfilename, &iterator->status))
2438 return false;
2439
2440 if(iterator->has_stats)
2441 set_file_stats_(iterator->filename, &iterator->stats);
2442
2443 if(!simple_iterator_prime_input_(iterator))
2444 return false;
2445 if(backup) {
Josh Coalsoneb643a22002-05-20 05:58:50 +00002446 while(iterator->offset[iterator->depth] + (long)FLAC__STREAM_METADATA_HEADER_LENGTH + (long)iterator->length < save_offset)
Josh Coalsoncae58732002-05-04 17:30:32 +00002447 if(!FLAC__metadata_simple_iterator_next(iterator))
2448 return false;
2449 return true;
2450 }
2451 else {
2452 /* move the iterator to it's original block faster by faking a push, then doing a pop_ */
2453 FLAC__ASSERT(iterator->depth == 0);
2454 iterator->offset[0] = save_offset;
2455 iterator->depth++;
2456 return simple_iterator_pop_(iterator);
2457 }
2458}
2459
2460FLAC__bool copy_n_bytes_from_file_(FILE *file, FILE *tempfile, unsigned bytes/*@@@ 4G limit*/, FLAC__MetaData_SimpleIteratorStatus *status)
2461{
2462 FLAC__byte buffer[8192];
2463 unsigned n;
2464
2465 while(bytes > 0) {
2466 n = min(sizeof(buffer), bytes);
2467 if(fread(buffer, 1, n, file) != n) {
2468 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
2469 return false;
2470 }
2471 if(fwrite(buffer, 1, n, tempfile) != n) {
2472 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2473 return false;
2474 }
2475 bytes -= n;
2476 }
2477
2478 return true;
2479}
2480
2481FLAC__bool copy_remaining_bytes_from_file_(FILE *file, FILE *tempfile, FLAC__MetaData_SimpleIteratorStatus *status)
2482{
2483 FLAC__byte buffer[8192];
2484 size_t n;
2485
2486 while(!feof(file)) {
2487 n = fread(buffer, 1, sizeof(buffer), file);
2488 if(n == 0 && !feof(file)) {
2489 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR;
2490 return false;
2491 }
2492 if(n > 0 && fwrite(buffer, 1, n, tempfile) != n) {
2493 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR;
2494 return false;
2495 }
2496 }
2497
2498 return true;
2499}
2500
2501FLAC__bool open_tempfile_(const char *filename, const char *tempfile_path_prefix, FILE **tempfile, char **tempfilename, FLAC__MetaData_SimpleIteratorStatus *status)
2502{
2503 static const char *tempfile_suffix = ".metadata_edit";
2504 if(0 == tempfile_path_prefix) {
2505 if(0 == (*tempfilename = malloc(strlen(filename) + strlen(tempfile_suffix) + 1))) {
2506 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
2507 return false;
2508 }
2509 strcpy(*tempfilename, filename);
2510 strcat(*tempfilename, tempfile_suffix);
2511 }
2512 else {
2513 const char *p = strrchr(filename, '/');
2514 if(0 == p)
2515 p = filename;
2516 else
2517 p++;
2518
2519 if(0 == (*tempfilename = malloc(strlen(tempfile_path_prefix) + 1 + strlen(p) + strlen(tempfile_suffix) + 1))) {
2520 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR;
2521 return false;
2522 }
2523 strcpy(*tempfilename, tempfile_path_prefix);
2524 strcat(*tempfilename, "/");
2525 strcat(*tempfilename, p);
2526 strcat(*tempfilename, tempfile_suffix);
2527 }
2528
Josh Coalson51ee4652002-05-09 05:46:23 +00002529 if(0 == (*tempfile = fopen(*tempfilename, "w+b"))) {
Josh Coalsoncae58732002-05-04 17:30:32 +00002530 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE;
2531 return false;
2532 }
2533
2534 return true;
2535}
2536
2537FLAC__bool transport_tempfile_(const char *filename, FILE **tempfile, char **tempfilename, FLAC__MetaData_SimpleIteratorStatus *status)
2538{
2539 FLAC__ASSERT(0 != filename);
2540 FLAC__ASSERT(0 != tempfile);
2541 FLAC__ASSERT(0 != *tempfile);
2542 FLAC__ASSERT(0 != tempfilename);
2543 FLAC__ASSERT(0 != *tempfilename);
2544 FLAC__ASSERT(0 != status);
2545
2546 (void)fclose(*tempfile);
2547 *tempfile = 0;
2548 /*@@@ to fully support the tempfile_path_prefix we need to update this piece to actually copy across filesystems instead of just rename(): */
2549 if(0 != rename(*tempfilename, filename)) {
2550 cleanup_tempfile_(tempfile, tempfilename);
2551 *status = FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR;
2552 return false;
2553 }
2554
2555 cleanup_tempfile_(tempfile, tempfilename);
2556
2557 return true;
2558}
2559
2560void cleanup_tempfile_(FILE **tempfile, char **tempfilename)
2561{
2562 if(0 != *tempfile) {
2563 (void)fclose(*tempfile);
2564 *tempfile = 0;
2565 }
2566
2567 if(0 != *tempfilename) {
2568 (void)unlink(*tempfilename);
2569 free(*tempfilename);
2570 *tempfilename = 0;
2571 }
2572}
2573
2574FLAC__bool get_file_stats_(const char *filename, struct stat *stats)
2575{
2576 FLAC__ASSERT(0 != filename);
2577 FLAC__ASSERT(0 != stats);
2578 return (0 == stat(filename, stats));
2579}
2580
2581void set_file_stats_(const char *filename, struct stat *stats)
2582{
2583 struct utimbuf srctime;
2584
2585 FLAC__ASSERT(0 != filename);
2586 FLAC__ASSERT(0 != stats);
2587
2588 srctime.actime = stats->st_atime;
2589 srctime.modtime = stats->st_mtime;
2590 (void)chmod(filename, stats->st_mode);
2591 (void)utime(filename, &srctime);
2592#if !defined _MSC_VER && !defined __MINGW32__
2593 (void)chown(filename, stats->st_uid, -1);
2594 (void)chown(filename, -1, stats->st_gid);
2595#endif
2596}
2597
2598FLAC__MetaData_ChainStatus get_equivalent_status_(FLAC__MetaData_SimpleIteratorStatus status)
2599{
2600 switch(status) {
2601 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK:
2602 return FLAC__METADATA_CHAIN_STATUS_OK;
2603 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ILLEGAL_INPUT:
2604 return FLAC__METADATA_CHAIN_STATUS_ILLEGAL_INPUT;
2605 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_ERROR_OPENING_FILE:
2606 return FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE;
2607 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_A_FLAC_FILE:
2608 return FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE;
2609 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_NOT_WRITABLE:
2610 return FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE;
Josh Coalsona7b673c2002-05-25 02:13:38 +00002611 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_BAD_METADATA:
2612 return FLAC__METADATA_CHAIN_STATUS_BAD_METADATA;
Josh Coalsoncae58732002-05-04 17:30:32 +00002613 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_READ_ERROR:
2614 return FLAC__METADATA_CHAIN_STATUS_READ_ERROR;
2615 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_SEEK_ERROR:
2616 return FLAC__METADATA_CHAIN_STATUS_SEEK_ERROR;
2617 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_WRITE_ERROR:
2618 return FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR;
2619 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_RENAME_ERROR:
2620 return FLAC__METADATA_CHAIN_STATUS_RENAME_ERROR;
2621 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_UNLINK_ERROR:
2622 return FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR;
2623 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_MEMORY_ALLOCATION_ERROR:
2624 return FLAC__METADATA_CHAIN_STATUS_MEMORY_ALLOCATION_ERROR;
2625 case FLAC__METADATA_SIMPLE_ITERATOR_STATUS_INTERNAL_ERROR:
2626 default:
2627 return FLAC__METADATA_CHAIN_STATUS_INTERNAL_ERROR;
2628 }
2629}
Josh Coalsona7b673c2002-05-25 02:13:38 +00002630#if 0
2631@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2632@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2633@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2634@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2635@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2636@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2637@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2638@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2639@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2640@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
2641FLAC__bool list(FILE *f, FLAC__bool verbose)
2642{
2643 FLAC__byte buf[65536];
2644 FLAC__byte *b = buf;
2645 FLAC__StreamMetaData metadata;
2646 unsigned blocknum = 0, byte_offset = 0, i;
2647
2648 /* skip any id3v2 tag */
2649 if(fread(buf, 1, 4, f) < 4) {
2650 fprintf(stderr, "ERROR: not a FLAC file\n");
2651 return false;
2652 }
2653 if(0 == memcmp(buf, "ID3", 3)) {
2654 unsigned tag_length = 0;
2655
2656 /* skip to the tag length */
2657 if(fseek(f, 2, SEEK_CUR) < 0) {
2658 fprintf(stderr, "ERROR: bad ID3v2 tag\n");
2659 return false;
2660 }
2661
2662 /* read the length */
2663 for(i = 0; i < 4; i++) {
2664 if(fread(buf, 1, 1, f) < 1 || buf[0] & 0x80) {
2665 fprintf(stderr, "ERROR: bad ID3v2 tag\n");
2666 return false;
2667 }
2668 tag_length <<= 7;
2669 tag_length |= (buf[0] & 0x7f);
2670 }
2671
2672 /* skip the rest of the tag */
2673 if(fseek(f, tag_length, SEEK_CUR) < 0) {
2674 fprintf(stderr, "ERROR: bad ID3v2 tag\n");
2675 return false;
2676 }
2677
2678 /* read the stream sync code */
2679 if(fread(buf, 1, 4, f) < 4) {
2680 fprintf(stderr, "ERROR: not a FLAC file (no '%s' header)\n", sync_string_);
2681 return false;
2682 }
2683 }
2684
2685 /* check the stream sync code */
2686 if(memcmp(buf, sync_string_, 4)) {
2687 fprintf(stderr, "ERROR: not a FLAC file (no '%s' header)\n", sync_string_);
2688 return false;
2689 }
2690 byte_offset += 4;
2691
2692 /* read the metadata blocks */
2693 do {
2694 /* read the metadata block header */
2695 if(fread(buf, 1, 4, f) < 4) {
2696 fprintf(stderr, "ERROR: short count reading metadata block header\n");
2697 return false;
2698 }
2699 metadata.is_last = (buf[0] & 0x80)? true:false;
2700 metadata.type = (FLAC__MetaDataType)(buf[0] & 0x7f);
2701 metadata.length = unpack_uint32(buf+1, 3);
2702
2703 /* print header */
2704 printf("METADATA block #%u\n", blocknum);
2705 printf(" byte offset: %u\n", byte_offset);
2706 printf(" type: %u (%s)\n", (unsigned)metadata.type, metadata.type<=FLAC__METADATA_TYPE_SEEKTABLE? metadata_type_string_[metadata.type] : "UNKNOWN");
2707 printf(" is last: %s\n", metadata.is_last? "true":"false");
2708 printf(" length: %u\n", metadata.length);
2709
2710 if(metadata.length > sizeof(buf)) {
2711 printf(" SKIPPING large block\n");
2712 if(fseek(f, metadata.length, SEEK_CUR) < 0) {
2713 fprintf(stderr, "ERROR: short count skipping metadata block data\n");
2714 return false;
2715 }
2716 continue;
2717 }
2718
2719 /* read the metadata block data */
2720 if(fread(buf, 1, metadata.length, f) < metadata.length) {
2721 fprintf(stderr, "ERROR: short count reading metadata block data\n");
2722 return false;
2723 }
2724 switch(metadata.type) {
2725 case FLAC__METADATA_TYPE_STREAMINFO:
2726 b = buf;
2727 metadata.data.stream_info.min_blocksize = unpack_uint32(b, 2); b += 2;
2728 metadata.data.stream_info.max_blocksize = unpack_uint32(b, 2); b += 2;
2729 metadata.data.stream_info.min_framesize = unpack_uint32(b, 3); b += 3;
2730 metadata.data.stream_info.max_framesize = unpack_uint32(b, 3); b += 3;
2731 metadata.data.stream_info.sample_rate = (unpack_uint32(b, 2) << 4) | ((unsigned)(b[2] & 0xf0) >> 4);
2732 metadata.data.stream_info.channels = (unsigned)((b[2] & 0x0e) >> 1) + 1;
2733 metadata.data.stream_info.bits_per_sample = ((((unsigned)(b[2] & 0x01)) << 1) | (((unsigned)(b[3] & 0xf0)) >> 4)) + 1;
2734 metadata.data.stream_info.total_samples = (((FLAC__uint64)(b[3] & 0x0f)) << 32) | unpack_uint64(b+4, 4);
2735 memcpy(metadata.data.stream_info.md5sum, b+8, 16);
2736 break;
2737 case FLAC__METADATA_TYPE_PADDING:
2738 if(verbose) {
2739 /* dump contents */
2740 }
2741 break;
2742 case FLAC__METADATA_TYPE_APPLICATION:
2743 memcpy(metadata.data.application.id, buf, 4);
2744 metadata.data.application.data = buf+4;
2745 break;
2746 case FLAC__METADATA_TYPE_SEEKTABLE:
2747 metadata.data.seek_table.num_points = metadata.length / SEEKPOINT_LEN_;
2748 b = buf; /* we leave the points in buf for printing later */
2749 break;
2750 default:
2751 printf("SKIPPING block of unknown type\n");
2752 continue;
2753 }
2754
2755 /* print data */
2756 switch(metadata.type) {
2757 case FLAC__METADATA_TYPE_STREAMINFO:
2758 printf(" minumum blocksize: %u samples\n", metadata.data.stream_info.min_blocksize);
2759 printf(" maximum blocksize: %u samples\n", metadata.data.stream_info.max_blocksize);
2760 printf(" minimum framesize: %u bytes\n", metadata.data.stream_info.min_framesize);
2761 printf(" maximum framesize: %u bytes\n", metadata.data.stream_info.max_framesize);
2762 printf(" sample_rate: %u Hz\n", metadata.data.stream_info.sample_rate);
2763 printf(" channels: %u\n", metadata.data.stream_info.channels);
2764 printf(" bits-per-sample: %u\n", metadata.data.stream_info.bits_per_sample);
2765 printf(" total samples: %llu\n", metadata.data.stream_info.total_samples);
2766 printf(" MD5 signature: ");
2767 for(i = 0; i < 16; i++)
2768 printf("%02x", metadata.data.stream_info.md5sum[i]);
2769 printf("\n");
2770 break;
2771 case FLAC__METADATA_TYPE_PADDING:
2772 if(verbose) {
2773 printf(" pad contents:\n");
2774 hexdump(buf, metadata.length, " ");
2775 }
2776 break;
2777 case FLAC__METADATA_TYPE_APPLICATION:
2778 printf(" application ID: ");
2779 for(i = 0; i < 4; i++)
2780 printf("%02x", metadata.data.application.id[i]);
2781 printf("\n");
2782 if(verbose) {
2783 printf(" data contents:\n");
2784 hexdump(metadata.data.application.data, metadata.length, " ");
2785 }
2786 break;
2787 case FLAC__METADATA_TYPE_SEEKTABLE:
2788 printf(" seek points: %u\n", metadata.data.seek_table.num_points);
2789 if(verbose) {
2790 for(i = 0; i < metadata.data.seek_table.num_points; i++, b += SEEKPOINT_LEN_)
2791 printf(" point %d: sample_number=%llu, stream_offset=%llu, frame_samples=%u\n", i, unpack_uint64(b, 8), unpack_uint64(b+8, 8), unpack_uint32(b+16, 2));
2792 }
2793 break;
2794 default:
2795 FLAC__ASSERT(0);
2796 }
2797
2798 blocknum++;
2799 byte_offset += (4 + metadata.length);
2800 } while (!metadata.is_last);
2801
2802 return true;
2803}
2804
2805FLAC__uint32 unpack_uint32(FLAC__byte *b, unsigned bytes)
2806{
2807 FLAC__uint32 ret = 0;
2808 unsigned i;
2809
2810 for(i = 0; i < bytes; i++)
2811 ret = (ret << 8) | (FLAC__uint32)(*b++);
2812
2813 return ret;
2814}
2815
2816FLAC__uint64 unpack_uint64(FLAC__byte *b, unsigned bytes)
2817{
2818 FLAC__uint64 ret = 0;
2819 unsigned i;
2820
2821 for(i = 0; i < bytes; i++)
2822 ret = (ret << 8) | (FLAC__uint64)(*b++);
2823
2824 return ret;
2825}
2826
2827void hexdump(const FLAC__byte *buf, unsigned bytes, const char *indent)
2828{
2829 unsigned i, left = bytes;
2830 const FLAC__byte *b = buf;
2831
2832 for(i = 0; i < bytes; i += 16) {
2833 printf("%s%08X: "
2834 "%02X %02X %02X %02X %02X %02X %02X %02X "
2835 "%02X %02X %02X %02X %02X %02X %02X %02X "
2836 "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
2837 indent, i,
2838 left > 0? (unsigned char)b[ 0] : 0,
2839 left > 1? (unsigned char)b[ 1] : 0,
2840 left > 2? (unsigned char)b[ 2] : 0,
2841 left > 3? (unsigned char)b[ 3] : 0,
2842 left > 4? (unsigned char)b[ 4] : 0,
2843 left > 5? (unsigned char)b[ 5] : 0,
2844 left > 6? (unsigned char)b[ 6] : 0,
2845 left > 7? (unsigned char)b[ 7] : 0,
2846 left > 8? (unsigned char)b[ 8] : 0,
2847 left > 9? (unsigned char)b[ 9] : 0,
2848 left > 10? (unsigned char)b[10] : 0,
2849 left > 11? (unsigned char)b[11] : 0,
2850 left > 12? (unsigned char)b[12] : 0,
2851 left > 13? (unsigned char)b[13] : 0,
2852 left > 14? (unsigned char)b[14] : 0,
2853 left > 15? (unsigned char)b[15] : 0,
2854 (left > 0) ? (isprint(b[ 0]) ? b[ 0] : '.') : ' ',
2855 (left > 1) ? (isprint(b[ 1]) ? b[ 1] : '.') : ' ',
2856 (left > 2) ? (isprint(b[ 2]) ? b[ 2] : '.') : ' ',
2857 (left > 3) ? (isprint(b[ 3]) ? b[ 3] : '.') : ' ',
2858 (left > 4) ? (isprint(b[ 4]) ? b[ 4] : '.') : ' ',
2859 (left > 5) ? (isprint(b[ 5]) ? b[ 5] : '.') : ' ',
2860 (left > 6) ? (isprint(b[ 6]) ? b[ 6] : '.') : ' ',
2861 (left > 7) ? (isprint(b[ 7]) ? b[ 7] : '.') : ' ',
2862 (left > 8) ? (isprint(b[ 8]) ? b[ 8] : '.') : ' ',
2863 (left > 9) ? (isprint(b[ 9]) ? b[ 9] : '.') : ' ',
2864 (left > 10) ? (isprint(b[10]) ? b[10] : '.') : ' ',
2865 (left > 11) ? (isprint(b[11]) ? b[11] : '.') : ' ',
2866 (left > 12) ? (isprint(b[12]) ? b[12] : '.') : ' ',
2867 (left > 13) ? (isprint(b[13]) ? b[13] : '.') : ' ',
2868 (left > 14) ? (isprint(b[14]) ? b[14] : '.') : ' ',
2869 (left > 15) ? (isprint(b[15]) ? b[15] : '.') : ' '
2870 );
2871 left -= 16;
2872 b += 16;
2873 }
2874}
2875#endif