Coverage Report

Created: 2026-01-17 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/flac/src/metaflac/utils.c
Line
Count
Source
1
/* metaflac - Command-line FLAC metadata editor
2
 * Copyright (C) 2001-2009  Josh Coalson
3
 * Copyright (C) 2011-2025  Xiph.Org Foundation
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License along
16
 * with this program; if not, write to the Free Software Foundation, Inc.,
17
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
 */
19
20
#ifdef HAVE_CONFIG_H
21
#  include <config.h>
22
#endif
23
24
#include <ctype.h>
25
#include <stdarg.h>
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <string.h>
29
#include "utils.h"
30
#include "FLAC/assert.h"
31
#include "share/alloc.h"
32
#include "share/safe_str.h"
33
#include "share/utf8.h"
34
#include "share/compat.h"
35
36
void die(const char *message)
37
0
{
38
0
  FLAC__ASSERT(0 != message);
39
0
  flac_fprintf(stderr, "ERROR: %s\n", message);
40
0
  exit(1);
41
0
}
42
43
#ifdef FLAC__VALGRIND_TESTING
44
size_t local_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
45
{
46
  size_t ret = fwrite(ptr, size, nmemb, stream);
47
  if(!ferror(stream))
48
    fflush(stream);
49
  return ret;
50
}
51
#endif
52
53
char *local_strdup(const char *source)
54
315k
{
55
315k
  char *ret;
56
315k
  FLAC__ASSERT(0 != source);
57
315k
  if(0 == (ret = strdup(source)))
58
0
    die("out of memory during strdup()");
59
315k
  return ret;
60
315k
}
61
62
void local_strcat(char **dest, const char *source)
63
31.6k
{
64
31.6k
  size_t ndest, nsource, outlen;
65
66
31.6k
  FLAC__ASSERT(0 != dest);
67
31.6k
  FLAC__ASSERT(0 != source);
68
69
31.6k
  ndest = *dest ? strlen(*dest) : 0;
70
31.6k
  nsource = strlen(source);
71
31.6k
  outlen = ndest + nsource + 1;
72
73
31.6k
  if(nsource == 0)
74
0
    return;
75
76
31.6k
  *dest = safe_realloc_add_3op_(*dest, ndest, /*+*/nsource, /*+*/1);
77
31.6k
  if(*dest == NULL)
78
0
    die("out of memory growing string");
79
  /* If ndest == 0, strlen in safe_strncat reads
80
   * uninitialized data. To prevent that, set first character
81
   * to zero */
82
31.6k
  if(ndest == 0)
83
1.89k
    *dest[0] = 0;
84
31.6k
  safe_strncat(*dest, source, outlen);
85
31.6k
}
86
87
static inline int local_isprint(int c)
88
138M
{
89
138M
  if (c < 32) return 0;
90
8.55M
  if (c > 127) return 0;
91
6.94M
  return isprint(c);
92
8.55M
}
93
94
void hexdump(const char *filename, const FLAC__byte *buf, unsigned bytes, const char *indent)
95
4.92k
{
96
4.92k
  unsigned i, left = bytes;
97
4.92k
  const FLAC__byte *b = buf;
98
99
8.66M
  for(i = 0; i < bytes; i += 16) {
100
8.65M
    flac_printf("%s%s", filename? filename:"", filename? ":":"");
101
8.65M
    flac_printf("%s%08X: "
102
8.65M
      "%02X %02X %02X %02X %02X %02X %02X %02X "
103
8.65M
      "%02X %02X %02X %02X %02X %02X %02X %02X "
104
8.65M
      "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c\n",
105
8.65M
      indent, i,
106
8.65M
      left >  0? (unsigned char)b[ 0] : 0,
107
8.65M
      left >  1? (unsigned char)b[ 1] : 0,
108
8.65M
      left >  2? (unsigned char)b[ 2] : 0,
109
8.65M
      left >  3? (unsigned char)b[ 3] : 0,
110
8.65M
      left >  4? (unsigned char)b[ 4] : 0,
111
8.65M
      left >  5? (unsigned char)b[ 5] : 0,
112
8.65M
      left >  6? (unsigned char)b[ 6] : 0,
113
8.65M
      left >  7? (unsigned char)b[ 7] : 0,
114
8.65M
      left >  8? (unsigned char)b[ 8] : 0,
115
8.65M
      left >  9? (unsigned char)b[ 9] : 0,
116
8.65M
      left > 10? (unsigned char)b[10] : 0,
117
8.65M
      left > 11? (unsigned char)b[11] : 0,
118
8.65M
      left > 12? (unsigned char)b[12] : 0,
119
8.65M
      left > 13? (unsigned char)b[13] : 0,
120
8.65M
      left > 14? (unsigned char)b[14] : 0,
121
8.65M
      left > 15? (unsigned char)b[15] : 0,
122
8.65M
      (left >  0) ? (local_isprint(b[ 0]) ? b[ 0] : '.') : ' ',
123
8.65M
      (left >  1) ? (local_isprint(b[ 1]) ? b[ 1] : '.') : ' ',
124
8.65M
      (left >  2) ? (local_isprint(b[ 2]) ? b[ 2] : '.') : ' ',
125
8.65M
      (left >  3) ? (local_isprint(b[ 3]) ? b[ 3] : '.') : ' ',
126
8.65M
      (left >  4) ? (local_isprint(b[ 4]) ? b[ 4] : '.') : ' ',
127
8.65M
      (left >  5) ? (local_isprint(b[ 5]) ? b[ 5] : '.') : ' ',
128
8.65M
      (left >  6) ? (local_isprint(b[ 6]) ? b[ 6] : '.') : ' ',
129
8.65M
      (left >  7) ? (local_isprint(b[ 7]) ? b[ 7] : '.') : ' ',
130
8.65M
      (left >  8) ? (local_isprint(b[ 8]) ? b[ 8] : '.') : ' ',
131
8.65M
      (left >  9) ? (local_isprint(b[ 9]) ? b[ 9] : '.') : ' ',
132
8.65M
      (left > 10) ? (local_isprint(b[10]) ? b[10] : '.') : ' ',
133
8.65M
      (left > 11) ? (local_isprint(b[11]) ? b[11] : '.') : ' ',
134
8.65M
      (left > 12) ? (local_isprint(b[12]) ? b[12] : '.') : ' ',
135
8.65M
      (left > 13) ? (local_isprint(b[13]) ? b[13] : '.') : ' ',
136
8.65M
      (left > 14) ? (local_isprint(b[14]) ? b[14] : '.') : ' ',
137
8.65M
      (left > 15) ? (local_isprint(b[15]) ? b[15] : '.') : ' '
138
8.65M
    );
139
8.65M
    left -= 16;
140
8.65M
    b += 16;
141
8.65M
   }
142
4.92k
}
143
144
void print_error_with_chain_status(FLAC__Metadata_Chain *chain, const char *format, ...)
145
19.4k
{
146
19.4k
  const FLAC__Metadata_ChainStatus status = FLAC__metadata_chain_status(chain);
147
19.4k
  va_list args;
148
149
19.4k
  FLAC__ASSERT(0 != format);
150
151
19.4k
  va_start(args, format);
152
153
19.4k
  (void) flac_vfprintf(stderr, format, args);
154
155
19.4k
  va_end(args);
156
157
19.4k
  flac_fprintf(stderr, ", status = \"%s\"\n", FLAC__Metadata_ChainStatusString[status]);
158
159
19.4k
  if(status == FLAC__METADATA_CHAIN_STATUS_ERROR_OPENING_FILE) {
160
14.5k
    flac_fprintf(stderr, "\n"
161
14.5k
      "The FLAC file could not be opened.  Most likely the file does not exist\n"
162
14.5k
      "or is not readable.\n"
163
14.5k
    );
164
14.5k
  }
165
4.97k
  else if(status == FLAC__METADATA_CHAIN_STATUS_NOT_A_FLAC_FILE) {
166
3.05k
    flac_fprintf(stderr, "\n"
167
3.05k
      "The file does not appear to be a FLAC file.\n"
168
3.05k
    );
169
3.05k
  }
170
1.92k
  else if(status == FLAC__METADATA_CHAIN_STATUS_NOT_WRITABLE) {
171
0
    flac_fprintf(stderr, "\n"
172
0
      "The FLAC file does not have write permissions.\n"
173
0
    );
174
0
  }
175
1.92k
  else if(status == FLAC__METADATA_CHAIN_STATUS_BAD_METADATA) {
176
492
    flac_fprintf(stderr, "\n"
177
492
      "The metadata to be written does not conform to the FLAC metadata\n"
178
492
      "specifications.\n"
179
492
    );
180
492
  }
181
1.43k
  else if(status == FLAC__METADATA_CHAIN_STATUS_READ_ERROR) {
182
1.26k
    flac_fprintf(stderr, "\n"
183
1.26k
      "There was an error while reading the FLAC file.\n"
184
1.26k
    );
185
1.26k
  }
186
168
  else if(status == FLAC__METADATA_CHAIN_STATUS_WRITE_ERROR) {
187
0
    flac_fprintf(stderr, "\n"
188
0
      "There was an error while writing FLAC file; most probably the disk is\n"
189
0
      "full.\n"
190
0
    );
191
0
  }
192
168
  else if(status == FLAC__METADATA_CHAIN_STATUS_UNLINK_ERROR) {
193
0
    flac_fprintf(stderr, "\n"
194
0
      "There was an error removing the temporary FLAC file.\n"
195
0
    );
196
0
  }
197
19.4k
}
198
199
FLAC__bool parse_vorbis_comment_field(const char *field_ref, char **field, char **name, char **value, unsigned *length, const char **violation)
200
66.2k
{
201
66.2k
  static const char * const violations[] = {
202
66.2k
    "field name contains invalid character",
203
66.2k
    "field contains no '=' character"
204
66.2k
  };
205
206
66.2k
  char *p, *q, *s;
207
208
66.2k
  if(0 != field)
209
66.2k
    *field = local_strdup(field_ref);
210
211
66.2k
  s = local_strdup(field_ref);
212
213
66.2k
  if(0 == (p = strchr(s, '='))) {
214
52
    free(s);
215
52
    *violation = violations[1];
216
52
    return false;
217
52
  }
218
66.1k
  *p++ = '\0';
219
220
2.11M
  for(q = s; *q; q++) {
221
2.05M
    if(*q < 0x20 || *q > 0x7d || *q == 0x3d) {
222
45
      free(s);
223
45
      *violation = violations[0];
224
45
      return false;
225
45
    }
226
2.05M
  }
227
228
66.1k
  *name = local_strdup(s);
229
66.1k
  *value = local_strdup(p);
230
66.1k
  *length = strlen(p);
231
232
66.1k
  free(s);
233
66.1k
  return true;
234
66.1k
}
235
236
void write_vc_field(const char *filename, const FLAC__StreamMetadata_VorbisComment_Entry *entry, FLAC__bool raw, FILE *f)
237
19.1k
{
238
19.1k
  if(0 != entry->entry) {
239
19.1k
    if(filename)
240
610
      flac_fprintf(f, "%s:", filename);
241
242
19.1k
    if(!raw) {
243
      /*
244
       * WATCHOUT: comments that contain an embedded null will
245
       * be truncated by utf_decode().
246
       */
247
#ifdef _WIN32
248
      flac_fprintf(f, "%s", entry->entry);
249
#else
250
18.4k
      char *converted;
251
252
18.4k
      if(utf8_decode((const char *)entry->entry, &converted) >= 0) {
253
18.0k
        (void) local_fwrite(converted, 1, strlen(converted), f);
254
18.0k
        free(converted);
255
18.0k
      }
256
401
      else {
257
401
        (void) local_fwrite(entry->entry, 1, entry->length, f);
258
401
      }
259
18.4k
#endif
260
18.4k
    }
261
630
    else {
262
630
      (void) local_fwrite(entry->entry, 1, entry->length, f);
263
630
    }
264
19.1k
  }
265
#ifdef _WIN32
266
  flac_fprintf(f,"\n");
267
#else
268
19.1k
  putc('\n', f);
269
19.1k
#endif
270
19.1k
}
271
272
void write_vc_fields(const char *filename, const char *field_name, const FLAC__StreamMetadata_VorbisComment_Entry entry[], unsigned num_entries, FLAC__bool raw, FILE *f)
273
370
{
274
370
  unsigned i;
275
370
  const unsigned field_name_length = (0 != field_name)? strlen(field_name) : 0;
276
277
10.8k
  for(i = 0; i < num_entries; i++) {
278
10.5k
    if(0 == field_name || FLAC__metadata_object_vorbiscomment_entry_matches(entry[i], field_name, field_name_length))
279
7.13k
      write_vc_field(filename, entry + i, raw, f);
280
10.5k
  }
281
370
}