From 783118944874d559e30eea1600352e3feb5263a7 Mon Sep 17 00:00:00 2001
From: Andreas Wacknitz <A.Wacknitz@gmx.de>
Date: Sun, 31 Mar 2024 09:35:05 +0200
Subject: [PATCH] libcares: update to 1.28.0

---
 tools/userland-mangler |  448 +++++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 296 insertions(+), 152 deletions(-)

diff --git a/tools/userland-mangler b/tools/userland-mangler
index 825cd37..8cdbbe3 100755
--- a/tools/userland-mangler
+++ b/tools/userland-mangler
@@ -1,4 +1,4 @@
-#!/usr/bin/python2.6
+#!/usr/bin/python3.9
 #
 # CDDL HEADER START
 #
@@ -7,7 +7,7 @@
 # You may not use this file except in compliance with the License.
 #
 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
-# or http://www.opensolaris.org/os/licensing.
+# or http://www.illumos.org/license/CDDL.
 # See the License for the specific language governing permissions
 # and limitations under the License.
 #
@@ -19,7 +19,7 @@
 #
 # CDDL HEADER END
 #
-# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
 #
 #
 # userland-mangler - a file mangling utility
@@ -31,11 +31,18 @@
 import os
 import sys
 import re
+import subprocess
+import shutil
+import stat
+
 
 import pkg.fmri
 import pkg.manifest
 import pkg.actions
 import pkg.elf as elf
+
+attribute_oracle_table_header = """
+.\\\" Oracle has added the ARC stability level to this manual page"""
 
 attribute_table_header = """
 .SH ATTRIBUTES
@@ -58,22 +65,29 @@
 Stability	%s"""
 
 attribute_table_footer = """
-.TE 
+.TE
 .PP
 """
-def write_attributes_section(ofp, availability, stability):
-	# is there anything to do?
-	if availability is None and stability is None:
-		return
+def attributes_section_text(availability, stability, modified_date):
+        result = ''
 
-	# append the ATTRIBUTES section
-	ofp.write(attribute_table_header)
-	if availability is not None:
-		ofp.write(attribute_table_availability % availability)
-	if stability is not None:
-		ofp.write(attribute_table_stability % stability.capitalize())
-	ofp.write(attribute_table_footer)
+        # is there anything to do?
+        if availability is not None or stability is not None:
+                result = attribute_oracle_table_header
+                if modified_date is not None:
+                        result += ("\n.\\\" on %s" % modified_date)
+                result += attribute_table_header
 
+                if availability is not None:
+                        result += (attribute_table_availability % availability)
+                if stability is not None:
+                        result += (attribute_table_stability % stability.capitalize())
+                result += attribute_table_footer
+
+        return result
+
+notes_oracle_comment = """
+.\\\" Oracle has added source availability information to this manual page"""
 
 notes_header = """
 .SH NOTES
@@ -83,194 +97,324 @@
 Further information about this software can be found on the open source community website at %s.
 """
 notes_source = """
-This software was built from source available at http://opensolaris.org/.  The original community source was downloaded from  %s
+This software was built from source available at https://openindiana.org/.  The original community source was downloaded from  %s
 """
 
-def write_notes_section(ofp, header_seen, community, source):
-	# is there anything to do?
-	if community is None and source is None:
-		return
+def notes_section_text(header_seen, community, source, modified_date):
+        result = ''
 
-	# append the NOTES section
-	if header_seen == False:
-		ofp.write(notes_header)
-	if source is not None:
-		ofp.write(notes_source % source)
-	if community is not None:
-		ofp.write(notes_community % community)
+        # is there anything to do?
+        if community is not None or source is not None:
+                if header_seen == False:
+                        result += notes_header
+                result += notes_oracle_comment
+                if modified_date is not None:
+                        result += ("\n.\\\" on %s" % modified_date)
+                if source is not None:
+                        result += (notes_source % source)
+                if community is not None:
+                        result += (notes_community % community)
 
+        return result
 
