-int ucs4cmp(const uint32_t *a, const uint32_t *b) {
- while(*a && *b && *a == *b) ++a, ++b;
- if(*a > *b) return 1;
- else if(*a < *b) return -1;
- else return 0;
+/** @brief Truncate a string for display purposes
+ * @param s Pointer to UTF-8 string
+ * @param max Maximum number of columns
+ * @return @p or truncated string (never NULL)
+ *
+ * Returns a string that is no longer than @p max graphemes long and is either
+ * (canonically) equal to @p s or is a truncated form of it with an ellipsis
+ * appended.
+ *
+ * We don't take display width into account (tricky for HTML!) and we don't
+ * attempt to implement the Bidi algorithm. If you have track names for which
+ * either of these matter in practice then get in touch.
+ */
+const char *truncate_for_display(const char *s, long max) {
+ uint32_t *s32;
+ size_t l32, cut;
+ utf32_iterator it;
+ long graphemes;
+
+ /* Convert to UTF-32 for processing */
+ if(!(s32 = utf8_to_utf32(s, strlen(s), &l32)))
+ return 0;
+ it = utf32_iterator_new(s32, l32);
+ cut = l32;
+ graphemes = 0; /* # of graphemes left of it */
+ while(graphemes <= max && utf32_iterator_where(it) < l32) {
+ if(graphemes == max - 1)
+ cut = utf32_iterator_where(it);
+ utf32_iterator_advance(it, 1);
+ if(utf32_iterator_grapheme_boundary(it))
+ ++graphemes;
+ }
+ if(graphemes > max) { /* we need to cut */
+ s32[cut] = 0x2026; /* HORIZONTAL ELLIPSIS */
+ l32 = cut + 1;
+ s = utf32_to_utf8(s32, l32, 0);
+ }
+ xfree(s32);
+ return s;