#! /usr/bin/python ############################################################################## # # Copyright (c) 2002 Nexedi SARL and Contributors. All Rights Reserved. # Yoshinori Okuji # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # garantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## """Generate repository information on Business Templates. """ import tarfile import os import os.path import sys import tempfile import shutil import cgi property_list = ''' title version revision description license dependency_list provision_list copyright_list '''.strip().splitlines() bt_title_path = os.path.join('bt', 'title') def info(message): """Print a message to stdout. """ sys.stdout.write(message) def err(message): """Print a message to stderr. """ sys.stderr.write(message) def readProperty(property_dict, property_name, property_file): try: text = property_file.read() if property_name.endswith('_list'): property_dict[property_name[:-5]] = text and text.split('\n') or [] else: property_dict[property_name] = text finally: property_file.close() def readBusinessTemplate(tar): """Read an archived Business Template info. """ property_dict = {} for info in tar: name_list = info.name.split('/') if len(name_list) == 3 and name_list[1] == 'bt' and name_list[2] in property_list: property_file = tar.extractfile(info) property_name = name_list[2] readProperty(property_dict, property_name, property_file) return property_dict def readBusinessTemplateDirectory(dir): """Read Business Template Directory info. """ property_dict = {} for property_name in property_list: filename = os.path.join(dir, 'bt', property_name) if os.path.isfile(filename): property_file = open(filename, 'rb') readProperty(property_dict, property_name, property_file) return property_dict def generateInformation(fd): os.write(fd, '\n') os.write(fd, '\n') for file in sorted(os.listdir(os.getcwd())): if file.endswith('.bt5'): info('Reading %s... ' % (file,)) try: tar = tarfile.open(file, 'r:gz') except tarfile.TarError: err('An error happened in %s; skipping\n' % (file,)) continue try: property_dict = readBusinessTemplate(tar) finally: tar.close() elif os.path.isfile(os.path.join(file, bt_title_path)): info('Reading Directory %s... ' % (file,)) property_dict = readBusinessTemplateDirectory(file) else: continue property_id_list = property_dict.keys() property_id_list.sort() os.write(fd, ' \n') info('done\n') os.write(fd, '\n') def main(): if len(sys.argv) < 2: dir_list = ['.'] else: dir_list = sys.argv[1:] cwd = os.getcwd() for d in dir_list: os.chdir(d) fd, path = tempfile.mkstemp() try: try: generateInformation(fd) finally: os.close(fd) except: os.remove(path) raise else: shutil.move(path, 'bt5list') cur_umask = os.umask(0666) os.chmod('bt5list', 0666 & ~cur_umask) os.umask(cur_umask) os.chdir(cwd) # monkey patch tarfile library if python version is 2.4. if sys.version_info[0:2] == (2, 4): from tarfile import BLOCKSIZE, GNUTYPE_SPARSE, SUPPORTED_TYPES, TarFile, \ TarInfo, calc_chksum, normpath, nts def frombuf(cls, buf): """Construct a TarInfo object from a 512 byte string buffer. """ tarinfo = cls() tarinfo.name = nts(buf[0:100]) tarinfo.mode = int(buf[100:108], 8) tarinfo.uid = int(buf[108:116],8) tarinfo.gid = int(buf[116:124],8) # There are two possible codings for the size field we # have to discriminate, see comment in tobuf() below. if buf[124] != chr(0200): tarinfo.size = long(buf[124:136], 8) else: tarinfo.size = 0L for i in range(11): tarinfo.size <<= 8 tarinfo.size += ord(buf[125 + i]) tarinfo.mtime = long(buf[136:148], 8) tarinfo.chksum = int(buf[148:156], 8) tarinfo.type = buf[156:157] tarinfo.linkname = nts(buf[157:257]) tarinfo.uname = nts(buf[265:297]) tarinfo.gname = nts(buf[297:329]) try: tarinfo.devmajor = int(buf[329:337], 8) tarinfo.devminor = int(buf[337:345], 8) except ValueError: tarinfo.devmajor = tarinfo.devmajor = 0 tarinfo.prefix = buf[345:500] # The prefix field is used for filenames > 100 in # the POSIX standard. # name = prefix + '/' + name if tarinfo.type != GNUTYPE_SPARSE: tarinfo.name = normpath(os.path.join(nts(tarinfo.prefix), tarinfo.name)) # Directory names should have a '/' at the end. if tarinfo.isdir() and tarinfo.name[-1:] != "/": tarinfo.name += "/" return tarinfo TarInfo.frombuf = classmethod(frombuf) def next(self): """Return the next member of the archive as a TarInfo object, when TarFile is opened for reading. Return None if there is no more available. """ self._check("ra") if self.firstmember is not None: m = self.firstmember self.firstmember = None return m # Read the next block. self.fileobj.seek(self.offset) while True: buf = self.fileobj.read(BLOCKSIZE) if not buf: return None try: tarinfo = TarInfo.frombuf(buf) except ValueError: if self.ignore_zeros: if buf.count(NUL) == BLOCKSIZE: adj = "empty" else: adj = "invalid" self._dbg(2, "0x%X: %s block" % (self.offset, adj)) self.offset += BLOCKSIZE continue else: # Block is empty or unreadable. if self.offset == 0: # If the first block is invalid. That does not # look like a tar archive we can handle. raise ReadError,"empty, unreadable or compressed file" return None break # We shouldn't rely on this checksum, because some tar programs # calculate it differently and it is merely validating the # header block. We could just as well skip this part, which would # have a slight effect on performance... if tarinfo.chksum != calc_chksum(buf): self._dbg(1, "tarfile: Bad Checksum %r" % tarinfo.name) # Set the TarInfo object's offset to the current position of the # TarFile and set self.offset to the position where the data blocks # should begin. tarinfo.offset = self.offset self.offset += BLOCKSIZE # Check if the TarInfo object has a typeflag for which a callback # method is registered in the TYPE_METH. If so, then call it. if tarinfo.type in self.TYPE_METH: return self.TYPE_METH[tarinfo.type](self, tarinfo) tarinfo.offset_data = self.offset if tarinfo.isreg() or tarinfo.type not in SUPPORTED_TYPES: # Skip the following data blocks. self.offset += self._block(tarinfo.size) if tarinfo.isreg() and tarinfo.name[:-1] == "/": # some old tar programs don't know DIRTYPE tarinfo.type = DIRTYPE self.members.append(tarinfo) return tarinfo TarFile.next = next if __name__ == "__main__": main()