Revert "Merge changes from topic 'fsconfig-2'"

This reverts commit 1b2de51881e0ff76e2ad9d3d0405fc7185b0f86f, reversing
changes made to 77ea32f378651a3585e245eb964dab979d191310.
diff --git a/tools/fs_config/fs_config_generator.py b/tools/fs_config/fs_config_generator.py
index 8609657..6a16fea 100755
--- a/tools/fs_config/fs_config_generator.py
+++ b/tools/fs_config/fs_config_generator.py
@@ -1,1317 +1,294 @@
 #!/usr/bin/env python
-"""Generates config files for Android file system properties.
 
-This script is used for generating configuration files for configuring
-Android filesystem properties. Internally, its composed of a plug-able
-interface to support the understanding of new input and output parameters.
-
-Run the help for a list of supported plugins and their capabilities.
-
-Further documentation can be found in the README.
-"""
-
-import argparse
 import ConfigParser
 import re
 import sys
-import textwrap
-
-# Keep the tool in one file to make it easy to run.
-# pylint: disable=too-many-lines
 
 
-# Lowercase generator used to be inline with @staticmethod.
-class generator(object):  # pylint: disable=invalid-name
-    """A decorator class to add commandlet plugins.
+GENERATED = '''
+/*
+ * THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY
+ */
+'''
 
-    Used as a decorator to classes to add them to
-    the internal plugin interface. Plugins added
-    with @generator() are automatically added to
-    the command line.
+INCLUDE = '#include <private/android_filesystem_config.h>'
 
-    For instance, to add a new generator
-    called foo and have it added just do this:
+DEFINE_NO_DIRS = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS\n'
+DEFINE_NO_FILES = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES\n'
 
-        @generator("foo")
-        class FooGen(object):
-            ...
-    """
-    _generators = {}
+DEFAULT_WARNING = '#warning No device-supplied android_filesystem_config.h, using empty default.'
 
-    def __init__(self, gen):
-        """
-        Args:
-            gen (str): The name of the generator to add.
+NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY = '{ 00000, AID_ROOT,      AID_ROOT,      0, "system/etc/fs_config_dirs" },'
+NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES_ENTRY = '{ 00000, AID_ROOT,      AID_ROOT,      0, "system/etc/fs_config_files" },'
 
-        Raises:
-            ValueError: If there is a similarly named generator already added.
+IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS = '#ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS'
+ENDIF = '#endif'
 
-        """
-        self._gen = gen
+OPEN_FILE_STRUCT = 'static const struct fs_path_config android_device_files[] = {'
+OPEN_DIR_STRUCT = 'static const struct fs_path_config android_device_dirs[] = {'
+CLOSE_FILE_STRUCT = '};'
 
-        if gen in generator._generators:
-            raise ValueError('Duplicate generator name: ' + gen)
+GENERIC_DEFINE = "#define %s\t%s"
 
-        generator._generators[gen] = None
+FILE_COMMENT = '// Defined in file: \"%s\"'
 
-    def __call__(self, cls):
-
-        generator._generators[self._gen] = cls()
-        return cls
-
-    @staticmethod
-    def get():
-        """Gets the list of generators.
-
-        Returns:
-           The list of registered generators.
-        """
-        return generator._generators
+# from system/core/include/private/android_filesystem_config.h
+AID_OEM_RESERVED_RANGES = [
+    (2900, 2999),
+    (5000, 5999),
+]
 
 
-class Utils(object):
-    """Various assorted static utilities."""
+AID_MATCH = re.compile('AID_[a-zA-Z]+')
 
-    @staticmethod
-    def in_any_range(value, ranges):
-        """Tests if a value is in a list of given closed range tuples.
+def handle_aid(file_name, section_name, config, aids, seen_aids):
+    value = config.get(section_name, 'value')
 
-        A range tuple is a closed range. That means it's inclusive of its
-        start and ending values.
+    errmsg = '%s for: \"' + section_name + '" file: \"' + file_name + '\"'
 
-        Args:
-            value (int): The value to test.
-            range [(int, int)]: The closed range list to test value within.
+    if not value:
+        raise Exception(errmsg % 'Found specified but unset "value"')
 
-        Returns:
-            True if value is within the closed range, false otherwise.
-        """
+    v = convert_int(value)
+    if not v:
+        raise Exception(errmsg % ('Invalid "value", not a number, got: \"%s\"' % value))
 
-        return any(lower <= value <= upper for (lower, upper) in ranges)
+    # Values must be within OEM range
+    if not any(lower <= v <= upper for (lower, upper) in AID_OEM_RESERVED_RANGES):
+        s = '"value" not in valid range %s, got: %s'
+        s = s % (str(AID_OEM_RESERVED_RANGES), value)
+        raise Exception(errmsg % s)
 
-    @staticmethod
-    def get_login_and_uid_cleansed(aid):
-        """Returns a passwd/group file safe logon and uid.
+    # use the normalized int value in the dict and detect
+    # duplicate definitions of the same vallue
+    v = str(v)
+    if v in seen_aids[1]:
+        # map of value to aid name
+        a = seen_aids[1][v]
 
-        This checks that the logon and uid of the AID do not
-        contain the delimiter ":" for a passwd/group file.
+        # aid name to file
+        f = seen_aids[0][a]
 
-        Args:
-            aid (AID): The aid to check
+        s = 'Duplicate AID value "%s" found on AID: "%s".' % (value, seen_aids[1][v])
+        s += ' Previous found in file: "%s."' % f
+        raise Exception(errmsg % s)
 
-        Returns:
-            logon, uid of the AID after checking its safe.
+    seen_aids[1][v] = section_name
 
-        Raises:
-            ValueError: If there is a delimiter charcter found.
-        """
-        logon = aid.friendly
-        uid = aid.normalized_value
-        if ':' in uid:
-            raise ValueError(
-                'Cannot specify delimiter character ":" in uid: "%s"' % uid)
-        if ':' in logon:
-            raise ValueError(
-                'Cannot specify delimiter character ":" in logon: "%s"' % logon)
-        return logon, uid
+    # Append a tuple of (AID_*, base10(value), str(value))
+    # We keep the str version of value so we can print that out in the
+    # generated header so investigating parties can identify parts.
+    # We store the base10 value for sorting, so everything is ascending
+    # later.
+    aids.append((file_name, section_name, v, value))
 
