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(©_value); 125 } 126 m->count++; 127 } else { 128 av_freep(©_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