fs_config: introduce group generator

Introduce a generator that outputs group files per man(5) group.

Succinctly, the output is a colon delimited string containing the following
fields:
  * group name
  * encrypted password (optional)
  * gid (int)
  * userlist (str,...)

Multiple colon delimited lines may exist, but will not be separated
across lines.

Sample generator output:
foo::2900:
foo_bar::2901:
custom_oem1::2902:

Test: That make group produces the group file.
Change-Id: Idd3fe925a09a227c6e894e1b5d2b3873b01531c6
Signed-off-by: William Roberts <william.c.roberts@intel.com>
diff --git a/tools/fs_config/fs_config_generator.py b/tools/fs_config/fs_config_generator.py
index 91967df..d46db9f 100755
--- a/tools/fs_config/fs_config_generator.py
+++ b/tools/fs_config/fs_config_generator.py
@@ -89,6 +89,32 @@
 
         return any(lower <= value <= upper for (lower, upper) in ranges)
 
+    @staticmethod
+    def get_login_and_uid_cleansed(aid):
+        """Returns a passwd/group file safe logon and uid.
+
+        This checks that the logon and uid of the AID do not
+        contain the delimiter ":" for a passwd/group file.
+
+        Args:
+            aid (AID): The aid to check
+
+        Returns:
+            logon, uid of the AID after checking its safe.
+
+        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
+
 
 class AID(object):
     """This class represents an Android ID or an AID.
@@ -1191,12 +1217,10 @@
         print PasswdGen._GENERATED
 
         for aid in aids:
-            self._print_aid_passwd_line(aid)
+            self._print_formatted_line(aid)
 
-    def _print_aid_passwd_line(self, aid):
-        """
-        Prints the aid to stdout in the passwd format.
-        Internal use only.
+    def _print_formatted_line(self, aid):
+        """Prints the aid to stdout in the passwd format. Internal use only.
 
         Colon delimited:
             login name, friendly name
@@ -1214,18 +1238,40 @@
             self._old_file = aid.found
             print PasswdGen._FILE_COMMENT % aid.found
 
-        logon = aid.friendly
-        uid = aid.normalized_value
-        if ':' in uid:
-            sys.exit('Cannot specify delimiter character ":" in uid: "%s"' %
-                     uid)
-        if ':' in logon:
-            sys.exit('Cannot specify delimiter character ":" in logon: "%s"' %
-                     logon)
+        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)
+
+
 def main():
     """Main entry point for execution."""