font_cache.sh   [plain text]


#!/bin/bash
# Copyright (c) 2008-2010 Apple Inc. All Rights Reserved.
#
# @APPLE_LICENSE_HEADER_START@
#
# This file contains Original Code and/or Modifications of Original Code
# as defined in and that are subject to the Apple Public Source License
# Version 2.0 (the 'License'). You may not use this file except in
# compliance with the License. Please obtain a copy of the License at
# http://www.opensource.apple.com/apsl/ and read it before using this
# file.
#
# The Original Code and all software distributed under the License are
# distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
# EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
# INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
# Please see the License for the specific language governing rights and
# limitations under the License.
#
# @APPLE_LICENSE_HEADER_END@

X11DIR=__PREFIX__
X11FONTDIR=${X11DIR}/share/fonts
FC_LOCKFILE=""

# Are we caching system fonts or user fonts?
system=0

# Are we including OSX font dirs ({/,~/,/System/}Library/Fonts)
osxfonts=1

# Do we want to force a recache?
force=0

# How noisy are we?
verbose=0

# Check if the data in the given directory is newer than its cache
check_dirty() {
    local dir=$1
    local fontfiles=""
    local retval=1

    # If the dir does not exist, we just exit
    if [[ ! -d "${dir}" ]]; then
        return 1
    fi

    # Create a list of all files in the dir
    # Filter out config / cache files.  Ugly... counting down the day until
    # xfs finally goes away
    fontfiles="$(find ${dir}/ -maxdepth 1 -type f | awk '$0 !~ /fonts\..*$|^.*\.dir$/ {print}')"

    # Fonts were deleted (or never there).  Kill off the caches
    if [[ -z "${fontfiles}" ]] ; then
        local f
        for f in "${dir}"/fonts.* "${dir}"/encodings.dir; do
            if [[ -f ${f} ]] ; then
                rm -f "${f}"
            fi
        done
        return 1
    fi

    # Force a recache
    if [[ ${force} == 1 ]] ; then
        retval=0
    fi

    # If we don't have our caches, we are dirty
    if [[ ! -f "${dir}/fonts.list" || ! -f "${dir}/fonts.dir" || ! -f "${dir}/encodings.dir" ]]; then
        retval=0
    fi

    # Check that no files were added or removed....
    if [[ "${retval}" -ne 0 && "$(cat ${dir}/fonts.list)" != "${fontfiles}" ]] ; then
        retval=0
    fi

    # Check that no files were updated....
    if [[ "${retval}" -ne 0 ]] ; then
        local changed="$(find ${dir}/ -type f -cnewer ${dir}/fonts.dir | awk '$0 !~ /fonts\..*$|^.*\.dir$/ {print}')"

        if [[ -n "${changed}" ]] ; then
            retval=0
        fi
    fi

    # Recreate fonts.list since something changed
    if [[ "${retval}" == 0 ]] ; then
        echo "${fontfiles}" > "${dir}"/fonts.list
    fi

    return ${retval}
}

