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