xref: /third_party/ffmpeg/libavutil/dict.c (revision cabdff1a)
1/*
2 * copyright (c) 2009 Michael Niedermayer
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <string.h>
22
23#include "avstring.h"
24#include "dict.h"
25#include "internal.h"
26#include "mem.h"
27#include "time_internal.h"
28#include "bprint.h"
29
30struct AVDictionary {
31    int count;
32    AVDictionaryEntry *elems;
33};
34
35int av_dict_count(const AVDictionary *m)
36{
37    return m ? m->count : 0;
38}
39
40AVDictionaryEntry *av_dict_get(const AVDictionary *m, const char *key,
41                               const AVDictionaryEntry *prev, int flags)
42{
43    unsigned int i, j;
44
45    if (!m)
46        return NULL;
47
48    if (prev)
49        i = prev - m->elems + 1;
50    else
51        i = 0;
52
53    for (; i < m->count; i++) {
54        const char *s = m->elems[i].key;
55        if (flags & AV_DICT_MATCH_CASE)
56            for (j = 0; s[j] == key[j] && key[j]; j++)
57                ;
58        else
59            for (j = 0; av_toupper(s[j]) == av_toupper(key[j]) && key[j]; j++)
60                ;
61        if (key[j])
62            continue;
63        if (s[j] && !(flags & AV_DICT_IGNORE_SUFFIX))
64            continue;
65        return &m->elems[i];
66    }
67    return NULL;
68}
69
70int av_dict_set(AVDictionary **pm, const char *key, const char *value,
71                int flags)
72{
73    AVDictionary *m = *pm;
74    AVDictionaryEntry *tag = NULL;
75    char *oldval = NULL, *copy_key = NULL, *copy_value = NULL;
76
77    if (!(flags & AV_DICT_MULTIKEY)) {
78        tag = av_dict_get(m, key, NULL, flags);
79    }
80    if (flags & AV_DICT_DONT_STRDUP_KEY)
81        copy_key = (void *)key;
82    else
83        copy_key = av_strdup(key);
84    if (flags & AV_DICT_DONT_STRDUP_VAL)
85        copy_value = (void *)value;
86    else if (copy_key)
87        copy_value = av_strdup(value);
88    if (!m)
89        m = *pm = av_mallocz(sizeof(*m));
90    if (!m || (key && !copy_key) || (value && !copy_value))
91        goto err_out;
92
93    if (tag) {
94        if (flags & AV_DICT_DONT_OVERWRITE) {
95            av_free(copy_key);
96            av_free(copy_value);
97            return 0;
98        }
99        if (flags & AV_DICT_APPEND)
100            oldval = tag->value;
101        else
102            av_free(tag->value);
103        av_free(tag->key);
104        *tag = m->elems[--m->count];
105    } else if (copy_value) {
106        AVDictionaryEntry *tmp = av_realloc_array(m->elems,
107                                                  m->count + 1, sizeof(*m->elems));
108        if (!tmp)
109            goto err_out;
110        m->elems = tmp;
111    }
112    if (copy_value) {
113        m->elems[m->count].key = copy_key;
114        m->elems[m->count].value = copy_value;
115        if (oldval && flags & AV_DICT_APPEND) {
116            size_t len = strlen(oldval) + strlen(copy_value) + 1;
117            char *newval = av_mallocz(len);
118            if (!newval)
119                goto err_out;
120            av_strlcat(newval, oldval, len);
121            av_freep(&oldval);
122            av_strlcat(newval, copy_value, len);
123            m->elems[m->count].value = newval;
124            av_freep(&copy_value);
125        }
126        m->count++;
127    } else {
128        av_freep(&copy_key);
129    }
130    if (!m->count) {
131        av_freep(&m->elems);
132        av_freep(pm);
133    }
134
135    return 0;
136
137err_out:
138    if (m && !m->count) {
139        av_freep(&m->elems);
140        av_freep(pm);
141    }
142    av_free(copy_key);
143    av_free(copy_value);
144    return AVERROR(ENOMEM);
145}
146
147int av_dict_set_int(AVDictionary **pm, const char *key, int64_t value,
148                int flags)
149{
150    char valuestr[22];
151    snprintf(valuestr, sizeof(valuestr), "%"PRId64, value);
152    flags &= ~AV_DICT_DONT_STRDUP_VAL;
153    return av_dict_set(pm, key, valuestr, flags);
154}
155
156static int parse_key_value_pair(AVDictionary **pm, const char **buf,
157                                const char *key_val_sep, const char *pairs_sep,
158                                int flags)
159{
160    char *key = av_get_token(buf, key_val_sep);
161    char *val = NULL;
162    int ret;
163
164    if (key && *key && strspn(*buf, key_val_sep)) {
165        (*buf)++;
166        val = av_get_token(buf, pairs_sep);
167    }
168
169    if (key && *key && val && *val)
170        ret = av_dict_set(pm, key, val, flags);
171    else
172        ret = AVERROR(EINVAL);
173
174    av_freep(&key);
175    av_freep(&val);
176
177    return ret;
178}
179
180int av_dict_parse_string(AVDictionary **pm, const char *str,
181                         const char *key_val_sep, const char *pairs_sep,
182                         int flags)
183{
184    int ret;
185
186    if (!str)
187        return 0;
188
189    /* ignore STRDUP flags */
190    flags &= ~(AV_DICT_DONT_STRDUP_KEY | AV_DICT_DONT_STRDUP_VAL);
191
192    while (*str) {
193        if ((ret = parse_key_value_pair(pm, &str, key_val_sep, pairs_sep, flags)) < 0)
194            return ret;
195
196        if (*str)
197            str++;
198    }
199
200    return 0;
201}
202
203void av_dict_free(AVDictionary **pm)
204{
205    AVDictionary *m = *pm;
206
207    if (m) {
208        while (m->count--) {
209            av_freep(&m->elems[m->count].key);
210            av_freep(&m->elems[m->count].value);
211        }
212        av_freep(&m->elems);
213    }
214    av_freep(pm);
215}
216
217int av_dict_copy(AVDictionary **dst, const AVDictionary *src, int flags)
218{
219    AVDictionaryEntry *t = NULL;
220
221    while ((t = av_dict_get(src, "", t, AV_DICT_IGNORE_SUFFIX))) {
222        int ret = av_dict_set(dst, t->key, t->value, flags);
223        if (ret < 0)
224            return ret;
225    }
226
227    return 0;
228}
229
230int av_dict_get_string(const AVDictionary *m, char **buffer,
231                       const char key_val_sep, const char pairs_sep)
232{
233    AVDictionaryEntry *t = NULL;
234    AVBPrint bprint;
235    int cnt = 0;
236    char special_chars[] = {pairs_sep, key_val_sep, '\0'};
237
238    if (!buffer || pairs_sep == '\0' || key_val_sep == '\0' || pairs_sep == key_val_sep ||
239        pairs_sep == '\\' || key_val_sep == '\\')
240        return AVERROR(EINVAL);
241
242    if (!av_dict_count(m)) {
243        *buffer = av_strdup("");
244        return *buffer ? 0 : AVERROR(ENOMEM);
245    }
246
247    av_bprint_init(&bprint, 64, AV_BPRINT_SIZE_UNLIMITED);
248    while ((t = av_dict_get(m, "", t, AV_DICT_IGNORE_SUFFIX))) {
249        if (cnt++)
250            av_bprint_append_data(&bprint, &pairs_sep, 1);
251        av_bprint_escape(&bprint, t->key, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
252        av_bprint_append_data(&bprint, &key_val_sep, 1);
253        av_bprint_escape(&bprint, t->value, special_chars, AV_ESCAPE_MODE_BACKSLASH, 0);
254    }
255    return av_bprint_finalize(&bprint, buffer);
256}
257
258int avpriv_dict_set_timestamp(AVDictionary **dict, const char *key, int64_t timestamp)
259{
260    time_t seconds = timestamp / 1000000;
261    struct tm *ptm, tmbuf;
262    ptm = gmtime_r(&seconds, &tmbuf);
263    if (ptm) {
264        char buf[32];
265        if (!strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", ptm))
266            return AVERROR_EXTERNAL;
267        av_strlcatf(buf, sizeof(buf), ".%06dZ", (int)(timestamp % 1000000));
268        return av_dict_set(dict, key, buf, 0);
269    } else {
270        return AVERROR_EXTERNAL;
271    }
272}
273