Today's changes:

2022-08-07  Bruno Haible  <br...@clisp.org>

        gnulib-tool.py: Finish implementing option --extract-test-module.
        * gnulib-tool.py (main): Accept option --extract-tests-module.

        gnulib-tool.py: Fix handling of nonexistent module names in --extract-*.
        * gnulib-tool.py (main): To test whether a module exists, just call
        GLModuleSystem.find and test its return value.

        gnulib-tool.py: Fix --extract-dependencies result.
        * pygnulib/GLModuleSystem.py (GLModule.getDependencies): Return a
        snippet, not a list. Implement dependency of ${module}-tests on
        ${module}.
        (GLModule.getDependenciesWithoutConditions,
        GLModule.getDependenciesWithConditions): New methods.
        (GLModuleTable.transitive_closure): Call getDependenciesWithConditions.
        * pygnulib/GLEmiter.py (GLEmiter.autoconfSnippets): Call
        getDependenciesWithoutConditions.
        * gnulib-tool.py (main) [--extract-dependencies]: Update.

        gnulib-tool.py: Rename a method.
        * pygnulib/GLModuleSystem.py (GLModule.getAutoconfEarlySnippet): Renamed
        from GLModule.getAutoconfSnippet_Early.
        * pygnulib/GLImport.py: Update.
        * pygnulib/GLTestDir.py: Likewise.

        gnulib-tool.py: Fix section extraction from module descriptions.
        The code with   self.content.split(section)[-1]
        was broken because it recognizes an indented section label.
        Similar code with   ('\n' + self.content).split('\n' + section)[-1]
        would still be broken because it recognizes an indented section label
        in the first line of the file.
        The code with   section_label_regex
        was broken because sometimes it returns the second-to-last section with
        the given label, not the last one.
        Also, whitespace after the colon was not ignored.
        * pygnulib/GLModuleSystem.py (GLModule.__init__): Dissect the module
        description's contents immediately, once only, in a reliable way.
        (GLModule.getDescription, GLModule.getComment): Simplify.
        (GLModule.getStatus): Simplify. Return a string.
        (GLModule.getStatuses): New function. Return a list.
        (GLModule.getNotice, GLModule.getApplicability, GLModule.getFiles,
        GLModule.getDependencies, GLModules.getAutoconfSnippet_Early,
        GLModules.getAutoconfSnippet, GLModule.getAutomakeSnippet_Conditional,
        GLModule.getInclude, GLModule.getLink, GLModule.getLicense_Raw):
        Simplify.
        (GLModule.getLicense): Remove whitespace after calling getLicense_Raw.
        (GLModule.getMaintainer): Simplify.
        (GLModuleTable.transitive_closure): Call getStatuses() instead of
        getStatus().
        * pygnulib/GLEmiter.py: Likewise.
        * gnulib-tool.py (main): For --extract-description, --extract-comment,
        --extract-status, --extract-notice, --extract-autoconf-snippet,
        --extract-automake-snippet, --extract-include-directive,
        --extract-link-directive, --extract-maintainer, don't add an extra
        newline after the snippet.

        gnulib-tool.py: Improve field naming.
        * pygnulib/GLModuleSystem.py (GLModule): Rename field 'module' to
        'path'. Fix a typo in a TypeError message.

        gnulib-tool.py: Simplify.
        * pygnulib/GLModuleSystem.py (GLModule): Convert Windows newlines right
        after reading the module description, not in every accessor.

        gnulib-tool.py: Reduce code duplication.
        * pygnulib/GLModuleSystem.py (GLModule): Declare two regexes are class
        variables.

        gnulib-tool.py: Implement option --find.
        * pygnulib/GLModuleSystem.py (GLModuleSystem.file_is_module): New
        method.
        (GLModuleSystem.list): Filter the listing in memory; don't use a 'sed'
        subprocess.
        * gnulib-tool.py (main): Handle mode 'find'.

        gnulib-tool.py: Fix some regexes.
        * pygnulib/GLEmiter.py (GLEmiter.lib_Makefile_am): Use an equivalent
        regex as gnulib-tool.
        * pygnulib/GLTestDir.py (GLTestDir.execute): Likewise.

        gnulib-tool.py: Fix some regex uses.
        * pygnulib/GLModuleSystem.py (GLModuleTable.transitive_closure): Match
        the regex against all lines of the snippet, not only the first line.
        * pygnulib/GLEmiter.py (GLEmiter.autoconfSnippet): Likewise.

        gnulib-tool.py: Make regex uses more straightforward.
        * pygnulib/constants.py: Don't use the "minimal matching" *? construct
        when it makes no difference (because we're matching a single line only
        and flag re.M is not specified).
        * pygnulib/GLModuleSystem.py: Likewise.

        gnulib-tool.py: Make regex uses more straightforward.
        * pygnulib/GLModuleSystem.py: Don't use flag re.S on regular expressions
        that are meant to match a single line only, and remove the use of the
        "minimal matching" *? construct whose only purpose was to neutralize the
        re.S flag.
        * pygnulib/GLEmiter.py: Likewise.
        * pygnulib/GLImport.py: Likewise.
        * pygnulib/GLTestDir.py: Likewise.

        gnulib-tool.py: Make regex uses more straightforward.
        * pygnulib/GLEmiter.py: Don't use flag re.S on regular expressions on
        regular expressions with no '.'.
        * pygnulib/GLImport.py: Likewise.

2022-08-06  Bruno Haible  <br...@clisp.org>

        gnulib-tool.py: Fix output in --dry-run mode.
        * pygnulib/GLImport.py (GLImport._update_ignorelist_): In dry-run mode,
        say "Update", not "Updating".

        gnulib-tool.py: Finish implementing options --vc-files, --no-vc-files.
        * gnulib-tool.py (main): Accept options --vc-files, --no-vc-files.
        * pygnulib/GLImport.py (GLImport.__init__): Correct parsing of
        gl_VC_FILES directive.
        (GLImport.gnulib_cache): Don't treat the value False like None.
        (GLImport.execute): Skip the .gitignore file manipulations if vc_files
        is False.

        gnulib-tool.py: Finish implementing option --witness-c-macro.
        * gnulib-tool.py (main): Accept option --witness-c-macro.
        * pygnulib/GLConfig.py (GLConfig.__init__): Remove wrong type check of
        witness_c_macro argument.

>From 860df12df83dd27ae2b83e0ed6b485c4ffbb640e Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sat, 6 Aug 2022 21:54:04 +0200
Subject: [PATCH 01/19] gnulib-tool.py: Finish implementing option
 --witness-c-macro.

* gnulib-tool.py (main): Accept option --witness-c-macro.
* pygnulib/GLConfig.py (GLConfig.__init__): Remove wrong type check of
witness_c_macro argument.
---
 ChangeLog            |  7 +++++++
 gnulib-tool.py       | 12 ++++++++++--
 gnulib-tool.py.TODO  |  1 -
 pygnulib/GLConfig.py |  9 +--------
 4 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index dc968b8982..0043103914 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2022-08-06  Bruno Haible  <br...@clisp.org>
+
+	gnulib-tool.py: Finish implementing option --witness-c-macro.
+	* gnulib-tool.py (main): Accept option --witness-c-macro.
+	* pygnulib/GLConfig.py (GLConfig.__init__): Remove wrong type check of
+	witness_c_macro argument.
+
 2022-08-05  Bruno Haible  <br...@clisp.org>
 
 	gnulib-tool.py: Don't initialize local variables too early.
diff --git a/gnulib-tool.py b/gnulib-tool.py
index 78bb326a69..0bf72bf3a0 100755
--- a/gnulib-tool.py
+++ b/gnulib-tool.py
@@ -358,6 +358,11 @@ def main():
                         dest='podomain',
                         default=None,
                         nargs=1)
+    # witness-c-macro
+    parser.add_argument('--witness-c-macro',
+                        dest='witness_c_macro',
+                        default=None,
+                        nargs=1)
     # single-configure
     parser.add_argument('--single-configure',
                         dest='single_configure',
@@ -547,7 +552,8 @@ def main():
                  or cmdargs.excl_unportable_tests != None
                  or cmdargs.avoids != None or cmdargs.lgpl != None
                  or cmdargs.makefile_name != None
-                 or cmdargs.macro_prefix != None or cmdargs.podomain != None))):
+                 or cmdargs.macro_prefix != None or cmdargs.podomain != None
+                 or cmdargs.witness_c_macro != None))):
         message = '%s: *** ' % constants.APP['name']
         message += 'invalid options for --%s mode\n' % mode
         message += 'Try \'gnulib-tool --help\' for more information.\n'
@@ -640,6 +646,9 @@ def main():
     podomain = cmdargs.podomain
     if podomain != None:
         podomain = podomain[0]
+    witness_c_macro = cmdargs.witness_c_macro
+    if witness_c_macro != None:
+        witness_c_macro = witness_c_macro[0]
     avoids = cmdargs.avoids
     if avoids != None:
         avoids = [ module
@@ -650,7 +659,6 @@ def main():
     single_configure = cmdargs.single_configure
     docbase = None
     conddeps = None
-    witness_c_macro = None
     vc_files = None
 
     # Create pygnulib configuration.
diff --git a/gnulib-tool.py.TODO b/gnulib-tool.py.TODO
index b59e89cc59..c37fcbeebf 100644
--- a/gnulib-tool.py.TODO
+++ b/gnulib-tool.py.TODO
@@ -29,7 +29,6 @@ Implement the options:
   --gnu-make
   --tests-makefile-name
   --automake-subdir
-  --witness-c-macro
   --vc-files
   --no-vc-files
   -h | --hardlink
diff --git a/pygnulib/GLConfig.py b/pygnulib/GLConfig.py
index e571dff2b3..5f30a80797 100644
--- a/pygnulib/GLConfig.py
+++ b/pygnulib/GLConfig.py
@@ -153,14 +153,7 @@ class GLConfig(object):
         # witness_c_macro
         self.resetWitnessCMacro()
         if witness_c_macro != None:
-            if type(witness_c_macro) is bool:
-                if not witness_c_macro:
-                    self.setWitnessCMacro()
-                else:  # if witness_c_macro
-                    self.resetWitnessCMacro()
-            else:  # if type(witness_c_macro) is not bool
-                raise TypeError('witness_c_macro must be a bool, not %s'
-                                % type(witness_c_macro).__name__)
+            self.setWitnessCMacro(witness_c_macro)
         # vc_files
         self.resetVCFiles()
         if vc_files != None:
-- 
2.34.1

>From 9812027a9afa87b12eb3b7e3c28600e6eff7ca98 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sat, 6 Aug 2022 23:01:36 +0200
Subject: [PATCH 02/19] gnulib-tool.py: Finish implementing options --vc-files,
 --no-vc-files.

* gnulib-tool.py (main): Accept options --vc-files, --no-vc-files.
* pygnulib/GLImport.py (GLImport.__init__): Correct parsing of
gl_VC_FILES directive.
(GLImport.gnulib_cache): Don't treat the value False like None.
(GLImport.execute): Skip the .gitignore file manipulations if vc_files
is False.
---
 ChangeLog            |  8 ++++++
 gnulib-tool.py       | 13 ++++++++--
 gnulib-tool.py.TODO  |  4 +--
 pygnulib/GLConfig.py |  2 +-
 pygnulib/GLImport.py | 62 ++++++++++++++++++++++----------------------
 5 files changed, 53 insertions(+), 36 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 0043103914..29da7a3a6e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2022-08-06  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Finish implementing options --vc-files, --no-vc-files.
+	* gnulib-tool.py (main): Accept options --vc-files, --no-vc-files.
+	* pygnulib/GLImport.py (GLImport.__init__): Correct parsing of
+	gl_VC_FILES directive.
+	(GLImport.gnulib_cache): Don't treat the value False like None.
+	(GLImport.execute): Skip the .gitignore file manipulations if vc_files
+	is False.
+
 	gnulib-tool.py: Finish implementing option --witness-c-macro.
 	* gnulib-tool.py (main): Accept option --witness-c-macro.
 	* pygnulib/GLConfig.py (GLConfig.__init__): Remove wrong type check of
diff --git a/gnulib-tool.py b/gnulib-tool.py
index 0bf72bf3a0..bb763a2245 100755
--- a/gnulib-tool.py
+++ b/gnulib-tool.py
@@ -363,6 +363,15 @@ def main():
                         dest='witness_c_macro',
                         default=None,
                         nargs=1)
+    # vc-files
+    parser.add_argument('--vc-files',
+                        dest='vc_files',
+                        default=None,
+                        action='store_true')
+    parser.add_argument('--no-vc-files',
+                        dest='vc_files',
+                        default=None,
+                        action='store_false')
     # single-configure
     parser.add_argument('--single-configure',
                         dest='single_configure',
@@ -553,7 +562,7 @@ def main():
                  or cmdargs.avoids != None or cmdargs.lgpl != None
                  or cmdargs.makefile_name != None
                  or cmdargs.macro_prefix != None or cmdargs.podomain != None
-                 or cmdargs.witness_c_macro != None))):
+                 or cmdargs.witness_c_macro != None or cmdargs.vc_files != None))):
         message = '%s: *** ' % constants.APP['name']
         message += 'invalid options for --%s mode\n' % mode
         message += 'Try \'gnulib-tool --help\' for more information.\n'
@@ -649,6 +658,7 @@ def main():
     witness_c_macro = cmdargs.witness_c_macro
     if witness_c_macro != None:
         witness_c_macro = witness_c_macro[0]