+so_re = re.compile('^\.so.+$', re.MULTILINE)
 section_re = re.compile('\.SH "?([^"]+).*$', re.IGNORECASE)
+TH_re = re.compile('\.TH\s+(?:"[^"]+"|\S+)\s+(\S+)', re.IGNORECASE)
 #
 # mangler.man.stability = (mangler.man.stability)
+# mangler.man.modified_date = (mangler.man.modified-date)
 # mangler.man.availability = (pkg.fmri)
-# mangler.man.source_url = (pkg.source_url)
-# mangler.man.upstream_url = (pkg.upstream_url)
+# mangler.man.source-url = (pkg.source-url)
+# mangler.man.upstream-url = (pkg.upstream-url)
 #
-def mangle_manpage(manifest, action, src, dest):
-	# manpages must have a taxonomy defined
-	stability = action.attrs.pop('mangler.man.stability', None)
-	if stability is None:
-		sys.stderr.write("ERROR: manpage action missing mangler.man.stability: %s" % action)
-		sys.exit(1)
+def mangle_manpage(manifest, action, text):
+        # manpages must have a taxonomy defined
+        stability = action.attrs.pop('mangler.man.stability', None)
+        if stability is None:
+                sys.stderr.write("ERROR: manpage action missing mangler.man.stability: %s" % action)
+                sys.exit(1)
 
-	attributes_written = False
-	notes_seen = False
+        # manpages may have a 'modified date'
+        modified_date = action.attrs.pop('mangler.man.modified-date', None)
 
-	if 'pkg.fmri' in manifest.attributes:
-		fmri = pkg.fmri.PkgFmri(manifest.attributes['pkg.fmri'])
-		availability = fmri.pkg_name
+        # Rewrite the section in the .TH line to match the section in which
+        # we're delivering it.
+        rewrite_sect = action.attrs.pop('mangler.man.rewrite-section', 'true')
 
-	community = None
-	if 'info.upstream_url' in manifest.attributes:
-		community = manifest.attributes['info.upstream_url']
+        attributes_written = False
+        notes_seen = False
 
-	source = None
-	if 'info.source_url' in manifest.attributes:
-		source = manifest.attributes['info.source_url']
-	elif 'info.repository_url' in manifest.attributes:
-		source = manifest.attributes['info.repository_url']
+        if 'pkg.fmri' in manifest.attributes:
+                fmri = pkg.fmri.PkgFmri(manifest.attributes['pkg.fmri'])
+                availability = fmri.pkg_name
 
-	# create a directory to write to
-	destdir = os.path.dirname(dest)
-	if not os.path.exists(destdir):
-		os.makedirs(destdir)
+        community = None
+        if 'info.upstream-url' in manifest.attributes:
+                community = manifest.attributes['info.upstream-url']
 
-	# read the source document
-	ifp = open(src, "r")
-	lines = ifp.readlines()
-	ifp.close()
+        source = None
+        if 'info.source-url' in manifest.attributes:
+                source = manifest.attributes['info.source-url']
+        elif 'info.repository-url' in manifest.attributes:
+                source = manifest.attributes['info.repository-url']
 
-	# skip reference only pages
-	if lines[0].startswith(".so "):
-		return
+        # skip reference only pages
+        if so_re.match(text) is not None:
+                return text
 
-	# open a destination
-	ofp = open(dest, "w+")
+        # tell man that we want tables (and eqn)
+        result = "'\\\" te\n"
 
-	# tell man that we want tables (and eqn)
-	ofp.write("'\\\" te\n")
+        # write the orginal data
+        for line in text.split('\n'):
+                match = section_re.match(line)
+                if match is not None:
+                        section = match.group(1)
+                        if section in ['SEE ALSO', 'NOTES']:
+                                if attributes_written == False:
+                                        result += attributes_section_text(
+                                                                 availability,
+                                                                 stability,
+                                                                 modified_date)
+                                        attributes_written = True
+                                if section == 'NOTES':
+                                        notes_seen = True
+                        match = TH_re.match(line)
+                        if match and rewrite_sect.lower() == "true":
+                                # Use the section defined by the filename, rather than
+                                # the directory in which it sits.
+                                sect = os.path.splitext(action.attrs["path"])[1][1:]
+                                line = line[:match.span(1)[0]] + sect + \
+                                    line[match.span(1)[1]:]
 
