Merge pull request #2846 from pyhalov/ptlib
[unleashed-userland.git] / tools / userland-mangler
blob080b33badc92f00cb4f50eb911d99fcea372635b
1 #!/usr/bin/python2.7
3 # CDDL HEADER START
5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
20 # CDDL HEADER END
22 # Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
25 # userland-mangler - a file mangling utility
27 #  A simple program to mangle files to conform to Solaris WOS or Consoldation
28 #  requirements.
31 import os
32 import sys
33 import re
35 import pkg.fmri
36 import pkg.manifest
37 import pkg.actions
38 import pkg.elf as elf
40 attribute_oracle_table_header = """
41 .\\\" Oracle has added the ARC stability level to this manual page"""
43 attribute_table_header = """
44 .SH ATTRIBUTES
45 See
46 .BR attributes (5)
47 for descriptions of the following attributes:
48 .sp
49 .TS
50 box;
51 cbp-1 | cbp-1
52 l | l .
53 ATTRIBUTE TYPE  ATTRIBUTE VALUE """
55 attribute_table_availability = """
57 Availability    %s"""
59 attribute_table_stability = """
61 Stability       %s"""
63 attribute_table_footer = """
64 .TE 
65 .PP
66 """
67 def attributes_section_text(availability, stability, modified_date):
68         result = ''
70         # is there anything to do?
71         if availability is not None or stability is not None:
72                 result = attribute_oracle_table_header
73                 if modified_date is not None:
74                         result += ("\n.\\\" on %s" % modified_date)
75                 result += attribute_table_header
77                 if availability is not None:
78                         result += (attribute_table_availability % availability)
79                 if stability is not None:
80                         result += (attribute_table_stability % stability.capitalize())
81                 result += attribute_table_footer
83         return result
85 notes_oracle_comment = """
86 .\\\" Oracle has added source availability information to this manual page"""
88 notes_header = """
89 .SH NOTES
90 """
92 notes_community = """
93 Further information about this software can be found on the open source community website at %s.
94 """
95 notes_source = """
96 This software was built from source available at http://opensolaris.org/.  The original community source was downloaded from  %s
97 """
99 def notes_section_text(header_seen, community, source, modified_date):
100         result = ''
102         # is there anything to do?
103         if community is not None or source is not None:
104                 if header_seen == False:
105                         result += notes_header
106                 result += notes_oracle_comment
107                 if modified_date is not None:
108                         result += ("\n.\\\" on %s" % modified_date)
109                 if source is not None:
110                         result += (notes_source % source)
111                 if community is not None:
112                         result += (notes_community % community)
114         return result
116 so_re = re.compile('^\.so.+$', re.MULTILINE)
117 section_re = re.compile('\.SH "?([^"]+).*$', re.IGNORECASE)
118 TH_re = re.compile('\.TH\s+(?:"[^"]+"|\S+)\s+(\S+)', re.IGNORECASE)
120 # mangler.man.stability = (mangler.man.stability)
121 # mangler.man.modified_date = (mangler.man.modified-date)
122 # mangler.man.availability = (pkg.fmri)
123 # mangler.man.source-url = (pkg.source-url)
124 # mangler.man.upstream-url = (pkg.upstream-url)
126 def mangle_manpage(manifest, action, text):
127         # manpages must have a taxonomy defined
128         stability = action.attrs.pop('mangler.man.stability', None)
129         if stability is None:
130                 sys.stderr.write("ERROR: manpage action missing mangler.man.stability: %s" % action)
131                 sys.exit(1)
133         # manpages may have a 'modified date'
134         modified_date = action.attrs.pop('mangler.man.modified-date', None)
136         # Rewrite the section in the .TH line to match the section in which
137         # we're delivering it.
138         rewrite_sect = action.attrs.pop('mangler.man.rewrite-section', 'true')
140         attributes_written = False
141         notes_seen = False
143         if 'pkg.fmri' in manifest.attributes:
144                 fmri = pkg.fmri.PkgFmri(manifest.attributes['pkg.fmri'])
145                 availability = fmri.pkg_name
147         community = None
148         if 'info.upstream-url' in manifest.attributes:
149                 community = manifest.attributes['info.upstream-url']
151         source = None
152         if 'info.source-url' in manifest.attributes:
153                 source = manifest.attributes['info.source-url']
154         elif 'info.repository-url' in manifest.attributes:
155                 source = manifest.attributes['info.repository-url']
157         # skip reference only pages
158         if so_re.match(text) is not None:
159                 return text
161         # tell man that we want tables (and eqn)
162         result = "'\\\" te\n"
164         # write the orginal data
165         for line in text.split('\n'):
166                 match = section_re.match(line)
167                 if match is not None:
168                         section = match.group(1)
169                         if section in ['SEE ALSO', 'NOTES']:
170                                 if attributes_written == False:
171                                         result += attributes_section_text(
172                                                                  availability,
173                                                                  stability,
174                                                                  modified_date)
175                                         attributes_written = True
176                                 if section == 'NOTES':
177                                         notes_seen = True
178                         match = TH_re.match(line)
179                         if match and rewrite_sect.lower() == "true":
180                                 # Use the section defined by the filename, rather than
181                                 # the directory in which it sits.
182                                 sect = os.path.splitext(action.attrs["path"])[1][1:]
183                                 line = line[:match.span(1)[0]] + sect + \
184                                     line[match.span(1)[1]:]
186                 result += ("%s\n" % line)
188         if attributes_written == False:
189                 result += attributes_section_text(availability, stability,
190                     modified_date)
192         result += notes_section_text(notes_seen, community, source,
193             modified_date)
195         return result
199 # mangler.elf.strip = (true|false)
201 def mangle_elf(manifest, action, src, dest):
202         pass
205 # mangler.script.file-magic =
207 def mangle_script(manifest, action, text):
208         return text
211 # mangler.strip_cddl = false
213 def mangle_cddl(manifest, action, text):
214         strip_cddl = action.attrs.pop('mangler.strip_cddl', 'true')
215         if strip_cddl is 'false':
216                 return text
217         cddl_re = re.compile('^[^\n]*CDDL HEADER START.+CDDL HEADER END[^\n]*$',
218                              re.MULTILINE|re.DOTALL)
219         return cddl_re.sub('', text)
221 def mangle_path(manifest, action, src, dest):
222         if elf.is_elf_object(src):
223                 mangle_elf(manifest, action, src, dest)
224         else:
225                 # a 'text' document (script, man page, config file, ...
226                 ifp = open(src, 'r')
227                 text = ifp.read()
228                 ifp.close()
230                 # remove the CDDL from files
231                 result = mangle_cddl(manifest, action, text)
233                 if 'facet.doc.man' in action.attrs:
234                         result = mangle_manpage(manifest, action, result)
235                 elif 'mode' in action.attrs and int(action.attrs['mode'], 8) & 0111 != 0:
236                         result = mangle_script(manifest, action, result)
238                 if text != result:
239                         destdir = os.path.dirname(dest)
240                         if not os.path.exists(destdir):
241                                 os.makedirs(destdir)
242                         with open(dest, 'w') as ofp:
243                             ofp.write(result)
246 # mangler.bypass = (true|false)
248 def mangle_paths(manifest, search_paths, destination):
249         for action in manifest.gen_actions_by_type("file"):
250                 bypass = action.attrs.pop('mangler.bypass', 'false').lower()
251                 if bypass == 'true':
252                         continue
254                 path = None
255                 if 'path' in action.attrs:
256                         path = action.attrs['path']
257                 if action.hash and action.hash != 'NOHASH':
258                         path = action.hash
259                 if not path:
260                         continue
262                 if not os.path.exists(destination):
263                         os.makedirs(destination)
265                 dest = os.path.join(destination, path)
266                 for directory in search_paths:
267                         if directory != destination:
268                                 src = os.path.join(directory, path)
269                                 if os.path.isfile(src):
270                                         mangle_path(manifest, action, src, dest)
271                                         break
273 def load_manifest(manifest_file):
274         manifest = pkg.manifest.Manifest()
275         manifest.set_content(pathname=manifest_file)
277         return manifest
279 def usage():
280         print "Usage: %s [-m|--manifest (file)] [-d|--search-directory (dir)] [-D|--destination (dir)] " % (sys.argv[0].split('/')[-1])
281         sys.exit(1)
283 def main():
284         import getopt
286         # FLUSH STDOUT 
287         sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
289         search_paths = []
290         destination = None
291         manifests = []
293         try:
294                 opts, args = getopt.getopt(sys.argv[1:], "D:d:m:",
295                         ["destination=", "search-directory=", "manifest="])
296         except getopt.GetoptError, err:
297                 print str(err)
298                 usage()
300         for opt, arg in opts:
301                 if opt in [ "-D", "--destination" ]:
302                         destination = arg
303                 elif opt in [ "-d", "--search-directory" ]:
304                         search_paths.append(arg)
305                 elif opt in [ "-m", "--manifest" ]:
306                         try:
307                                 manifest = load_manifest(arg)
308                         except IOError, err:
309                                 print "oops, %s: %s" % (arg, str(err))
310                                 usage()
311                         else:
312                                 manifests.append(manifest)
313                 else:
314                         usage()
316         if destination == None:
317                 usage()
319         for manifest in manifests:
320                 mangle_paths(manifest, search_paths, destination)
321                 print manifest
323         sys.exit(0)
325 if __name__ == "__main__":
326         main()