-
-class AID(object):
-    """This class represents an Android ID or an AID.
-
-    Attributes:
-        identifier (str): The identifier name for a #define.
-        value (str) The User Id (uid) of the associate define.
-        found (str) The file it was found in, can be None.
-        normalized_value (str): Same as value, but base 10.
-        friendly (str): The friendly name of aid.
-    """
-
-    PREFIX = 'AID_'
-
-    # Some of the AIDS like AID_MEDIA_EX had names like mediaex
-    # list a map of things to fixup until we can correct these
-    # at a later date.
-    _FIXUPS = {
-        'media_drm': 'mediadrm',
-        'media_ex': 'mediaex',
-        'media_codec': 'mediacodec'
-    }
-
-    def __init__(self, identifier, value, found):
-        """
-        Args:
-            identifier: The identifier name for a #define <identifier>.
-            value: The value of the AID, aka the uid.
-            found (str): The file found in, not required to be specified.
-
-        Raises:
-            ValueError: if value is not a valid string number as processed by
-                int(x, 0)
-        """
-        self.identifier = identifier
-        self.value = value
-        self.found = found
-        self.normalized_value = str(int(value, 0))
-
-        # Where we calculate the friendly name
-        friendly = identifier[len(AID.PREFIX):].lower()
-        self.friendly = AID._fixup_friendly(friendly)
-
-    def __eq__(self, other):
-
-        return self.identifier == other.identifier \
-            and self.value == other.value and self.found == other.found \
-            and self.normalized_value == other.normalized_value
-
-    @staticmethod
-    def is_friendly(name):
-        """Determines if an AID is a freindly name or C define.
-
-        For example if name is AID_SYSTEM it returns false, if name
-        was system, it would return true.
-
-        Returns:
-            True if name is a friendly name False otherwise.
-        """
-
-        return not name.startswith(AID.PREFIX)
-
-    @staticmethod
-    def _fixup_friendly(friendly):
-        """Fixup friendly names that historically don't follow the convention.
-
-        Args:
-            friendly (str): The friendly name.
-
-        Returns:
-            The fixedup friendly name as a str.
-        """
-
-        if friendly in AID._FIXUPS:
-            return AID._FIXUPS[friendly]
-
-        return friendly
-
-
-class FSConfig(object):
-    """Represents a filesystem config array entry.
-
-    Represents a file system configuration entry for specifying
-    file system capabilities.
-
-    Attributes:
-        mode (str): The mode of the file or directory.
-        user (str): The uid or #define identifier (AID_SYSTEM)
-        group (str): The gid or #define identifier (AID_SYSTEM)
-        caps (str): The capability set.
-        filename (str): The file it was found in.
-    """
-
-    def __init__(self, mode, user, group, caps, path, filename):
-        """
-        Args:
-            mode (str): The mode of the file or directory.
-            user (str): The uid or #define identifier (AID_SYSTEM)
-            group (str): The gid or #define identifier (AID_SYSTEM)
-            caps (str): The capability set as a list.
-            filename (str): The file it was found in.
-        """
-        self.mode = mode
-        self.user = user
-        self.group = group
-        self.caps = caps
-        self.path = path
-        self.filename = filename
-
-    def __eq__(self, other):
-
-        return self.mode == other.mode and self.user == other.user \
-            and self.group == other.group and self.caps == other.caps \
-            and self.path == other.path and self.filename == other.filename
-
-
-class AIDHeaderParser(object):
-    """Parses an android_filesystem_config.h file.
-
-    Parses a C header file and extracts lines starting with #define AID_<name>
-    It provides some basic sanity checks. The information extracted from this
-    file can later be used to sanity check other things (like oem ranges) as
-    well as generating a mapping of names to uids. It was primarily designed to
-    parse the private/android_filesystem_config.h, but any C header should
-    work.
-    """
-
-    _SKIPWORDS = ['UNUSED']
-    _AID_DEFINE = re.compile(r'\s*#define\s+%s.*' % AID.PREFIX)
-    _OEM_START_KW = 'START'
-    _OEM_END_KW = 'END'
-    _OEM_RANGE = re.compile('AID_OEM_RESERVED_[0-9]*_{0,1}(%s|%s)' %
-                            (_OEM_START_KW, _OEM_END_KW))
-
-    def __init__(self, aid_header):
-        """
-        Args:
-            aid_header (str): file name for the header
-                file containing AID entries.
-        """
-        self._aid_header = aid_header
-        self._aid_name_to_value = {}
-        self._aid_value_to_name = {}
-        self._oem_ranges = {}
-
-        with open(aid_header) as open_file:
-            self._parse(open_file)
+def convert_int(num):
 
         try:
-            self._process_and_check()
-        except ValueError as exception:
-            sys.exit('Error processing parsed data: "%s"' % (str(exception)))
-
-    def _parse(self, aid_file):
-        """Parses an AID header file. Internal use only.
-
-        Args:
-            aid_file (file): The open AID header file to parse.
-        """
-
-        for lineno, line in enumerate(aid_file):
-            def error_message(msg):
-                """Creates an error message with the current parsing state."""
-                return 'Error "{}" in file: "{}" on line: {}'.format(
-                    msg, self._aid_header, str(lineno))
-
-            if AIDHeaderParser._AID_DEFINE.match(line):
-                chunks = line.split()
-
-                if any(x in chunks[1] for x in AIDHeaderParser._SKIPWORDS):
-                    continue
-
-                identifier = chunks[1]
-                value = chunks[2]
-
-                try:
-                    if AIDHeaderParser._is_oem_range(identifier):
-                        self._handle_oem_range(identifier, value)
-                    else:
-                        self._handle_aid(identifier, value)
-                except ValueError as exception:
-                    sys.exit(error_message(
-                        '{} for "{}"'.format(exception, identifier)))
-
-    def _handle_aid(self, identifier, value):
-        """Handle an AID C #define.
-
-        Handles an AID, sanity checking, generating the friendly name and
-        adding it to the internal maps. Internal use only.
-
-        Args:
-            identifier (str): The name of the #define identifier. ie AID_FOO.
-            value (str): The value associated with the identifier.
-
-        Raises:
-            ValueError: With message set to indicate the error.
-        """
-
-        aid = AID(identifier, value, self._aid_header)
-
-        # duplicate name
-        if aid.friendly in self._aid_name_to_value:
-            raise ValueError('Duplicate aid "%s"' % identifier)
-
-        if value in self._aid_value_to_name:
-            raise ValueError('Duplicate aid value "%u" for %s' % value,
-                             identifier)
-
-        self._aid_name_to_value[aid.friendly] = aid
-        self._aid_value_to_name[value] = aid.friendly
-
-    def _handle_oem_range(self, identifier, value):
-        """Handle an OEM range C #define.
-
-        When encountering special AID defines, notably for the OEM ranges
-        this method handles sanity checking and adding them to the internal
-        maps. For internal use only.
-
-        Args:
-            identifier (str): The name of the #define identifier.
-                ie AID_OEM_RESERVED_START/END.
-            value (str): The value associated with the identifier.
-
-        Raises:
-            ValueError: With message set to indicate the error.
-        """
-
-        try:
-            int_value = int(value, 0)
+            if num.startswith('0x'):
+                return int(num, 16)
+            elif num.startswith('0b'):
+                return int(num, 2)
+            elif num.startswith('0'):
+                return int(num, 8)
+            else:
+                return int(num, 10)
         except ValueError:
-            raise ValueError(
-                'Could not convert "%s" to integer value, got: "%s"' %
-                (identifier, value))
+            pass
+        return None
 
-        # convert AID_OEM_RESERVED_START or AID_OEM_RESERVED_<num>_START
-        # to AID_OEM_RESERVED or AID_OEM_RESERVED_<num>
-        is_start = identifier.endswith(AIDHeaderParser._OEM_START_KW)
+def handle_path(file_name, section_name, config, files, dirs):
 
-        if is_start:
-            tostrip = len(AIDHeaderParser._OEM_START_KW)
-        else:
-            tostrip = len(AIDHeaderParser._OEM_END_KW)
+            mode = config.get(section_name, 'mode')
+            user = config.get(section_name, 'user')
+            group = config.get(section_name, 'group')
+            caps = config.get(section_name, 'caps')
 
-        # ending _
-        tostrip = tostrip + 1
+            errmsg = 'Found specified but unset option: \"%s" in file: \"' + file_name + '\"'
 
-        strip = identifier[:-tostrip]
-        if strip not in self._oem_ranges:
-            self._oem_ranges[strip] = []
+            if not mode:
+                raise Exception(errmsg % 'mode')
 
-        if len(self._oem_ranges[strip]) > 2:
-            raise ValueError('Too many same OEM Ranges "%s"' % identifier)
+            if not user:
+                raise Exception(errmsg % 'user')
 
-        if len(self._oem_ranges[strip]) == 1:
-            tmp = self._oem_ranges[strip][0]
+            if not group:
+                raise Exception(errmsg % 'group')
 
-            if tmp == int_value:
-                raise ValueError('START and END values equal %u' % int_value)
-            elif is_start and tmp < int_value:
-                raise ValueError('END value %u less than START value %u' %
-                                 (tmp, int_value))
-            elif not is_start and tmp > int_value:
-                raise ValueError('END value %u less than START value %u' %
-                                 (int_value, tmp))
+            if not caps:
+                raise Exception(errmsg % 'caps')
 
-        # Add START values to the head of the list and END values at the end.
-        # Thus, the list is ordered with index 0 as START and index 1 as END.
-        if is_start:
-            self._oem_ranges[strip].insert(0, int_value)
-        else:
-            self._oem_ranges[strip].append(int_value)
+            caps = caps.split()
 
-    def _process_and_check(self):
-        """Process, check and populate internal data structures.
+            tmp = []
+            for x in caps:
+                if convert_int(x):
+                    tmp.append('(' + x + ')')
+                else:
+                    tmp.append('(1ULL << CAP_' + x.upper() + ')')
 
-        After parsing and generating the internal data structures, this method
-        is responsible for sanity checking ALL of the acquired data.
+            caps = tmp
 
-        Raises:
-            ValueError: With the message set to indicate the specific error.
-        """
+            path = '"' + section_name + '"'
 
-        # tuplefy the lists since range() does not like them mutable.
-        self._oem_ranges = [
-            AIDHeaderParser._convert_lst_to_tup(k, v)
-            for k, v in self._oem_ranges.iteritems()
-        ]
+            if len(mode) == 3:
+                mode = '0' + mode
 
-        # Check for overlapping ranges
-        for i, range1 in enumerate(self._oem_ranges):
-            for range2 in self._oem_ranges[i + 1:]:
-                if AIDHeaderParser._is_overlap(range1, range2):
-                    raise ValueError("Overlapping OEM Ranges found %s and %s" %
-                                     (str(range1), str(range2)))
+            try:
+                int(mode, 8)
+            except:
+                raise Exception('Mode must be octal characters, got: "' + mode + '"')
 
