1f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin#!/usr/bin/env python
2f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin#
3f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin# Copyright (C) 2012 The Android Open Source Project
4f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin#
5f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin# Licensed under the Apache License, Version 2.0 (the "License");
6f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin# you may not use this file except in compliance with the License.
7f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin# You may obtain a copy of the License at
8f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin#
9f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin#      http://www.apache.org/licenses/LICENSE-2.0
10f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin#
11f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin# Unless required by applicable law or agreed to in writing, software
12f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin# distributed under the License is distributed on an "AS IS" BASIS,
13f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin# See the License for the specific language governing permissions and
15f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin# limitations under the License.
16f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin"""
17f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel BerlinUsage: generate-notice-files [plain text output file] [html output file] [file title] [directory of notices]
18f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
19f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel BerlinGenerate the Android notice files, including both text and html files.
20f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
21f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin-h to display this usage message and exit.
22f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin"""
23f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinfrom collections import defaultdict
24f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinimport getopt
25f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinimport hashlib
26f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinimport itertools
27f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinimport os
28f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinimport os.path
29f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinimport re
30f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinimport sys
31f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
32f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel BerlinMD5_BLOCKSIZE = 1024 * 1024
33f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel BerlinHTML_ESCAPE_TABLE = {
34f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    "&": "&",
35f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    '"': """,
36f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    "'": "'",
37f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    ">": ">",
38f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    "<": "&lt;",
39f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    }
40f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
41f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlintry:
42f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin  opts, args = getopt.getopt(sys.argv[1:], "h")
43f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinexcept getopt.GetoptError, err:
44f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print str(err)
45f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print __doc__
46f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    sys.exit(2)
47f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
48f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinfor o, a in opts:
49f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin  if o == "-h":
50f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print __doc__
51f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    sys.exit(2)
52f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin  else:
53f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >> sys.stderr, "unhandled option %s" % (o,)
54f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
55f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinif len(args) != 4:
56f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print """need exactly four arguments, the two output files, the file title
57f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin             and the directory containing notices, not %d""" % (len(args),)
58f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print __doc__
59f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    sys.exit(1)
60f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
61f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlindef hexify(s):
62f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    return ("%02x"*len(s)) % tuple(map(ord, s))
63f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
64f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlindef md5sum(filename):
65f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    """Calculate an MD5 of the file given by FILENAME,
66f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    and return hex digest as a string.
67f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    Output should be compatible with md5sum command"""
68f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
69f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    f = open(filename, "rb")
70f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    sum = hashlib.md5()
71f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    while 1:
72f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        block = f.read(MD5_BLOCKSIZE)
73f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        if not block:
74f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin            break
75f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        sum.update(block)
76f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    f.close()
77f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    return hexify(sum.digest())
78f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
79f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
80f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlindef html_escape(text):
81f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    """Produce entities within text."""
82f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    return "".join(HTML_ESCAPE_TABLE.get(c,c) for c in text)
83f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
84f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel BerlinHTML_OUTPUT_CSS="""
85f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin<style type="text/css">
86f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinbody { padding: 0; font-family: sans-serif; }
87f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin.same-license { background-color: #eeeeee; border-top: 20px solid white; padding: 10px; }
88f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin.label { font-weight: bold; }
89f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin.file-list { margin-left: 1em; color: blue; }
90f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin</style>
91f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin"""
92f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
93f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlindef combine_notice_files_html(file_hash, input_dir, output_filename):
94f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    """Combine notice files in FILE_HASH and output a HTML version to OUTPUT_FILENAME."""
95f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
96f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
97f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
98f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    # Set up a filename to row id table (anchors inside tables don't work in
99f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    # most browsers, but href's to table row ids do)
100f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    id_table = {}
101f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    id_count = 0
102f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    for value in file_hash.values():
103f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        for filename in value:
104f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin             id_table[filename] = id_count
105f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        id_count += 1
106f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
107f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    # Open the output file, and output the header pieces
108f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    output_file = open(output_filename, "wb")
109f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
110f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >> output_file, "<html><head>"
111f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >> output_file, HTML_OUTPUT_CSS
112f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >> output_file, '</head><body topmargin="0" leftmargin="0" rightmargin="0" bottommargin="0">'
113f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
114f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    # Output our table of contents
115f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >> output_file, '<div class="toc">'
116f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >> output_file, "<ul>"
117f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
118f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    # Flatten the list of lists into a single list of filenames
119f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    sorted_filenames = sorted(itertools.chain.from_iterable(file_hash.values()))
120f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
121f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    # Print out a nice table of contents
122f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    for filename in sorted_filenames:
123f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        stripped_filename = SRC_DIR_STRIP_RE.sub(r"\1", filename)
124f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file, '<li><a href="#id%d">%s</a></li>' % (id_table.get(filename), stripped_filename)
125f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
126f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >> output_file, "</ul>"
127f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >> output_file, "</div><!-- table of contents -->"
128f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    # Output the individual notice file lists
129f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >>output_file, '<table cellpadding="0" cellspacing="0" border="0">'
130f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    for value in file_hash.values():
131f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file, '<tr id="id%d"><td class="same-license">' % id_table.get(value[0])
132f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file, '<div class="label">Notices for file(s):</div>'
133f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file, '<div class="file-list">'
134f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        for filename in sorted(value):
135f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin            print >> output_file, "%s <br/>" % (SRC_DIR_STRIP_RE.sub(r"\1", filename))
136f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file, "</div><!-- file-list -->"
137f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file
138f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file, '<pre class="license-text">'
139f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file, html_escape(open(value[0]).read())
140f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file, "</pre><!-- license-text -->"
141f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file, "</td></tr><!-- same-license -->"
142f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file
143f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file
144f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file
145f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
146f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    # Finish off the file output
147f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >> output_file, "</table>"
148f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >> output_file, "</body></html>"
149f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    output_file.close()
150f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
151f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlindef combine_notice_files_text(file_hash, input_dir, output_filename, file_title):
152f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    """Combine notice files in FILE_HASH and output a text version to OUTPUT_FILENAME."""
153f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
154f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    SRC_DIR_STRIP_RE = re.compile(input_dir + "(/.*).txt")
155f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    output_file = open(output_filename, "wb")
156f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print >> output_file, file_title
157f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    for value in file_hash.values():
158f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin      print >> output_file, "============================================================"
159f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin      print >> output_file, "Notices for file(s):"
160f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin      for filename in sorted(value):
161f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        print >> output_file, SRC_DIR_STRIP_RE.sub(r"\1", filename)
162f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin      print >> output_file, "------------------------------------------------------------"
163f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin      print >> output_file, open(value[0]).read()
164f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    output_file.close()
165f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
166f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlindef main(args):
167f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    txt_output_file = args[0]
168f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    html_output_file = args[1]
169f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    file_title = args[2]
170f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
171f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    # Find all the notice files and md5 them
172f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    input_dir = os.path.normpath(args[3])
173f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    files_with_same_hash = defaultdict(list)
174f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    for root, dir, files in os.walk(input_dir):
175f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin        for file in files:
176f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin            if file.endswith(".txt"):
177f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin                filename = os.path.join(root, file)
178f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin                file_md5sum = md5sum(filename)
179f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin                files_with_same_hash[file_md5sum].append(filename)
180f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
181f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
182f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print "Combining NOTICE files into HTML"
183f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    combine_notice_files_html(files_with_same_hash, input_dir, html_output_file)
184f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    print "Combining NOTICE files into text"
185f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    combine_notice_files_text(files_with_same_hash, input_dir, txt_output_file, file_title)
186f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin
187f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlinif __name__ == "__main__":
188f5a97d7fbbda5ec8ad4ea73a8035d7fa88bbe1daDaniel Berlin    main(args)
189