File CVE-2020-24994_1.patch of Package libass.20681

From 691b422247552a86e9d3310c4f1ea096dc52a3f2 Mon Sep 17 00:00:00 2001
From: Oleg Oshmyan <chortos@inbox.lv>
Date: Thu, 4 Jan 2018 02:37:02 +0200
Subject: [PATCH] Move parse_tag loop into parse_tag itself, now called
 parse_tags

This commit is mostly transparent to `git blame -w`.
---
 libass/ass_parse.c  | 1172 +++++++++++++++++++++----------------------
 libass/ass_parse.h  |    2 +-
 libass/ass_render.c |    3 +-
 3 files changed, 588 insertions(+), 589 deletions(-)

diff --git a/libass/ass_parse.c b/libass/ass_parse.c
index c83634ab..b20ef664 100644
--- a/libass/ass_parse.c
+++ b/libass/ass_parse.c
@@ -224,620 +224,620 @@ static int parse_vector_clip(ASS_Renderer *render_priv,
 }
 
 /**
- * \brief Parse style override tag.
+ * \brief Parse style override tags.
  * \param p string to parse
  * \param end end of string to parse, which must be '}', ')', or the first
  *            of a number of spaces immediately preceding '}' or ')'
  * \param pwr multiplier for some tag effects (comes from \t tags)
  */
