]>
git.wh0rd.org - fontconfig.git/blob - src/fcformat.c
2 * Copyright © 2008,2009 Red Hat, Inc.
4 * Red Hat Author(s): Behdad Esfahbod
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of Keith Packard not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission. Keith Packard makes no
13 * representations about the suitability of this software for any purpose. It
14 * is provided "as is" without express or implied warranty.
16 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
32 * Some ideas for future syntax extensions:
34 * - langset enumeration using same syntax as array enumeration
35 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
36 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
40 #define FCMATCH_FORMAT "%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\""
41 #define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}"
45 message (const char *fmt
, ...)
49 fprintf (stderr
, "Fontconfig: Pattern format error: ");
50 vfprintf (stderr
, fmt
, args
);
51 fprintf (stderr
, ".\n");
56 typedef struct _FcFormatContext
58 const FcChar8
*format_orig
;
59 const FcChar8
*format
;
62 FcBool word_allocated
;
66 FcFormatContextInit (FcFormatContext
*c
,
67 const FcChar8
*format
,
71 c
->format_orig
= c
->format
= format
;
72 c
->format_len
= strlen ((const char *) format
);
74 if (c
->format_len
< scratch_len
)
77 c
->word_allocated
= FcFalse
;
81 c
->word
= malloc (c
->format_len
+ 1);
82 c
->word_allocated
= FcTrue
;
85 return c
->word
!= NULL
;
89 FcFormatContextDone (FcFormatContext
*c
)
91 if (c
&& c
->word_allocated
)
98 consume_char (FcFormatContext
*c
,
101 if (*c
->format
!= term
)
109 expect_char (FcFormatContext
*c
,
112 FcBool res
= consume_char (c
, term
);
115 if (c
->format
== c
->format_orig
+ c
->format_len
)
116 message ("format ended while expecting '%c'",
119 message ("expected '%c' at %d",
120 term
, c
->format
- c
->format_orig
+ 1);
126 FcCharIsPunct (const FcChar8 c
)
145 static char escaped_char(const char ch
)
148 case 'a': return '\a';
149 case 'b': return '\b';
150 case 'f': return '\f';
151 case 'n': return '\n';
152 case 'r': return '\r';
153 case 't': return '\t';
154 case 'v': return '\v';
160 read_word (FcFormatContext
*c
)
168 if (*c
->format
== '\\')
172 *p
++ = escaped_char (*c
->format
++);
175 else if (FcCharIsPunct (*c
->format
))
184 message ("expected identifier at %d",
185 c
->format
- c
->format_orig
+ 1);
193 read_chars (FcFormatContext
*c
,
200 while (*c
->format
&& *c
->format
!= '}' && *c
->format
!= term
)
202 if (*c
->format
== '\\')
206 *p
++ = escaped_char (*c
->format
++);
216 message ("expected character data at %d",
217 c
->format
- c
->format_orig
+ 1);
225 FcPatternFormatToBuf (FcPattern
*pat
,
226 const FcChar8
*format
,
230 interpret_builtin (FcFormatContext
*c
,
237 if (!expect_char (c
, '=') ||
241 /* try simple builtins first */
243 #define BUILTIN(name, func) \
244 else if (0 == strcmp ((const char *) c->word, name))\
245 do { new_str = func (pat); ret = FcTrue; } while (0)
246 BUILTIN ("unparse", FcNameUnparse
);
247 /* BUILTIN ("verbose", FcPatternPrint); XXX */
256 FcStrBufString (buf
, new_str
);
264 /* now try our custom formats */
266 #define BUILTIN(name, format) \
267 else if (0 == strcmp ((const char *) c->word, name))\
268 ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf)
269 BUILTIN ("fcmatch", FCMATCH_FORMAT
);
270 BUILTIN ("fclist", FCLIST_FORMAT
);
276 message ("unknown builtin \"%s\"",
283 interpret_expr (FcFormatContext
*c
,
289 interpret_subexpr (FcFormatContext
*c
,
293 return expect_char (c
, '{') &&
294 interpret_expr (c
, pat
, buf
, '}') &&
295 expect_char (c
, '}');
299 maybe_interpret_subexpr (FcFormatContext
*c
,
303 return (*c
->format
== '{') ?
304 interpret_subexpr (c
, pat
, buf
) :
309 skip_subexpr (FcFormatContext
*c
);
312 skip_percent (FcFormatContext
*c
)
316 if (!expect_char (c
, '%'))
319 /* skip an optional width specifier */
320 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
322 if (!expect_char (c
, '{'))
325 while(*c
->format
&& *c
->format
!= '}')
330 c
->format
++; /* skip over '\\' */
335 if (!skip_subexpr (c
))
342 return expect_char (c
, '}');
346 skip_expr (FcFormatContext
*c
)
348 while(*c
->format
&& *c
->format
!= '}')
353 c
->format
++; /* skip over '\\' */
358 if (!skip_percent (c
))
369 skip_subexpr (FcFormatContext
*c
)
371 return expect_char (c
, '{') &&
373 expect_char (c
, '}');
377 maybe_skip_subexpr (FcFormatContext
*c
)
379 return (*c
->format
== '{') ?
385 interpret_filter (FcFormatContext
*c
,
392 if (!expect_char (c
, '+'))
395 os
= FcObjectSetCreate ();
401 if (!read_word (c
) ||
402 !FcObjectSetAdd (os
, (const char *) c
->word
))
404 FcObjectSetDestroy (os
);
408 while (consume_char (c
, ','));
410 subpat
= FcPatternFilter (pat
, os
);
411 FcObjectSetDestroy (os
);
414 !interpret_subexpr (c
, subpat
, buf
))
417 FcPatternDestroy (subpat
);
422 interpret_delete (FcFormatContext
*c
,
428 if (!expect_char (c
, '-'))
431 subpat
= FcPatternDuplicate (pat
);
439 FcPatternDestroy (subpat
);
443 FcPatternDel (subpat
, (const char *) c
->word
);
445 while (consume_char (c
, ','));
447 if (!interpret_subexpr (c
, subpat
, buf
))
450 FcPatternDestroy (subpat
);
455 interpret_cond (FcFormatContext
*c
,
461 if (!expect_char (c
, '?'))
471 negate
= consume_char (c
, '!');
479 FcPatternGet (pat
, (const char *) c
->word
, 0, &v
)));
481 while (consume_char (c
, ','));
485 if (!interpret_subexpr (c
, pat
, buf
) ||
486 !maybe_skip_subexpr (c
))
491 if (!skip_subexpr (c
) ||
492 !maybe_interpret_subexpr (c
, pat
, buf
))
500 interpret_count (FcFormatContext
*c
,
506 FcChar8 buf_static
[64];
508 if (!expect_char (c
, '#'))
515 e
= FcPatternObjectFindElt (pat
,
516 FcObjectFromName ((const char *) c
->word
));
521 for (l
= FcPatternEltValues(e
);
527 snprintf ((char *) buf_static
, sizeof (buf_static
), "%d", count
);
528 FcStrBufString (buf
, buf_static
);
534 interpret_array (FcFormatContext
*c
,
540 const FcChar8
*format_save
;
544 if (!expect_char (c
, '[') ||
545 !expect_char (c
, ']'))
548 os
= FcObjectSetCreate ();
556 if (!read_word (c
) ||
557 !FcObjectSetAdd (os
, (const char *) c
->word
))
559 FcObjectSetDestroy (os
);
563 while (consume_char (c
, ','));
565 subpat
= FcPatternDuplicate (pat
);
569 format_save
= c
->format
;
577 for (i
= 0; i
< os
->nobject
; i
++)
581 /* XXX this can be optimized by accessing valuelist linked lists
582 * directly and remembering where we were. Most (all) value lists
583 * in normal uses are pretty short though (language tags are
584 * stored as a LangSet, not separate values.). */
585 FcPatternDel (subpat
, os
->objects
[i
]);
587 FcPatternGet (pat
, os
->objects
[i
], idx
, &v
))
589 FcPatternAdd (subpat
, os
->objects
[i
], v
, FcFalse
);
596 c
->format
= format_save
;
597 ret
= interpret_subexpr (c
, subpat
, buf
);
606 FcPatternDestroy (subpat
);
608 FcObjectSetDestroy (os
);
614 interpret_simple (FcFormatContext
*c
,
619 FcBool add_colon
= FcFalse
;
620 FcBool add_elt_name
= FcFalse
;
622 FcChar8
*else_string
;
624 if (consume_char (c
, ':'))
631 if (consume_char (c
, '['))
633 idx
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
636 message ("expected non-negative number at %d",
637 c
->format
-1 - c
->format_orig
+ 1);
640 if (!expect_char (c
, ']'))
644 if (consume_char (c
, '='))
645 add_elt_name
= FcTrue
;
649 if (consume_char (c
, ':'))
652 /* divert the c->word for now */
654 c
->word
= c
->word
+ strlen ((const char *) c
->word
) + 1;
655 /* for now we just support 'default value' */
656 if (!expect_char (c
, '-') ||
657 !read_chars (c
, '\0'))
662 else_string
= c
->word
;
666 e
= FcPatternObjectFindElt (pat
,
667 FcObjectFromName ((const char *) c
->word
));
673 FcStrBufChar (buf
, ':');
676 FcStrBufString (buf
, c
->word
);
677 FcStrBufChar (buf
, '=');
680 l
= FcPatternEltValues(e
);
686 l
= FcValueListNext(l
);
691 if (!FcNameUnparseValue (buf
, &l
->value
, '\0'))
698 FcNameUnparseValueList (buf
, l
, '\0');
705 printf ("%s", else_string
);
712 cescape (FcFormatContext
*c
,
722 FcStrBufChar (buf
, '\\');
725 FcStrBufChar (buf
, *str
++);
731 shescape (FcFormatContext
*c
,
735 FcStrBufChar (buf
, '\'');
739 FcStrBufString (buf
, (const FcChar8
*) "'\\''");
741 FcStrBufChar (buf
, *str
);
744 FcStrBufChar (buf
, '\'');
749 xmlescape (FcFormatContext
*c
,
757 case '&': FcStrBufString (buf
, (const FcChar8
*) "&"); break;
758 case '<': FcStrBufString (buf
, (const FcChar8
*) "<"); break;
759 case '>': FcStrBufString (buf
, (const FcChar8
*) ">"); break;
760 default: FcStrBufChar (buf
, *str
); break;
768 delete_chars (FcFormatContext
*c
,
772 /* XXX not UTF-8 aware */
774 if (!expect_char (c
, '(') ||
775 !read_chars (c
, ')') ||
776 !expect_char (c
, ')'))
783 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
786 FcStrBufData (buf
, str
, p
- str
);
791 FcStrBufString (buf
, str
);
801 escape_chars (FcFormatContext
*c
,
805 /* XXX not UTF-8 aware */
807 if (!expect_char (c
, '(') ||
808 !read_chars (c
, ')') ||
809 !expect_char (c
, ')'))
816 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
819 FcStrBufData (buf
, str
, p
- str
);
820 FcStrBufChar (buf
, c
->word
[0]);
821 FcStrBufChar (buf
, *p
);
826 FcStrBufString (buf
, str
);
836 translate_chars (FcFormatContext
*c
,
840 char *from
, *to
, repeat
;
841 int from_len
, to_len
;
843 /* XXX not UTF-8 aware */
845 if (!expect_char (c
, '(') ||
846 !read_chars (c
, ',') ||
847 !expect_char (c
, ','))
850 from
= (char *) c
->word
;
851 from_len
= strlen (from
);
852 to
= from
+ from_len
+ 1;
854 /* hack: we temporarily divert c->word */
855 c
->word
= (FcChar8
*) to
;
856 if (!read_chars (c
, ')'))
858 c
->word
= (FcChar8
*) from
;
861 c
->word
= (FcChar8
*) from
;
863 to_len
= strlen (to
);
864 repeat
= to
[to_len
- 1];
866 if (!expect_char (c
, ')'))
873 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) from
);
877 FcStrBufData (buf
, str
, p
- str
);
878 i
= strchr (from
, *p
) - from
;
879 FcStrBufChar (buf
, i
< to_len
? to
[i
] : repeat
);
884 FcStrBufString (buf
, str
);
894 interpret_convert (FcFormatContext
*c
,
901 FcChar8 buf_static
[8192];
904 if (!expect_char (c
, '|') ||
908 /* prepare the buffer */
909 FcStrBufChar (buf
, '\0');
912 str
= buf
->buf
+ start
;
915 /* try simple converters first */
917 #define CONVERTER(name, func) \
918 else if (0 == strcmp ((const char *) c->word, name))\
919 do { new_str = func (str); ret = FcTrue; } while (0)
920 CONVERTER ("downcase", FcStrDowncase
);
921 CONVERTER ("basename", FcStrBasename
);
922 CONVERTER ("dirname", FcStrDirname
);
931 FcStrBufString (buf
, new_str
);
939 FcStrBufInit (&new_buf
, buf_static
, sizeof (buf_static
));
941 /* now try our custom converters */
943 #define CONVERTER(name, func) \
944 else if (0 == strcmp ((const char *) c->word, name))\
945 ret = func (c, str, &new_buf)
946 CONVERTER ("cescape", cescape
);
947 CONVERTER ("shescape", shescape
);
948 CONVERTER ("xmlescape", xmlescape
);
949 CONVERTER ("delete", delete_chars
);
950 CONVERTER ("escape", escape_chars
);
951 CONVERTER ("translate", translate_chars
);
958 FcStrBufChar (&new_buf
, '\0');
959 FcStrBufString (buf
, new_buf
.buf
);
962 message ("unknown converter \"%s\"",
965 FcStrBufDestroy (&new_buf
);
971 maybe_interpret_converts (FcFormatContext
*c
,
975 while (*c
->format
== '|')
976 if (!interpret_convert (c
, buf
, start
))
983 align_to_width (FcStrBuf
*buf
,
992 len
= buf
->len
- start
;
996 while (len
++ < -width
)
997 FcStrBufChar (buf
, ' ');
999 else if (len
< width
)
1004 while (len
++ < width
)
1005 FcStrBufChar (buf
, ' ');
1009 memmove (buf
->buf
+ buf
->len
- len
,
1010 buf
->buf
+ buf
->len
- width
,
1012 memset (buf
->buf
+ buf
->len
- width
,
1017 return !buf
->failed
;
1020 interpret_percent (FcFormatContext
*c
,
1027 if (!expect_char (c
, '%'))
1030 if (consume_char (c
, '%')) /* "%%" */
1032 FcStrBufChar (buf
, '%');
1036 /* parse an optional width specifier */
1037 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
1039 if (!expect_char (c
, '{'))
1044 switch (*c
->format
) {
1045 case '=': ret
= interpret_builtin (c
, pat
, buf
); break;
1046 case '{': ret
= interpret_subexpr (c
, pat
, buf
); break;
1047 case '+': ret
= interpret_filter (c
, pat
, buf
); break;
1048 case '-': ret
= interpret_delete (c
, pat
, buf
); break;
1049 case '?': ret
= interpret_cond (c
, pat
, buf
); break;
1050 case '#': ret
= interpret_count (c
, pat
, buf
); break;
1051 case '[': ret
= interpret_array (c
, pat
, buf
); break;
1052 default: ret
= interpret_simple (c
, pat
, buf
); break;
1056 maybe_interpret_converts (c
, buf
, start
) &&
1057 align_to_width (buf
, start
, width
) &&
1058 expect_char (c
, '}');
1062 interpret_expr (FcFormatContext
*c
,
1067 while (*c
->format
&& *c
->format
!= term
)
1072 c
->format
++; /* skip over '\\' */
1074 FcStrBufChar (buf
, escaped_char (*c
->format
++));
1077 if (!interpret_percent (c
, pat
, buf
))
1081 FcStrBufChar (buf
, *c
->format
++);
1087 FcPatternFormatToBuf (FcPattern
*pat
,
1088 const FcChar8
*format
,
1092 FcChar8 word_static
[1024];
1095 if (!FcFormatContextInit (&c
, format
, word_static
, sizeof (word_static
)))
1098 ret
= interpret_expr (&c
, pat
, buf
, '\0');
1100 FcFormatContextDone (&c
);
1106 FcPatternFormat (FcPattern
*pat
,
1107 const FcChar8
*format
)
1110 FcChar8 buf_static
[8192 - 1024];
1113 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
1115 ret
= FcPatternFormatToBuf (pat
, format
, &buf
);
1118 return FcStrBufDone (&buf
);
1121 FcStrBufDestroy (&buf
);
1126 #define __fcformat__
1127 #include "fcaliastail.h"