(root)/
fontconfig-2.14.2/
src/
fcformat.c
       1  /*
       2   * Copyright © 2008,2009 Red Hat, Inc.
       3   *
       4   * Red Hat Author(s): Behdad Esfahbod
       5   *
       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 the author(s) not be used in
      11   * advertising or publicity pertaining to distribution of the software without
      12   * specific, written prior permission.  The authors make no
      13   * representations about the suitability of this software for any purpose.  It
      14   * is provided "as is" without express or implied warranty.
      15   *
      16   * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
      17   * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
      18   * EVENT SHALL THE AUTHOR(S) 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.
      23   */
      24  
      25  #include "fcint.h"
      26  #include <stdlib.h>
      27  #include <string.h>
      28  #include <stdarg.h>
      29  
      30  
      31  /* The language is documented in doc/fcformat.fncs
      32   * These are the features implemented:
      33   *
      34   * simple	%{elt}
      35   * width	%width{elt}
      36   * index	%{elt[idx]}
      37   * name=	%{elt=}
      38   * :name=	%{:elt}
      39   * default	%{elt:-word}
      40   * count	%{#elt}
      41   * subexpr	%{{expr}}
      42   * filter-out	%{-elt1,elt2,elt3{expr}}
      43   * filter-in	%{+elt1,elt2,elt3{expr}}
      44   * conditional	%{?elt1,elt2,!elt3{}{}}
      45   * enumerate	%{[]elt1,elt2{expr}}
      46   * langset	langset enumeration using the same syntax
      47   * builtin	%{=blt}
      48   * convert	%{elt|conv1|conv2|conv3}
      49   *
      50   * converters:
      51   * basename	FcStrBasename
      52   * dirname	FcStrDirname
      53   * downcase	FcStrDowncase
      54   * shescape
      55   * cescape
      56   * xmlescape
      57   * delete	delete chars
      58   * escape	escape chars
      59   * translate	translate chars
      60   *
      61   * builtins:
      62   * unparse	FcNameUnparse
      63   * fcmatch	fc-match default
      64   * fclist	fc-list default
      65   * fccat	fc-cat default
      66   * pkgkit	PackageKit package tag format
      67   *
      68   *
      69   * Some ideas for future syntax extensions:
      70   *
      71   * - verbose builtin that is like FcPatternPrint
      72   * - allow indexing subexprs using '%{[idx]elt1,elt2{subexpr}}'
      73   * - allow indexing in +, -, ? filtering?
      74   * - conditional/filtering/deletion on binding (using '(w)'/'(s)'/'(=)' notation)
      75   */
      76  
      77  
      78  #define FCCAT_FORMAT	"\"%{file|basename|cescape}\" %{index} \"%{-file{%{=unparse|cescape}}}\""
      79  #define FCMATCH_FORMAT	"%{file:-<unknown filename>|basename}: \"%{family[0]:-<unknown family>}\" \"%{style[0]:-<unknown style>}\""
      80  #define FCLIST_FORMAT	"%{?file{%{file}: }}%{-file{%{=unparse}}}"
      81  #define PKGKIT_FORMAT	"%{[]family{font(%{family|downcase|delete( )})\n}}%{[]lang{font(:lang=%{lang|downcase|translate(_,-)})\n}}"
      82  
      83  
      84  static void
      85  message (const char *fmt, ...)
      86  {
      87      va_list	args;
      88      va_start (args, fmt);
      89      fprintf (stderr, "Fontconfig: Pattern format error: ");
      90      vfprintf (stderr, fmt, args);
      91      fprintf (stderr, ".\n");
      92      va_end (args);
      93  }
      94  
      95  
      96  typedef struct _FcFormatContext
      97  {
      98      const FcChar8 *format_orig;
      99      const FcChar8 *format;
     100      int            format_len;
     101      FcChar8       *word;
     102      FcBool         word_allocated;
     103  } FcFormatContext;
     104  
     105  static FcBool
     106  FcFormatContextInit (FcFormatContext *c,
     107  		     const FcChar8   *format,
     108  		     FcChar8         *scratch,
     109  		     int              scratch_len)
     110  {
     111      c->format_orig = c->format = format;
     112      c->format_len = strlen ((const char *) format);
     113  
     114      if (c->format_len < scratch_len)
     115      {
     116  	c->word = scratch;
     117  	c->word_allocated = FcFalse;
     118      }
     119      else
     120      {
     121  	c->word = malloc (c->format_len + 1);
     122  	c->word_allocated = FcTrue;
     123      }
     124  
     125      return c->word != NULL;
     126  }
     127  
     128  static void
     129  FcFormatContextDone (FcFormatContext *c)
     130  {
     131      if (c && c->word_allocated)
     132      {
     133  	free (c->word);
     134      }
     135  }
     136  
     137  static FcBool
     138  consume_char (FcFormatContext *c,
     139  	      FcChar8          term)
     140  {
     141      if (*c->format != term)
     142  	return FcFalse;
     143  
     144      c->format++;
     145      return FcTrue;
     146  }
     147  
     148  static FcBool
     149  expect_char (FcFormatContext *c,
     150  	      FcChar8          term)
     151  {
     152      FcBool res = consume_char (c, term);
     153      if (!res)
     154      {
     155  	if (c->format == c->format_orig + c->format_len)
     156  	    message ("format ended while expecting '%c'",
     157  		     term);
     158  	else
     159  	    message ("expected '%c' at %d",
     160  		     term, c->format - c->format_orig + 1);
     161      }
     162      return res;
     163  }
     164  
     165  static FcBool
     166  FcCharIsPunct (const FcChar8 c)
     167  {
     168      if (c < '0')
     169  	return FcTrue;
     170      if (c <= '9')
     171  	return FcFalse;
     172      if (c < 'A')
     173  	return FcTrue;
     174      if (c <= 'Z')
     175  	return FcFalse;
     176      if (c < 'a')
     177  	return FcTrue;
     178      if (c <= 'z')
     179  	return FcFalse;
     180      if (c <= '~')
     181  	return FcTrue;
     182      return FcFalse;
     183  }
     184  
     185  static char escaped_char(const char ch)
     186  {
     187      switch (ch) {
     188      case 'a':   return '\a';
     189      case 'b':   return '\b';
     190      case 'f':   return '\f';
     191      case 'n':   return '\n';
     192      case 'r':   return '\r';
     193      case 't':   return '\t';
     194      case 'v':   return '\v';
     195      default:    return ch;
     196      }
     197  }
     198  
     199  static FcBool
     200  read_word (FcFormatContext *c)
     201  {
     202      FcChar8 *p;
     203  
     204      p = c->word;
     205  
     206      while (*c->format)
     207      {
     208  	if (*c->format == '\\')
     209  	{
     210  	    c->format++;
     211  	    if (*c->format)
     212  	      *p++ = escaped_char (*c->format++);
     213  	    continue;
     214  	}
     215  	else if (FcCharIsPunct (*c->format))
     216  	    break;
     217  
     218  	*p++ = *c->format++;
     219      }
     220      *p = '\0';
     221  
     222      if (p == c->word)
     223      {
     224  	message ("expected identifier at %d",
     225  		 c->format - c->format_orig + 1);
     226  	return FcFalse;
     227      }
     228  
     229      return FcTrue;
     230  }
     231  
     232  static FcBool
     233  read_chars (FcFormatContext *c,
     234  	    FcChar8          term)
     235  {
     236      FcChar8 *p;
     237  
     238      p = c->word;
     239  
     240      while (*c->format && *c->format != '}' && *c->format != term)
     241      {
     242  	if (*c->format == '\\')
     243  	{
     244  	    c->format++;
     245  	    if (*c->format)
     246  	      *p++ = escaped_char (*c->format++);
     247  	    continue;
     248  	}
     249  
     250  	*p++ = *c->format++;
     251      }
     252      *p = '\0';
     253  
     254      if (p == c->word)
     255      {
     256  	message ("expected character data at %d",
     257  		 c->format - c->format_orig + 1);
     258  	return FcFalse;
     259      }
     260  
     261      return FcTrue;
     262  }
     263  
     264  static FcBool
     265  FcPatternFormatToBuf (FcPattern     *pat,
     266  		      const FcChar8 *format,
     267  		      FcStrBuf      *buf);
     268  
     269  static FcBool
     270  interpret_builtin (FcFormatContext *c,
     271  		   FcPattern       *pat,
     272  		   FcStrBuf        *buf)
     273  {
     274      FcChar8       *new_str;
     275      FcBool         ret;
     276  
     277      if (!expect_char (c, '=') ||
     278  	!read_word (c))
     279  	return FcFalse;
     280  
     281      /* try simple builtins first */
     282      if (0) { }
     283  #define BUILTIN(name, func) \
     284      else if (0 == strcmp ((const char *) c->word, name))\
     285  	do { new_str = func (pat); ret = FcTrue; } while (0)
     286      BUILTIN ("unparse",  FcNameUnparse);
     287   /* BUILTIN ("verbose",  FcPatternPrint); XXX */
     288  #undef BUILTIN
     289      else
     290  	ret = FcFalse;
     291  
     292      if (ret)
     293      {
     294  	if (new_str)
     295  	{
     296  	    FcStrBufString (buf, new_str);
     297  	    FcStrFree (new_str);
     298  	    return FcTrue;
     299  	}
     300  	else
     301  	    return FcFalse;
     302      }
     303  
     304      /* now try our custom formats */
     305      if (0) { }
     306  #define BUILTIN(name, format) \
     307      else if (0 == strcmp ((const char *) c->word, name))\
     308  	ret = FcPatternFormatToBuf (pat, (const FcChar8 *) format, buf)
     309      BUILTIN ("fccat",    FCCAT_FORMAT);
     310      BUILTIN ("fcmatch",  FCMATCH_FORMAT);
     311      BUILTIN ("fclist",   FCLIST_FORMAT);
     312      BUILTIN ("pkgkit",   PKGKIT_FORMAT);
     313  #undef BUILTIN
     314      else
     315  	ret = FcFalse;
     316  
     317      if (!ret)
     318  	message ("unknown builtin \"%s\"",
     319  		 c->word);
     320  
     321      return ret;
     322  }
     323  
     324  static FcBool
     325  interpret_expr (FcFormatContext *c,
     326  		FcPattern       *pat,
     327  		FcStrBuf        *buf,
     328  		FcChar8          term);
     329  
     330  static FcBool
     331  interpret_subexpr (FcFormatContext *c,
     332  		   FcPattern       *pat,
     333  		   FcStrBuf        *buf)
     334  {
     335      return expect_char (c, '{') &&
     336  	   interpret_expr (c, pat, buf, '}') &&
     337  	   expect_char (c, '}');
     338  }
     339  
     340  static FcBool
     341  maybe_interpret_subexpr (FcFormatContext *c,
     342  			 FcPattern       *pat,
     343  			 FcStrBuf        *buf)
     344  {
     345      return (*c->format == '{') ?
     346  	   interpret_subexpr (c, pat, buf) :
     347  	   FcTrue;
     348  }
     349  
     350  static FcBool
     351  skip_subexpr (FcFormatContext *c);
     352  
     353  static FcBool
     354  skip_percent (FcFormatContext *c)
     355  {
     356      if (!expect_char (c, '%'))
     357  	return FcFalse;
     358  
     359      /* skip an optional width specifier */
     360      if (strtol ((const char *) c->format, (char **) &c->format, 10))
     361          {/* don't care */}
     362  
     363      if (!expect_char (c, '{'))
     364  	return FcFalse;
     365  
     366      while(*c->format && *c->format != '}')
     367      {
     368  	switch (*c->format)
     369  	{
     370  	case '\\':
     371  	    c->format++; /* skip over '\\' */
     372  	    if (*c->format)
     373  		c->format++;
     374  	    continue;
     375  	case '{':
     376  	    if (!skip_subexpr (c))
     377  		return FcFalse;
     378  	    continue;
     379  	}
     380  	c->format++;
     381      }
     382  
     383      return expect_char (c, '}');
     384  }
     385  
     386  static FcBool
     387  skip_expr (FcFormatContext *c)
     388  {
     389      while(*c->format && *c->format != '}')
     390      {
     391  	switch (*c->format)
     392  	{
     393  	case '\\':
     394  	    c->format++; /* skip over '\\' */
     395  	    if (*c->format)
     396  		c->format++;
     397  	    continue;
     398  	case '%':
     399  	    if (!skip_percent (c))
     400  		return FcFalse;
     401  	    continue;
     402  	}
     403  	c->format++;
     404      }
     405  
     406      return FcTrue;
     407  }
     408  
     409  static FcBool
     410  skip_subexpr (FcFormatContext *c)
     411  {
     412      return expect_char (c, '{') &&
     413  	   skip_expr (c) &&
     414  	   expect_char (c, '}');
     415  }
     416  
     417  static FcBool
     418  maybe_skip_subexpr (FcFormatContext *c)
     419  {
     420      return (*c->format == '{') ?
     421  	   skip_subexpr (c) :
     422  	   FcTrue;
     423  }
     424  
     425  static FcBool
     426  interpret_filter_in (FcFormatContext *c,
     427  		     FcPattern       *pat,
     428  		     FcStrBuf        *buf)
     429  {
     430      FcObjectSet  *os;
     431      FcPattern    *subpat;
     432  
     433      if (!expect_char (c, '+'))
     434  	return FcFalse;
     435  
     436      os = FcObjectSetCreate ();
     437      if (!os)
     438  	return FcFalse;
     439  
     440      do
     441      {
     442  	/* XXX binding */
     443  	if (!read_word (c) ||
     444  	    !FcObjectSetAdd (os, (const char *) c->word))
     445  	{
     446  	    FcObjectSetDestroy (os);
     447  	    return FcFalse;
     448  	}
     449      }
     450      while (consume_char (c, ','));
     451  
     452      subpat = FcPatternFilter (pat, os);
     453      FcObjectSetDestroy (os);
     454  
     455      if (!subpat ||
     456  	!interpret_subexpr (c, subpat, buf))
     457  	return FcFalse;
     458  
     459      FcPatternDestroy (subpat);
     460      return FcTrue;
     461  }
     462  
     463  static FcBool
     464  interpret_filter_out (FcFormatContext *c,
     465  		      FcPattern       *pat,
     466  		      FcStrBuf        *buf)
     467  {
     468      FcPattern    *subpat;
     469  
     470      if (!expect_char (c, '-'))
     471  	return FcFalse;
     472  
     473      subpat = FcPatternDuplicate (pat);
     474      if (!subpat)
     475  	return FcFalse;
     476  
     477      do
     478      {
     479  	if (!read_word (c))
     480  	{
     481  	    FcPatternDestroy (subpat);
     482  	    return FcFalse;
     483  	}
     484  
     485  	FcPatternDel (subpat, (const char *) c->word);
     486      }
     487      while (consume_char (c, ','));
     488  
     489      if (!interpret_subexpr (c, subpat, buf))
     490  	return FcFalse;
     491  
     492      FcPatternDestroy (subpat);
     493      return FcTrue;
     494  }
     495  
     496  static FcBool
     497  interpret_cond (FcFormatContext *c,
     498  		FcPattern       *pat,
     499  		FcStrBuf        *buf)
     500  {
     501      FcBool pass;
     502  
     503      if (!expect_char (c, '?'))
     504  	return FcFalse;
     505  
     506      pass = FcTrue;
     507  
     508      do
     509      {
     510  	FcBool negate;
     511  	FcValue v;
     512  
     513  	negate = consume_char (c, '!');
     514  
     515  	if (!read_word (c))
     516  	    return FcFalse;
     517  
     518  	pass = pass &&
     519  	       (negate ^
     520  		(FcResultMatch ==
     521  		 FcPatternGet (pat, (const char *) c->word, 0, &v)));
     522      }
     523      while (consume_char (c, ','));
     524  
     525      if (pass)
     526      {
     527  	if (!interpret_subexpr  (c, pat, buf) ||
     528  	    !maybe_skip_subexpr (c))
     529  	    return FcFalse;
     530      }
     531      else
     532      {
     533  	if (!skip_subexpr (c) ||
     534  	    !maybe_interpret_subexpr  (c, pat, buf))
     535  	    return FcFalse;
     536      }
     537  
     538      return FcTrue;
     539  }
     540  
     541  static FcBool
     542  interpret_count (FcFormatContext *c,
     543  		 FcPattern       *pat,
     544  		 FcStrBuf        *buf)
     545  {
     546      int count;
     547      FcPatternIter iter;
     548      FcChar8 buf_static[64];
     549  
     550      if (!expect_char (c, '#'))
     551  	return FcFalse;
     552  
     553      if (!read_word (c))
     554  	return FcFalse;
     555  
     556      count = 0;
     557      if (FcPatternFindIter (pat, &iter, (const char *) c->word))
     558      {
     559  	count = FcPatternIterValueCount (pat, &iter);
     560      }
     561  
     562      snprintf ((char *) buf_static, sizeof (buf_static), "%d", count);
     563      FcStrBufString (buf, buf_static);
     564  
     565      return FcTrue;
     566  }
     567  
     568  static FcBool
     569  interpret_enumerate (FcFormatContext *c,
     570  		     FcPattern       *pat,
     571  		     FcStrBuf        *buf)
     572  {
     573      FcObjectSet   *os;
     574      FcPattern     *subpat;
     575      const FcChar8 *format_save;
     576      int            idx;
     577      FcBool         ret, done;
     578      FcStrList      *lang_strs;
     579  
     580      if (!expect_char (c, '[') ||
     581  	!expect_char (c, ']'))
     582  	return FcFalse;
     583  
     584      os = FcObjectSetCreate ();
     585      if (!os)
     586  	return FcFalse;
     587  
     588      ret = FcTrue;
     589  
     590      do
     591      {
     592  	if (!read_word (c) ||
     593  	    !FcObjectSetAdd (os, (const char *) c->word))
     594  	{
     595  	    FcObjectSetDestroy (os);
     596  	    return FcFalse;
     597  	}
     598      }
     599      while (consume_char (c, ','));
     600  
     601      /* If we have one element and it's of type FcLangSet, we want
     602       * to enumerate the languages in it. */
     603      lang_strs = NULL;
     604      if (os->nobject == 1)
     605      {
     606  	FcLangSet *langset;
     607  	if (FcResultMatch ==
     608  	    FcPatternGetLangSet (pat, os->objects[0], 0, &langset))
     609  	{
     610  	    FcStrSet *ss;
     611  	    if (!(ss = FcLangSetGetLangs (langset)) ||
     612  		!(lang_strs = FcStrListCreate (ss)))
     613  		goto bail0;
     614  	}
     615      }
     616  
     617      subpat = FcPatternDuplicate (pat);
     618      if (!subpat)
     619  	goto bail0;
     620  
     621      format_save = c->format;
     622      idx = 0;
     623      do
     624      {
     625  	int i;
     626  
     627  	done = FcTrue;
     628  
     629  	if (lang_strs)
     630  	{
     631  	    FcChar8 *lang;
     632  
     633  	    FcPatternDel (subpat, os->objects[0]);
     634  	    if ((lang = FcStrListNext (lang_strs)))
     635  	    {
     636  		/* XXX binding? */
     637  		FcPatternAddString (subpat, os->objects[0], lang);
     638  		done = FcFalse;
     639  	    }
     640  	}
     641  	else
     642  	{
     643  	    for (i = 0; i < os->nobject; i++)
     644  	    {
     645  		FcValue v;
     646  
     647  		/* XXX this can be optimized by accessing valuelist linked lists
     648  		 * directly and remembering where we were.  Most (all) value lists
     649  		 * in normal uses are pretty short though (language tags are
     650  		 * stored as a LangSet, not separate values.). */
     651  		FcPatternDel (subpat, os->objects[i]);
     652  		if (FcResultMatch ==
     653  		    FcPatternGet (pat, os->objects[i], idx, &v))
     654  		{
     655  		    /* XXX binding */
     656  		    FcPatternAdd (subpat, os->objects[i], v, FcFalse);
     657  		    done = FcFalse;
     658  		}
     659  	    }
     660  	}
     661  
     662  	if (!done)
     663  	{
     664  	    c->format = format_save;
     665  	    ret = interpret_subexpr (c, subpat, buf);
     666  	    if (!ret)
     667  		goto bail;
     668  	}
     669  
     670  	idx++;
     671      } while (!done);
     672  
     673      if (c->format == format_save)
     674  	skip_subexpr (c);
     675  
     676  bail:
     677      FcPatternDestroy (subpat);
     678  bail0:
     679      if (lang_strs)
     680  	FcStrListDone (lang_strs);
     681      FcObjectSetDestroy (os);
     682  
     683      return ret;
     684  }
     685  
     686  static FcBool
     687  interpret_simple (FcFormatContext *c,
     688  		  FcPattern       *pat,
     689  		  FcStrBuf        *buf)
     690  {
     691      FcPatternIter iter;
     692      FcBool        add_colon = FcFalse;
     693      FcBool        add_elt_name = FcFalse;
     694      int           idx;
     695      FcChar8      *else_string;
     696  
     697      if (consume_char (c, ':'))
     698  	add_colon = FcTrue;
     699  
     700      if (!read_word (c))
     701  	return FcFalse;
     702  
     703      idx = -1;
     704      if (consume_char (c, '['))
     705      {
     706  	idx = strtol ((const char *) c->format, (char **) &c->format, 10);
     707  	if (idx < 0)
     708  	{
     709  	    message ("expected non-negative number at %d",
     710  		     c->format-1 - c->format_orig + 1);
     711  	    return FcFalse;
     712  	}
     713  	if (!expect_char (c, ']'))
     714  	    return FcFalse;
     715      }
     716  
     717      if (consume_char (c, '='))
     718  	add_elt_name = FcTrue;
     719  
     720      /* modifiers */
     721      else_string = NULL;
     722      if (consume_char (c, ':'))
     723      {
     724  	FcChar8 *orig;
     725  	/* divert the c->word for now */
     726  	orig = c->word;
     727  	c->word = c->word + strlen ((const char *) c->word) + 1;
     728  	/* for now we just support 'default value' */
     729  	if (!expect_char (c, '-') ||
     730  	    !read_chars (c, '|'))
     731  	{
     732  	    c->word = orig;
     733  	    return FcFalse;
     734  	}
     735  	else_string = c->word;
     736  	c->word = orig;
     737      }
     738  
     739      if (FcPatternFindIter (pat, &iter, (const char *) c->word) || else_string)
     740      {
     741  	FcValueListPtr l = NULL;
     742  
     743  	if (add_colon)
     744  	    FcStrBufChar (buf, ':');
     745  	if (add_elt_name)
     746  	{
     747  	    FcStrBufString (buf, c->word);
     748  	    FcStrBufChar (buf, '=');
     749  	}
     750  
     751  	l = FcPatternIterGetValues (pat, &iter);
     752  
     753  	if (idx != -1)
     754  	{
     755  	    while (l && idx > 0)
     756  	    {
     757  		l = FcValueListNext(l);
     758  		idx--;
     759  	    }
     760  	    if (l && idx == 0)
     761  	    {
     762  		if (!FcNameUnparseValue (buf, &l->value, NULL))
     763  		    return FcFalse;
     764  	    }
     765  	    else goto notfound;
     766          }
     767  	else if (l)
     768  	{
     769  	    FcNameUnparseValueList (buf, l, NULL);
     770  	}
     771  	else
     772  	{
     773      notfound:
     774  	    if (else_string)
     775  		FcStrBufString (buf, else_string);
     776  	}
     777      }
     778  
     779      return FcTrue;
     780  }
     781  
     782  static FcBool
     783  cescape (FcFormatContext *c FC_UNUSED,
     784  	 const FcChar8   *str,
     785  	 FcStrBuf        *buf)
     786  {
     787      /* XXX escape \n etc? */
     788  
     789      while(*str)
     790      {
     791  	switch (*str)
     792  	{
     793  	case '\\':
     794  	case '"':
     795  	    FcStrBufChar (buf, '\\');
     796  	    break;
     797  	}
     798  	FcStrBufChar (buf, *str++);
     799      }
     800      return FcTrue;
     801  }
     802  
     803  static FcBool
     804  shescape (FcFormatContext *c FC_UNUSED,
     805  	  const FcChar8   *str,
     806  	  FcStrBuf        *buf)
     807  {
     808      FcStrBufChar (buf, '\'');
     809      while(*str)
     810      {
     811  	if (*str == '\'')
     812  	    FcStrBufString (buf, (const FcChar8 *) "'\\''");
     813  	else
     814  	    FcStrBufChar (buf, *str);
     815  	str++;
     816      }
     817      FcStrBufChar (buf, '\'');
     818      return FcTrue;
     819  }
     820  
     821  static FcBool
     822  xmlescape (FcFormatContext *c FC_UNUSED,
     823  	   const FcChar8   *str,
     824  	   FcStrBuf        *buf)
     825  {
     826      /* XXX escape \n etc? */
     827  
     828      while(*str)
     829      {
     830  	switch (*str)
     831  	{
     832  	case '&': FcStrBufString (buf, (const FcChar8 *) "&amp;"); break;
     833  	case '<': FcStrBufString (buf, (const FcChar8 *) "&lt;");  break;
     834  	case '>': FcStrBufString (buf, (const FcChar8 *) "&gt;");  break;
     835  	default:  FcStrBufChar   (buf, *str);                      break;
     836  	}
     837  	str++;
     838      }
     839      return FcTrue;
     840  }
     841  
     842  static FcBool
     843  delete_chars (FcFormatContext *c,
     844  	      const FcChar8   *str,
     845  	      FcStrBuf        *buf)
     846  {
     847      /* XXX not UTF-8 aware */
     848  
     849      if (!expect_char (c, '(') ||
     850  	!read_chars (c, ')') ||
     851  	!expect_char (c, ')'))
     852  	return FcFalse;
     853  
     854      while(*str)
     855      {
     856  	FcChar8 *p;
     857  
     858  	p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
     859  	if (p)
     860  	{
     861  	    FcStrBufData (buf, str, p - str);
     862  	    str = p + 1;
     863  	}
     864  	else
     865  	{
     866  	    FcStrBufString (buf, str);
     867  	    break;
     868  	}
     869  
     870      }
     871  
     872      return FcTrue;
     873  }
     874  
     875  static FcBool
     876  escape_chars (FcFormatContext *c,
     877  	      const FcChar8   *str,
     878  	      FcStrBuf        *buf)
     879  {
     880      /* XXX not UTF-8 aware */
     881  
     882      if (!expect_char (c, '(') ||
     883  	!read_chars (c, ')') ||
     884  	!expect_char (c, ')'))
     885  	return FcFalse;
     886  
     887      while(*str)
     888      {
     889  	FcChar8 *p;
     890  
     891  	p = (FcChar8 *) strpbrk ((const char *) str, (const char *) c->word);
     892  	if (p)
     893  	{
     894  	    FcStrBufData (buf, str, p - str);
     895  	    FcStrBufChar (buf, c->word[0]);
     896  	    FcStrBufChar (buf, *p);
     897  	    str = p + 1;
     898  	}
     899  	else
     900  	{
     901  	    FcStrBufString (buf, str);
     902  	    break;
     903  	}
     904  
     905      }
     906  
     907      return FcTrue;
     908  }
     909  
     910  static FcBool
     911  translate_chars (FcFormatContext *c,
     912  		 const FcChar8   *str,
     913  		 FcStrBuf        *buf)
     914  {
     915      char *from, *to, repeat;
     916      int from_len, to_len;
     917  
     918      /* XXX not UTF-8 aware */
     919  
     920      if (!expect_char (c, '(') ||
     921  	!read_chars (c, ',') ||
     922  	!expect_char (c, ','))
     923  	return FcFalse;
     924  
     925      from = (char *) c->word;
     926      from_len = strlen (from);
     927      to = from + from_len + 1;
     928  
     929      /* hack: we temporarily divert c->word */
     930      c->word = (FcChar8 *) to;
     931      if (!read_chars (c, ')'))
     932      {
     933        c->word = (FcChar8 *) from;
     934        return FcFalse;
     935      }
     936      c->word = (FcChar8 *) from;
     937  
     938      to_len = strlen (to);
     939      repeat = to[to_len - 1];
     940  
     941      if (!expect_char (c, ')'))
     942  	return FcFalse;
     943  
     944      while(*str)
     945      {
     946  	FcChar8 *p;
     947  
     948  	p = (FcChar8 *) strpbrk ((const char *) str, (const char *) from);
     949  	if (p)
     950  	{
     951  	    int i;
     952  	    FcStrBufData (buf, str, p - str);
     953  	    i = strchr (from, *p) - from;
     954  	    FcStrBufChar (buf, i < to_len ? to[i] : repeat);
     955  	    str = p + 1;
     956  	}
     957  	else
     958  	{
     959  	    FcStrBufString (buf, str);
     960  	    break;
     961  	}
     962  
     963      }
     964  
     965      return FcTrue;
     966  }
     967  
     968  static FcBool
     969  interpret_convert (FcFormatContext *c,
     970  		   FcStrBuf        *buf,
     971  		   int              start)
     972  {
     973      const FcChar8 *str;
     974      FcChar8       *new_str;
     975      FcStrBuf       new_buf;
     976      FcChar8        buf_static[8192];
     977      FcBool         ret;
     978  
     979      if (!expect_char (c, '|') ||
     980  	!read_word (c))
     981  	return FcFalse;
     982  
     983      /* prepare the buffer */
     984      FcStrBufChar (buf, '\0');
     985      if (buf->failed)
     986  	return FcFalse;
     987      str = buf->buf + start;
     988      buf->len = start;
     989  
     990      /* try simple converters first */
     991      if (0) { }
     992  #define CONVERTER(name, func) \
     993      else if (0 == strcmp ((const char *) c->word, name))\
     994  	do { new_str = func (str); ret = FcTrue; } while (0)
     995      CONVERTER  ("downcase",  FcStrDowncase);
     996      CONVERTER  ("basename",  FcStrBasename);
     997      CONVERTER  ("dirname",   FcStrDirname);
     998  #undef CONVERTER
     999      else
    1000  	ret = FcFalse;
    1001  
    1002      if (ret)
    1003      {
    1004  	if (new_str)
    1005  	{
    1006  	    FcStrBufString (buf, new_str);
    1007  	    FcStrFree (new_str);
    1008  	    return FcTrue;
    1009  	}
    1010  	else
    1011  	    return FcFalse;
    1012      }
    1013  
    1014      FcStrBufInit (&new_buf, buf_static, sizeof (buf_static));
    1015  
    1016      /* now try our custom converters */
    1017      if (0) { }
    1018  #define CONVERTER(name, func) \
    1019      else if (0 == strcmp ((const char *) c->word, name))\
    1020  	ret = func (c, str, &new_buf)
    1021      CONVERTER ("cescape",   cescape);
    1022      CONVERTER ("shescape",  shescape);
    1023      CONVERTER ("xmlescape", xmlescape);
    1024      CONVERTER ("delete",    delete_chars);
    1025      CONVERTER ("escape",    escape_chars);
    1026      CONVERTER ("translate", translate_chars);
    1027  #undef CONVERTER
    1028      else
    1029  	ret = FcFalse;
    1030  
    1031      if (ret)
    1032      {
    1033  	FcStrBufChar (&new_buf, '\0');
    1034  	FcStrBufString (buf, new_buf.buf);
    1035      }
    1036      else
    1037  	message ("unknown converter \"%s\"",
    1038  		 c->word);
    1039  
    1040      FcStrBufDestroy (&new_buf);
    1041  
    1042      return ret;
    1043  }
    1044  
    1045  static FcBool
    1046  maybe_interpret_converts (FcFormatContext *c,
    1047  			   FcStrBuf        *buf,
    1048  			   int              start)
    1049  {
    1050      while (*c->format == '|')
    1051  	if (!interpret_convert (c, buf, start))
    1052  	    return FcFalse;
    1053  
    1054      return FcTrue;
    1055  }
    1056  
    1057  static FcBool
    1058  align_to_width (FcStrBuf *buf,
    1059  		int       start,
    1060  		int       width)
    1061  {
    1062      int len;
    1063  
    1064      if (buf->failed)
    1065  	return FcFalse;
    1066  
    1067      len = buf->len - start;
    1068      if (len < -width)
    1069      {
    1070  	/* left align */
    1071  	while (len++ < -width)
    1072  	    FcStrBufChar (buf, ' ');
    1073      }
    1074      else if (len < width)
    1075      {
    1076  	int old_len;
    1077  	old_len = len;
    1078  	/* right align */
    1079  	while (len++ < width)
    1080  	    FcStrBufChar (buf, ' ');
    1081  	if (buf->failed)
    1082  	    return FcFalse;
    1083  	len = old_len;
    1084  	memmove (buf->buf + buf->len - len,
    1085  		 buf->buf + buf->len - width,
    1086  		 len);
    1087  	memset (buf->buf + buf->len - width,
    1088  		' ',
    1089  		width - len);
    1090      }
    1091  
    1092      return !buf->failed;
    1093  }
    1094  static FcBool
    1095  interpret_percent (FcFormatContext *c,
    1096  		   FcPattern       *pat,
    1097  		   FcStrBuf        *buf)
    1098  {
    1099      int width, start;
    1100      FcBool ret;
    1101  
    1102      if (!expect_char (c, '%'))
    1103  	return FcFalse;
    1104  
    1105      if (consume_char (c, '%')) /* "%%" */
    1106      {
    1107  	FcStrBufChar (buf, '%');
    1108  	return FcTrue;
    1109      }
    1110  
    1111      /* parse an optional width specifier */
    1112      width = strtol ((const char *) c->format, (char **) &c->format, 10);
    1113  
    1114      if (!expect_char (c, '{'))
    1115  	return FcFalse;
    1116  
    1117      start = buf->len;
    1118  
    1119      switch (*c->format) {
    1120      case '=': ret = interpret_builtin    (c, pat, buf); break;
    1121      case '{': ret = interpret_subexpr    (c, pat, buf); break;
    1122      case '+': ret = interpret_filter_in  (c, pat, buf); break;
    1123      case '-': ret = interpret_filter_out (c, pat, buf); break;
    1124      case '?': ret = interpret_cond       (c, pat, buf); break;
    1125      case '#': ret = interpret_count      (c, pat, buf); break;
    1126      case '[': ret = interpret_enumerate  (c, pat, buf); break;
    1127      default:  ret = interpret_simple     (c, pat, buf); break;
    1128      }
    1129  
    1130      return ret &&
    1131  	   maybe_interpret_converts (c, buf, start) &&
    1132  	   align_to_width (buf, start, width) &&
    1133  	   expect_char (c, '}');
    1134  }
    1135  
    1136  static FcBool
    1137  interpret_expr (FcFormatContext *c,
    1138  		FcPattern       *pat,
    1139  		FcStrBuf        *buf,
    1140  		FcChar8          term)
    1141  {
    1142      while (*c->format && *c->format != term)
    1143      {
    1144  	switch (*c->format)
    1145  	{
    1146  	case '\\':
    1147  	    c->format++; /* skip over '\\' */
    1148  	    if (*c->format)
    1149  		FcStrBufChar (buf, escaped_char (*c->format++));
    1150  	    continue;
    1151  	case '%':
    1152  	    if (!interpret_percent (c, pat, buf))
    1153  		return FcFalse;
    1154  	    continue;
    1155  	}
    1156  	FcStrBufChar (buf, *c->format++);
    1157      }
    1158      return FcTrue;
    1159  }
    1160  
    1161  static FcBool
    1162  FcPatternFormatToBuf (FcPattern     *pat,
    1163  		      const FcChar8 *format,
    1164  		      FcStrBuf      *buf)
    1165  {
    1166      FcFormatContext c;
    1167      FcChar8         word_static[1024];
    1168      FcBool          ret;
    1169  
    1170      if (!FcFormatContextInit (&c, format, word_static, sizeof (word_static)))
    1171  	return FcFalse;
    1172  
    1173      ret = interpret_expr (&c, pat, buf, '\0');
    1174  
    1175      FcFormatContextDone (&c);
    1176  
    1177      return ret;
    1178  }
    1179  
    1180  FcChar8 *
    1181  FcPatternFormat (FcPattern *pat,
    1182  		 const FcChar8 *format)
    1183  {
    1184      FcStrBuf        buf;
    1185      FcChar8         buf_static[8192 - 1024];
    1186      FcPattern      *alloced = NULL;
    1187      FcBool          ret;
    1188  
    1189      if (!pat)
    1190  	alloced = pat = FcPatternCreate ();
    1191  
    1192      FcStrBufInit (&buf, buf_static, sizeof (buf_static));
    1193  
    1194      ret = FcPatternFormatToBuf (pat, format, &buf);
    1195  
    1196      if (alloced)
    1197        FcPatternDestroy (alloced);
    1198  
    1199      if (ret)
    1200  	return FcStrBufDone (&buf);
    1201      else
    1202      {
    1203  	FcStrBufDestroy (&buf);
    1204  	return NULL;
    1205      }
    1206  }
    1207  
    1208  #define __fcformat__
    1209  #include "fcaliastail.h"
    1210  #undef __fcformat__