File fonts-config of Package fonts-config

#! /usr/bin/perl -w                                       # -*- perl -*-
#
# Copyright (c) 2000-2004 SuSE Linux AG, Nuernberg, Germany.
# All rights reserved.
#
########################################################################
# create a man-page with:
#
#     pod2man --section 1 --center=" " fonts-config | gzip -9 -c > fonts-config.1.gz
#     cp fonts-config.1.gz /usr/share/man/man1/fonts-config.1.gz

=head1 NAME

fonts-config - configures installed X11 fonts.

=head1 SYNOPSIS

fonts-config [B<OPTION>]...

=head1 OPTIONS

=over 4

=item B<-f>, B<--force>

Force the update of all generated files even if it appears to be
unnecessary according to the time stamps.

=item B<-q>, B<--quiet>

Work silently, unless an error occurs.

=item B<-v>, B<--verbose>

Print some progress messages to standard output.

=item B<-d>, B<--debug>

Print a lot of debugging messages to standard output.

=item B<--(no)gs-fontmap>

Generate (or don't generate)  a Fontmap for Ghostscript.

Can only work if the 'ftdump' binary which is in the 'ft2demos'
packages is available.

=item B<--(no)ttcap>

Generate (or don't generate) TTCap entries.  TTCap entries can be used
with the xtt module and with recent versions of the freetype module.

=item B<--(no)ooo>

Generate (or don't generate) font setup for OpenOffice

=item B<--(no)java>

Generate (or don't generate) font setup for Java 1.4.x and Java 1.5.x.

=item B<--bcbwmax> size

Maximum pixel size to use the byte code interpreter with black and
white rendering.

=item B<--version>

Display version and exit.

=item B<-h>, B<--help>

Display a short help message and exit.

=back

=head1 DESCRIPTION

Configures installed X11 fonts. Basically it does the following
things:

=over 4

=item B<call cidfont-x11-config>

cidfont-x11-config is another little perl script which configures
CID-keyed fonts for use with X11, see L<cidfont-x11-config(1)>.

=item B<creates fonts.scale and fonts.dir files>

To find the list of directories currently used for server side fonts,
B</etc/X11/xorg.conf> is parsed and merged with a hardcoded list of
directories. If the font server xfs is running, B</etc/X11/fs/config>
is also parsed and the list of directories found there is merged as
well.

For each directory from this list, the time stamps of the directory,
the B<fonts.scale> file, the B<fonts.dir> file and an extra time stamp
file B<.fonts-config-timestamp> are checked.  If not all the time
stamps are equal or any of these files is missing, the B<fonts.scale>
and B<fonts.dir> files will be updated as follows:

First of all a B<fonts.scale> file is created by calling
B<mkfontscale>.

Then, the entries found in the B<fonts.scale> file are merged with
the entries from all B<fonts.scale.*> files.

B<fonts.scale.*> files may be supplied by rpm-packages or manually
added by the user to override or amend the entries created
automatically by B<mkfontscale>. Entries in a B<fonts.scale.*> file
have higher priority than entries automatically created by
B<mkfontscale>.  All entries generated automatically by B<mkfontscale>
for a certain font file are discarded if any B<fonts.scale.*> file
contains an entry for the same font file.

If the B<xtt> module is configured to load in B</etc/X11/xorg.conf>,
additional entries may be created to make use of the artificial bold
and italic features of B<xtt>. The time stamp of
B</etc/X11/xorg.conf> is not checked, i.e. you have to use
B<fonts-config --force> after editing B</etc/X11/xorg.conf> to switch
between the B<xtt> and B<freetype> modules.

After the final list of entries has been written back to
B<fonts.scale>, B<mkfontdir> is called.

Finally, the time stamps of the directory, B<fonts.scale>,
B<fonts.dir>, and B<.fonts-config-timestamp> are set to the time when
B<fonts-config> started.

If any B<fonts.scale> file in the directory list needed an update and
the option B<--gs-fontmap> is set, a Ghostcript Fontmap is also
generated for all scalable fonts in the directory list and the result
is written to B</usr/share/ghostscript/*/lib/Fontmap.X11-auto>.

=item B<call fc-cache>

creates cache files for fonts to use with client side font rendering
via fontconfig/libXft, for details see L<fc-cache(1)>.
B<fonts.cache-2> cache files are generated in B</var/cache/fontconfig>
for all directories which are configured in B</etc/fonts/fonts.conf>
and all their subdirectories.

=back

Usually B<fonts-config> is called automatically via SuSEconfig
(B<SuSEconfig --module fonts>), which is usually automatically
called by YaST2. But you can also execute B<fonts-config> directly,
which is mainly useful to debug it.

=head1 FILES

=over 4

=item B</etc/sysconfig/fonts-config>

Default values for some command line options of fonts-config are read
from this file if it exists.  The options currently supported in
this file are:

=over 4

=item GENERATE_TTCAP_ENTRIES

can be set to "yes" or "no" and sets the default for the option B<--(no)ttcap>.

=item GENERATE_GHOSTSCRIPT_FONTMAPS

can be set to "yes" or "no" and sets the default for the option B<--(no)gs-fontmap>.

This can only work if the 'ftdump' binary which is in the 'ft2demos'
packages is available.

=item GENERATE_OOO_FONT_SETUP

can be set to "yes" or "no" and sets the default for the option B<--(no)ooo>.

=item GENERATE_JAVA_FONT_SETUP

can be set to "yes" or "no" and sets the default for the option B<--(no)java>.

=item BYTECODE_BW_MAX_PIXEL

can be set to any integer value and sets the default for the option B<--bcbwmax>.

=back

=back

=head1 SEE ALSO

L<fc-cache(1)>, L<cidfont-x11-config(1)>, L<mkfontdir(1)>, L<mkfontscale(1)>

=head1 AUTHOR

Mike FABIAN <I<mfabian@suse.de>>, 2003.

=cut

########################################################################

use utf8;
use English;
use Getopt::Long;
use strict;

my $script_start_time = time();

my $cvs_id = '$Id: fonts-config,v 1.73 2009/01/28 16:23:08 mfabian Exp $';
my $cvs_date = '$Date: 2009/01/28 16:23:08 $';
$cvs_date =~ /^\$[[:alpha:]]+: (.*) \$$/;
my $version = $1;

sub usage {
  print "Usage: fonts-config [option] ...\n";
  print "-f, --force              Force the update of all generated files even\n";
  print "                         if it appears unnecessary according to the time stamps\n";
  print "-q, --quiet              Work silently, unless an error occurs.\n";
  print "-v, --verbose            Print some progress messages to standard output.\n";
  print "-d, --debug              Print a lot of debugging messages to standard output.\n";
  print "    --(no)gs-fontmap     generate a Fontmap file for Ghostscript.\n";
  print "    --(no)ttcap          generate TTCap entries for xtt and recent freetype modules.\n";
  print "    --(no)ooo            generate font setup for OpenOffice.\n";
  print "    --(no)java           generate font setup for Java 1.4.x and Java 1.5.x.\n";
  print "    --bcbwmax  size      maximum pixel size to use the byte code interpreter with\n";
  print "                         black and white rendering.\n";
  print "    --(no)ebitmap        whether to use embedded bitmaps or not.\n";
  print "    --ebitmaplang string the argument is a string which contains a list of colon\n";
  print "                         separated languages, for example \"ja:ko:zh-CN\" \n";
  print "                         which means \"use embedded bitmaps only for\n";
  print "                         fonts supporting Japanese, Korean, or simplified Chinese.\n";
  print "    --version            Display version and exit.\n";
  print "-h, --help               Display this help and exit.\n";
  exit 1;
}

my $OPT_FORCE = 0;
my $OPT_QUIET = 0;
my $OPT_VERBOSE = 1;
my $OPT_DEBUG = 0;
my $OPT_TTCAP = 0;
my $OPT_GS_FONTMAP = 1;
my $OPT_OOO = 1;
my $OPT_JAVA = 1;
my $OPT_BCBWMAX = 0;
my $OPT_EBITMAP = 1;
my $OPT_EBITMAP_LANG = "ja:ko:zh-CN:zh-TW:zh-HK:zh-SG";
my $OPT_VERSION = 0;
my $OPT_HELP = 0;

my %sysconfig_options = (
			 "GENERATE_TTCAP_ENTRIES"         , "OPT_TTCAP",
			 "GENERATE_GHOSTSCRIPT_FONTMAPS"  , "OPT_GS_FONTMAP",
			 "GENERATE_OOO_FONT_SETUP"        , "OPT_OOO",
			 "GENERATE_JAVA_FONT_SETUP"       , "OPT_JAVA",
			 "BYTECODE_BW_MAX_PIXEL"          , "OPT_BCBWMAX",
			 "USE_EMBEDDED_BITMAPS"           , "OPT_EBITMAP",
			 "EMBEDDED_BITMAPS_LANGUAGES"     , "OPT_EBITMAP_LANG"
			);

my $sysconfig_file = "/etc/sysconfig/fonts-config";

sub get_option_defaults_from_sysconfig {
  if (open (SYSCONFIG, "$sysconfig_file")) {
    no strict "vars";
    # print "reading defaults from $sysconfig.\n";
    while (<SYSCONFIG>) {
      chomp ($ARG);
      if ($ARG =~ /([^#]*)#?.*/) { # strip comments
	$ARG = $1;
      }
      eval ("\$$ARG;");
    }
    close (SYSCONFIG);
    for my $i (keys %sysconfig_options) {
      if (eval ("\$$i") =~ /yes/i) {
	eval("\$$sysconfig_options{$i}=1");
      }
      elsif (eval ("\$$i") =~ /no/i) {
	eval("\$$sysconfig_options{$i}=0");
      }
      elsif (eval ("\$$i") =~ /[0-9]+/) { # Type integer
	eval("\$$sysconfig_options{$i}=\$$i");
      }
      elsif (eval ("\$$i") =~ /[-:a-zA-Z0-9]+/) { # Type string
	eval("\$$sysconfig_options{$i}=\$$i");
      }
      else { # this case also occurs when the variable is the empty string!
	eval("\$$sysconfig_options{$i}=0");
      }
    }
    return (0);
  }
  else {
    # print "$sysconfig doesn't exist, using builtin defaults.\n";
    return (1);
  }
}


get_option_defaults_from_sysconfig();

# Process command line options
my %opt;
unless (GetOptions(\%opt,
		   'force|f',       \$OPT_FORCE,
		   'quiet|q',       \$OPT_QUIET,
		   'verbose|v',     \$OPT_VERBOSE,
		   'debug|d',       \$OPT_DEBUG,
                   'gs-fontmap!',   \$OPT_GS_FONTMAP,
		   'ttcap!',        \$OPT_TTCAP,
		   'ooo!',          \$OPT_OOO,
		   'java!',         \$OPT_JAVA,
		   'bcbwmax=i',     \$OPT_BCBWMAX,
		   'ebitmap!',      \$OPT_EBITMAP,
		   'ebitmaplang=s', \$OPT_EBITMAP_LANG,
		   'version',       \$OPT_VERSION,
		   'help|h',        \$OPT_HELP,
		  )) {
  &usage ();
  exit 1;
}

if ($OPT_VERSION) {
  print "fonts-config $version\n";
  exit 0;
}

if ($OPT_HELP) {
  &usage ();
  exit 0;
}