-	# write the orginal data
-	for line in lines:
-		match = section_re.match(line)
-		if match is not None:
-			section = match.group(1)
-			if section in ['SEE ALSO', 'NOTES']:
-				if attributes_written == False:
-					write_attributes_section(ofp,
-								 availability,
-								 stability)
-					attributes_written = True
-				if section == 'NOTES':
-					notes_seen = True
-		ofp.write(line)
+                result += ("%s\n" % line)
 
-	if attributes_written == False:
-		write_attributes_section(ofp, availability, stability)
+        if attributes_written == False:
+                result += attributes_section_text(availability, stability,
+                    modified_date)
 
-	write_notes_section(ofp, notes_seen, community, source)
+        result += notes_section_text(notes_seen, community, source,
+            modified_date)
 
-	ofp.close()
-
+        return result
 
 #
-# mangler.elf.strip = (true|false)
+# mangler.elf.strip_runpath = (true|false)
 #
 def mangle_elf(manifest, action, src, dest):
-	pass
+        strip_elf_runpath = action.attrs.pop('mangler.elf.strip_runpath', 'true')
+        if strip_elf_runpath == 'false':
+                return
+
+        #
+        # Strip any runtime linker default search path elements from the file
+        # and replace relative paths with absolute paths
+        #
+        ELFEDIT = '/usr/bin/elfedit'
+
+        # runtime linker default search path elements + /64 link
+        rtld_default_dirs = [ '/lib', '/usr/lib',
+                              '/lib/64', '/usr/lib/64',
+                              '/lib/amd64', '/usr/lib/amd64',
+                              '/lib/sparcv9', '/usr/lib/sparcv9' ]
+
+        runpath_re = re.compile('.+\s(RPATH|RUNPATH)\s+\S+\s+(\S+)')
+
+        # Retreive the search path from the object file.  Use elfedit(1) because pkg.elf only
+        # retrieves the RUNPATH.  Note that dyn:rpath and dyn:runpath return both values.
+        # Both RPATH and RUNPATH are expected to be the same, but in an overabundand of caution,
+        # process each element found separately.
+        result = subprocess.Popen([ELFEDIT, '-re', 'dyn:runpath', src ],
+                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                                  universal_newlines=True)
+        result.wait()
+        if result.returncode != 0:        # no RUNPATH or RPATH to potentially strip
+                return
+
+        for line in result.stdout:
+                result = runpath_re.match(line)
+                if result != None:
+                        element = result.group(1)
+                        original_dirs = result.group(2).split(":")
+                        keep_dirs = []
+                        matched_dirs = []
+
+                        for dir in original_dirs:
+                                if dir not in rtld_default_dirs:
+                                        if dir.startswith('$ORIGIN'):
+                                                path = action.attrs['path']
+                                                dirname = os.path.dirname(path)
+                                                if dirname[0] != '/':
+                                                        dirname = '/' + dirname
+                                                corrected_dir = dir.replace('$ORIGIN', dirname)
+                                                corrected_dir = os.path.realpath(corrected_dir)
+                                                matched_dirs.append(dir)
+                                                keep_dirs.append(corrected_dir)
+                                        else:
+                                            keep_dirs.append(dir)
+                                else:
+                                        matched_dirs.append(dir)
+
+                        if len(matched_dirs) != 0:
+                                # Emit an "Error" message in case someone wants to look at the build log
+                                # and fix the component build so that this is a NOP.
+                                print("Stripping %s from %s in %s" % (":".join(matched_dirs), element, src), file=sys.stderr)
+
+                                # Make sure that there is a destdir to copy the file into for mangling.
+                                destdir = os.path.dirname(dest)
+                                if not os.path.exists(destdir):
+                                        os.makedirs(destdir)
+                                # Create a copy to mangle
+                                # Earlier the code would check that the destination file does not exist
+                                # yet, however internal library versioning can be different while the
+                                # filename remains the same.
+                                # When publishing from a non-clean prototype directory older libraries
+                                # which may be ABI incompatible would then be republished in the new
+                                # package instead of the new version.
+                                shutil.copy2(src, dest)
+
+                                # Make sure we do have write permission before we try to modify the file
+                                os.chmod(dest, os.stat(dest).st_mode | stat.S_IWUSR)
+
+                                # Mangle the copy by deleting the tag if there is nothing left to keep
+                                # or replacing the value if there is something left.
+                                elfcmd = "dyn:delete %s" % element.lower()
+                                if len(keep_dirs) > 0:
+                                        elfcmd = "dyn:%s '%s'" % (element.lower(), ":".join(keep_dirs))
+                                subprocess.call([ELFEDIT, '-e', elfcmd, dest])
 
 #
 # mangler.script.file-magic =
 #