+    vc_files = cmdargs.vc_files
     avoids = cmdargs.avoids
     if avoids != None:
         avoids = [ module
@@ -659,7 +669,6 @@ def main():
     single_configure = cmdargs.single_configure
     docbase = None
     conddeps = None
-    vc_files = None
 
     # Create pygnulib configuration.
     config = classes.GLConfig(
diff --git a/gnulib-tool.py.TODO b/gnulib-tool.py.TODO
index c37fcbeebf..53915662b8 100644
--- a/gnulib-tool.py.TODO
+++ b/gnulib-tool.py.TODO
@@ -29,14 +29,14 @@ Implement the options:
   --gnu-make
   --tests-makefile-name
   --automake-subdir
-  --vc-files
-  --no-vc-files
   -h | --hardlink
   --local-hardlink
   -S | --more-symlinks
   -H | --more-hardlinks
   --help (same output)
 
+Remove exit() in GLImport.py.
+
 --------------------------------------------------------------------------------
 
 commit 76c7703cb2e9e0e803d1296618d8ab9e86e13d6c
diff --git a/pygnulib/GLConfig.py b/pygnulib/GLConfig.py
index 5f30a80797..216f2e05fa 100644
--- a/pygnulib/GLConfig.py
+++ b/pygnulib/GLConfig.py
@@ -276,7 +276,7 @@ class GLConfig(object):
             elif key in ['libtool', 'lgpl', 'conddeps', 'symbolic', 'lsymbolic',
                          'libtests', 'dryrun']:
                 return False
-            if key == 'vc_files':
+            elif key == 'vc_files':
                 return None
             elif key == 'errors':
                 return True
diff --git a/pygnulib/GLImport.py b/pygnulib/GLImport.py
index 9de4b3b93c..b06fc5b603 100644
--- a/pygnulib/GLImport.py
+++ b/pygnulib/GLImport.py
@@ -141,9 +141,6 @@ class GLImport(object):
             if 'gl_CONDITIONAL_DEPENDENCIES' in data:
                 self.cache.setCondDeps(True)
                 data = data.replace('gl_CONDITIONAL_DEPENDENCIES', '')
-            if 'gl_VC_FILES' in data:
-                self.cache.setVCFiles(True)
-                data = data.replace('gl_VC_FILES', '')
             if 'gl_WITH_TESTS' in data:
                 self.cache.enableInclTestCategory(TESTS['tests'])
                 data = data.replace('gl_WITH_TESTS', '')
@@ -202,6 +199,8 @@ class GLImport(object):
                 self.cache.setPoDomain(cleaner(tempdict['gl_PO_DOMAIN']))
             if tempdict['gl_WITNESS_C_MACRO']:
                 self.cache.setWitnessCMacro(cleaner(tempdict['gl_WITNESS_C_MACRO']))
+            if tempdict['gl_VC_FILES']:
+                self.cache.setVCFiles(cleaner(tempdict['gl_VC_FILES']))
 
             # Get cached filelist from gnulib-comp.m4.
             destdir, m4base = self.config.getDestDir(), self.config.getM4Base()
@@ -542,7 +541,7 @@ class GLImport(object):
         emit += 'gl_MACRO_PREFIX([%s])\n' % macro_prefix
         emit += 'gl_PO_DOMAIN([%s])\n' % podomain
         emit += 'gl_WITNESS_C_MACRO([%s])\n' % witness_c_macro
-        if vc_files:
+        if vc_files != None:
             emit += 'gl_VC_FILES([%s])\n' % vc_files
         return constants.nlconvert(emit)
 
@@ -1334,33 +1333,34 @@ AC_DEFUN([%s_FILE_LIST], [\n''' % macro_prefix
             if isfile(tmpfile):
                 os.remove(tmpfile)
 
-        # Update the .cvsignore and .gitignore files.
-        ignorelist = list()
-        filetable['added'] = sorted(set(filetable['added']))
-        filetable['removed'] = sorted(set(filetable['added']))
-        for file in filetable['added']:
-            directory, basename = os.path.split(file)
-            ignorelist += [tuple([directory, '|A|', basename])]
-        for file in filetable['removed']:
-            directory, basename = os.path.split(file)
-            ignorelist += [tuple([directory, '|R|', basename])]
-        last_dir = ''
-        last_dirs_added = list()
-        last_dirs_removed = list()
-        for row in ignorelist:
-            next_dir = row[0]
-            operand = row[1]
-            filename = row[2]
-            if next_dir != last_dir:
-                self._done_dir_(last_dir, last_dirs_added, last_dirs_removed)
-                last_dir = next_dir
-                last_dirs_added = list()
-                last_dirs_removed = list()
-            if operand == '|A|':
-                last_dirs_added += [filename]
-            elif operand == '|R|':
-                last_dirs_removed += [filename]
-        self._done_dir_(last_dir, last_dirs_added, last_dirs_removed)
+        if vc_files != False:
+            # Update the .cvsignore and .gitignore files.
+            ignorelist = list()
+            filetable['added'] = sorted(set(filetable['added']))
+            filetable['removed'] = sorted(set(filetable['added']))
+            for file in filetable['added']:
+                directory, basename = os.path.split(file)
+                ignorelist += [tuple([directory, '|A|', basename])]
+            for file in filetable['removed']:
+                directory, basename = os.path.split(file)
+                ignorelist += [tuple([directory, '|R|', basename])]
+            last_dir = ''
+            last_dirs_added = list()
+            last_dirs_removed = list()
+            for row in ignorelist:
+                next_dir = row[0]
+                operand = row[1]
+                filename = row[2]
+                if next_dir != last_dir:
+                    self._done_dir_(last_dir, last_dirs_added, last_dirs_removed)
+                    last_dir = next_dir
+                    last_dirs_added = list()
+                    last_dirs_removed = list()
+                if operand == '|A|':
+                    last_dirs_added += [filename]
+                elif operand == '|R|':
+                    last_dirs_removed += [filename]
+            self._done_dir_(last_dir, last_dirs_added, last_dirs_removed)
         exit()
 
         # Finish the work.
-- 
2.34.1

>From 07d41642fbad4a36a7ece9ee33db6a4d641cb585 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sat, 6 Aug 2022 23:10:14 +0200
Subject: [PATCH 03/19] gnulib-tool.py: Fix output in --dry-run mode.

* pygnulib/GLImport.py (GLImport._update_ignorelist_): In dry-run mode,
say "Update", not "Updating".
---
 ChangeLog            | 4 ++++
 pygnulib/GLImport.py | 2 +-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index 29da7a3a6e..e5af4e34c0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2022-08-06  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Fix output in --dry-run mode.
+	* pygnulib/GLImport.py (GLImport._update_ignorelist_): In dry-run mode,
+	say "Update", not "Updating".
+
 	gnulib-tool.py: Finish implementing options --vc-files, --no-vc-files.
 	* gnulib-tool.py (main): Accept options --vc-files, --no-vc-files.
 	* pygnulib/GLImport.py (GLImport.__init__): Correct parsing of
diff --git a/pygnulib/GLImport.py b/pygnulib/GLImport.py
index b06fc5b603..13418400af 100644
--- a/pygnulib/GLImport.py
+++ b/pygnulib/GLImport.py
@@ -739,7 +739,7 @@ AC_DEFUN([%s_FILE_LIST], [\n''' % macro_prefix
                         with codecs.open(srcpath, 'ab', 'UTF-8') as file:
                             file.write(destdata)
                     else:  # if self.config['dryrun']
-                        print('Updating %s (backup in %s)' % (srcpath, backupname))
+                        print('Update %s (backup in %s)' % (srcpath, backupname))
         else:  # if not isfile(srcpath)
             if dirs_added:
                 if not self.config['dryrun']:
-- 
2.34.1

>From a68f103cc3863310e1c1b4dd9e05b7924381d9a1 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Aug 2022 13:11:07 +0200
Subject: [PATCH 05/19] gnulib-tool.py: Make regex uses more straightforward.

* pygnulib/GLEmiter.py: Don't use flag re.S on regular expressions on
regular expressions with no '.'.
* pygnulib/GLImport.py: Likewise.
---
 ChangeLog            | 7 +++++++
 pygnulib/GLEmiter.py | 4 ++--
 pygnulib/GLImport.py | 6 +++---
 3 files changed, 12 insertions(+), 5 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 5396f935a3..bcaedf98e4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2022-08-07  Bruno Haible  <br...@clisp.org>
+
+	gnulib-tool.py: Make regex uses more straightforward.
+	* pygnulib/GLEmiter.py: Don't use flag re.S on regular expressions on
+	regular expressions with no '.'.
+	* pygnulib/GLImport.py: Likewise.
+
 2022-08-06  Bruno Haible  <br...@clisp.org>
 
 	gnulib-tool: In the VC files messages, omit the destination directory.
diff --git a/pygnulib/GLEmiter.py b/pygnulib/GLEmiter.py
index b6c31fa603..f85ef30d52 100644
--- a/pygnulib/GLEmiter.py
+++ b/pygnulib/GLEmiter.py
@@ -750,7 +750,7 @@ AC_DEFUN([%V1%_LIBSOURCES], [
             #  * https://debbugs.gnu.org/10997
             #  * https://debbugs.gnu.org/11030
             # So we need this workaround.
-            pattern = re.compile('^pkgdata_DATA *\\+=', re.S | re.M)
+            pattern = re.compile('^pkgdata_DATA *\\+=', re.M)
             if pattern.findall(allsnippets):
                 emit += 'pkgdata_DATA =\n'
             emit += 'EXTRA_DIST =\n'
@@ -1032,7 +1032,7 @@ AC_DEFUN([%V1%_LIBSOURCES], [
         #  * https://debbugs.gnu.org/10997
         #  * https://debbugs.gnu.org/11030
         # So we need this workaround.
-        pattern = re.compile('^pkgdata_DATA *\\+=', re.S | re.M)
+        pattern = re.compile('^pkgdata_DATA *\\+=', re.M)
         if bool(pattern.findall(main_snippets)) or bool(pattern.findall(longrun_snippets)):
             emit += 'pkgdata_DATA =\n'
 
diff --git a/pygnulib/GLImport.py b/pygnulib/GLImport.py
index 935a662593..7a6312ec39 100644
--- a/pygnulib/GLImport.py
+++ b/pygnulib/GLImport.py
@@ -100,7 +100,7 @@ class GLImport(object):
         if match:
             result = cleaner(match)[0]
             self.cache.setAuxDir(joinpath(self.config['destdir'], result))
-        pattern = re.compile(r'A[CM]_PROG_LIBTOOL', re.S | re.M)
+        pattern = re.compile(r'A[CM]_PROG_LIBTOOL', re.M)
         guessed_libtool = bool(pattern.findall(data))
         if self.config['auxdir'] == None:
             self.config.setAuxDir(self.cache['auxdir'])
@@ -1439,9 +1439,9 @@ in <library>_a_LDFLAGS or <library>_la_LDFLAGS when linking a library.''')
         with codecs.open(configure_ac, 'rb', 'UTF-8') as file:
             data = file.read()
         match_result1 = \
-            bool(re.compile('^ *AC_PROG_CC_STDC', re.S | re.M).findall(data))
+            bool(re.compile('^ *AC_PROG_CC_STDC', re.M).findall(data))
         match_result2 = \
-            bool(re.compile('^ *AC_PROG_CC_C99', re.S | re.M).findall(data))
+            bool(re.compile('^ *AC_PROG_CC_C99', re.M).findall(data))
         if match_result1:
             position_early_after = 'AC_PROG_CC_STDC'
         elif match_result2:
-- 
2.34.1

>From 54bf87fe01941049356177a1902bdf1a3eb49f7f Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Aug 2022 13:29:59 +0200
Subject: [PATCH 06/19] gnulib-tool.py: Make regex uses more straightforward.

* pygnulib/GLModuleSystem.py: Don't use flag re.S on regular expressions
that are meant to match a single line only, and remove the use of the
"minimal matching" *? construct whose only purpose was to neutralize the
re.S flag.
* pygnulib/GLEmiter.py: Likewise.
* pygnulib/GLImport.py: Likewise.
* pygnulib/GLTestDir.py: Likewise.
---
 ChangeLog                  |  9 +++++++++
 pygnulib/GLEmiter.py       | 18 +++++++++---------
 pygnulib/GLImport.py       |  4 ++--
 pygnulib/GLModuleSystem.py |  6 +++---
 pygnulib/GLTestDir.py      | 20 ++++++++++----------
 5 files changed, 33 insertions(+), 24 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index bcaedf98e4..0e162e8d0a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Make regex uses more straightforward.
+	* pygnulib/GLModuleSystem.py: Don't use flag re.S on regular expressions
+	that are meant to match a single line only, and remove the use of the
+	"minimal matching" *? construct whose only purpose was to neutralize the
+	re.S flag.
+	* pygnulib/GLEmiter.py: Likewise.
+	* pygnulib/GLImport.py: Likewise.
+	* pygnulib/GLTestDir.py: Likewise.
+
 	gnulib-tool.py: Make regex uses more straightforward.
 	* pygnulib/GLEmiter.py: Don't use flag re.S on regular expressions on
 	regular expressions with no '.'.
diff --git a/pygnulib/GLEmiter.py b/pygnulib/GLEmiter.py
index f85ef30d52..02fa6654e7 100644
--- a/pygnulib/GLEmiter.py
+++ b/pygnulib/GLEmiter.py
@@ -153,7 +153,7 @@ class GLEmiter(object):
                       if line.strip() ]
             snippet = '%s\n' % '\n'.join(lines)
             transformer = fileassistant.transformers.get('aux', '')
-            pattern = re.compile('(^.*?$)', re.S | re.M)
+            pattern = re.compile('^(.*)$', re.M)
             snippet = pattern.sub('%s\\1' % indentation, snippet)
             if transformer:
                 args = ['sed', '-e', transformer]
@@ -182,9 +182,9 @@ class GLEmiter(object):
                 emit += 'changequote([, ])dnl\n'
                 emit += 'AC_SUBST([LTALLOCA])'
         if replace_auxdir:
-            regex = 'AC_CONFIG_FILES\\(\\[(.*?)\\:build-aux/(.*?)\\]\\)'
+            regex = 'AC_CONFIG_FILES\\(\\[(.*)\\:build-aux/(.*)\\]\\)'
             repl = 'AC_CONFIG_FILES([\\1:%s/\\2])' % auxdir
-            pattern = re.compile(regex, re.S | re.M)
+            pattern = re.compile(regex, re.M)
             emit = pattern.sub(repl, emit)
         lines = [ line
                   for line in emit.split('\n')
@@ -688,9 +688,9 @@ AC_DEFUN([%V1%_LIBSOURCES], [
                 amsnippet1 = amsnippet1.replace('lib_LIBRARIES', 'lib%_LIBRARIES')
                 amsnippet1 = amsnippet1.replace('lib_LTLIBRARIES', 'lib%_LTLIBRARIES')
                 if LD_flags:
-                    pattern = re.compile('lib_LDFLAGS[\t ]*\\+=(.*?)$', re.S | re.M)
+                    pattern = re.compile('lib_LDFLAGS[\t ]*\\+=(.*)$', re.M)
                     amsnippet1 = pattern.sub('', amsnippet1)
-                pattern = re.compile('lib_([A-Z][A-Z](?:.*?))', re.S | re.M)
+                pattern = re.compile('lib_([A-Z][A-Z](?:.*))', re.M)
                 amsnippet1 = pattern.sub('%s_%s_\\1' % (libname, libext),
                                          amsnippet1)
                 amsnippet1 = amsnippet1.replace('$(GNULIB_', '$(' + module_indicator_prefix + '_GNULIB_')
@@ -708,7 +708,7 @@ AC_DEFUN([%V1%_LIBSOURCES], [
 
                 # Get unconditional snippet, edit it and save to amsnippet2.
                 amsnippet2 = module.getAutomakeSnippet_Unconditional()
-                pattern = re.compile('lib_([A-Z][A-Z](?:.*?))', re.S | re.M)
+                pattern = re.compile('lib_([A-Z][A-Z](?:.*))', re.M)
                 amsnippet2 = pattern.sub('%s_%s_\\1' % (libname, libext),
                                          amsnippet2)
                 amsnippet2 = amsnippet2.replace('$(GNULIB_',
@@ -802,7 +802,7 @@ AC_DEFUN([%V1%_LIBSOURCES], [
         insnippets = False
         inmakefile = False
         regex = '^[a-zA-Z0-9_]*_%sLIBRARIES *\\+{0,1}= *%s.%s' % (perhapsLT, libname, libext)
-        pattern = re.compile(regex, re.S | re.M)
+        pattern = re.compile(regex, re.M)
         insnippets = bool(pattern.findall(allsnippets))
         # Then test if $sourcebase/Makefile.am (if it exists) specifies it.
         path = joinpath(sourcebase, 'Makefile.am')
@@ -955,9 +955,9 @@ AC_DEFUN([%V1%_LIBSOURCES], [
                 snippet = snippet.replace('lib_LIBRARIES', 'lib%_LIBRARIES')
                 snippet = snippet.replace('lib_LTLIBRARIES', 'lib%_LTLIBRARIES')
                 if LD_flags:
-                    pattern = re.compile('lib_LDFLAGS[\t ]*\\+=(.*?)$', re.S | re.M)
+                    pattern = re.compile('lib_LDFLAGS[\t ]*\\+=(.*)$', re.M)
                     snippet = pattern.sub('', snippet)
-                pattern = re.compile('lib_([A-Z][A-Z](?:.*?))', re.S | re.M)
+                pattern = re.compile('lib_([A-Z][A-Z](?:.*))', re.M)
                 snippet = pattern.sub('libtests_a_\\1', snippet)
                 snippet = snippet.replace('$(GNULIB_', '$(' + module_indicator_prefix + '_GNULIB_')
                 snippet = snippet.replace('lib%_LIBRARIES', 'lib_LIBRARIES')
diff --git a/pygnulib/GLImport.py b/pygnulib/GLImport.py
index 7a6312ec39..818f3d57c0 100644
--- a/pygnulib/GLImport.py
+++ b/pygnulib/GLImport.py
@@ -95,7 +95,7 @@ class GLImport(object):
         self.config.setAutoconfFile(path)
         with codecs.open(path, 'rb', 'UTF-8') as file:
             data = file.read()
-        pattern = re.compile(r'^AC_CONFIG_AUX_DIR\((.*?)\)$', re.S | re.M)
+        pattern = re.compile(r'^AC_CONFIG_AUX_DIR\((.*)\)$', re.M)
         match = pattern.findall(data)
         if match:
             result = cleaner(match)[0]
@@ -106,7 +106,7 @@ class GLImport(object):
             self.config.setAuxDir(self.cache['auxdir'])
 
         # Guess autoconf version.
-        pattern = re.compile(r'.*AC_PREREQ\((.*?)\)', re.S | re.M)
+        pattern = re.compile(r'.*AC_PREREQ\((.*)\)', re.M)
         versions = cleaner(pattern.findall(data))
         if versions:
             version = sorted(set([ float(version)
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index 6b8f20e54f..afc7a472cf 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -705,7 +705,7 @@ Include:|Link:|License:|Maintainer:)'
                 # TODO: unconditional automake snippet for nontests modules
                 snippet = self.getAutomakeSnippet_Conditional()
                 snippet = constants.combine_lines(snippet)
-                pattern = re.compile('^lib_SOURCES[\t ]*\\+=[\t ]*(.*?)$', re.S | re.M)
+                pattern = re.compile('^lib_SOURCES[\t ]*\\+=[\t ]*(.*)$', re.M)
                 mentioned_files = pattern.findall(snippet)
                 if mentioned_files != list():
                     mentioned_files = mentioned_files[-1].split(' ')
@@ -780,7 +780,7 @@ Include:|Link:|License:|Maintainer:)'
                     parts += [line]
                 result = ''.join(parts)
             result = result.strip()
-            pattern = re.compile('^(["<].*?[>"])', re.S | re.M)
+            pattern = re.compile('^(["<].*[>"])', re.M)
             result = pattern.sub('#include \\1', result)
             self.cache['include'] = result
         return self.cache['include']
@@ -1164,7 +1164,7 @@ class GLModuleTable(object):
                 raise TypeError('each module must be a GLModule instance')
             snippet = module.getAutomakeSnippet()
             snippet = constants.remove_backslash_newline(snippet)
-            pattern = re.compile('^lib_SOURCES[\t ]*\\+=[\t ]*(.*?)$', re.S | re.M)
+            pattern = re.compile('^lib_SOURCES[\t ]*\\+=[\t ]*(.*)$', re.M)
             files = pattern.findall(snippet)
             if files:  # if source files were found
                 files = files[-1].split(' ')
diff --git a/pygnulib/GLTestDir.py b/pygnulib/GLTestDir.py
index 757c494011..f8d9910ab7 100644
--- a/pygnulib/GLTestDir.py
+++ b/pygnulib/GLTestDir.py
@@ -299,7 +299,7 @@ class GLTestDir(object):
                 notice = module.getNotice()
                 if notice:
                     print('Notice from module %s:' % str(module))
-                    pattern = re.compile('^(.*?)$', re.S | re.M)
+                    pattern = re.compile('^(.*)$', re.M)
                     notice = pattern.sub('  \\1', notice)
                     print(notice)
         else:  # if not single_configure
@@ -307,7 +307,7 @@ class GLTestDir(object):
                 notice = module.getNotice()
                 if notice:
                     print('Notice from module %s:' % str(module))
-                    pattern = re.compile('^(.*?)$', re.S | re.M)
+                    pattern = re.compile('^(.*)$', re.M)
                     notice = pattern.sub('  \\1', notice)
                     print(notice)
 
@@ -461,7 +461,7 @@ class GLTestDir(object):
                                   for line in snippet.split('\n')
                                   if line.strip() ]
                         snippet = '\n'.join(lines)
-                        pattern = re.compile('AC_REQUIRE\\(\\[([^()].*?)\\]\\)', re.S | re.M)
+                        pattern = re.compile('AC_REQUIRE\\(\\[([^()].*)\\]\\)', re.M)
                         snippet = pattern.sub('\\1', snippet)
                         snippet = snippet.strip()
                         snippets += [snippet]
@@ -578,7 +578,7 @@ class GLTestDir(object):
                           for line in snippet.split('\n')
                           if line.strip() ]
                 snippet = '\n'.join(lines)
-                pattern = re.compile('AC_REQUIRE\\(\\[([^()].*?)\\]\\)', re.S | re.M)
+                pattern = re.compile('AC_REQUIRE\\(\\[([^()].*)\\]\\)', re.M)
                 snippet = pattern.sub('\\1', snippet)
                 snippet = snippet.strip()
                 snippets += [snippet]
@@ -749,9 +749,9 @@ class GLTestDir(object):
 
         # Extract the value of "CLEANFILES += ..." and "MOSTLYCLEANFILES += ...".
         regex_find = list()
-        pattern = re.compile('^CLEANFILES[\t ]*\\+=(.*?)$', re.S | re.M)
+        pattern = re.compile('^CLEANFILES[\t ]*\\+=(.*)$', re.M)
         regex_find += pattern.findall(snippet)
-        pattern = re.compile('^MOSTLYCLEANFILES[\t ]*\\+=(.*?)$', re.S | re.M)
+        pattern = re.compile('^MOSTLYCLEANFILES[\t ]*\\+=(.*)$', re.M)
         regex_find += pattern.findall(snippet)
         regex_find = [ line.strip()
                        for line in regex_find
@@ -765,7 +765,7 @@ class GLTestDir(object):
         # Extract the value of "BUILT_SOURCES += ...". Remove variable references
         # such $(FOO_H) because they don't refer to distributed files.
         regex_find = list()
-        pattern = re.compile('^BUILT_SOURCES[\t ]*\\+=(.*?)$', re.S | re.M)
+        pattern = re.compile('^BUILT_SOURCES[\t ]*\\+=(.*)$', re.M)
         regex_find += pattern.findall(snippet)
         regex_find = [ line.strip()
                        for line in regex_find
@@ -791,9 +791,9 @@ class GLTestDir(object):
 
             # Extract the value of "CLEANFILES += ..." and "MOSTLYCLEANFILES += ...".
             regex_find = list()
-            pattern = re.compile('^CLEANFILES[\t ]*\\+=(.*?)$', re.S | re.M)
+            pattern = re.compile('^CLEANFILES[\t ]*\\+=(.*)$', re.M)
             regex_find += pattern.findall(snippet)
-            pattern = re.compile('^MOSTLYCLEANFILES[\t ]*\\+=(.*?)$', re.S | re.M)
+            pattern = re.compile('^MOSTLYCLEANFILES[\t ]*\\+=(.*)$', re.M)
             regex_find += pattern.findall(snippet)
             regex_find = [ line.strip()
                            for line in regex_find
@@ -808,7 +808,7 @@ class GLTestDir(object):
             # such $(FOO_H) because they don't refer to distributed files.
             regex_find = list()
             tests_built_sources = list()
-            pattern = re.compile('^BUILT_SOURCES[\t ]*\\+=(.*?)$', re.S | re.M)
+            pattern = re.compile('^BUILT_SOURCES[\t ]*\\+=(.*)$', re.M)
             regex_find += pattern.findall(snippet)
             regex_find = [ line.strip()
                            for line in regex_find
-- 
2.34.1

>From 9d722ddced2e46a06255be8eb24a54015f611af7 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Aug 2022 13:40:07 +0200
Subject: [PATCH 07/19] gnulib-tool.py: Make regex uses more straightforward.

* pygnulib/constants.py: Don't use the "minimal matching" *? construct
when it makes no difference (because we're matching a single line only
and flag re.M is not specified).
* pygnulib/GLModuleSystem.py: Likewise.
---
 ChangeLog                  | 6 ++++++
 pygnulib/GLModuleSystem.py | 2 +-
 pygnulib/constants.py      | 2 +-
 3 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 0e162e8d0a..f50e756d60 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Make regex uses more straightforward.
+	* pygnulib/constants.py: Don't use the "minimal matching" *? construct
+	when it makes no difference (because we're matching a single line only
+	and flag re.M is not specified).
+	* pygnulib/GLModuleSystem.py: Likewise.
+
 	gnulib-tool.py: Make regex uses more straightforward.
 	* pygnulib/GLModuleSystem.py: Don't use flag re.S on regular expressions
 	that are meant to match a single line only, and remove the use of the
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index afc7a472cf..e1b62ec56c 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -280,7 +280,7 @@ Include:|Link:|License:|Maintainer:)'
         '''GLModule.getName() -> str
 
         Return the name of the module.'''
-        pattern = re.compile(joinpath('modules', '(.*?)$'))
+        pattern = re.compile(joinpath('modules', '(.*)$'))
         result = pattern.findall(self.module)[0]
         return result
 
diff --git a/pygnulib/constants.py b/pygnulib/constants.py
index 46c0cbc152..ba0ebc9942 100644
--- a/pygnulib/constants.py
+++ b/pygnulib/constants.py
@@ -412,7 +412,7 @@ def filter_filelist(separator, filelist,
     listing = list()
     for filename in filelist:
         if filename.startswith(prefix) and filename.endswith(suffix):
-            pattern = re.compile('^%s(.*?)%s$'
+            pattern = re.compile('^%s(.*)%s$'
                                  % (removed_prefix, removed_suffix))
             result = pattern.sub('%s\\1%s'
                                  % (added_prefix, added_suffix), filename)
-- 
2.34.1

>From 5c087011efa2817a7e857f69a1cdd22c21a5053d Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Aug 2022 13:46:15 +0200
Subject: [PATCH 08/19] gnulib-tool.py: Fix some regex uses.

* pygnulib/GLModuleSystem.py (GLModuleTable.transitive_closure): Match
the regex against all lines of the snippet, not only the first line.
* pygnulib/GLEmiter.py (GLEmiter.autoconfSnippet): Likewise.
---
 ChangeLog                  | 5 +++++
 pygnulib/GLEmiter.py       | 2 +-
 pygnulib/GLModuleSystem.py | 2 +-
 3 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f50e756d60..a44504ae20 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Fix some regex uses.
+	* pygnulib/GLModuleSystem.py (GLModuleTable.transitive_closure): Match
+	the regex against all lines of the snippet, not only the first line.
+	* pygnulib/GLEmiter.py (GLEmiter.autoconfSnippet): Likewise.
+
 	gnulib-tool.py: Make regex uses more straightforward.
 	* pygnulib/constants.py: Don't use the "minimal matching" *? construct
 	when it makes no difference (because we're matching a single line only
diff --git a/pygnulib/GLEmiter.py b/pygnulib/GLEmiter.py
index 02fa6654e7..9aa638d693 100644
--- a/pygnulib/GLEmiter.py
+++ b/pygnulib/GLEmiter.py
@@ -174,7 +174,7 @@ class GLEmiter(object):
             else:
                 # Don't indent AM_GNU_GETTEXT_VERSION line, as that confuses
                 # autopoint through at least GNU gettext version 0.18.2.
-                snippet = re.compile('^ *AM_GNU_GETTEXT_VERSION').sub('AM_GNU_GETTEXT_VERSION', snippet)
+                snippet = re.compile('^ *AM_GNU_GETTEXT_VERSION', re.M).sub('AM_GNU_GETTEXT_VERSION', snippet)
             emit += snippet
             if str(module) == 'alloca' and libtool and not disable_libtool:
                 emit += 'changequote(,)dnl\n'
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index e1b62ec56c..75316515b8 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -1041,7 +1041,7 @@ class GLModuleTable(object):
                     if self.config['conddeps']:
                         automake_snippet = \
                             module.getAutomakeSnippet_Conditional()
-                        pattern = re.compile('^if')
+                        pattern = re.compile('^if', re.M)
                         if not pattern.findall(automake_snippet):
                             self.addUnconditional(module)
                         conditional = self.isConditional(module)
-- 
2.34.1

>From 6a795cb6b5c9f2b42058f444c61291f751c356e2 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Aug 2022 13:49:49 +0200
Subject: [PATCH 09/19] gnulib-tool.py: Fix some regexes.

* pygnulib/GLEmiter.py (GLEmiter.lib_Makefile_am): Use an equivalent
regex as gnulib-tool.
* pygnulib/GLTestDir.py (GLTestDir.execute): Likewise.
---
 ChangeLog             | 5 +++++
 pygnulib/GLEmiter.py  | 2 +-
 pygnulib/GLTestDir.py | 4 ++--
 3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index a44504ae20..b052583c01 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Fix some regexes.
+	* pygnulib/GLEmiter.py (GLEmiter.lib_Makefile_am): Use an equivalent
+	regex as gnulib-tool.
+	* pygnulib/GLTestDir.py (GLTestDir.execute): Likewise.
+
 	gnulib-tool.py: Fix some regex uses.
 	* pygnulib/GLModuleSystem.py (GLModuleTable.transitive_closure): Match
 	the regex against all lines of the snippet, not only the first line.
diff --git a/pygnulib/GLEmiter.py b/pygnulib/GLEmiter.py
index 9aa638d693..e0164ed7cc 100644
--- a/pygnulib/GLEmiter.py
+++ b/pygnulib/GLEmiter.py
@@ -801,7 +801,7 @@ AC_DEFUN([%V1%_LIBSOURCES], [
         # First test if allsnippets already specify an installation location.
         insnippets = False
         inmakefile = False
-        regex = '^[a-zA-Z0-9_]*_%sLIBRARIES *\\+{0,1}= *%s.%s' % (perhapsLT, libname, libext)
+        regex = '^[a-zA-Z0-9_]*_%sLIBRARIES *\\+{0,1}= *%s\\.%s' % (perhapsLT, libname, libext)
         pattern = re.compile(regex, re.M)
         insnippets = bool(pattern.findall(allsnippets))
         # Then test if $sourcebase/Makefile.am (if it exists) specifies it.
diff --git a/pygnulib/GLTestDir.py b/pygnulib/GLTestDir.py
index f8d9910ab7..9a3fde66ea 100644
--- a/pygnulib/GLTestDir.py
+++ b/pygnulib/GLTestDir.py
@@ -461,7 +461,7 @@ class GLTestDir(object):
                                   for line in snippet.split('\n')
                                   if line.strip() ]
                         snippet = '\n'.join(lines)
-                        pattern = re.compile('AC_REQUIRE\\(\\[([^()].*)\\]\\)', re.M)
+                        pattern = re.compile('AC_REQUIRE\\(\\[([^()]*)\\]\\)', re.M)
                         snippet = pattern.sub('\\1', snippet)
                         snippet = snippet.strip()
                         snippets += [snippet]
@@ -578,7 +578,7 @@ class GLTestDir(object):
                           for line in snippet.split('\n')
                           if line.strip() ]
                 snippet = '\n'.join(lines)
-                pattern = re.compile('AC_REQUIRE\\(\\[([^()].*)\\]\\)', re.M)
+                pattern = re.compile('AC_REQUIRE\\(\\[([^()]*)\\]\\)', re.M)
                 snippet = pattern.sub('\\1', snippet)
                 snippet = snippet.strip()
                 snippets += [snippet]
-- 
2.34.1

>From 72ac023526e84e7847cc67788be9afca2f3204fb Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Aug 2022 17:02:21 +0200
Subject: [PATCH 11/19] gnulib-tool.py: Implement option --find.

* pygnulib/GLModuleSystem.py (GLModuleSystem.file_is_module): New
method.
(GLModuleSystem.list): Filter the listing in memory; don't use a 'sed'
subprocess.
* gnulib-tool.py (main): Handle mode 'find'.
---
 ChangeLog                  |  7 +++++
 gnulib-tool.py             | 60 +++++++++++++++++++++++++++++++++++++-
 gnulib-tool.py.TODO        |  5 +++-
 pygnulib/GLModuleSystem.py | 59 +++++++++++++++++--------------------
 4 files changed, 97 insertions(+), 34 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index e259a09333..acb82e903c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Implement option --find.
+	* pygnulib/GLModuleSystem.py (GLModuleSystem.file_is_module): New
+	method.
+	(GLModuleSystem.list): Filter the listing in memory; don't use a 'sed'
+	subprocess.
+	* gnulib-tool.py (main): Handle mode 'find'.
+
 	gnulib-tool: Fix option --find in combination with option --local-dir.
 	* gnulib-tool (func_prefixed_modules_in_dir): New function.
 	(find): Use it, and filter the directory names away after the 'grep'
diff --git a/gnulib-tool.py b/gnulib-tool.py
index bb763a2245..032deee6e0 100755
--- a/gnulib-tool.py
+++ b/gnulib-tool.py
@@ -43,15 +43,18 @@ import codecs
 import random
 import argparse
 import subprocess as sp
+import shlex
 from tempfile import mktemp
 from pygnulib import constants
 from pygnulib import classes
+from pygnulib import GLError
 
 
 #===============================================================================
 # Define global constants
 #===============================================================================
 APP = constants.APP
+DIRS = constants.DIRS
 ENCS = constants.ENCS
 UTILS = constants.UTILS
 MODES = constants.MODES
@@ -422,6 +425,7 @@ def main():
     # Determine when user tries to combine modes.
     args = [
         cmdargs.mode_list,
+        cmdargs.mode_find,
         cmdargs.mode_import,
         cmdargs.mode_add_import,
         cmdargs.mode_remove_import,
@@ -460,6 +464,9 @@ def main():
     files = None
     if cmdargs.mode_list != None:
         mode = 'list'
+    if cmdargs.mode_find != None:
+        mode = 'find'
+        files = list(cmdargs.non_option_arguments)
     if cmdargs.mode_import != None:
         mode = 'import'
         modules = list(cmdargs.non_option_arguments)
@@ -701,13 +708,64 @@ def main():
     )
 
     # Work in the given mode.
-    if mode in ['list']:
+    if mode == 'list':
         modulesystem = classes.GLModuleSystem(config)
         listing = modulesystem.list()
         result = '\n'.join(listing)
         os.rmdir(config['tempdir'])
         print(result)
 
+    elif mode == 'find':
+        # Prepare GLModuleSystem.find to throw an exception.
+        config.setErrors(True)
+        modulesystem = classes.GLModuleSystem(config)
+        for filename in files:
+            if (isfile(joinpath(DIRS['root'], filename))
+                    or (localpath != None
+                        and any([ isfile(joinpath(localdir, filename))
+                                  for localdir in localpath ]))):
+                # Convert the file name to a POSIX basic regex.
+                # Needs to handle . [ \ * ^ $.
+                filename_regex = filename.replace('\\', '\\\\').replace('[', '\\[').replace('^', '\\^')
+                filename_regex = re.compile('([.*$])').sub('[\\1]', filename_regex)
+                filename_line_regex = '^' + filename_regex + '$'
+                # Read module candidates from gnulib root directory.
+                command = "find modules -type f -print | xargs -n 100 grep -l %s /dev/null | sed -e 's,^modules/,,'" % shlex.quote(filename_line_regex)
+                os.chdir(constants.DIRS['root'])
+                with sp.Popen(command, shell=True, stdout=sp.PIPE) as proc:
+                    result = proc.stdout.read().decode("UTF-8")
+                os.chdir(DIRS['cwd'])
+                # Read module candidates from local directories.
+                if localpath != None and len(localpath) > 0:
+                    command = "find modules -type f -print | xargs -n 100 grep -l %s /dev/null | sed -e 's,^modules/,,' -e 's,\\.diff$,,'" % shlex.quote(filename_line_regex)
+                    for localdir in localpath:
+                        os.chdir(localdir)
+                        with sp.Popen(command, shell=True, stdout=sp.PIPE) as proc:
+                            result += proc.stdout.read().decode("UTF-8")
+                        os.chdir(DIRS['cwd'])
+                listing = [ line
+                            for line in result.split('\n')
+                            if line.strip() ]
+                # Remove modules/ prefix from each file name.
+                pattern = re.compile('^modules/')
+                listing = [ pattern.sub('', line)
+                            for line in listing ]
+                # Filter out undesired file names.
+                listing = [ line
+                            for line in listing
+                            if modulesystem.file_is_module(line) ]
+                module_candidates = sorted(set(listing))
+                for module in module_candidates:
+                    try:
+                        if filename in modulesystem.find(module).getFiles():
+                            print(module)
+                    except GLError:
+                        # Ignore module candidates that don't actually exist.
+                        pass
+            else:
+                message = '%s: warning: file %s does not exist\n' % (constants.APP['name'], filename)
+                sys.stderr.write(message)
+
     elif mode in ['import', 'add-import', 'remove-import', 'update']:
         mode = MODES[mode]
         if not destdir:
diff --git a/gnulib-tool.py.TODO b/gnulib-tool.py.TODO
index 53915662b8..a46da5e2ad 100644
--- a/gnulib-tool.py.TODO
+++ b/gnulib-tool.py.TODO
@@ -20,7 +20,6 @@ Inline all 'sed' invocations.
 --------------------------------------------------------------------------------
 
 Implement the options:
-  --find
   --extract-recursive-dependencies
   --extract-recursive-link-directive
   --extract-tests-module
@@ -37,6 +36,10 @@ Implement the options:
 
 Remove exit() in GLImport.py.
 
+Optimize:
+  - GLModuleSystem: Parse each module description only once.
+  - os.chdir around subprocess creation -> cwd=... argument instead.
+
 --------------------------------------------------------------------------------
 
 commit 76c7703cb2e9e0e803d1296618d8ab9e86e13d6c
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index 75316515b8..3383b22b28 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -43,6 +43,7 @@ DIRS = constants.DIRS
 ENCS = constants.ENCS
 TESTS = constants.TESTS
 joinpath = constants.joinpath
+subend = constants.subend
 isdir = os.path.isdir
 isfile = os.path.isfile
 filter_filelist = constants.filter_filelist
@@ -113,6 +114,18 @@ class GLModuleSystem(object):
                 sys.stderr.write('gnulib-tool: warning: ')
                 sys.stderr.write('file %s does not exist\n' % str(module))
 
+    def file_is_module(self, filename):
+        '''Given the name of a file in the modules/ directory, return true
+        if should be viewed as a module description file.'''
+        return not (filename == 'ChangeLog' or filename.endswith('/ChangeLog')
+                    or filename == 'COPYING' or filename.endswith('/COPYING')
+                    or filename == 'README' or filename.endswith('/README')
+                    or filename == 'TEMPLATE'
+                    or filename == 'TEMPLATE-EXTENDED'
+                    or filename == 'TEMPLATE-TESTS'
+                    or filename.startswith('.')
+                    or filename.endswith('~'))
+
     def list(self):
         '''GLModuleSystem.list() -> list
 
@@ -123,23 +136,6 @@ class GLModuleSystem(object):
         listing = list()
         localpath = self.config['localpath']
         find_args = ['find', 'modules', '-type', 'f', '-print']
-        sed_args = \
-            [
-                'sed',
-                '-e', r's,^modules/,,',
-                '-e', r'/^ChangeLog$/d',
-                '-e', r'/\/ChangeLog$/d',
-                '-e', r'/^COPYING$/d',
-                '-e', r'/\/COPYING$/d',
-                '-e', r'/^README$/d',
-                '-e', r'/\/README$/d',
-                '-e', r'/^TEMPLATE$/d',
-                '-e', r'/^TEMPLATE-EXTENDED$/d',
-                '-e', r'/^TEMPLATE-TESTS$/d',
-                '-e', r'/^\..*/d',
-                '-e', r'/~$/d',
-                '-e', r'/-tests$/d',
-            ]
 
         # Read modules from gnulib root directory.
         os.chdir(constants.DIRS['root'])
@@ -154,24 +150,23 @@ class GLModuleSystem(object):
                 find = sp.Popen(find_args, stdout=sp.PIPE)
                 result += find.stdout.read().decode("UTF-8")
                 os.chdir(DIRS['cwd'])
-            sed_args += ['-e', r's,\.diff$,,']
-
-        # Save the list of the modules to file.
-        path = joinpath(self.config['tempdir'], 'list')
-        with codecs.open(path, 'wb', 'UTF-8') as file:
-            file.write(result)
-
-        # Filter the list of the modules.
-        stdin = codecs.open(path, 'rb', 'UTF-8')
-        sed = sp.Popen(sed_args, stdin=stdin, stdout=sp.PIPE)
-        result = sed.stdout.read().decode("UTF-8")
-        stdin.close()
-        os.remove(path)
+
         listing = [ line
                     for line in result.split('\n')
                     if line.strip() ]
-        listing = sorted(set(listing))
-        return listing
+        if len(localpath) > 0:
+            listing = [ subend('.diff', '', line)
+                        for line in listing ]
+        # Remove modules/ prefix from each file name.
+        pattern = re.compile('^modules/')
+        listing = [ pattern.sub('', line)
+                    for line in listing ]
+        # Filter out undesired file names.
+        listing = [ line
+                    for line in listing
+                    if self.file_is_module(line) and not line.endswith('-tests') ]
+        modules = sorted(set(listing))
+        return modules
 
 
 #===============================================================================
-- 
2.34.1

>From 8341b4a206b8f1f6ec14966292afb5134de2a4c2 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Aug 2022 17:29:16 +0200
Subject: [PATCH 12/19] gnulib-tool.py: Reduce code duplication.

* pygnulib/GLModuleSystem.py (GLModule): Declare two regexes are class
variables.
---
 ChangeLog                  |  4 ++
 pygnulib/GLModuleSystem.py | 84 +++++++++++---------------------------
 2 files changed, 27 insertions(+), 61 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index acb82e903c..a8d020edab 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Reduce code duplication.
+	* pygnulib/GLModuleSystem.py (GLModule): Declare two regexes are class
+	variables.
+
 	gnulib-tool.py: Implement option --find.
 	* pygnulib/GLModuleSystem.py (GLModuleSystem.file_is_module): New
 	method.
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index 3383b22b28..33495f9c07 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -177,6 +177,15 @@ class GLModule(object):
     path. GLModule can get all information about module, get its dependencies,
     files, etc.'''
 
+    section_label_regex = '(?:Description:|Comment:|Status:|Notice:|Applicability:|\
+Files:|Depends-on:|configure\\.ac-early:|configure\\.ac:|Makefile\\.am:|\
+Include:|Link:|License:|Maintainer:)'
+
+    section_label_pattern = \
+        re.compile('^(Description|Comment|Status|Notice|Applicability|'
+                   + 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
+                   + 'Makefile\\.am|Include|Link|License|Maintainer):$')
+
     def __init__(self, config, module, patched=False):
         '''GLModule.__init__(config, module[, patched]) -> GLModule
 
@@ -202,9 +211,6 @@ class GLModule(object):
         self.modulesystem = GLModuleSystem(self.config)
         with codecs.open(module, 'rb', 'UTF-8') as file:
             self.content = file.read()
-        self.regex = '(?:Description:|Comment:|Status:|Notice:|Applicability:|\
-Files:|Depends-on:|configure\\.ac-early:|configure\\.ac:|Makefile\\.am:|\
-Include:|Link:|License:|Maintainer:)'
 
     def __eq__(self, module):
         '''x.__eq__(y) <==> x==y'''
@@ -379,7 +385,7 @@ Include:|Link:|License:|Maintainer:)'
             if section not in self.content:
                 result = ''
             else:  # if section in self.content
-                pattern = '^%s[\t ]*(.*?)%s' % (section, self.regex)
+                pattern = '^%s[\t ]*(.*?)%s' % (section, GLModule.section_label_regex)
                 pattern = re.compile(pattern, re.S | re.M)
                 result = pattern.findall(self.content)
                 if type(result) is list:
@@ -400,7 +406,7 @@ Include:|Link:|License:|Maintainer:)'
             if section not in self.content:
                 result = ''
             else:  # if section in self.content
-                pattern = '^%s[\t ]*(.*?)%s' % (section, self.regex)
+                pattern = '^%s[\t ]*(.*?)%s' % (section, GLModule.section_label_regex)
                 pattern = re.compile(pattern, re.S | re.M)
                 result = pattern.findall(self.content)
                 if type(result) is list:
@@ -427,11 +433,7 @@ Include:|Link:|License:|Maintainer:)'
                           for line in snippet.split('\n') ]
                 parts = list()
                 for line in lines:
-                    regex = '^(Description|Comment|Status|Notice|Applicability|'
-                    regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                    regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
-                    pattern = re.compile(regex)
-                    findflag = pattern.findall(line)
+                    findflag = GLModule.section_label_pattern.findall(line)
                     if findflag:
                         break
                     parts += [line]
@@ -456,11 +458,7 @@ Include:|Link:|License:|Maintainer:)'
                           for line in snippet.split('\n') ]
                 parts = list()
                 for line in lines:
-                    regex = '^(Description|Comment|Status|Notice|Applicability|'
-                    regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                    regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
-                    pattern = re.compile(regex)
-                    findflag = pattern.findall(line)
+                    findflag = GLModule.section_label_pattern.findall(line)
                     if findflag:
                         break
                     parts += [line]
@@ -483,11 +481,7 @@ Include:|Link:|License:|Maintainer:)'
                           for line in snippet.split('\n') ]
                 parts = list()
                 for line in lines:
-                    regex = '^(Description|Comment|Status|Notice|Applicability|'
-                    regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                    regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
-                    pattern = re.compile(regex)
-                    findflag = pattern.findall(line)
+                    findflag = GLModule.section_label_pattern.findall(line)
                     if findflag:
                         break
                     parts += [line]
@@ -521,11 +515,7 @@ Include:|Link:|License:|Maintainer:)'
                           for line in snippet.split('\n') ]
                 parts = list()
                 for line in lines:
-                    regex = '^(Description|Comment|Status|Notice|Applicability|'
-                    regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                    regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
-                    pattern = re.compile(regex)
-                    findflag = pattern.findall(line)
+                    findflag = GLModule.section_label_pattern.findall(line)
                     if findflag:
                         break
                     parts += [line]
@@ -555,11 +545,7 @@ Include:|Link:|License:|Maintainer:)'
                           for line in snippet.split('\n') ]
                 parts = list()
                 for line in lines:
-                    regex = '^(Description|Comment|Status|Notice|Applicability|'
-                    regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                    regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
-                    pattern = re.compile(regex)
-                    findflag = pattern.findall(line)
+                    findflag = GLModule.section_label_pattern.findall(line)
                     if findflag:
                         break
                     parts += [line]
@@ -599,11 +585,7 @@ Include:|Link:|License:|Maintainer:)'
                           for line in snippet.split('\n') ]
                 parts = list()
                 for line in lines:
-                    regex = '^(Description|Comment|Status|Notice|Applicability|'
-                    regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                    regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
-                    pattern = re.compile(regex)
-                    findflag = pattern.findall(line)
+                    findflag = GLModule.section_label_pattern.findall(line)
                     if findflag:
                         break
                     parts += [line]
@@ -626,11 +608,7 @@ Include:|Link:|License:|Maintainer:)'
                           for line in snippet.split('\n') ]
                 parts = list()
                 for line in lines:
-                    regex = '^(Description|Comment|Status|Notice|Applicability|'
-                    regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                    regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
-                    pattern = re.compile(regex)
-                    findflag = pattern.findall(line)
+                    findflag = GLModule.section_label_pattern.findall(line)
                     if findflag:
                         break
                     parts += [line]
@@ -667,11 +645,7 @@ Include:|Link:|License:|Maintainer:)'
                           for line in snippet.split('\n') ]
                 parts = list()
                 for line in lines:
-                    regex = '^(Description|Comment|Status|Notice|Applicability|'
-                    regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                    regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
-                    pattern = re.compile(regex)
-                    findflag = pattern.findall(line)
+                    findflag = GLModule.section_label_pattern.findall(line)
                     if findflag:
                         break
                     parts += [line]
@@ -765,11 +739,7 @@ Include:|Link:|License:|Maintainer:)'
                           for line in snippet.split('\n') ]
                 parts = list()
                 for line in lines:
-                    regex = '^(Description|Comment|Status|Notice|Applicability|'
-                    regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                    regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
-                    pattern = re.compile(regex)
-                    findflag = pattern.findall(line)
+                    findflag = GLModule.section_label_pattern.findall(line)
                     if findflag:
                         break
                     parts += [line]
@@ -793,11 +763,7 @@ Include:|Link:|License:|Maintainer:)'
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 for line in lines:
-                    regex = '^(Description|Comment|Status|Notice|Applicability|'
-                    regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                    regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
-                    pattern = re.compile(regex)
-                    findflag = pattern.findall(line)
+                    findflag = GLModule.section_label_pattern.findall(line)
                     if findflag:
                         break
                     parts += [line]
@@ -838,7 +804,7 @@ Include:|Link:|License:|Maintainer:)'
             if section not in self.content:
                 result = ''
             else:  # if section in self.content
-                pattern = '^%s[\t ]*(.*?)%s' % (section, self.regex)
+                pattern = '^%s[\t ]*(.*?)%s' % (section, GLModule.section_label_regex)
                 pattern = re.compile(pattern, re.S | re.M)
                 result = pattern.findall(self.content)
                 if type(result) is list:
@@ -865,11 +831,7 @@ Include:|Link:|License:|Maintainer:)'
                           for line in snippet.split('\n') ]
                 parts = list()
                 for line in lines:
-                    regex = '^(Description|Comment|Status|Notice|Applicability|'
-                    regex += 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                    regex += 'Makefile\\.am|Include|Link|License|Maintainer):$'
-                    pattern = re.compile(regex)
-                    findflag = pattern.findall(line)
+                    findflag = GLModule.section_label_pattern.findall(line)
                     if findflag:
                         break
                     parts += [line]
-- 
2.34.1

>From 9b2a7c800a82de247a92d43c10cc926fdcc11031 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Aug 2022 19:26:51 +0200
Subject: [PATCH 13/19] gnulib-tool.py: Simplify.

* pygnulib/GLModuleSystem.py (GLModule): Convert Windows newlines right
after reading the module description, not in every accessor.
---
 ChangeLog                  |  4 ++++
 pygnulib/GLModuleSystem.py | 13 +------------
 2 files changed, 5 insertions(+), 12 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index a8d020edab..fceb1538a8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Simplify.
+	* pygnulib/GLModuleSystem.py (GLModule): Convert Windows newlines right
+	after reading the module description, not in every accessor.
+
 	gnulib-tool.py: Reduce code duplication.
 	* pygnulib/GLModuleSystem.py (GLModule): Declare two regexes are class
 	variables.
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index 33495f9c07..b736bf2ed1 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -210,7 +210,7 @@ Include:|Link:|License:|Maintainer:)'
         self.filesystem = GLFileSystem(self.config)
         self.modulesystem = GLModuleSystem(self.config)
         with codecs.open(module, 'rb', 'UTF-8') as file:
-            self.content = file.read()
+            self.content = file.read().replace('\r\n', '\n')
 
     def __eq__(self, module):
         '''x.__eq__(y) <==> x==y'''
@@ -428,7 +428,6 @@ Include:|Link:|License:|Maintainer:)'
                 result = ''
             else:  # if section in self.content
                 snippet = self.content.split(section)[-1]
-                snippet = snippet.replace('\r\n', '\n')
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 parts = list()
@@ -453,7 +452,6 @@ Include:|Link:|License:|Maintainer:)'
                 result = ''
             else:  # if section in self.content
                 snippet = self.content.split(section)[-1]
-                snippet = snippet.replace('\r\n', '\n')
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 parts = list()
@@ -476,7 +474,6 @@ Include:|Link:|License:|Maintainer:)'
                 result = ''
             else:  # if section in self.content
                 snippet = self.content.split(section)[-1]
-                snippet = snippet.replace('\r\n', '\n')
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 parts = list()
@@ -510,7 +507,6 @@ Include:|Link:|License:|Maintainer:)'
                 result = list()
             else:  # if section in self.content
                 snippet = self.content.split(section)[-1]
-                snippet = snippet.replace('\r\n', '\n')
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 parts = list()
@@ -540,7 +536,6 @@ Include:|Link:|License:|Maintainer:)'
                 depmodules = list()
             else:  # if section in self.content
                 snippet = self.content.split(section)[-1]
-                snippet = snippet.replace('\r\n', '\n')
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 parts = list()
@@ -580,7 +575,6 @@ Include:|Link:|License:|Maintainer:)'
                 result = ''
             else:  # if section in self.content
                 snippet = self.content.split(section)[-1]
-                snippet = snippet.replace('\r\n', '\n')
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 parts = list()
@@ -603,7 +597,6 @@ Include:|Link:|License:|Maintainer:)'
                 result = ''
             else:  # if section in self.content
                 snippet = self.content.split(section)[-1]
-                snippet = snippet.replace('\r\n', '\n')
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 parts = list()
@@ -640,7 +633,6 @@ Include:|Link:|License:|Maintainer:)'
                 result = ''
             else:  # if section in self.content
                 snippet = self.content.split(section)[-1]
-                snippet = snippet.replace('\r\n', '\n')
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 parts = list()
@@ -734,7 +726,6 @@ Include:|Link:|License:|Maintainer:)'
                 result = ''
             else:  # if section in self.content
                 snippet = self.content.split(section)[-1]
-                snippet = snippet.replace('\r\n', '\n')
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 parts = list()
@@ -759,7 +750,6 @@ Include:|Link:|License:|Maintainer:)'
             parts = list()
             if section in self.content:
                 snippet = self.content.split(section)[-1]
-                snippet = snippet.replace('\r\n', '\n')
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 for line in lines:
@@ -826,7 +816,6 @@ Include:|Link:|License:|Maintainer:)'
                 result = ''
             else:  # if section in self.content
                 snippet = self.content.split(section)[-1]
-                snippet = snippet.replace('\r\n', '\n')
                 lines = [ '%s\n' % line
                           for line in snippet.split('\n') ]
                 parts = list()
-- 
2.34.1

>From 586000e597d4ef7cba8de869a15ad5c922a2010c Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Aug 2022 20:04:56 +0200
Subject: [PATCH 14/19] gnulib-tool.py: Improve field naming.

* pygnulib/GLModuleSystem.py (GLModule): Rename field 'module' to
'path'. Fix a typo in a TypeError message.
---
 ChangeLog                  |  4 ++++
 pygnulib/GLModuleSystem.py | 38 ++++++++++++++++++--------------------
 2 files changed, 22 insertions(+), 20 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index fceb1538a8..36962c2dd2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Improve field naming.
+	* pygnulib/GLModuleSystem.py (GLModule): Rename field 'module' to
+	'path'. Fix a typo in a TypeError message.
+
 	gnulib-tool.py: Simplify.
 	* pygnulib/GLModuleSystem.py (GLModule): Convert Windows newlines right
 	after reading the module description, not in every accessor.
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index b736bf2ed1..ec2ff0c35c 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -186,11 +186,11 @@ Include:|Link:|License:|Maintainer:)'
                    + 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
                    + 'Makefile\\.am|Include|Link|License|Maintainer):$')
 
-    def __init__(self, config, module, patched=False):
-        '''GLModule.__init__(config, module[, patched]) -> GLModule
+    def __init__(self, config, path, patched=False):
+        '''GLModule.__init__(config, path[, patched]) -> GLModule
 
-        Create new GLModule instance. Arguments are module and patched, where
-        module is a string representing the path to the module and patched is a
+        Create new GLModule instance. Arguments are path and patched, where
+        path is a string representing the path to the module and patched is a
         bool indicating that module was created after applying patch.'''
         self.args = dict()
         self.cache = dict()
@@ -198,25 +198,25 @@ Include:|Link:|License:|Maintainer:)'
         if type(config) is not GLConfig:
             raise TypeError('config must be a GLConfig, not %s'
                             % type(config).__name__)
-        if type(module) is not str:
-            raise TypeError('module must be a string, not %s'
-                            % type(module).__name__)
+        if type(path) is not str:
+            raise TypeError('path must be a string, not %s'
+                            % type(path).__name__)
         if type(patched) is not bool:
             raise TypeError('patched must be a bool, not %s'
-                            % type(module).__name__)
-        self.module = module
+                            % type(patched).__name__)
+        self.path = path
         self.patched = patched
         self.config = config
         self.filesystem = GLFileSystem(self.config)
         self.modulesystem = GLModuleSystem(self.config)
-        with codecs.open(module, 'rb', 'UTF-8') as file:
+        with codecs.open(path, 'rb', 'UTF-8') as file:
             self.content = file.read().replace('\r\n', '\n')
 
     def __eq__(self, module):
         '''x.__eq__(y) <==> x==y'''
         result = bool()
         if type(module) is GLModule:
-            if self.module == module.module:
+            if self.path == module.path:
                 result = True
         return result
 
@@ -224,7 +224,7 @@ Include:|Link:|License:|Maintainer:)'
         '''x.__ne__(y) <==> x!=y'''
         result = bool()
         if type(module) is GLModule:
-            if self.module != module.module:
+            if self.path != module.path:
                 result = True
         return result
 
@@ -232,7 +232,7 @@ Include:|Link:|License:|Maintainer:)'
         '''x.__ge__(y) <==> x>=y'''
         result = bool()
         if type(module) is GLModule:
-            if self.module >= module.module:
+            if self.path >= module.path:
                 result = True
         return result
 
@@ -240,22 +240,20 @@ Include:|Link:|License:|Maintainer:)'
         '''x.__gt__(y) <==> x>y'''
         result = bool()
         if type(module) is GLModule:
-            if self.module > module.module:
+            if self.path > module.path:
                 result = True
         return result
 
     def __hash__(self):
         '''x.__hash__() <==> hash(x)'''
-        module = hash(self.module)
-        patched = hash(self.patched)
-        result = module ^ patched
+        result = hash(self.path) ^ hash(self.patched)
         return result
 
     def __le__(self, module):
         '''x.__le__(y) <==> x<=y'''
         result = bool()
         if type(module) is GLModule:
-            if self.module <= module.module:
+            if self.path <= module.path:
                 result = True
         return result
 
@@ -263,7 +261,7 @@ Include:|Link:|License:|Maintainer:)'
         '''x.__lt__(y) <==> x<y'''
         result = bool()
         if type(module) is GLModule:
-            if self.module < module.module:
+            if self.path < module.path:
                 result = True
         return result
 
@@ -282,7 +280,7 @@ Include:|Link:|License:|Maintainer:)'
 
         Return the name of the module.'''
         pattern = re.compile(joinpath('modules', '(.*)$'))