-        # No core AIDs should be within any oem range.
-        for aid in self._aid_value_to_name:
-
-            if Utils.in_any_range(aid, self._oem_ranges):
-                name = self._aid_value_to_name[aid]
-                raise ValueError(
-                    'AID "%s" value: %u within reserved OEM Range: "%s"' %
-                    (name, aid, str(self._oem_ranges)))
-
-    @property
-    def oem_ranges(self):
-        """Retrieves the OEM closed ranges as a list of tuples.
-
-        Returns:
-            A list of closed range tuples: [ (0, 42), (50, 105) ... ]
-        """
-        return self._oem_ranges
-
-    @property
-    def aids(self):
-        """Retrieves the list of found AIDs.
-
-        Returns:
-            A list of AID() objects.
-        """
-        return self._aid_name_to_value.values()
-
-    @staticmethod
-    def _convert_lst_to_tup(name, lst):
-        """Converts a mutable list to a non-mutable tuple.
-
-        Used ONLY for ranges and thus enforces a length of 2.
-
-        Args:
-            lst (List): list that should be "tuplefied".
-
-        Raises:
-            ValueError if lst is not a list or len is not 2.
-
-        Returns:
-            Tuple(lst)
-        """
-        if not lst or len(lst) != 2:
-            raise ValueError('Mismatched range for "%s"' % name)
-
-        return tuple(lst)
-
-    @staticmethod
-    def _is_oem_range(aid):
-        """Detects if a given aid is within the reserved OEM range.
-
-        Args:
-            aid (int): The aid to test
-
-        Returns:
-            True if it is within the range, False otherwise.
-        """
-
-        return AIDHeaderParser._OEM_RANGE.match(aid)
-
-    @staticmethod
-    def _is_overlap(range_a, range_b):
-        """Calculates the overlap of two range tuples.
-
-        A range tuple is a closed range. A closed range includes its endpoints.
-        Note that python tuples use () notation which collides with the
-        mathematical notation for open ranges.
-
-        Args:
-            range_a: The first tuple closed range eg (0, 5).
-            range_b: The second tuple closed range eg (3, 7).
-
-        Returns:
-            True if they overlap, False otherwise.
-        """
-
-        return max(range_a[0], range_b[0]) <= min(range_a[1], range_b[1])
+            if len(mode) != 4:
+                raise Exception('Mode must be 3 or 4 characters, got: "' + mode + '"')
 
 
-class FSConfigFileParser(object):
-    """Parses a config.fs ini format file.
+            caps = '|'.join(caps)
 
-    This class is responsible for parsing the config.fs ini format files.
-    It collects and checks all the data in these files and makes it available
-    for consumption post processed.
-    """
+            x = [ mode, user, group, caps, section_name ]
+            if section_name[-1] == '/':
+                dirs.append((file_name, x))
+            else:
+                files.append((file_name, x))
 
-    # These _AID vars work together to ensure that an AID section name
-    # cannot contain invalid characters for a C define or a passwd/group file.
-    # Since _AID_PREFIX is within the set of _AID_MATCH the error logic only
-    # checks end, if you change this, you may have to update the error
-    # detection code.
-    _AID_MATCH = re.compile('%s[A-Z0-9_]+' % AID.PREFIX)
-    _AID_ERR_MSG = 'Expecting upper case, a number or underscore'
+def handle_dup(name, file_name, section_name, seen):
+        if section_name in seen:
+            dups = '"' + seen[section_name] + '" and '
+            dups += file_name
+            raise Exception('Duplicate ' + name + ' "' + section_name + '" found in files: ' + dups)
 
-    # list of handler to required options, used to identify the
-    # parsing section
-    _SECTIONS = [('_handle_aid', ('value',)),
-                 ('_handle_path', ('mode', 'user', 'group', 'caps'))]
-
-    def __init__(self, config_files, oem_ranges):
-        """
-        Args:
-            config_files ([str]): The list of config.fs files to parse.
-                Note the filename is not important.
-            oem_ranges ([(),()]): range tuples indicating reserved OEM ranges.
-        """
-
-        self._files = []
-        self._dirs = []
-        self._aids = []
-
-        self._seen_paths = {}
-        # (name to file, value to aid)
-        self._seen_aids = ({}, {})
-
-        self._oem_ranges = oem_ranges
-
-        self._config_files = config_files
-
-        for config_file in self._config_files:
-            self._parse(config_file)
-
-    def _parse(self, file_name):
-        """Parses and verifies config.fs files. Internal use only.
-
-        Args:
-            file_name (str): The config.fs (PythonConfigParser file format)
-                file to parse.
-
-        Raises:
-            Anything raised by ConfigParser.read()
-        """
-
-        # Separate config parsers for each file found. If you use
-        # read(filenames...) later files can override earlier files which is
-        # not what we want. Track state across files and enforce with
-        # _handle_dup(). Note, strict ConfigParser is set to true in
-        # Python >= 3.2, so in previous versions same file sections can
-        # override previous
-        # sections.
+def parse(file_name, files, dirs, aids, seen_paths, seen_aids):
 
         config = ConfigParser.ConfigParser()
         config.read(file_name)
 
-        for section in config.sections():
+        for s in config.sections():
 
-            found = False
+            if AID_MATCH.match(s) and config.has_option(s, 'value'):
+                handle_dup('AID', file_name, s, seen_aids[0])
+                seen_aids[0][s] = file_name
+                handle_aid(file_name, s, config, aids, seen_aids)
+            else:
+                handle_dup('path', file_name, s, seen_paths)
+                seen_paths[s] = file_name
+                handle_path(file_name, s, config, files, dirs)
 
-            for test in FSConfigFileParser._SECTIONS:
-                handler = test[0]
-                options = test[1]
+def generate(files, dirs, aids):
+    print GENERATED
+    print INCLUDE
+    print
 
-                if all([config.has_option(section, item) for item in options]):
-                    handler = getattr(self, handler)
-                    handler(file_name, section, config)
-                    found = True
-                    break
+    are_dirs = len(dirs) > 0
+    are_files = len(files) > 0
+    are_aids = len(aids) > 0
 
