diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-07-16 22:41:20 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2011-07-16 22:41:20 +0300 |
commit | 9a2df634f322d174014ac8d5090bfa87a25bb775 (patch) | |
tree | 4346c4b392898c087c55fd20ca998eb39294027b | |
parent | 8d89ce088cc25786cb9520f67e9fe81c86c70277 (diff) | |
download | idest-9a2df634f322d174014ac8d5090bfa87a25bb775.tar.gz idest-9a2df634f322d174014ac8d5090bfa87a25bb775.tar.bz2 |
Major rewrite.
* src/idest.h (ed_item): Remove union, leave only value instead.
<ref>: New member.
(describe_option,verbose_option,all_frames): New externs.
(set_frame_value): Return int.
(IDEST_OK,IDEST_ERR_*): New constants.
(_idest_errstr, _idest_nerrs): New externs.
(idest_frame_cmp_t,idest_frame_encoder_t)
(idest_frame_decoder_t): New typedefs.
(idest_frametab): New struct.
(idest_frame_lookup)
(find_matching_frame): New protos.
(ed_list_add_item)
(ed_list_print,ed_list_add_assignment)
(ed_list_clear,ed_item_matches_frame)
(ed_item_set_comment_fields): Remove.
(qv_free,input_list_locate)
(parse_ed_items): New protos.
* src/idop.c: Rewrite.
* src/.gitignore: Add src/frametab.c
* src/Makefile.am (idest_SOURCES): Add editem.c and frametab.c
(BUILT_SOURCES): Add frametab.c
(.gperf.c): New rule
(GPERF_FLAGS): New variable.
* src/editem.c: New source.
* src/frametab.gperf: New source.
* src/cmdline.opt: Use input_list_add_assignment in --set handler.
* src/guile.c (frame_to_scm,scm_to_tag): Rewrite using the new
logic.
-rw-r--r-- | src/.gitignore | 1 | ||||
-rw-r--r-- | src/Makefile.am | 9 | ||||
-rw-r--r-- | src/cmdline.opt | 2 | ||||
-rw-r--r-- | src/editem.c | 249 | ||||
-rw-r--r-- | src/frametab.gperf | 430 | ||||
-rw-r--r-- | src/guile.c | 199 | ||||
-rw-r--r-- | src/idest.h | 81 | ||||
-rw-r--r-- | src/idop.c | 567 | ||||
-rw-r--r-- | src/main.c | 278 |
9 files changed, 1215 insertions, 601 deletions
diff --git a/src/.gitignore b/src/.gitignore index 43f7eeb..8147ba9 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -2,3 +2,4 @@ cmdline.h idest guile.x +frametab.c diff --git a/src/Makefile.am b/src/Makefile.am index ed125b6..db5513a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,6 +17,8 @@ bin_PROGRAMS=idest idest_SOURCES=\ backup.c\ + editem.c\ + frametab.c\ guile.c\ guile.x\ idest.h\ @@ -24,7 +26,7 @@ idest_SOURCES=\ main.c\ cmdline.h\ slist.c -BUILT_SOURCES=cmdline.h +BUILT_SOURCES=cmdline.h frametab.c EXTRA_DIST=cmdline.opt getopt.m4 INCLUDES=-I$(top_srcdir)/gnu -I$(top_builddir)/gnu -I$(top_srcdir)/libid3tag @GUILE_INCLUDES@ LDADD=../gnu/libgnu.a ../libid3tag/libid3tag.a -lz @GUILE_LIBS@ @@ -33,6 +35,11 @@ SUFFIXES=.opt .c .h .opt.h: m4 -s $(srcdir)/getopt.m4 $< | sed '1d' > $@ +GPERF_FLAGS=-tCcTnD -K id + +.gperf.c: + $(AM_V_GEN)gperf $(GPERF_FLAGS) $< > $@ + DOT_X_FILES = guile.x CLEANFILES= DISTCLEANFILES= diff --git a/src/cmdline.opt b/src/cmdline.opt index 6cf4d88..c1de3b5 100644 --- a/src/cmdline.opt +++ b/src/cmdline.opt @@ -78,7 +78,7 @@ BEGIN if (!p) error(1, 0, "missing `=' sign in assignment"); *p++ = 0; - ed_list_add_assignment(optarg, p); + input_list_add_assignment(optarg, p); END OPTION(delete,d,[FLIST], diff --git a/src/editem.c b/src/editem.c new file mode 100644 index 0000000..ba03c91 --- /dev/null +++ b/src/editem.c @@ -0,0 +1,249 @@ +/* This file is part of Idest. + Copyright (C) 2009-2011 Sergey Poznyakoff + + Idest is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Idest is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Idest. If not, see <http://www.gnu.org/licenses/>. */ + +#include "idest.h" + +void +ed_item_zero(struct ed_item *item) +{ + memset(item, 0, sizeof(*item)); +} + +void +ed_item_free_content(struct ed_item *item) +{ + qv_free(item->qc, item->qv); + free(item->value); + free(item->name); +} + +void +ed_item_free(struct ed_item *item) +{ + qv_free(item->qc, item->qv); + free(item->value); + free(item->name); + free(item); +} + +#define ISWILD(s) ((s)[0] == '*' && (s)[1] == 0) + +int +ed_item_qv_cmp(struct ed_item const *a, struct ed_item const *b) +{ + int i, count; + + if (a->qc != b->qc) + return 1; + count = a->qc; + for (i = 0; i < count; i++) { + if (!a->qv[i] || !b->qv[i] || + ISWILD(a->qv[i]) || ISWILD(b->qv[i])) + continue; + if (strcmp(a->qv[i], b->qv[i])) + return 1; + } + return 0; +} + +struct ed_item * +ed_item_create0(char *name, const char *id) +{ + struct ed_item *itm = xzalloc(sizeof(itm[0])); + itm->name = name; + strncpy(itm->id, id, sizeof(itm->id)); + return itm; +} + +struct ed_item * +ed_item_create(const char *name, const char *id) +{ + return ed_item_create0(xstrdup(name), id); +} + +struct ed_item * +ed_item_dup(struct ed_item const *orig) +{ + int i; + struct ed_item *copy; + + copy = ed_item_create(orig->name, orig->id); + copy->qc = orig->qc; + copy->qv = xcalloc(orig->qc, sizeof(orig->qv[0])); + + for (i = 0; i < orig->qc; i++) + copy->qv[i] = orig->qv[i] ? xstrdup(orig->qv[i]) : NULL; + copy->value = orig->value ? xstrdup(orig->value) : NULL; + return copy; +} + +static const char * +str_split_col(const char *str, size_t *plen, size_t *pn) +{ + size_t n; + size_t len = *plen; + + for (n = 0; len; len--, n++, str++) { + if (*str == ':') { + *pn = n; + *plen = len - 1; + return str + 1; + } + } + *pn = n; + *plen = len; + return NULL; +} + +static int +split_item_name(struct ed_item *itm, const char *arg, size_t len) +{ + const char *p; + size_t n; + + p = str_split_col(arg, &len, &n); + itm->name = xmalloc(n + 1); + memcpy(itm->name, arg, n); + itm->name[n] = 0; + + if (p) { + size_t qc; + int i; + + /* count qualifier elements */ + for (qc = 1, i = 0; i < len; i++) + if (p[i] == ':') + qc++; + + itm->qc = qc; + itm->qv = xcalloc(qc, sizeof(itm->qv[0])); + + for (i = 0; len && i < qc; i++) { + char *s; + + arg = p; + p = str_split_col(arg, &len, &n); + if (n == 0) + s = NULL; + else { + s = xmalloc(n + 1); + memcpy(s, arg, n); + s[n] = 0; + } + itm->qv[i] = s; + } + } else { + itm->qc = 0; + itm->qv = NULL; + } + return 0; +} + +struct ed_item * +ed_item_from_frame_spec(const char *arg, size_t len) +{ + struct ed_item itm; + struct id3_frametype const *frametype; + struct idest_frametab const *ft; + const char *id; + + ed_item_zero(&itm); + if (split_item_name(&itm, arg, len)) + error(1, 0, "ivalid frame spec: %s", arg); + + id = name_to_frame_id(itm.name); + if (!id) { + frametype = id3_frametype_lookup(itm.name, strlen(itm.name)); + if (!frametype) + error(1, 0, "%s: unknown frame name", itm.name); + id = itm.name; + } else { + frametype = id3_frametype_lookup(id, 4); + assert(frametype != NULL); + } + + ft = idest_frame_lookup(id); + if (!ft) + error(1, 0, "don't know how to handle frame %s", id); + + memcpy(itm.id, id, sizeof(itm.id)); + if (itm.qc > ft->qc) + error(1, 0, + "too many field qualifiers for this frame: %s", arg); + + if (describe_option) { + free(itm.name); + itm.name = xstrdup(frametype->description); + } + + return ed_item_dup(&itm); +} + +struct ed_item * +ed_item_from_frame(struct id3_frame *frame) +{ + //FIXME; + abort(); +} + +void +ed_item_print(struct ed_item const *itm) +{ + printf("%s", itm->name); + if (itm->qc) { + int i; + + for (i = 0; i < itm->qc; i++) + printf(":%s", itm->qv[i] ? itm->qv[i] : ""); + } + printf(": %s\n", itm->value ? itm->value : ""); +} + +static bool +ed_item_eq(const void *elt1, const void *elt2) +{ + const struct ed_item *i1 = elt1; + const struct ed_item *i2 = elt2; + return strcmp(i1->id, i2->id) == 0; +} + +static void +ed_item_dispose(const void *elt) +{ + struct ed_item *itm = (struct ed_item *) elt; + ed_item_free(itm); +} + +gl_list_t +ed_list_create() +{ + return gl_list_create_empty(&gl_linked_list_implementation, + ed_item_eq, + NULL, + ed_item_dispose, + false); +} + + + + + + + + + + + diff --git a/src/frametab.gperf b/src/frametab.gperf new file mode 100644 index 0000000..84199fb --- /dev/null +++ b/src/frametab.gperf @@ -0,0 +1,430 @@ +%{ +/* This file is part of Idest. + Copyright (C) 2009-2011 Sergey Poznyakoff + + Idest is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + Idest is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Idest. If not, see <http://www.gnu.org/licenses/>. */ + +#include "idest.h" +#include "field.h" + +#define QID(name) _qual_##name +#define DQ(name) static char *QID(name)[] + +DQ(COMM) = { "lang", "condesc" }; +DQ(TXXX) = { "descr" }; + +#define QUAL(name) sizeof(QID(name))/sizeof(QID(name)[0]), QID(name) +#define QUALTEXT 0, NULL + +static int +decode_qv(struct ed_item *item, struct id3_frame const *frame, + int n, int count) +{ + union id3_field *field; + int i; + + item->qc = count; + item->qv = xcalloc(item->qc, sizeof(item->qv[0])); + for (i = 0; i < count; i++) { + field = id3_frame_field(frame, n + i); + if (!field) + break; + item->qv[i] = field_to_string(field, 0); + } + return 0; +} + +char *_idest_errstr[] = { + "no error", + "no such field", + "bad conversion", + "set error", + "operation not supported", + "unknown type", + "field number out of range" +}; +int _idest_nerrs = sizeof(_idest_errstr)/sizeof(_idest_errstr[0]); + +const char * +idest_strerror(int code) +{ + if (code < 0 || code > _idest_nerrs) + return "unknown error"; + return _idest_errstr[code]; +} + +static int +encode_field(struct id3_frame *frame, int n, const char *value) +{ + int rc = 0, res = 0; + enum id3_field_type type; + union id3_field *field; + id3_ucs4_t *ucs4; + struct id3_frametype const *frametype = + id3_frametype_lookup(frame->id, 4); + + if (!frametype) + return IDEST_ERR_BADTYPE; + if (n > frametype->nfields - 1) + /* last field is supposed to be a value */ + return IDEST_ERR_BADFIELD; + type = frametype->fields[n]; + + field = id3_frame_field(frame, n); + if (!field) + return IDEST_ERR_NOFIELD; + if (frametype->fields[n] == ID3_FIELD_TYPE_TEXTENCODING) { + /* Special case */ + if (id3_field_settextencoding(field, + latin1_option ? + ID3_FIELD_TEXTENCODING_ISO_8859_1 + : ID3_FIELD_TEXTENCODING_UTF_8)) + rc = IDEST_ERR_SET; + return rc; + } + + if (!value) { + id3_field_init(field, type); + return 0; + } + + switch (type) { + case ID3_FIELD_TYPE_INT8: + case ID3_FIELD_TYPE_INT16: + case ID3_FIELD_TYPE_INT24: + case ID3_FIELD_TYPE_INT32: { + char *p; + signed long number = strtoul(value, &p, 10); + if (*p) + rc = IDEST_ERR_BADCONV; + else + res = id3_field_setint(field, number); + break; + } + + case ID3_FIELD_TYPE_INT32PLUS: + /* FIXME: Unsupported */ + rc = IDEST_ERR_NOTSUPP; + break; + + case ID3_FIELD_TYPE_LATIN1: + case ID3_FIELD_TYPE_LATIN1FULL: + /* FIXME: Recode */ + res = id3_field_setlatin1(field, value); + break; + + case ID3_FIELD_TYPE_LATIN1LIST: + /* FIXME */ + rc = IDEST_ERR_NOTSUPP; + break; + + case ID3_FIELD_TYPE_STRING: + if (latin1_option) + ucs4 = id3_latin1_ucs4duplicate( + (const id3_latin1_t *)value); + else + ucs4 = id3_utf8_ucs4duplicate( + (const id3_utf8_t *)value); + res = id3_field_setstring(field, ucs4); + free(ucs4); + break; + + case ID3_FIELD_TYPE_STRINGFULL: + if (latin1_option) + ucs4 = id3_latin1_ucs4duplicate( + (const id3_latin1_t *)value); + else + ucs4 = id3_utf8_ucs4duplicate( + (const id3_utf8_t *)value); + res = id3_field_setfullstring(field, ucs4); + free(ucs4); + break; + + case ID3_FIELD_TYPE_STRINGLIST: + if (latin1_option) + ucs4 = id3_latin1_ucs4duplicate( + (const id3_latin1_t *)value); + else + ucs4 = id3_utf8_ucs4duplicate( + (const id3_utf8_t *)value); + res = id3_field_setstrings(field, 1, &ucs4); + free(ucs4); + break; + + case ID3_FIELD_TYPE_LANGUAGE: + res = id3_field_setlanguage(field, value); + break; + + case ID3_FIELD_TYPE_FRAMEID: + res = id3_field_setframeid(field, value); + break; + + case ID3_FIELD_TYPE_DATE: + /* FIXME: Convert and copy 8 bytes into + field->immediate.value */ + rc = IDEST_ERR_NOTSUPP; + break; + + case ID3_FIELD_TYPE_BINARYDATA: + rc = IDEST_ERR_NOTSUPP; + break; + + default: + rc = IDEST_ERR_BADTYPE; + } + if (rc == 0 && res) + rc = IDEST_ERR_SET; + return rc; +} + + +static int +encode_qv(struct id3_frame *frame, int frameoff, const struct ed_item *item) +{ + int i; + + for (i = 0; i < item->qc; i++) { + int rc = encode_field(frame, i + frameoff, item->qv[i]); + if (rc) + return rc; + } + return 0; +} + + +static int +comm_cmp(struct id3_frame const *frame, const struct ed_item const *item) +{ + int rc; + struct ed_item itm; + + memset(&itm, 0, sizeof(itm)); + if (decode_qv(&itm, frame, 1, 2)) + return 1; + + rc = ed_item_qv_cmp(&itm, item); + ed_item_free_content(&itm); + return rc; +} + +static int +comm_encode(struct id3_frame *frame, const struct ed_item *item) +{ + int rc; + union id3_field *field; + id3_ucs4_t *ucs4; + + /* Set encoding */ + encode_field(frame, 0, NULL); + /* Set rest of subfields */ + rc = encode_qv(frame, 1, item); + if (rc) + return rc; + + field = id3_frame_field(frame, 3); + if (latin1_option) + ucs4 = id3_latin1_ucs4duplicate( + (const id3_latin1_t *) item->value); + else + ucs4 = id3_utf8_ucs4duplicate( + (const id3_utf8_t *) item->value); + if (id3_field_setfullstring(field, ucs4)) + rc = IDEST_ERR_SET; + free(ucs4); + return rc; +} + +static int +comm_decode(struct ed_item *item, struct id3_frame const *frame) +{ + union id3_field *field; + + if (decode_qv(item, frame, 1, 2)) + return 1; + field = id3_frame_field(frame, 3); + if (!field) + return 1; + /* FIXME: Recode as necessary */ + item->value = field_to_string(field, 0); + + return 0; +} + +static int +text_encode(struct id3_frame *frame, struct ed_item const *item) +{ + const char *value = item->value; + union id3_field *field; + enum id3_field_textencoding encoding = + (latin1_option ? ID3_FIELD_TEXTENCODING_ISO_8859_1 + : ID3_FIELD_TEXTENCODING_UTF_8); + id3_ucs4_t *ucs4; + + field = id3_frame_field(frame, 0); + id3_field_settextencoding(field, encoding); + + if (latin1_option) + ucs4 = id3_latin1_ucs4duplicate((const id3_latin1_t *) value); + else + ucs4 = id3_utf8_ucs4duplicate((const id3_utf8_t *)value); + + + field = id3_frame_field(frame, 1); + id3_field_setstrings(field, 1, &ucs4); + free(ucs4); + return 0; +} + +static int +text_decode(struct ed_item *item, struct id3_frame const *frame) +{ + int isgenre = strcmp(frame->id, ID3_FRAME_GENRE) == 0; + enum id3_field_textencoding encoding; + union id3_field *field; + + field = id3_frame_field(frame, 0); + if (!field) + return 1; + encoding = id3_field_gettextencoding(field); + if (encoding == -1) + return 1; + field = id3_frame_field(frame, 1); + if (!field) + return 1; + /* FIXME: Recode as necessary */ + item->value = field_to_string(field, isgenre); + return 0; +} + +static int +txxx_cmp(struct id3_frame const *frame, const struct ed_item const *item) +{ + int rc; + struct ed_item itm; + + memset(&itm, 0, sizeof(itm)); + if (decode_qv(&itm, frame, 1, 1)) + return 1; + + rc = ed_item_qv_cmp(&itm, item); + ed_item_free_content(&itm); + return rc; +} + +static int +txxx_encode(struct id3_frame *frame, const struct ed_item *item) +{ + int rc; + union id3_field *field; + id3_ucs4_t *ucs4; + + /* Set encoding */ + encode_field(frame, 0, NULL); + /* Set rest of subfields */ + rc = encode_qv(frame, 1, item); + if (rc) + return rc; + + field = id3_frame_field(frame, 2); + if (latin1_option) + ucs4 = id3_latin1_ucs4duplicate( + (const id3_latin1_t *) item->value); + else + ucs4 = id3_utf8_ucs4duplicate( + (const id3_utf8_t *) item->value); + if (id3_field_setstring(field, ucs4)) + rc = IDEST_ERR_SET; + free(ucs4); + return rc; +} + +static int +txxx_decode(struct ed_item *item, struct id3_frame const *frame) +{ + union id3_field *field; + + if (decode_qv(item, frame, 1, 1)) + return 1; + field = id3_frame_field(frame, 2); + if (!field) + return 1; + /* FIXME: Recode as necessary */ + item->value = field_to_string(field, 0); + + return 0; +} + + +%} +%define lookup-function-name idest_frame_lookup_len +struct idest_frametab; +%% +COMM, QUAL(COMM), comm_cmp, comm_encode, comm_decode +TALB, QUALTEXT, NULL, text_encode, text_decode +TBPM, QUALTEXT, NULL, text_encode, text_decode +TCOM, QUALTEXT, NULL, text_encode, text_decode +TCON, QUALTEXT, NULL, text_encode, text_decode +TCOP, QUALTEXT, NULL, text_encode, text_decode +TDEN, QUALTEXT, NULL, text_encode, text_decode +TDLY, QUALTEXT, NULL, text_encode, text_decode +TDOR, QUALTEXT, NULL, text_encode, text_decode +TDRC, QUALTEXT, NULL, text_encode, text_decode +TDRL, QUALTEXT, NULL, text_encode, text_decode +TDTG, QUALTEXT, NULL, text_encode, text_decode +TENC, QUALTEXT, NULL, text_encode, text_decode +TEXT, QUALTEXT, NULL, text_encode, text_decode +TFLT, QUALTEXT, NULL, text_encode, text_decode +TIPL, QUALTEXT, NULL, text_encode, text_decode +TIT1, QUALTEXT, NULL, text_encode, text_decode +TIT2, QUALTEXT, NULL, text_encode, text_decode +TIT3, QUALTEXT, NULL, text_encode, text_decode +TKEY, QUALTEXT, NULL, text_encode, text_decode +TLAN, QUALTEXT, NULL, text_encode, text_decode +TLEN, QUALTEXT, NULL, text_encode, text_decode +TMCL, QUALTEXT, NULL, text_encode, text_decode +TMED, QUALTEXT, NULL, text_encode, text_decode +TMOO, QUALTEXT, NULL, text_encode, text_decode +TOAL, QUALTEXT, NULL, text_encode, text_decode +TOFN, QUALTEXT, NULL, text_encode, text_decode +TOLY, QUALTEXT, NULL, text_encode, text_decode +TOPE, QUALTEXT, NULL, text_encode, text_decode +TOWN, QUALTEXT, NULL, text_encode, text_decode +TPE1, QUALTEXT, NULL, text_encode, text_decode +TPE2, QUALTEXT, NULL, text_encode, text_decode +TPE3, QUALTEXT, NULL, text_encode, text_decode +TPE4, QUALTEXT, NULL, text_encode, text_decode +TPOS, QUALTEXT, NULL, text_encode, text_decode +TPRO, QUALTEXT, NULL, text_encode, text_decode +TPUB, QUALTEXT, NULL, text_encode, text_decode +TRCK, QUALTEXT, NULL, text_encode, text_decode +TRSN, QUALTEXT, NULL, text_encode, text_decode +TRSO, QUALTEXT, NULL, text_encode, text_decode +TSOA, QUALTEXT, NULL, text_encode, text_decode +TSOP, QUALTEXT, NULL, text_encode, text_decode +TSOT, QUALTEXT, NULL, text_encode, text_decode +TSRC, QUALTEXT, NULL, text_encode, text_decode +TSSE, QUALTEXT, NULL, text_encode, text_decode +TSST, QUALTEXT, NULL, text_encode, text_decode +TXXX, QUAL(TXXX), txxx_cmp, txxx_encode, txxx_decode +%% +const struct idest_frametab * +idest_frame_lookup(const char *str) +{ + return idest_frame_lookup_len(str, strlen(str)); +} +/* +Local variables: +mode: c +end: +*/ diff --git a/src/guile.c b/src/guile.c index c8b6e47..36427cf 100644 --- a/src/guile.c +++ b/src/guile.c @@ -30,10 +30,9 @@ SCM_GLOBAL_VARIABLE_INIT(sym_idest_main, "idest-main", SCM_EOL); SCM_GLOBAL_VARIABLE_INIT(sym_idest_readonly, "idest-readonly", SCM_BOOL_T); SCM_GLOBAL_SYMBOL(idest_text, "text"); -SCM_GLOBAL_SYMBOL(idest_lang, "lang"); -SCM_GLOBAL_SYMBOL(idest_condesc, "condesc"); SCM_GLOBAL_SYMBOL(idest_descr, "descr"); +SCM_GLOBAL_SYMBOL(idest_error, "idest-error"); static SCM eval_catch_body(void *list) @@ -224,52 +223,54 @@ field_to_scm(union id3_field *field, int genre) static SCM frame_to_scm(struct id3_frame *frame) { - SCM val; - union id3_field *field; - - switch (frame_to_item_id(frame->id)) { - case item_comment: - field = id3_frame_field(frame, 3); - val = scm_list_1( - scm_cons(idest_text, - scm_from_locale_string( - field_to_string(field, 0)))); - - field = id3_frame_field(frame, 1); - if (!field) - break; - val = scm_cons( - scm_cons(idest_lang, - scm_from_locale_string( - field_to_string(field, 0))), - val); - field = id3_frame_field(frame, 2); - if (!field) - break; - val = scm_cons( - scm_cons(idest_condesc, - scm_from_locale_string( - field_to_string(field, 0))), - val); - break; - - case item_genre: - val = scm_list_1( - scm_cons(idest_text, - field_to_scm(id3_frame_field(frame, 1), 1))); - break; - - default: - val = scm_list_1( - scm_cons(idest_text, - field_to_scm(id3_frame_field(frame, 1), 0))); - break; + int rc; + int i; + const struct idest_frametab *ft = idest_frame_lookup(frame->id); + struct ed_item itm; + SCM head = SCM_EOL, tail = SCM_EOL; + + if (!ft) + scm_error(idest_error, "frame_to_scm", + "frame ~A: ~A (~A)", + scm_list_3(scm_from_locale_string(frame->id), + scm_from_locale_string(idest_strerror(IDEST_ERR_BADTYPE)), + scm_from_int(IDEST_ERR_BADTYPE)), + SCM_BOOL_F); + + ed_item_zero(&itm); + itm.name = xstrdup(frame->id); + memcpy(itm.id, frame->id, 4); + + rc = ft->decode(&itm, frame); + if (rc) + scm_error(idest_error, "frame_to_scm", + "frame ~A: ~A (~A)", + scm_list_3(scm_from_locale_string(frame->id), + scm_from_locale_string(idest_strerror(rc)), + scm_from_int(rc)), + SCM_BOOL_F); + + head = scm_cons(scm_cons(idest_text, + scm_from_locale_string(itm.value)), + SCM_EOL); + tail = head; + + for (i = 0; i < itm.qc; i++) { + SCM cell; + + cell = scm_cons( + scm_cons(scm_string_to_symbol(scm_from_locale_string(ft->qv[i])), + scm_from_locale_string(itm.qv[i])), + SCM_EOL); + SCM_SETCDR(tail, cell); + tail = cell; } + ed_item_free_content(&itm); return scm_cons(scm_from_locale_string(frame->id), scm_cons( - scm_cons(idest_descr, - scm_from_locale_string(frame->description)), - val)); + scm_cons(idest_descr, + scm_from_locale_string(frame->description)), + head)); } static SCM @@ -297,11 +298,74 @@ tag_to_scm(struct id3_tag *tag) } static int +qvname_to_ind(const struct idest_frametab *ft, const char *name) +{ + int i; + + for (i = 0; i < ft->qc; i++) + if (strcmp(ft->qv[i], name) == 0) + return i; + return -1; +} + +static void +ed_item_from_scm(struct ed_item *itm, SCM list) +{ + const struct idest_frametab *ft = idest_frame_lookup(itm->id); + + if (!ft) + scm_error(idest_error, "ed_item_from_scm", + "frame ~A: ~A (~A)", + scm_list_3(scm_from_locale_string(itm->id), + scm_from_locale_string(idest_strerror(IDEST_ERR_BADTYPE)), + scm_from_int(IDEST_ERR_BADTYPE)), + SCM_BOOL_F); + itm->qc = ft->qc; + itm->qv = xcalloc(itm->qc, sizeof(itm->qv[0])); + for (; !scm_is_null(list) && scm_is_pair(list); list = SCM_CDR(list)) { + SCM elt = SCM_CAR(list); + SCM key; + char *s; + int n; + + if (!scm_is_pair(elt)) + scm_misc_error(NULL, + "Wrong element type: ~S", + scm_list_1(elt)); + key = SCM_CAR(elt); + if (key == idest_text || key == idest_descr) + continue; + s = scm_to_locale_string(scm_symbol_to_string(key)); + n = qvname_to_ind(ft, s); + if (n == -1) { + /* FIXME: field name? */ + scm_error(idest_error, "ed_item_from_scm", + "frame ~A: ~A (~A)", + scm_list_3(scm_from_locale_string(itm->id), + scm_from_locale_string(idest_strerror(IDEST_ERR_BADFIELD)), + scm_from_int(IDEST_ERR_BADFIELD)), + SCM_BOOL_F); + } + free(s); + itm->qv[n] = scm_to_locale_string(SCM_CDR(elt)); + } +#if 0 + /* FIXME: Provide defaults? */ + for (i = 0; i < itm->qc; i++) { + if (!itm->qv[i]) + itm->qv[i] = xstrdup(""); + } +#endif +} + +static int scm_to_tag(SCM scm, struct id3_tag *tag) { int modified = 0; for (; !scm_is_null(scm) && scm_is_pair(scm); scm = SCM_CDR(scm)) { + int rc; + struct id3_frametype const *frametype; struct id3_frame *frame; struct ed_item itm; char *id; @@ -318,22 +382,29 @@ scm_to_tag(SCM scm, struct id3_tag *tag) "Wrong car type: ~S", scm_list_1(elt)); id = scm_to_locale_string(x); - - memset(&itm, 0, sizeof(itm)); + frametype = id3_frametype_lookup(id, strlen(id)); + if (!id) + scm_error(idest_error, "guile-transform", + "frame ~A: ~A (~A)", + scm_list_3(x, + scm_from_locale_string(idest_strerror(IDEST_ERR_BADTYPE)), + scm_from_int(IDEST_ERR_BADTYPE)), + SCM_BOOL_F); + + ed_item_zero(&itm); + memcpy(itm.id, id, sizeof(itm.id)); + itm.name = id; + x = SCM_CDR(elt); if (scm_is_string(x)) { - itm.v.value = scm_to_locale_string(x); + itm.value = scm_to_locale_string(x); } else if (scm_is_pair(x) && - (text = scm_assoc_ref(x, idest_text)) != SCM_BOOL_F) { - itm.v.value = scm_to_locale_string(text); - text = scm_assoc_ref(x, idest_lang); - if (text != SCM_BOOL_F) - itm.lang = scm_to_locale_string(text); - text = scm_assoc_ref(x, idest_condesc); - if (text != SCM_BOOL_F) - itm.condesc = scm_to_locale_string(text); + (text = scm_assoc_ref(x, idest_text)) != + SCM_BOOL_F) { + itm.value = scm_to_locale_string(text); + ed_item_from_scm(&itm, x); } else scm_misc_error(NULL, "Wrong cdr type: ~S", @@ -341,13 +412,21 @@ scm_to_tag(SCM scm, struct id3_tag *tag) frame = id3_frame_new(id); if (id3_tag_attachframe(tag, frame)) + /* FIXME: user scm_error */ error(1, 0, "cannot attach new frame"); - set_frame_value(frame, &itm); + rc = set_frame_value(frame, &itm); + if (rc) + scm_error(idest_error, "guile-transform", + "frame ~A: ~A (~A)", + scm_list_3(scm_from_locale_string(frame->id), + scm_from_locale_string(idest_strerror(rc)), + scm_from_int(rc)), + SCM_BOOL_F); + modified |= 1; free(id); - free(itm.v.value); - free(itm.condesc); - free(itm.lang); + free(itm.value); + qv_free(itm.qc, itm.qv); } return modified; } diff --git a/src/idest.h b/src/idest.h index 3494b08..54f6fb6 100644 --- a/src/idest.h +++ b/src/idest.h @@ -28,6 +28,7 @@ #include <backupfile.h> #include <id3tag.h> #include <frametype.h> +#include <assert.h> #define gettext(s) s #define _(s) s @@ -53,13 +54,10 @@ enum item_id { struct ed_item { char *name; /* Printable name */ char id[5]; /* Item ID */ - char *lang; /* Comment language */ - char *condesc; /* Content descriptor */ - int flags; /* Implementation-dependent flags */ - union { - gl_list_t vlist; /* List of strings, used with --query */ - char *value; /* New value, used with --set */ - } v; + int qc; /* Number of name qualifiers */ + char **qv; /* Name qualifiers */ + char *value; /* Value */ + struct ed_item const *ref; /* Reference item */ }; #define MODE_QUERY 0 @@ -69,6 +67,9 @@ struct ed_item { extern int mode; extern int latin1_option; +extern int describe_option; +extern int verbose_option; +extern int all_frames; extern enum backup_type backup_type; extern char *backup_dir; extern gl_list_t ed_list; @@ -81,7 +82,7 @@ extern char *guile_function; extern char **guile_argv; /* idop.c */ -void set_frame_value(struct id3_frame *frame, const struct ed_item *item); +int set_frame_value(struct id3_frame *frame, const struct ed_item *item); void set_tags(const char *name); void del_tags(const char *name); char *idest_ucs4_cvt(id3_ucs4_t const *ucs4); @@ -98,15 +99,66 @@ gl_list_t new_string_list(bool allow_duplicates); int do_string_list(gl_list_t list, string_list_action_fn action, void *data); void concat_string_list(gl_list_t list, gl_list_t addlist); void print_string_list(FILE *fp, gl_list_t list); + +/* frametab.gperf */ +#define IDEST_OK 0 +#define IDEST_ERR_NOFIELD 1 +#define IDEST_ERR_BADCONV 2 +#define IDEST_ERR_SET 3 +#define IDEST_ERR_NOTSUPP 4 +#define IDEST_ERR_BADTYPE 5 +#define IDEST_ERR_BADFIELD 6 + +extern char *_idest_errstr[]; +extern int _idest_nerrs; +const char *idest_strerror(int code); + +typedef int (*idest_frame_cmp_t) (struct id3_frame const *frame, + const struct ed_item const *item); +typedef int (*idest_frame_encoder_t) (struct id3_frame *frame, + const struct ed_item const *item); +typedef int (*idest_frame_decoder_t) (struct ed_item *pitem, + struct id3_frame const *frame); + + +struct idest_frametab { + const char id[5]; + int qc; + char **qv; + idest_frame_cmp_t cmp; + idest_frame_encoder_t encode; + idest_frame_decoder_t decode; +}; + +const struct idest_frametab *idest_frame_lookup(const char *str); +struct id3_frame *find_matching_frame(struct id3_tag *tag, + const struct ed_item *item, + idest_frame_cmp_t cmp); +/* main.c */ +const char *name_to_frame_id(const char *name); int frame_to_item_id(const char *arg); -void ed_list_add_item(struct id3_frame *frame, gl_list_t list); -void ed_list_print(void); -void ed_list_add_assignment(const char *name, const char *value); -void ed_list_clear(void); -int ed_item_matches_frame(struct ed_item const *itm, struct id3_frame *frame); -void ed_item_set_comment_fields(struct ed_item *itm, struct id3_frame *frame); +void qv_free(size_t qc, char **qv); +struct ed_item const *input_list_locate(struct id3_frame *frame, + idest_frame_cmp_t cmp); +void parse_ed_items(const char *arg); +void output_list_append(struct ed_item const *item, struct ed_item const *ref); +void output_list_print(void); +void output_list_free(void); + + +/* editem.c */ +void ed_item_zero(struct ed_item *item); +void ed_item_free(struct ed_item *item); +void ed_item_free_content(struct ed_item *item); +int ed_item_qv_cmp(struct ed_item const *a, struct ed_item const *b); +struct ed_item *ed_item_create0(char *name, const char *id); +struct ed_item *ed_item_create(const char *name, const char *id); +struct ed_item *ed_item_dup(struct ed_item const *orig); +struct ed_item *ed_item_from_frame_spec(const char *arg, size_t len); +gl_list_t ed_list_create(void); +void ed_item_print(struct ed_item const *itm); /* backup.c */ @@ -117,3 +169,4 @@ int backup_file(const char *file); void guile_init(int *pargc, char ***pargv); int guile_transform(const char *file, struct id3_tag *tag); int guile_list(const char *file, struct id3_tag |