#!/usr/bin/env python # Copyright (C) 2001 Alexander S. Guy # Andern Research Labs # # 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, 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. */ # # Copyright 2001, Russell Nelson # Added reading in of packages. # Added missing package information fields. # Changed render_control() to __repr__(). # # Current Issues: # The API doesn't validate package information fields. It should be # throwing exceptions in the right places. # Executions of tar could silently fail. # Executions of tar *do* fail, and loudly, because you have to specify a full filename, # and tar complains if any files are missing, and the opkg spec doesn't require # people to say "./control.tar.gz" or "./control" when they package files. # It would be much better to require ./control or disallow ./control (either) # rather than letting people pick. Some freedoms aren't worth their cost. import tempfile import os import sys import glob import md5 import re import string import commands from stat import ST_SIZE import arfile import tarfile class Version: """A class for holding parsed package version information.""" def __init__(self, epoch, version): self.epoch = epoch self.version = version def _versioncompare(self, selfversion, refversion): if not selfversion: selfversion = "" if not refversion: refversion = "" while 1: ## first look for non-numeric version component selfm = re.match('([^0-9]*)(.*)', selfversion) #print 'selfm', selfm.groups() (selfalpha, selfversion) = selfm.groups() refm = re.match('([^0-9]*)(.*)', refversion) #print 'refm', refm.groups() (refalpha, refversion) = refm.groups() if (selfalpha > refalpha): return 1 elif (selfalpha < refalpha): return -1 ## now look for numeric version component (selfnum, selfversion) = re.match('([0-9]*)(.*)', selfversion).groups() (refnum, refversion) = re.match('([0-9]*)(.*)', refversion).groups() #print 'selfnum', selfnum, selfversion #print 'refnum', refnum, refversion if (selfnum != ''): selfnum = int(selfnum) else: selfnum = -1 if (refnum != ''): refnum = int(refnum) else: refnum = -1 if (selfnum > refnum): return 1 elif (selfnum < refnum): return -1 if selfversion == '' and refversion == '': return 0 def compare(self, ref): if (self.epoch > ref.epoch): return 1 elif (self.epoch < ref.epoch): return -1 else: self_ver_comps = re.match(r"(.+?)(-r.+)?$", self.version) ref_ver_comps = re.match(r"(.+?)(-r.+)?$", ref.version) #print (self_ver_comps.group(1), self_ver_comps.group(2)) #print (ref_ver_comps.group(1), ref_ver_comps.group(2)) r = self._versioncompare(self_ver_comps.group(1), ref_ver_comps.group(1)) if r == 0: r = self._versioncompare(self_ver_comps.group(2), ref_ver_comps.group(2)) #print "compare: %s vs %s = %d" % (self, ref, r) return r def __str__(self): return str(self.epoch) + ":" + self.version def parse_version(versionstr): epoch = 0 # check for epoch m = re.match('([0-9]*):(.*)', versionstr) if m: (epochstr, versionstr) = m.groups() epoch = int(epochstr) return Version(epoch, versionstr) class Package: """A class for creating objects to manipulate (e.g. create) opkg packages.""" def __init__(self, fn=None): self.package = None self.version = 'none' self.parsed_version = None self.architecture = None self.maintainer = None self.source = None self.description = None self.depends = None self.provides = None self.replaces = None self.conflicts = None self.recommends = None self.suggests = None self.section = None self.filename_header = None self.file_list = [] # md5 and size is lazy attribute, computed on demand #self.md5 = None #self.size = None self.installed_size = None self.filename = None self.isdeb = 0 self.file_ext_opk = "ipk" self.homepage = None self.oe = None self.priority = None self.tags = None self.fn = fn if fn: # see if it is deb format f = open(fn, "rb") magic = f.read(4) f.seek(0, 0) if (magic == "!= 0: self.packages[name] = pkg return 0 else: return 1 def read_packages_file(self, fn): f = open(fn, "r") while 1: pkg = Package() pkg.read_control(f) if pkg.get_package(): self.add_package(pkg) else: break f.close() return def write_packages_file(self, fn): f = open(fn, "w") names = self.packages.keys() names.sort() for name in names: f.write(self.packages[name].__repr__()) return def keys(self): return self.packages.keys() def __getitem__(self, key): return self.packages[key] if __name__ == "__main__": assert Version(0, "1.2.2-r1").compare(Version(0, "1.2.3-r0")) == -1 assert Version(0, "1.2.2-r0").compare(Version(0, "1.2.2+cvs20070308-r0")) == -1 assert Version(0, "1.2.2+cvs20070308").compare(Version(0, "1.2.2-r0")) == 1 assert Version(0, "1.2.2-r0").compare(Version(0, "1.2.2-r0")) == 0 assert Version(0, "1.2.2-r5").compare(Version(0, "1.2.2-r0")) == 1 package = Package() package.set_package("FooBar") package.set_version("0.1-fam1") package.set_architecture("arm") package.set_maintainer("Testing ") package.set_depends("libc") package.set_description("A test of the APIs.") print "<" sys.stdout.write(package) print ">" package.write_package("/tmp")