my $VERBOSITY_QUIET   = 0;
my $VERBOSITY_VERBOSE = 1;
my $VERBOSITY_DEBUG   = 256;
my $VERBOSITY         = $VERBOSITY_VERBOSE; # default

if ($OPT_DEBUG) {
  $VERBOSITY = $VERBOSITY_DEBUG;
}
elsif ($OPT_VERBOSE) {
  $VERBOSITY = $VERBOSITY_VERBOSE;
}
elsif ($OPT_QUIET) {
  $VERBOSITY = $VERBOSITY_QUIET;
}

no strict "vars";
no warnings;
if ($VERBOSITY >= $VERBOSITY_DEBUG) {
  print "The following options were read from $sysconfig_file:\n";
  for my $i (keys %sysconfig_options) {
    if (defined eval ("\$$i")) {
      printf "$i=%s\n", eval("\$$i");
    }
  }
  print "--- end of options froom $sysconfig_file ---\n";
}
use warnings;
use strict;

# check if we are started as root
# only one of UID and USER must be set correctly

if ($UID != 0 && $ENV{USER} !~ /root/) {
    print "*** Error: You must be root to start $0\n";
    usage();
    exit 1;
}

# external binaries:
my $fc_cache_bin = search_executable ("/usr/bin/fc-cache", "/usr/X11R6/bin/fc-cache");
my $fc_cache32_bin = search_executable ("/usr/bin/fc-cache32", "/usr/bin/fc-cache-x86");
my $fc_cache64_bin = search_executable ("/usr/bin/fc-cache64");
my $fc_match_bin = search_executable ("/usr/bin/fc-match");
my $cidfont_x11_config_bin = search_executable ("/usr/sbin/cidfont-x11-config");
my $xset_bin = search_executable ("/usr/bin/xset", "/usr/X11R6/bin/xset");
my $xfs_bin = search_executable ("/usr/bin/xfs", "/usr/X11R6/bin/xfs");
my $checkproc_bin = search_executable ("/sbin/checkproc");
my $killproc_bin = search_executable ("/sbin/killproc");
my $XFree86_bin = search_executable ("/usr/bin/Xorg", "/usr/X11R6/bin/Xorg", "/usr/X11R6/bin/XFree86");
my $mkfontscale_bin = search_executable ("/usr/bin/mkfontscale", "/usr/X11R6/bin/mkfontscale");
my $mkfontdir_bin = search_executable ("/usr/bin/mkfontdir", "/usr/X11R6/bin/mkfontdir");
my $ftdump_bin = search_executable ("/usr/bin/ftdump");

my $xtt_module_used = 0;
my $freetype_module_used = 0;

my $have_file_mmagic = 0;
eval { require File::MMagic; };
unless ($EVAL_ERROR) {
  $have_file_mmagic = 1;
  import File::MMagic;
}

my $command = "";

# search whether true CID-keyed fonts are available
# in /usr/share/ghostscript/Resource and if yes make them
# available to X11 as well:
if ($cidfont_x11_config_bin) {
  $command = $cidfont_x11_config_bin;
  if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
    print "Configure X11 to use available CID-keyed fonts ...\n";
  }
  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
    $command .= " --verbose ";
  }
  if ($OPT_FORCE)   {
    $command .= " --force ";
  }
  my_system ($command);
}

my @x11_font_dirs = x11_font_dirs();
my $gs_fontmap_needs_update = 0;

if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
  print "Creating fonts.{scale,dir} files ";
}
for my $dir (@x11_font_dirs) {
  make_fonts_scale_and_fonts_dir ($dir);
  if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
    print "."; # "progress bar"
  }
}
if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
  print "\n";
}

# The following three lines may change files in /etc/fonts, therefore
# they have to be called *before* fc-cache. If anything is
# changed in /etc/fonts after calling fc-cache, fontconfig
# will think that the cache files are out of date again.
#font_dirs_setup ();
hinting_setup ();
embedded_bitmap_setup ();

my $fc_cache_exit_status = 256;

if ($fc_cache_bin) {
  $command = "$fc_cache_bin";
  if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
    print "Creating cache files for fontconfig ";
  }
  if ($OPT_FORCE)   {
    $command .= " --really-force ";
  }
  if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
    $command .= " --verbose ";
  }
  if ($VERBOSITY >= $VERBOSITY_VERBOSE) { # with "progress bar"
    open (FC_CACHE, "$command |");
    select (STDOUT);
    $OUTPUT_AUTOFLUSH = 1;
    while(<FC_CACHE>) {
      print ".";
    }
    print "\n";
    close (FC_CACHE);
  }
  else { # without "progress bar"
    my_system($command);
  }
}
$fc_cache_exit_status = $CHILD_ERROR;
if ($VERBOSITY >= $VERBOSITY_DEBUG) {
  print "exit status of fc-cache: $fc_cache_exit_status\n";
}

my $fc_cache32_exit_status = 256;

if ($fc_cache32_bin) {
  $command = "$fc_cache32_bin";
  if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
    print "Creating 32bit cache files for fontconfig ";
  }
  if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
    $command .= " --verbose ";
  }
  if ($VERBOSITY >= $VERBOSITY_VERBOSE) { # with "progress bar"
    open (FC_CACHE, "$command |");
    select (STDOUT);
    $OUTPUT_AUTOFLUSH = 1;
    while(<FC_CACHE>) {
      print ".";
    }
    print "\n";
    close (FC_CACHE);
  }
  else { # without "progress bar"
    my_system($command);
  }
}
$fc_cache32_exit_status = $CHILD_ERROR;
if ($VERBOSITY >= $VERBOSITY_DEBUG) {
  print "exit status of fc-cache: $fc_cache32_exit_status\n";
}

my $fc_cache64_exit_status = 256;

if ($fc_cache64_bin) {
  $command = "$fc_cache64_bin";
  if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
    print "Creating 64bit cache files for fontconfig ";
  }
  if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
    $command .= " --verbose ";
  }
  if ($VERBOSITY >= $VERBOSITY_VERBOSE) { # with "progress bar"
    open (FC_CACHE, "$command |");
    select (STDOUT);
    $OUTPUT_AUTOFLUSH = 1;
    while(<FC_CACHE>) {
      print ".";
    }
    print "\n";
    close (FC_CACHE);
  }
  else { # without "progress bar"
    my_system($command);
  }
}
$fc_cache64_exit_status = $CHILD_ERROR;
if ($VERBOSITY >= $VERBOSITY_DEBUG) {
  print "exit status of fc-cache: $fc_cache64_exit_status\n";
}

# make_gs_fontmap uses fontconfig,
# therefore it has to be called *after* fc-cache
if ($OPT_GS_FONTMAP) {
  if ($OPT_FORCE || $gs_fontmap_needs_update ) {
    make_gs_fontmap (@x11_font_dirs);
  }
}

# generate_ooo_font_setup uses fontconfig,
# therefore it has to be called *after* fc-cache
if ($OPT_OOO) {
  generate_ooo_font_setup ();
}

# generate_java_font_setup uses fontconfig,
# therefore it has to be called *after* fc-cache
if ($OPT_JAVA) {
  generate_java_font_setup ();
}

# source all shell scripts from the directory /usr/lib/fonts-config/conf.d
# which start with at least one digit (sorted in POSIX sort order because this
# perl script does *not* use "use locale"):
if (opendir (DIR, "/usr/lib/fonts-config/conf.d")) {
  my @entries = readdir (DIR);
  for my $entry (sort (@entries)) {
      if ("$entry" =~ /\./ || "$entry" =~ /\.\./
	  || "$entry" !~ /^[0-9]+/
	  || ! -f "/usr/lib/fonts-config/conf.d/$entry") {
	next;
      }
      my_system ("sh /usr/lib/fonts-config/conf.d/$entry");
  }
  closedir DIR;
}

if ($xset_bin) {
  if ($ENV{DISPLAY} && $ENV{DISPLAY} =~ /^:[0-9].*/) {
    # it's a local display
    $command = "$xset_bin fp rehash ";
    unless ($VERBOSITY >= $VERBOSITY_DEBUG) {
      $command .= " > /dev/null 2>&1 ";
    }
    my_system ($command);
  }
}

if ($xfs_bin && my_system ("$checkproc_bin $xfs_bin") == 0) {
  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
    print "Reloading config file of X Font Server ...\n";
  }
  my_system ("$killproc_bin $xfs_bin -USR1");
}
elsif ($VERBOSITY >= $VERBOSITY_DEBUG) {
  print "X Font Server not used.\n";
}

exit 0;

########################################################################

sub version_compare {
  my ($version_string_1, $version_string_2) = @_;
  # compare two version numbers consisting only of digits and dots
  # returns an integer less than, equal to, or greater than zero
  # if $version_string_1 is found, respectively, to be a smaller version,
  # equal version, or greater version than $version_string_2.
  my @v1 = split (/\./, $version_string_1);
  my @v2 = split (/\./, $version_string_2);
  for (my $i = 0 ; $i <= $#v1 && $i <= $#v2; $i++) {
    if ($v1[$i] < $v2[$i]) {
      return -1;
    } elsif ($v1[$i] > $v2[$i]) {
      return 1;
    }
  }
  if ($#v1 < $#v2) {
    return -1;
  } elsif ($#v1 > $#v2) {
    return 1;
  }
  return 0;
}

sub fontconfig_version {
  if ($fc_cache_bin) {
    open (VERSION, "$fc_cache_bin -V 2>&1 |");
    binmode VERSION, ":bytes";
    while (<VERSION>) {
      if ($ARG =~ /fontconfig\s+version\s+([.[:digit:]]+)\s*/) {
	close (VERSION);
	return "$1";
      }
    }
    close (VERSION);
  }
  # don't know the version
  return "";
}

sub xfree86_version {
  if ($XFree86_bin) {
    open (VERSION, "$XFree86_bin -version 2>&1 |");
    binmode VERSION, ":bytes";
    while (<VERSION>) {
      if ($ARG =~ /Version\s+([.[:digit:]]+)\s*/) {
	close (VERSION);
	return "$1";
      }
    }
    close (VERSION);
  }
  # don't know the version
  return "";
}

sub freetype_module_supports_ttcap {
  my ($v1, $v2, $v3, $v4) = xfree86_version();
  if (version_compare(xfree86_version(), "4.3.99") >= 0) {
    if ($VERBOSITY >= $VERBOSITY_DEBUG) {
      print "freetype module supports TTCap.\n";
    }
    return 1;
  }
  else {
    if ($VERBOSITY >= $VERBOSITY_DEBUG) {
      print "freetype module does not support TTCap.\n";
    }
    return 0;
  }
}