-            if not found:
-                sys.exit('Invalid section "%s" in file: "%s"' %
-                         (section, file_name))
-
-            # sort entries:
-            # * specified path before prefix match
-            # ** ie foo before f*
-            # * lexicographical less than before other
-            # ** ie boo before foo
-            # Given these paths:
-            # paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*']
-            # The sort order would be:
-            # paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*']
-            # Thus the fs_config tools will match on specified paths before
-            # attempting prefix, and match on the longest matching prefix.
-            self._files.sort(key=FSConfigFileParser._file_key)
-
-            # sort on value of (file_name, name, value, strvalue)
-            # This is only cosmetic so AIDS are arranged in ascending order
-            # within the generated file.
-            self._aids.sort(key=lambda item: item.normalized_value)
-
-    def _handle_aid(self, file_name, section_name, config):
-        """Verifies an AID entry and adds it to the aid list.
-
-        Calls sys.exit() with a descriptive message of the failure.
-
-        Args:
-            file_name (str): The filename of the config file being parsed.
-            section_name (str): The section name currently being parsed.
-            config (ConfigParser): The ConfigParser section being parsed that
-                the option values will come from.
-        """
-
-        def error_message(msg):
-            """Creates an error message with current parsing state."""
-            return '{} for: "{}" file: "{}"'.format(msg, section_name,
-                                                    file_name)
-
-        FSConfigFileParser._handle_dup_and_add('AID', file_name, section_name,
-                                               self._seen_aids[0])
-
-        match = FSConfigFileParser._AID_MATCH.match(section_name)
-        invalid = match.end() if match else len(AID.PREFIX)
-        if invalid != len(section_name):
-            tmp_errmsg = ('Invalid characters in AID section at "%d" for: "%s"'
-                          % (invalid, FSConfigFileParser._AID_ERR_MSG))
-            sys.exit(error_message(tmp_errmsg))
-
-        value = config.get(section_name, 'value')
-
-        if not value:
-            sys.exit(error_message('Found specified but unset "value"'))
-
-        try:
-            aid = AID(section_name, value, file_name)
-        except ValueError:
-            sys.exit(
-                error_message('Invalid "value", not aid number, got: \"%s\"' %
-                              value))
-
-        # Values must be within OEM range
-        if not Utils.in_any_range(int(aid.value, 0), self._oem_ranges):
-            emsg = '"value" not in valid range %s, got: %s'
-            emsg = emsg % (str(self._oem_ranges), value)
-            sys.exit(error_message(emsg))
-
-        # use the normalized int value in the dict and detect
-        # duplicate definitions of the same value
-        FSConfigFileParser._handle_dup_and_add(
-            'AID', file_name, aid.normalized_value, self._seen_aids[1])
-
-        # Append aid tuple of (AID_*, base10(value), _path(value))
-        # We keep the _path version of value so we can print that out in the
-        # generated header so investigating parties can identify parts.
-        # We store the base10 value for sorting, so everything is ascending
-        # later.
-        self._aids.append(aid)
-
-    def _handle_path(self, file_name, section_name, config):
-        """Add a file capability entry to the internal list.
-
-        Handles a file capability entry, verifies it, and adds it to
-        to the internal dirs or files list based on path. If it ends
-        with a / its a dir. Internal use only.
-
-        Calls sys.exit() on any validation error with message set.
-
-        Args:
-            file_name (str): The current name of the file being parsed.
-            section_name (str): The name of the section to parse.
-            config (str): The config parser.
-        """
-
-        FSConfigFileParser._handle_dup_and_add('path', file_name, section_name,
-                                               self._seen_paths)
-
-        mode = config.get(section_name, 'mode')
-        user = config.get(section_name, 'user')
-        group = config.get(section_name, 'group')
-        caps = config.get(section_name, 'caps')
-
-        errmsg = ('Found specified but unset option: \"%s" in file: \"' +
-                  file_name + '\"')
-
-        if not mode:
-            sys.exit(errmsg % 'mode')
-
-        if not user:
-            sys.exit(errmsg % 'user')
-
-        if not group:
-            sys.exit(errmsg % 'group')
-
-        if not caps:
-            sys.exit(errmsg % 'caps')
-
-        caps = caps.split()
-
-        tmp = []
-        for cap in caps:
-            try:
-                # test if string is int, if it is, use as is.
-                int(cap, 0)
-                tmp.append('(' + cap + ')')
-            except ValueError:
-                tmp.append('(1ULL << CAP_' + cap.upper() + ')')
-
-        caps = tmp
-
-        if len(mode) == 3:
-            mode = '0' + mode
-
-        try:
-            int(mode, 8)
-        except ValueError:
-            sys.exit('Mode must be octal characters, got: "%s"' % mode)
-
-        if len(mode) != 4:
-            sys.exit('Mode must be 3 or 4 characters, got: "%s"' % mode)
-
-        caps_str = '|'.join(caps)
-
-        entry = FSConfig(mode, user, group, caps_str, section_name, file_name)
-        if section_name[-1] == '/':
-            self._dirs.append(entry)
-        else:
-            self._files.append(entry)
-
-    @property
-    def files(self):
-        """Get the list of FSConfig file entries.
-
-        Returns:
-             a list of FSConfig() objects for file paths.
-        """
-        return self._files
-
-    @property
-    def dirs(self):
-        """Get the list of FSConfig dir entries.
-
-        Returns:
-            a list of FSConfig() objects for directory paths.
-        """
-        return self._dirs
-
-    @property
-    def aids(self):
-        """Get the list of AID entries.
-
-        Returns:
-            a list of AID() objects.
-        """
-        return self._aids
-
-    @staticmethod
-    def _file_key(fs_config):
-        """Used as the key paramter to sort.
-
-        This is used as a the function to the key parameter of a sort.
-        it wraps the string supplied in a class that implements the
-        appropriate __lt__ operator for the sort on path strings. See
-        StringWrapper class for more details.
-
-        Args:
-            fs_config (FSConfig): A FSConfig entry.
-
-        Returns:
-            A StringWrapper object
-        """
-
-        # Wrapper class for custom prefix matching strings
-        class StringWrapper(object):
-            """Wrapper class used for sorting prefix strings.
-
-            The algorithm is as follows:
-              - specified path before prefix match
-                - ie foo before f*
-              - lexicographical less than before other
-                - ie boo before foo
-
-            Given these paths:
-            paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*']
-            The sort order would be:
-            paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*']
-            Thus the fs_config tools will match on specified paths before
-            attempting prefix, and match on the longest matching prefix.
-            """
-
-            def __init__(self, path):
-                """
-                Args:
-                    path (str): the path string to wrap.
-                """
-                self.is_prefix = path[-1] == '*'
-                if self.is_prefix:
-                    self.path = path[:-1]
-                else:
-                    self.path = path
-
-            def __lt__(self, other):
-
-                # if were both suffixed the smallest string
-                # is 'bigger'
-                if self.is_prefix and other.is_prefix:
-                    result = len(self.path) > len(other.path)
-                # If I am an the suffix match, im bigger
-                elif self.is_prefix:
-                    result = False
-                # If other is the suffix match, he's bigger
-                elif other.is_prefix:
-                    result = True
-                # Alphabetical
-                else:
-                    result = self.path < other.path
-                return result
-
-        return StringWrapper(fs_config.path)
-
-    @staticmethod
-    def _handle_dup_and_add(name, file_name, section_name, seen):
-        """Tracks and detects duplicates. Internal use only.
-
-        Calls sys.exit() on a duplicate.
-
-        Args:
-            name (str): The name to use in the error reporting. The pretty
-                name for the section.
-            file_name (str): The file currently being parsed.
-            section_name (str): The name of the section. This would be path
-                or identifier depending on what's being parsed.
-            seen (dict): The dictionary of seen things to check against.
-        """
-        if section_name in seen:
-            dups = '"' + seen[section_name] + '" and '
-            dups += file_name
-            sys.exit('Duplicate %s "%s" found in files: %s' %
-                     (name, section_name, dups))
-
-        seen[section_name] = file_name
-
-
-class BaseGenerator(object):
-    """Interface for Generators.
-
-    Base class for generators, generators should implement
-    these method stubs.
-    """
-
-    def add_opts(self, opt_group):
-        """Used to add per-generator options to the command line.
-
-        Args:
-            opt_group (argument group object): The argument group to append to.
-                See the ArgParse docs for more details.
-        """
-
-        raise NotImplementedError("Not Implemented")
-
-    def __call__(self, args):
-        """This is called to do whatever magic the generator does.
-
-        Args:
-            args (dict): The arguments from ArgParse as a dictionary.
-                ie if you specified an argument of foo in add_opts, access
-                it via args['foo']
-        """
-
-        raise NotImplementedError("Not Implemented")
-
-
-@generator('fsconfig')
-class FSConfigGen(BaseGenerator):
-    """Generates the android_filesystem_config.h file.
-
-    Output is  used in generating fs_config_files and fs_config_dirs.
-    """
-
-    _GENERATED = textwrap.dedent("""\
-        /*
-         * THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY
-         */
-        """)
-
-    _INCLUDES = [
-        '<private/android_filesystem_config.h>', '"generated_oem_aid.h"'
-    ]
-
-    _DEFINE_NO_DIRS = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS'
-    _DEFINE_NO_FILES = '#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES'
-
-    _DEFAULT_WARNING = (
-        '#warning No device-supplied android_filesystem_config.h,'
-        ' using empty default.')
-
-    # Long names.
-    # pylint: disable=invalid-name
-    _NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY = (
-        '{ 00000, AID_ROOT, AID_ROOT, 0,'
-        '"system/etc/fs_config_dirs" },')
-
-    _NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES_ENTRY = (
-        '{ 00000, AID_ROOT, AID_ROOT, 0,'
-        '"system/etc/fs_config_files" },')
-
-    _IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS = (
-        '#ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS')
-    # pylint: enable=invalid-name
-
-    _ENDIF = '#endif'
-
-    _OPEN_FILE_STRUCT = (
-        'static const struct fs_path_config android_device_files[] = {')
-
-    _OPEN_DIR_STRUCT = (
-        'static const struct fs_path_config android_device_dirs[] = {')
-
-    _CLOSE_FILE_STRUCT = '};'
-
-    _GENERIC_DEFINE = "#define %s\t%s"
-
-    _FILE_COMMENT = '// Defined in file: \"%s\"'
-
-    def __init__(self, *args, **kwargs):
-        BaseGenerator.__init__(args, kwargs)
-
-        self._oem_parser = None
-        self._base_parser = None
-        self._friendly_to_aid = None
-
-    def add_opts(self, opt_group):
-
-        opt_group.add_argument(
-            'fsconfig', nargs='+', help='The list of fsconfig files to parse')
-
-        opt_group.add_argument(
-            '--aid-header',
-            required=True,
-            help='An android_filesystem_config.h file'
-            ' to parse AIDs and OEM Ranges from')
-
-    def __call__(self, args):
-
-        self._base_parser = AIDHeaderParser(args['aid_header'])
-        self._oem_parser = FSConfigFileParser(args['fsconfig'],
-                                              self._base_parser.oem_ranges)
-        base_aids = self._base_parser.aids
-        oem_aids = self._oem_parser.aids
-
-        # Detect name collisions on AIDs. Since friendly works as the
-        # identifier for collision testing and we need friendly later on for
-        # name resolution, just calculate and use friendly.
-        # {aid.friendly: aid for aid in base_aids}
-        base_friendly = {aid.friendly: aid for aid in base_aids}
-        oem_friendly = {aid.friendly: aid for aid in oem_aids}
-
-        base_set = set(base_friendly.keys())
-        oem_set = set(oem_friendly.keys())
-
-        common = base_set & oem_set
-
-        if len(common) > 0:
-            emsg = 'Following AID Collisions detected for: \n'
-            for friendly in common:
-                base = base_friendly[friendly]
-                oem = oem_friendly[friendly]
-                emsg += (
-                    'Identifier: "%s" Friendly Name: "%s" '
-                    'found in file "%s" and "%s"' %
-                    (base.identifier, base.friendly, base.found, oem.found))
-                sys.exit(emsg)
-
-        self._friendly_to_aid = oem_friendly
-        self._friendly_to_aid.update(base_friendly)
-
-        self._generate()
-
-    def _to_fs_entry(self, fs_config):
-        """Converts an FSConfig entry to an fs entry.
-
-        Prints '{ mode, user, group, caps, "path" },'.
-
-        Calls sys.exit() on error.
-
-        Args:
-            fs_config (FSConfig): The entry to convert to
-                a valid C array entry.
-        """
-
-        # Get some short names
-        mode = fs_config.mode
-        user = fs_config.user
-        group = fs_config.group
-        fname = fs_config.filename
-        caps = fs_config.caps
-        path = fs_config.path
-
-        emsg = 'Cannot convert friendly name "%s" to identifier!'
-
-        # remap friendly names to identifier names
-        if AID.is_friendly(user):
-            if user not in self._friendly_to_aid:
-                sys.exit(emsg % user)
-            user = self._friendly_to_aid[user].identifier
-
-        if AID.is_friendly(group):
-            if group not in self._friendly_to_aid:
-                sys.exit(emsg % group)
-            group = self._friendly_to_aid[group].identifier
-
-        fmt = '{ %s, %s, %s, %s, "%s" },'
-
-        expanded = fmt % (mode, user, group, caps, path)
-
-        print FSConfigGen._FILE_COMMENT % fname
-        print '    ' + expanded
-
-    @staticmethod
-    def _gen_inc():
-        """Generate the include header lines and print to stdout."""
-        for include in FSConfigGen._INCLUDES:
-            print '#include %s' % include
-
-    def _generate(self):
-        """Generates an OEM android_filesystem_config.h header file to stdout.
+    if are_aids:
+        for a in aids:
+            # use the preserved str value
+            print FILE_COMMENT % a[0]
+            print GENERIC_DEFINE % (a[1], a[2])
 
-        Args:
-            files ([FSConfig]): A list of FSConfig objects for file entries.
-            dirs ([FSConfig]): A list of FSConfig objects for directory
-                entries.
-            aids ([AIDS]): A list of AID objects for Android Id entries.
-        """
-        print FSConfigGen._GENERATED
         print
 