get_fontdirs() {
    local d
    if [[ $system == 1 ]] ; then
        if [[ $osxfonts == 1 ]] ; then
            find {/System/,/}Library/Fonts -type d
        fi

        for d in "${X11FONTDIR}"/* ; do
            case ${d#${X11FONTDIR}/} in
                conf*|encodings*) ;;
                *) find "$d" -type d ;;
            esac
        done
    else 
	if [[ $osxfonts == 1 && -d "${HOME}/Library/Fonts" ]] ; then
            find "${HOME}/Library/Fonts" -type d
	fi

        if [[ -d "${HOME}/.fonts" ]] ; then
            find "${HOME}/.fonts" -type d
        fi
    fi
}

setup_fontdirs() {
    local x=""
    local fontdirs=""
    local changed="no"

    umask 022

    if [[ $system == 1 ]] ; then
        echo "font_cache: Scanning system font directories to generate X11 font caches"
    else
        echo "font_cache: Scanning user font directories to generate X11 font caches"
    fi

    # Generate the encodings.dir ...
    if [[ $system == 1 ]] ; then
        ${X11DIR}/bin/mkfontdir -n \
            -e ${X11FONTDIR}/encodings \
            -e ${X11FONTDIR}/encodings/large \
            -- ${X11FONTDIR}/encodings
    fi

    OIFS=$IFS
    IFS='
'
    for x in $(get_fontdirs) ; do
        if [[ -d "${x}" ]] && check_dirty "${x}" ; then
            if [[ -z "${fontdirs}" ]] ; then
                fontdirs="${x}"
            else
                fontdirs="${fontdirs}${IFS}${x}"
            fi
        fi
    done

    if [[ -n "${fontdirs}" ]] ; then
        echo "font_cache: Making fonts.dir for updated directories."
        for x in ${fontdirs} ; do
            if [[ $verbose == 1 ]] ; then
                echo "font_cache:    ${x}"
            fi

            # First, generate fonts.scale for scaleable fonts that might be there
            ${X11DIR}/bin/mkfontscale \
                -a ${X11FONTDIR}/encodings/encodings.dir \
                -- ${x}

            # Next, generate fonts.dir
            if [[ $verbose == 1 ]] ; then
                ${X11DIR}/bin/mkfontdir \
                    -e ${X11FONTDIR}/encodings \
                    -e ${X11FONTDIR}/encodings/large \
                    -- ${x}
            else
                ${X11DIR}/bin/mkfontdir \
                    -e ${X11FONTDIR}/encodings \
                    -e ${X11FONTDIR}/encodings/large \
                    -- ${x} > /dev/null
            fi
        done
    fi
    IFS=$OIFS

    # Finally, update fontconfig's cache
    echo "font_cache: Updating FC cache"
    if [[ $system == 1 ]] ; then
        ${X11DIR}/bin/fc-cache -s \
            $([[ $force == 1 ]] && echo "-f -r") \
            $([[ $verbose == 1 ]] && echo "-v")
    else
        ${X11DIR}/bin/fc-cache \
            $([[ $force == 1 ]] && echo "-f -r") \
            $([[ $verbose == 1 ]] && echo "-v")
    fi
    echo "font_cache: Done"
}

do_usage() {
    echo "font_cache [options]"
    echo "    -f, --force        : Force cache recreation"
    echo "    -n, --no-osxfonts  : Just cache X11 font directories"
    echo "                         (-n just pertains to XFont cache, not fontconfig)"
    echo "    -s, --system       : Cache system font dirs instead of user dirs"
    echo "    -v, --verbose      : Verbose Output"
}

cleanup() {
    [[ -r "${FC_LOCKFILE}" ]] && rm -f "${FC_LOCKFILE}"
    exit 1
}

while [[ $# -gt 0 ]] ; do
    case $1 in
        -s|--system) system=1 ;;
        -f|--force) force=1 ;;
        -v|--verbose) verbose=1 ;;
        -n|--no-osxfonts) osxfonts=0 ;;
        --help) do_usage ; exit 0 ;;
        *) do_usage ; exit 1 ;;
    esac
    shift
done

if [[ $system == 1 ]] ; then
    FC_LOCKFILE="${X11DIR}/var/run/font_cache.lock"
elif [[ -w "${TMPDIR}" ]] ; then
    FC_LOCKFILE="${TMPDIR}/font_cache.lock"
elif [[ -w "/tmp" ]] ; then
    FC_LOCKFILE="/tmp/font_cache.$UID.lock"
else
    FC_LOCKFILE="${HOME}/.font_cache.lock"
fi

if [[ -x /usr/bin/lockfile ]] ; then
    if /usr/bin/lockfile -r 0 -l 240 -s 4 -! "${FC_LOCKFILE}" ; then
            echo "font_cache is already running." >&2
            echo "If you believe this to be erroneous, please remove ${FC_LOCKFILE}." >&2
            exit 1
    fi
else
    if [[ -r "${FC_LOCKFILE}" ]] ; then
        read OLD_PID < "${FC_LOCKFILE}"
        if kill -0 ${OLD_PID} >& /dev/null ; then
            echo "font_cache is already running with PID ${OLD_PID}." >&2
            echo "If you believe this to be erroneous, please remove ${FC_LOCKFILE}." >&2
            exit 1
        fi

        echo "Removing stale ${FC_LOCKFILE}" >&2
        rm -f "${FC_LOCKFILE}"
    fi

    echo $$ > "${FC_LOCKFILE}"

    if [[ ! -r "${FC_LOCKFILE}" ]] ; then
        echo "Unable to write to ${FC_LOCKFILE}." >&2
        exit 1
    fi

    # Now make sure we didn't collide mid-air
    read OLD_PID < "${FC_LOCKFILE}"
    if [[ $$ != ${OLD_PID} ]] ; then
        echo "font_cache is already running with PID ${OLD_PID}." >&2
        exit 1
    fi
fi

trap cleanup SIGINT SIGQUIT SIGABRT SIGTERM

setup_fontdirs

rm -f "${FC_LOCKFILE}"