]>
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 * - array enumeration using '%{[]family,familylang{expr}|decorator}'
35 * - langset enumeration using same syntax as array enumeration
36 * - allow indexing simple tags using '%{elt[idx]}'
37 * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
38 * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
42 /* fc-match needs '<unknown filename>', etc handling. */
43 #define FCMATCH_FORMAT "%{file|basename}: \"%{family[0]}\" \"%{style[0]}\""
44 #define FCLIST_FORMAT "%{?file{%{file}: }}%{=unparse}"
48 message (const char *fmt
, ...)
52 fprintf (stderr
, "Fontconfig: Pattern format error: ");
53 vfprintf (stderr
, fmt
, args
);
54 fprintf (stderr
, ".\n");
59 typedef struct _FcFormatContext
61 const FcChar8
*format_orig
;
62 const FcChar8
*format
;
65 FcBool word_allocated
;
69 FcFormatContextInit (FcFormatContext
*c
,
70 const FcChar8
*format
,
74 c
->format_orig
= c
->format
= format
;
75 c
->format_len
= strlen ((const char *) format
);
77 if (c
->format_len
< scratch_len
)
80 c
->word_allocated
= FcFalse
;
84 c
->word
= malloc (c
->format_len
+ 1);
85 c
->word_allocated
= FcTrue
;
88 return c
->word
!= NULL
;
92 FcFormatContextDone (FcFormatContext
*c
)
94 if (c
&& c
->word_allocated
)
101 consume_char (FcFormatContext
*c
,
104 if (*c
->format
!= term
)
112 expect_char (FcFormatContext
*c
,
115 FcBool res
= consume_char (c
, term
);
118 if (c
->format
== c
->format_orig
+ c
->format_len
)
119 message ("format ended while expecting '%c'",
122 message ("expected '%c' at %d",
123 term
, c
->format
- c
->format_orig
+ 1);
129 FcCharIsPunct (const FcChar8 c
)
148 static char escaped_char(const char ch
)
151 case 'a': return '\a';
152 case 'b': return '\b';
153 case 'f': return '\f';
154 case 'n': return '\n';
155 case 'r': return '\r';
156 case 't': return '\t';
157 case 'v': return '\v';
163 read_word (FcFormatContext
*c
)
171 if (*c
->format
== '\\')
175 *p
++ = escaped_char (*c
->format
++);
178 else if (FcCharIsPunct (*c
->format
))
187 message ("expected identifier at %d",
188 c
->format
- c
->format_orig
+ 1);
196 read_chars (FcFormatContext
*c
,
203 while (*c
->format
&& *c
->format
!= '}' && *c
->format
!= term
)
205 if (*c
->format
== '\\')
209 *p
++ = escaped_char (*c
->format
++);
219 message ("expected character data at %d",
220 c
->format
- c
->format_orig
+ 1);
228 FcPatternFormatToBuf (FcPattern
*pat
,
229 const FcChar8
*format
,
233 interpret_builtin (FcFormatContext
*c
,
240 if (!expect_char (c
, '=') ||
244 /* try simple builtins first */
246 #define BUILTIN(name, func) \
247 else if (0 == strcmp ((const char *) c->word, name))\
248 do { new_str = func (pat); ret = FcTrue; } while (0)
249 BUILTIN ("unparse", FcNameUnparse
);
250 /* BUILTIN ("verbose", FcPatternPrint); */
259 FcStrBufString (buf
, new_str
);
267 /* now try our custom formats */
269 #define BUILTIN(name, format) \
270 else if (0 == strcmp ((const char *) c->word, name))\
271 ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf)
272 BUILTIN ("fcmatch", FCMATCH_FORMAT
);
273 BUILTIN ("fclist", FCLIST_FORMAT
);
279 message ("unknown builtin \"%s\"",
286 interpret_expr (FcFormatContext
*c
,
292 interpret_subexpr (FcFormatContext
*c
,
296 return expect_char (c
, '{') &&
297 interpret_expr (c
, pat
, buf
, '}') &&
298 expect_char (c
, '}');
302 maybe_interpret_subexpr (FcFormatContext
*c
,
306 return (*c
->format
== '{') ?
307 interpret_subexpr (c
, pat
, buf
) :
312 skip_subexpr (FcFormatContext
*c
);
315 skip_percent (FcFormatContext
*c
)
319 if (!expect_char (c
, '%'))
322 /* skip an optional width specifier */
323 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
325 if (!expect_char (c
, '{'))
328 while(*c
->format
&& *c
->format
!= '}')
333 c
->format
++; /* skip over '\\' */
338 if (!skip_subexpr (c
))
345 return expect_char (c
, '}');
349 skip_expr (FcFormatContext
*c
)
351 while(*c
->format
&& *c
->format
!= '}')
356 c
->format
++; /* skip over '\\' */
361 if (!skip_percent (c
))
372 skip_subexpr (FcFormatContext
*c
)
374 return expect_char (c
, '{') &&
376 expect_char (c
, '}');
380 maybe_skip_subexpr (FcFormatContext
*c
)
382 return (*c
->format
== '{') ?
388 interpret_filter (FcFormatContext
*c
,
395 if (!expect_char (c
, '+'))
398 os
= FcObjectSetCreate ();
404 if (!read_word (c
) ||
405 !FcObjectSetAdd (os
, (const char *) c
->word
))
407 FcObjectSetDestroy (os
);
411 while (consume_char (c
, ','));
413 subpat
= FcPatternFilter (pat
, os
);
414 FcObjectSetDestroy (os
);
417 !interpret_subexpr (c
, subpat
, buf
))
420 FcPatternDestroy (subpat
);
425 interpret_delete (FcFormatContext
*c
,
431 if (!expect_char (c
, '-'))
434 subpat
= FcPatternDuplicate (pat
);
442 FcPatternDestroy (subpat
);
446 FcPatternDel (subpat
, (const char *) c
->word
);
448 while (consume_char (c
, ','));
450 if (!interpret_subexpr (c
, subpat
, buf
))
453 FcPatternDestroy (subpat
);
458 interpret_cond (FcFormatContext
*c
,
464 if (!expect_char (c
, '?'))
474 negate
= consume_char (c
, '!');
481 (FcResultMatch
== FcPatternGet (pat
,
482 (const char *) c
->word
,
485 while (consume_char (c
, ','));
489 if (!interpret_subexpr (c
, pat
, buf
) ||
490 !maybe_skip_subexpr (c
))
495 if (!skip_subexpr (c
) ||
496 !maybe_interpret_subexpr (c
, pat
, buf
))
504 interpret_count (FcFormatContext
*c
,
510 FcChar8 buf_static
[64];
512 if (!expect_char (c
, '#'))
519 e
= FcPatternObjectFindElt (pat
,
520 FcObjectFromName ((const char *) c
->word
));
525 for (l
= FcPatternEltValues(e
);
531 snprintf ((char *) buf_static
, sizeof (buf_static
), "%d", count
);
532 FcStrBufString (buf
, buf_static
);
538 interpret_simple (FcFormatContext
*c
,
543 FcBool add_colon
= FcFalse
;
544 FcBool add_elt_name
= FcFalse
;
547 if (consume_char (c
, ':'))
554 if (consume_char (c
, '['))
556 idx
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
559 message ("expected non-negative number at %d",
560 c
->format
-1 - c
->format_orig
+ 1);
563 expect_char (c
, ']');
566 if (consume_char (c
, '='))
567 add_elt_name
= FcTrue
;
569 e
= FcPatternObjectFindElt (pat
,
570 FcObjectFromName ((const char *) c
->word
));
576 FcStrBufChar (buf
, ':');
579 FcStrBufString (buf
, c
->word
);
580 FcStrBufChar (buf
, '=');
583 l
= FcPatternEltValues(e
);
589 l
= FcValueListNext(l
);
594 if (!FcNameUnparseValue (buf
, &l
->value
, '\0'))
601 FcNameUnparseValueList (buf
, l
, '\0');
613 cescape (FcFormatContext
*c
,
623 FcStrBufChar (buf
, '\\');
626 FcStrBufChar (buf
, *str
++);
632 shescape (FcFormatContext
*c
,
636 FcStrBufChar (buf
, '\'');
640 FcStrBufString (buf
, (const FcChar8
*) "'\\''");
642 FcStrBufChar (buf
, *str
);
645 FcStrBufChar (buf
, '\'');
650 xmlescape (FcFormatContext
*c
,
658 case '&': FcStrBufString (buf
, (const FcChar8
*) "&"); break;
659 case '<': FcStrBufString (buf
, (const FcChar8
*) "<"); break;
660 case '>': FcStrBufString (buf
, (const FcChar8
*) ">"); break;
661 default: FcStrBufChar (buf
, *str
); break;
669 delete_chars (FcFormatContext
*c
,
673 /* XXX not UTF-8 aware */
675 if (!expect_char (c
, '(') ||
676 !read_chars (c
, ')') ||
677 !expect_char (c
, ')'))
684 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
687 FcStrBufData (buf
, str
, p
- str
);
692 FcStrBufString (buf
, str
);
702 escape_chars (FcFormatContext
*c
,
706 /* XXX not UTF-8 aware */
708 if (!expect_char (c
, '(') ||
709 !read_chars (c
, ')') ||
710 !expect_char (c
, ')'))
717 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) c
->word
);
720 FcStrBufData (buf
, str
, p
- str
);
721 FcStrBufChar (buf
, c
->word
[0]);
722 FcStrBufChar (buf
, *p
);
727 FcStrBufString (buf
, str
);
737 translate_chars (FcFormatContext
*c
,
741 char *from
, *to
, repeat
;
742 int from_len
, to_len
;
744 /* XXX not UTF-8 aware */
746 if (!expect_char (c
, '(') ||
747 !read_chars (c
, ',') ||
748 !expect_char (c
, ','))
751 from
= (char *) c
->word
;
752 from_len
= strlen (from
);
753 to
= from
+ from_len
+ 1;
755 /* hack: we temporarily diverge c->word */
756 c
->word
= (FcChar8
*) to
;
757 if (!read_chars (c
, ')'))
759 c
->word
= (FcChar8
*) from
;
762 c
->word
= (FcChar8
*) from
;
764 to_len
= strlen (to
);
765 repeat
= to
[to_len
- 1];
767 if (!expect_char (c
, ')'))
774 p
= (FcChar8
*) strpbrk ((const char *) str
, (const char *) from
);
778 FcStrBufData (buf
, str
, p
- str
);
779 i
= strchr (from
, *p
) - from
;
780 FcStrBufChar (buf
, i
< to_len
? to
[i
] : repeat
);
785 FcStrBufString (buf
, str
);
795 interpret_convert (FcFormatContext
*c
,
802 FcChar8 buf_static
[8192];
805 if (!expect_char (c
, '|') ||
809 /* prepare the buffer */
810 FcStrBufChar (buf
, '\0');
813 str
= buf
->buf
+ start
;
816 /* try simple converters first */
818 #define CONVERTER(name, func) \
819 else if (0 == strcmp ((const char *) c->word, name))\
820 do { new_str = func (str); ret = FcTrue; } while (0)
821 CONVERTER ("downcase", FcStrDowncase
);
822 CONVERTER ("basename", FcStrBasename
);
823 CONVERTER ("dirname", FcStrDirname
);
832 FcStrBufString (buf
, new_str
);
840 FcStrBufInit (&new_buf
, buf_static
, sizeof (buf_static
));
842 /* now try our custom converters */
844 #define CONVERTER(name, func) \
845 else if (0 == strcmp ((const char *) c->word, name))\
846 ret = func (c, str, &new_buf)
847 CONVERTER ("cescape", cescape
);
848 CONVERTER ("shescape", shescape
);
849 CONVERTER ("xmlescape", xmlescape
);
850 CONVERTER ("delete", delete_chars
);
851 CONVERTER ("escape", escape_chars
);
852 CONVERTER ("translate", translate_chars
);
859 FcStrBufChar (&new_buf
, '\0');
860 FcStrBufString (buf
, new_buf
.buf
);
863 message ("unknown converter \"%s\"",
866 FcStrBufDestroy (&new_buf
);
872 maybe_interpret_converts (FcFormatContext
*c
,
876 while (*c
->format
== '|')
877 if (!interpret_convert (c
, buf
, start
))
884 align_to_width (FcStrBuf
*buf
,
893 len
= buf
->len
- start
;
897 while (len
++ < -width
)
898 FcStrBufChar (buf
, ' ');
900 else if (len
< width
)
905 while (len
++ < width
)
906 FcStrBufChar (buf
, ' ');
910 memmove (buf
->buf
+ buf
->len
- len
,
911 buf
->buf
+ buf
->len
- width
,
913 memset (buf
->buf
+ buf
->len
- width
,
921 interpret_percent (FcFormatContext
*c
,
928 if (!expect_char (c
, '%'))
931 if (consume_char (c
, '%')) /* "%%" */
933 FcStrBufChar (buf
, '%');
937 /* parse an optional width specifier */
938 width
= strtol ((const char *) c
->format
, (char **) &c
->format
, 10);
940 if (!expect_char (c
, '{'))
945 switch (*c
->format
) {
946 case '=': ret
= interpret_builtin (c
, pat
, buf
); break;
947 case '{': ret
= interpret_subexpr (c
, pat
, buf
); break;
948 case '+': ret
= interpret_filter (c
, pat
, buf
); break;
949 case '-': ret
= interpret_delete (c
, pat
, buf
); break;
950 case '?': ret
= interpret_cond (c
, pat
, buf
); break;
951 case '#': ret
= interpret_count (c
, pat
, buf
); break;
952 default: ret
= interpret_simple (c
, pat
, buf
); break;
956 maybe_interpret_converts (c
, buf
, start
) &&
957 align_to_width (buf
, start
, width
) &&
958 expect_char (c
, '}');
962 interpret_expr (FcFormatContext
*c
,
967 while (*c
->format
&& *c
->format
!= term
)
972 c
->format
++; /* skip over '\\' */
974 FcStrBufChar (buf
, escaped_char (*c
->format
++));
977 if (!interpret_percent (c
, pat
, buf
))
981 FcStrBufChar (buf
, *c
->format
++);
987 FcPatternFormatToBuf (FcPattern
*pat
,
988 const FcChar8
*format
,
992 FcChar8 word_static
[1024];
995 if (!FcFormatContextInit (&c
, format
, word_static
, sizeof (word_static
)))
998 ret
= interpret_expr (&c
, pat
, buf
, '\0');
1000 FcFormatContextDone (&c
);
1006 FcPatternFormat (FcPattern
*pat
,
1007 const FcChar8
*format
)
1010 FcChar8 buf_static
[8192 - 1024];
1013 FcStrBufInit (&buf
, buf_static
, sizeof (buf_static
));
1015 ret
= FcPatternFormatToBuf (pat
, format
, &buf
);
1018 return FcStrBufDone (&buf
);
1021 FcStrBufDestroy (&buf
);
1026 #define __fcformat__
1027 #include "fcaliastail.h"