postprocess/CustomTarget_images.mk |    2 
 solenv/bin/pack_images.py          |  590 -------------------------------------
 solenv/bin/packimages.pl           |  550 ++++++++++++++++++++++++++++++++++
 3 files changed, 551 insertions(+), 591 deletions(-)

New commits:
commit 138c1252787256a20807caacc9df2af7b19b245e
Author: Samuel Mehrbrodt <samuel.mehrbr...@cib.de>
Date:   Tue Sep 26 09:10:57 2017 +0200

    Revert "tdf#106894 Rewrite packimages.pl in Python (pack_images.py)"
    
    This broke the build: 
https://ci.libreoffice.org/job/lo_tb_master_win/14994/console
    
    This reverts commit 423ee1020afe1bca896f2ecfc67ffbd49db5081c.
    
    Change-Id: I05e6fd68f2bbec189236cbe265f6510731327997
    Reviewed-on: https://gerrit.libreoffice.org/42778
    Reviewed-by: Samuel Mehrbrodt <samuel.mehrbr...@cib.de>
    Tested-by: Samuel Mehrbrodt <samuel.mehrbr...@cib.de>

diff --git a/postprocess/CustomTarget_images.mk 
b/postprocess/CustomTarget_images.mk
index 603c73d522d4..44899e6105a5 100644
--- a/postprocess/CustomTarget_images.mk
+++ b/postprocess/CustomTarget_images.mk
@@ -33,7 +33,7 @@ $(packimages_DIR)/%.zip : \
        $(call gb_Output_announce,$(subst $(WORKDIR)/,,$@),$(true),PRL,2)
        $(call gb_Helper_abbreviate_dirs, \
                ILSTFILE=$(call var2file,$(shell $(gb_MKTEMP)),100,$(filter 
%.ilst,$^)) && \
-               $(PYTHON) $(SRCDIR)/solenv/bin/pack_images.py \
+               $(PERL) $(SRCDIR)/solenv/bin/packimages.pl \
                        $(if $(DEFAULT_THEME),\
                                -g $(packimages_DIR) -m $(packimages_DIR) -c 
$(packimages_DIR),\
                                -g $(SRCDIR)/icon-themes/$(subst images_,,$*) 
-m $(SRCDIR)/icon-themes/$(subst images_,,$*) -c $(SRCDIR)/icon-themes/$(subst 
images_,,$*) \
diff --git a/solenv/bin/pack_images.py b/solenv/bin/pack_images.py
deleted file mode 100755
index 2e43c1815de4..000000000000
--- a/solenv/bin/pack_images.py
+++ /dev/null
@@ -1,590 +0,0 @@
-#!/usr/bin/env python2
-# -*- coding: utf-8 -*-
-# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*-
-#
-# This file is part of the LibreOffice project.
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-#
-""" Pack images into archives. """
-
-from __future__ import with_statement
-
-import argparse
-from collections import OrderedDict
-import logging
-import os
-import shutil
-import sys
-import tempfile
-import zipfile
-
-
-logging.basicConfig(format='%(message)s')
-LOGGER = logging.getLogger()
-
-
-def main(args):
-    """ Main function. """
-
-    tmp_output_file = "%s.%d.tmp" % (args.output_file, os.getpid())
-
-    # Sanity checks
-    if not os.path.exists(args.imagelist_file):
-        LOGGER.error("imagelist_file '%s' doesn't exists", args.imagelist_file)
-        sys.exit(2)
-
-    out_path = os.path.dirname(args.output_file)
-    for path in (out_path, args.global_path, args.module_path):
-        if not os.path.exists(path):
-            LOGGER.error("Path '%s' doesn't exists", path)
-            sys.exit(2)
-        if not os.access(path, os.X_OK):
-            LOGGER.error("Unable to search path %s", path)
-            sys.exit(2)
-
-    if not os.access(out_path, os.W_OK):
-        LOGGER.error("Unable to write into path %s", out_path)
-
-    custom_paths = []
-    for path in args.custom_path:
-        if not os.path.exists(path):
-            LOGGER.warning("Skipping non-existing path: %s", path)
-            continue
-        elif not os.access(path, os.X_OK):
-            LOGGER.error("Unable to search path %s", path)
-            sys.exit(2)
-            continue
-
-        custom_paths.append(path)
-
-    imagelist_filenames = get_imagelist_filenames(args.imagelist_file)
-    global_image_list, module_image_list = 
parse_image_list(imagelist_filenames)
-    custom_image_list = find_custom(custom_paths)
-
-    links = {}
-    read_links(links, ARGS.global_path)
-    for path in custom_paths:
-        read_links(links, path)
-    check_links(links)
-
-    zip_list = create_zip_list(global_image_list, module_image_list, 
custom_image_list,
-                               args.global_path, args.module_path)
-    remove_links_from_zip_list(zip_list, links)
-
-    if check_rebuild(args.output_file, imagelist_filenames, custom_paths, 
zip_list):
-        tmp_dir = copy_images(zip_list)
-        create_zip_archive(zip_list, links, tmp_dir, tmp_output_file, 
args.sort_file)
-
-        replace_zip_file(tmp_output_file, args.output_file)
-        try:
-            LOGGER.info("Remove temporary directory %s", tmp_dir)
-            shutil.rmtree(tmp_dir)
-        except Exception as e:
-            LOGGER.error("Unable to remove temporary directory %s", tmp_dir)
-            sys.exit(2)
-    else:
-        LOGGER.info("No rebuild needed. %s is up to date.", args.output_file)
-
-
-def check_rebuild(zip_file, imagelist_filenames, custom_paths, zip_list):
-    """ Check if a rebuild is needed.
-
-    :type zip_file: str
-    :param zip_file: File to check against (use st_mtime).
-
-    :type imagelist_filenames: dict
-    :param imagelist_filenames: List of imagelist filename.
-
-    :type custom_paths: list
-    :param custom_paths: List of paths to use with links.txt files.
-
-    :type zip_list: dict
-    :param zip_list: List of filenames to create the zip archive.
-
-    :rtype: bool
-    :return: True if rebuild is needed and False if not.
-    """
-
-    if not os.path.exists(zip_file):
-        return True
-
-    zip_file_stat = os.stat(zip_file)
-
-    def compare_modtime(filenames):
-        """ Check if modification time of zip archive is older the provided
-        list of files.
-
-        :type filenames: dict
-        :param filesnames: List of filenames to check against.
-
-        :rtype: bool
-        :return: True if zip archive is older and False if not.
-        """
-        for path, filename in filenames.iteritems():
-            filename = os.path.join(path, filename) if filename else path
-            if zip_file_stat.st_mtime < os.stat(filename).st_mtime:
-                return True
-        return False
-
-    if compare_modtime(imagelist_filenames):
-        return True
-    if compare_modtime(zip_list):
-        return True
-    for path in custom_paths:
-        link_file = os.path.join(path, 'links.txt')
-        if os.path.exists(link_file):
-            if zip_file_stat.st_mtime < os.stat(link_file).st_mtime:
-                return True
-
-    return False
-
-
-def replace_zip_file(src, dst):
-    """ Replace the old archive file with the newly created one.
-
-    :type src: str
-    :param src: Source file name.
-
-    :type dst: str
-    :param dst: Destination file name.
-    """
-    LOGGER.info("Replace old zip archive with new archive")
-    if os.path.exists(dst):
-        try:
-            os.unlink(dst)
-        except OSError as e:
-            if os.path.exists(src):
-                os.unlink(src)
-
-            LOGGER.error("Unable to unlink old archive '%s'", dst)
-            LOGGER.error(str(e))
-            sys.exit(1)
-
-    try:
-        LOGGER.info("Copy archive '%s' to '%s'", src, dst)
-        shutil.copyfile(src, dst)
-    except (shutil.SameFileError, OSError) as e:
-        os.unlink(src)
-        LOGGER.error("Cannot copy file '%s' to %s: %s", src, dst, str(e))
-        sys.exit(1)
-
-
-def optimize_zip_layout(zip_list, sort_file=None):
-    """ Optimzie the zip layout by ordering the list of filename alphabetically
-    or with provided sort file (can be partly).
-
-    :type zip_list: dict
-    :param zip_list: List of files to include in the zip archive.
-
-    :type sort_file: str
-    :param sort_file: Path/Filename to file with sort order.
-
-    :rtype: OrderedDict
-    :return: Dict with right sort order.
-    """
-    if sort_file is None:
-        LOGGER.info("No sort file provided")
-        LOGGER.info("Sorting entries alphabetically")
-
-        return OrderedDict(sorted(zip_list.items(), key=lambda t: t[0]))
-
-    LOGGER.info("Sort entries from file '%s'", sort_file)
-    sorted_zip_list = OrderedDict()
-    try:
-        fh = open(sort_file)
-    except IOError:
-        LOGGER.error("Cannot open file %s", sort_file)
-        sys.exit(1)
-    else:
-        with fh:
-            for line in fh:
-                line = line.strip()
-                if line == '' or line.startswith('#'):
-                    continue
-
-                if line in zip_list:
-                    sorted_zip_list[line] = ''
-                else:
-                    LOGGER.warning("Unknown file '%s'", line)
-
-    for key in sorted(zip_list.keys()):
-        if key not in sorted_zip_list:
-            sorted_zip_list[key] = ''
-
-    return sorted_zip_list
-
-
-def create_zip_archive(zip_list, links, tmp_dir, tmp_zip_file, sort_file=None):
-    """ Create the zip archive.
-
-    :type zip_list: dict
-    :param zip_list: All filenames to be included in the archive.
-
-    :type links: dict
-    :param links: All filenames to create links.txt file.
-
-    :type tmp_dir: str
-    :param tmp_dir: Path to tempory saved files.
-
-    :type tmp_zip_file: str
-    :param tmp_zip_file: Filename/Path of temporary zip archive.
-
-    :type sort_file: str|None
-    :param sort_file: Optional filename with sort order to apply.
-    """
-    LOGGER.info("Creating image archive")
-
-    old_pwd = os.getcwd()
-    os.chdir(tmp_dir)
-
-    ordered_zip_list = optimize_zip_layout(zip_list, sort_file)
-
-    with zipfile.ZipFile(tmp_zip_file, 'w') as tmp_zip:
-        if links.keys():
-            LOGGER.info("Add file 'links.txt' to zip archive")
-            create_links_file(tmp_dir, links)
-            tmp_zip.write('links.txt')
-
-        for link in ordered_zip_list:
-            LOGGER.info("Add file '%s' from path '%s' to zip archive", link, 
tmp_dir)
-            try:
-                tmp_zip.write(link)
-            except OSError:
-                LOGGER.warning("Unable to add file '%s' to zip archive", link)
-
-    os.chdir(old_pwd)
-
-
-def create_links_file(path, links):
-    """ Create file links.txt. Contains all links.
-
-    :type path: str
-    :param path: Path where to create the file.
-
-    :type links: dict
-    :param links: Dict with links (source -> target).
-    """
-    LOGGER.info("Create file links.txt")
-
-    try:
-        filename = os.path.join(path, 'links.txt')
-        fh = open(filename, 'w')
-    except IOError:
-        LOGGER.error("Cannot open file %s", filename)
-        sys.exit(1)
-    else:
-        with fh:
-            for key in sorted(links.keys()):
-                fh.write("%s %s\n" % (key, links[key]))
-
-
-def copy_images(zip_list):
-    """ Create a temporary directory and copy images to that directory.
-
-    :type zip_list: dict
-    :param zip_list: Dict with all files.
-
-    :rtype: str
-    :return: Path of the temporary directory.
-    """
-    LOGGER.info("Copy image files to temporary directory")
-
-    tmp_dir = tempfile.mkdtemp()
-    for key, value in zip_list.items():
-        path = os.path.join(value, key)
-        out_path = os.path.join(tmp_dir, key)
-
-        LOGGER.debug("Copying '%s' to '%s'", path, out_path)
-        if os.path.exists(path):
-            dirname = os.path.dirname(out_path)
-            if not os.path.exists(dirname):
-                os.makedirs(dirname)
-
-            try:
-                shutil.copyfile(path, out_path)
-            except (shutil.SameFileError, OSError) as e:
-                LOGGER.error("Cannot add file '%s' to image dir: %s", path, 
str(e))
-                sys.exit(1)
-
-    LOGGER.debug("Temporary directory '%s'", tmp_dir)
-    return tmp_dir
-
-
-def remove_links_from_zip_list(zip_list, links):
-    """ Remove any files from zip list that are linked.
-
-    :type zip_list: dict
-    :param zip_list: Files to include in the zip archive.
-
-    :type links: dict
-    :param links: Images which are linked.
-    """
-    LOGGER.info("Remove linked files from zip list")
-
-    for link in links.keys():
-        if link in zip_list:
-            del zip_list[link]
-
-    LOGGER.debug("Cleaned zip list:\n%s", "\n".join(zip_list))
-
-
-def create_zip_list(global_image_list, module_image_list, custom_image_list, 
global_path, module_path):
-    """ Create list of images for the zip archive.
-
-    :type global_image_list: dict
-    :param global_image_list: Global images.
-
-    :type module_image_list: dict
-    :param module_image_list: Module images.
-
-    :type custom_image_list: dict
-    :param custom_image_list: Custom images.
-
-    :type global_path: str
-    :param global_path: Global path (use when no custom path is available).
-
-    :type module_path: str
-    :param module_path: Module path (use when no custom path is available).
-
-    :rtype: dict
-    :return List of images to include in zip archive.
-    """
-    LOGGER.info("Assemble image list")
-
-    zip_list = {}
-    duplicates = []
-
-    for key in global_image_list.keys():
-        if key in module_image_list:
-            duplicates.append(key)
-            continue
-
-        if key in custom_image_list:
-            zip_list[key] = custom_image_list[key]
-            continue
-
-        zip_list[key] = global_path
-
-    for key in module_image_list.keys():
-        if key in custom_image_list:
-            zip_list[key] = custom_image_list[key]
-            continue
-
-        zip_list[key] = module_path
-
-    if duplicates:
-        LOGGER.warning("Found duplicate entries in 'global' and 'module' list")
-        LOGGER.warning("\n".join(duplicates))
-
-    LOGGER.debug("Assembled image list:\n%s", "\n".join(zip_list))
-    return zip_list
-
-
-def check_links(links):
-    """ Check that no link points to another link.
-
-    :type links: dict
-    :param links: Links to icons
-    """
-
-    stop = False
-
-    for link, target in links.items():
-        if target in links.keys():
-            LOGGER.error("Link %s -> %s -> %s", link, target, links[target])
-            stop = True
-
-    if stop:
-        LOGGER.error("Some icons in links.txt were found to link to other 
linked icons")
-        sys.exit(1)
-
-
-def read_links(links, path):
-    """ Read links from file.
-
-    :type links: dict
-    :param links: Hash to store all links
-
-    :type path: str
-    :param path: Path to use
-    """
-
-    filename = os.path.join(path, "links.txt")
-    LOGGER.info("Read links from file '%s'", filename)
-    if not os.path.isfile(filename):
-        LOGGER.info("No file to read")
-        return
-
-    try:
-        fh = open(filename)
-    except IOError:
-        LOGGER.error("Cannot open file %s", filename)
-        sys.exit(1)
-    else:
-        with fh:
-            for line in fh:
-                line = line.strip()
-                if line == '' or line.startswith('#'):
-                    continue
-
-                tmp = line.split(' ')
-                if len(tmp) == 2:
-                    links[tmp[0]] = tmp[1]
-                else:
-                    LOGGER.error("Malformed links line '%s' in file %s", line, 
filename)
-                    sys.exit(1)
-
-    LOGGER.debug("Read links:\n%s", "\n".join(links))
-
-
-def find_custom(custom_paths):
-    """ Find all custom images.
-
-    :type custom_paths: list
-    :param custom_paths: List of custom paths to search within.
-
-    :rtype: dict
-    :return: List of all custom images.
-    """
-    LOGGER.info("Find all images in custom paths")
-
-    custom_image_list = {}
-    for path in custom_paths:
-        # find all png files under the path including subdirs
-        custom_files = [val for sublist in [
-            [os.path.join(i[0], j) for j in i[2]
-             if j.endswith('.png') and os.path.isfile(os.path.join(i[0], j))]
-            for i in os.walk(path)] for val in sublist]
-
-        for filename in custom_files:
-            if filename.startswith(path):
-                key = filename.replace(os.path.join(path, ''), '')
-                if key not in custom_image_list:
-                    custom_image_list[key] = path
-
-    LOGGER.debug("Custom image list:\n%s", "\n".join(custom_image_list.keys()))
-    return custom_image_list
-
-
-def parse_image_list(imagelist_filenames):
-    """ Parse and extract filename from the imagelist files.
-
-    :type imagelist_filenames: list
-    :param imagelist_filenames: List of imagelist files.
-
-    :rtype: tuple
-    :return: Tuple with dicts containing the global or module image list.
-    """
-
-    global_image_list = {}
-    module_image_list = {}
-    for imagelist_filename in imagelist_filenames.keys():
-        LOGGER.info("Parsing '%s'", imagelist_filename)
-
-        try:
-            fh = open(imagelist_filename)
-        except IOError as e:
-            LOGGER.error("Cannot open imagelist file %s", imagelist_filename)
-            sys.exit(2)
-        else:
-            line_count = 0
-            with fh:
-                for line in fh:
-                    line_count += 1
-                    line = line.strip()
-                    if line == '' or line.startswith('#'):
-                        continue
-                    # clean up backslashes and double slashes
-                    line = line.replace('\\', '/')
-                    line = line.replace('//', '/')
-
-                    if line.startswith('%GLOBALRES%'):
-                        key = "res/%s" % line.replace('%GLOBALRES%/', '')
-                        if key in global_image_list:
-                            global_image_list[key] += 1
-                        else:
-                            global_image_list[key] = 0
-                        continue
-
-                    if line.startswith('%MODULE%'):
-                        key = line.replace('%MODULE%/', '')
-                        if key in global_image_list:
-                            module_image_list[key] += 1
-                        else:
-                            module_image_list[key] = 0
-                        continue
-
-                    LOGGER.error("Cannot parse line %s:%d", 
imagelist_filename, line_count)
-                    sys.exit(1)
-
-    LOGGER.debug("Global image list:\n%s", "\n".join(global_image_list))
-    LOGGER.debug("Module image list:\n%s", "\n".join(module_image_list))
-    return global_image_list, module_image_list
-
-
-def get_imagelist_filenames(filename):
-    """ Extract a list of imagelist filenames.
-
-    :type filename: str
-    :param filename: Name of file from extracting the list.
-
-    :rtype: dict
-    :return: List of imagelist filenames.
-    """
-    LOGGER.info("Extract the imagelist filenames")
-
-    imagelist_filenames = {}
-    try:
-        fh = open(filename)
-    except IOError:
-        LOGGER.error("Cannot open imagelist file %s", filename)
-        sys.exit(1)
-    else:
-        with fh:
-            for line in fh:
-                line = line.strip()
-                if not line or line == '':
-                    continue
-
-                for line_split in line.split(' '):
-                    line_split.strip()
-                    imagelist_filenames[line_split] = ''
-
-    LOGGER.debug("Extraced imagelist filenames:\n%s", 
"\n".join(imagelist_filenames.keys()))
-    return imagelist_filenames
-
-
-if __name__ == '__main__':
-    parser = argparse.ArgumentParser("Pack images into archives")
-    parser.add_argument('-o', '--output-file', dest='output_file',
-                        action='store', required=True,
-                        help='path to output archive')
-    parser.add_argument('-l', '--imagelist-file', dest='imagelist_file',
-                        action='store', required=True,
-                        help='file containing list of image list file')
-    parser.add_argument('-s', '--sort-file', dest='sort_file',
-                        action='store', required=True, default=None,
-                        help='image sort order file')
-    parser.add_argument('-c', '--custom-path', dest='custom_path',
-                        action='append', required=True,
-                        help='path to custom path directory')
-    parser.add_argument('-g', '--global-path', dest='global_path',
-                        action='store', required=True,
-                        help='path to global images directory')
-    parser.add_argument('-m', '--module-path', dest='module_path',
-                        action='store', required=True,
-                        help='path to module images directory')
-    parser.add_argument('-v', '--verbose', dest='verbose',
-                        action='count', default=0,
-                        help='set the verbosity (can be used multiple times)')
-
-    ARGS = parser.parse_args()
-    LOGGER.setLevel(logging.ERROR - (10 * ARGS.verbose if ARGS.verbose <= 3 
else 3))
-
-    main(ARGS)
-    sys.exit(0)
-
-# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/solenv/bin/packimages.pl b/solenv/bin/packimages.pl
new file mode 100755
index 000000000000..96a31de3b2fd
--- /dev/null
+++ b/solenv/bin/packimages.pl
@@ -0,0 +1,550 @@
+:
+eval 'exec perl -wS $0 ${1+"$@"}'
+    if 0;
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+# This file incorporates work covered by the following license notice:
+#
+#   Licensed to the Apache Software Foundation (ASF) under one or more
+#   contributor license agreements. See the NOTICE file distributed
+#   with this work for additional information regarding copyright
+#   ownership. The ASF licenses this file to you under the Apache
+#   License, Version 2.0 (the "License"); you may not use this file
+#   except in compliance with the License. You may obtain a copy of
+#   the License at http://www.apache.org/licenses/LICENSE-2.0 .
+#
+
+#
+# packimages.pl - pack images into archives
+#
+
+use strict;
+use Getopt::Long;
+use File::Find;
+use File::Basename;
+use File::Copy qw(copy);
+use File::Path qw(make_path rmtree);
+require File::Temp;
+use File::Temp qw(tempdir);
+
+#### globals ####
+
+my $img_global = '%GLOBALRES%';  # 'global' image prefix
+my $img_module = '%MODULE%';  # 'module' image prefix
+
+my $out_file;                # path to output archive
+my $tmp_out_file;            # path to temporary output file
+my $global_path;             # path to global images directory
+my $module_path;             # path to module images directory
+my $sort_file;               # path to file containing sorting data
+my @custom_path;             # path to custom images directory
+my $imagelist_file;          # file containing list of image list files
+my $verbose;                 # be verbose
+my $extra_verbose;           # be extra verbose
+my $do_rebuild = 0;          # is rebuilding zipfile required?
+
+my @custom_list;
+#### script id #####
+
+( my $script_name = $0 ) =~ s/^.*\b(\w+)\.pl$/$1/;
+
+print "$script_name -- version: 1.17\n" if $verbose;
+
+#### main #####
+
+parse_options();
+my $image_lists_ref = get_image_lists();
+my %image_lists_hash;
+foreach ( @{$image_lists_ref} ) {
+    $image_lists_hash{$_}="";
+}
+$do_rebuild = is_file_newer(\%image_lists_hash) if $do_rebuild == 0;
+my ($global_hash_ref, $module_hash_ref, $custom_hash_ref) = 
iterate_image_lists($image_lists_ref);
+# custom_hash filled from filesystem lookup
+find_custom($custom_hash_ref);
+
+# build a consolidated set of links
+my %links;
+read_links(\%links, $global_path);
+for my $path (@custom_path) {
+    read_links(\%links, $path);
+}
+check_links(\%links);
+
+# rebuild if links.txt has been modified
+for my $path (@custom_path) {
+    my $links_file = $path."/links.txt";
+    if ((-e $links_file ) && ( -e $out_file )){
+        if ((stat($out_file))[9] < (stat($links_file))[9]){
+            $do_rebuild = 1;
+            print_message("$links_file has been modified.") if $verbose;
+        }
+    }
+}
+
+my $zip_hash_ref = create_zip_list($global_hash_ref, $module_hash_ref, 
$custom_hash_ref);
+
+remove_links_from_zip_list($zip_hash_ref, \%links);
+
+$do_rebuild = is_file_newer($zip_hash_ref) if $do_rebuild == 0;
+if ( $do_rebuild == 1 ) {
+    my $tmpdir = copy_images($zip_hash_ref);
+    create_zip_archive($zip_hash_ref, \%links, $tmpdir);
+    replace_file($tmp_out_file, $out_file);
+    print_message("packing  $out_file finished.") if $verbose;
+
+    rmtree($tmpdir);
+    print_error("failed to delete $tmpdir") if -e $tmpdir;
+} else {
+    print_message("$out_file up to date. nothing to do.") if $verbose;
+}
+
+exit(0);
+
+#### subroutines ####
+
+sub parse_options
+{
+    my $opt_help;
+    my $p = Getopt::Long::Parser->new();
+    my @custom_path_list;
+    my $success =$p->getoptions(
+                             '-h' => \$opt_help,
+                             '-o=s' => \$out_file,
+                             '-g=s' => \$global_path,
+                 '-s=s' => \$sort_file,
+                             '-m=s' => \$module_path,
+                             '-c=s' => \@custom_path_list,
+                             '-l=s' => \$imagelist_file,
+                             '-v'   => \$verbose,
+                             '-vv'  => \$extra_verbose
+                            );
+    if ( $opt_help || !$success || !$out_file || !$global_path
+        || !$module_path || !@custom_path_list || !$imagelist_file )
+    {
+        usage();
+        exit(1);
+    }
+    #define intermediate output file
+    $tmp_out_file="$out_file"."$$".".tmp";
+    # Sanity checks.
+
+    # Check if out_file can be written.
+    my $out_dir = dirname($out_file);
+
+    # Check paths.
+    print_error("no such file '$_'", 2) if ! -f $imagelist_file;
+
+    my @check_directories = ($out_dir, $global_path, $module_path);
+
+    foreach (@check_directories) {
+        print_error("no such directory: '$_'", 2) if ! -d $_;
+        print_error("can't search directory: '$_'", 2) if ! -x $_;
+    }
+    print_error("directory is not writable: '$out_dir'", 2) if ! -w $out_dir;
+
+    # Use just the working paths
+    @custom_path = ();
+    foreach (@custom_path_list) {
+        if ( ! -d $_ ) {
+            print_warning("skipping non-existing directory: '$_'", 2);
+        }
+        elsif ( ! -x $_ ) {
+            print_error("can't search directory: '$_'", 2);
+        }
+        else {
+            push @custom_path, $_;
+        }
+    }
+}
+
+sub get_image_lists
+{
+    my @image_lists;
+
+    open (my $fh, $imagelist_file) or die "cannot open imagelist file 
$imagelist_file\n";
+    while (<$fh>) {
+        chomp;
+        next if /^\s*$/;
+        my @ilsts = split ' ';
+        push @image_lists, @ilsts;
+    }
+    close $fh;
+
+    return wantarray ? @image_lists : \@image_lists;
+}
+
+sub iterate_image_lists
+{
+    my $image_lists_ref = shift;
+
+    my %global_hash;
+    my %module_hash;
+    my %custom_hash;
+    my %help_hash;
+
+    foreach my $i ( @{$image_lists_ref} ) {
+        parse_image_list($i, \%global_hash, \%module_hash, \%custom_hash, 
\%help_hash);
+    }
+
+    return (\%global_hash, \%module_hash, \%custom_hash, \%help_hash);
+}
+
+sub parse_image_list
+{
+    my $image_list      = shift;
+    my $global_hash_ref = shift;
+    my $module_hash_ref = shift;
+    my $custom_hash_ref = shift;
+
+    print_message("parsing '$image_list' ...") if $verbose;
+    my $linecount = 0;
+    open(IMAGE_LIST, "< $image_list") or die "ERROR: can't open $image_list: 
$!";
+    while ( <IMAGE_LIST> ) {
+        $linecount++;
+        next if /^\s*#/;
+        next if /^\s*$/;
+        # clean up trailing whitespace
+        tr/\r\n//d;
+        s/\s+$//;
+        # clean up backslashes and double slashes
+        tr{\\}{/}s;
+        tr{/}{}s;
+        # hack "res" back into globals
+        if ( /^\Q$img_global\E\/(.*)$/o ) {
+            $global_hash_ref->{"res/".$1}++;
+            next;
+        }
+        if ( /^\Q$img_module\E\/(.*)$/o ) {
+            $module_hash_ref->{$1}++;
+            next;
+        }
+        # parse failed if we reach this point, bail out
+        close(IMAGE_LIST);
+        print_error("can't parse line $linecount from file '$image_list'", 4);
+    }
+    close(IMAGE_LIST);
+
+    return ($global_hash_ref, $module_hash_ref, $custom_hash_ref);
+}
+
+sub find_custom
+{
+    my $custom_hash_ref = shift;
+    my $keep_back;
+    for my $path (@custom_path) {
+    find({ wanted => \&wanted, no_chdir => 0 }, $path);
+    foreach ( @custom_list ) {
+        if ( /^\Q$path\E\/(.*)$/ ) {
+        $keep_back=$1;
+        if (!defined $custom_hash_ref->{$keep_back}) {
+            $custom_hash_ref->{$keep_back} = $path;
+        }
+        }
+    }
+    }
+}
+
+sub wanted
+{
+    my $file = $_;
+
+    if ( $file =~ /.*\.png$/ && -f $file ) {
+        push @custom_list, $File::Find::name;
+    }
+}
+
+sub create_zip_list
+{
+    my $global_hash_ref = shift;
+    my $module_hash_ref = shift;
+    my $custom_hash_ref = shift;
+
+    my %zip_hash;
+    my @warn_list;
+
+    print_message("assemble image list ...") if $verbose;
+    foreach ( keys %{$global_hash_ref} ) {
+        # check if in 'global' and in 'module' list and add to warn list
+        if ( exists $module_hash_ref->{$_} ) {
+            push(@warn_list, $_);
+            next;
+        }
+        if ( exists $custom_hash_ref->{$_} ) {
+            $zip_hash{$_} = $custom_hash_ref->{$_};
+            next;
+        }
+        # it's neither in 'module' nor 'custom', record it in zip hash
+        $zip_hash{$_} = $global_path;
+    }
+    foreach ( keys %{$module_hash_ref} ) {
+        if ( exists $custom_hash_ref->{$_} ) {
+            $zip_hash{$_} = $custom_hash_ref->{$_};
+            next;
+        }
+        # it's not in 'custom', record it in zip hash
+        $zip_hash{$_} = $module_path;
+    }
+
+    if ( @warn_list ) {
+        foreach ( @warn_list ) {
+            print_warning("$_ is duplicated in 'global' and 'module' list");
+        }
+    }
+
+    return \%zip_hash
+}
+
+sub is_file_newer
+{
+    my $test_hash_ref = shift;
+    my $reference_stamp = 0;
+
+    print_message("checking timestamps ...") if $verbose;
+    if ( -e $out_file ) {
+        $reference_stamp = (stat($out_file))[9];
+        print_message("found $out_file with $reference_stamp ...") if $verbose;
+    }
+    return 1 if $reference_stamp == 0;
+
+    foreach ( sort keys %{$test_hash_ref} ) {
+        my $path = $test_hash_ref->{$_};
+        $path .= "/" if "$path" ne "";
+        $path .= "$_";
+        print_message("checking '$path' ...") if $extra_verbose;
+        my $mtime = (stat($path))[9];
+        return 1 if $reference_stamp < $mtime;
+    }
+    return 0;
+}
+
+sub optimize_zip_layout($)
+{
+    my $zip_hash_ref = shift;
+
+    if (!defined $sort_file) {
+    print_message("no sort file - sorting alphabetically ...") if $verbose;
+    return sort keys %{$zip_hash_ref};
+    }
+    print_message("sorting from $sort_file ...") if $verbose;
+
+    my $orderh;
+    my %included;
+    my @sorted;
+    open ($orderh, $sort_file) || die "Can't open $sort_file: $!";
+    while (<$orderh>) {
+    /^\#.*/ && next; # comments
+    s/[\r\n]*$//;
+    /^\s*$/ && next;
+    my $file = $_;
+    if (!defined $zip_hash_ref->{$file}) {
+        print "unknown file '$file'\n" if ($extra_verbose);
+    } else {
+        push @sorted, $file;
+        $included{$file} = 1;
+    }
+    }
+    close ($orderh);
+
+    for my $img (sort keys %{$zip_hash_ref}) {
+    push @sorted, $img if (!$included{$img});
+    }
+
+    print_message("done sort ...") if $verbose;
+
+    return @sorted;
+}
+
+sub copy_images($)
+{
+    my ($zip_hash_ref) = @_;
+    my $dir = tempdir();
+    foreach (keys %$zip_hash_ref) {
+        my $path = $zip_hash_ref->{$_} . "/$_";
+        my $outpath = $dir . "/$_";
+        print_message("copying '$path' to '$outpath' ...") if $extra_verbose;
+        if ( -e $path) {
+            my $dirname = dirname($outpath);
+            if (!-d $dirname) {
+                make_path($dirname);
+            }
+            copy($path, $outpath)
+                or print_error("can't add file '$path' to image dir: $!", 5);
+        }
+    }
+    return $dir;
+}
+
+sub create_zip_archive($$$)
+{
+    my ($zip_hash_ref, $links_hash_ref, $image_dir_ref) = @_;
+
+    print_message("creating image archive ...") if $verbose;
+
+    chdir $image_dir_ref;
+
+    if (keys %{$links_hash_ref}) {
+        write_links($links_hash_ref, $image_dir_ref);
+        system "zip $tmp_out_file links.txt";
+            # print_error("failed to add links file: $!", 5);
+    }
+
+    my @sorted_list = optimize_zip_layout($zip_hash_ref);
+    my $sorted_file = File::Temp->new();
+    foreach my $item (@sorted_list) {
+        print $sorted_file "$item\n";
+    }
+    binmode $sorted_file; # flush
+
+    system "cat $sorted_file | zip -0 -@ $tmp_out_file";
+        # print_error("write image zip archive '$tmp_out_file' failed. Reason: 
$!", 6);
+    chdir; # just go out of the temp dir
+}
+
+sub replace_file
+{
+    my $source_file = shift;
+    my $dest_file = shift;
+    my $result = 0;
+
+    $result = unlink($dest_file) if -f $dest_file;
+    if ( $result != 1 && -f $dest_file ) {
+        unlink $source_file;
+        print_error("couldn't remove '$dest_file'",1);
+    }  else {
+        if ( !rename($source_file, $dest_file)) {
+            unlink $source_file;
+            print_error("couldn't rename '$source_file'",1);
+        }
+    }
+    return;
+}
+
+sub usage
+{
+    print STDERR "Usage: packimages.pl [-h] -o out_file -g g_path -m m_path -c 
c_path -l imagelist_file\n";
+    print STDERR "Creates archive of images\n";
+    print STDERR "Options:\n";
+    print STDERR "    -h                 print this help\n";
+    print STDERR "    -o out_file        path to output archive\n";
+    print STDERR "    -g g_path          path to global images directory\n";
+    print STDERR "    -m m_path          path to module images directory\n";
+    print STDERR "    -c c_path          path to custom images directory\n";
+    print STDERR "    -s sort_file       path to image sort order file\n";
+    print STDERR "    -l imagelist_file  file containing list of image list 
files\n";
+    print STDERR "    -v                 verbose\n";
+    print STDERR "    -vv                very verbose\n";
+}
+
+sub print_message
+{
+    my $message     = shift;
+
+    print "$script_name: ";
+    print "$message\n";
+    return;
+}
+
+sub print_warning
+{
+    my $message     = shift;
+
+    print STDERR "$script_name: ";
+    print STDERR "WARNING $message\n";
+    return;
+}
+
+sub print_error
+{
+    my $message     = shift;
+    my $error_code  = shift;
+
+    print STDERR "$script_name: ";
+    print STDERR "ERROR: $message\n";
+
+    if ( $error_code ) {
+        print STDERR "\nFAILURE: $script_name aborted.\n";
+        exit($error_code);
+    }
+    return;
+}
+
+sub read_links($$)
+{
+    my $links = shift;
+    my $path = shift;
+
+    my $fname = "$path/links.txt";
+    if (!-f "$fname") {
+        return;
+    }
+
+    my $fh;
+    open ($fh, $fname) || die "Can't open: $fname: $!";
+    # Syntax of links file:
+    # # comment
+    # missing-image image-to-load-instead
+    while (<$fh>) {
+        my $line = $_;
+        $line =~ s/\r//g;   # DOS line-feeds
+        $line =~ s/\#.*$//; # kill comments
+        $line =~ m/^\s*$/ && next; # blank lines
+        if ($line =~ m/^([^\s]+)\s+(.*)$/) {
+            my ($missing, $replace) = ($1, $2);
+            # enter into hash, and overwrite previous layer if necessary
+            $links->{$1} = $2;
+        } else {
+            die "Malformed links line: '$line'\n";
+        }
+    }
+    close ($fh);
+}
+
+# write out the links
+sub write_links($$)
+{
+    my ($links, $out_dir_ref) = @_;
+    open (my $fh, ">", "$out_dir_ref/links.txt")
+        || die "can't create links.txt";
+    for my $missing (sort keys %{$links}) {
+        my $line = $missing . " " . $links->{$missing} . "\n";
+        print $fh $line;
+    }
+    close $fh;
+}
+
+# Ensure that no link points to another link
+sub check_links($)
+{
+    my $links = shift;
+    my $stop_die = 0;
+
+    for my $link (keys %{$links}) {
+        my $value = $links->{$link};
+        if (defined $links->{$value}) {
+            print STDERR "\nLink: $link -> $value -> " . $links->{$value};
+            $stop_die = 1;
+        }
+    }
+    if ( $stop_die ) {
+        die "\nSome icons in links.txt were found to link to other linked 
icons.\n\n";
+    }
+
+}
+
+# remove any files from our zip list that are linked
+sub remove_links_from_zip_list($$)
+{
+    my $zip_hash_ref = shift;
+    my $links = shift;
+    for my $link (keys %{$links}) {
+        if (defined $zip_hash_ref->{$link}) {
+            delete $zip_hash_ref->{$link};
+        }
+    }
+}
_______________________________________________
Libreoffice-commits mailing list
libreoffice-comm...@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits

Reply via email to