-char *parse_tag(ASS_Renderer *render_priv, char *p, char *end, double pwr)
+char *parse_tags(ASS_Renderer *render_priv, char *p, char *end, double pwr)
 {
-    while (*p != '\\' && p != end)
+    for (char *q; p < end; p = q) {
+        while (*p != '\\' && p != end)
+            ++p;
+        if (*p != '\\')
+            break;
         ++p;
-    if (*p != '\\')
-        return p;
-    ++p;
-    if (p != end)
-        skip_spaces(&p);
+        if (p != end)
+            skip_spaces(&p);
 
-    char *q = p;
-    while (*q != '(' && *q != '\\' && q != end)
-        ++q;
-    if (q == p)
-        return q;
+        q = p;
+        while (*q != '(' && *q != '\\' && q != end)
+            ++q;
+        if (q == p)
+            continue;
 
-    char *name_end = q;
+        char *name_end = q;
 
-    // Store one extra element to be able to detect excess arguments
-    struct arg args[MAX_VALID_NARGS + 1];
-    int nargs = 0;
-    for (int i = 0; i <= MAX_VALID_NARGS; ++i)
-        args[i].start = args[i].end = "";
+        // Store one extra element to be able to detect excess arguments
+        struct arg args[MAX_VALID_NARGS + 1];
+        int nargs = 0;
+        for (int i = 0; i <= MAX_VALID_NARGS; ++i)
+            args[i].start = args[i].end = "";
 
-    // Split parenthesized arguments. Do this for all tags and before
-    // any non-parenthesized argument because that's what VSFilter does.
-    if (*q == '(') {
-        ++q;
-        while (1) {
-            if (q != end)
-                skip_spaces(&q);
-
-            // Split on commas. If there is a backslash, ignore any
-            // commas following it and lump everything starting from
-            // the last comma, through the backslash and all the way
-            // to the end of the argument string into a single argument.
+        // Split parenthesized arguments. Do this for all tags and before
+        // any non-parenthesized argument because that's what VSFilter does.
+        if (*q == '(') {
+            ++q;
+            while (1) {
+                if (q != end)
+                    skip_spaces(&q);
 
-            char *r = q;
-            while (*r != ',' && *r != '\\' && *r != ')' && r != end)
-                ++r;
+                // Split on commas. If there is a backslash, ignore any
+                // commas following it and lump everything starting from
+                // the last comma, through the backslash and all the way
+                // to the end of the argument string into a single argument.
 
-            if (*r == ',') {
-                push_arg(args, &nargs, q, r);
-                q = r + 1;
-            } else {
-                // Swallow the rest of the parenthesized string. This could
-                // be either a backslash-argument or simply the last argument.
-                while (*r != ')' && r != end)
+                char *r = q;
+                while (*r != ',' && *r != '\\' && *r != ')' && r != end)
                     ++r;
-                push_arg(args, &nargs, q, r);
-                q = r;
-                // The closing parenthesis could be missing.
-                if (q != end)
-                    ++q;
-                break;
+
+                if (*r == ',') {
+                    push_arg(args, &nargs, q, r);
+                    q = r + 1;
+                } else {
+                    // Swallow the rest of the parenthesized string. This could
+                    // be either a backslash-argument or simply the last argument.
+                    while (*r != ')' && r != end)
+                        ++r;
+                    push_arg(args, &nargs, q, r);
+                    q = r;
+                    // The closing parenthesis could be missing.
+                    if (q != end)
+                        ++q;
+                    break;
+                }
             }
         }
-    }
 
 #define tag(name) (mystrcmp(&p, (name)) && (push_arg(args, &nargs, p, name_end), 1))
 #define complex_tag(name) mystrcmp(&p, (name))
 
-    // New tags introduced in vsfilter 2.39
-    if (tag("xbord")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args);
-            val = render_priv->state.border_x * (1 - pwr) + val * pwr;
-            val = (val < 0) ? 0 : val;
-        } else
-            val = render_priv->state.style->Outline;
-        render_priv->state.border_x = val;
-    } else if (tag("ybord")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args);
-            val = render_priv->state.border_y * (1 - pwr) + val * pwr;
-            val = (val < 0) ? 0 : val;
-        } else
-            val = render_priv->state.style->Outline;
-        render_priv->state.border_y = val;
-    } else if (tag("xshad")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args);
-            val = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
-        } else
-            val = render_priv->state.style->Shadow;
-        render_priv->state.shadow_x = val;
-    } else if (tag("yshad")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args);
-            val = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
-        } else
-            val = render_priv->state.style->Shadow;
-        render_priv->state.shadow_y = val;
-    } else if (tag("fax")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args);
-            render_priv->state.fax =
-                val * pwr + render_priv->state.fax * (1 - pwr);
-        } else
-            render_priv->state.fax = 0.;
-    } else if (tag("fay")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args);
-            render_priv->state.fay =
-                val * pwr + render_priv->state.fay * (1 - pwr);
-        } else
-            render_priv->state.fay = 0.;
-    } else if (complex_tag("iclip")) {
-        if (nargs == 4) {
-            int x0, y0, x1, y1;
-            x0 = argtoi(args[0]);
-            y0 = argtoi(args[1]);
-            x1 = argtoi(args[2]);
-            y1 = argtoi(args[3]);
-            render_priv->state.clip_x0 =
-                render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
-            render_priv->state.clip_x1 =
-                render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr;
-            render_priv->state.clip_y0 =
-                render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
-            render_priv->state.clip_y1 =
-                render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
-            render_priv->state.clip_mode = 1;
-        } else if (!render_priv->state.clip_drawing) {
-            if (parse_vector_clip(render_priv, args, nargs))
-                render_priv->state.clip_drawing_mode = 1;
-        }
-    } else if (tag("blur")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args);
-            val = render_priv->state.blur * (1 - pwr) + val * pwr;
-            val = (val < 0) ? 0 : val;
-            val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val;
-            render_priv->state.blur = val;
-        } else
-            render_priv->state.blur = 0.0;
-        // ASS standard tags
-    } else if (tag("fscx")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args) / 100;
-            val = render_priv->state.scale_x * (1 - pwr) + val * pwr;
-            val = (val < 0) ? 0 : val;
-        } else
-            val = render_priv->state.style->ScaleX;
-        render_priv->state.scale_x = val;
-    } else if (tag("fscy")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args) / 100;
-            val = render_priv->state.scale_y * (1 - pwr) + val * pwr;
-            val = (val < 0) ? 0 : val;
-        } else
-            val = render_priv->state.style->ScaleY;
-        render_priv->state.scale_y = val;
-    } else if (tag("fsc")) {
-        render_priv->state.scale_x = render_priv->state.style->ScaleX;
-        render_priv->state.scale_y = render_priv->state.style->ScaleY;
-    } else if (tag("fsp")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args);
-            render_priv->state.hspacing =
-                render_priv->state.hspacing * (1 - pwr) + val * pwr;
-        } else
-            render_priv->state.hspacing = render_priv->state.style->Spacing;
-    } else if (tag("fs")) {
-        double val = 0;
-        if (nargs) {
-            val = argtod(*args);
-            if (*args->start == '+' || *args->start == '-')
-                val = render_priv->state.font_size * (1 + pwr * val / 10);
-            else
-                val = render_priv->state.font_size * (1 - pwr) + val * pwr;
-        }
-        if (val <= 0)
-            val = render_priv->state.style->FontSize;
-        if (render_priv->state.font)
-            change_font_size(render_priv, val);
-    } else if (tag("bord")) {
-        double val, xval, yval;
-        if (nargs) {
-            val = argtod(*args);
-            xval = render_priv->state.border_x * (1 - pwr) + val * pwr;
-            yval = render_priv->state.border_y * (1 - pwr) + val * pwr;
-            xval = (xval < 0) ? 0 : xval;
-            yval = (yval < 0) ? 0 : yval;
-        } else
-            xval = yval = render_priv->state.style->Outline;
-        render_priv->state.border_x = xval;
-        render_priv->state.border_y = yval;
-    } else if (complex_tag("move")) {
-        double x1, x2, y1, y2;
-        long long t1, t2, delta_t, t;
-        double x, y;
-        double k;
-        if (nargs == 4 || nargs == 6) {
-            x1 = argtod(args[0]);
-            y1 = argtod(args[1]);
-            x2 = argtod(args[2]);
-            y2 = argtod(args[3]);
-            t1 = t2 = 0;
-            if (nargs == 6) {
-                t1 = argtoll(args[4]);
-                t2 = argtoll(args[5]);
-                if (t1 > t2) {
-                    long long tmp = t2;
-                    t2 = t1;
-                    t1 = tmp;
+        // New tags introduced in vsfilter 2.39
+        if (tag("xbord")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args);
+                val = render_priv->state.border_x * (1 - pwr) + val * pwr;
+                val = (val < 0) ? 0 : val;
+            } else
+                val = render_priv->state.style->Outline;
+            render_priv->state.border_x = val;
+        } else if (tag("ybord")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args);
+                val = render_priv->state.border_y * (1 - pwr) + val * pwr;
+                val = (val < 0) ? 0 : val;
+            } else
+                val = render_priv->state.style->Outline;
+            render_priv->state.border_y = val;
+        } else if (tag("xshad")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args);
+                val = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
+            } else
+                val = render_priv->state.style->Shadow;
+            render_priv->state.shadow_x = val;
+        } else if (tag("yshad")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args);
+                val = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
+            } else
+                val = render_priv->state.style->Shadow;
+            render_priv->state.shadow_y = val;
+        } else if (tag("fax")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args);
+                render_priv->state.fax =
+                    val * pwr + render_priv->state.fax * (1 - pwr);
+            } else
+                render_priv->state.fax = 0.;
+        } else if (tag("fay")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args);
+                render_priv->state.fay =
+                    val * pwr + render_priv->state.fay * (1 - pwr);
+            } else
+                render_priv->state.fay = 0.;
+        } else if (complex_tag("iclip")) {
+            if (nargs == 4) {
+                int x0, y0, x1, y1;
+                x0 = argtoi(args[0]);
+                y0 = argtoi(args[1]);
+                x1 = argtoi(args[2]);
+                y1 = argtoi(args[3]);
+                render_priv->state.clip_x0 =
+                    render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
+                render_priv->state.clip_x1 =
+                    render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr;
+                render_priv->state.clip_y0 =
+                    render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
+                render_priv->state.clip_y1 =
+                    render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
+                render_priv->state.clip_mode = 1;
+            } else if (!render_priv->state.clip_drawing) {
+                if (parse_vector_clip(render_priv, args, nargs))
+                    render_priv->state.clip_drawing_mode = 1;
+            }
+        } else if (tag("blur")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args);
+                val = render_priv->state.blur * (1 - pwr) + val * pwr;
+                val = (val < 0) ? 0 : val;
+                val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val;
+                render_priv->state.blur = val;
+            } else
+                render_priv->state.blur = 0.0;
+            // ASS standard tags
+        } else if (tag("fscx")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args) / 100;
+                val = render_priv->state.scale_x * (1 - pwr) + val * pwr;
+                val = (val < 0) ? 0 : val;
+            } else
+                val = render_priv->state.style->ScaleX;
+            render_priv->state.scale_x = val;
+        } else if (tag("fscy")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args) / 100;
+                val = render_priv->state.scale_y * (1 - pwr) + val * pwr;
+                val = (val < 0) ? 0 : val;
+            } else
+                val = render_priv->state.style->ScaleY;
+            render_priv->state.scale_y = val;
+        } else if (tag("fsc")) {
+            render_priv->state.scale_x = render_priv->state.style->ScaleX;
+            render_priv->state.scale_y = render_priv->state.style->ScaleY;
+        } else if (tag("fsp")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args);
+                render_priv->state.hspacing =
+                    render_priv->state.hspacing * (1 - pwr) + val * pwr;
+            } else
+                render_priv->state.hspacing = render_priv->state.style->Spacing;
+        } else if (tag("fs")) {
+            double val = 0;
+            if (nargs) {
+                val = argtod(*args);
+                if (*args->start == '+' || *args->start == '-')
+                    val = render_priv->state.font_size * (1 + pwr * val / 10);
+                else
+                    val = render_priv->state.font_size * (1 - pwr) + val * pwr;
+            }
+            if (val <= 0)
+                val = render_priv->state.style->FontSize;
+            if (render_priv->state.font)
+                change_font_size(render_priv, val);
+        } else if (tag("bord")) {
+            double val, xval, yval;
+            if (nargs) {
+                val = argtod(*args);
+                xval = render_priv->state.border_x * (1 - pwr) + val * pwr;
+                yval = render_priv->state.border_y * (1 - pwr) + val * pwr;
+                xval = (xval < 0) ? 0 : xval;
+                yval = (yval < 0) ? 0 : yval;
+            } else
+                xval = yval = render_priv->state.style->Outline;
+            render_priv->state.border_x = xval;
+            render_priv->state.border_y = yval;
+        } else if (complex_tag("move")) {
+            double x1, x2, y1, y2;
+            long long t1, t2, delta_t, t;
+            double x, y;
+            double k;
+            if (nargs == 4 || nargs == 6) {
+                x1 = argtod(args[0]);
+                y1 = argtod(args[1]);
+                x2 = argtod(args[2]);
+                y2 = argtod(args[3]);
+                t1 = t2 = 0;
+                if (nargs == 6) {
+                    t1 = argtoll(args[4]);
+                    t2 = argtoll(args[5]);
+                    if (t1 > t2) {
+                        long long tmp = t2;
+                        t2 = t1;
+                        t1 = tmp;
+                    }
                 }
+            } else
+                continue;
+            if (t1 <= 0 && t2 <= 0) {
+                t1 = 0;
+                t2 = render_priv->state.event->Duration;
             }
-        } else
-            return q;
-        if (t1 <= 0 && t2 <= 0) {
-            t1 = 0;
-            t2 = render_priv->state.event->Duration;
-        }
-        delta_t = t2 - t1;
-        t = render_priv->time - render_priv->state.event->Start;
-        if (t <= t1)
-            k = 0.;
-        else if (t >= t2)
-            k = 1.;
-        else
-            k = ((double) (t - t1)) / delta_t;
-        x = k * (x2 - x1) + x1;
-        y = k * (y2 - y1) + y1;
-        if (render_priv->state.evt_type != EVENT_POSITIONED) {
-            render_priv->state.pos_x = x;
-            render_priv->state.pos_y = y;
+            delta_t = t2 - t1;
+            t = render_priv->time - render_priv->state.event->Start;
+            if (t <= t1)
+                k = 0.;
+            else if (t >= t2)
+                k = 1.;
+            else
+                k = ((double) (t - t1)) / delta_t;
+            x = k * (x2 - x1) + x1;
+            y = k * (y2 - y1) + y1;
+            if (render_priv->state.evt_type != EVENT_POSITIONED) {
+                render_priv->state.pos_x = x;
+                render_priv->state.pos_y = y;
+                render_priv->state.detect_collisions = 0;
+                render_priv->state.evt_type = EVENT_POSITIONED;
+            }
+        } else if (tag("frx")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args);
+                val *= M_PI / 180;
+                render_priv->state.frx =
+                    val * pwr + render_priv->state.frx * (1 - pwr);
+            } else
+                render_priv->state.frx = 0.;
+        } else if (tag("fry")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args);
+                val *= M_PI / 180;
+                render_priv->state.fry =
+                    val * pwr + render_priv->state.fry * (1 - pwr);
+            } else
+                render_priv->state.fry = 0.;
+        } else if (tag("frz") || tag("fr")) {
+            double val;
+            if (nargs) {
+                val = argtod(*args);
+                val *= M_PI / 180;
+                render_priv->state.frz =
+                    val * pwr + render_priv->state.frz * (1 - pwr);
+            } else
+                render_priv->state.frz =
+                    M_PI * render_priv->state.style->Angle / 180.;
+        } else if (tag("fn")) {
+            char *family;
+            char *start = args->start;
+            if (nargs && strncmp(start, "0", args->end - start)) {
+                skip_spaces(&start);
+                family = strndup(start, args->end - start);
+            } else
+                family = strdup(render_priv->state.style->FontName);
+            free(render_priv->state.family);
+            render_priv->state.family = family;
+            update_font(render_priv);
+        } else if (tag("alpha")) {
+            int i;
+            if (nargs) {
+                int32_t a = parse_alpha_tag(args->start);
+                for (i = 0; i < 4; ++i)
+                    change_alpha(&render_priv->state.c[i], a, pwr);
+            } else {
+                change_alpha(&render_priv->state.c[0],
+                             _a(render_priv->state.style->PrimaryColour), 1);
+                change_alpha(&render_priv->state.c[1],
+                             _a(render_priv->state.style->SecondaryColour), 1);
+                change_alpha(&render_priv->state.c[2],
+                             _a(render_priv->state.style->OutlineColour), 1);
+                change_alpha(&render_priv->state.c[3],
+                             _a(render_priv->state.style->BackColour), 1);
+            }
+            // FIXME: simplify
+        } else if (tag("an")) {
+            int val = argtoi(*args);
+            if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
+                if (val >= 1 && val <= 9)
+                    render_priv->state.alignment = numpad2align(val);
+                else
+                    render_priv->state.alignment =
+                        render_priv->state.style->Alignment;
+                render_priv->state.parsed_tags |= PARSED_A;
+            }
+        } else if (tag("a")) {
+            int val = argtoi(*args);
+            if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
+                if (val >= 1 && val <= 11)
+                    // take care of a vsfilter quirk:
+                    // handle illegal \a8 and \a4 like \a5
+                    render_priv->state.alignment = ((val & 3) == 0) ? 5 : val;
+                else
+                    render_priv->state.alignment =
+                        render_priv->state.style->Alignment;
+                render_priv->state.parsed_tags |= PARSED_A;
+            }
+        } else if (complex_tag("pos")) {
+            double v1, v2;
+            if (nargs == 2) {
+                v1 = argtod(args[0]);
+                v2 = argtod(args[1]);
+            } else
+                continue;
+            if (render_priv->state.evt_type == EVENT_POSITIONED) {
+                ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos "
+                       "after \\move or \\pos, ignoring");
+            } else {
+                render_priv->state.evt_type = EVENT_POSITIONED;
+                render_priv->state.detect_collisions = 0;
+                render_priv->state.pos_x = v1;
+                render_priv->state.pos_y = v2;
+            }
+        } else if (complex_tag("fade") || complex_tag("fad")) {
+            int a1, a2, a3;
+            long long t1, t2, t3, t4;
+            if (nargs == 2) {
+                // 2-argument version (\fad, according to specs)
+                a1 = 0xFF;
+                a2 = 0;
+                a3 = 0xFF;
+                t1 = -1;
+                t2 = argtoll(args[0]);
+                t3 = argtoll(args[1]);
+                t4 = -1;
+            } else if (nargs == 7) {
+                // 7-argument version (\fade)
+                a1 = argtoi(args[0]);
+                a2 = argtoi(args[1]);
+                a3 = argtoi(args[2]);
+                t1 = argtoll(args[3]);
+                t2 = argtoll(args[4]);
+                t3 = argtoll(args[5]);
+                t4 = argtoll(args[6]);
+            } else
+                continue;
+            if (t1 == -1 && t4 == -1) {
+                t1 = 0;
+                t4 = render_priv->state.event->Duration;
+                t3 = t4 - t3;
+            }
+            if ((render_priv->state.parsed_tags & PARSED_FADE) == 0) {
+                render_priv->state.fade =
+                    interpolate_alpha(render_priv->time -
+                            render_priv->state.event->Start, t1, t2,
+                            t3, t4, a1, a2, a3);
+                render_priv->state.parsed_tags |= PARSED_FADE;
+            }
+        } else if (complex_tag("org")) {
+            double v1, v2;
+            if (nargs == 2) {
+                v1 = argtod(args[0]);
+                v2 = argtod(args[1]);
+            } else
+                continue;
+            if (!render_priv->state.have_origin) {
+                render_priv->state.org_x = v1;
+                render_priv->state.org_y = v2;
+                render_priv->state.have_origin = 1;
+                render_priv->state.detect_collisions = 0;
+            }
+        } else if (complex_tag("t")) {
+            double accel;
+            int cnt = nargs - 1;
+            long long t1, t2, t, delta_t;
+            double k;
+            if (cnt == 3) {
+                t1 = argtoll(args[0]);
+                t2 = argtoll(args[1]);
+                accel = argtod(args[2]);
+            } else if (cnt == 2) {
+                t1 = argtoll(args[0]);
+                t2 = argtoll(args[1]);
+                accel = 1.;
+            } else if (cnt == 1) {
+                t1 = 0;
+                t2 = 0;
+                accel = argtod(args[0]);
+            } else if (cnt == 0) {
+                t1 = 0;
+                t2 = 0;
+                accel = 1.;
+            } else
+                continue;
             render_priv->state.detect_collisions = 0;
-            render_priv->state.evt_type = EVENT_POSITIONED;
-        }
-    } else if (tag("frx")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args);
-            val *= M_PI / 180;
-            render_priv->state.frx =
-                val * pwr + render_priv->state.frx * (1 - pwr);
-        } else
-            render_priv->state.frx = 0.;
-    } else if (tag("fry")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args);
-            val *= M_PI / 180;
-            render_priv->state.fry =
-                val * pwr + render_priv->state.fry * (1 - pwr);
-        } else
-            render_priv->state.fry = 0.;
-    } else if (tag("frz") || tag("fr")) {
-        double val;
-        if (nargs) {
-            val = argtod(*args);
-            val *= M_PI / 180;
-            render_priv->state.frz =
-                val * pwr + render_priv->state.frz * (1 - pwr);
-        } else
-            render_priv->state.frz =
-                M_PI * render_priv->state.style->Angle / 180.;
-    } else if (tag("fn")) {
-        char *family;
-        char *start = args->start;
-        end = args->end;
-        if (nargs && strncmp(start, "0", end - start)) {
-            skip_spaces(&start);
-            family = strndup(start, end - start);
-        } else
-            family = strdup(render_priv->state.style->FontName);
-        free(render_priv->state.family);
-        render_priv->state.family = family;
-        update_font(render_priv);
-    } else if (tag("alpha")) {
-        int i;
-        if (nargs) {
-            int32_t a = parse_alpha_tag(args->start);
-            for (i = 0; i < 4; ++i)
-                change_alpha(&render_priv->state.c[i], a, pwr);
-        } else {
-            change_alpha(&render_priv->state.c[0],
-                         _a(render_priv->state.style->PrimaryColour), 1);
-            change_alpha(&render_priv->state.c[1],
-                         _a(render_priv->state.style->SecondaryColour), 1);
-            change_alpha(&render_priv->state.c[2],
-                         _a(render_priv->state.style->OutlineColour), 1);
-            change_alpha(&render_priv->state.c[3],
-                         _a(render_priv->state.style->BackColour), 1);
-        }
-        // FIXME: simplify
-    } else if (tag("an")) {
-        int val = argtoi(*args);
-        if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
-            if (val >= 1 && val <= 9)
-                render_priv->state.alignment = numpad2align(val);
+            if (t2 == 0)
+                t2 = render_priv->state.event->Duration;
+            delta_t = t2 - t1;
+            t = render_priv->time - render_priv->state.event->Start;        // FIXME: move to render_context
+            if (t <= t1)
+                k = 0.;
+            else if (t >= t2)
+                k = 1.;
+            else {
+                assert(delta_t != 0.);
+                k = pow(((double) (t - t1)) / delta_t, accel);
+            }
+            p = args[cnt].start;
+            p = parse_tags(render_priv, p, args[cnt].end, k);    // maybe k*pwr ? no, specs forbid nested \t's
+        } else if (complex_tag("clip")) {
+            if (nargs == 4) {
+                int x0, y0, x1, y1;
+                x0 = argtoi(args[0]);
+                y0 = argtoi(args[1]);
+                x1 = argtoi(args[2]);
+                y1 = argtoi(args[3]);
+                render_priv->state.clip_x0 =
+                    render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
+                render_priv->state.clip_x1 =
+                    render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr;
+                render_priv->state.clip_y0 =
+                    render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
+                render_priv->state.clip_y1 =
+                    render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
+                render_priv->state.clip_mode = 0;
+            } else if (!render_priv->state.clip_drawing) {
+                if (parse_vector_clip(render_priv, args, nargs))
+                    render_priv->state.clip_drawing_mode = 0;
+            }
+        } else if (tag("c") || tag("1c")) {
+            if (nargs) {
+                uint32_t val = parse_color_tag(args->start);
+                change_color(&render_priv->state.c[0], val, pwr);
+            } else
+                change_color(&render_priv->state.c[0],
+                             render_priv->state.style->PrimaryColour, 1);
+        } else if (tag("2c")) {
+            if (nargs) {
+                uint32_t val = parse_color_tag(args->start);
+                change_color(&render_priv->state.c[1], val, pwr);
+            } else
+                change_color(&render_priv->state.c[1],
+                             render_priv->state.style->SecondaryColour, 1);
+        } else if (tag("3c")) {
+            if (nargs) {
+                uint32_t val = parse_color_tag(args->start);
+                change_color(&render_priv->state.c[2], val, pwr);
+            } else
+                change_color(&render_priv->state.c[2],
+                             render_priv->state.style->OutlineColour, 1);
+        } else if (tag("4c")) {
+            if (nargs) {
+                uint32_t val = parse_color_tag(args->start);
+                change_color(&render_priv->state.c[3], val, pwr);
+            } else
+                change_color(&render_priv->state.c[3],
+                             render_priv->state.style->BackColour, 1);
+        } else if (tag("1a")) {
+            if (nargs) {
+                uint32_t val = parse_alpha_tag(args->start);
+                change_alpha(&render_priv->state.c[0], val, pwr);
+            } else
+                change_alpha(&render_priv->state.c[0],
+                             _a(render_priv->state.style->PrimaryColour), 1);
+        } else if (tag("2a")) {
+            if (nargs) {
+                uint32_t val = parse_alpha_tag(args->start);
+                change_alpha(&render_priv->state.c[1], val, pwr);
+            } else
+                change_alpha(&render_priv->state.c[1],
+                             _a(render_priv->state.style->SecondaryColour), 1);
+        } else if (tag("3a")) {
+            if (nargs) {
+                uint32_t val = parse_alpha_tag(args->start);
+                change_alpha(&render_priv->state.c[2], val, pwr);
+            } else
+                change_alpha(&render_priv->state.c[2],
+                             _a(render_priv->state.style->OutlineColour), 1);
+        } else if (tag("4a")) {
+            if (nargs) {
+                uint32_t val = parse_alpha_tag(args->start);
+                change_alpha(&render_priv->state.c[3], val, pwr);
+            } else
+                change_alpha(&render_priv->state.c[3],
+                             _a(render_priv->state.style->BackColour), 1);
+        } else if (tag("r")) {
+            if (nargs) {
+                int len = args->end - args->start;
+                reset_render_context(render_priv,
+                        lookup_style_strict(render_priv->track, args->start, len));
+            } else
+                reset_render_context(render_priv, NULL);
+        } else if (tag("be")) {
+            double dval;
+            if (nargs) {
+                int val;
+                dval = argtod(*args);
+                // VSFilter always adds +0.5, even if the value is negative
+                val = (int) (render_priv->state.be * (1 - pwr) + dval * pwr + 0.5);
+                // Clamp to a safe upper limit, since high values need excessive CPU
+                val = (val < 0) ? 0 : val;
+                val = (val > MAX_BE) ? MAX_BE : val;
+                render_priv->state.be = val;
+            } else
+                render_priv->state.be = 0;
+        } else if (tag("b")) {
+            int val = argtoi(*args);
+            if (!nargs || !(val == 0 || val == 1 || val >= 100))
+                val = render_priv->state.style->Bold;
+            render_priv->state.bold = val;
+            update_font(render_priv);
+        } else if (tag("i")) {
+            int val = argtoi(*args);
+            if (!nargs || !(val == 0 || val == 1))
+                val = render_priv->state.style->Italic;
+            render_priv->state.italic = val;
+            update_font(render_priv);
+        } else if (tag("kf") || tag("K")) {
+            double val = 100;
+            if (nargs)
+                val = argtod(*args);
+            render_priv->state.effect_type = EF_KARAOKE_KF;
+            if (render_priv->state.effect_timing)
+                render_priv->state.effect_skip_timing +=
+                    render_priv->state.effect_timing;
+            render_priv->state.effect_timing = val * 10;
+        } else if (tag("ko")) {
+            double val = 100;
+            if (nargs)
+                val = argtod(*args);
+            render_priv->state.effect_type = EF_KARAOKE_KO;
+            if (render_priv->state.effect_timing)
+                render_priv->state.effect_skip_timing +=
+                    render_priv->state.effect_timing;
+            render_priv->state.effect_timing = val * 10;
+        } else if (tag("k")) {
+            double val = 100;
+            if (nargs)
+                val = argtod(*args);
+            render_priv->state.effect_type = EF_KARAOKE;
+            if (render_priv->state.effect_timing)
+                render_priv->state.effect_skip_timing +=
+                    render_priv->state.effect_timing;
+            render_priv->state.effect_timing = val * 10;
+        } else if (tag("shad")) {
+            double val, xval, yval;
+            if (nargs) {
+                val = argtod(*args);
+                xval = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
+                yval = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
+                // VSFilter compatibility: clip for \shad but not for \[xy]shad
+                xval = (xval < 0) ? 0 : xval;
+                yval = (yval < 0) ? 0 : yval;
+            } else
+                xval = yval = render_priv->state.style->Shadow;
+            render_priv->state.shadow_x = xval;
+            render_priv->state.shadow_y = yval;
+        } else if (tag("s")) {
+            int val = argtoi(*args);
+            if (!nargs || !(val == 0 || val == 1))
+                val = render_priv->state.style->StrikeOut;
+            if (val)
+                render_priv->state.flags |= DECO_STRIKETHROUGH;
             else
-                render_priv->state.alignment =
-                    render_priv->state.style->Alignment;
-            render_priv->state.parsed_tags |= PARSED_A;
-        }
-    } else if (tag("a")) {
-        int val = argtoi(*args);
-        if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
-            if (val >= 1 && val <= 11)
-                // take care of a vsfilter quirk:
-                // handle illegal \a8 and \a4 like \a5
-                render_priv->state.alignment = ((val & 3) == 0) ? 5 : val;
+                render_priv->state.flags &= ~DECO_STRIKETHROUGH;
+        } else if (tag("u")) {
+            int val = argtoi(*args);
+            if (!nargs || !(val == 0 || val == 1))
+                val = render_priv->state.style->Underline;
+            if (val)
+                render_priv->state.flags |= DECO_UNDERLINE;
             else
-                render_priv->state.alignment =
-                    render_priv->state.style->Alignment;
-            render_priv->state.parsed_tags |= PARSED_A;
-        }
-    } else if (complex_tag("pos")) {
-        double v1, v2;
-        if (nargs == 2) {
-            v1 = argtod(args[0]);
-            v2 = argtod(args[1]);
-        } else
-            return q;
-        if (render_priv->state.evt_type == EVENT_POSITIONED) {
-            ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos "
-                   "after \\move or \\pos, ignoring");
-        } else {
-            render_priv->state.evt_type = EVENT_POSITIONED;
-            render_priv->state.detect_collisions = 0;
-            render_priv->state.pos_x = v1;
-            render_priv->state.pos_y = v2;
-        }
-    } else if (complex_tag("fade") || complex_tag("fad")) {
-        int a1, a2, a3;
-        long long t1, t2, t3, t4;
-        if (nargs == 2) {
-            // 2-argument version (\fad, according to specs)
-            a1 = 0xFF;
-            a2 = 0;
-            a3 = 0xFF;
-            t1 = -1;
-            t2 = argtoll(args[0]);
-            t3 = argtoll(args[1]);
-            t4 = -1;
-        } else if (nargs == 7) {
-            // 7-argument version (\fade)
-            a1 = argtoi(args[0]);
-            a2 = argtoi(args[1]);
-            a3 = argtoi(args[2]);
-            t1 = argtoll(args[3]);
-            t2 = argtoll(args[4]);
-            t3 = argtoll(args[5]);
-            t4 = argtoll(args[6]);
-        } else
-            return q;
-        if (t1 == -1 && t4 == -1) {
-            t1 = 0;
-            t4 = render_priv->state.event->Duration;
-            t3 = t4 - t3;
-        }
-        if ((render_priv->state.parsed_tags & PARSED_FADE) == 0) {
-            render_priv->state.fade =
-                interpolate_alpha(render_priv->time -
-                        render_priv->state.event->Start, t1, t2,
-                        t3, t4, a1, a2, a3);
-            render_priv->state.parsed_tags |= PARSED_FADE;
-        }
-    } else if (complex_tag("org")) {
-        double v1, v2;
-        if (nargs == 2) {
-            v1 = argtod(args[0]);
-            v2 = argtod(args[1]);
-        } else
-            return q;
-        if (!render_priv->state.have_origin) {
-            render_priv->state.org_x = v1;
-            render_priv->state.org_y = v2;
-            render_priv->state.have_origin = 1;
-            render_priv->state.detect_collisions = 0;
-        }
-    } else if (complex_tag("t")) {
-        double accel;
-        int cnt = nargs - 1;
-        long long t1, t2, t, delta_t;
-        double k;
-        if (cnt == 3) {
-            t1 = argtoll(args[0]);
-            t2 = argtoll(args[1]);
-            accel = argtod(args[2]);
-        } else if (cnt == 2) {
-            t1 = argtoll(args[0]);
-            t2 = argtoll(args[1]);
-            accel = 1.;
-        } else if (cnt == 1) {
-            t1 = 0;
-            t2 = 0;
-            accel = argtod(args[0]);
-        } else if (cnt == 0) {
-            t1 = 0;
-            t2 = 0;
-            accel = 1.;
-        } else
-            return q;
-        render_priv->state.detect_collisions = 0;
-        if (t2 == 0)
-            t2 = render_priv->state.event->Duration;
-        delta_t = t2 - t1;
-        t = render_priv->time - render_priv->state.event->Start;        // FIXME: move to render_context
-        if (t <= t1)
-            k = 0.;
-        else if (t >= t2)
-            k = 1.;
-        else {
-            assert(delta_t != 0.);
-            k = pow(((double) (t - t1)) / delta_t, accel);
-        }
-        p = args[cnt].start;
-        while (p < args[cnt].end)
-            p = parse_tag(render_priv, p, args[cnt].end, k);    // maybe k*pwr ? no, specs forbid nested \t's
-    } else if (complex_tag("clip")) {
-        if (nargs == 4) {
-            int x0, y0, x1, y1;
-            x0 = argtoi(args[0]);
-            y0 = argtoi(args[1]);
-            x1 = argtoi(args[2]);
-            y1 = argtoi(args[3]);
-            render_priv->state.clip_x0 =
-                render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
-            render_priv->state.clip_x1 =
-                render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr;
-            render_priv->state.clip_y0 =
-                render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
-            render_priv->state.clip_y1 =
-                render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
-            render_priv->state.clip_mode = 0;
-        } else if (!render_priv->state.clip_drawing) {
-            if (parse_vector_clip(render_priv, args, nargs))
-                render_priv->state.clip_drawing_mode = 0;
-        }
-    } else if (tag("c") || tag("1c")) {
-        if (nargs) {
-            uint32_t val = parse_color_tag(args->start);
-            change_color(&render_priv->state.c[0], val, pwr);
-        } else
-            change_color(&render_priv->state.c[0],
-                         render_priv->state.style->PrimaryColour, 1);
-    } else if (tag("2c")) {
-        if (nargs) {
-            uint32_t val = parse_color_tag(args->start);
-            change_color(&render_priv->state.c[1], val, pwr);
-        } else
-            change_color(&render_priv->state.c[1],
-                         render_priv->state.style->SecondaryColour, 1);
-    } else if (tag("3c")) {
-        if (nargs) {
-            uint32_t val = parse_color_tag(args->start);
-            change_color(&render_priv->state.c[2], val, pwr);
-        } else
-            change_color(&render_priv->state.c[2],
-                         render_priv->state.style->OutlineColour, 1);
-    } else if (tag("4c")) {
-        if (nargs) {
-            uint32_t val = parse_color_tag(args->start);
-            change_color(&render_priv->state.c[3], val, pwr);
-        } else
-            change_color(&render_priv->state.c[3],
-                         render_priv->state.style->BackColour, 1);
-    } else if (tag("1a")) {
-        if (nargs) {
-            uint32_t val = parse_alpha_tag(args->start);
-            change_alpha(&render_priv->state.c[0], val, pwr);
-        } else
-            change_alpha(&render_priv->state.c[0],
-                         _a(render_priv->state.style->PrimaryColour), 1);
-    } else if (tag("2a")) {
-        if (nargs) {
-            uint32_t val = parse_alpha_tag(args->start);
-            change_alpha(&render_priv->state.c[1], val, pwr);
-        } else
-            change_alpha(&render_priv->state.c[1],
-                         _a(render_priv->state.style->SecondaryColour), 1);
-    } else if (tag("3a")) {
-        if (nargs) {
-            uint32_t val = parse_alpha_tag(args->start);
-            change_alpha(&render_priv->state.c[2], val, pwr);
-        } else
-            change_alpha(&render_priv->state.c[2],
-                         _a(render_priv->state.style->OutlineColour), 1);
-    } else if (tag("4a")) {
-        if (nargs) {
-            uint32_t val = parse_alpha_tag(args->start);
-            change_alpha(&render_priv->state.c[3], val, pwr);
-        } else
-            change_alpha(&render_priv->state.c[3],
-                         _a(render_priv->state.style->BackColour), 1);
-    } else if (tag("r")) {
-        if (nargs) {
-            int len = args->end - args->start;
-            reset_render_context(render_priv,
-                    lookup_style_strict(render_priv->track, args->start, len));
-        } else
-            reset_render_context(render_priv, NULL);
-    } else if (tag("be")) {
-        double dval;
-        if (nargs) {
-            int val;
-            dval = argtod(*args);
-            // VSFilter always adds +0.5, even if the value is negative
-            val = (int) (render_priv->state.be * (1 - pwr) + dval * pwr + 0.5);
-            // Clamp to a safe upper limit, since high values need excessive CPU
+                render_priv->state.flags &= ~DECO_UNDERLINE;
+        } else if (tag("pbo")) {
+            double val = argtod(*args);
+            render_priv->state.pbo = val;
+        } else if (tag("p")) {
+            int val = argtoi(*args);
             val = (val < 0) ? 0 : val;
-            val = (val > MAX_BE) ? MAX_BE : val;
-            render_priv->state.be = val;
-        } else
-            render_priv->state.be = 0;
-    } else if (tag("b")) {
-        int val = argtoi(*args);
-        if (!nargs || !(val == 0 || val == 1 || val >= 100))
-            val = render_priv->state.style->Bold;
-        render_priv->state.bold = val;
-        update_font(render_priv);
-    } else if (tag("i")) {
-        int val = argtoi(*args);
-        if (!nargs || !(val == 0 || val == 1))
-            val = render_priv->state.style->Italic;
-        render_priv->state.italic = val;
-        update_font(render_priv);
-    } else if (tag("kf") || tag("K")) {
-        double val = 100;
-        if (nargs)
-            val = argtod(*args);
-        render_priv->state.effect_type = EF_KARAOKE_KF;
-        if (render_priv->state.effect_timing)
-            render_priv->state.effect_skip_timing +=
-                render_priv->state.effect_timing;
-        render_priv->state.effect_timing = val * 10;
-    } else if (tag("ko")) {
-        double val = 100;
-        if (nargs)
-            val = argtod(*args);
-        render_priv->state.effect_type = EF_KARAOKE_KO;
-        if (render_priv->state.effect_timing)
-            render_priv->state.effect_skip_timing +=
-                render_priv->state.effect_timing;
-        render_priv->state.effect_timing = val * 10;
-    } else if (tag("k")) {
-        double val = 100;
-        if (nargs)
-            val = argtod(*args);
-        render_priv->state.effect_type = EF_KARAOKE;
-        if (render_priv->state.effect_timing)
-            render_priv->state.effect_skip_timing +=
-                render_priv->state.effect_timing;
-        render_priv->state.effect_timing = val * 10;
-    } else if (tag("shad")) {
-        double val, xval, yval;
-        if (nargs) {
-            val = argtod(*args);
-            xval = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
-            yval = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
-            // VSFilter compatibility: clip for \shad but not for \[xy]shad
-            xval = (xval < 0) ? 0 : xval;
-            yval = (yval < 0) ? 0 : yval;
-        } else
-            xval = yval = render_priv->state.style->Shadow;
-        render_priv->state.shadow_x = xval;
-        render_priv->state.shadow_y = yval;
-    } else if (tag("s")) {
-        int val = argtoi(*args);
-        if (!nargs || !(val == 0 || val == 1))
-            val = render_priv->state.style->StrikeOut;
-        if (val)
-            render_priv->state.flags |= DECO_STRIKETHROUGH;
-        else
-            render_priv->state.flags &= ~DECO_STRIKETHROUGH;
-    } else if (tag("u")) {
-        int val = argtoi(*args);
-        if (!nargs || !(val == 0 || val == 1))
-            val = render_priv->state.style->Underline;
-        if (val)
-            render_priv->state.flags |= DECO_UNDERLINE;
-        else
-            render_priv->state.flags &= ~DECO_UNDERLINE;
-    } else if (tag("pbo")) {
-        double val = argtod(*args);
-        render_priv->state.pbo = val;
-    } else if (tag("p")) {
-        int val = argtoi(*args);
-        val = (val < 0) ? 0 : val;
-        render_priv->state.drawing_scale = val;
-    } else if (tag("q")) {
-        int val = argtoi(*args);
-        if (!nargs || !(val >= 0 && val <= 3))
-            val = render_priv->track->WrapStyle;
-        render_priv->state.wrap_style = val;
-    } else if (tag("fe")) {
-        int val;
-        if (nargs)
-            val = argtoi(*args);
-        else
-            val = render_priv->state.style->Encoding;
-        render_priv->state.font_encoding = val;
+            render_priv->state.drawing_scale = val;
+        } else if (tag("q")) {
+            int val = argtoi(*args);
+            if (!nargs || !(val >= 0 && val <= 3))
+                val = render_priv->track->WrapStyle;
+            render_priv->state.wrap_style = val;
+        } else if (tag("fe")) {
+            int val;
+            if (nargs)
+                val = argtoi(*args);
+            else
+                val = render_priv->state.style->Encoding;
+            render_priv->state.font_encoding = val;
+        }
     }
 
