From 8c31a2434d5dfa475ef710ad52c992111caac424 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Mon, 9 Feb 2009 23:08:08 -0500 Subject: [PATCH] [fcformat] Add element filtering and deletion The filtering, '%{+elt1,elt2,elt3{subexpr}}' will evaluate subexpr with a pattern only having the listed elements from the surrounding pattern. The deletion, '%{-elt1,elt2,elt3{subexpr}}' will evaluate subexpr with a the surrounding pattern sans the listed elements. --- doc/fcpattern.fncs | 2 +- fc-list/fc-list.c | 7 +- fc-match/fc-match.c | 6 +- fc-query/fc-query.c | 7 +- fc-scan/fc-scan.c | 7 +- src/fcformat.c | 214 +++++++++++++++++++++++++++++++++----------- 6 files changed, 183 insertions(+), 60 deletions(-) diff --git a/doc/fcpattern.fncs b/doc/fcpattern.fncs index 22a89ad..35ef68b 100644 --- a/doc/fcpattern.fncs +++ b/doc/fcpattern.fncs @@ -413,5 +413,5 @@ pattern, use the format "%{family} %{style}\n". There can be an option width specifier after the percent sign and before the opening brace. The width modifier acts similar to those in printf. The return value refers to newly allocated memory which should be freed by the -caller using free(). +caller using free(), or NULL if format is invalid. @@ diff --git a/fc-list/fc-list.c b/fc-list/fc-list.c index 470b4b9..cbcc3c0 100644 --- a/fc-list/fc-list.c +++ b/fc-list/fc-list.c @@ -178,8 +178,11 @@ main (int argc, char **argv) FcChar8 *s; s = FcPatternFormat (fs->fonts[j], format); - printf ("%s", s); - free (s); + if (s) + { + printf ("%s", s); + free (s); + } } else { diff --git a/fc-match/fc-match.c b/fc-match/fc-match.c index 6f3d2dc..86ff253 100644 --- a/fc-match/fc-match.c +++ b/fc-match/fc-match.c @@ -214,7 +214,11 @@ main (int argc, char **argv) FcChar8 *s; s = FcPatternFormat (font, format); - free (s); + if (s) + { + printf ("%s", s); + free (s); + } } else if (os) { diff --git a/fc-query/fc-query.c b/fc-query/fc-query.c index 8c5672c..4d707bc 100644 --- a/fc-query/fc-query.c +++ b/fc-query/fc-query.c @@ -162,8 +162,11 @@ main (int argc, char **argv) FcChar8 *s; s = FcPatternFormat (pat, format); - printf ("%s", s); - free (s); + if (s) + { + printf ("%s", s); + free (s); + } } else { diff --git a/fc-scan/fc-scan.c b/fc-scan/fc-scan.c index 573f7ef..1f023fb 100644 --- a/fc-scan/fc-scan.c +++ b/fc-scan/fc-scan.c @@ -165,8 +165,11 @@ main (int argc, char **argv) FcChar8 *s; s = FcPatternFormat (pat, format); - printf ("%s", s); - free (s); + if (s) + { + printf ("%s", s); + free (s); + } } else { diff --git a/src/fcformat.c b/src/fcformat.c index 30c5a8d..31f3a74 100644 --- a/src/fcformat.c +++ b/src/fcformat.c @@ -33,7 +33,7 @@ message (const char *fmt, ...) { va_list args; va_start (args, fmt); - fprintf (stderr, "Fontconfig: Pattern format error:"); + fprintf (stderr, "Fontconfig: Pattern format error: "); vfprintf (stderr, fmt, args); fprintf (stderr, ".\n"); va_end (args); @@ -48,13 +48,15 @@ typedef struct _FcFormatContext FcChar8 *scratch; } FcFormatContext; -static void +static FcBool FcFormatContextInit (FcFormatContext *c, const FcChar8 *format) { c->format_orig = c->format = format; c->format_len = strlen ((const char *) format); c->scratch = malloc (c->format_len + 1); + + return c->scratch != NULL; } static void @@ -147,60 +149,36 @@ read_elt_name_to_scratch (FcFormatContext *c) return FcTrue; } -static void +static FcBool interpret (FcFormatContext *c, FcPattern *pat, FcStrBuf *buf, FcChar8 term); -static void -interpret_percent (FcFormatContext *c, +static FcBool +interpret_subexpr (FcFormatContext *c, FcPattern *pat, FcStrBuf *buf) { - int width, before; - FcChar8 *p; - FcPatternElt *e; + return expect_char (c, '{') && + interpret (c, pat, buf, '}') && + expect_char (c, '}'); +} - FcPattern *subpat = pat; +static FcBool +interpret_simple_tag (FcFormatContext *c, + FcPattern *pat, + FcStrBuf *buf) +{ + FcPatternElt *e; FcBool add_colon = FcFalse; FcBool add_elt_name = FcFalse; - if (!expect_char (c, '%')) - return; - - if (consume_char (c, '%')) /* "%%" */ - { - FcStrBufChar (buf, '%'); - return; - } - - /* parse an optional width specifier */ - width = strtol ((const char *) c->format, (char **) &c->format, 10); - - before = buf->len; - - if (!expect_char (c, '{')) - goto bail; - - if (consume_char (c, '{')) - { - /* it's just a subexpression. no tag involved */ - interpret (c, pat, buf, '}'); - expect_char (c, '}'); - goto filter; - } - - switch (*c->format) { - case ':': + if (consume_char (c, ':')) add_colon = FcTrue; - consume_char (c, ':'); - break; - } -parse_tag: if (!read_elt_name_to_scratch (c)) - goto bail; + return FcFalse; if (consume_char (c, '=')) add_elt_name = FcTrue; @@ -223,7 +201,131 @@ parse_tag: FcNameUnparseValueList (buf, l, '\0'); } -filter: + return FcTrue; +} + +static FcBool +interpret_filter (FcFormatContext *c, + FcPattern *pat, + FcStrBuf *buf) +{ + FcObjectSet *os; + FcPattern *subpat; + + if (!expect_char (c, '+')) + return FcFalse; + + os = FcObjectSetCreate (); + if (!os) + return FcFalse; + + do + { + if (!read_elt_name_to_scratch (c) || + !FcObjectSetAdd (os, (const char *) c->scratch)) + { + FcObjectSetDestroy (os); + return FcFalse; + } + } + while (consume_char (c, ',')); + + subpat = FcPatternFilter (pat, os); + FcObjectSetDestroy (os); + + if (!subpat || + !interpret_subexpr (c, subpat, buf)) + return FcFalse; + + FcPatternDestroy (subpat); + return FcTrue; +} + +static FcBool +interpret_delete (FcFormatContext *c, + FcPattern *pat, + FcStrBuf *buf) +{ + FcPattern *subpat; + + if (!expect_char (c, '-')) + return FcFalse; + + subpat = FcPatternDuplicate (pat); + if (!subpat) + return FcFalse; + + do + { + if (!read_elt_name_to_scratch (c)) + { + FcPatternDestroy (subpat); + return FcFalse; + } + + FcPatternDel (subpat, (const char *) c->scratch); + } + while (consume_char (c, ',')); + + if (!interpret_subexpr (c, subpat, buf)) + return FcFalse; + + FcPatternDestroy (subpat); + return FcTrue; +} + +static FcBool +interpret_percent (FcFormatContext *c, + FcPattern *pat, + FcStrBuf *buf) +{ + int width, before; + + if (!expect_char (c, '%')) + return FcFalse; + + if (consume_char (c, '%')) /* "%%" */ + { + FcStrBufChar (buf, '%'); + return FcTrue; + } + + /* parse an optional width specifier */ + width = strtol ((const char *) c->format, (char **) &c->format, 10); + + before = buf->len; + + if (!expect_char (c, '{')) + return FcFalse; + + switch (*c->format) { + + case '{': + /* subexpression */ + if (!interpret_subexpr (c, pat, buf)) + return FcFalse; + break; + + case '+': + /* filtering pattern elements */ + if (!interpret_filter (c, pat, buf)) + return FcFalse; + break; + + case '-': + /* deleting pattern elements */ + if (!interpret_delete (c, pat, buf)) + return FcFalse; + break; + + default: + /* simple tag */ + if (!interpret_simple_tag (c, pat, buf)) + return FcFalse; + break; + + } + /* handle filters, if any */ /* XXX */ @@ -257,11 +359,7 @@ filter: } } - expect_char (c, '}'); - -bail: - if (subpat != pat) - FcPatternDestroy (subpat); + return expect_char (c, '}'); } static char escaped_char(const char ch) @@ -278,7 +376,7 @@ static char escaped_char(const char ch) } } -static void +static FcBool interpret (FcFormatContext *c, FcPattern *pat, FcStrBuf *buf, @@ -294,11 +392,13 @@ interpret (FcFormatContext *c, FcStrBufChar (buf, escaped_char (*c->format++)); continue; case '%': - interpret_percent (c, pat, buf); + if (!interpret_percent (c, pat, buf)) + return FcFalse; continue; } FcStrBufChar (buf, *c->format++); } + return FcTrue; } FcChar8 * @@ -306,14 +406,24 @@ FcPatternFormat (FcPattern *pat, const FcChar8 *format) { FcStrBuf buf; FcFormatContext c; + FcBool ret; FcStrBufInit (&buf, 0, 0); - FcFormatContextInit (&c, format); + if (!FcFormatContextInit (&c, format)) + return NULL; - interpret (&c, pat, &buf, '\0'); + ret = interpret (&c, pat, &buf, '\0'); + if (buf.failed) + ret = FcFalse; FcFormatContextDone (&c); - return FcStrBufDone (&buf); + if (ret) + return FcStrBufDone (&buf); + else + { + FcStrBufDestroy (&buf); + return NULL; + } } #define __fcformat__ -- 2.39.2