-def mangle_script(manifest, action, src, dest):
-	pass
+def mangle_script(manifest, action, text):
+        return text
 
-def mangle_path(manifest, action, src, dest):
-	if 'facet.doc.man' in action.attrs:
-		 mangle_manpage(manifest, action, src, dest)
-	elif 'mode' in action.attrs and int(action.attrs['mode'], 8) & 0111 != 0:
-		if elf.is_elf_object(src):
-			 mangle_elf(manifest, action, src, dest)
-		else:
-			 mangle_script(manifest, action, src, dest)
+#
+# mangler.strip_cddl = false
+#
+def mangle_cddl(manifest, action, text):
+        strip_cddl = action.attrs.pop('mangler.strip_cddl', 'false')
+        if strip_cddl == 'false':
+                return text
+        cddl_re = re.compile('^[^\n]*CDDL HEADER START.+CDDL HEADER END[^\n]*$',
+                             re.MULTILINE|re.DOTALL)
+        return cddl_re.sub('', text)
+
+def do_ctfconvert(converter, file):
+        args = [converter, '-i', '-m', '-k', file]
+        print(*args, file=sys.stderr)
+        subprocess.call(args)
+
+def mangle_path(manifest, action, src, dest, ctfconvert):
+        if elf.is_elf_object(src):
+                if ctfconvert is not None:
+                        do_ctfconvert(ctfconvert, src)
+                mangle_elf(manifest, action, src, dest)
+        else:
+                # a 'text' document (script, man page, config file, ...
+                # We treat all documents as latin-1 text to avoid
+                # reencoding them and loosing data
+                ifp = open(src, 'r', encoding='latin-1')
+                text = ifp.read()
+                ifp.close()
+
+                # remove the CDDL from files
+                result = mangle_cddl(manifest, action, text)
+
+                if 'facet.doc.man' in action.attrs:
+                         result = mangle_manpage(manifest, action, result)
+                elif 'mode' in action.attrs and int(action.attrs['mode'], 8) & 0o111 != 0:
+                        result = mangle_script(manifest, action, result)
+
+                if text != result:
+                        destdir = os.path.dirname(dest)
+                        if not os.path.exists(destdir):
+                                os.makedirs(destdir)
+                        with open(dest, 'w', encoding='latin-1') as ofp:
+                            ofp.write(result)
 
 #
 # mangler.bypass = (true|false)
 #
-def mangle_paths(manifest, search_paths, destination):
-	for action in manifest.gen_actions_by_type("file"):
-		bypass = action.attrs.pop('mangler.bypass', 'false').lower()
-		if bypass == 'true':
-			continue
+def mangle_paths(manifest, search_paths, destination, ctfconvert):
+        for action in manifest.gen_actions_by_type("file"):
+                bypass = action.attrs.pop('mangler.bypass', 'false').lower()
+                if bypass == 'true':
+                        continue
 