-    return q;
+    return p;
 }
 
 void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event)
diff --git a/libass/ass_parse.h b/libass/ass_parse.h
index 947f7ae6..1b1b131a 100644
--- a/libass/ass_parse.h
+++ b/libass/ass_parse.h
@@ -31,7 +31,7 @@ double ensure_font_size(ASS_Renderer *priv, double size);
 void apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event);
 void process_karaoke_effects(ASS_Renderer *render_priv);
 unsigned get_next_char(ASS_Renderer *render_priv, char **str);
-char *parse_tag(ASS_Renderer *render_priv, char *p, char *end, double pwr);
+char *parse_tags(ASS_Renderer *render_priv, char *p, char *end, double pwr);
 int event_has_hard_overrides(char *str);
 extern void change_alpha(uint32_t *var, int32_t new, double pwr);
 extern uint32_t mult_alpha(uint32_t a, uint32_t b);
diff --git a/libass/ass_render.c b/libass/ass_render.c
index be76f763..d5f881fb 100644
--- a/libass/ass_render.c
+++ b/libass/ass_render.c
@@ -1712,8 +1712,7 @@ static int parse_events(ASS_Renderer *render_priv, ASS_Event *event)
         code = 0;
         while (*p) {
             if ((*p == '{') && (q = strchr(p, '}'))) {
-                while (p < q)
-                    p = parse_tag(render_priv, p, q, 1.);
+                p = parse_tags(render_priv, p, q, 1.);
                 assert(*p == '}');
                 p++;
             } else if (render_priv->state.drawing_scale) {
openSUSE Build Service is sponsored by