sub x11_font_dirs {
  my @dirs = ();
  my @standard_dirs = (
		       "/usr/share/fonts/local",
		       "/usr/share/fonts/misc",
		       "/usr/share/fonts/75dpi",
		       "/usr/share/fonts/100dpi",
		       "/usr/share/fonts/Type1",
		       "/usr/share/fonts/URW",
		       "/usr/share/fonts/Speedo",
		       "/usr/share/fonts/PEX",
		       "/usr/share/fonts/cyrillic",
		       "/usr/share/fonts/latin2/misc",
		       "/usr/share/fonts/latin2/75dpi",
		       "/usr/share/fonts/latin2/100dpi",
		       "/usr/share/fonts/latin2/Type1",
		       "/usr/share/fonts/latin7/75dpi",
		       "/usr/share/fonts/baekmuk",
		       "/usr/share/fonts/japanese",
		       "/usr/share/fonts/kwintv",
		       "/usr/share/fonts/truetype",
		       "/usr/share/fonts/uni",
		       "/usr/share/fonts/CID",
		       "/usr/share/fonts/ucs/misc",
		       "/usr/share/fonts/ucs/75dpi",
		       "/usr/share/fonts/ucs/100dpi",
		       "/usr/share/fonts/hellas/misc",
		       "/usr/share/fonts/hellas/75dpi",
		       "/usr/share/fonts/hellas/100dpi",
		       "/usr/share/fonts/hellas/Type1",
		       "/usr/share/fonts/misc/sgi",
		       "/usr/share/fonts/xtest",
		       "/usr/X11R6/lib/X11/fonts/local",
		       "/usr/X11R6/lib/X11/fonts/misc",
		       "/usr/X11R6/lib/X11/fonts/75dpi",
		       "/usr/X11R6/lib/X11/fonts/100dpi",
		       "/usr/X11R6/lib/X11/fonts/Type1",
		       "/usr/X11R6/lib/X11/fonts/URW",
		       "/usr/X11R6/lib/X11/fonts/Speedo",
		       "/usr/X11R6/lib/X11/fonts/PEX",
		       "/usr/X11R6/lib/X11/fonts/cyrillic",
		       "/usr/X11R6/lib/X11/fonts/latin2/misc",
		       "/usr/X11R6/lib/X11/fonts/latin2/75dpi",
		       "/usr/X11R6/lib/X11/fonts/latin2/100dpi",
		       "/usr/X11R6/lib/X11/fonts/latin2/Type1",
		       "/usr/X11R6/lib/X11/fonts/latin7/75dpi",
		       "/usr/X11R6/lib/X11/fonts/baekmuk",
		       "/usr/X11R6/lib/X11/fonts/japanese",
		       "/usr/X11R6/lib/X11/fonts/kwintv",
		       "/usr/X11R6/lib/X11/fonts/truetype",
		       "/usr/X11R6/lib/X11/fonts/uni",
		       "/usr/X11R6/lib/X11/fonts/CID",
		       "/usr/X11R6/lib/X11/fonts/ucs/misc",
		       "/usr/X11R6/lib/X11/fonts/ucs/75dpi",
		       "/usr/X11R6/lib/X11/fonts/ucs/100dpi",
		       "/usr/X11R6/lib/X11/fonts/hellas/misc",
		       "/usr/X11R6/lib/X11/fonts/hellas/75dpi",
		       "/usr/X11R6/lib/X11/fonts/hellas/100dpi",
		       "/usr/X11R6/lib/X11/fonts/hellas/Type1",
		       "/usr/X11R6/lib/X11/fonts/misc/sgi",
		       "/usr/X11R6/lib/X11/fonts/xtest",
		       "/opt/kde3/share/fonts"
		      );

  push (@dirs, @standard_dirs);
  push (@dirs, xorg_config_font_dirs());
  push (@dirs, xfs_font_dirs());

  # Attention!:
  # /usr/X11R6/lib/X11/fonts/encodings usually doesn't contain any fonts.
  # Neverthelesss a valid encodings.dir file must exist in that directory
  # because some applications, e.g. luit, read encodings.dir from there
  # (See for example "LANG=ja_JP strace -eopen luit -c").
  # The easiest way to make sure that the encodings.dir in that directory
  # is correct and up to date is to add the "encodings" directory to the
  # list of font directories.
  # Then mkfontdir will be called on that directory as well and a correct
  # encodings.dir will be generated.
  if (-d "/usr/share/fonts/encodings") {
      push (@dirs, ("/usr/share/fonts/encodings"));
  } else {
      push (@dirs, ("/usr/X11R6/lib/X11/fonts/encodings"));
  }
  
  # remove non existing directories and duplicates:
  my %dirs = ();
  for my $dir (@dirs) {
    if (-d $dir) {
      $dirs{$dir} = "";
    }
  }
  return (keys %dirs);
}

# gets font paths configured in /etc/X11/xorg.conf
# and check wether the "xtt" and/or "freetype" modules are loaded:
sub xorg_config_font_dirs {
  my @dirs = ();
  my $file = "/etc/X11/xorg.conf";
  if (-e $file) {
    open (CONFIGFILE, $file) || die "can't open file $file: $!";
    while (<CONFIGFILE>) {
      chomp ($ARG);
      if ($ARG =~ /([^#]*)#?.*/) { # strip comments
	$ARG = $1;
      }
      if ($ARG =~ /\s*FontPath\s+"(.+?)(:unscaled|:scaled)?"\s*/i) {
	push (@dirs, $1);
      }
      if ($ARG =~ /\s*Load\s+"xtt"/) {
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "xtt module is used.\n";
	}
	$xtt_module_used = 1;
      }
      if ($ARG =~ /\s*Load\s+"freetype"/) {
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "freetype module is used.\n";
	}
	$freetype_module_used = 1;
      }
    }
    close (CONFIGFILE);
  }
  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
    print "font paths found in $file:\n";
    for my $dir (@dirs) {
      print "$dir\n";
    }
  }
  return @dirs;
}

# gets font paths configured for xfs if xfs exists and is running:
sub xfs_font_dirs {
  my @dirs = ();
  my $file = "/etc/X11/fs/config";
  if ($xfs_bin && my_system ("$checkproc_bin $xfs_bin") == 0) {
    if (-e $file) {
      my $config_file_contents = "";
      open (CONFIGFILE, $file) || die "can't open file $file: $!";
      while (<CONFIGFILE>) {
	$ARG =~ /([^#]*)#?.*/; # strip comments
	$config_file_contents .= $1;
      }
      close (CONFIGFILE);
      $config_file_contents =~ /catalogue[^=]*=([^=]*?)\n[^\n]*=/si;
      my $catalogue = $1;
      @dirs = split(/[\s,\\]+/, $catalogue);
      for my $index (0 .. $#dirs) {
	if ($dirs[$index] =~ /(.+?)(:unscaled|:scaled)/) {
	  $dirs[$index] = $1;
	}
      }
    }
  }
  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
    print "font paths found in $file:\n";
    for my $dir (@dirs) {
      print "$dir\n";
    }
  }
  return @dirs;
}

sub create_symbolic_links {
  my ($dir) = @_;
  
  my $cwd;
  chomp ($cwd = `pwd`);
  chdir $dir ||die "Can't cd to $dir: $!\n";
  
  # create symbolic links for PostScript fonts in pfa format
  # which have no extension:
  # (seems like all such fonts are gone now, I can't find
  # such fonts any more on my system 2005.09.01)
  if ($have_file_mmagic) {
    my $mm = new File::MMagic; # use internal magic file
    # $mm = File::MMagic::new('/etc/magic'); # use external magic file
    my $type;
    for my $file (glob ("*")) {
      unless ($file =~ /\./i) {
	$type = $mm->checktype_filename($file);
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "File::MMagic $file: $type\n";
	}
	if ($type =~ /application\/postscript/) {
	  # it's probably a PostScript font in pfa format
	  # create a symbolic link unless it has the .pfa extension already
	  # or is a .gsf file (File::MMagic detects .gsf files also as
	  # "application/postscript", but it makes no sense to link these
	  # to .pfa)
	  unless ($file =~ /\.pfa|\.gsf/i) {
	    if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	      printf "symlink ($file, \"${file}.pfa\")\n";
	    }
	    symlink ($file, "${file}.pfa");
	  }
	}
      }
    }
  }
  
  # create symbolic links for files which have characters in
  # their name which are impossible to use in the file name in
  # fonts.dir:

  my @forbidden_characters = (" ", ":");
  for my $file (glob ("*.*")) {
    for my $forbidden_character (@forbidden_characters) {
      if ($file =~ /$forbidden_character/) {
	my $file_new;
	($file_new = $file) =~ s/$forbidden_character/_/g;
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  printf "symlink ($file, $file_new)\n";
	}
	symlink ($file, $file_new);
      }
    }
  }

  chdir $cwd ||die "Can't cd to $cwd: $!\n";
}

sub make_fonts_scale_and_fonts_dir {
  my ($dir) = @_;
  my $timestamp = "$dir/.fonts-config-timestamp";
  my $try_again = 0;

  # workaround for a bug in older versions of /usr/sbin/Check which
  # gzips the timestamp files:
  if (-e "${timestamp}.gz") {
    unlink ("${timestamp}.gz");
  }

  if ($OPT_FORCE ||
      mtime_differs_or_missing ("$timestamp","$dir") ||
      mtime_differs_or_missing ("$timestamp","$dir/fonts.scale") ||
      mtime_differs_or_missing ("$timestamp","$dir/fonts.dir")) {

    # Touch and delete fonts.scale and fonts.dir just to make sure
    # we are starting from scratch and the directory is writeable:
    my_system ("touch $dir/fonts.scale $dir/fonts.dir > /dev/null 2>&1");
    if (!unlink ("$dir/fonts.scale", "$dir/fonts.dir")) {
      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	print "Cannot write in $dir. Readonly filesystem?\n";
      }
      return;
    }

    $gs_fontmap_needs_update = 1;

    create_symbolic_links($dir);
    
    if ($VERBOSITY >= $VERBOSITY_DEBUG) {
      print "creating $dir/fonts.{scale,dir} ...\n";
    }
    if ($mkfontscale_bin) {
      $command = "$mkfontscale_bin $dir";
      unless ($VERBOSITY >= $VERBOSITY_DEBUG) {
	$command .= " > /dev/null 2>&1 ";
      }
      my_system ($command);
    }

    if (! -e "$dir/fonts.scale" ) {
      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	print "mkfontscale is not available or it failed. ";
	print "-> Create an empty fonts.scale file.\n";
      }
      $try_again = 1;
      my_system ("echo 0 > $dir/fonts.scale");
    }
    
    fix_fonts_scale ($dir);
    
    if ($mkfontdir_bin) {
      $command = "$mkfontdir_bin ";
      if (-d "/usr/X11R6/lib/X11/fonts/encodings") {
	$command .= " -e /usr/X11R6/lib/X11/fonts/encodings";
      }
      if (-d "/usr/X11R6/lib/X11/fonts/encodings/large") {
	$command .= " -e /usr/X11R6/lib/X11/fonts/encodings/large";
      }
      if (-d "/usr/share/fonts/encodings") {
	$command .= " -e /usr/share/fonts/encodings";
      }
      if (-d "/usr/share/fonts/encodings/large") {
	$command .= " -e /usr/share/fonts/encodings/large";
      }
      $command .= " $dir";
      my_system ($command);
    }

    if (! -e "$dir/fonts.dir" ) {
      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	print "mkfontdir is not available or it failed. ";
      }
      $try_again = 1;
      if (-f "$dir/fonts.scale" ){
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "A fonts.scale file exists, copy it to fonts.dir.";
	}
	my_system ("cp $dir/fonts.scale $dir/fonts.dir");
      }
      else {
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "No fonts.scale file exists either, create an empty fonts.dir.";
	}
	my_system ("echo 0 > $dir/fonts.dir");
      }
    }

    # Directory done. Now update time stamps:
    if ($try_again) {
      # mkfontscale and/or mkfontdir failed or didn't exist. Remove the
      # timestamp to make sure this script tries again next time
      # when the problem with mkfontscale and/or mkfontdir is fixed:
      unlink ("$timestamp");
    }
    else {
      # fonts.cache-* files are now generated in /var/cache/fontconfig,
      # remove old cache files in the individual directories
      # (fc-cache does this as well when the cache files are out of date
      # but it can't hurt to remove them here as well just to make sure).
      for my $file (glob ("$dir/fonts.cache-*")) {
	if (-e "$file") {
	  unlink ("$file");
	}
      }
      if (! -e $timestamp) {
	my_system ("touch $timestamp");
      }
      utime ($script_start_time, $script_start_time,
	     ("$dir/fonts.dir", "$dir/fonts.scale", $timestamp, $dir));
    }
  }
}