-        result = pattern.findall(self.module)[0]
+        result = pattern.findall(self.path)[0]
         return result
 
     def isPatched(self):
-- 
2.34.1

>From 81b8c4d5565dbbea10eb3561063d2e8da52148d7 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Aug 2022 22:53:32 +0200
Subject: [PATCH 15/19] gnulib-tool.py: Fix section extraction from module
 descriptions.

The code with   self.content.split(section)[-1]
was broken because it recognizes an indented section label.
Similar code with   ('\n' + self.content).split('\n' + section)[-1]
would still be broken because it recognizes an indented section label
in the first line of the file.
The code with   section_label_regex
was broken because sometimes it returns the second-to-last section with
the given label, not the last one.
Also, whitespace after the colon was not ignored.

* pygnulib/GLModuleSystem.py (GLModule.__init__): Dissect the module
description's contents immediately, once only, in a reliable way.
(GLModule.getDescription, GLModule.getComment): Simplify.
(GLModule.getStatus): Simplify. Return a string.
(GLModule.getStatuses): New function. Return a list.
(GLModule.getNotice, GLModule.getApplicability, GLModule.getFiles,
GLModule.getDependencies, GLModules.getAutoconfSnippet_Early,
GLModules.getAutoconfSnippet, GLModule.getAutomakeSnippet_Conditional,
GLModule.getInclude, GLModule.getLink, GLModule.getLicense_Raw):
Simplify.
(GLModule.getLicense): Remove whitespace after calling getLicense_Raw.
(GLModule.getMaintainer): Simplify.
(GLModuleTable.transitive_closure): Call getStatuses() instead of
getStatus().
* pygnulib/GLEmiter.py: Likewise.
* gnulib-tool.py (main): For --extract-description, --extract-comment,
--extract-status, --extract-notice, --extract-autoconf-snippet,
--extract-automake-snippet, --extract-include-directive,
--extract-link-directive, --extract-maintainer, don't add an extra
newline after the snippet.
---
 ChangeLog                  |  31 +++
 gnulib-tool.py             |  19 +-
 gnulib-tool.py.TODO        |   1 -
 pygnulib/GLEmiter.py       |   4 +-
 pygnulib/GLModuleSystem.py | 387 ++++++++++---------------------------
 5 files changed, 140 insertions(+), 302 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 36962c2dd2..305d02be51 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,36 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Fix section extraction from module descriptions.
+	The code with   self.content.split(section)[-1]
+	was broken because it recognizes an indented section label.
+	Similar code with   ('\n' + self.content).split('\n' + section)[-1]
+	would still be broken because it recognizes an indented section label
+	in the first line of the file.
+	The code with   section_label_regex
+	was broken because sometimes it returns the second-to-last section with
+	the given label, not the last one.
+	Also, whitespace after the colon was not ignored.
+	* pygnulib/GLModuleSystem.py (GLModule.__init__): Dissect the module
+	description's contents immediately, once only, in a reliable way.
+	(GLModule.getDescription, GLModule.getComment): Simplify.
+	(GLModule.getStatus): Simplify. Return a string.
+	(GLModule.getStatuses): New function. Return a list.
+	(GLModule.getNotice, GLModule.getApplicability, GLModule.getFiles,
+	GLModule.getDependencies, GLModules.getAutoconfSnippet_Early,
+	GLModules.getAutoconfSnippet, GLModule.getAutomakeSnippet_Conditional,
+	GLModule.getInclude, GLModule.getLink, GLModule.getLicense_Raw):
+	Simplify.
+	(GLModule.getLicense): Remove whitespace after calling getLicense_Raw.
+	(GLModule.getMaintainer): Simplify.
+	(GLModuleTable.transitive_closure): Call getStatuses() instead of
+	getStatus().
+	* pygnulib/GLEmiter.py: Likewise.
+	* gnulib-tool.py (main): For --extract-description, --extract-comment,
+	--extract-status, --extract-notice, --extract-autoconf-snippet,
+	--extract-automake-snippet, --extract-include-directive,
+	--extract-link-directive, --extract-maintainer, don't add an extra
+	newline after the snippet.
+
 	gnulib-tool.py: Improve field naming.
 	* pygnulib/GLModuleSystem.py (GLModule): Rename field 'module' to
 	'path'. Fix a typo in a TypeError message.
diff --git a/gnulib-tool.py b/gnulib-tool.py
index 032deee6e0..0e888e6fd8 100755
--- a/gnulib-tool.py
+++ b/gnulib-tool.py
@@ -974,29 +974,28 @@ def main():
         modules = [ modulesystem.find(module)
                     for module in modules ]
         for module in modules:
-            print(module.getDescription())
+            sys.stdout.write(module.getDescription())
 
     elif mode == 'extract-comment':
         modulesystem = classes.GLModuleSystem(config)
         modules = [ modulesystem.find(module)
                     for module in modules ]
         for module in modules:
-            print(module.getComment())
+            sys.stdout.write(module.getComment())
 
     elif mode == 'extract-status':
         modulesystem = classes.GLModuleSystem(config)
         modules = [ modulesystem.find(module)
                     for module in modules ]
         for module in modules:
-            status = module.getStatus()
-            print('\n'.join(status))
+            sys.stdout.write(module.getStatus())
 
     elif mode == 'extract-notice':
         modulesystem = classes.GLModuleSystem(config)
         modules = [ modulesystem.find(module)
                     for module in modules ]
         for module in modules:
-            print(module.getNotice())
+            sys.stdout.write(module.getNotice())
 
     elif mode == 'extract-applicability':
         modulesystem = classes.GLModuleSystem(config)
@@ -1039,28 +1038,28 @@ def main():
         modules = [ modulesystem.find(module)
                     for module in modules ]
         for module in modules:
-            print(module.getAutoconfSnippet())
+            sys.stdout.write(module.getAutoconfSnippet())
 
     elif mode == 'extract-automake-snippet':
         modulesystem = classes.GLModuleSystem(config)
         modules = [ modulesystem.find(module)
                     for module in modules ]
         for module in modules:
-            print(module.getAutomakeSnippet())
+            sys.stdout.write(module.getAutomakeSnippet())
 
     elif mode == 'extract-include-directive':
         modulesystem = classes.GLModuleSystem(config)
         modules = [ modulesystem.find(module)
                     for module in modules ]
         for module in modules:
-            print(module.getInclude())
+            sys.stdout.write(module.getInclude())
 
     elif mode == 'extract-link-directive':
         modulesystem = classes.GLModuleSystem(config)
         modules = [ modulesystem.find(module)
                     for module in modules ]
         for module in modules:
-            print(module.getLink())
+            sys.stdout.write(module.getLink())
 
     elif mode == 'extract-license':
         modulesystem = classes.GLModuleSystem(config)
@@ -1074,7 +1073,7 @@ def main():
         modules = [ modulesystem.find(module)
                     for module in modules ]
         for module in modules:
-            print(module.getMaintainer())
+            sys.stdout.write(module.getMaintainer())
 
     elif mode == 'extract-tests-module':
         modulesystem = classes.GLModuleSystem(config)
diff --git a/gnulib-tool.py.TODO b/gnulib-tool.py.TODO
index a46da5e2ad..9efcda510d 100644
--- a/gnulib-tool.py.TODO
+++ b/gnulib-tool.py.TODO
@@ -37,7 +37,6 @@ Implement the options:
 Remove exit() in GLImport.py.
 
 Optimize:
-  - GLModuleSystem: Parse each module description only once.
   - os.chdir around subprocess creation -> cwd=... argument instead.
 
 --------------------------------------------------------------------------------
diff --git a/pygnulib/GLEmiter.py b/pygnulib/GLEmiter.py
index e0164ed7cc..61cabc92fa 100644
--- a/pygnulib/GLEmiter.py
+++ b/pygnulib/GLEmiter.py
@@ -974,9 +974,9 @@ AC_DEFUN([%V1%_LIBSOURCES], [
                 # Skip the contents if it's entirely empty.
                 if snippet.strip():
                     # Check status of the module.
-                    status = module.getStatus()
+                    statuses = module.getStatuses()
                     islongrun = False
-                    for word in status:
+                    for word in statuses:
                         if word == 'longrunning-test':
                             islongrun = True
                             break
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index ec2ff0c35c..32a133009a 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -177,14 +177,11 @@ class GLModule(object):
     path. GLModule can get all information about module, get its dependencies,
     files, etc.'''
 
-    section_label_regex = '(?:Description:|Comment:|Status:|Notice:|Applicability:|\
-Files:|Depends-on:|configure\\.ac-early:|configure\\.ac:|Makefile\\.am:|\
-Include:|Link:|License:|Maintainer:)'
-
     section_label_pattern = \
         re.compile('^(Description|Comment|Status|Notice|Applicability|'
                    + 'Files|Depends-on|configure\\.ac-early|configure\\.ac|'
-                   + 'Makefile\\.am|Include|Link|License|Maintainer):$')
+                   + 'Makefile\\.am|Include|Link|License|Maintainer):$',
+                   re.M)
 
     def __init__(self, config, path, patched=False):
         '''GLModule.__init__(config, path[, patched]) -> GLModule
@@ -209,8 +206,20 @@ Include:|Link:|License:|Maintainer:)'
         self.config = config
         self.filesystem = GLFileSystem(self.config)
         self.modulesystem = GLModuleSystem(self.config)
+        # Read the module description file into memory.
         with codecs.open(path, 'rb', 'UTF-8') as file:
             self.content = file.read().replace('\r\n', '\n')
+        # Dissect it into sections.
+        self.sections = dict()
+        last_section_label = None
+        last_section_start = 0
+        for match in GLModule.section_label_pattern.finditer(self.content):
+            if last_section_label != None:
+                self.sections[last_section_label] = self.content[last_section_start : match.start()]
+            last_section_label = match.group(1)
+            last_section_start = match.end() + 1
+        if last_section_label != None:
+            self.sections[last_section_label] = self.content[last_section_start:]
 
     def __eq__(self, module):
         '''x.__eq__(y) <==> x==y'''
@@ -378,117 +387,51 @@ Include:|Link:|License:|Maintainer:)'
         '''GLModule.getDescription() -> str
 
         Return description of the module.'''
-        section = 'Description:'
-        if 'description' not in self.cache:
-            if section not in self.content:
-                result = ''
-            else:  # if section in self.content
-                pattern = '^%s[\t ]*(.*?)%s' % (section, GLModule.section_label_regex)
-                pattern = re.compile(pattern, re.S | re.M)
-                result = pattern.findall(self.content)
-                if type(result) is list:
-                    if not result:
-                        result = ''
-                    else:  # if result
-                        result = result[-1]
-            result = result.strip()
-            self.cache['description'] = result
-        return self.cache['description']
+        return self.sections.get('Description', '')
 
     def getComment(self):
         '''GLModule.getComment() -> str
 
         Return comment to module.'''
-        section = 'Comment:'
-        if 'comment' not in self.cache:
-            if section not in self.content:
-                result = ''
-            else:  # if section in self.content
-                pattern = '^%s[\t ]*(.*?)%s' % (section, GLModule.section_label_regex)
-                pattern = re.compile(pattern, re.S | re.M)
-                result = pattern.findall(self.content)
-                if type(result) is list:
-                    if not result:
-                        result = ''
-                    else:  # if result
-                        result = result[-1]
-            result = result.strip()
-            self.cache['comment'] = result
-        return self.cache['comment']
+        return self.sections.get('Comment', '')
 
     def getStatus(self):
         '''GLModule.getStatus() -> str
 
         Return module status.'''
-        section = 'Status:'
-        if 'status' not in self.cache:
-            if section not in self.content:
-                result = ''
-            else:  # if section in self.content
-                snippet = self.content.split(section)[-1]
-                lines = [ '%s\n' % line
-                          for line in snippet.split('\n') ]
-                parts = list()
-                for line in lines:
-                    findflag = GLModule.section_label_pattern.findall(line)
-                    if findflag:
-                        break
-                    parts += [line]
-                result = [ part.strip()
-                           for part in parts
-                           if part.strip() ]
-            self.cache['status'] = list(result)
-        return list(self.cache['status'])
+        return self.sections.get('Status', '')
+
+    def getStatuses(self):
+        '''GLModule.getStatuses() -> list
+
+        Return module status.'''
+        if 'statuses' not in self.cache:
+            snippet = self.getStatus()
+            result = [ line.strip()
+                       for line in snippet.split('\n')
+                       if line.strip() ]
+            self.cache['statuses'] = result
+        return self.cache['statuses']
 
     def getNotice(self):
         '''GLModule.getNotice() -> str
 
         Return notice to module.'''
-        section = 'Notice:'
-        if 'notice' not in self.cache:
-            if section not in self.content:
-                result = ''
-            else:  # if section in self.content
-                snippet = self.content.split(section)[-1]
-                lines = [ '%s\n' % line
-                          for line in snippet.split('\n') ]
-                parts = list()
-                for line in lines:
-                    findflag = GLModule.section_label_pattern.findall(line)
-                    if findflag:
-                        break
-                    parts += [line]
-                result = ''.join(parts)
-            self.cache['notice'] = result
-        return self.cache['notice']
+        return self.sections.get('Notice', '')
 
     def getApplicability(self):
         '''GLModule.getApplicability() -> str
 
         Return applicability of module.'''
-        section = 'Applicability:'
         if 'applicability' not in self.cache:
-            if section not in self.content:
-                result = ''
-            else:  # if section in self.content
-                snippet = self.content.split(section)[-1]
-                lines = [ '%s\n' % line
-                          for line in snippet.split('\n') ]
-                parts = list()
-                for line in lines:
-                    findflag = GLModule.section_label_pattern.findall(line)
-                    if findflag:
-                        break
-                    parts += [line]
-                parts = [ part.strip()
-                          for part in parts ]
-                result = ''.join(parts)
-            if not result.strip():
-                if self.getName().endswith('-tests'):
+            result = self.sections.get('Applicability', '')
+            result = result.strip()
+            if not result:
+                # The default is 'main' or 'tests', depending on the module's name.
+                if self.isTests():
                     result = 'tests'
-                else:  # if not self.getName().endswith('-tests')
+                else:
                     result = 'main'
-            result = result.strip()
             self.cache['applicability'] = result
         return self.cache['applicability']
 