-        FSConfigGen._gen_inc()
-        print
+    if not are_dirs:
+        print DEFINE_NO_DIRS
 
-        dirs = self._oem_parser.dirs
-        files = self._oem_parser.files
-        aids = self._oem_parser.aids
+    if not are_files:
+        print DEFINE_NO_FILES
 
-        are_dirs = len(dirs) > 0
-        are_files = len(files) > 0
-        are_aids = len(aids) > 0
+    if not are_files and not are_dirs and not are_aids:
+        print DEFAULT_WARNING
+        return
 
-        if are_aids:
-            for aid in aids:
-                # use the preserved _path value
-                print FSConfigGen._FILE_COMMENT % aid.found
-                print FSConfigGen._GENERIC_DEFINE % (aid.identifier, aid.value)
-
-            print
+    if are_files:
+        print OPEN_FILE_STRUCT
+        for tup in files:
+            f = tup[0]
+            c = tup[1]
+            c[4] = '"' + c[4] + '"'
+            c = '{ ' + '    ,'.join(c) + ' },'
+            print FILE_COMMENT % f
+            print '    ' + c
 
         if not are_dirs:
-            print FSConfigGen._DEFINE_NO_DIRS + '\n'
+            print IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS
+            print '    ' + NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY
+            print ENDIF
+        print CLOSE_FILE_STRUCT
 