sub fix_fonts_scale {
  my ($dir) = @_;
  my ($file, $options, $font, $xlfd);
  my %fonts_scale_entries = ();
  my %blacklist = ();

  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
    print "----------------------------------------------------------------------\n";
    print "fix fonts.scale in $dir:\n";
  }

  ######################################################################
  # first parse the "handmade" fonts.scale.* files:
  for $file (glob ("$dir/fonts.scale.*")) {
    if (-e $file) {
      if ($file =~ /~$|\.swp$|\.bak$|\.sav$|\.save$|\.rpmsave$|\.rpmorig|\.rpmnew$/) {
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "$file is considered a backup file, ignored.\n";
	}
	next;
      }
      open (FONTS_SCALE, $file) || die "can't open file $file: $!";
      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	print "reading $dir/$file ...\n";
      }
      while (<FONTS_SCALE>) {
	if ($ARG =~ /^(.*?)([^:\s]+)\s+(-.+?)\s*$/) { # font name and xlfd found
	  $options = $1;
	  $font = $2;
	  $xlfd = $3;
	  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	    print "handmade entry found: options=$options font=$font xlfd=$xlfd\n";
	  }
	  if (( $OPT_TTCAP ) && $options =~ /:([0-9]):/) {
	    if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	      print "--ttcap option is set: convert face number to TTCap syntax: fn=$1:\n";
	    }
	    $options = "fn=$1:";
	  }
	  if ($freetype_module_used && ! $OPT_TTCAP ) {
	    if ($options =~ /fn=([0-9]):/) {
	      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
		print "freetype module used and --ttcap option is not set: convert face number to freetype syntax: :$1:\n";
	      }
	      $options = ":$1:";
	    } elsif ($options =~ /[a-z=]/i) {
	      # there's more then just a face number, better ignore it 
	      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
		print "xtt module not used: discard xtt only entry $ARG\n";
	      }
	      next;
	    }
	  }
	  unless ( $font =~ /\.cid$/) {
	    # For font file name entries ending with ".cid", such a file
	    # usually doesn't exist and it doesn't need to. The backend which
	    # renders CID-keyed fonts just parses this name to find the real
	    # font files and mapping tables
	    #
	    # For other entries, we check whether the file exists.
	    if (! -e "$dir/$font") {
	      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
		print "file $dir/$font doesn't exist, discard entry $ARG\n";
	      }
	      next;
	    }
	  }
	  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	    print "adding handmade entry $ARG\n";
	  }
	  $fonts_scale_entries{$xlfd} = "${options}${font}";
	  # This font has "handmade" fonts.scale entries.
	  # Add it to the blacklist to discard any entries for this font
	  # which which might have been automatically created
	  # by mkfontscale:
	  $blacklist{$font} = "yes";
	}
      }
      close (FONTS_SCALE);
    }
  }

  ######################################################################
  # blacklist globs to avoid fonts known to be broken:
  #
  # (broken fonts may still be included in manually edited fonts.scale.*
  # files. He who edits these files manually should know what he is doing ...)

  my @blacklist_globs = ();
  
  # push (@blacklist_globs, ("totally-broken*.ttf", "fubar*.ttc"));
  
  # the Hershey-Fonts from ghostscript-fonts-other.rpm have broken outlines
  # and don't work with X11, neither with the freetype module nor via libXft.
  push (@blacklist_globs, ("hrger.pfa", "hrgrr.pfa", "hritr.pfa", "hrpld.pfa",
			   "hrpldi.pfa", "hrplt.pfa", "hrplti.pfa", "hrscc.pfa",
			   "hrscs.pfa",
			   "u003043t.gsf",
			   "u004006t.gsf",
			   # .bdf fonts currently don't work with Xft2:
			   "*.bdf" 
			  ));

  my @blacklist_fonts_cache = ();
  
  for my $glob (@blacklist_globs) {
    if ($VERBOSITY >= $VERBOSITY_DEBUG) {
      print "expanding blacklist glob: $dir/$glob\n";
    }
    for my $font (glob ("$dir/$glob")) {
      if (-e $font) {
	$font =~ /\/([^\/]+)$/; # basename (strip directory)
	$font = $1;
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "$dir/$font matched by blacklist glob, adding $font to blacklist.\n";
	}
	$blacklist{$font} = "yes";
	push (@blacklist_fonts_cache, ($font));
      }
    }
  }

  ######################################################################
  # remove broken entries from fonts.cache-1:
  if (@blacklist_fonts_cache) {
    my @entries = ();
    $file = "$dir/fonts.cache-1";
    if (-e $file) {
      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	print "removing blacklisted fonts @blacklist_fonts_cache from $file\n";
      }
      open (FONTS_CACHE, $file) || die "can't open file $file: $!";
      while (<FONTS_CACHE>) {
	$ARG =~ /^"([^"]+)" /;
	if (! grep /$1/, @blacklist_fonts_cache) {
	  push (@entries, ($ARG));
	}
      }
      close (FONTS_CACHE);
      open (FONTS_CACHE, ">$file") || die "can't open file $file: $!";
      for my $entry (@entries) {
	print FONTS_CACHE "$entry";
      }
      close (FONTS_CACHE);
    }
  }
  
  ######################################################################
  # Now parse the fonts.scale file automatically created by mkfontscale:
  $file = "$dir/fonts.scale";
  open (FONTS_SCALE, $file) || die "can't open file $file: $!";
  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
    print "reading $dir/$file ...\n";
  }
  while (<FONTS_SCALE>) {
    if ($ARG =~ /^(.*?)([^:\s]+)\s+(-.+?)\s*$/) { # font name and xlfd found
      $options = $1;
      $font = $2;
      $xlfd = $3;
      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "mkfontscale entry found: options=$options font=$font xlfd=$xlfd\n";
      }
      # mkfontscale apparently doesn't yet generate the special options for
      # the freetype module to use different face numbers in .ttc files.
      # But this might change, therefore it is probably better to check this as well:
      if ( ( $OPT_TTCAP ) && $options =~ /:([0-9]):/) {
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "--ttcap option is set: convert face number to TTCap syntax: fn=$1:\n";
	}
	$options = "fn=$1:";
      }
      if ($blacklist{$font}) {
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "$dir/$font is blacklisted, ignored.\n";
	}
	next;
      }
      $fonts_scale_entries{$xlfd} = "${options}${font}";
    }
  }
  close (FONTS_SCALE);

  ######################################################################
  # generate an oblique entry if only italic is there and vice versa:

  for my $old_xlfd (keys %fonts_scale_entries) {
    if ($old_xlfd =~ /(-[^-]+-[^-]+-[^-]+)(-[io]-)([^-]+-[^-]*-\d+-\d+-\d+-\d+-[pmc]-\d+-[^-]+-[^-]+)/i) {
      my $new_xlfd = "";
      if ("$2" eq "-i-") {
 	$new_xlfd = "${1}-o-${3}";
      } else {
 	$new_xlfd = "${1}-i-${3}";
      }
      unless ($fonts_scale_entries{$new_xlfd}) {
	$fonts_scale_entries{$new_xlfd} = $fonts_scale_entries{$old_xlfd};
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "generated o/i: $fonts_scale_entries{$new_xlfd} $new_xlfd\n";
	}
      }
    }
  }
  
  ######################################################################
  # generate TTCap options for xtt:
  if ($OPT_TTCAP) {
    if ($VERBOSITY >= $VERBOSITY_DEBUG) {
      print "generating TTCap options for use with xtt or recent freetype modules ...\n";
    }
    my ($italic, $oblique, $bold, $bold_italic, $bold_oblique);
    my $artificial_italic = "ai=0.2:";
    my $doublestrike = "ds=y:";
    for my $medium (sort (keys %fonts_scale_entries)) {
      if ($medium !~ /-medium-r-/) {
	next;
      }
      if ($fonts_scale_entries{$medium} !~ /^(.*?)([^:]+)$/) {
	next; # should not happen ...
      }
      $options = $1;
      $font = $2;
      if ( $xtt_module_used && $font !~ /\.ttf$|\.ttc$/i ) {
	next; # xtt can only handle TrueType fonts.
      }
      elsif ( $freetype_module_used && $font !~ /\.ttf$|\.ttc$|\.otf|\.otc|\.pfa|\.pfb/i ) {
	next; # the freetype module handles TrueType, OpenType, and Type1 fonts.
      }
      elsif ( !$freetype_module_used && !$xtt_module_used) {
	# neither the xtt module nor the freetype module is used, generating TTCap
	# options makes no sense.
	next;
      }
      if ($options) {
	next; # there are already some TTCap options, better don't touch this
      }
      ($italic       = $medium) =~ s/-medium-r-/-medium-i-/;
      ($oblique      = $medium) =~ s/-medium-r-/-medium-o-/;
      ($bold         = $medium) =~ s/-medium-r-/-bold-r-/;
      ($bold_italic  = $medium) =~ s/-medium-r-/-bold-i-/;
      ($bold_oblique = $medium) =~ s/-medium-r-/-bold-o-/;
      unless ($fonts_scale_entries{$italic} ||
	      $fonts_scale_entries{$oblique}) {
	$fonts_scale_entries{$italic}  = "${artificial_italic}${font}";
	$fonts_scale_entries{$oblique} = "${artificial_italic}${font}";
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "generated TTCap entry: $fonts_scale_entries{$italic} $italic\n";
	  print "generated TTCap entry: $fonts_scale_entries{$oblique} $oblique\n";
	}
      }
      unless ($fonts_scale_entries{$bold}) {
	$fonts_scale_entries{$bold}  = "${doublestrike}${font}";
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "generated TTCap entry: $fonts_scale_entries{$bold} $bold\n";
	}
      }
      unless ($fonts_scale_entries{$bold_italic} ||
	      $fonts_scale_entries{$bold_oblique}) {
	$fonts_scale_entries{$bold_italic}  = "${doublestrike}${artificial_italic}${font}";
	$fonts_scale_entries{$bold_oblique} = "${doublestrike}${artificial_italic}${font}";
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "generated TTCap entry: $fonts_scale_entries{$bold_italic} $bold_italic\n";
	  print "generated TTCap entry: $fonts_scale_entries{$bold_oblique} $bold_oblique\n";
	}
      }
    }
    # add bw=0.5 option when necessary:
    for my $xlfd (sort (keys %fonts_scale_entries)) {
      if ($fonts_scale_entries{$xlfd} !~ /^(.*?)([^:]+)$/) {
	next; # should not happen ...
      }
      $options = $1;
      $font = $2;
      if ( $xtt_module_used && $font !~ /\.ttf$|\.ttc$/i ) {
	next; # xtt can only handle TrueType fonts.
      }
      elsif ( $freetype_module_used && $font !~ /\.ttf$|\.ttc$|\.otf|\.otc|\.pfa|\.pfb/i ) {
	next; # the new freetype module handles TrueType, OpenType, and Type1 fonts.
      }
      elsif ( !$freetype_module_used && !$xtt_module_used) {
	# neither the xtt module nor the freetype module is used, generating TTCap
	# options makes no sense.
	next;
      }
      if ($options =~ /bw=/) {
	next; # there is already a bw=<something> TTCap option, better don't touch this
      }
      if ($xlfd =~ /c-0-jisx0201.1976-0/) {
	$fonts_scale_entries{$xlfd} = "${options}bw=0.5:${font}";
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print "added bw=0.5 option: $fonts_scale_entries{$xlfd} $xlfd\n";
	}
      }
    }
  }
  ######################################################################
  # weed out entries which certainly cannot work:

  for my $xlfd (keys %fonts_scale_entries) {
    if ($fonts_scale_entries{$xlfd} !~ /\.([[:alnum:]]+)(\.gz)?$/i) {
      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	print "file has no extension, discarding $fonts_scale_entries{$xlfd} $xlfd\n";
      }
      delete $fonts_scale_entries{$xlfd};
      next;
    }
    else {
      my $extension = $1 ;

      # Currently only the freetype module can handle .otf fonts.
      #
      # If both xtt and freetype are used at the same time, xtt will only register
      # for .ttf and .ttc fonts and freetype will still be able to handle
      # the .otf fonts.
      # But if freetype is not used, .otf fonts won't work at all and
      # directories containing entries for .otf fonts in fonts.dir will be
      # removed from the font path.
      # This is not nice because there may be other fonts in that directory which
      # could be used, for example .ttf or .ttc.
      # Therefore it is better to throw away all entries for .otf fonts if
      # the freetype module is not used:
      if ($extension =~ /otf/i && ! $freetype_module_used) {
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  print ".otf won't work without freetype module, discarding $fonts_scale_entries{$xlfd} $xlfd\n";
	}
	delete $fonts_scale_entries{$xlfd};
      }
      if ($extension =~ /gsf/i) {
 	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
 	  print ".gsf fonts won't work with X11, discarding $fonts_scale_entries{$xlfd} $xlfd\n";
 	}
 	delete $fonts_scale_entries{$xlfd};
      }
    }
  }

  ######################################################################
  # write final result to fonts.scale:
  
  $file = "$dir/fonts.scale";
  open (FONTS_SCALE, ">$file") || die "can't open file $file: $!";
  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
    print "writing $dir/$file ...\n";
  }
  printf FONTS_SCALE "%d\n", scalar(keys %fonts_scale_entries);
  for my $xlfd (sort (keys %fonts_scale_entries)) {  
    print FONTS_SCALE "$fonts_scale_entries{$xlfd} $xlfd\n";
  }
  close (FONTS_SCALE);

}