@@ -497,115 +440,56 @@ Include:|Link:|License:|Maintainer:)'
 
         Return list of files.
         GLConfig: ac_version.'''
-        ac_version = self.config['ac_version']
-        section = 'Files:'
-        result = list()
         if 'files' not in self.cache:
-            if section not in self.content:
-                result = list()
-            else:  # if section in self.content
-                snippet = self.content.split(section)[-1]
-                lines = [ '%s\n' % line
-                          for line in snippet.split('\n') ]
-                parts = list()
-                for line in lines:
-                    findflag = GLModule.section_label_pattern.findall(line)
-                    if findflag:
-                        break
-                    parts += [line]
-                result = [ part.strip()
-                           for part in parts
-                           if part.strip() ]
-            result += [joinpath('m4', '00gnulib.m4')]
-            result += [joinpath('m4', 'zzgnulib.m4')]
-            result += [joinpath('m4', 'gnulib-common.m4')]
-            self.cache['files'] = list(result)
-        return list(self.cache['files'])
+            snippet = self.sections.get('Files', '')
+            result = [ line.strip()
+                       for line in snippet.split('\n')
+                       if line.strip() ]
+            result.append(joinpath('m4', '00gnulib.m4'))
+            result.append(joinpath('m4', 'zzgnulib.m4'))
+            result.append(joinpath('m4', 'gnulib-common.m4'))
+            self.cache['files'] = result
+        return self.cache['files']
 
     def getDependencies(self):
         '''GLModule.getDependencies() -> list
 
         Return list of dependencies.
         GLConfig: localpath.'''
-        result = list()
-        section = 'Depends-on:'
         if 'dependencies' not in self.cache:
-            if section not in self.content:
-                depmodules = list()
-            else:  # if section in self.content
-                snippet = self.content.split(section)[-1]
-                lines = [ '%s\n' % line
-                          for line in snippet.split('\n') ]
-                parts = list()
-                for line in lines:
-                    findflag = GLModule.section_label_pattern.findall(line)
-                    if findflag:
-                        break
-                    parts += [line]
-                modules = ''.join(parts)
-                modules = [ line
-                            for line in modules.split('\n')
-                            if line.strip() ]
-                modules = [ module
-                            for module in modules
-                            if not module.startswith('#') ]
-                for line in modules:
-                    split = [ part
-                              for part in line.split(' ')
-                              if part.strip() ]
-                    if len(split) == 1:
-                        module = line.strip()
-                        condition = None
-                    else:  # if len(split) != 1
-                        module = split[0]
-                        condition = split[1]
-                    result += [tuple([self.modulesystem.find(module), condition])]
+            snippet = self.sections.get('Depends-on', '')
+            modules = [ line.strip()
+                        for line in snippet.split('\n')
+                        if line.strip() ]
+            modules = [ module
+                        for module in modules
+                        if not module.startswith('#') ]
+            result = list()
+            for line in modules:
+                split = [ part
+                          for part in line.split(' ')
+                          if part.strip() ]
+                if len(split) == 1:
+                    module = line.strip()
+                    condition = None
+                else:  # if len(split) != 1
+                    module = split[0]
+                    condition = split[1]
+                result += [tuple([self.modulesystem.find(module), condition])]
             self.cache['dependencies'] = result
-        return list(self.cache['dependencies'])
+        return self.cache['dependencies']
 
     def getAutoconfSnippet_Early(self):
         '''GLModule.getAutoconfSnippet_Early() -> str
 
         Return autoconf-early snippet.'''
-        section = 'configure.ac-early:'
-        if 'autoconf-early' not in self.cache:
-            if section not in self.content:
-                result = ''
-            else:  # if section in self.content
-                snippet = self.content.split(section)[-1]
-                lines = [ '%s\n' % line
-                          for line in snippet.split('\n') ]
-                parts = list()
-                for line in lines:
-                    findflag = GLModule.section_label_pattern.findall(line)
-                    if findflag:
-                        break
-                    parts += [line]
-                result = ''.join(parts)
-            self.cache['autoconf-early'] = result
-        return self.cache['autoconf-early']
+        return self.sections.get('configure.ac-early', '')
 
     def getAutoconfSnippet(self):
         '''GLModule.getAutoconfSnippet() -> str
 
         Return autoconf snippet.'''
-        section = 'configure.ac:'
-        if 'autoconf' not in self.cache:
-            if section not in self.content:
-                result = ''
-            else:  # if section in self.content
-                snippet = self.content.split(section)[-1]
-                lines = [ '%s\n' % line
-                          for line in snippet.split('\n') ]
-                parts = list()
-                for line in lines:
-                    findflag = GLModule.section_label_pattern.findall(line)
-                    if findflag:
-                        break
-                    parts += [line]
-                result = ''.join(parts)
-            self.cache['autoconf'] = result
-        return self.cache['autoconf']
+        return self.sections.get('configure.ac', '')
 
     def getAutomakeSnippet(self):
         '''getAutomakeSnippet() -> str
@@ -625,23 +509,7 @@ Include:|Link:|License:|Maintainer:)'
         '''GLModule.getAutomakeSnippet_Conditional() -> str
 
         Return conditional automake snippet.'''
-        section = 'Makefile.am:'
-        if 'makefile-conditional' not in self.cache:
-            if section not in self.content:
-                result = ''
-            else:  # if section in self.content
-                snippet = self.content.split(section)[-1]
-                lines = [ '%s\n' % line
-                          for line in snippet.split('\n') ]
-                parts = list()
-                for line in lines:
-                    findflag = GLModule.section_label_pattern.findall(line)
-                    if findflag:
-                        break
-                    parts += [line]
-                result = ''.join(parts)
-            self.cache['makefile-conditional'] = result
-        return self.cache['makefile-conditional']
+        return self.sections.get('Makefile.am', '')
 
     def getAutomakeSnippet_Unconditional(self):
         '''GLModule.getAutomakeSnippet_Unconditional() -> str
@@ -718,24 +586,10 @@ Include:|Link:|License:|Maintainer:)'
         '''GLModule.getInclude() -> str
 
         Return include directive.'''
-        section = 'Include:'
         if 'include' not in self.cache:
-            if section not in self.content:
-                result = ''
-            else:  # if section in self.content
-                snippet = self.content.split(section)[-1]
-                lines = [ '%s\n' % line
-                          for line in snippet.split('\n') ]
-                parts = list()
-                for line in lines:
-                    findflag = GLModule.section_label_pattern.findall(line)
-                    if findflag:
-                        break
-                    parts += [line]
-                result = ''.join(parts)
-            result = result.strip()
-            pattern = re.compile('^(["<].*[>"])', re.M)
-            result = pattern.sub('#include \\1', result)
+            snippet = self.sections.get('Include', '')
+            pattern = re.compile('^(["<])', re.M)
+            result = pattern.sub('#include \\1', snippet)
             self.cache['include'] = result
         return self.cache['include']
 
@@ -743,64 +597,36 @@ Include:|Link:|License:|Maintainer:)'
         '''GLModule.getLink() -> str
 
         Return link directive.'''
-        section = 'Link:'
-        if 'link' not in self.cache:
-            parts = list()
-            if section in self.content:
-                snippet = self.content.split(section)[-1]
-                lines = [ '%s\n' % line
-                          for line in snippet.split('\n') ]
-                for line in lines:
-                    findflag = GLModule.section_label_pattern.findall(line)
-                    if findflag:
-                        break
-                    parts += [line]
-                parts = [ part.strip()
-                          for part in parts
-                          if part.strip() ]
-                # result = ' '.join(parts)
-            self.cache['link'] = parts
-        return self.cache['link']
-
-    def getLicense(self):
-        '''GLModule.getLicense(self) -> str
-
-        Get license and warn user if module lacks a license.'''
-        if str(self) == 'parse-datetime':
-            # This module is under a weaker license only for the purpose of some
-            # users who hand-edit it and don't use gnulib-tool. For the regular
-            # gnulib users they are under a stricter license.
-            return 'GPL'
-        else:
-            license = self.getLicense_Raw()
-            if not self.isTests():
-                if not license:
-                    if self.config['errors']:
-                        raise GLError(18, str(self))
-                    else:  # if not self.config['errors']
-                        sys.stderr.write('gnulib-tool: warning: module %s lacks a license\n' % str(self))
-            if not license:
-                license = 'GPL'
-            return license
+        return self.sections.get('Link', '')
 
     def getLicense_Raw(self):
         '''GLModule.getLicense_Raw() -> str
 
         Return module license.'''
-        section = 'License:'
+        return self.sections.get('License', '')
+
+    def getLicense(self):
+        '''GLModule.getLicense(self) -> str
+
+        Get license and warn user if module lacks a license.'''
         if 'license' not in self.cache:
-            if section not in self.content:
-                result = ''
-            else:  # if section in self.content
-                pattern = '^%s[\t ]*(.*?)%s' % (section, GLModule.section_label_regex)
-                pattern = re.compile(pattern, re.S | re.M)
-                result = pattern.findall(self.content)
-                if type(result) is list:
-                    if not result:
-                        result = ''
-                    else:  # if result
-                        result = result[-1]
-            result = result.strip()
+            result = None
+            if str(self) == 'parse-datetime':
+                # This module is under a weaker license only for the purpose of some
+                # users who hand-edit it and don't use gnulib-tool. For the regular
+                # gnulib users they are under a stricter license.
+                result = 'GPL'
+            else:
+                license = self.getLicense_Raw().strip()
+                if not self.isTests():
+                    if not license:
+                        if self.config['errors']:
+                            raise GLError(18, str(self))
+                        else:  # if not self.config['errors']
+                            sys.stderr.write('gnulib-tool: warning: module %s lacks a license\n' % str(self))
+                if not license:
+                    license = 'GPL'
+                result = license
             self.cache['license'] = result
         return self.cache['license']
 
@@ -808,24 +634,7 @@ Include:|Link:|License:|Maintainer:)'
         '''GLModule.getMaintainer() -> str
 
         Return maintainer directive.'''
-        section = 'Maintainer:'
-        if 'maintainer' not in self.cache:
-            if section not in self.content:
-                result = ''
-            else:  # if section in self.content
-                snippet = self.content.split(section)[-1]
-                lines = [ '%s\n' % line
-                          for line in snippet.split('\n') ]
-                parts = list()
-                for line in lines:
-                    findflag = GLModule.section_label_pattern.findall(line)
-                    if findflag:
-                        break
-                    parts += [line]
-                result = ''.join(parts)
-            result = result.strip()
-            self.cache['maintainer'] = result
-        return self.cache['maintainer']
+        return self.sections.get('Maintainer', '')
 
 
 #===============================================================================
@@ -1002,8 +811,8 @@ class GLModuleTable(object):
                             conditions += [None]
                     for depmodule in depmodules:
                         include = True
-                        status = depmodule.getStatus()
-                        for word in status:
+                        statuses = depmodule.getStatuses()
+                        for word in statuses:
                             if word == 'obsolete':
                                 if not self.config.checkInclTestCategory(TESTS['obsolete']):
                                     include = False
-- 
2.34.1

>From d528738ac3449ce1c4897882e6032eb3a3e929e2 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Sun, 7 Aug 2022 22:59:08 +0200
Subject: [PATCH 16/19] gnulib-tool.py: Rename a method.

* pygnulib/GLModuleSystem.py (GLModule.getAutoconfEarlySnippet): Renamed
from GLModule.getAutoconfSnippet_Early.
* pygnulib/GLImport.py: Update.
* pygnulib/GLTestDir.py: Likewise.
---
 ChangeLog                  | 6 ++++++
 pygnulib/GLImport.py       | 2 +-
 pygnulib/GLModuleSystem.py | 4 ++--
 pygnulib/GLTestDir.py      | 4 ++--
 4 files changed, 11 insertions(+), 5 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 305d02be51..1278d8e4d9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Rename a method.
+	* pygnulib/GLModuleSystem.py (GLModule.getAutoconfEarlySnippet): Renamed
+	from GLModule.getAutoconfSnippet_Early.
+	* pygnulib/GLImport.py: Update.
+	* pygnulib/GLTestDir.py: Likewise.
+
 	gnulib-tool.py: Fix section extraction from module descriptions.
 	The code with   self.content.split(section)[-1]
 	was broken because it recognizes an indented section label.
diff --git a/pygnulib/GLImport.py b/pygnulib/GLImport.py
index 818f3d57c0..87d85c5181 100644
--- a/pygnulib/GLImport.py
+++ b/pygnulib/GLImport.py
@@ -606,7 +606,7 @@ AC_DEFUN([%s_EARLY],
             emit += '  AC_REQUIRE([AM_PROG_CC_C_O])\n'
         for module in moduletable['final']:
             emit += '  # Code from module %s:\n' % str(module)
-            snippet = module.getAutoconfSnippet_Early()
+            snippet = module.getAutoconfEarlySnippet()
             lines = [ line
                       for line in snippet.split(constants.NL)
                       if line != '' ]
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index 32a133009a..14bc089b27 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -479,8 +479,8 @@ class GLModule(object):
             self.cache['dependencies'] = result
         return self.cache['dependencies']
 
-    def getAutoconfSnippet_Early(self):
-        '''GLModule.getAutoconfSnippet_Early() -> str
+    def getAutoconfEarlySnippet(self):
+        '''GLModule.getAutoconfEarlySnippet() -> str
 
         Return autoconf-early snippet.'''
         return self.sections.get('configure.ac-early', '')
diff --git a/pygnulib/GLTestDir.py b/pygnulib/GLTestDir.py
index 9a3fde66ea..746b815b49 100644
--- a/pygnulib/GLTestDir.py
+++ b/pygnulib/GLTestDir.py
@@ -456,7 +456,7 @@ class GLTestDir(object):
                         pass
                     # if str(module) not in ['gnumakefile', 'maintainer-makefile']
                     else:
-                        snippet = module.getAutoconfSnippet_Early()
+                        snippet = module.getAutoconfEarlySnippet()
                         lines = [ line
                                   for line in snippet.split('\n')
                                   if line.strip() ]
@@ -573,7 +573,7 @@ class GLTestDir(object):
             else:  # if not single_configure
                 solution = module.isNonTests()
             if solution:
-                snippet = module.getAutoconfSnippet_Early()
+                snippet = module.getAutoconfEarlySnippet()
                 lines = [ line
                           for line in snippet.split('\n')
                           if line.strip() ]
-- 
2.34.1

>From 276c20ee288873b519be96ffe9b3a98bfe3bb8f1 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Mon, 8 Aug 2022 00:02:59 +0200
Subject: [PATCH 17/19] gnulib-tool.py: Fix --extract-dependencies result.

