]> git.sesse.net Git - vlc/blob - modules/demux/asademux.c
Replace argument = realloc( argument, size ); with realloc_or_free() in modules/...
[vlc] / modules / demux / asademux.c
1 /*****************************************************************************
2  * asademux.c: asa demuxer VM
3  *****************************************************************************
4  * Copyright (C) 2007 the VideoLAN team
5  *
6  * Originated from asa: portable digital subtitle renderer
7  *
8  * Authors: David Lamparter <equinox at diac24 dot net>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
23  ****************************************************************************/
24
25 /****************************************************************************
26  * Changes from asa version:
27  *  - headers adapted
28  *  - external definition file support dropped
29  *  - integer timestamps
30  ****************************************************************************
31  * Please retain Linux kernel CodingStyle for sync.
32  * base commit d8c269b0fae9a8f8904e16e92313da165d664c74
33  ****************************************************************************/
34 #include "config.h"
35 #include <vlc_common.h>
36 #include <vlc_input.h>
37 #include <vlc_demux.h>
38 #include <vlc_memory.h>
39
40 #include <limits.h>
41 #include <string.h>
42 #include <assert.h>
43
44 #include "asademux.h"
45
46 #define MAXDELTA        4       /**< nr of times kept for delta backref */
47 #define MAXGROUP        24      /**< maximum number of regex match groups */
48
49 #define xmalloc malloc
50 #define xrealloc realloc_or_free
51 #define xfree free
52 #define xstrdup strdup
53
54 /** state of a running import */
55 struct asa_import_state {
56         demux_t *demux;                 /**< demuxer for msg* funcs */
57         const char *line;               /**< beginning of current line */
58         size_t remain;                  /**< remaining data at line */
59
60         char **matches;                 /**< active matchgroups */
61         unsigned nmatches;              /**< number of matchgroups */
62         char *selstr;                   /**< buffer for currently selected */
63         size_t sellen;                  /**< strlen of selstr */
64         char *out;                      /**< output buffer (NULL if empty) */
65         size_t outlen;                  /**< length of output string w/o \0 */
66
67         int64_t usecperf,               /**< microseconds per frame, active */
68                 origusecperf,           /**< microseconds per frame, from app */
69                 start,                  /**< start time */
70                 end;                    /**< end time */
71         int64_t delta[MAXDELTA];                /**< hist of last times for delta */
72
73         asa_import_callback *cb;        /**< commit callback */
74         void *cb_arg;                   /**< callback argument */
75 };
76
77 #define iargs struct asa_import_state *state, struct asa_import_insn *insn
78 /** asa instruction function.
79  * @param state import state
80  * @param insn instruction to execute
81  * @return status code.\n
82  *   0: continue
83  *   -1: restart from beginning
84  *   >0: break level
85  */
86 typedef int (*asa_import_func)(iargs);
87
88 static int asai_commit (iargs);
89 static int asai_discard(iargs);
90 static int asai_break  (iargs);
91 static int asai_select (iargs);
92 static int asai_sg     (iargs);
93 static int asai_sgu    (iargs);
94 static int asai_append (iargs);
95 static int asai_fps    (iargs);
96 static int asai_show   (iargs);
97 static int asai_hide   (iargs);
98 static int asai_child  (iargs);
99 #undef iargs
100
101 /** vm functions. KEEP IN SYNC WITH imports.h! */
102 static const asa_import_func importfuncs[] = {
103         asai_commit,
104         asai_discard,
105         asai_break,
106         asai_select,
107         asai_sg,
108         asai_sgu,
109         asai_append,
110         asai_fps,
111         asai_show,
112         asai_hide,
113         asai_child
114 };
115 #define ASAI_MAX (unsigned)(sizeof(importfuncs) / sizeof(importfuncs[0]))
116
117 struct asa_import_detect *asa_det_first = NULL,
118         **asa_det_last = &asa_det_first;
119 struct asa_import_format *asa_fmt_first = NULL,
120         **asa_fmt_last = &asa_fmt_first;
121
122 /** asa_imports_crosslink - resolve references in imports file.
123  * updates asa_import_format.prevtgt, asa_import_format.nexttgt,
124  * asa_import_detect.fmt
125  */
126 static void asa_imports_crosslink(void)
127 {
128         struct asa_import_format *fmt, *fmt2;
129         struct asa_import_detect *det;
130
131         for (fmt = asa_fmt_first; fmt; fmt = fmt->next) {
132                 for (fmt2 = fmt->next; fmt2; fmt2 = fmt2->next)
133                         if (!strcmp(fmt->name, fmt2->name)) {
134                                 fmt->nexttgt = fmt2;
135                                 fmt2->prevtgt = fmt;
136                                 break;
137                         }
138         }
139
140         for (det = asa_det_first; det; det = det->next) {
141                 det->fmt = NULL;
142                 for (fmt = asa_fmt_first; fmt; fmt = fmt->next)
143                         if (!strcmp(det->name, fmt->name)) {
144                                 det->fmt = fmt;
145                                 break;
146                         }
147         }
148 }
149
150 /** asa_imports_detect - autodetect subtitle format.
151  * @param data pointer to subtitle byte data
152  * @param datalen byte length of data
153  * @return the detect structure that hit or NULL if detection failed
154  */
155 struct asa_import_detect *asa_imports_detect(const void *data, size_t dlen)
156 {
157         struct asa_import_detect *det;
158         const char *d = (const char *)data;
159         int v[64];
160
161         if (dlen > 2048)
162                 dlen = 2048;
163         for (det = asa_det_first; det; det = det->next)
164                 if (pcre_exec(det->re.pcre, NULL, d, dlen, 0, 0, v, 64) >= 0)
165                         return det;
166         return NULL;
167 }
168
169 /** asai_run_insns - execute a list of instructions.
170  * @see asa_import_func */
171 static int asai_run_insns(struct asa_import_state *state,
172         struct asa_import_insn *insn)
173 {
174         struct asa_import_insn *inow = insn, preload;
175         int rv, repeating = 0;
176
177         preload.next = insn;
178         for (; inow; inow = inow->next) {
179                 if (repeating && inow->insn != ASAI_CHILD)
180                         continue;
181                 if (inow->insn >= ASAI_MAX)
182                         continue;
183                 rv = importfuncs[inow->insn](state, inow);
184                 if (rv == -1) {
185                         inow = &preload;
186                         continue;
187                 }
188                 if (rv > 0)
189                         return rv - 1;
190         }
191         /* ran through everything, let's try another round */
192         return -1;
193 }
194
195 /** asai_commit - commit a block to the user.
196  * @see asa_import_func */
197 static int asai_commit(struct asa_import_state *state,
198         struct asa_import_insn *insn)
199 {
200         int rv = 0;
201
202         if (!state->out)
203                 return 0;
204         if (state->outlen > 0)
205                 rv = state->cb(state->demux, state->cb_arg,
206                         state->start, state->end,
207                         state->out, state->outlen);
208         xfree(state->out);
209         state->out = NULL;
210         state->outlen = 0;
211         if (rv)
212                 return INT_MAX;
213         return 0;
214 }
215
216 /** asai_discard - clear the buffer without committing.
217  * @see asa_import_func */
218 static int asai_discard(struct asa_import_state *state,
219         struct asa_import_insn *insn)
220 {
221         if (!state->out)
222                 return 0;
223         xfree(state->out);
224         state->out = NULL;
225         state->outlen = 0;
226         return 0;
227 }
228
229 /** asai_break - jump out of child.
230  * @see asa_import_func */
231 static int asai_break(struct asa_import_state *state,
232         struct asa_import_insn *insn)
233 {
234         return insn->v.break_depth;
235 }
236
237 /** asai_select - choose a match group to be the active one.
238  * @see asa_import_func */
239 static int asai_select (struct asa_import_state *state,
240         struct asa_import_insn *insn)
241 {
242         if (insn->v.select < 0
243                 || (unsigned)insn->v.select >= state->nmatches) {
244                 msg_Err(state->demux, "import script trying to "
245                         "reference group %d, maximum is %d",
246                         insn->v.select, state->nmatches);
247                 return 0;
248         }
249         if (state->selstr)
250                 xfree(state->selstr);
251         state->selstr = xstrdup(state->matches[insn->v.select]);
252         assert(state->selstr);
253         state->sellen = strlen(state->selstr);
254         return 0;
255 }
256
257 #include <stddef.h>
258 static ptrdiff_t asai_process_replace(struct asa_import_state *state,
259         struct asa_import_insn *insn, int *v, int rv)
260 {
261         struct asa_repl *r;
262         char *newstr;
263         ptrdiff_t newpos, firstold;
264         size_t newstr_size;
265
266         newstr_size = v[0] * 2;
267         newstr = (char *)xmalloc(newstr_size);
268         assert(newstr);
269         memcpy(newstr, state->selstr, v[0]);
270         newpos = v[0];
271
272         for (r = insn->v.sg.repl; r; r = r->next) {
273                 size_t avail = newstr_size - newpos, need;
274                 const char *src;
275
276                 if (r->group >= rv) {
277                         msg_Err(state->demux,
278                                 "import script trying to replace by "
279                                 "reference group %d, maximum is %d",
280                                 r->group, rv);
281                         continue;
282                 }
283                 if (r->group >= 0) {
284                         need = v[r->group * 2 + 1] - v[r->group * 2];
285                         src = state->selstr + v[r->group * 2];
286                 } else {
287                         need = strlen(r->text);
288                         src = r->text;
289                 }
290                 if (need > avail) {
291                         newstr_size += need - avail + 256;
292                         newstr = (char *)xrealloc(newstr, newstr_size);
293                         assert(newstr);
294                 }
295                 memcpy(newstr + newpos, src, need);
296                 newpos += need;
297         }
298         firstold = newpos;
299         newstr_size = newpos + state->sellen - v[1];
300         newstr = (char *)xrealloc(newstr, newstr_size + 1);
301         assert(newstr);
302         memcpy(newstr + newpos, state->selstr + v[1],
303                 state->sellen - v[1] + 1);
304         state->selstr = newstr;
305         state->sellen = newstr_size;
306         return firstold;
307 }
308
309 /** asai_sg - search and replace.
310  * @see asa_import_func */
311 static int asai_sg(struct asa_import_state *state,
312         struct asa_import_insn *insn)
313 {
314         int rv, v[MAXGROUP * 2];
315         char *oldstr;
316         ptrdiff_t s = 0;
317
318         if (!state->selstr)
319                 return 0;
320         while ((unsigned)s < state->sellen &&
321                 (rv = pcre_exec(insn->v.sg.regex.pcre, NULL, state->selstr,
322                         state->sellen, s, 0, v, MAXGROUP * 2)) >= 0) {
323                 oldstr = state->selstr;
324                 s = asai_process_replace(state, insn, v, rv);
325                 xfree(oldstr);
326         }
327         return 0;
328 }
329
330 /** asai_chunk_alloc - allocate composite chunk.
331  * @see asa_import_func */
332 static inline char **asai_chunk_alloc(char **old, int *v, int rv)
333 {
334         size_t s = rv * sizeof(char *);
335         int i;
336         for (i = 0; i < rv; i++)
337                 s += v[i * 2 + 1] - v[i * 2] + 1;
338         return (char **)xrealloc(old, s);
339 }
340
341 /** asai_set_matches - load result from pcre_exec into matches */
342 static void asai_set_matches(struct asa_import_state *state,
343         const char *src, int *v, int rv)
344 {
345         unsigned i;
346         char *dst;
347
348         state->matches = asai_chunk_alloc(state->matches, v, rv);
349         state->nmatches = rv;
350         dst = (char *)(state->matches + rv);
351         for (i = 0; i < state->nmatches; i++) {
352                 size_t len = v[2 * i + 1] - v[2 * i];
353                 state->matches[i] = dst;
354                 memcpy(dst, src + v[2 * i], len);
355                 dst[len] = '\0';
356                 dst += len + 1;
357         }
358         if (state->selstr)
359                 xfree(state->selstr);
360         state->selstr = xstrdup(state->matches[0]);
361         assert(state->selstr);
362         state->sellen = strlen(state->selstr);
363 }
364
365 /** asai_sgu - replace one time and update matches.
366  * @see asa_import_func */
367 static int asai_sgu(struct asa_import_state *state,
368         struct asa_import_insn *insn)
369 {
370         int rv, v[MAXGROUP * 2];
371         char *oldstr;
372
373         if (!state->selstr)
374                 return 0;
375         if ((rv = pcre_exec(insn->v.sg.regex.pcre, NULL, state->selstr,
376                         state->sellen, 0, 0, v, MAXGROUP * 2)) >= 0) {
377                 oldstr = state->selstr;
378                 asai_process_replace(state, insn, v, rv);
379
380                 asai_set_matches(state, oldstr, v, rv);
381                 xfree(oldstr);
382         }
383         return 0;
384 }
385
386 /** asai_append - append selected string to output buffer.
387  * @see asa_import_func */
388 static int asai_append (struct asa_import_state *state,
389         struct asa_import_insn *insn)
390 {
391         state->out = (char *)xrealloc(state->out,
392                 state->outlen + state->sellen + 1);
393         assert(state->out);
394         memcpy(state->out + state->outlen, state->selstr, state->sellen);
395         state->outlen += state->sellen;
396         state->out[state->outlen] = '\0';
397         return 0;
398 }
399
400 /** asai_fps - override fps.
401  * @see asa_import_func */
402 static int asai_fps(struct asa_import_state *state,
403         struct asa_import_insn *insn)
404 {
405         if (insn->v.fps_value == 0)
406                 state->usecperf = state->origusecperf;
407         else
408                 state->usecperf = (int64_t)(1000000. / insn->v.fps_value);
409         return 0;
410 }
411
412 /** asai_gettime - walk asa_tspec and sum up the time.
413  * @see asa_import_func
414  * @return the calculated time, delta[0] on error
415  * also updates the delta history.
416  */
417 static int64_t asai_gettime(struct asa_import_state *state,
418         struct asa_import_insn *insn)
419 {
420         struct asa_tspec *tsp;
421         int64_t t = 0;
422         if (insn->v.tspec.delta_select != -1) {
423                 if (insn->v.tspec.delta_select < MAXDELTA)
424                         t += state->delta[insn->v.tspec.delta_select];
425                 else
426                         msg_Err(state->demux, "imports: tspec "
427                                 "delta %d exceeds compiled-in maximum of %d",
428                                 insn->v.tspec.delta_select, MAXDELTA);
429         }
430         for (tsp = insn->v.tspec.tsp; tsp; tsp = tsp->next) {
431                 char *errptr;
432                 double src;
433
434                 if ((unsigned)tsp->group >= state->nmatches) {
435                         msg_Err(state->demux, "imports: tspec "
436                                 "tries to access group %d, but only "
437                                 "%d groups exist",
438                                 tsp->group, state->nmatches);
439                         continue;
440                 }
441                 if (!*state->matches[tsp->group])
442                         continue;
443                 src = strtod(state->matches[tsp->group], &errptr);
444                 if (*errptr)
445                         msg_Warn(state->demux, "imports: invalid tspec '%s'",
446                                 state->matches[tsp->group]);
447                 t += (src * tsp->mult * 1000000)
448                         + src * tsp->fps_mult * state->usecperf;
449         }
450         memmove(state->delta + 1, state->delta,
451                 sizeof(state->delta[0]) * (MAXDELTA - 1));
452         state->delta[0] = t;
453         return t;
454 }
455
456 /** asai_show - set start time.
457  * @see asa_import_func */
458 static int asai_show(struct asa_import_state *state,
459         struct asa_import_insn *insn)
460 {
461         state->start = asai_gettime(state, insn);
462         return 0;
463 }
464
465 /** asai_hide - set end time.
466  * @see asa_import_func */
467 static int asai_hide(struct asa_import_state *state,
468         struct asa_import_insn *insn)
469 {
470         state->end = asai_gettime(state, insn);
471         return 0;
472 }
473
474 /** asai_child - execute childs if we match.
475  * @see asa_import_func */
476 static int asai_child(struct asa_import_state *state,
477         struct asa_import_insn *insn)
478 {
479         int rv, v[MAXGROUP * 2];
480         if ((rv = pcre_exec(insn->v.child.regex.pcre, NULL, state->line,
481                         state->remain, 0, 0, v, MAXGROUP * 2)) >= 0) {
482                 asai_set_matches(state, state->line, v, rv);
483                 state->line += v[1];
484                 state->remain -= v[1];
485                 rv = asai_run_insns(state, insn->v.child.insns);
486                 return rv;
487         }
488         return 0;
489 }
490
491 int asa_import(demux_t *d, const void *data, size_t dlen,
492         int64_t usecperframe, struct asa_import_detect *det,
493         asa_import_callback *callback, void *arg)
494 {
495         struct asa_import_format *fmt = det->fmt;
496         struct asa_import_state state;
497         int rv;
498
499         memset(&state, 0, sizeof(state));
500         state.demux = d;
501         state.usecperf = state.origusecperf = usecperframe;
502         state.line = (const char *)data;
503         state.remain = dlen;
504         state.cb = callback;
505         state.cb_arg = arg;
506
507         rv = asai_run_insns(&state, fmt->insns);
508         if (state.matches)
509                 xfree(state.matches);
510         if (state.out) {
511                 callback(d, arg, state.start, state.end,
512                         state.out, state.outlen);
513                 xfree(state.out);
514         }
515         if (state.selstr)
516                 xfree(state.selstr);
517         return rv;
518 }
519
520 int asa_pcre_compile(asa_pcre *out, const char *str)
521 {
522         const char *err;
523         int ec, eo;
524
525         out->pcre = pcre_compile2(str, 0, &ec, &err, &eo, NULL);
526         if (out->pcre)
527                 return 0;
528         return 1;
529 }
530
531 #include "asademux_defs.h"
532
533 void asa_init_import()
534 {
535         static int setup = 0;
536         if (setup)
537                 return;
538
539         preparse_add();
540         asa_imports_crosslink();
541         setup = 1;
542 }