######################################################################

sub get_font_name_entries {
  my ($font) = @_;

  # quote " in font names:
  $font =~ s/\"/\\\"/g;
  open (FONT, "ftdump \"$font\" 2>/dev/null |");
  # Apparently there are some fonts which have non-ASCII PostScript names.
  # If we are running in a UTF-8 locale, Perl will complain
  #    "Malformed UTF-8 character"
  # when reading the ftdump output of such a font.
  # Avoid that error by using the layer ":bytes" for this file handle.
  binmode FONT, ":bytes";

  my $postscript = "";
  my $family = "";
  my $style = "";
  my $face_number = 0;

  while (<FONT>) {
    if ($ARG =~ /face.*number.*:\s*([0-9]+)\s*/i) {
      $face_number = $1;
    }
    if ($face_number > 0) {
      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	print "face_number=$face_number found. All faces with number > 0 ignored.\n";
      }
      last;
    }
    # the following regexp should work with ftdump from either Freetype-1 or Freetype-2
    if ($ARG =~ /postscript.*:\s+([[:alnum:]\-]+?)\s*$/i) {
      $postscript = $1;
      if ($postscript eq "UNAVAILABLE") {
	$postscript = "";
      }
    }
    if ($ARG =~ /family.*:\s+([[:alnum:]].*[[:alnum:]])\s*$/i) {
      $family = $1;
    }
    if ($ARG =~ /style.*:\s+([[:alnum:]].*[[:alnum:]])\s*$/i) {
      $style = $1;
    }
  }
  close (FONT);
  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
    print "ftdump: font=$font postscript=$postscript family=$family style=$style\n";
  }
  return ($postscript, $family, $style);
}

sub gs_fontmap_aliases_add {
  my ($aliases, $alias) = @_;
  my $alias_case_normalized = $alias;
  $alias_case_normalized =~ s/\b(\w)(\w*)/ uc($1) . lc($2) /eg;
  $alias_case_normalized =~ s/ //g;
  $alias =~ s/ //g;
  $aliases->{$alias} = "";
  $aliases->{$alias_case_normalized} = "";
}

sub gs_fontmap_aliases {
  my ($postscript, $family, $style, $xlfd) = @_;
  my $xlfd_fmly = "";
  my $xlfd_wght = "";
  my $xlfd_slant = "";
  my %aliases = ();

  if ($xlfd && $xlfd =~
      /-[^-]+-([^-]+)-([^-]+)-([^-])-[^-]+-[^-]*-\d+-\d+-\d+-\d+-[pmc]-\d+-[^-]+-[^-]+/i) {
    $xlfd_fmly = $1;
    $xlfd_wght = $2;
    $xlfd_slant = $3;
    # Some aliases for applications which don't use the correct PostScript
    # names but rather some combination of family, weight, and slant from the XLFD.
    # Probably because there is no way to get the PostScript name without
    # direct access to the font file. When X11 core fonts are used instead of
    # fontconfig/libXft, the application may only see the XLFD and can only
    # try to "guess" the PostScript name from the XLFD.
    # (For example Qt3 does something like this ...)
    if ($xlfd_fmly) {
      if ($xlfd_wght !~ /bold/i && $xlfd_slant ne "i" && $xlfd_slant ne "o") {
	gs_fontmap_aliases_add(\%aliases, "$xlfd_fmly");
      }
      if ($xlfd_wght !~ /bold/i && ($xlfd_slant eq "i" || $xlfd_slant eq "o")) {
	gs_fontmap_aliases_add(\%aliases, "$xlfd_fmly-Italic");
      }
      if ($xlfd_wght =~ /bold/i && $xlfd_slant ne "i" && $xlfd_slant ne "o") {
	gs_fontmap_aliases_add(\%aliases, "$xlfd_fmly-Bold");
      }
      if ($xlfd_wght =~ /bold/i && ($xlfd_slant eq "i" || $xlfd_slant eq "o")) {
	gs_fontmap_aliases_add(\%aliases, "$xlfd_fmly-Bold Italic");
      }
    }
  }
  # an alias to the PostScript name would make no sense, delete it if
  # it was accidentally created:
  delete $aliases{$postscript};
  return (sort (keys %aliases));
}

sub make_gs_fontmap {
  my (@font_dirs) = @_;

  if (!$ftdump_bin) {
    print "Warning: Creation of a Fontmap for Ghostmap has been requested\n";
    print "         (either the the option --gs-fontmap has been used or the variable\n";
    print"           GENERATE_GHOSTSCRIPT_FONTMAPS in /etc/sysconfig/fonts-config\n";
    print"           has been set to \"yes\").\n";
    print "         But the 'ftdump' program which is needed to do this is\n";
    print "         apparently not installed. 'ftdump' is in the 'ft2demos'\n";
    print "         package. Please install this package if you want a Fontmap\n";
    print "         for Ghostscript to be created.\n";
  }
  else {
    if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
      print "Creating Fontmap entries for Ghostscript ";
    }
    for my $fontmap_dir (glob ("/usr/share/ghostscript/[0-9]*/lib")) {
      if (-e $fontmap_dir) {
	my $file = "$fontmap_dir/Fontmap.X11-auto";
	open (FONTMAP, ">$file") || die "can't open file $file: $!";
	if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	  printf "writing $file ...\n";
	}
      
	print FONTMAP "% Fontmap for scalable fonts found in the X11 font paths\n";
	print FONTMAP "%\n";
	print FONTMAP "% This file is automatically generated by\n";
	print FONTMAP "% \"fonts-config\" (version $version) and will be\n";
	print FONTMAP "% overwritten during the next run of this script.\n";
	print FONTMAP "%\n";
	print FONTMAP "% Don't edit this file. If you want to create your own\n";
	print FONTMAP "% manual Fontmap entries, put them elsewhere.\n";
	print FONTMAP "%\n";

	my %fontmap_entries = ();
	for my $dir (@font_dirs) {
	
	  open (FONTS_SCALE, "$dir/fonts.scale") || die "can't open file $dir/fonts.scale: $!";
	  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	    print "creating hashtable of xlfds from $dir/fonts.scale ...\n";
	  }
	  my %xlfds;
	  while (<FONTS_SCALE>) {
	    my ($options, $font, $xlfd);
	    if ($ARG =~ /^(.*?)([^:\s]+)\s+(-.+?)\s*$/) { # font name and xlfd found
	      $options = $1;
	      $font = $2;
	      $xlfd = $3;
	      unless ($options) { # ignore face numbers > 0 and other options
		$xlfds{"$dir/$font"} = $xlfd;
	      }
	    }
	  }
	  close (FONTS_SCALE);

	  for my $font (glob ("$dir/*.{ttf,ttc,pfa,pfb}")) {
	    if (-e $font) {
	      my ($postscript, $family, $style) = get_font_name_entries($font);
	      if ($postscript) {
		print FONTMAP "%----------------------------------------------------------------------\n";
		print FONTMAP "% postscript=\"$postscript\" family=\"$family\" style=\"$style\"\n";
		if ($xlfds{$font}) {
		  print FONTMAP "% xlfd=\"$xlfds{$font}\"\n";
		} else {
		  print FONTMAP "% xlfd=\"\"\n";
		}
		if ($fontmap_entries{$postscript}) {
		  print FONTMAP "% /$postscript ($font) ; % warning: duplicate!\n";
		} else {
		  print FONTMAP "/$postscript ($font) ;\n";
		  $fontmap_entries{$postscript} = "true";
		}
		for my $alias (gs_fontmap_aliases($postscript, $family, $style, $xlfds{$font})) {
		  if ($fontmap_entries{$alias}) {
		    print FONTMAP "% /$alias /$postscript ; % warning: duplicate!\n";
		  } else {
		    print FONTMAP "/$alias /$postscript ;\n";
		    $fontmap_entries{$alias} = "true";
		  }
		}
	      }
	    }
	  }
	  if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
	    print ".";		# "progress bar"
	  }
	}
	close (FONTMAP);
      }
    }
    if ($VERBOSITY >= $VERBOSITY_VERBOSE) {
      print "\n";
    }
  }
}

