1 /*
2  * libid3tag - ID3 tag manipulation library
3  * Copyright (C) 2000-2004 Underbit Technologies, Inc.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (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
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  * $Id: field.c,v 1.16 2004/01/23 09:41:32 rob Exp $
20  */
21 
22 # ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 # endif
25 
26 # include "global.h"
27 
28 # include <stdlib.h>
29 # include <string.h>
30 
31 # ifdef HAVE_ASSERT_H
32 #  include <assert.h>
33 # endif
34 
35 # include "id3tag.h"
36 # include "field.h"
37 # include "frame.h"
38 # include "render.h"
39 # include "ucs4.h"
40 # include "latin1.h"
41 # include "parse.h"
42 
43 /*
44  * NAME:	field->init()
45  * DESCRIPTION:	initialize a field to a default value for the given type
46  */
id3_field_init(union id3_field * field,enum id3_field_type type)47 void id3_field_init(union id3_field *field, enum id3_field_type type)
48 {
49   assert(field);
50 
51   switch (field->type = type) {
52   case ID3_FIELD_TYPE_TEXTENCODING:
53   case ID3_FIELD_TYPE_INT8:
54   case ID3_FIELD_TYPE_INT16:
55   case ID3_FIELD_TYPE_INT24:
56   case ID3_FIELD_TYPE_INT32:
57     field->number.value = 0;
58     break;
59 
60   case ID3_FIELD_TYPE_LATIN1:
61   case ID3_FIELD_TYPE_LATIN1FULL:
62     field->latin1.ptr = 0;
63     break;
64 
65   case ID3_FIELD_TYPE_LATIN1LIST:
66     field->latin1list.nstrings = 0;
67     field->latin1list.strings  = 0;
68 
69   case ID3_FIELD_TYPE_STRING:
70   case ID3_FIELD_TYPE_STRINGFULL:
71     field->string.ptr = 0;
72     break;
73 
74   case ID3_FIELD_TYPE_STRINGLIST:
75     field->stringlist.nstrings = 0;
76     field->stringlist.strings  = 0;
77     break;
78 
79   case ID3_FIELD_TYPE_LANGUAGE:
80     strcpy(field->immediate.value, "XXX");
81     break;
82 
83   case ID3_FIELD_TYPE_FRAMEID:
84     strcpy(field->immediate.value, "XXXX");
85     break;
86 
87   case ID3_FIELD_TYPE_DATE:
88     memset(field->immediate.value, 0, sizeof(field->immediate.value));
89     break;
90 
91   case ID3_FIELD_TYPE_INT32PLUS:
92   case ID3_FIELD_TYPE_BINARYDATA:
93     field->binary.data   = 0;
94     field->binary.length = 0;
95     break;
96   }
97 }
98 
99 /*
100  * NAME:	field->finish()
101  * DESCRIPTION:	reset a field, deallocating memory if necessary
102  */
id3_field_finish(union id3_field * field)103 void id3_field_finish(union id3_field *field)
104 {
105   unsigned int i;
106 
107   assert(field);
108 
109   switch (field->type) {
110   case ID3_FIELD_TYPE_TEXTENCODING:
111   case ID3_FIELD_TYPE_INT8:
112   case ID3_FIELD_TYPE_INT16:
113   case ID3_FIELD_TYPE_INT24:
114   case ID3_FIELD_TYPE_INT32:
115   case ID3_FIELD_TYPE_LANGUAGE:
116   case ID3_FIELD_TYPE_FRAMEID:
117   case ID3_FIELD_TYPE_DATE:
118     break;
119 
120   case ID3_FIELD_TYPE_LATIN1:
121   case ID3_FIELD_TYPE_LATIN1FULL:
122     if (field->latin1.ptr)
123       free(field->latin1.ptr);
124     break;
125 
126   case ID3_FIELD_TYPE_LATIN1LIST:
127     for (i = 0; i < field->latin1list.nstrings; ++i)
128       free(field->latin1list.strings[i]);
129 
130     if (field->latin1list.strings)
131       free(field->latin1list.strings);
132     break;
133 
134   case ID3_FIELD_TYPE_STRING:
135   case ID3_FIELD_TYPE_STRINGFULL:
136     if (field->string.ptr)
137       free(field->string.ptr);
138     break;
139 
140   case ID3_FIELD_TYPE_STRINGLIST:
141     for (i = 0; i < field->stringlist.nstrings; ++i)
142       free(field->stringlist.strings[i]);
143 
144     if (field->stringlist.strings)
145       free(field->stringlist.strings);
146     break;
147 
148   case ID3_FIELD_TYPE_INT32PLUS:
149   case ID3_FIELD_TYPE_BINARYDATA:
150     if (field->binary.data)
151       free(field->binary.data);
152     break;
153   }
154 
155   id3_field_init(field, field->type);
156 }
157 
158 /*
159  * NAME:	field->type()
160  * DESCRIPTION:	return the value type of a field
161  */
id3_field_type(union id3_field const * field)162 enum id3_field_type id3_field_type(union id3_field const *field)
163 {
164   assert(field);
165 
166   return field->type;
167 }
168 
169 /*
170  * NAME:	field->parse()
171  * DESCRIPTION:	parse a field value
172  */
id3_field_parse(union id3_field * field,id3_byte_t const ** ptr,id3_length_t length,enum id3_field_textencoding * encoding)173 int id3_field_parse(union id3_field *field, id3_byte_t const **ptr,
174 		    id3_length_t length, enum id3_field_textencoding *encoding)
175 {
176   assert(field);
177 
178   id3_field_finish(field);
179 
180   switch (field->type) {
181   case ID3_FIELD_TYPE_INT32:
182     if (length < 4)
183       goto fail;
184 
185     field->number.value = id3_parse_uint(ptr, 4);
186     break;
187 
188   case ID3_FIELD_TYPE_INT24:
189     if (length < 3)
190       goto fail;
191 
192     field->number.value = id3_parse_uint(ptr, 3);
193     break;
194 
195   case ID3_FIELD_TYPE_INT16:
196     if (length < 2)
197       goto fail;
198 
199     field->number.value = id3_parse_uint(ptr, 2);
200     break;
201 
202   case ID3_FIELD_TYPE_INT8:
203   case ID3_FIELD_TYPE_TEXTENCODING:
204     if (length < 1)
205       goto fail;
206 
207     field->number.value = id3_parse_uint(ptr, 1);
208 
209     if (field->type == ID3_FIELD_TYPE_TEXTENCODING)
210       *encoding = field->number.value;
211     break;
212 
213   case ID3_FIELD_TYPE_LANGUAGE:
214     if (length < 3)
215       goto fail;
216 
217     id3_parse_immediate(ptr, 3, field->immediate.value);
218     break;
219 
220   case ID3_FIELD_TYPE_FRAMEID:
221     if (length < 4)
222       goto fail;
223 
224     id3_parse_immediate(ptr, 4, field->immediate.value);
225     break;
226 
227   case ID3_FIELD_TYPE_DATE:
228     if (length < 8)
229       goto fail;
230 
231     id3_parse_immediate(ptr, 8, field->immediate.value);
232     break;
233 
234   case ID3_FIELD_TYPE_LATIN1:
235   case ID3_FIELD_TYPE_LATIN1FULL:
236     {
237       id3_latin1_t *latin1;
238 
239       latin1 = id3_parse_latin1(ptr, length,
240 				field->type == ID3_FIELD_TYPE_LATIN1FULL);
241       if (latin1 == 0)
242 	goto fail;
243 
244       field->latin1.ptr = latin1;
245     }
246     break;
247 
248   case ID3_FIELD_TYPE_LATIN1LIST:
249     {
250       id3_byte_t const *end;
251       id3_latin1_t *latin1, **strings;
252 
253       end = *ptr + length;
254 
255       while (end - *ptr > 0) {
256 	latin1 = id3_parse_latin1(ptr, end - *ptr, 0);
257 	if (latin1 == 0)
258 	  goto fail;
259 
260 	strings = realloc(field->latin1list.strings,
261 			  (field->latin1list.nstrings + 1) * sizeof(*strings));
262 	if (strings == 0) {
263 	  free(latin1);
264 	  goto fail;
265 	}
266 
267 	field->latin1list.strings = strings;
268 	field->latin1list.strings[field->latin1list.nstrings++] = latin1;
269       }
270     }
271     break;
272 
273   case ID3_FIELD_TYPE_STRING:
274   case ID3_FIELD_TYPE_STRINGFULL:
275     {
276       id3_ucs4_t *ucs4;
277 
278       ucs4 = id3_parse_string(ptr, length, *encoding,
279 			      field->type == ID3_FIELD_TYPE_STRINGFULL);
280       if (ucs4 == 0)
281 	goto fail;
282 
283       field->string.ptr = ucs4;
284     }
285     break;
286 
287   case ID3_FIELD_TYPE_STRINGLIST:
288     {
289       id3_byte_t const *end;
290       id3_ucs4_t *ucs4, **strings;
291 
292       end = *ptr + length;
293 
294       while (end - *ptr > 0) {
295 	ucs4 = id3_parse_string(ptr, end - *ptr, *encoding, 0);
296 	if (ucs4 == 0)
297 	  goto fail;
298 
299 	strings = realloc(field->stringlist.strings,
300 			  (field->stringlist.nstrings + 1) * sizeof(*strings));
301 	if (strings == 0) {
302 	  free(ucs4);
303 	  goto fail;
304 	}
305 
306 	field->stringlist.strings = strings;
307 	field->stringlist.strings[field->stringlist.nstrings++] = ucs4;
308       }
309     }
310     break;
311 
312   case ID3_FIELD_TYPE_INT32PLUS:
313   case ID3_FIELD_TYPE_BINARYDATA:
314     {
315       id3_byte_t *data;
316 
317       data = id3_parse_binary(ptr, length);
318       if (data == 0)
319 	goto fail;
320 
321       field->binary.data   = data;
322       field->binary.length = length;
323     }
324     break;
325   }
326 
327   return 0;
328 
329  fail:
330   return -1;
331 }
332 
333 /*
334  * NAME:	field->render()
335  * DESCRIPTION:	render a field value
336  */
id3_field_render(union id3_field const * field,id3_byte_t ** ptr,enum id3_field_textencoding * encoding,int terminate)337 id3_length_t id3_field_render(union id3_field const *field, id3_byte_t **ptr,
338 			      enum id3_field_textencoding *encoding,
339 			      int terminate)
340 {
341   id3_length_t size;
342   unsigned int i;
343 
344   assert(field && encoding);
345 
346   switch (field->type) {
347   case ID3_FIELD_TYPE_INT32:
348     return id3_render_int(ptr, field->number.value, 4);
349 
350   case ID3_FIELD_TYPE_INT24:
351     return id3_render_int(ptr, field->number.value, 3);
352 
353   case ID3_FIELD_TYPE_INT16:
354     return id3_render_int(ptr, field->number.value, 2);
355 
356   case ID3_FIELD_TYPE_TEXTENCODING:
357     *encoding = field->number.value;
358   case ID3_FIELD_TYPE_INT8:
359     return id3_render_int(ptr, field->number.value, 1);
360 
361   case ID3_FIELD_TYPE_LATIN1:
362   case ID3_FIELD_TYPE_LATIN1FULL:
363     return id3_render_latin1(ptr, field->latin1.ptr, terminate);
364 
365   case ID3_FIELD_TYPE_LATIN1LIST:
366     size = 0;
367     for (i = 0; i < field->latin1list.nstrings; ++i) {
368       size += id3_render_latin1(ptr, field->latin1list.strings[i],
369 				(i < field->latin1list.nstrings - 1) ||
370 				terminate);
371     }
372     return size;
373 
374   case ID3_FIELD_TYPE_STRING:
375   case ID3_FIELD_TYPE_STRINGFULL:
376     return id3_render_string(ptr, field->string.ptr, *encoding, terminate);
377 
378   case ID3_FIELD_TYPE_STRINGLIST:
379     size = 0;
380     for (i = 0; i < field->stringlist.nstrings; ++i) {
381       size += id3_render_string(ptr, field->stringlist.strings[i], *encoding,
382 				(i < field->stringlist.nstrings - 1) ||
383 				terminate);
384     }
385     return size;
386 
387   case ID3_FIELD_TYPE_LANGUAGE:
388     return id3_render_immediate(ptr, field->immediate.value, 3);
389 
390   case ID3_FIELD_TYPE_FRAMEID:
391     return id3_render_immediate(ptr, field->immediate.value, 4);
392 
393   case ID3_FIELD_TYPE_DATE:
394     return id3_render_immediate(ptr, field->immediate.value, 8);
395 
396   case ID3_FIELD_TYPE_INT32PLUS:
397   case ID3_FIELD_TYPE_BINARYDATA:
398     return id3_render_binary(ptr, field->binary.data, field->binary.length);
399   }
400 
401   return 0;
402 }
403 
404 /*
405  * NAME:	field->setint()
406  * DESCRIPTION:	set the value of an int field
407  */
id3_field_setint(union id3_field * field,signed long number)408 int id3_field_setint(union id3_field *field, signed long number)
409 {
410   assert(field);
411 
412   switch (field->type) {
413   case ID3_FIELD_TYPE_INT8:
414     if (number > 0x7f || number < -0x80)
415       return -1;
416     break;
417 
418   case ID3_FIELD_TYPE_INT16:
419     if (number > 0x7fff || number < -0x8000)
420       return -1;
421     break;
422 
423   case ID3_FIELD_TYPE_INT24:
424     if (number > 0x7fffffL || number < -0x800000L)
425       return -1;
426     break;
427 
428   case ID3_FIELD_TYPE_INT32:
429     if (number > 0x7fffffffL || number < -0x80000000L)
430       return -1;
431     break;
432 
433   default:
434     return -1;
435   }
436 
437   id3_field_finish(field);
438 
439   field->number.value = number;
440 
441   return 0;
442 }
443 
444 /*
445  * NAME:	field->settextencoding()
446  * DESCRIPTION:	set the value of a textencoding field
447  */
id3_field_settextencoding(union id3_field * field,enum id3_field_textencoding encoding)448 int id3_field_settextencoding(union id3_field *field,
449 			      enum id3_field_textencoding encoding)
450 {
451   assert(field);
452 
453   if (field->type != ID3_FIELD_TYPE_TEXTENCODING)
454     return -1;
455 
456   id3_field_finish(field);
457 
458   field->number.value = encoding;
459 
460   return 0;
461 }
462 
463 static
set_latin1(union id3_field * field,id3_latin1_t const * latin1)464 int set_latin1(union id3_field *field, id3_latin1_t const *latin1)
465 {
466   id3_latin1_t *data;
467 
468   if (latin1 == 0 || *latin1 == 0)
469     data = 0;
470   else {
471     data = id3_latin1_duplicate(latin1);
472     if (data == 0)
473       return -1;
474   }
475 
476   field->latin1.ptr = data;
477 
478   return 0;
479 }
480 
481 /*
482  * NAME:	field->setlatin1()
483  * DESCRIPTION:	set the value of a latin1 field
484  */
id3_field_setlatin1(union id3_field * field,id3_latin1_t const * latin1)485 int id3_field_setlatin1(union id3_field *field, id3_latin1_t const *latin1)
486 {
487   assert(field);
488 
489   if (field->type != ID3_FIELD_TYPE_LATIN1)
490     return -1;
491 
492   id3_field_finish(field);
493 
494   if (latin1) {
495     id3_latin1_t const *ptr;
496 
497     for (ptr = latin1; *ptr; ++ptr) {
498       if (*ptr == '\n')
499 	return -1;
500     }
501   }
502 
503   return set_latin1(field, latin1);
504 }
505 
506 /*
507  * NAME:	field->setfulllatin1()
508  * DESCRIPTION:	set the value of a full latin1 field
509  */
id3_field_setfulllatin1(union id3_field * field,id3_latin1_t const * latin1)510 int id3_field_setfulllatin1(union id3_field *field, id3_latin1_t const *latin1)
511 {
512   assert(field);
513 
514   if (field->type != ID3_FIELD_TYPE_LATIN1FULL)
515     return -1;
516 
517   id3_field_finish(field);
518 
519   return set_latin1(field, latin1);
520 }
521 
522 static
set_string(union id3_field * field,id3_ucs4_t const * string)523 int set_string(union id3_field *field, id3_ucs4_t const *string)
524 {
525   id3_ucs4_t *data;
526 
527   if (string == 0 || *string == 0)
528     data = 0;
529   else {
530     data = id3_ucs4_duplicate(string);
531     if (data == 0)
532       return -1;
533   }
534 
535   field->string.ptr = data;
536 
537   return 0;
538 }
539 
540 /*
541  * NAME:	field->setstring()
542  * DESCRIPTION:	set the value of a string field
543  */
id3_field_setstring(union id3_field * field,id3_ucs4_t const * string)544 int id3_field_setstring(union id3_field *field, id3_ucs4_t const *string)
545 {
546   assert(field);
547 
548   if (field->type != ID3_FIELD_TYPE_STRING)
549     return -1;
550 
551   id3_field_finish(field);
552 
553   if (string) {
554     id3_ucs4_t const *ptr;
555 
556     for (ptr = string; *ptr; ++ptr) {
557       if (*ptr == '\n')
558 	return -1;
559     }
560   }
561 
562   return set_string(field, string);
563 }
564 
565 /*
566  * NAME:	field->setfullstring()
567  * DESCRIPTION:	set the value of a full string field
568  */
id3_field_setfullstring(union id3_field * field,id3_ucs4_t const * string)569 int id3_field_setfullstring(union id3_field *field, id3_ucs4_t const *string)
570 {
571   assert(field);
572 
573   if (field->type != ID3_FIELD_TYPE_STRINGFULL)
574     return -1;
575 
576   id3_field_finish(field);
577 
578   return set_string(field, string);
579 }
580 
581 /*
582  * NAME:	field->setstrings()
583  * DESCRIPTION:	set the value of a stringlist field
584  */
id3_field_setstrings(union id3_field * field,unsigned int length,id3_ucs4_t ** ptrs)585 int id3_field_setstrings(union id3_field *field,
586 			 unsigned int length, id3_ucs4_t **ptrs)
587 {
588   id3_ucs4_t **strings;
589   unsigned int i;
590 
591   assert(field);
592 
593   if (field->type != ID3_FIELD_TYPE_STRINGLIST)
594     return -1;
595 
596   id3_field_finish(field);
597 
598   if (length == 0)
599     return 0;
600 
601   strings = malloc(length * sizeof(*strings));
602   if (strings == 0)
603     return -1;
604 
605   for (i = 0; i < length; ++i) {
606     strings[i] = id3_ucs4_duplicate(ptrs[i]);
607     if (strings[i] == 0) {
608       while (i--)
609 	free(strings[i]);
610 
611       free(strings);
612       return -1;
613     }
614   }
615 
616   field->stringlist.strings  = strings;
617   field->stringlist.nstrings = length;
618 
619   return 0;
620 }
621 
622 /*
623  * NAME:	field->addstring()
624  * DESCRIPTION:	add a string to a stringlist field
625  */
id3_field_addstring(union id3_field * field,id3_ucs4_t const * string)626 int id3_field_addstring(union id3_field *field, id3_ucs4_t const *string)
627 {
628   id3_ucs4_t *new, **strings;
629 
630   assert(field);
631 
632   if (field->type != ID3_FIELD_TYPE_STRINGLIST)
633     return -1;
634 
635   if (string == 0)
636     string = id3_ucs4_empty;
637 
638   new = id3_ucs4_duplicate(string);
639   if (new == 0)
640     return -1;
641 
642   strings = realloc(field->stringlist.strings,
643 		    (field->stringlist.nstrings + 1) * sizeof(*strings));
644   if (strings == 0) {
645     free(new);
646     return -1;
647   }
648 
649   field->stringlist.strings = strings;
650   field->stringlist.strings[field->stringlist.nstrings++] = new;
651 
652   return 0;
653 }
654 
655 /*
656  * NAME:	field->setlanguage()
657  * DESCRIPTION:	set the value of a language field
658  */
id3_field_setlanguage(union id3_field * field,char const * language)659 int id3_field_setlanguage(union id3_field *field, char const *language)
660 {
661   assert(field);
662 
663   if (field->type != ID3_FIELD_TYPE_LANGUAGE)
664     return -1;
665 
666   id3_field_finish(field);
667 
668   if (language) {
669     if (strlen(language) != 3)
670       return -1;
671 
672     strcpy(field->immediate.value, language);
673   }
674 
675   return 0;
676 }
677 
678 /*
679  * NAME:	field->setframeid()
680  * DESCRIPTION:	set the value of a frameid field
681  */
id3_field_setframeid(union id3_field * field,char const * id)682 int id3_field_setframeid(union id3_field *field, char const *id)
683 {
684   assert(field);
685 
686   if (field->type != ID3_FIELD_TYPE_FRAMEID ||
687       !id3_frame_validid(id))
688     return -1;
689 
690   id3_field_finish(field);
691 
692   field->immediate.value[0] = id[0];
693   field->immediate.value[1] = id[1];
694   field->immediate.value[2] = id[2];
695   field->immediate.value[3] = id[3];
696   field->immediate.value[4] = 0;
697 
698   return 0;
699 }
700 
701 /*
702  * NAME:	field->setbinarydata()
703  * DESCRIPTION:	set the value of a binarydata field
704  */
id3_field_setbinarydata(union id3_field * field,id3_byte_t const * data,id3_length_t length)705 int id3_field_setbinarydata(union id3_field *field,
706 			    id3_byte_t const *data, id3_length_t length)
707 {
708   id3_byte_t *mem;
709 
710   assert(field);
711 
712   if (field->type != ID3_FIELD_TYPE_BINARYDATA)
713     return -1;
714 
715   id3_field_finish(field);
716 
717   if (length == 0)
718     mem = 0;
719   else {
720     mem = malloc(length);
721     if (mem == 0)
722       return -1;
723 
724     assert(data);
725 
726     memcpy(mem, data, length);
727   }
728 
729   field->binary.data   = mem;
730   field->binary.length = length;
731 
732   return 0;
733 }
734 
735 /*
736  * NAME:	field->getint()
737  * DESCRIPTION:	return the value of an integer field
738  */
id3_field_getint(union id3_field const * field)739 signed long id3_field_getint(union id3_field const *field)
740 {
741   assert(field);
742 
743   if (field->type != ID3_FIELD_TYPE_INT8 &&
744       field->type != ID3_FIELD_TYPE_INT16 &&
745       field->type != ID3_FIELD_TYPE_INT24 &&
746       field->type != ID3_FIELD_TYPE_INT32)
747     return -1;
748 
749   return field->number.value;
750 }
751 
752 /*
753  * NAME:	field->gettextencoding()
754  * DESCRIPTION:	return the value of a text encoding field
755  */
756 enum id3_field_textencoding
id3_field_gettextencoding(union id3_field const * field)757 id3_field_gettextencoding(union id3_field const *field)
758 {
759   assert(field);
760 
761   if (field->type != ID3_FIELD_TYPE_TEXTENCODING)
762     return -1;
763 
764   return field->number.value;
765 }
766 
767 /*
768  * NAME:	field->getlatin1()
769  * DESCRIPTION:	return the value of a latin1 field
770  */
id3_field_getlatin1(union id3_field const * field)771 id3_latin1_t const *id3_field_getlatin1(union id3_field const *field)
772 {
773   assert(field);
774 
775   if (field->type != ID3_FIELD_TYPE_LATIN1)
776     return 0;
777 
778   return field->latin1.ptr ? field->latin1.ptr : (id3_latin1_t const *) "";
779 }
780 
781 /*
782  * NAME:	field->getfulllatin1()
783  * DESCRIPTION:	return the value of a full latin1 field
784  */
id3_field_getfulllatin1(union id3_field const * field)785 id3_latin1_t const *id3_field_getfulllatin1(union id3_field const *field)
786 {
787   assert(field);
788 
789   if (field->type != ID3_FIELD_TYPE_LATIN1FULL)
790     return 0;
791 
792   return field->latin1.ptr ? field->latin1.ptr : (id3_latin1_t const *) "";
793 }
794 
795 /*
796  * NAME:	field->getstring()
797  * DESCRIPTION:	return the value of a string field
798  */
id3_field_getstring(union id3_field const * field)799 id3_ucs4_t const *id3_field_getstring(union id3_field const *field)
800 {
801   assert(field);
802 
803   if (field->type != ID3_FIELD_TYPE_STRING)
804     return 0;
805 
806   return field->string.ptr ? field->string.ptr : id3_ucs4_empty;
807 }
808 
809 /*
810  * NAME:	field->getfullstring()
811  * DESCRIPTION:	return the value of a fullstring field
812  */
id3_field_getfullstring(union id3_field const * field)813 id3_ucs4_t const *id3_field_getfullstring(union id3_field const *field)
814 {
815   assert(field);
816 
817   if (field->type != ID3_FIELD_TYPE_STRINGFULL)
818     return 0;
819 
820   return field->string.ptr ? field->string.ptr : id3_ucs4_empty;
821 }
822 
823 /*
824  * NAME:	field->getnstrings()
825  * DESCRIPTION:	return the number of strings in a stringlist field
826  */
id3_field_getnstrings(union id3_field const * field)827 unsigned int id3_field_getnstrings(union id3_field const *field)
828 {
829   assert(field);
830 
831   if (field->type != ID3_FIELD_TYPE_STRINGLIST)
832     return 0;
833 
834   return field->stringlist.nstrings;
835 }
836 
837 /*
838  * NAME:	field->getstrings()
839  * DESCRIPTION:	return one value of a stringlist field
840  */
id3_field_getstrings(union id3_field const * field,unsigned int index)841 id3_ucs4_t const *id3_field_getstrings(union id3_field const *field,
842 				       unsigned int index)
843 {
844   id3_ucs4_t const *string;
845 
846   assert(field);
847 
848   if (field->type != ID3_FIELD_TYPE_STRINGLIST ||
849       index >= field->stringlist.nstrings)
850     return 0;
851 
852   string = field->stringlist.strings[index];
853 
854   return string ? string : id3_ucs4_empty;
855 }
856 
857 /*
858  * NAME:	field->getframeid()
859  * DESCRIPTION:	return the value of a frameid field
860  */
id3_field_getframeid(union id3_field const * field)861 char const *id3_field_getframeid(union id3_field const *field)
862 {
863   assert(field);
864 
865   if (field->type != ID3_FIELD_TYPE_FRAMEID)
866     return 0;
867 
868   return field->immediate.value;
869 }
870 
871 /*
872  * NAME:	field->getbinarydata()
873  * DESCRIPTION:	return the value of a binarydata field
874  */
id3_field_getbinarydata(union id3_field const * field,id3_length_t * length)875 id3_byte_t const *id3_field_getbinarydata(union id3_field const *field,
876 					  id3_length_t *length)
877 {
878   static id3_byte_t const empty;
879 
880   assert(field && length);
881 
882   if (field->type != ID3_FIELD_TYPE_BINARYDATA)
883     return 0;
884 
885   assert(field->binary.length == 0 || field->binary.data);
886 
887   *length = field->binary.length;
888 
889   return field->binary.data ? field->binary.data : &empty;
890 }
891