diff options
author | Philip Withnall <philip@tecnocode.co.uk> | 2011-02-20 14:18:05 +0000 |
---|---|---|
committer | Philip Withnall <philip@tecnocode.co.uk> | 2011-02-20 19:24:42 +0000 |
commit | 68c5019448636f0d2f872dfd9b0e03a1cdf6b41b (patch) | |
tree | e20a181d4ddd9d2f5481fdf1441860a4c98a782c /src | |
parent | 6ab1e47cdbe0c5a47553c1170ee6c2313fc7e512 (diff) |
Bug 642758 — Chapters (CMML) plugin stores time offsets as localized floats
Don't use sprintf() or sscanf() in the CMML parser/writer code, as they're
locale-dependent. Instead, we have to use the g_ascii_*() functions, and
build our own parser. Closes: bgo#642758
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/chapters/totem-cmml-parser.c | 135 |
1 files changed, 110 insertions, 25 deletions
diff --git a/src/plugins/chapters/totem-cmml-parser.c b/src/plugins/chapters/totem-cmml-parser.c index e91c3f68..07a761ea 100644 --- a/src/plugins/chapters/totem-cmml-parser.c +++ b/src/plugins/chapters/totem-cmml-parser.c @@ -179,16 +179,61 @@ totem_cmml_parse_smpte (const gchar *str, { gint h = 0, m = 0, s = 0; gfloat frames; + const gchar *endptr, *old_endptr; if (G_UNLIKELY (str == NULL)) return -1.0; - /* all is according to specs */ - if (sscanf (str, "%d:%d:%d:%f", &h, &m, &s, &frames) == 4); - /* this is slightly off the specs, but we can handle it */ - else if (sscanf (str, "%d:%d:%f", &m, &s, &frames) == 3) - h = 0; + /* We expect something in the format %d:%d:%d:%f (hours:minutes:seconds:frames), but we can handle %d:%d:%f (minutes:seconds:frames) as well. + * We can't use sscanf() as it's locale-dependent. + * + * Test cases (with framerate = 24): + * "1:2:3:12.5" -> 3723.520833333 + * "1:2:3:12" -> 3723.5 + * "1:1:3:12.5" -> 3663.520833333 + * "1:2:3:-12.5" -> -1.0 + * "1:2:3:12.0" -> 3723.5 + * "0:2:3:12.5" -> 123.520833333 + * "2:3:12.5" -> 123.520833333 + * "2:3.6:12.5" -> -1.0 + * "::" -> -1.0 + * ":::" -> -1.0 + * "asd" -> -1.0 + * "" -> -1.0 + */ + old_endptr = str; + h = g_ascii_strtoll (str, (gchar**) &endptr, 10); + if (*endptr != ':' || endptr == old_endptr) + return -1.0; + else + endptr++; + + old_endptr = endptr; + m = g_ascii_strtoll (endptr, (gchar**) &endptr, 10); + if (*endptr != ':' || endptr == old_endptr) + return -1.0; else + endptr++; + + old_endptr = endptr; + s = g_ascii_strtoll (endptr, (gchar**) &endptr, 10); + if (*endptr == '\0' || endptr == old_endptr) { + return -1.0; + } else if (*endptr != ':') { + /* We've probably got the %d:%d:%f format; back up and try parsing a float */ + endptr = old_endptr; + + /* Shift all the previously-parsed parameters along */ + s = m; + m = h; + h = 0; + } else { + endptr++; + } + + old_endptr = endptr; + frames = g_ascii_strtod (endptr, (gchar**) &endptr); + if (*endptr != '\0' || endptr == old_endptr) return -1.0; /* check time and framerate bounds */ @@ -210,19 +255,59 @@ totem_cmml_parse_npt (const gchar *str) { gint h, m; gfloat s; + const gchar *endptr, *old_endptr; if (G_UNLIKELY (str == NULL)) return -1.0; - /* all is ok */ - if (sscanf (str, "%d:%d:%f", &h, &m, &s) == 3); - /* slightly off the spec */ - else if (sscanf (str, "%d:%f", &m, &s) == 2) + /* We expect something in the format %d:%d:%f (hours:minutes:seconds), but we can handle %d:%f (minutes:seconds) or %f (seconds) as well. + * We can't use sscanf() as it's locale-dependent. + * + * Test cases: + * "1:2:0.5" -> 3720.5 + * "1:2" -> 62.0 + * "0:2:0.5" -> 120.5 + * "2:0.5" -> 120.5 + * "0:0:0.5" -> 0.5 + * "0.5" -> 0.5 + * "6" -> 6.0 + * "::" -> -1.0 + * ":" -> -1.0 + * "asd" -> -1.0 + * "" -> -1.0 + */ + old_endptr = str; + h = g_ascii_strtoll (str, (gchar**) &endptr, 10); + if (endptr == old_endptr) { + return -1.0; + } else if (*endptr != ':') { + /* We've probably got the %f format */ + endptr = old_endptr; + + /* Zero the hours */ h = 0; - /* time in seconds, all is ok and we can return it now */ - else if (G_LIKELY (sscanf (str, "%f", &s))) - return s; - else + } else { + endptr++; + } + + old_endptr = endptr; + m = g_ascii_strtoll (endptr, (gchar**) &endptr, 10); + if (endptr == old_endptr) { + return -1.0; + } else if (*endptr != ':') { + /* We've probably got the %d:%f format */ + endptr = old_endptr; + + /* Shift all the previously-parsed parameters along */ + m = h; + h = 0; + } else { + endptr++; + } + + old_endptr = endptr; + s = g_ascii_strtod (endptr, (gchar**) &endptr); + if (*endptr != '\0' || endptr == old_endptr) return -1.0; if (G_UNLIKELY (h < 0)) @@ -239,37 +324,35 @@ totem_cmml_parse_npt (const gchar *str) static gdouble totem_cmml_parse_time_str (const gchar *str) { - gchar timespec[16]; - if (G_UNLIKELY (str == NULL)) return -1.0; /* we need to choose parsing function to use */ - if (sscanf (str, "npt:%16s", timespec) == 1) + if (g_str_has_prefix (str, "npt:")) return totem_cmml_parse_npt (str + 4); - if (sscanf (str, "smpte-24:%16s", timespec) == 1) + if (g_str_has_prefix (str, "smpte-24:")) return totem_cmml_parse_smpte (str + 9, 24.0); - if (sscanf (str, "smpte-24-drop:%16s", timespec) == 1) + if (g_str_has_prefix (str, "smpte-24-drop:")) return totem_cmml_parse_smpte (str + 14, 23.976); - if (sscanf (str, "smpte-25:%16s", timespec) == 1) + if (g_str_has_prefix (str, "smpte-25:")) return totem_cmml_parse_smpte (str + 9, 25.0); - if (sscanf (str, "smpte-30:%16s", timespec) == 1) + if (g_str_has_prefix (str, "smpte-30:")) return totem_cmml_parse_smpte (str + 9, 30.0); - if (sscanf (str, "smpte-30-drop:%16s", timespec) == 1) + if (g_str_has_prefix (str, "smpte-30-drop:")) return totem_cmml_parse_smpte (str + 14, 29.97); - if (sscanf (str, "smpte-50:%16s", timespec) == 1) + if (g_str_has_prefix (str, "smpte-50:")) return totem_cmml_parse_smpte (str + 9, 50.0); - if (sscanf (str, "smpte-60:%16s", timespec) == 1) + if (g_str_has_prefix (str, "smpte-60:")) return totem_cmml_parse_smpte (str + 9, 60); - if (sscanf (str, "smpte-60-drop:%16s", timespec) == 1) + if (g_str_has_prefix (str, "smpte-60-drop:")) return totem_cmml_parse_smpte (str + 14, 59.94); /* default is npt */ @@ -721,6 +804,7 @@ totem_cmml_write_file_async (TotemCmmlAsyncData *data) guint st_len; guint8 *stream; TotemCmmlClip *clip; + gchar start_buf[G_ASCII_DTOSTR_BUF_SIZE]; clip = (TotemCmmlClip *) cur_clip->data; time_start = ((gdouble) clip->time_start) / 1000; @@ -734,7 +818,8 @@ totem_cmml_write_file_async (TotemCmmlAsyncData *data) if (G_UNLIKELY (res < 0)) break; - res = xmlTextWriterWriteFormatAttribute (writer, (const xmlChar *) "start", "%.3f", time_start); + res = xmlTextWriterWriteAttribute (writer, (const xmlChar *) "start", + (const xmlChar *) g_ascii_dtostr (start_buf, sizeof (buf), time_start)); if (G_UNLIKELY (res < 0)) break; |