########################################################################
sub generate_ooo_font_setup {
  my $common_xcu_file = "/usr/lib/ooo-1.1/share/registry/data/org/openoffice/Office/Common.xcu";
  my $common_xcu_template_file = "/usr/share/fonts-config/Common.xcu.template";
  my $common_xcu = "";
  my $common_xcu_template = "";
  my $within_font_node_depth = 0;
  my $ooo_truetype_font_dir = "/usr/lib/ooo-1.1/share/fonts/truetype";
  
  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
    printf "generating OpenOffice font setup\n";
  }

  if (! -f "$common_xcu_file") {
    if ($VERBOSITY >= $VERBOSITY_DEBUG) {
      printf "Nothing to do for this version of OpenOffice.\n";
    }
    return;
  }
  
  if (! -f "$common_xcu_template_file") {
    printf "File $common_xcu_template_file missing. This should not happen.\n";
    printf "Skipping OpenOffice font setup\n";
    return;
  }

  my @msgothic_list = ("MS Gothic",
		       "HGGothicB",
		       "HG\\-GothicB\\-Sun",
		       "TLGothic",
		       "IPAGothic",
		       "Sazanami Gothic",
		       "Kochi Gothic");

  my @mspgothic_list = ("MS PGothic",
		       "HGPGothicB",
		       "HG\\-PGothicB\\-Sun",
		       "TLPGothic",
		       "IPAPGothic",
		       "Sazanami Gothic",
		       "Kochi Gothic");

  my @msmincho_list = ("MS Mincho",
		       "HGMinchoL",
		       "HG\\-MinchoL\\-Sun",
		       "TLMincho",
		       "IPAMincho",
		       "Sazanami Mincho",
		       "Kochi Mincho");

  my @mspmincho_list = ("MS PMincho",
			"HGPMinchoL",
			"HG\\-PMinchoL\\-Sun",
			"TLPMincho",
			"IPAPMincho",
			"Sazanami Mincho",
			"Kochi Mincho");

  my $msgothic = "Sazanami Gothic";
  my $mspgothic = "Sazanami Gothic";
  my $msmincho = "Sazanami Mincho";
  my $mspmincho = "Sazanami Mincho";

  for my $font (@msgothic_list) {
    if (`fc-list "$font"`) {
      $msgothic = $font;
      last;
    }
  }

  for my $font (@mspgothic_list) {
    if (`fc-list "$font"`) {
      $mspgothic = $font;
      last;
    }
  }
  for my $font (@msmincho_list) {
    if (`fc-list "$font"`) {
      $msmincho = $font;
      last;
    }
  }
  for my $font (@mspmincho_list) {
    if (`fc-list "$font"`) {
      $mspmincho = $font;
      last;
    }
  }

  open (TEMPLATE, "$common_xcu_template_file") || die "can't open file $common_xcu_template_file: $!";
  binmode TEMPLATE, ":utf8";
  while (<TEMPLATE>) {
    $ARG =~ s/_MSGOTHIC_/$msgothic/;
    $ARG =~ s/_MSPGOTHIC_/$mspgothic/;
    $ARG =~ s/_MSMINCHO_/$msmincho/;
    $ARG =~ s/_MSPMINCHO_/$mspmincho/;
    $common_xcu_template .= $ARG;
  }
  close (TEMPLATE);

  if (-f "$common_xcu_file" ) {
    open (XCU, "$common_xcu_file") || die "can't open file $common_xcu_file: $!";
    binmode XCU, ":utf8";
    while (<XCU>) {
      if ($ARG =~ /<node oor:name="Font">/) {
	$within_font_node_depth = 1;
	next;
      }
      if ($within_font_node_depth > 0) {
	if ($ARG =~ /<node.*>/) {
	  $within_font_node_depth++;
	}
	if ($ARG =~ /<\/node>/) {
	  $within_font_node_depth--;
	}
	next;
      }
      unless ($ARG =~ /<\/oor:component-data>/) {
	$common_xcu .= $ARG;
      }
    }
    close (XCU);
    open (XCU, ">$common_xcu_file") || die "can't open file $common_xcu_file: $!";
    binmode XCU, ":utf8";
    print XCU $common_xcu;
    print XCU $common_xcu_template;
    print XCU '</oor:component-data>'; print XCU "\n";
    close (XCU);
  }

  if ( -d "$ooo_truetype_font_dir" ) {
    for my $font (`fc-list :lang=ja:outline=true file`) {
      my $basename;
      chomp ($font);
      $font =~ s/:.*$//;
      ($basename = $font ) =~ s/.*\/([^\/]+)/$1/;
      if ($VERBOSITY >= $VERBOSITY_DEBUG) {
	printf "symlink (\"$font\", \"$ooo_truetype_font_dir/$basename\")\n";
      }
      symlink ("$font", "$ooo_truetype_font_dir/$basename");
    }

    # remove dead links:
    for my $file (glob ("$ooo_truetype_font_dir/*")) {
      if (! -f "$file") {
	unlink("$file");
      }
    }
  }

}

########################################################################
#   setup for Java 1.6 (for Latin1, Japanese, Chinese, and Korean):