* pygnulib/GLModuleSystem.py (GLModule.getDependencies): Return a
snippet, not a list. Implement dependency of ${module}-tests on
${module}.
(GLModule.getDependenciesWithoutConditions,
GLModule.getDependenciesWithConditions): New methods.
(GLModuleTable.transitive_closure): Call getDependenciesWithConditions.
* pygnulib/GLEmiter.py (GLEmiter.autoconfSnippets): Call
getDependenciesWithoutConditions.
* gnulib-tool.py (main) [--extract-dependencies]: Update.
---
 ChangeLog                  | 11 +++++
 gnulib-tool.py             | 10 +----
 pygnulib/GLEmiter.py       |  4 +-
 pygnulib/GLModuleSystem.py | 83 ++++++++++++++++++++++++++++----------
 4 files changed, 75 insertions(+), 33 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 1278d8e4d9..beb60578d0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,16 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Fix --extract-dependencies result.
+	* pygnulib/GLModuleSystem.py (GLModule.getDependencies): Return a
+	snippet, not a list. Implement dependency of ${module}-tests on
+	${module}.
+	(GLModule.getDependenciesWithoutConditions,
+	GLModule.getDependenciesWithConditions): New methods.
+	(GLModuleTable.transitive_closure): Call getDependenciesWithConditions.
+	* pygnulib/GLEmiter.py (GLEmiter.autoconfSnippets): Call
+	getDependenciesWithoutConditions.
+	* gnulib-tool.py (main) [--extract-dependencies]: Update.
+
 	gnulib-tool.py: Rename a method.
 	* pygnulib/GLModuleSystem.py (GLModule.getAutoconfEarlySnippet): Renamed
 	from GLModule.getAutoconfSnippet_Early.
diff --git a/gnulib-tool.py b/gnulib-tool.py
index 0e888e6fd8..0cd5accd21 100755
--- a/gnulib-tool.py
+++ b/gnulib-tool.py
@@ -1013,7 +1013,6 @@ def main():
             print('\n'.join(files))
 
     elif mode == 'extract-dependencies':
-        result = ''
         if avoids:
             message = '%s: *** ' % constants.APP['name']
             message += 'cannot combine --avoid and --extract-dependencies\n'
@@ -1024,14 +1023,7 @@ def main():
         modules = [ modulesystem.find(module)
                     for module in modules ]
         for module in modules:
-            dependencies = module.getDependencies()
-            if dependencies:
-                for depmodule, condition in dependencies:
-                    if condition == None:
-                        result += '%s\n' % str(depmodule)
-                    else:  # if condition != None
-                        result += '%s\t%s' % (str(depmodule), condition)
-        print(result)
+            sys.stdout.write(module.getDependencies())
 
     elif mode == 'extract-autoconf-snippet':
         modulesystem = classes.GLModuleSystem(config)
diff --git a/pygnulib/GLEmiter.py b/pygnulib/GLEmiter.py
index 61cabc92fa..a02b44bdd5 100644
--- a/pygnulib/GLEmiter.py
+++ b/pygnulib/GLEmiter.py
@@ -309,9 +309,7 @@ class GLEmiter(object):
                         emit += self.autoconfSnippet(module, fileassistant, toplevel,
                                                      disable_libtool, disable_gettext, replace_auxdir, '      ')
                         emit += '      %s=true\n' % shellvar
-                        dependencies = module.getDependencies()
-                        depmodules = [ pair[0]
-                                       for pair in dependencies ]
+                        depmodules = module.getDependenciesWithoutConditions()
                         # Intersect dependencies with the modules list.
                         depmodules = [ dep
                                        for dep in depmodules
diff --git a/pygnulib/GLModuleSystem.py b/pygnulib/GLModuleSystem.py
index 14bc089b27..19d13d213b 100644
--- a/pygnulib/GLModuleSystem.py
+++ b/pygnulib/GLModuleSystem.py
@@ -452,33 +452,74 @@ class GLModule(object):
         return self.cache['files']
 
     def getDependencies(self):
-        '''GLModule.getDependencies() -> list
+        '''GLModule.getDependencies() -> str
 
-        Return list of dependencies.
+        Return list of dependencies, as a snippet.
         GLConfig: localpath.'''
         if 'dependencies' not in self.cache:
+            result = ''
+            # ${module}-tests implicitly depends on ${module}, if that module exists.
+            if self.isTests():
+                main_module = subend('-tests', '', self.getName())
+                if self.modulesystem.exists(main_module):
+                    result += '%s\n' % main_module
+            # Then the explicit dependencies listed in the module description.
             snippet = self.sections.get('Depends-on', '')
-            modules = [ line.strip()
-                        for line in snippet.split('\n')
-                        if line.strip() ]
-            modules = [ module
-                        for module in modules
-                        if not module.startswith('#') ]
-            result = list()
-            for line in modules:
-                split = [ part
-                          for part in line.split(' ')
-                          if part.strip() ]
-                if len(split) == 1:
-                    module = line.strip()
-                    condition = None
-                else:  # if len(split) != 1
-                    module = split[0]
-                    condition = split[1]
-                result += [tuple([self.modulesystem.find(module), condition])]
+            # Remove comment lines.
+            snippet = re.compile('^#.*$[\n]', re.M).sub('', snippet)
+            result += snippet
             self.cache['dependencies'] = result
         return self.cache['dependencies']
 
+    def getDependenciesWithoutConditions(self):
+        '''GLModule.getDependenciesWithoutConditions() -> list
+
+        Return list of dependencies, as a list of GLModule objects.
+        GLConfig: localpath.'''
+        if 'dependenciesWithoutCond' not in self.cache:
+            snippet = self.getDependencies()
+            lines = [ line.strip()
+                      for line in snippet.split('\n')
+                      if line.strip() ]
+            pattern = re.compile(' *\\[.*$')
+            lines = [ pattern.sub('', line)
+                      for line in lines ]
+            result = [ self.modulesystem.find(module)
+                       for module in lines
+                       if module != '' ]
+            self.cache['dependenciesWithoutCond'] = result
+        return self.cache['dependenciesWithoutCond']
+
+    def getDependenciesWithConditions(self):
+        '''GLModule.getDependenciesWithConditions() -> list
+
+        Return list of dependencies, as a list of pairs (GLModule object, condition).
+        The "true" condition is denoted by None.
+        GLConfig: localpath.'''
+
+        if 'dependenciesWithCond' not in self.cache:
+            snippet = self.getDependencies()
+            lines = [ line.strip()
+                      for line in snippet.split('\n')
+                      if line.strip() ]
+            pattern = re.compile(' *\\[')
+            result = []
+            for line in lines:
+                match = pattern.search(line)
+                if match:
+                    module = line[0 : match.start()]
+                    condition = line[match.end() :]
+                    condition = subend(']', '', condition)
+                else:
+                    module = line
+                    condition = None
+                if module != '':
+                    if condition == 'true':
+                        condition = None
+                    result.append(tuple([self.modulesystem.find(module), condition]))
+            self.cache['dependenciesWithCond'] = result
+        return self.cache['dependenciesWithCond']
+
     def getAutoconfEarlySnippet(self):
         '''GLModule.getAutoconfEarlySnippet() -> str
 
@@ -798,7 +839,7 @@ class GLModuleTable(object):
                         if not pattern.findall(automake_snippet):
                             self.addUnconditional(module)
                         conditional = self.isConditional(module)
-                    dependencies = module.getDependencies()
+                    dependencies = module.getDependenciesWithConditions()
                     depmodules = [ pair[0]
                                    for pair in dependencies ]
                     conditions = [ pair[1]
-- 
2.34.1

>From 6d29a15180d7ba0360d8fb56793a0766926b0dee Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Mon, 8 Aug 2022 00:43:25 +0200
Subject: [PATCH 18/19] gnulib-tool.py: Fix handling of nonexistent module
 names in --extract-*.

* gnulib-tool.py (main): To test whether a module exists, just call
GLModuleSystem.find and test its return value.
---
 ChangeLog      |   4 ++
 gnulib-tool.py | 133 ++++++++++++++++++++++++-------------------------
 2 files changed, 68 insertions(+), 69 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index beb60578d0..f3a37cea1c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Fix handling of nonexistent module names in --extract-*.
+	* gnulib-tool.py (main): To test whether a module exists, just call
+	GLModuleSystem.find and test its return value.
+
 	gnulib-tool.py: Fix --extract-dependencies result.
 	* pygnulib/GLModuleSystem.py (GLModule.getDependencies): Return a
 	snippet, not a list. Implement dependency of ${module}-tests on
diff --git a/gnulib-tool.py b/gnulib-tool.py
index 0cd5accd21..0abe8c5ca7 100755
--- a/gnulib-tool.py
+++ b/gnulib-tool.py
@@ -47,7 +47,6 @@ import shlex
 from tempfile import mktemp
 from pygnulib import constants
 from pygnulib import classes
-from pygnulib import GLError
 
 
 #===============================================================================
@@ -716,8 +715,6 @@ def main():
         print(result)
 
     elif mode == 'find':
-        # Prepare GLModuleSystem.find to throw an exception.
-        config.setErrors(True)
         modulesystem = classes.GLModuleSystem(config)
         for filename in files:
             if (isfile(joinpath(DIRS['root'], filename))
@@ -754,14 +751,12 @@ def main():
                 listing = [ line
                             for line in listing
                             if modulesystem.file_is_module(line) ]
-                module_candidates = sorted(set(listing))
-                for module in module_candidates:
-                    try:
-                        if filename in modulesystem.find(module).getFiles():
-                            print(module)
-                    except GLError:
-                        # Ignore module candidates that don't actually exist.
-                        pass
+                candidates = sorted(set(listing))
+                for name in candidates:
+                    module = modulesystem.find(name)
+                    if module:  # Ignore module candidates that don't actually exist.
+                        if module.getFiles():
+                            print(name)
             else:
                 message = '%s: warning: file %s does not exist\n' % (constants.APP['name'], filename)
                 sys.stderr.write(message)
@@ -971,46 +966,46 @@ def main():
 
     elif mode == 'extract-description':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            sys.stdout.write(module.getDescription())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getDescription())
 
     elif mode == 'extract-comment':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            sys.stdout.write(module.getComment())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getComment())
 
     elif mode == 'extract-status':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            sys.stdout.write(module.getStatus())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getStatus())
 
     elif mode == 'extract-notice':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            sys.stdout.write(module.getNotice())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getNotice())
 
     elif mode == 'extract-applicability':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            print(module.getApplicability())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                print(module.getApplicability())
 
     elif mode == 'extract-filelist':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            files = module.getFiles()
-            print('\n'.join(files))
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                files = module.getFiles()
+                print('\n'.join(files))
 
     elif mode == 'extract-dependencies':
         if avoids:
@@ -1020,60 +1015,60 @@ def main():
             sys.stderr.write(message)
             sys.exit(1)
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            sys.stdout.write(module.getDependencies())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getDependencies())
 
     elif mode == 'extract-autoconf-snippet':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            sys.stdout.write(module.getAutoconfSnippet())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getAutoconfSnippet())
 
     elif mode == 'extract-automake-snippet':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            sys.stdout.write(module.getAutomakeSnippet())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getAutomakeSnippet())
 
     elif mode == 'extract-include-directive':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            sys.stdout.write(module.getInclude())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getInclude())
 
     elif mode == 'extract-link-directive':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            sys.stdout.write(module.getLink())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getLink())
 
     elif mode == 'extract-license':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            print(module.getLicense())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                print(module.getLicense())
 
     elif mode == 'extract-maintainer':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            sys.stdout.write(module.getMaintainer())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                sys.stdout.write(module.getMaintainer())
 
     elif mode == 'extract-tests-module':
         modulesystem = classes.GLModuleSystem(config)
-        modules = [ modulesystem.find(module)
-                    for module in modules ]
-        for module in modules:
-            if module.getTestsModule():
-                print(module.getTestsName())
+        for name in modules:
+            module = modulesystem.find(name)
+            if module:
+                if module.getTestsModule():
+                    print(module.getTestsName())
 
     elif mode == 'copy-file':
         srcpath = files[0]
-- 
2.34.1

>From a7bcb91088e7fa6a533528aec076fc536789b96f Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Mon, 8 Aug 2022 00:46:13 +0200
Subject: [PATCH 19/19] gnulib-tool.py: Finish implementing option
 --extract-test-module.

* gnulib-tool.py (main): Accept option --extract-tests-module.
---
 ChangeLog           | 3 +++
 gnulib-tool.py      | 8 ++++++++
 gnulib-tool.py.TODO | 1 -
 3 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index f3a37cea1c..63691ebfb6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,8 @@
 2022-08-07  Bruno Haible  <br...@clisp.org>
 
+	gnulib-tool.py: Finish implementing option --extract-test-module.
+	* gnulib-tool.py (main): Accept option --extract-tests-module.
+
 	gnulib-tool.py: Fix handling of nonexistent module names in --extract-*.
 	* gnulib-tool.py (main): To test whether a module exists, just call
 	GLModuleSystem.find and test its return value.
diff --git a/gnulib-tool.py b/gnulib-tool.py
index 0abe8c5ca7..9988c84de9 100755
--- a/gnulib-tool.py
+++ b/gnulib-tool.py
@@ -180,6 +180,10 @@ def main():
                         dest='mode_xmaintainer',
                         default=None,
                         action='store_true')
+    parser.add_argument('--extract-tests-module',
+                        dest='mode_xtests',
+                        default=None,
+                        action='store_true')
     # copy-file
     parser.add_argument('--copy-file',
                         dest='mode_copy_file',
@@ -446,6 +450,7 @@ def main():
         cmdargs.mode_xlink,
         cmdargs.mode_xlicense,
         cmdargs.mode_xmaintainer,
+        cmdargs.mode_xtests,
         cmdargs.mode_copy_file,
     ]
     overflow = [ arg
@@ -535,6 +540,9 @@ def main():
     if cmdargs.mode_xmaintainer != None:
         mode = 'extract-maintainer'
         modules = list(cmdargs.non_option_arguments)
+    if cmdargs.mode_xtests != None:
+        mode = 'extract-tests-module'
+        modules = list(cmdargs.non_option_arguments)
     if cmdargs.mode_copy_file != None:
         mode = 'copy-file'
         if len(cmdargs.non_option_arguments) < 1 or len(cmdargs.non_option_arguments) > 2:
diff --git a/gnulib-tool.py.TODO b/gnulib-tool.py.TODO
index 9efcda510d..be6a2e2b10 100644
--- a/gnulib-tool.py.TODO
+++ b/gnulib-tool.py.TODO
@@ -22,7 +22,6 @@ Inline all 'sed' invocations.
 Implement the options:
   --extract-recursive-dependencies
   --extract-recursive-link-directive
-  --extract-tests-module
   --conditional-dependencies
   --no-conditional-dependencies
   --gnu-make
-- 
2.34.1

Reply via email to