1 /*
2 * This file is part of mpv.
3 *
4 * mpv is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * mpv 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
12 * GNU Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stddef.h>
19 #include <limits.h>
20 #include <strings.h>
21 #include <assert.h>
22 #include <libavutil/dict.h>
23 #include "tags.h"
24 #include "misc/bstr.h"
25
mp_tags_set_str(struct mp_tags * tags,const char * key,const char * value)26 void mp_tags_set_str(struct mp_tags *tags, const char *key, const char *value)
27 {
28 mp_tags_set_bstr(tags, bstr0(key), bstr0(value));
29 }
30
mp_tags_set_bstr(struct mp_tags * tags,bstr key,bstr value)31 void mp_tags_set_bstr(struct mp_tags *tags, bstr key, bstr value)
32 {
33 for (int n = 0; n < tags->num_keys; n++) {
34 if (bstrcasecmp0(key, tags->keys[n]) == 0) {
35 talloc_free(tags->values[n]);
36 tags->values[n] = bstrto0(tags, value);
37 return;
38 }
39 }
40
41 MP_RESIZE_ARRAY(tags, tags->keys, tags->num_keys + 1);
42 MP_RESIZE_ARRAY(tags, tags->values, tags->num_keys + 1);
43 tags->keys[tags->num_keys] = bstrto0(tags, key);
44 tags->values[tags->num_keys] = bstrto0(tags, value);
45 tags->num_keys++;
46 }
47
mp_tags_remove_str(struct mp_tags * tags,const char * key)48 void mp_tags_remove_str(struct mp_tags *tags, const char *key)
49 {
50 mp_tags_remove_bstr(tags, bstr0(key));
51 }
52
mp_tags_remove_bstr(struct mp_tags * tags,bstr key)53 void mp_tags_remove_bstr(struct mp_tags *tags, bstr key)
54 {
55 for (int n = 0; n < tags->num_keys; n++) {
56 if (bstrcasecmp0(key, tags->keys[n]) == 0) {
57 talloc_free(tags->keys[n]);
58 talloc_free(tags->values[n]);
59 int num_keys = tags->num_keys; // copy so it's only decremented once
60 MP_TARRAY_REMOVE_AT(tags->keys, num_keys, n);
61 MP_TARRAY_REMOVE_AT(tags->values, tags->num_keys, n);
62 }
63 }
64 }
65
mp_tags_get_str(struct mp_tags * tags,const char * key)66 char *mp_tags_get_str(struct mp_tags *tags, const char *key)
67 {
68 return mp_tags_get_bstr(tags, bstr0(key));
69 }
70
mp_tags_get_bstr(struct mp_tags * tags,bstr key)71 char *mp_tags_get_bstr(struct mp_tags *tags, bstr key)
72 {
73 for (int n = 0; n < tags->num_keys; n++) {
74 if (bstrcasecmp0(key, tags->keys[n]) == 0)
75 return tags->values[n];
76 }
77 return NULL;
78 }
79
mp_tags_clear(struct mp_tags * tags)80 void mp_tags_clear(struct mp_tags *tags)
81 {
82 *tags = (struct mp_tags){0};
83 talloc_free_children(tags);
84 }
85
86
87
mp_tags_dup(void * tparent,struct mp_tags * tags)88 struct mp_tags *mp_tags_dup(void *tparent, struct mp_tags *tags)
89 {
90 struct mp_tags *new = talloc_zero(tparent, struct mp_tags);
91 mp_tags_replace(new, tags);
92 return new;
93 }
94
mp_tags_replace(struct mp_tags * dst,struct mp_tags * src)95 void mp_tags_replace(struct mp_tags *dst, struct mp_tags *src)
96 {
97 mp_tags_clear(dst);
98 MP_RESIZE_ARRAY(dst, dst->keys, src->num_keys);
99 MP_RESIZE_ARRAY(dst, dst->values, src->num_keys);
100 dst->num_keys = src->num_keys;
101 for (int n = 0; n < src->num_keys; n++) {
102 dst->keys[n] = talloc_strdup(dst, src->keys[n]);
103 dst->values[n] = talloc_strdup(dst, src->values[n]);
104 }
105 }
106
107 // Return a copy of the tags, but containing only keys in list. Also forces
108 // the order and casing of the keys (for cosmetic reasons).
109 // A trailing '*' matches the rest.
mp_tags_filtered(void * tparent,struct mp_tags * tags,char ** list)110 struct mp_tags *mp_tags_filtered(void *tparent, struct mp_tags *tags, char **list)
111 {
112 struct mp_tags *new = talloc_zero(tparent, struct mp_tags);
113 for (int n = 0; list && list[n]; n++) {
114 char *key = list[n];
115 size_t keylen = strlen(key);
116 if (keylen >= INT_MAX)
117 continue;
118 bool prefix = keylen && key[keylen - 1] == '*';
119 int matchlen = prefix ? keylen - 1 : keylen + 1;
120 for (int x = 0; x < tags->num_keys; x++) {
121 if (strncasecmp(tags->keys[x], key, matchlen) == 0) {
122 char skey[320];
123 snprintf(skey, sizeof(skey), "%.*s%s", matchlen, key,
124 prefix ? tags->keys[x] + keylen - 1 : "");
125 mp_tags_set_str(new, skey, tags->values[x]);
126 }
127 }
128 }
129 return new;
130 }
131
mp_tags_merge(struct mp_tags * tags,struct mp_tags * src)132 void mp_tags_merge(struct mp_tags *tags, struct mp_tags *src)
133 {
134 for (int n = 0; n < src->num_keys; n++)
135 mp_tags_set_str(tags, src->keys[n], src->values[n]);
136 }
137
mp_tags_copy_from_av_dictionary(struct mp_tags * tags,struct AVDictionary * av_dict)138 void mp_tags_copy_from_av_dictionary(struct mp_tags *tags,
139 struct AVDictionary *av_dict)
140 {
141 AVDictionaryEntry *entry = NULL;
142 while ((entry = av_dict_get(av_dict, "", entry, AV_DICT_IGNORE_SUFFIX)))
143 mp_tags_set_str(tags, entry->key, entry->value);
144 }
145