sub generate_java_font_setup {

  my @fontconfig_SuSE_properties_globs = "/usr/lib*/jvm/jre/lib/fontconfig.SuSE.properties";
  my $fontconfig_SuSE_properties_template_file = "/usr/share/fonts-config/fontconfig.SuSE.properties.template";
  my $fontconfig_SuSE_properties_template = "";
  
  if ($VERBOSITY >= $VERBOSITY_QUIET) {
    printf "generating java font setup\n";
  }

  my @sans_japanese_priority = ("MS Gothic",
				"HGGothicB",
				"IPAPGothic",
				"IPAexGothic",
				"Sazanami Gothic");

  my @mono_japanese_priority = ("MS Gothic",
				"HGGothicB",
				"IPAGothic",
				"Sazanami Gothic");

  my @serif_japanese_priority = ("MS Mincho",
				 "HGMinchoL",
				 "IPAPMincho",
				 "IPAexMincho",
				 "Sazanami Mincho");
  
  my @sans_simplified_chinese_priority = ("FZSongTi",
					  "AR PL ShanHeiSun Uni",
					  "AR PL SungtiL GB");
  
  my @serif_simplified_chinese_priority = ("FZSongTi",
					   "AR PL ShanHeiSun Uni",
					   "AR PL SungtiL GB");

  my @sans_traditional_chinese_priority = ("AR PL ShanHeiSun Uni",
					   "FZMingTiB",
					   "AR PL Mingti2L Big5");
  
  my @serif_traditional_chinese_priority = ("AR PL ShanHeiSun Uni",
					    "FZMingTiB",
					    "AR PL Mingti2L Big5");
  
  my @sans_korean_priority = ("UnDotum",
			      "Baekmuk Gulim",
			      "Baekmuk Dotum");
  
  my @serif_korean_priority = ("UnBatang",
			       "Baekmuk Batang");

  my @sans_latin1_priority = ("DejaVu Sans:style=Book:width=100",   # fc-list $font file should return only one result
                              "Liberation Sans:style=Regular",      # otherwise last one is taken in present 
                              "Droid Sans:style=Regular");          # generate_java_font_setup() structure 

  my @mono_latin1_priority = ("DejaVu Sans Mono:style=Book",
                              "Liberation Mono:style=Regular",
                              "Droid Sans Mono:style=Regular");

  my @serif_latin1_priority = ("DejaVu Serif:style=Book:width=100",
                               "Liberation Serif:style=Regular",
                               "Droid Serif:style=Regular");

  my %cjk_xlfds =
    (
     "MS Gothic"            , "-ricoh-ms gothic-",
     "HGGothicB"            , "-ricoh-hggothicb-",
     "IPAGothic"            , "-misc-ipagothic-",
     "IPAPGothic"           , "-misc-ipapgothic-",
     "IPAexGothic"          , "-misc-ipaexgothic-",
     "Sazanami Gothic"      , "-misc-sazanami gothic-",
     "MS Mincho"            , "-ricoh-ms mincho-",
     "HGMinchoL"            , "-ricoh-hgminchol-",
     "IPAMincho"            , "-misc-ipamincho-",
     "IPAPMincho"           , "-misc-ipapmincho-",
     "IPAexMincho"          , "-misc-ipaexmincho-",
     "Sazanami Mincho"      , "-misc-sazanami mincho-",
     "FZSongTi"             , "-*-SongTi-",
     "FZMingTiB"            , "-*-MingTiB-",
     "AR PL ShanHeiSun Uni" , "-*-ar pl shanheisun uni-",
     "AR PL SungtiL GB"     , "-arphic-ar pl sungtil gb-",
     "AR PL Mingti2L Big5"  , "-arphic-ar pl mingti2l big5-",
     "UnDotum"              , "-misc-undotum-",
     "Baekmuk Gulim"        , "-baekmukttf-gulim-",
     "Baekmuk Dotum"        , "-baekmukttf-dotum-",
     "UnBatang"             , "-misc-unbatang-",
     "Baekmuk Batang"       , "-baekmukttf-batang-"
    );

  my $sans_japanese_xlfd = "-misc-sazanami gothic-";
  my $sans_japanese_file = "/usr/share/fonts/truetype/sazanami-gothic.ttf";

  my $mono_japanese_xlfd = "-misc-sazanami gothic-";
  my $mono_japanese_file = "/usr/share/fonts/truetype/sazanami-gothic.ttf";

  my $serif_japanese_xlfd = "-misc-sazanami mincho-";
  my $serif_japanese_file = "/usr/share/fonts/truetype/sazanami-mincho.ttf";

  my $sans_simplified_chinese_xlfd = "-arphic-ar pl sungtil gb-";
  my $sans_simplified_chinese_file = "/usr/share/fonts/truetype/gbsn00lp.ttf";

  my $serif_simplified_chinese_xlfd = "-arphic-ar pl sungtil gb-";
  my $serif_simplified_chinese_file = "/usr/share/fonts/truetype/gbsn00lp.ttf";

  my $sans_traditional_chinese_xlfd = "-arphic-ar pl mingti2l big5-";
  my $sans_traditional_chinese_file = "/usr/share/fonts/truetype/bsmi00lp.ttf";

  my $serif_traditional_chinese_xlfd = "-arphic-ar pl mingti2l big5-";
  my $serif_traditional_chinese_file = "/usr/share/fonts/truetype/bsmi00lp.ttf";

  my $sans_korean_xlfd = "-baekmukttf-dotum-";
  my $sans_korean_file = "/usr/share/fonts/truetype/dotum.ttf";

  my $serif_korean_xlfd = "-baekmukttf-batang-";
  my $serif_korean_file = "/usr/share/fonts/truetype/batang.ttf";

  my $sans_latin1_xlfd = "";
  my $sans_latin1_file = "";

  my $mono_latin1_xlfd = "";
  my $mono_latin1_file = "";

  my $serif_latin1_xlfd = "";
  my $serif_latin1_file = "";
  
  for my $font (@sans_japanese_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) {
	chomp $ARG;
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $sans_japanese_file = $ARG;
	  $sans_japanese_xlfd = $cjk_xlfds{"$font"};
	}
      }
      close (NAMES);
      last;
    }
  }

  if ( ! -f $sans_japanese_file ) {
    print "Warning: cannot find a sans serif Japanese font. Japanese in Java might not work.\n";
  }

  for my $font (@mono_japanese_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) {
	chomp $ARG;
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $mono_japanese_file = $ARG;
	  $mono_japanese_xlfd = $cjk_xlfds{"$font"};
	}
      }
      close (NAMES);
      last;
    }
  }
  
  if ( ! -f $mono_japanese_file ) {
    print "Warning: cannot find a monospaced Japanese font. Japanese in Java might not work.\n";
  }
  
  for my $font (@serif_japanese_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) {
	chomp $ARG;
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $serif_japanese_file = $ARG;
	  $serif_japanese_xlfd = $cjk_xlfds{"$font"};
	}
      }
      close (NAMES);
      last;
    }
  }
  
  if ( ! -f $serif_japanese_file ) {
    print "Warning: cannot find a serif Japanese font. Japanese in Java might not work.\n";
  }

  for my $font (@sans_simplified_chinese_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) {
	chomp $ARG;
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $sans_simplified_chinese_file = $ARG;
	  $sans_simplified_chinese_xlfd = $cjk_xlfds{"$font"};
	}
      }
      close (NAMES);
      last;
    }
  }
  
  if ( ! -f $sans_simplified_chinese_file ) {
    print "Warning: cannot find a sans serif simplified Chinese font. Simplified Chinese in Java might not work.\n";
  }
  
  for my $font (@serif_simplified_chinese_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) {
	chomp $ARG;
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $serif_simplified_chinese_file = $ARG;
	  $serif_simplified_chinese_xlfd = $cjk_xlfds{"$font"};
	}
      }
      close (NAMES);
      last;
    }
  }

  if ( ! -f $serif_simplified_chinese_file ) {
    print "Warning: cannot find a serif simplified Chinese font. Simplified Chinese in Java might not work.\n";
  }

  for my $font (@sans_traditional_chinese_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) {
	chomp $ARG;
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $sans_traditional_chinese_file = $ARG;
	  $sans_traditional_chinese_xlfd = $cjk_xlfds{"$font"};
	}
      }
      close (NAMES);
      last;
    }
  }
  
  if ( ! -f $sans_traditional_chinese_file ) {
    print "Warning: cannot find a sans serif traditional Chinese font. Traditional Chinese in Java might not work.\n";
  }
  
  for my $font (@serif_traditional_chinese_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) {
	chomp $ARG;
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $serif_traditional_chinese_file = $ARG;
	  $serif_traditional_chinese_xlfd = $cjk_xlfds{"$font"};
	}
      }
      close (NAMES);
      last;
    }
  }
  
  if ( ! -f $serif_traditional_chinese_file ) {
    print "Warning: cannot find a serif traditional Chinese font. Traditional Chinese in Java might not work.\n";
  }
  
  for my $font (@sans_korean_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) {
	chomp $ARG;
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $sans_korean_file = $ARG;
	  $sans_korean_xlfd = $cjk_xlfds{"$font"};
	}
      }
      close (NAMES);
      last;
    }
  }
  
  if ( ! -f $sans_korean_file ) {
    print "Warning: cannot find a sans serif Korean font. Korean in Java might not work.\n";
  }
  
  for my $font (@serif_korean_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) {
	chomp $ARG;
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $serif_korean_file = $ARG;
	  $serif_korean_xlfd = $cjk_xlfds{"$font"};
	}
      }
      close (NAMES);
      last;
    }
  }
  
  if ( ! -f $serif_korean_file ) {
    print "Warning: cannot find a serif Korean font. Korean in Java might not work.\n";
  }

  for my $font (@sans_latin1_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) {   # should be only one line, see @sans_latin1_priority definition
	chomp $ARG;       # TODO: rewrite this to reflect it
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $sans_latin1_file = $ARG;
          $font =~ s/:.*$//;
	  ($sans_latin1_xlfd = "-misc-$font-") =~ tr/[A-Z]/[a-z]/;
	}
      }
      close (NAMES);
      last;
    }
  }
  
  if ( -z $sans_latin1_file ) {
    print "Warning: cannot find a serif font. Java font setup failed.\n";
  }

  for my $font (@mono_latin1_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) { # should be only one line, see @sans_latin1_priority definition
	chomp $ARG;     # TODO: rewrite this to reflect it
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $mono_latin1_file = $ARG;
          $font =~ s/:.*$//;
	  ($mono_latin1_xlfd = "-misc-$font-") =~ tr/[A-Z]/[a-z]/;
	}
      }
      close (NAMES);
      last;
    }
  }
  
  if ( -z $mono_latin1_file ) {
    print "Warning: cannot find a mono font. Java font setup failed.\n";
  }

  for my $font (@serif_latin1_priority) {
    if (`fc-list "$font"`) {
      open (NAMES, "fc-list \"$font\" file |");
      binmode NAMES, ":bytes";
      while (<NAMES>) { # should be only one line, see @sans_latin1_priority definition
	chomp $ARG;     # TODO: rewrite this to reflect it
	$ARG =~ s/:.*$//;
	if ($ARG =~ /\.ttf|\.ttc/ && -f "$ARG" && ! -l "$ARG") {
	  $serif_latin1_file = $ARG;
          $font =~ s/:.*$//;
	  ($serif_latin1_xlfd = "-misc-$font-") =~ tr/[A-Z]/[a-z]/;
	}
      }
      close (NAMES);
      last;
    }
  }
  
  if ( -z $serif_latin1_file ) {
    print "Warning: cannot find a serif font. Java font setup failed.\n";
  }
  
  (my $sans_japanese_xlfd_no_space = $sans_japanese_xlfd) =~ s/ /_/g;
  (my $mono_japanese_xlfd_no_space = $mono_japanese_xlfd) =~ s/ /_/g;
  (my $serif_japanese_xlfd_no_space = $serif_japanese_xlfd) =~ s/ /_/g;
  
  (my $sans_simplified_chinese_xlfd_no_space = $sans_simplified_chinese_xlfd) =~ s/ /_/g;
  (my $serif_simplified_chinese_xlfd_no_space = $serif_simplified_chinese_xlfd) =~ s/ /_/g;
  
  (my $sans_traditional_chinese_xlfd_no_space = $sans_traditional_chinese_xlfd) =~ s/ /_/g;
  (my $serif_traditional_chinese_xlfd_no_space = $serif_traditional_chinese_xlfd) =~ s/ /_/g;

  (my $sans_korean_xlfd_no_space = $sans_korean_xlfd) =~ s/ /_/g;
  (my $serif_korean_xlfd_no_space = $serif_korean_xlfd) =~ s/ /_/g;

  (my $sans_latin1_xlfd_no_space = $sans_latin1_xlfd) =~ s/ /_/g;
  (my $mono_latin1_xlfd_no_space = $mono_latin1_xlfd) =~ s/ /_/g;
  (my $serif_latin1_xlfd_no_space = $serif_latin1_xlfd) =~ s/ /_/g;

  if ($VERBOSITY >= $VERBOSITY_DEBUG) {
    printf ("sans_japanese_file=%s\n", $sans_japanese_file);
    printf ("sans_japanese_xlfd=%s\n", $sans_japanese_xlfd);
    printf ("sans_japanese_xlfd_no_space=%s\n", $sans_japanese_xlfd_no_space);

    printf ("sans_simplified_chinese_file=%s\n", $sans_simplified_chinese_file);
    printf ("sans_simplified_chinese_xlfd=%s\n", $sans_simplified_chinese_xlfd);
    printf ("sans_simplified_chinese_xlfd_no_space=%s\n", $sans_simplified_chinese_xlfd_no_space);

    printf ("sans_traditional_chinese_file=%s\n", $sans_traditional_chinese_file);
    printf ("sans_traditional_chinese_xlfd=%s\n", $sans_traditional_chinese_xlfd);
    printf ("sans_traditional_chinese_xlfd_no_space=%s\n", $sans_traditional_chinese_xlfd_no_space);

    printf ("sans_korean_file=%s\n", $sans_korean_file);
    printf ("sans_korean_xlfd=%s\n", $sans_korean_xlfd);
    printf ("sans_korean_xlfd_no_space=%s\n", $sans_korean_xlfd_no_space);

    printf ("sans_latin1_file=%s\n", $sans_latin1_file);
    printf ("sans_latin1_xlfd=%s\n", $sans_latin1_xlfd);
    printf ("sans_latin1_xlfd_no_space=%s\n", $sans_latin1_xlfd_no_space);
  }
  
  # I hope it is good enough to get the font directory from the full
  # path of the Japanese sans serif file.
  # Probably all fonts are in the same directory:
  (my $x11fontdir = $sans_japanese_file) =~ s/\/[^\/]+$//;
  
  if (-f "$fontconfig_SuSE_properties_template_file") {
    open (TEMPLATE, "$fontconfig_SuSE_properties_template_file") || die "can't open file $fontconfig_SuSE_properties_template_file: $!";
    binmode TEMPLATE, ":utf8";
    while (<TEMPLATE>) {
      $ARG =~ s/_SANS_JAPANESE_XLFD_NO_SPACE_/$sans_japanese_xlfd_no_space/g;
      $ARG =~ s/_SANS_JAPANESE_FILE_/$sans_japanese_file/g;
      $ARG =~ s/_SANS_JAPANESE_XLFD_/$sans_japanese_xlfd/g;
      $ARG =~ s/_MONO_JAPANESE_XLFD_NO_SPACE_/$mono_japanese_xlfd_no_space/g;
      $ARG =~ s/_MONO_JAPANESE_FILE_/$mono_japanese_file/g;
      $ARG =~ s/_MONO_JAPANESE_XLFD_/$mono_japanese_xlfd/g;
      $ARG =~ s/_SERIF_JAPANESE_XLFD_NO_SPACE_/$serif_japanese_xlfd_no_space/g;
      $ARG =~ s/_SERIF_JAPANESE_FILE_/$serif_japanese_file/g;
      $ARG =~ s/_SERIF_JAPANESE_XLFD_/$serif_japanese_xlfd/g;

      $ARG =~ s/_SANS_SIMPLIFIED_CHINESE_XLFD_NO_SPACE_/$sans_simplified_chinese_xlfd_no_space/g;
      $ARG =~ s/_SANS_SIMPLIFIED_CHINESE_FILE_/$sans_simplified_chinese_file/g;
      $ARG =~ s/_SANS_SIMPLIFIED_CHINESE_XLFD_/$sans_simplified_chinese_xlfd/g;
      $ARG =~ s/_SERIF_SIMPLIFIED_CHINESE_XLFD_NO_SPACE_/$serif_simplified_chinese_xlfd_no_space/g;
      $ARG =~ s/_SERIF_SIMPLIFIED_CHINESE_FILE_/$serif_simplified_chinese_file/g;
      $ARG =~ s/_SERIF_SIMPLIFIED_CHINESE_XLFD_/$serif_simplified_chinese_xlfd/g;

      $ARG =~ s/_SANS_TRADITIONAL_CHINESE_XLFD_NO_SPACE_/$sans_traditional_chinese_xlfd_no_space/g;
      $ARG =~ s/_SANS_TRADITIONAL_CHINESE_FILE_/$sans_traditional_chinese_file/g;
      $ARG =~ s/_SANS_TRADITIONAL_CHINESE_XLFD_/$sans_traditional_chinese_xlfd/g;
      $ARG =~ s/_SERIF_TRADITIONAL_CHINESE_XLFD_NO_SPACE_/$serif_traditional_chinese_xlfd_no_space/g;
      $ARG =~ s/_SERIF_TRADITIONAL_CHINESE_FILE_/$serif_traditional_chinese_file/g;
      $ARG =~ s/_SERIF_TRADITIONAL_CHINESE_XLFD_/$serif_traditional_chinese_xlfd/g;

      $ARG =~ s/_SANS_KOREAN_XLFD_NO_SPACE_/$sans_korean_xlfd_no_space/g;
      $ARG =~ s/_SANS_KOREAN_FILE_/$sans_korean_file/g;
      $ARG =~ s/_SANS_KOREAN_XLFD_/$sans_korean_xlfd/g;
      $ARG =~ s/_SERIF_KOREAN_XLFD_NO_SPACE_/$serif_korean_xlfd_no_space/g;
      $ARG =~ s/_SERIF_KOREAN_FILE_/$serif_korean_file/g;
      $ARG =~ s/_SERIF_KOREAN_XLFD_/$serif_korean_xlfd/g;

      $ARG =~ s/_SANS_LATIN1_XLFD_NO_SPACE_/$sans_latin1_xlfd_no_space/g;
      $ARG =~ s/_SANS_LATIN1_FILE_/$sans_latin1_file/g;
      $ARG =~ s/_SANS_LATIN1_XLFD_/$sans_latin1_xlfd/g;
      $ARG =~ s/_MONO_LATIN1_XLFD_NO_SPACE_/$mono_latin1_xlfd_no_space/g;
      $ARG =~ s/_MONO_LATIN1_FILE_/$mono_latin1_file/g;
      $ARG =~ s/_MONO_LATIN1_XLFD_/$mono_latin1_xlfd/g;
      $ARG =~ s/_SERIF_LATIN1_XLFD_NO_SPACE_/$serif_latin1_xlfd_no_space/g;
      $ARG =~ s/_SERIF_LATIN1_FILE_/$serif_latin1_file/g;
      $ARG =~ s/_SERIF_LATIN1_XLFD_/$serif_latin1_xlfd/g;

      $ARG =~ s/_X11FONTDIR_/$x11fontdir/;
      $fontconfig_SuSE_properties_template .= $ARG;
    }
    close (TEMPLATE);
    for my $globpattern (@fontconfig_SuSE_properties_globs) {
      for my $file (glob ("$globpattern")) {
	if (-f "$file") {
	  if ($VERBOSITY >= $VERBOSITY_QUIET) {
	    printf "writing $file\n";
	  }
	  open (FONTPROP, ">$file") || die "can't open file $file: $!";
	  binmode FONTPROP, ":utf8";
	  print FONTPROP $fontconfig_SuSE_properties_template;
	  close (FONTPROP);
	}
      }
    }
  }

}