-        if not are_files:
-            print FSConfigGen._DEFINE_NO_FILES + '\n'
+    if are_dirs:
+        print OPEN_DIR_STRUCT
+        for d in dirs:
+            f[4] = '"' + f[4] + '"'
+            d = '{ ' + '    ,'.join(d) + ' },'
+            print '    ' + d
 
-        if not are_files and not are_dirs and not are_aids:
-            return
+        print CLOSE_FILE_STRUCT
 
-        if are_files:
-            print FSConfigGen._OPEN_FILE_STRUCT
-            for fs_config in files:
-                self._to_fs_entry(fs_config)
+def file_key(x):
 
-            if not are_dirs:
-                print FSConfigGen._IFDEF_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS
-                print(
-                    '    ' +
-                    FSConfigGen._NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS_ENTRY)
-                print FSConfigGen._ENDIF
-            print FSConfigGen._CLOSE_FILE_STRUCT
+    # Wrapper class for custom prefix matching strings
+    class S(object):
+        def __init__(self, str):
 
-        if are_dirs:
-            print FSConfigGen._OPEN_DIR_STRUCT
-            for dir_entry in dirs:
-                self._to_fs_entry(dir_entry)
+            self.orig = str
+            self.is_prefix = str[-1] == '*'
+            if self.is_prefix:
+                self.str = str[:-1]
+            else:
+                self.str = str
 
-            print FSConfigGen._CLOSE_FILE_STRUCT
+        def __lt__(self, other):
 
+            # if were both suffixed the smallest string
+            # is 'bigger'
+            if self.is_prefix and other.is_prefix:
+                b = len(self.str) > len(other.str)
+            # If I am an the suffix match, im bigger
+            elif self.is_prefix:
+                b = False
+            # If other is the suffix match, he's bigger
+            elif other.is_prefix:
+                b = True
+            # Alphabetical
+            else:
+                b = self.str < other.str
+            return b
 