-		path = None
-		if 'path' in action.attrs:
-			path = action.attrs['path']
-		if action.hash and action.hash != 'NOHASH':
-			path = action.hash
-		if not path:
-			continue
+                path = None
+                if 'path' in action.attrs:
+                        path = action.attrs['path']
+                if action.hash and action.hash != 'NOHASH':
+                        path = action.hash
+                if not path:
+                        continue
 
-		dest = os.path.join(destination, path)
-		for directory in search_paths:
-			if directory != destination:
-				src = os.path.join(directory, path)
-				if os.path.exists(src):
-					mangle_path(manifest, action, src, dest)
-					break
+                if not os.path.exists(destination):
+                        os.makedirs(destination)
+
+                dest = os.path.join(destination, path)
+                for directory in search_paths:
+                        if directory != destination:
+                                src = os.path.join(directory, path)
+                                if os.path.isfile(src):
+                                        mangle_path(manifest, action,
+                                                    src, dest, ctfconvert)
+                                        break
 
 def load_manifest(manifest_file):
-	manifest = pkg.manifest.Manifest()
-	manifest.set_content(pathname=manifest_file)
+        manifest = pkg.manifest.Manifest()
+        manifest.set_content(pathname=manifest_file)
 
-	return manifest
+        return manifest
 
 def usage():
-	print "Usage: %s [-m|--manifest (file)] [-d|--search-directory (dir)] [-D|--destination (dir)] " % (sys.argv[0].split('/')[-1])
-	sys.exit(1)
+        print("Usage: %s [-m|--manifest (file)] [-d|--search-directory (dir)] [-D|--destination (dir)] " % (sys.argv[0].split('/')[-1]))
+        sys.exit(1)
 
 def main():
-	import getopt
+        import getopt
 
-	# FLUSH STDOUT 
-	sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
+        sys.stdout.flush()
 
-	search_paths = []
-	destination = None
-	manifests = []
+        search_paths = []
+        destination = None
+        manifests = []
+        ctfconvert = None
 
-	try:
-		opts, args = getopt.getopt(sys.argv[1:], "D:d:m:",
-			["destination=", "search-directory=", "manifest="])
-	except getopt.GetoptError, err:
-		print str(err)
-		usage()
+        try:
+                opts, args = getopt.getopt(sys.argv[1:], "c:D:d:m:",
+                        ["ctf=", "destination=", "search-directory=", "manifest="])
+        except getopt.GetoptError as err:
+                print(str(err))
+                usage()
 
-	for opt, arg in opts:
-		if opt in [ "-D", "--destination" ]:
-			destination = arg
-		elif opt in [ "-d", "--search-directory" ]:
-			search_paths.append(arg)
-		elif opt in [ "-m", "--manifest" ]:
-			try:
-				manifest = load_manifest(arg)
-			except IOError, err:
-				print "oops, %s: %s" % (arg, str(err))
-				usage()
-			else:
-				manifests.append(manifest)
-		else:
-			usage()
+        for opt, arg in opts:
+                if opt in [ "-D", "--destination" ]:
+                        destination = arg
+                elif opt in [ "-d", "--search-directory" ]:
+                        search_paths.append(arg)
+                elif opt in [ "-m", "--manifest" ]:
+                        try:
+                                manifest = load_manifest(arg)
+                        except IOError as err:
+                                print("oops, %s: %s" % (arg, str(err)))
+                                usage()
+                        else:
+                                manifests.append(manifest)
+                elif opt in [ "-c", "--ctf" ]:
+                        ctfconvert = arg
+                else:
+                        usage()
 
-	if destination == None:
-		usage()
+        if destination == None:
+                usage()
 
-	for manifest in manifests:
-		mangle_paths(manifest, search_paths, destination)
-		print manifest
+        for manifest in manifests:
+                mangle_paths(manifest, search_paths, destination, ctfconvert)
+                print(manifest)
 
-	sys.exit(0)
+        sys.exit(0)
 
 if __name__ == "__main__":
-	main()
+        main()

--
Gitblit v1.9.3