########################################################################

my @font_dirs = ();

sub font_dirs_setup_add_font_dir {
  my ($dir) = @_;

  while (-l "$dir") {
    $dir = readlink ("$dir");
  }
  if (! -d "$dir") {
    return;
  }
  if (grep (/$dir/, @font_dirs)) {
    return;
  }
  push (@font_dirs, ("$dir"));

  my $dir_handle;
  if (opendir ($dir_handle, $dir)) {
    my @entries = readdir ($dir_handle);
    for my $entry (@entries) {
      if ("$entry" =~ /\./ || "$entry" =~ /\.\./ || ! -d "$dir/$entry") {
	next;
      }
      font_dirs_setup_add_font_dir("$dir/$entry");
    }
    closedir $dir_handle;
  }
}

sub font_dirs_setup {
  my $suse_font_dirs_file = "/etc/fonts/suse-font-dirs.conf";
  my $suse_font_dirs_template_file = "/usr/share/fonts-config/suse-font-dirs.conf.template";
  my $suse_font_dirs = "";
  my $suse_font_dirs_template = "";

  if (-f "$suse_font_dirs_template_file") {
    open (TEMPLATE, "$suse_font_dirs_template_file") || die "can't open file $suse_font_dirs_template_file: $!";
    binmode TEMPLATE, ":utf8";
    while (<TEMPLATE>) {
      if ($ARG =~ /<dir>(.*)<\/dir>/) {
	for my $dir (glob ("$1")) {
	  if (-d "$dir") {
	    font_dirs_setup_add_font_dir ("$dir");
	  }
	}
      }
      elsif ($ARG =~ /<\/fontconfig>/) {
	for my $dir (@font_dirs) {
	  $suse_font_dirs_template .= "    <dir>$dir</dir>\n";
	}
	$suse_font_dirs_template .= "\n</fontconfig>\n";
      }
      else {
	$suse_font_dirs_template .= $ARG;
      }
    }
    close (TEMPLATE);
    if (open (CONF, "$suse_font_dirs_file")) {
      while (<CONF>) {
	$suse_font_dirs .= $ARG;
      }
      close (CONF);
    }
    if ("$suse_font_dirs_template" eq "$suse_font_dirs") {
      if ($VERBOSITY >= $VERBOSITY_QUIET) {
	printf "$suse_font_dirs_file unchanged\n";
      }
    } else {
      if ($VERBOSITY >= $VERBOSITY_QUIET) {
	printf "writing $suse_font_dirs_file\n";
      }
      open (CONF, ">$suse_font_dirs_file") || die "can't open file $suse_font_dirs_file: $!";
      print CONF $suse_font_dirs_template;
      close (CONF);
    }
  }
}
########################################################################
sub hinting_setup {
  my $suse_hinting_file = "/etc/fonts/conf.avail/12-suse-hinting-bc.conf";
  my $suse_hinting_template_file = "/usr/share/fonts-config/suse-hinting.conf.template";
  my $suse_hinting = "";
  my $suse_hinting_template = "";

  if (-f "$suse_hinting_template_file") {
    open (TEMPLATE, "$suse_hinting_template_file") || die "can't open file $suse_hinting_template_file: $!";
    binmode TEMPLATE, ":utf8";
    while (<TEMPLATE>) {
      $ARG =~ s/_BYTECODE_BW_MAX_PIXEL_/$OPT_BCBWMAX/;
      $suse_hinting_template .= $ARG;
    }
    close (TEMPLATE);
    if (open (CONF, "$suse_hinting_file")) {
      while (<CONF>) {
	$suse_hinting .= $ARG;
      }
      close (CONF);
    }
    if ("$suse_hinting_template" eq "$suse_hinting") {
      if ($VERBOSITY >= $VERBOSITY_QUIET) {
	printf "$suse_hinting_file unchanged\n";
      }
    } else {
      if ($VERBOSITY >= $VERBOSITY_QUIET) {
	printf "writing $suse_hinting_file\n";
      }
      open (CONF, ">$suse_hinting_file") || die "can't open file $suse_hinting_file: $!";
      print CONF $suse_hinting_template;
      close (CONF);
    }
  }
}

########################################################################
sub embedded_bitmap_setup {
  my $suse_bitmaps_file = "/etc/fonts/conf.avail/17-suse-bitmaps.conf";
  my $suse_bitmaps_template_file = "/usr/share/fonts-config/suse-bitmaps.conf.template";
  my $suse_bitmaps = "";
  my $suse_bitmaps_template = "";

  if (-f "$suse_bitmaps_template_file") {
    open (TEMPLATE, "$suse_bitmaps_template_file") || die "can't open file $suse_bitmaps_template_file: $!";
    binmode TEMPLATE, ":utf8";
    while (<TEMPLATE>) {
      if ($ARG !~ /_USE_EMBEDDED_BITMAPS_PLACEHOLDER_/) {
	$suse_bitmaps_template .= $ARG;
      }
      else {
	if (! $OPT_EBITMAP) {
	  $suse_bitmaps_template .= "	<match target=\"font\">\n";
	  $suse_bitmaps_template .= "		<edit name=\"embeddedbitmap\" mode=\"assign\">\n";
	  $suse_bitmaps_template .= "			<bool>false</bool>\n";
	  $suse_bitmaps_template .= "		</edit>\n";
	  $suse_bitmaps_template .= "	</match>\n";
	} elsif ($OPT_EBITMAP && $OPT_EBITMAP_LANG =~ /0/) {
	  $suse_bitmaps_template .= "	<match target=\"font\">\n";
	  $suse_bitmaps_template .= "		<edit name=\"embeddedbitmap\" mode=\"assign\">\n";
	  $suse_bitmaps_template .= "			<bool>true</bool>\n";
	  $suse_bitmaps_template .= "		</edit>\n";
	  $suse_bitmaps_template .= "	</match>\n";
	} else {
	  $suse_bitmaps_template .= "	<match target=\"font\">\n";
	  $suse_bitmaps_template .= "		<edit name=\"embeddedbitmap\" mode=\"assign\">\n";
	  $suse_bitmaps_template .= "			<bool>false</bool>\n";
	  $suse_bitmaps_template .= "		</edit>\n";
	  $suse_bitmaps_template .= "	</match>\n";
	  $suse_bitmaps_template .= "	<match target=\"font\">\n";
	  $suse_bitmaps_template .= "		<test name=\"lang\" compare=\"contains\">\n";
	  my @languages = split (":", "$OPT_EBITMAP_LANG");
	  for my $i (@languages) {
	    $suse_bitmaps_template .= "			<string>$i</string>\n";
	  }
	  $suse_bitmaps_template .= "		</test>\n";
	  $suse_bitmaps_template .= "		<edit name=\"embeddedbitmap\" mode=\"assign\">\n";
	  $suse_bitmaps_template .= "			<bool>true</bool>\n";
	  $suse_bitmaps_template .= "		</edit>\n";
	  $suse_bitmaps_template .= "	</match>\n";
	}
      }
    }
    close (TEMPLATE);
    if (open (CONF, "$suse_bitmaps_file")) {
      while (<CONF>) {
	$suse_bitmaps .= $ARG;
      }
      close (CONF);
    }
    if ("$suse_bitmaps_template" eq "$suse_bitmaps") {
      if ($VERBOSITY >= $VERBOSITY_QUIET) {
	printf "$suse_bitmaps_file unchanged\n";
      }
    } else {
      if ($VERBOSITY >= $VERBOSITY_QUIET) {
	printf "writing $suse_bitmaps_file\n";
      }
      open (CONF, ">$suse_bitmaps_file") || die "can't open file $suse_bitmaps_file: $!";
      print CONF $suse_bitmaps_template;
      close (CONF);
    }
  }
}

########################################################################

# Returns true if the modification time of $f1 differs from
# the modification time of $f2 
sub mtime_differs {
  my ($f1,$f2) = @_;
  if ( -e $f1 && -e $f2) {
    my @f1s = stat ($f1);
    my @f2s = stat ($f2);
    return ($f1s[9] != $f2s[9]);
  } else {
    return 0;
  }
}

# Returns true if the modification time of $f1 differs from
# the modification time of $f2 or if one of the files is missing
sub mtime_differs_or_missing {
    my ($f1,$f2) = @_;
    if (! -e $f1 || ! -e $f2 || mtime_differs ($f1,$f2)) {
      return 1;
    } else {
      return 0;
    }
}  

# Returns true if $f1 is newer than $f2
sub newer {
  my ($f1,$f2) = @_;
  if ( -e $f1 && -e $f2) {
    my @f1s = stat ($f1);
    my @f2s = stat ($f2);
    return ($f1s[9] > $f2s[9]);
  } else {
    return 0;
  }
}

# Returns true if $f1 is newer than $f2 or if one of the files is missing
sub newer_or_missing {
    my ($f1,$f2) = @_;
    if (! -e $f1 || ! -e $f2 || newer ($f1,$f2)) {
      return 1;
    } else {
      return 0;
    }
}

sub my_system {
  my ($command) = @_;
  if ($VERBOSITY >= $VERBOSITY_DEBUG)  {
    print "executing: $command\n";
  }
  return system ($command);
}

sub search_executable {
  for my $file (@_) {
    if (-x $file) {
      return $file;
    }
  }
  return "";
}
openSUSE Build Service is sponsored by