-@generator('aidarray')
-class AIDArrayGen(BaseGenerator):
-    """Generates the android_id static array."""
-
-    _GENERATED = ('/*\n'
-                  ' * THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY!\n'
-                  ' */')
-
-    _INCLUDE = '#include <private/android_filesystem_config.h>'
-
-    _STRUCT_FS_CONFIG = textwrap.dedent("""
-                         struct android_id_info {
-                             const char *name;
-                             unsigned aid;
-                         };""")
-
-    _OPEN_ID_ARRAY = 'static const struct android_id_info android_ids[] = {'
-
-    _ID_ENTRY = '    { "%s", %s },'
-
-    _CLOSE_FILE_STRUCT = '};'
-
-    _COUNT = ('#define android_id_count \\\n'
-              '    (sizeof(android_ids) / sizeof(android_ids[0]))')
-
-    def add_opts(self, opt_group):
-
-        opt_group.add_argument(
-            'hdrfile', help='The android_filesystem_config.h'
-            'file to parse')
-
-    def __call__(self, args):
-
-        hdr = AIDHeaderParser(args['hdrfile'])
-
-        print AIDArrayGen._GENERATED
-        print
-        print AIDArrayGen._INCLUDE
-        print
-        print AIDArrayGen._STRUCT_FS_CONFIG
-        print
-        print AIDArrayGen._OPEN_ID_ARRAY
-
-        for aid in hdr.aids:
-            print AIDArrayGen._ID_ENTRY % (aid.friendly, aid.identifier)
-
-        print AIDArrayGen._CLOSE_FILE_STRUCT
-        print
-        print AIDArrayGen._COUNT
-        print
-
-
-@generator('oemaid')
-class OEMAidGen(BaseGenerator):
-    """Generates the OEM AID_<name> value header file."""
-
-    _GENERATED = ('/*\n'
-                  ' * THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY!\n'
-                  ' */')
-
-    _GENERIC_DEFINE = "#define %s\t%s"
-
-    _FILE_COMMENT = '// Defined in file: \"%s\"'
-
-    # Intentional trailing newline for readability.
-    _FILE_IFNDEF_DEFINE = ('#ifndef GENERATED_OEM_AIDS_H_\n'
-                           '#define GENERATED_OEM_AIDS_H_\n')
-
-    _FILE_ENDIF = '#endif'
-
-    def __init__(self):
-
-        self._old_file = None
-
-    def add_opts(self, opt_group):
-
-        opt_group.add_argument(
-            'fsconfig', nargs='+', help='The list of fsconfig files to parse.')
-
-        opt_group.add_argument(
-            '--aid-header',
-            required=True,
-            help='An android_filesystem_config.h file'
-            'to parse AIDs and OEM Ranges from')
-
-    def __call__(self, args):
-
-        hdr_parser = AIDHeaderParser(args['aid_header'])
-
-        parser = FSConfigFileParser(args['fsconfig'], hdr_parser.oem_ranges)
-
-        print OEMAidGen._GENERATED
-
-        print OEMAidGen._FILE_IFNDEF_DEFINE
-
-        for aid in parser.aids:
-            self._print_aid(aid)
-            print
-
-        print OEMAidGen._FILE_ENDIF
-
-    def _print_aid(self, aid):
-        """Prints a valid #define AID identifier to stdout.
-
-        Args:
-            aid to print
-        """
-
-        # print the source file location of the AID
-        found_file = aid.found
-        if found_file != self._old_file:
-            print OEMAidGen._FILE_COMMENT % found_file
-            self._old_file = found_file
-
-        print OEMAidGen._GENERIC_DEFINE % (aid.identifier, aid.value)
-
-
-@generator('passwd')
-class PasswdGen(BaseGenerator):
-    """Generates the /etc/passwd file per man (5) passwd."""
-
-    _GENERATED = ('#\n# THIS IS AN AUTOGENERATED FILE! DO NOT MODIFY!\n#')
-
-    _FILE_COMMENT = '# Defined in file: \"%s\"'
-
-    def __init__(self):
-
-        self._old_file = None
-
-    def add_opts(self, opt_group):
-
-        opt_group.add_argument(
-            'fsconfig', nargs='+', help='The list of fsconfig files to parse.')
-
-        opt_group.add_argument(
-            '--aid-header',
-            required=True,
-            help='An android_filesystem_config.h file'
-            'to parse AIDs and OEM Ranges from')
-
-    def __call__(self, args):
-
-        hdr_parser = AIDHeaderParser(args['aid_header'])
-
-        parser = FSConfigFileParser(args['fsconfig'], hdr_parser.oem_ranges)
-
-        aids = parser.aids
-
-        # nothing to do if no aids defined
-        if len(aids) == 0:
-            return
-
-        print PasswdGen._GENERATED
-
-        for aid in aids:
-            self._print_formatted_line(aid)
-
-    def _print_formatted_line(self, aid):
-        """Prints the aid to stdout in the passwd format. Internal use only.
-
-        Colon delimited:
-            login name, friendly name
-            encrypted password (optional)
-            uid (int)
-            gid (int)
-            User name or comment field
-            home directory
-            interpreter (optional)
-
-        Args:
-            aid (AID): The aid to print.
-        """
-        if self._old_file != aid.found:
-            self._old_file = aid.found
-            print PasswdGen._FILE_COMMENT % aid.found
-
-        try:
-            logon, uid = Utils.get_login_and_uid_cleansed(aid)
-        except ValueError as exception:
-            sys.exit(exception)
-
-        print "%s::%s:%s::/:/system/bin/sh" % (logon, uid, uid)
-
-
-@generator('group')
-class GroupGen(PasswdGen):
-    """Generates the /etc/group file per man (5) group."""
-
-    # Overrides parent
-    def _print_formatted_line(self, aid):
-        """Prints the aid to stdout in the group format. Internal use only.
-
-        Formatted (per man 5 group) like:
-            group_name:password:GID:user_list
-
-        Args:
-            aid (AID): The aid to print.
-        """
-        if self._old_file != aid.found:
-            self._old_file = aid.found
-            print PasswdGen._FILE_COMMENT % aid.found
-
-        try:
-            logon, uid = Utils.get_login_and_uid_cleansed(aid)
-        except ValueError as exception:
-            sys.exit(exception)
-
-        print "%s::%s:" % (logon, uid)
-
+    return S(x[4])
 
 def main():
-    """Main entry point for execution."""
 
-    opt_parser = argparse.ArgumentParser(
-        description='A tool for parsing fsconfig config files and producing' +
-        'digestable outputs.')
-    subparser = opt_parser.add_subparsers(help='generators')
+    files = []
+    dirs = []
+    aids = []
+    seen_paths = {}
 
-    gens = generator.get()
+    # (name to file, value to aid)
+    seen_aids = ({}, {})
 
-    # for each gen, instantiate and add them as an option
-    for name, gen in gens.iteritems():
+    for x in sys.argv[1:]:
+        parse(x, files, dirs, aids, seen_paths, seen_aids)
 
-        generator_option_parser = subparser.add_parser(name, help=gen.__doc__)
-        generator_option_parser.set_defaults(which=name)
+    # sort entries:
+    # * specified path before prefix match
+    # ** ie foo before f*
+    # * lexicographical less than before other
+    # ** ie boo before foo
+    # Given these paths:
+    # paths=['ac', 'a', 'acd', 'an', 'a*', 'aa', 'ac*']
+    # The sort order would be:
+    # paths=['a', 'aa', 'ac', 'acd', 'an', 'ac*', 'a*']
+    # Thus the fs_config tools will match on specified paths before attempting
+    # prefix, and match on the longest matching prefix.
+    files.sort(key= lambda x: file_key(x[1]))
 
-        opt_group = generator_option_parser.add_argument_group(name +
-                                                               ' options')
-        gen.add_opts(opt_group)
+    # sort on value of (file_name, name, value, strvalue)
+    # This is only cosmetic so AIDS are arranged in ascending order
+    # within the generated file.
+    aids.sort(key=lambda x: x[2])
 
-    args = opt_parser.parse_args()
-
-    args_as_dict = vars(args)
-    which = args_as_dict['which']
-    del args_as_dict['which']
-
-    gens[which](args_as_dict)
-
+    generate(files, dirs, aids)
 
 if __name__ == '__main__':
     main()