FFmpeg
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
bprint.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012 Nicolas George
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 <stdarg.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <time.h>
25 #include "avassert.h"
26 #include "avstring.h"
27 #include "bprint.h"
28 #include "common.h"
29 #include "error.h"
30 #include "mem.h"
31 
32 #define av_bprint_room(buf) ((buf)->size - FFMIN((buf)->len, (buf)->size))
33 #define av_bprint_is_allocated(buf) ((buf)->str != (buf)->reserved_internal_buffer)
34 
35 static int av_bprint_alloc(AVBPrint *buf, unsigned room)
36 {
37  char *old_str, *new_str;
38  unsigned min_size, new_size;
39 
40  if (buf->size == buf->size_max)
41  return AVERROR(EIO);
42  if (!av_bprint_is_complete(buf))
43  return AVERROR_INVALIDDATA; /* it is already truncated anyway */
44  min_size = buf->len + 1 + FFMIN(UINT_MAX - buf->len - 1, room);
45  new_size = buf->size > buf->size_max / 2 ? buf->size_max : buf->size * 2;
46  if (new_size < min_size)
47  new_size = FFMIN(buf->size_max, min_size);
48  old_str = av_bprint_is_allocated(buf) ? buf->str : NULL;
49  new_str = av_realloc(old_str, new_size);
50  if (!new_str)
51  return AVERROR(ENOMEM);
52  if (!old_str)
53  memcpy(new_str, buf->str, buf->len + 1);
54  buf->str = new_str;
55  buf->size = new_size;
56  return 0;
57 }
58 
59 static void av_bprint_grow(AVBPrint *buf, unsigned extra_len)
60 {
61  /* arbitrary margin to avoid small overflows */
62  extra_len = FFMIN(extra_len, UINT_MAX - 5 - buf->len);
63  buf->len += extra_len;
64  if (buf->size)
65  buf->str[FFMIN(buf->len, buf->size - 1)] = 0;
66 }
67 
68 void av_bprint_init(AVBPrint *buf, unsigned size_init, unsigned size_max)
69 {
70  unsigned size_auto = (char *)buf + sizeof(*buf) -
71  buf->reserved_internal_buffer;
72 
73  if (size_max == 1)
74  size_max = size_auto;
75  buf->str = buf->reserved_internal_buffer;
76  buf->len = 0;
77  buf->size = FFMIN(size_auto, size_max);
78  buf->size_max = size_max;
79  *buf->str = 0;
80  if (size_init > buf->size)
81  av_bprint_alloc(buf, size_init - 1);
82 }
83 
84 void av_bprint_init_for_buffer(AVBPrint *buf, char *buffer, unsigned size)
85 {
86  buf->str = buffer;
87  buf->len = 0;
88  buf->size = size;
89  buf->size_max = size;
90  *buf->str = 0;
91 }
92 
93 void av_bprintf(AVBPrint *buf, const char *fmt, ...)
94 {
95  unsigned room;
96  char *dst;
97  va_list vl;
98  int extra_len;
99 
100  while (1) {
101  room = av_bprint_room(buf);
102  dst = room ? buf->str + buf->len : NULL;
103  va_start(vl, fmt);
104  extra_len = vsnprintf(dst, room, fmt, vl);
105  va_end(vl);
106  if (extra_len <= 0)
107  return;
108  if (extra_len < room)
109  break;
110  if (av_bprint_alloc(buf, extra_len))
111  break;
112  }
113  av_bprint_grow(buf, extra_len);
114 }
115 
116 void av_bprint_chars(AVBPrint *buf, char c, unsigned n)
117 {
118  unsigned room, real_n;
119 
120  while (1) {
121  room = av_bprint_room(buf);
122  if (n < room)
123  break;
124  if (av_bprint_alloc(buf, n))
125  break;
126  }
127  if (room) {
128  real_n = FFMIN(n, room - 1);
129  memset(buf->str + buf->len, c, real_n);
130  }
131  av_bprint_grow(buf, n);
132 }
133 
134 void av_bprint_strftime(AVBPrint *buf, const char *fmt, const struct tm *tm)
135 {
136  unsigned room;
137  size_t l;
138 
139  if (!*fmt)
140  return;
141  while (1) {
142  room = av_bprint_room(buf);
143  if (room && (l = strftime(buf->str + buf->len, room, fmt, tm)))
144  break;
145  /* strftime does not tell us how much room it would need: let us
146  retry with twice as much until the buffer is large enough */
147  room = !room ? strlen(fmt) + 1 :
148  room <= INT_MAX / 2 ? room * 2 : INT_MAX;
149  if (av_bprint_alloc(buf, room)) {
150  /* impossible to grow, try to manage something useful anyway */
151  room = av_bprint_room(buf);
152  if (room < 1024) {
153  /* if strftime fails because the buffer has (almost) reached
154  its maximum size, let us try in a local buffer; 1k should
155  be enough to format any real date+time string */
156  char buf2[1024];
157  if ((l = strftime(buf2, sizeof(buf2), fmt, tm))) {
158  av_bprintf(buf, "%s", buf2);
159  return;
160  }
161  }
162  if (room) {
163  /* if anything else failed and the buffer is not already
164  truncated, let us add a stock string and force truncation */
165  static const char txt[] = "[truncated strftime output]";
166  memset(buf->str + buf->len, '!', room);
167  memcpy(buf->str + buf->len, txt, FFMIN(sizeof(txt) - 1, room));
168  av_bprint_grow(buf, room); /* force truncation */
169  }
170  return;
171  }
172  }
173  av_bprint_grow(buf, l);
174 }
175 
176 void av_bprint_get_buffer(AVBPrint *buf, unsigned size,
177  unsigned char **mem, unsigned *actual_size)
178 {
179  if (size > av_bprint_room(buf))
180  av_bprint_alloc(buf, size);
181  *actual_size = av_bprint_room(buf);
182  *mem = *actual_size ? buf->str + buf->len : NULL;
183 }
184 
186 {
187  if (buf->len) {
188  *buf->str = 0;
189  buf->len = 0;
190  }
191 }
192 
194 {
195  unsigned real_size = FFMIN(buf->len + 1, buf->size);
196  char *str;
197  int ret = 0;
198 
199  if (ret_str) {
200  if (av_bprint_is_allocated(buf)) {
201  str = av_realloc(buf->str, real_size);
202  if (!str)
203  str = buf->str;
204  buf->str = NULL;
205  } else {
206  str = av_malloc(real_size);
207  if (str)
208  memcpy(str, buf->str, real_size);
209  else
210  ret = AVERROR(ENOMEM);
211  }
212  *ret_str = str;
213  } else {
214  if (av_bprint_is_allocated(buf))
215  av_freep(&buf->str);
216  }
217  buf->size = real_size;
218  return ret;
219 }
220 
221 #define WHITESPACES " \n\t"
222 
223 void av_bprint_escape(AVBPrint *dstbuf, const char *src, const char *special_chars,
224  enum AVEscapeMode mode, int flags)
225 {
226  const char *src0 = src;
227 
228  if (mode == AV_ESCAPE_MODE_AUTO)
229  mode = AV_ESCAPE_MODE_BACKSLASH; /* TODO: implement a heuristic */
230 
231  switch (mode) {
233  /* enclose the string between '' */
234  av_bprint_chars(dstbuf, '\'', 1);
235  for (; *src; src++) {
236  if (*src == '\'')
237  av_bprintf(dstbuf, "'\\''");
238  else
239  av_bprint_chars(dstbuf, *src, 1);
240  }
241  av_bprint_chars(dstbuf, '\'', 1);
242  break;
243 
244  /* case AV_ESCAPE_MODE_BACKSLASH or unknown mode */
245  default:
246  /* \-escape characters */
247  for (; *src; src++) {
248  int is_first_last = src == src0 || !*(src+1);
249  int is_ws = !!strchr(WHITESPACES, *src);
250  int is_strictly_special = special_chars && strchr(special_chars, *src);
251  int is_special =
252  is_strictly_special || strchr("'\\", *src) ||
253  (is_ws && (flags & AV_ESCAPE_FLAG_WHITESPACE));
254 
255  if (is_strictly_special ||
256  (!(flags & AV_ESCAPE_FLAG_STRICT) &&
257  (is_special || (is_ws && is_first_last))))
258  av_bprint_chars(dstbuf, '\\', 1);
259  av_bprint_chars(dstbuf, *src, 1);
260  }
261  break;
262  }
263 }
264 
265 #ifdef TEST
266 
267 #undef printf
268 
269 static void bprint_pascal(AVBPrint *b, unsigned size)
270 {
271  unsigned i, j;
272  unsigned p[42];
273 
274  av_assert0(size < FF_ARRAY_ELEMS(p));
275 
276  p[0] = 1;
277  av_bprintf(b, "%8d\n", 1);
278  for (i = 1; i <= size; i++) {
279  p[i] = 1;
280  for (j = i - 1; j > 0; j--)
281  p[j] = p[j] + p[j - 1];
282  for (j = 0; j <= i; j++)
283  av_bprintf(b, "%8d", p[j]);
284  av_bprintf(b, "\n");
285  }
286 }
287 
288 int main(void)
289 {
290  AVBPrint b;
291  char buf[256];
292  struct tm testtime = { .tm_year = 100, .tm_mon = 11, .tm_mday = 20 };
293 
294  av_bprint_init(&b, 0, -1);
295  bprint_pascal(&b, 5);
296  printf("Short text in unlimited buffer: %u/%u\n", (unsigned)strlen(b.str), b.len);
297  printf("%s\n", b.str);
299 
300  av_bprint_init(&b, 0, -1);
301  bprint_pascal(&b, 25);
302  printf("Long text in unlimited buffer: %u/%u\n", (unsigned)strlen(b.str), b.len);
304 
305  av_bprint_init(&b, 0, 2048);
306  bprint_pascal(&b, 25);
307  printf("Long text in limited buffer: %u/%u\n", (unsigned)strlen(b.str), b.len);
309 
310  av_bprint_init(&b, 0, 1);
311  bprint_pascal(&b, 5);
312  printf("Short text in automatic buffer: %u/%u\n", (unsigned)strlen(b.str), b.len);
313 
314  av_bprint_init(&b, 0, 1);
315  bprint_pascal(&b, 25);
316  printf("Long text in automatic buffer: %u/%u\n", (unsigned)strlen(b.str)/8*8, b.len);
317  /* Note that the size of the automatic buffer is arch-dependant. */
318 
319  av_bprint_init(&b, 0, 0);
320  bprint_pascal(&b, 25);
321  printf("Long text count only buffer: %u/%u\n", (unsigned)strlen(b.str), b.len);
322 
323  av_bprint_init_for_buffer(&b, buf, sizeof(buf));
324  bprint_pascal(&b, 25);
325  printf("Long text count only buffer: %u/%u\n", (unsigned)strlen(buf), b.len);
326 
327  av_bprint_init(&b, 0, -1);
328  av_bprint_strftime(&b, "%Y-%m-%d", &testtime);
329  printf("strftime full: %u/%u \"%s\"\n", (unsigned)strlen(buf), b.len, b.str);
331 
332  av_bprint_init(&b, 0, 8);
333  av_bprint_strftime(&b, "%Y-%m-%d", &testtime);
334  printf("strftime truncated: %u/%u \"%s\"\n", (unsigned)strlen(buf), b.len, b.str);
335 
336  return 0;
337 }
338 
339 #endif