summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2012-11-21 15:28:00 -0800
committerAnas Nashif <anas.nashif@intel.com>2012-11-21 15:28:00 -0800
commitec6268183d43997c7fe124ca40a877edb0d7555b (patch)
treeeb871af5b189b33b9bdbb18fc7fd77d40b0e8c8f
downloadrpmlint-ec6268183d43997c7fe124ca40a877edb0d7555b.tar.gz
rpmlint-ec6268183d43997c7fe124ca40a877edb0d7555b.tar.bz2
rpmlint-ec6268183d43997c7fe124ca40a877edb0d7555b.zip
Imported Upstream version 1.4upstream/1.4
-rw-r--r--AUTHORS14
-rw-r--r--AbstractCheck.py108
-rw-r--r--BinariesCheck.py568
-rw-r--r--COPYING340
-rw-r--r--ChangeLog4871
-rw-r--r--Config.py161
-rw-r--r--ConfigCheck.py65
-rw-r--r--DistributionCheck.py92
-rw-r--r--DocFilesCheck.py105
-rw-r--r--FHSCheck.py83
-rw-r--r--FilesCheck.py1341
-rw-r--r--Filter.py147
-rw-r--r--I18NCheck.py206
-rw-r--r--INSTALL16
-rw-r--r--InitScriptCheck.py286
-rw-r--r--LSBCheck.py66
-rw-r--r--Makefile110
-rw-r--r--MenuCheck.py460
-rw-r--r--MenuXDGCheck.py53
-rw-r--r--NamingPolicyCheck.py114
-rw-r--r--PamCheck.py42
-rw-r--r--Pkg.py865
-rw-r--r--PostCheck.py257
-rw-r--r--README67
-rw-r--r--README.devel15
-rw-r--r--RpmFileCheck.py50
-rw-r--r--SignatureCheck.py60
-rw-r--r--SourceCheck.py76
-rw-r--r--SpecCheck.py812
-rw-r--r--TagsCheck.py1138
-rw-r--r--ZipCheck.py110
-rw-r--r--__isocodes__.py7965
-rw-r--r--__version__.py2
-rw-r--r--config214
-rwxr-xr-xrpmdiff294
-rwxr-xr-xrpmlint387
-rw-r--r--rpmlint.1115
-rw-r--r--rpmlint.bash-completion94
-rwxr-xr-xtest.sh17
-rw-r--r--test/PamCheck-0.1-1.i586.rpmbin0 -> 2100 bytes
-rw-r--r--test/SpecCheck.spec62
-rw-r--r--test/test.PamCheck.py22
-rw-r--r--test/test.Pkg.py30
-rw-r--r--test/test.SpecCheck.py30
-rw-r--r--tools/Testing.py42
-rwxr-xr-xtools/generate-isocodes.py44
46 files changed, 22016 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..0f8ae9c
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,14 @@
+Chmouel Boudjnah
+Christian Belisle
+David Baudens <baudens at mandriva.com>
+Frederic Crozat <fcrozat at mandriva.com>
+Frédéric Lepied <flepied at mandriva.com>
+Guillaume Rousse <guillomovitch at mandriva.org>
+Gwenole Beauchesne <gbeauchesne at mandriva.com>
+Michael Scherer <misc at mandriva.org>
+Nicolas Planel <nplanel at mandriva.com>
+Pablo Saratxaga <pablo at mandriva.com>
+Pixel <pixel at mandriva.com>
+Rafael Garcia-Suarez <rgarciasuarez at mandriva.com>
+Thierry Vignaud <tvignaud at mandriva.com>
+Ville Skyttä <ville.skytta at iki.fi>
diff --git a/AbstractCheck.py b/AbstractCheck.py
new file mode 100644
index 0000000..91c5879
--- /dev/null
+++ b/AbstractCheck.py
@@ -0,0 +1,108 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : AbstractCheck.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Tue Sep 28 00:22:38 1999
+# Version : $Id: AbstractCheck.py 1891 2011-11-23 20:03:00Z scop $
+# Purpose : Abstract class to hold all the derived classes.
+#############################################################################
+
+import re
+import socket
+import urllib2
+
+from Filter import addDetails, printInfo, printWarning
+import Config
+
+# Note: do not add any capturing parentheses here
+macro_regex = re.compile('%+[{(]?\w+[)}]?')
+
+class _HeadRequest(urllib2.Request):
+ def get_method(self):
+ return "HEAD"
+class _HeadRedirectHandler(urllib2.HTTPRedirectHandler):
+ def redirect_request(*args):
+ res = urllib2.HTTPRedirectHandler.redirect_request(*args)
+ if res:
+ res = _HeadRequest(res.get_full_url())
+ return res
+
+class AbstractCheck:
+ known_checks = {}
+
+ def __init__(self, name):
+ if not AbstractCheck.known_checks.get(name):
+ AbstractCheck.known_checks[name] = self
+ self.name = name
+ self.verbose = False
+ self.network_enabled = Config.getOption("NetworkEnabled", False)
+ self.network_timeout = Config.getOption("NetworkTimeout", 10)
+
+ def check(self, pkg):
+ raise NotImplementedError('check must be implemented in subclass')
+
+ def check_url(self, pkg, tag, url):
+ """Check that URL points to something that seems to exist.
+ Return info() of the response if available."""
+ if not self.network_enabled:
+ if self.verbose:
+ printInfo(pkg, 'network-checks-disabled', url)
+ return
+
+ if self.verbose:
+ printInfo(pkg, 'checking-url', url,
+ '(timeout %s seconds)' % self.network_timeout)
+
+ # Could use timeout kwarg to urlopen, but that's python >= 2.6 only
+ socket.setdefaulttimeout(self.network_timeout)
+ res = None
+ try:
+ opener = urllib2.build_opener(_HeadRedirectHandler())
+ opener.addheaders = [('User-Agent',
+ 'rpmlint/%s' % Config.__version__)]
+ res = opener.open(_HeadRequest(url))
+ except Exception, e:
+ errstr = str(e) or repr(e) or type(e)
+ printWarning(pkg, 'invalid-url', '%s:' % tag, url, errstr)
+ info = None
+ if res:
+ info = res.info()
+ res.close()
+ return info
+
+class AbstractFilesCheck(AbstractCheck):
+ def __init__(self, name, file_regexp):
+ self.__files_re = re.compile(file_regexp)
+ AbstractCheck.__init__(self, name)
+ def check(self, pkg):
+ if pkg.isSource():
+ return
+ ghosts = pkg.ghostFiles()
+ for filename in (x for x in pkg.files() if x not in ghosts):
+ if self.__files_re.match(filename):
+ self.check_file(pkg, filename)
+
+
+ def check_file(self, pkg, filename):
+ """Virtual method called for each file that match the regexp passed
+ to the constructor.
+ """
+ raise NotImplementedError('check must be implemented in subclass')
+
+addDetails(
+'invalid-url',
+'''The value should be a valid, public HTTP, HTTPS, or FTP URL.''',
+
+'network-checks-disabled',
+'''Checks requiring network access have not been enabled in configuration,
+see the NetworkEnabled option.''',
+)
+
+# AbstractCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/BinariesCheck.py b/BinariesCheck.py
new file mode 100644
index 0000000..0355557
--- /dev/null
+++ b/BinariesCheck.py
@@ -0,0 +1,568 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : BinariesCheck.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Tue Sep 28 07:01:42 1999
+# Version : $Id: BinariesCheck.py 1893 2011-11-23 20:24:32Z scop $
+# Purpose : check binary files in a binary rpm package.
+#############################################################################
+
+import re
+import stat
+
+import rpm
+
+from Filter import addDetails, printError, printWarning
+import AbstractCheck
+import Config
+import Pkg
+
+
+DEFAULT_SYSTEM_LIB_PATHS = (
+ '/lib', '/usr/lib', '/usr/X11R6/lib',
+ '/lib64', '/usr/lib64', '/usr/X11R6/lib64')
+
+class BinaryInfo:
+
+ needed_regex = re.compile('\s+\(NEEDED\).*\[(\S+)\]')
+ rpath_regex = re.compile('\s+\(RPATH\).*\[(\S+)\]')
+ soname_regex = re.compile('\s+\(SONAME\).*\[(\S+)\]')
+ comment_regex = re.compile('^\s+\[\s*\d+\]\s+\.comment\s+')
+ pic_regex = re.compile('^\s+\[\s*\d+\]\s+\.rela?\.(data|text)')
+ # GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4
+ stack_regex = re.compile('^\s+GNU_STACK\s+(?:(?:\S+\s+){5}(\S+)\s+)?')
+ stack_exec_regex = re.compile('^..E$')
+ undef_regex = re.compile('^undefined symbol:\s+(\S+)')
+ unused_regex = re.compile('^\s+(\S+)')
+ exit_call_regex = re.compile('\s+FUNC\s+.*?\s+(_?exit(?:@\S+)?)(?:\s|$)')
+ fork_call_regex = re.compile('\s+FUNC\s+.*?\s+(fork(?:@\S+)?)(?:\s|$)')
+
+ def __init__(self, pkg, path, file, is_ar, is_shlib):
+ self.readelf_error = False
+ self.needed = []
+ self.rpath = []
+ self.undef = []
+ self.unused = []
+ self.comment = False
+ self.soname = False
+ self.non_pic = True
+ self.stack = False
+ self.exec_stack = False
+ self.exit_calls = []
+ fork_called = False
+ self.tail = ''
+
+ is_debug = path.endswith('.debug')
+
+ cmd = ['env', 'LC_ALL=C', 'readelf', '-W', '-S', '-l', '-d', '-s']
+ cmd.append(path)
+ res = Pkg.getstatusoutput(cmd)
+ if not res[0]:
+ for l in res[1].splitlines():
+
+ r = BinaryInfo.needed_regex.search(l)
+ if r:
+ self.needed.append(r.group(1))
+ continue
+
+ r = BinaryInfo.rpath_regex.search(l)
+ if r:
+ for p in r.group(1).split(':'):
+ self.rpath.append(p)
+ continue
+
+ if BinaryInfo.comment_regex.search(l):
+ self.comment = True
+ continue
+
+ if BinaryInfo.pic_regex.search(l):
+ self.non_pic = False
+ continue
+
+ r = BinaryInfo.soname_regex.search(l)
+ if r:
+ self.soname = r.group(1)
+ continue
+
+ r = BinaryInfo.stack_regex.search(l)
+ if r:
+ self.stack = True
+ flags = r.group(1)
+ if flags and BinaryInfo.stack_exec_regex.search(flags):
+ self.exec_stack = True
+ continue
+
+ if is_shlib:
+ r = BinaryInfo.exit_call_regex.search(l)
+ if r:
+ self.exit_calls.append(r.group(1))
+ continue
+ r = BinaryInfo.fork_call_regex.search(l)
+ if r:
+ fork_called = True
+ continue
+
+ if self.non_pic:
+ self.non_pic = 'TEXTREL' in res[1]
+
+ # Ignore all exit() calls if fork() is being called.
+ # Does not have any context at all but without this kludge, the
+ # number of false positives would probably be intolerable.
+ if fork_called:
+ self.exit_calls = []
+
+ else:
+ self.readelf_error = True
+ printWarning(pkg, 'binaryinfo-readelf-failed',
+ file, re.sub('\n.*', '', res[1]))
+
+ fobj = None
+ try:
+ fobj = open(path)
+ fobj.seek(-12, 2) # 2 == os.SEEK_END, for python 2.4 compat (#172)
+ self.tail = fobj.read()
+ except Exception, e:
+ printWarning(pkg, 'binaryinfo-tail-failed %s: %s' % (file, e))
+ if fobj:
+ fobj.close()
+
+ # Undefined symbol and unused direct dependency checks make sense only
+ # for installed packages.
+ # skip debuginfo: https://bugzilla.redhat.com/190599
+ if not is_ar and not is_debug and isinstance(pkg, Pkg.InstalledPkg):
+ # We could do this with objdump, but it's _much_ simpler with ldd.
+ res = Pkg.getstatusoutput(
+ ('env', 'LC_ALL=C', 'ldd', '-d', '-r', path))
+ if not res[0]:
+ for l in res[1].splitlines():
+ undef = BinaryInfo.undef_regex.search(l)
+ if undef:
+ self.undef.append(undef.group(1))
+ if self.undef:
+ cmd = self.undef[:]
+ cmd.insert(0, 'c++filt')
+ try:
+ res = Pkg.getstatusoutput(cmd)
+ if not res[0]:
+ self.undef = res[1].splitlines()
+ except:
+ pass
+ else:
+ printWarning(pkg, 'ldd-failed', file)
+ res = Pkg.getstatusoutput(
+ ('env', 'LC_ALL=C', 'ldd', '-r', '-u', path))
+ if res[0]:
+ # Either ldd doesn't grok -u (added in glibc 2.3.4) or we have
+ # unused direct dependencies
+ in_unused = False
+ for l in res[1].splitlines():
+ if not l.rstrip():
+ pass
+ elif l.startswith('Unused direct dependencies'):
+ in_unused = True
+ elif in_unused:
+ unused = BinaryInfo.unused_regex.search(l)
+ if unused:
+ self.unused.append(unused.group(1))
+ else:
+ in_unused = False
+
+path_regex = re.compile('(.*/)([^/]+)')
+numeric_dir_regex = re.compile('/usr(?:/share)/man/man./(.*)\.[0-9](?:\.gz|\.bz2)')
+versioned_dir_regex = re.compile('[^.][0-9]')
+ldso_soname_regex = re.compile('^ld(-linux(-(ia|x86_)64))?\.so')
+so_regex = re.compile('/lib(64)?/[^/]+\.so(\.[0-9]+)*$')
+validso_regex = re.compile('(\.so\.\d+(\.\d+)*|\d\.so)$')
+sparc_regex = re.compile('SPARC32PLUS|SPARC V9|UltraSPARC')
+system_lib_paths = Config.getOption('SystemLibPaths', DEFAULT_SYSTEM_LIB_PATHS)
+pie_exec_re = Config.getOption('PieExecutables')
+if pie_exec_re:
+ pie_exec_re = re.compile(pie_exec_re)
+usr_lib_regex = re.compile('^/usr/lib(64)?/')
+bin_regex = re.compile('^(/usr(/X11R6)?)?/s?bin/')
+soversion_regex = re.compile('.*?([0-9][.0-9]*)\\.so|.*\\.so\\.([0-9][.0-9]*).*')
+reference_regex = re.compile('\.la$|^/usr/lib(64)?/pkgconfig/')
+usr_lib_exception_regex = re.compile(Config.getOption('UsrLibBinaryException', '^/usr/lib(64)?/(perl|python|ruby|menu|pkgconfig|ocaml|lib[^/]+\.(so|l?a)$|bonobo/servers/)'))
+srcname_regex = re.compile('(.*?)-[0-9]')
+invalid_dir_ref_regex = re.compile('/(home|tmp)(\W|$)')
+ocaml_mixed_regex = re.compile('^Caml1999X0\d\d$')
+
+def dir_base(path):
+ res = path_regex.search(path)
+ if res:
+ return res.group(1), res.group(2)
+ else:
+ return '', path
+
+class BinariesCheck(AbstractCheck.AbstractCheck):
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, 'BinariesCheck')
+
+ def check(self, pkg):
+ # Check only binary package
+ if pkg.isSource():
+ return
+
+ files = pkg.files()
+ exec_files = []
+ has_lib = False
+ version = None
+ binary = False
+ binary_in_usr_lib = False
+ has_usr_lib_file = False
+
+ multi_pkg = False
+ res = srcname_regex.search(pkg[rpm.RPMTAG_SOURCERPM] or '')
+ if res:
+ multi_pkg = (pkg.name != res.group(1))
+
+ for fname, pkgfile in files.items():
+
+ if not stat.S_ISDIR(pkgfile.mode) and usr_lib_regex.search(fname):
+ has_usr_lib_file = True
+ if not binary_in_usr_lib and \
+ usr_lib_exception_regex.search(fname):
+ # Fake that we have binaries there to avoid
+ # only-non-binary-in-usr-lib false positives
+ binary_in_usr_lib = True
+
+ is_elf = 'ELF' in pkgfile.magic
+ is_ar = 'current ar archive' in pkgfile.magic
+ is_ocaml_native = 'Objective caml native' in pkgfile.magic
+ is_binary = is_elf or is_ar or is_ocaml_native
+
+ if not is_binary:
+ if reference_regex.search(fname):
+ lines = pkg.grep(invalid_dir_ref_regex, fname)
+ if lines:
+ printError(pkg, 'invalid-directory-reference', fname,
+ '(line %s)' % ", ".join(lines))
+ continue
+
+ # binary files only from here on
+
+ binary = True
+
+ if has_usr_lib_file and not binary_in_usr_lib and \
+ usr_lib_regex.search(fname):
+ binary_in_usr_lib = True
+
+ if pkg.arch == 'noarch':
+ printError(
+ pkg,
+ 'arch-independent-package-contains-binary-or-object',
+ fname)
+ continue
+
+ # arch dependent packages only from here on
+
+ # in /usr/share ?
+ if fname.startswith('/usr/share/'):
+ printError(pkg, 'arch-dependent-file-in-usr-share', fname)
+
+ # in /etc ?
+ if fname.startswith('/etc/'):
+ printError(pkg, 'binary-in-etc', fname)
+
+ if pkg.arch == 'sparc' and sparc_regex.search(pkgfile.magic):
+ printError(pkg, 'non-sparc32-binary', fname)
+
+ if is_ocaml_native or fname.endswith('.o') or \
+ fname.endswith('.static'):
+ continue
+
+ # stripped ?
+ if 'not stripped' in pkgfile.magic:
+ printWarning(pkg, 'unstripped-binary-or-object', fname)
+
+ # inspect binary file
+ is_shlib = so_regex.search(fname)
+ bin_info = BinaryInfo(pkg, pkgfile.path, fname, is_ar, is_shlib)
+
+ if is_shlib:
+ has_lib = True
+
+ # shared libs
+ if is_shlib and not bin_info.readelf_error:
+
+ # so name in library
+ if not bin_info.soname:
+ printWarning(pkg, 'no-soname', fname)
+ else:
+ if not validso_regex.search(bin_info.soname):
+ printError(pkg, 'invalid-soname', fname,
+ bin_info.soname)
+ else:
+ (directory, base) = dir_base(fname)
+ try:
+ symlink = directory + bin_info.soname
+ link = files[symlink].linkto
+ if link not in (fname, base, ''):
+ printError(pkg, 'invalid-ldconfig-symlink',
+ fname, link)
+ except KeyError:
+ if base.startswith("lib") or base.startswith("ld-"):
+ printError(pkg, 'no-ldconfig-symlink', fname)
+
+ res = soversion_regex.search(bin_info.soname)
+ if res:
+ soversion = res.group(1) or res.group(2)
+ if version is None:
+ version = soversion
+ elif version != soversion:
+ version = -1
+
+ if bin_info.non_pic:
+ printError(pkg, 'shlib-with-non-pic-code', fname)
+
+ # It could be useful to check these for others than shared
+ # libs only, but that has potential to generate lots of
+ # false positives and noise.
+ for s in bin_info.undef:
+ printWarning(pkg, 'undefined-non-weak-symbol', fname, s)
+ for s in bin_info.unused:
+ printWarning(pkg, 'unused-direct-shlib-dependency',
+ fname, s)
+
+ # calls exit() or _exit()?
+ for ec in bin_info.exit_calls:
+ printWarning(pkg, 'shared-lib-calls-exit', fname, ec)
+
+ # rpath ?
+ if bin_info.rpath:
+ for p in bin_info.rpath:
+ if p in system_lib_paths or not usr_lib_regex.search(p):
+ printError(pkg, 'binary-or-shlib-defines-rpath',
+ fname, bin_info.rpath)
+ break
+
+ is_exec = 'executable' in pkgfile.magic
+ is_shobj = 'shared object' in pkgfile.magic
+
+ if not is_exec and not is_shobj:
+ continue
+
+ if is_shobj and not is_exec and '.so' not in fname and \
+ bin_regex.search(fname):
+ # pkgfile.magic does not contain "executable" for PIEs
+ is_exec = True
+
+ if is_exec:
+
+ if bin_regex.search(fname):
+ exec_files.append(fname)
+
+ if ocaml_mixed_regex.search(bin_info.tail):
+ printWarning(pkg, 'ocaml-mixed-executable', fname)
+
+ if not is_shobj and pie_exec_re and pie_exec_re.search(fname):
+ printError(pkg, 'non-position-independent-executable',
+ fname)
+
+ if bin_info.readelf_error:
+ continue
+
+ if not bin_info.needed and not (
+ bin_info.soname and ldso_soname_regex.search(bin_info.soname)):
+ if is_shobj:
+ printError(pkg,
+ 'shared-lib-without-dependency-information',
+ fname)
+ else:
+ printError(pkg, 'statically-linked-binary', fname)
+
+ else:
+ # linked against libc ?
+ if "libc." not in fname and \
+ (not bin_info.soname or \
+ ("libc." not in bin_info.soname and \
+ not ldso_soname_regex.search(bin_info.soname))):
+
+ found_libc = False
+ for lib in bin_info.needed:
+ if "libc." in lib:
+ found_libc = True
+ break
+
+ if not found_libc:
+ if is_shobj:
+ printError(pkg, 'library-not-linked-against-libc',
+ fname)
+ else:
+ printError(pkg, 'program-not-linked-against-libc',
+ fname)
+
+ if bin_info.stack:
+ if bin_info.exec_stack:
+ printWarning(pkg, 'executable-stack', fname)
+ elif not bin_info.readelf_error and (
+ pkg.arch.endswith("86") or pkg.arch.startswith("pentium") or
+ pkg.arch in ("athlon", "x86_64")):
+ printError(pkg, 'missing-PT_GNU_STACK-section', fname)
+
+ if has_lib:
+ for f in exec_files:
+ printError(pkg, 'executable-in-library-package', f)
+ for f in files:
+ res = numeric_dir_regex.search(f)
+ fn = res and res.group(1) or f
+ if f not in exec_files and not so_regex.search(f) and \
+ not versioned_dir_regex.search(fn):
+ printError(pkg, 'non-versioned-file-in-library-package', f)
+ if version and version != -1 and version not in pkg.name:
+ printError(pkg, 'incoherent-version-in-name', version)
+
+ if not binary and not multi_pkg and pkg.arch != 'noarch':
+ printError(pkg, 'no-binary')
+
+ if has_usr_lib_file and not binary_in_usr_lib:
+ printWarning(pkg, 'only-non-binary-in-usr-lib')
+
+# Create an object to enable the auto registration of the test
+check = BinariesCheck()
+
+# Add information about checks
+addDetails(
+'arch-independent-package-contains-binary-or-object',
+'''The package contains a binary or object file but is tagged
+noarch.''',
+
+'arch-dependent-file-in-usr-share',
+'''This package installs an ELF binary in the /usr/share
+ hierarchy, which is reserved for architecture-independent files.''',
+
+'binary-in-etc',
+'''This package installs an ELF binary in /etc. Both the
+FHS and the FSSTND forbid this.''',
+
+# 'non-sparc32-binary',
+# '',
+
+'invalid-soname',
+'''The soname of the library is neither of the form lib<libname>.so.<major> or
+lib<libname>-<major>.so.''',
+
+'invalid-ldconfig-symlink',
+'''The symbolic link references the wrong file. It should reference
+the shared library.''',
+
+'no-ldconfig-symlink',
+'''The package should not only include the shared library itself, but
+also the symbolic link which ldconfig would produce. (This is
+necessary, so that the link gets removed by rpm automatically when
+the package gets removed, even if for some reason ldconfig would not be
+run at package postinstall phase.)''',
+
+'shlib-with-non-pic-code',
+'''The listed shared libraries contain object code that was compiled
+without -fPIC. All object code in shared libraries should be
+recompiled separately from the static libraries with the -fPIC option.
+
+Another common mistake that causes this problem is linking with
+``gcc -Wl,-shared'' instead of ``gcc -shared''.''',
+
+'binary-or-shlib-defines-rpath',
+'''The binary or shared library defines `RPATH'. Usually this is a
+bad thing because it hardcodes the path to search libraries and so
+makes it difficult to move libraries around. Most likely you will find a
+Makefile with a line like: gcc test.o -o test -Wl,--rpath. Also, sometimes
+configure scripts provide a --disable-rpath flag to avoid this.''',
+
+'statically-linked-binary',
+'''The package installs a statically linked binary or object file.
+
+Usually this is a packaging bug. If not, contact your rpmlint distributor
+about this so that this error gets included in the exception file for rpmlint
+and will not be flagged as a packaging bug in the future (or add it to your
+local configuration if you installed rpmlint from the source tarball).''',
+
+'executable-in-library-package',
+'''The package mixes up libraries and executables. Mixing up these
+both types of files makes upgrades quite impossible.''',
+
+'non-versioned-file-in-library-package',
+'''The package contains files in non versioned directories. This makes it
+impossible to have multiple major versions of the libraries installed.
+One solution can be to change the directories which contain the files
+to subdirs of /usr/lib/<name>-<version> or /usr/share/<name>-<version>.
+Another solution can be to include a version number in the file names
+themselves.''',
+
+'incoherent-version-in-name',
+'''The package name should contain the major version of the library.''',
+
+'invalid-directory-reference',
+'This file contains a reference to /tmp or /home.',
+
+'no-binary',
+'''The package should be of the noarch architecture because it doesn't contain
+any binaries.''',
+
+# http://sources.redhat.com/ml/libc-alpha/2003-05/msg00034.html
+'undefined-non-weak-symbol',
+'''The binary contains undefined non-weak symbols. This may indicate improper
+linkage; check that the binary has been linked as expected.''',
+
+# http://www.redhat.com/archives/fedora-maintainers/2006-June/msg00176.html
+'unused-direct-shlib-dependency',
+'''The binary contains unused direct shared library dependencies. This may
+indicate gratuitously bloated linkage; check that the binary has been linked
+with the intended shared libraries only.''',
+
+'only-non-binary-in-usr-lib',
+'''There are only non binary files in /usr/lib so they should be in
+/usr/share.''',
+
+'binaryinfo-readelf-failed',
+'''Executing readelf on this file failed, all checks could not be run.''',
+
+'binaryinfo-tail-failed',
+'''Reading trailing bytes of this file failed, all checks could not be run.''',
+
+'ldd-failed',
+'''Executing ldd on this file failed, all checks could not be run.''',
+
+'executable-stack',
+'''The binary declares the stack as executable. Executable stack is usually an
+error as it is only needed if the code contains GCC trampolines or similar
+constructs which uses code on the stack. One common source for needlessly
+executable stack cases are object files built from assembler files which
+don\'t define a proper .note.GNU-stack section.''',
+
+'missing-PT_GNU_STACK-section',
+'''The binary lacks a PT_GNU_STACK section. This forces the dynamic linker to
+make the stack executable. Usual suspects include use of a non-GNU linker or
+an old GNU linker version.''',
+
+'shared-lib-calls-exit',
+'''This library package calls exit() or _exit(), probably in a non-fork()
+context. Doing so from a library is strongly discouraged - when a library
+function calls exit(), it prevents the calling program from handling the
+error, reporting it to the user, closing files properly, and cleaning up any
+state that the program has. It is preferred for the library to return an
+actual error code and let the calling program decide how to handle the
+situation.''',
+
+'ocaml-mixed-executable',
+'''Executables built with ocamlc -custom are deprecated. Packagers should ask
+upstream maintainers to build these executables without the -custom option. If
+this cannot be changed and the executable needs to be packaged in its current
+form, make sure that rpmbuild does not strip it during the build, and on setups
+that use prelink, make sure that prelink does not strip it either, usually by
+placing a blacklist file in /etc/prelink.conf.d. For more information, see
+http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=256900#49''',
+
+'non-position-independent-executable',
+'''This executable must be position independent. Check that it is built with
+-fPIE/-fpie in compiler flags and -pie in linker flags.''',
+)
+
+# BinariesCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..3912109
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..54fad30
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,4871 @@
+2011-12-04 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 1.4.
+
+2011-11-24 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Add Python 3.3 magic number.
+
+2011-11-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py, InitScriptCheck.py: Get rid of some unnecessary
+ regexps.
+ * BinariesCheck.py, DocFilesCheck.py, Pkg.py, SpecCheck.py,
+ TagsCheck.py: pylint warning cleanups.
+ * AbstractCheck.py: Fix indentation.
+
+2011-11-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Don't assume rpm changelog timestamps are always at
+ noon (#246, thanks to Paul Howarth).
+
+2011-11-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.bash-completion: Hush when trying to load
+ _rpm_installed_packages.
+
+2011-11-04 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile, rpmlint.bash-completion: Adjust bash completion for
+ bash-completion >= 1.90's dynamic loading.
+ * __isocodes__.py: Regenerate ISO codes list with iso-codes 3.29.
+
+2011-10-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Skip more checks/commands on files that cannot be
+ read.
+
+ https://bugzilla.redhat.com/show_bug.cgi?id=745446
+
+2011-09-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py, InitScriptCheck.py, MenuCheck.py, Pkg.py,
+ PostCheck.py: Prepare for rpm-python possibly returning script
+ progs as arrays.
+
+ http://rpm.org/ticket/847#comment:2
+ * PostCheck.py: Code cleanups.
+ * rpmlint.1: Force plain ASCII quotes in man page examples.
+
+2011-09-12 Michael Scherer <misc at mandriva.org>
+
+ * SpecCheck.py: refactor the noarch check into the previous commit
+ ( one less regexp )
+ * SpecCheck.py: merge patch from #97, using BuildArch with
+ something else than Noarch is likely a error and causing issues.
+
+2011-09-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * PostCheck.py: Warn about one line scriptlet commands only if the
+ interpreter is a shell.
+
+2011-08-17 Ville Skyttä <ville.skytta at iki.fi>
+
+ * PostCheck.py: Don't emit use-tmp-in-* for commented out /tmp in
+ scriptlets.
+
+2011-07-26 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmdiff: Comment spelling fix.
+ * rpmdiff: Teach rpmdiff about pretrans and posttrans.
+
+2011-07-20 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Drop some throwaway code for packages with <>
+ 1 init script.
+ * InitScriptCheck.py: Add special init script naming treatment for
+ for *-sysvinit subpackages.
+
+ https://bugzilla.redhat.com/723460
+
+2011-07-07 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 1.3.
+ * DocFilesCheck.py: Warn about INSTALL files included in docs.
+
+2011-06-18 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Config.py, Filter.py: Drop unnecessary global statements.
+ * Filter.py, rpmlint, rpmlint.1, rpmlint.bash-completion: Add
+ --rawout option (Ludwig Nussel).
+
+ Allows to specify a file that gets the unfiltered result. Useful
+ to not bother the developer with unimportant stuff but still
+ retain a way for distro maintainers to evaluate all results.
+ * INSTALL: Clarify that python 3 is not yet supported.
+
+2011-06-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py, config: Add UseVarLockSubsys option for
+ forbidding use of /var/lock/subsys (Ludwig Nussel).
+
+2011-05-31 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py, test/SpecCheck.spec, test/test.SpecCheck.py:
+ Recognize patches applied via some common pipe usages.
+ * PostCheck.py: Add info message for non-empty-*.
+
+2011-05-15 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py, config: Check for position independent
+ executables (based on patch by Ludwig Nussel).
+ * BinariesCheck.py: Do executable checks for PIEs too.
+
+2011-05-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Warn about non-ghost files in /var/run and
+ /var/lock (based on patch from Ludwig Nussel).
+
+2011-05-11 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Filter.py: Fix setting message type for reasons with badness
+ threshold defined (Ludwig Nussel).
+
+2011-04-24 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.bash-completion: Use "declare -F" instead of "type" to
+ check if a bash function exists.
+
+2011-04-22 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 1.2.
+ * rpmlint.bash-completion: Avoid completing on installed packages
+ for some more cases that look like paths.
+ * rpmlint.bash-completion: Add *.spm to completed file extensions.
+ * rpmlint.bash-completion: Add -I/--explain argument completion.
+ * rpmlint, rpmlint.1: Allow -I/--explain to be specified multiple
+ times.
+
+ Specifying comma separated message ids is deprecated but still
+ supported for now for backwards compatibility.
+ * rpmlint, rpmlint.1, rpmlint.bash-completion: Add --explain as
+ alias for -I.
+ * __isocodes__.py: Regenerate ISO codes list with iso-codes 3.25.
+ * SpecCheck.py: Downgrade files-attr-not-set to a warning, improve
+ its description.
+
+2011-04-14 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Run c++filt only once for all undefined
+ non-weak symbols in a file.
+ * BinariesCheck.py: Run undefined non-weak symbol names through
+ c++filt (Richard Shaw).
+
+ https://bugzilla.redhat.com/show_bug.cgi?id=696749
+
+2011-04-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Dereference symlinks for python bytecode mtime
+ check.
+
+ https://bugzilla.redhat.com/694090
+ * Pkg.py: Sort imports.
+ * Pkg.py: Add Pkg.readfile() for dereferencing PkgFile symlinks.
+ * rpmdiff: Update FSF address.
+
+2011-03-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Filter.py: Don't overwrite already existing details (Ludwig
+ Nussel).
+
+ This allows the distro config to provide alternative descriptions
+ and distro specific explanations without having to patch checks.
+
+2011-03-07 Ville Skyttä <ville.skytta at iki.fi>
+
+ * INSTALL, Pkg.py: Implement is_utf8() without iconv.
+ * test/test.Pkg.py: Remove unused import.
+
+2011-03-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Check outdated or misspelled FSF addresses in text
+ files.
+
+ https://bugzilla.redhat.com/637712
+
+2011-03-05 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py, config: Make man warning category configurable.
+ * config: Spelling fix.
+
+2011-02-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * AbstractCheck.py, FilesCheck.py, Makefile, Pkg.py, rpmdiff,
+ rpmlint, test.sh: Delete trailing whitespace.
+ * __isocodes__.py: Regenerate ISO codes list with isocodes 3.20.
+ * FilesCheck.py: Don't try to do python bytecode checks for
+ unreadable files (#201).
+
+2011-02-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Don't try to check Content-Length and Content-MD5
+ for invalid source URLs.
+
+2011-02-05 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Check for private shared object provides in both
+ lib and lib64 subdirs.
+
+ https://bugzilla.redhat.com/675360
+
+2011-01-25 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 1.1.
+ * FilesCheck.py: Don't crash if a file cannot be opened when trying
+ to peek into it.
+
+2011-01-24 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Update expected Python 3.2 bytecode magic value to
+ 3180.
+
+ https://bugzilla.redhat.com/672352
+ * TagsCheck.py: Improve no-description-tag info message.
+ * TagsCheck.py: Improve changelog related info messages.
+ * Pkg.py: Treat 'Unspecified' as a valid Group the same way as
+ 'Development/Debug'.
+
+ rpmbuild >= 4.6.0 adds it automatically if not specified in
+ specfiles.
+ * TagsCheck.py: Add future and overflow checks for changelog
+ timestamps.
+
+ http://lists.fedoraproject.org/pipermail/buildsys/2010-July/003174.html
+
+2011-01-14 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: stringToVersion cleanups, ignore dash before colon as
+ version/release separator.
+
+2010-12-15 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Don't emit spurious executable perm warnings for
+ docs with shebangs.
+ * FilesCheck.py: Don't read "head" of files for in each test that
+ needs to peek into them.
+
+ Do it only once instead. istextfile() is now called peek().
+
+2010-12-11 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Check source and patch file sizes and MD5 hashes
+ against Content-Length and Content-MD5 from their URLs.
+ * AbstractCheck.py: Return urllib info() from check_url() if
+ available.
+
+2010-12-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint: Be smarter when deciding whether arguments are rpm files
+ or installed packages.
+
+ Arguments that are existing files (not directories) but do not
+ contain slashes and do not end with .rpm, .spm or .spec are now
+ treated as installed packages.
+
+2010-12-07 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint: Make rpmlint executable again.
+ * README.devel, rpmlint, rpmlint.py, test.sh: Get rid of shell
+ script wrapper to make prefixed executables easier to set up.
+ * rpmlint.1: Improve --checkdir documentation.
+
+2010-12-02 Ville Skyttä <ville.skytta at iki.fi>
+
+ * ., Makefile: Use xz to compress tarball.
+
+2010-11-27 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: Fix traceback when stringifying epoch-only versions
+ (Roman Rakus).
+
+ https://bugzilla.redhat.com/657593
+
+2010-11-19 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Fix TypeError when outputting
+ incoherent-version-dependency-on messages (#192).
+ * Filter.py: Allow info messages even if badness scoring is used
+ (Ludwig Nussel).
+
+2010-11-04 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: Make file capabilities available in PkgFile (rpm >=
+ 4.7.0, Ludwig Nussel).
+
+2010-11-01 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 1.0.
+
+2010-10-25 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Add note about file capabilities to setuid-binary
+ info message.
+
+ https://bugzilla.redhat.com/646455
+
+2010-10-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Turn some comments into docstrings.
+ * FilesCheck.py: Add support for PEP 3147 dirs when finding Python
+ sources (David Malcolm).
+
+ https://bugzilla.redhat.com/637956
+
+2010-10-04 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.bash-completion: Offer only long rpmdiff completion
+ options.
+ * rpmlint.bash-completion: Add basic -c/--check completion.
+ * rpmlint.1: Document format of values for --check.
+
+ https://bugzilla.redhat.com/639823
+
+2010-08-19 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 0.99.
+ * FilesCheck.py: Drop duplicate -mandoc arg to groff.
+ * FilesCheck.py: Add Python 3.2 magic number handling.
+ * FilesCheck.py: Add Python 2.7 magic number
+ (https://bugzilla.redhat.com/623607, Nils Philippsen)
+
+2010-07-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.1, rpmlint.py: Choose built in config based on prefix
+ used when invoked (Tom Callaway).
+
+ https://bugzilla.redhat.com/show_bug.cgi?id=537430#c8
+
+2010-06-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * I18NCheck.py: Use rpm.RPMTAG_HEADERI18NTABLE instead of assuming
+ it's 100.
+ * Makefile: Release 0.98.
+
+2010-06-20 Ville Skyttä <ville.skytta at iki.fi>
+
+ * I18NCheck.py, Makefile, __isocodes__.py,
+ tools/generate-isocodes.py: Extend list of valid language country
+ codes from the iso-codes project.
+
+ http://alioth.debian.org/projects/pkg-isocodes/
+ https://bugzilla.redhat.com/show_bug.cgi?id=599516
+
+2010-06-05 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py, test/test.Pkg.py: Fix self-obsoletion bug with Provides
+ containing Epoch.
+ * Pkg.py: Don't stringify None Epoch to 'None' string in
+ compareEVR().
+ * Pkg.py: Sync rangeCompare() with yum 3.2.27.
+
+2010-06-04 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Reduce some spell check noise.
+ * rpmlint: Fix handling of arguments containing spaces.
+
+2010-06-03 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Revert accidental bit in previous commit.
+ * SpecCheck.py: Try to avoid messages about ./configure in
+ comments.
+
+ https://bugzilla.redhat.com/599427
+
+2010-06-02 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Simplify code, remove misleading comment.
+ * TagsCheck.py: Suppress spell check errors for words containing
+ digits.
+
+ This is a workaround for enchant's digit tokenizing behavior:
+ http://github.com/rfk/pyenchant/issues/issue/3
+
+2010-05-19 Ville Skyttä <ville.skytta at iki.fi>
+
+ * DocFilesCheck.py: Bypass doc file check earlier if package has no
+ doc files.
+
+ As a side effect, works around https://bugzilla.redhat.com/593553
+ with rpm 4.8.0 and gpg-pubkeys.
+
+2010-05-18 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 0.97.
+ * FilesCheck.py: Do man page warning check only in UTF-8 mode for
+ now.
+ * SpecCheck.py, test/SpecCheck.spec: Avoid some false
+ macro-in-comment positives.
+
+2010-05-17 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Add manual page warning check,
+ https://bugzilla.redhat.com/589432
+ * Pkg.py: Split "cat" command into catcmd().
+
+2010-04-27 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Output no-ldconfig-symlink only for files
+ starting with "lib" or "ld-".
+
+ ldconfig (as of glibc 2.11.90) itself handles only files starting
+ with these prefixes. https://bugzilla.redhat.com/459452
+
+2010-04-26 Ville Skyttä <ville.skytta at iki.fi>
+
+ * test/test.PamCheck.py: Clean up after ourselves.
+
+2010-04-21 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 0.96.
+
+2010-04-20 Ville Skyttä <ville.skytta at iki.fi>
+
+ * ConfigCheck.py: Get rid of app-defaults-must-not-be-conffile
+ check, it's covered by non-etc-or-var-file-marked-as-conffile.
+ * BinariesCheck.py, ConfigCheck.py: Replace some trivial regexps
+ with string matches.
+ * ConfigCheck.py: Get rid of file-in-usr-marked-as-conffile check,
+ it's covered by non-etc-or-var-file-marked-as-conffile.
+
+2010-04-16 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Warn about shared object provides in private dirs,
+ https://bugzilla.redhat.com/487974
+ * Pkg.py: Record file provides.
+ * Pkg.py: Fix check_versioned_dep().
+ * test/test.DocFilesCheck.py: Remove obsolete test case.
+ * DocFilesCheck.py, Pkg.py: Rename pkgfile.deps to .requires, parse
+ it to (name, flags, (e, v, r)).
+ * Pkg.py, SpecCheck.py, test/SpecCheck.spec, test/test.Pkg.py,
+ test/test.SpecCheck.py: Move dep token parsing to Pkg, add some
+ tests.
+
+2010-04-15 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Drop no longer used epoch_regex.
+ * Pkg.py, TagsCheck.py: Store dependencies, provides etc as (name,
+ flags, (epoch, version, release)) internally.
+ * TagsCheck.py: More improvements to dependency formatting in
+ messages.
+ * TagsCheck.py: Improve no-epoch-in-* message formatting.
+ * TagsCheck.py: Check for unexpanded macros in
+ requires/provides/obsoletes/conflicts names in addition to
+ versions.
+
+2010-04-01 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.py: Drop dead code.
+ * rpmlint.py: Do not use unnecessary getopt backwards compatibility
+ things.
+
+2010-03-31 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Simplify some regexps.
+ * SpecCheck.py: Make ifarch etc regexps stricter.
+ * SpecCheck.py: Anchor BuildRoot regex at start of line (Mads
+ Kiilerich).
+ * SpecCheck.py: Don't check for buildroot usage in comments
+ (http://bugzilla.redhat.com/578390)
+
+2010-03-30 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py, InitScriptCheck.py, MenuCheck.py, PostCheck.py,
+ SourceCheck.py, SpecCheck.py, TagsCheck.py: Code cleanups, avoid
+ deeply nested blocks etc.
+
+2010-03-19 Ville Skyttä <ville.skytta at iki.fi>
+
+ * AbstractCheck.py, test/test.PamCheck.py: Clean up unused
+ variables and imports.
+ * BinariesCheck.py: Code cleanups, avoid deeply nested blocks etc.
+ * FilesCheck.py: Simplify code.
+ * FilesCheck.py: Move setuid/setgid bit check to same block with
+ rest of normal file checks.
+ * FilesCheck.py: Fix bad reuse of "pkgfile" var.
+ * Pkg.py: Don't bother checking match iterator boolean values.
+
+ Fixes installed package globbing with rpm 4.8:
+ http://rpm.org/ticket/153 No need to raise KeyError for no
+ matches at this level either.
+ * FilesCheck.py: Check that executables in standard binary dirs
+ have man pages (https://bugzilla.redhat.com/572090, based on
+ patch by Radek Novacek).
+ * FilesCheck.py: Move text file checks to the block of other normal
+ file checks.
+
+2010-03-18 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Check for same named executables in standard bin
+ dirs (https://bugzilla.redhat.com/572097, based on patch from
+ Radek Novacek).
+ * FilesCheck.py: Treat /usr/games as a "bin" dir.
+
+2010-03-15 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Add dictionary install tip in
+ enchant-dictionary-not-found info message.
+
+2010-03-14 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Report all occurrences of egrep/fgrep on a line.
+ * SpecCheck.py: Check for uses of deprecated egrep and fgrep
+ (https://bugzilla.redhat.com/571386).
+ * SpecCheck.py: Recognize scriptlet section markers.
+
+2010-03-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py, test/SpecCheck.spec, test/test.SpecCheck.py: Test
+ for macros in shell comments (#162,
+ https://bugzilla.redhat.com/571375)
+
+2010-03-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Fix non-coherent-filename for source packages
+ (regression in r1729).
+
+2010-03-03 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 0.95.
+ * AbstractCheck.py: Skip ghost files in files checks,
+ https://bugzilla.redhat.com/570086
+
+2010-02-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Filter.py: Work around Python's ASCII non-TTY stdout and our
+ non-ASCII output.
+ * TagsCheck.py: Omit spell check warnings for capitalized words
+ that do not start a sentence.
+
+ https://bugzilla.redhat.com/567285
+
+2010-02-21 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py, Config.py, DistributionCheck.py, FilesCheck.py,
+ I18NCheck.py, InitScriptCheck.py, MenuCheck.py, MenuXDGCheck.py,
+ NamingPolicyCheck.py, PamCheck.py, Pkg.py, PostCheck.py,
+ RpmFileCheck.py, SignatureCheck.py, SpecCheck.py, TagsCheck.py,
+ ZipCheck.py: Wrap bunch of long lines, tune comments and info
+ messages.
+ * INSTALL: Require rpm-python >= 4.4.2.2 for expandMacro() in
+ r1729.
+
+2010-02-09 Michael Scherer <misc at mandriva.org>
+
+ * AbstractCheck.py: do not execute check if the network test are
+ marked as disabled, and simplify the logic
+ * TagsCheck.py: - do not expand the format of the filename format,
+ (patch from Per Oyvind Karlsten )
+
+2010-01-31 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 0.94.
+ * SpecCheck.py: Output the latter occurrence of spaces/tabs as
+ mixed-use-of-spaces-and-tabs' context line number.
+ * TagsCheck.py: Sync opensource.org list of licenses with upstream
+ (fixes #58).
+ * SpecCheck.py, rpmlint.1, rpmlint.py: Add ability to read spec
+ file content from stdin (#63).
+ * SpecCheck.py: Do not try to pass non-file spec content to rpm for
+ parsing.
+ * TagsCheck.py: Fix unexpanded macros check with non-string tag
+ values.
+
+ https://admin.fedoraproject.org/updates/F12/FEDORA-2010-1105
+
+2010-01-27 Ville Skyttä <ville.skytta at iki.fi>
+
+ * config: Improve accuracy of config value type documentation.
+ * FilesCheck.py: LSB user/group status comment update.
+
+2010-01-25 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Fix iterating spec sources with rpm >= 4.8.0.
+
+ http://rpm.org/wiki/Releases/4.8.0#Specclass
+ * Makefile: Release 0.93.
+ * Pkg.py: Treat all failures opening the (installed) 'rpm' package
+ for finding default groups as non-fatal.
+ * TagsCheck.py: Load default valid groups only if ValidGroups is
+ not set (or is None).
+ * README, config, rpmlint.1: Move list of available config options
+ to "config", improve documentation.
+
+2010-01-21 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Ignore filenames contained in package when spell
+ checking.
+ * AbstractCheck.py: Use our own User-Agent in urllib2 requests.
+ * Makefile: Fix install target when no *.pyc are generated.
+ * ., Config.py, Makefile, rpmlint.py: Move version number to
+ __version__.py, make it available as Config.__version__.
+ * TagsCheck.py: Include Enchant suggestions in spelling-error
+ output.
+ * AbstractCheck.py: Try harder to get something to print about
+ inaccessible URLs.
+
+2010-01-20 Ville Skyttä <ville.skytta at iki.fi>
+
+ * MenuXDGCheck.py: Include errors from desktop-file-validate in
+ output (#85).
+
+ Based on patch from dmueller at suse.de.
+ * DistributionCheck.py, README, SourceCheck.py, config, rpmlint.1:
+ Add arbitrary manpage/infopage/source compression support (#142).
+
+ Based on patch from pkarlsen at mandriva.com.
+
+ The UseBzip2, UseLzma, and UseXz config options have been
+ replaced by the new CompressExtension option (which takes a
+ filename extension without the leading dot), the
+ *page-not-compressed-with-* message ids have been replaced by
+ *page-not-compressed, and the source-or-patch-not-*zipped message
+ ids have been replaced by source-or-patch-not-compressed.
+ * AbstractCheck.py: Do HTTP redirects with HEAD too, urllib2
+ appears to reset them to GET.
+ * SpecCheck.py, TagsCheck.py: Python 2.4 compatibility fixes.
+ * AbstractCheck.py, SpecCheck.py, TagsCheck.py, ZipCheck.py: Avoid
+ embedding anything other than reason in reason id for -i to work.
+ * SpecCheck.py: Demand only tarball sources to be URLs (#170).
+ * SpecCheck.py: Check that SourceX are URLs (#170).
+ * AbstractCheck.py, README, SpecCheck.py, TagsCheck.py: Check that
+ *URL, SourceX and PatchX are not broken links (#165).
+
+ The new config option NetworkEnabled needs to be True for this
+ check to happen (default is False). See also the new
+ NetworkTimeout option.
+ * TagsCheck.py: Require at least one dot in URL net locations.
+ * AbstractCheck.py, rpmlint.py: Pass verbosity flag to check
+ classes.
+
+2010-01-19 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py, README: Add svg to default SkipDocsRegexp.
+ * FilesCheck.py, TagsCheck.py: Check for unexpanded macros in many
+ more tags.
+
+ Various misspelled-macro, percent-in-*, and macro-in-* messages
+ are now reported as unexpanded-macro, and their formatting has
+ been improved.
+ * AbstractCheck.py, FilesCheck.py, SpecCheck.py: Report all
+ unexpanded macros in files and changelogs, not just first of
+ line.
+ * TagsCheck.py: Rename spelling-error-in-* to spelling-error,
+ improve formatting.
+ * TagsCheck.py: Check DistURL and BugURL validity.
+ * TagsCheck.py: Check URLs using urlparse, remove Mandrake
+ reference from info message.
+
+2010-01-16 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Recognize BugURL tag.
+
+2010-01-09 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Do not suppress bunch of filename checks for files
+ in "bad" dirs.
+ * FilesCheck.py: Make backup file check look for patch backup
+ files.
+
+2009-12-02 Michael Scherer <misc at mandriva.org>
+
+ * rpmlint.py: - make -I work again when using addCheckDir, spotted
+ by incubusss
+
+2009-11-25 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: Fix default groups lookup when the rpm package is not
+ installed.
+
+2009-11-05 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py, README, config: Add check for version magic in
+ byte-compiled Python files (David Malcolm, modified by yours
+ truly).
+ https://www.zarb.org/pipermail/rpmlint-discuss/2009-November/000787.html
+ * Filter.py, Pkg.py, SignatureCheck.py, rpmdiff, rpmlint.py: Use
+ alternative Python 2/3 'print' compatibility hack; drop rlprint,
+ rename rlwarn to warn.
+ * FilesCheck.py: Drop unused safe_normpath import.
+ * SignatureCheck.py: Fix signature check with rpm versions that
+ output e.g. '(MD5) ' at start of unknown key name.
+ * SignatureCheck.py: Include more info in 'Error checking
+ signature' output.
+
+2009-11-02 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: 0.92.
+ * Makefile: Add --reparagraph to svn2cl options.
+ * Makefile, tools/compile.py: Add option to generate *.pyc too, use
+ py_compile directly instead of a script of our own, remove
+ "print" check from the all target.
+ * TagsCheck.py: Warn only once per misspelled word per tag.
+ * TagsCheck.py: Avoid unnecessary try-except in BAD_WORDS lookup.
+
+2009-10-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Trivial code cleanup, works around pychecker bug.
+ * BinariesCheck.py, DistributionCheck.py, FilesCheck.py,
+ InitScriptCheck.py, MenuCheck.py, PostCheck.py, SpecCheck.py:
+ Replace bunch of trivial regexps with simple string searches.
+ * FilesCheck.py: Output differing python mtimes as formatted
+ datetimes.
+ * TagsCheck.py: Cache and reuse instantiated enchant checkers.
+ * TagsCheck.py: Don't flag package name 'components' as spelling
+ errors with enchant.
+
+2009-10-27 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Make python regex match /usr/lib64 too.
+ * FilesCheck.py: Add check for python byte compiled/source mtime
+ checks (David Malcolm).
+ https://www.zarb.org/pipermail/rpmlint-discuss/2009-October/000775.html
+ * Pkg.py: Avoid import loop introduced in previous change.
+ * Filter.py, Pkg.py, SignatureCheck.py, rpmdiff, rpmlint.py:
+ Introduce Pkg.rlprint and Pkg.rlwarn print-like functions, use
+ them instead of direct writes to sys.std{out,err} (#181).
+
+2009-10-18 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmdiff: Python 3 compatibility tweaks.
+ * rpmdiff: Output usage errors to stderr.
+
+2009-10-16 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Filter.py: Sort diagnostics using key instead of cmp.
+
+2009-10-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmdiff: Don't output an empty line if no differences are found,
+ https://bugzilla.redhat.com/528535
+ * README, TagsCheck.py: Add UseEnchant config option.
+
+2009-10-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmdiff: Fix all Provides/Obsoletes/Conflicts being marked as
+ Requires.
+
+2009-10-02 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.bash-completion: Quote one more $cur for "compgen -W" to
+ avoid globbing.
+ * rpmlint.bash-completion: Reindent.
+ * rpmlint.bash-completion: Quote $cur for "compgen -W" to avoid
+ globbing.
+ * rpmlint.1, rpmlint.bash-completion, rpmlint.py: Add -o/--option
+ option for overriding config options.
+ * rpmlint.bash-completion: Don't offer short option completions
+ where long ones exist.
+ * rpmlint.bash-completion: Add (non)completion of -I and -c/--check
+ arguments.
+ * rpmlint.bash-completion: Cleanups.
+ * rpmlint.bash-completion: Remove no longer existing -p/--profile
+ option completion.
+ * rpmlint.py: Option processing and documentation
+ cleanups/improvements.
+
+2009-10-01 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.py: Clean up help message printing.
+
+2009-09-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Yell only once per missing enchant dictionary.
+ * INSTALL: Note enchant dependency.
+ * Pkg.py, TagsCheck.py: Add summary and description spell checking
+ using enchant (#166, most of the work by Debarshi Ray).
+
+2009-09-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: 0.91
+
+2009-09-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * README, rpmlint.1, rpmlint.py: Honor $XDG_CONFIG_HOME when
+ finding default user config file.
+
+2009-09-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py, Config.py, PostCheck.py, SpecCheck.py,
+ TagsCheck.py: Code cleanups.
+
+2009-09-02 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Report character index where nbsp was found.
+ * Pkg.py: Don't treat '[' without a following ']' and something in
+ between as a glob in getInstalledPkgs().
+
+2009-08-31 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.py: Process installed packages matched by a wildcard in
+ locale's alphabetic order.
+
+2009-08-30 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: Try to identify and bypass magic info originating from
+ inside compressed files for now,
+ https://bugzilla.redhat.com/519694
+
+2009-08-20 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Include test suite files in dist tarballs.
+ * Makefile: Rename test target to check per GNU conventions.
+ * test.sh: Really make it executable.
+ * test.sh: Make executable, trivial cleanups.
+
+2009-08-19 Ville Skyttä <ville.skytta at iki.fi>
+
+ * INSTALL: Note gzip, bzip2 and xz dependencies.
+ * Pkg.py: Decompress lzma files with xz in is_utf8.
+ * README.devel: Remove flepied's email address, no need to bother
+ him with rpmlint development nowadays.
+ * README.devel: Note that the run examples apply to unpacked
+ tarballs too.
+
+2009-08-17 Ville Skyttä <ville.skytta at iki.fi>
+
+ * README, TagsCheck.py: Make max line length configurable (config
+ option MaxLineLength, default still 79).
+
+2009-08-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.bash-completion: Don't rely on bash_completion setting
+ $filenames, just use -o filenames instead.
+
+2009-08-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmdiff: Print friendlier error message than a traceback when
+ loading a package fails, https://bugzilla.redhat.com/516492
+
+2009-08-03 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Downgrade no-buildroot-tag and
+ no-cleaning-of-buildroot to warnings; they are not needed in some
+ rpm configurations/versions, https://bugzilla.redhat.com/515185
+ * SpecCheck.py: Improve rpm-buildroot-usage info message.
+
+2009-07-28 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.bash-completion: Simplify bash completion code a bit.
+
+2009-07-26 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py, TagsCheck.py: Match "foo%_isa" when checking base package
+ dependencies on foo, https://bugzilla.redhat.com/513811
+
+2009-07-22 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.py: Do -a/--all though globbing.
+ * rpmlint.py: Cleanups, NFC.
+
+2009-07-21 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Fix crash if subsys name in an init script
+ ends up being empty.
+ * AbstractCheck.py, FilesCheck.py, SpecCheck.py, TagsCheck.py:
+ Output complete found macro candidate in misspelled macro related
+ messages.
+ * TagsCheck.py: Check for unexpanded macros in summary,
+ description, group, buildhost, and license.
+ * FilesCheck.py: Further improve misspelled-macro info message.
+ * AbstractCheck.py, FilesCheck.py, SpecCheck.py: Move regex that
+ matches macros to AbstractCheck, use it, improve misspelled-macro
+ info message.
+ * FilesCheck.py: s/mispell/misspell/ :þ
+
+2009-07-15 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.1: Rephrase CAVEATS more.
+ * rpmlint.1: Rephrase CAVEATS a bit.
+
+2009-06-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: 0.90.
+ * rpmdiff: Add missing magic coding comment
+ (https://bugzilla.redhat.com/508683, Michal Nowak).
+
+2009-06-21 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: 0.89
+ * TagsCheck.py: Fix incorrect info in self-obsoletion message.
+ * FilesCheck.py: Output interpreter in non-executable-script
+ warning, remove quotes from wrong-script-interpreter one.
+ * Pkg.py: Remove stale comments.
+ * TagsCheck.py: Code cleanup, NFC.
+ * TagsCheck.py: improve percent-in-* message details.
+ * TagsCheck.py: Remove obsolete-on-name check (superseded by
+ self-obsoletion).
+ * TagsCheck.py: Add check for self-obsoletion cases,
+ https://bugzilla.redhat.com/461610
+ * Pkg.py: Borrow various utilities from yum's rpmUtils.miscutils.
+ * Makefile, rpmUtils: Revert previous commit.
+ * Makefile, rpmUtils, rpmUtils/miscutils.py,
+ rpmUtils/transaction.py: Borrow
+ rpmUtils/{miscutils,transaction}.py from yum.
+ * rpmdiff: Improve dependency formatting.
+ * rpmdiff: Take Epoch into account in self-provides filtering.
+ * Pkg.py: Improve legacy prereq tracking with new rpm versions.
+
+2009-06-18 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmdiff: Add enough space padding to output.
+
+2009-06-17 Ville Skyttä <ville.skytta at iki.fi>
+
+ * DistributionCheck.py, FilesCheck.py, Pkg.py, README: Add xz
+ support, improve *page-not-compressed-with-* info messages.
+ * BinariesCheck.py: Avoid a couple of unnecesary regex matches.
+
+2009-06-16 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py, Config.py, DistributionCheck.py, FilesCheck.py,
+ Filter.py, InitScriptCheck.py, MenuCheck.py, MenuXDGCheck.py,
+ Pkg.py, PostCheck.py, README, SourceCheck.py, SpecCheck.py,
+ TagsCheck.py, ZipCheck.py, rpmdiff, rpmlint.py: Code cleanups.
+
+2009-06-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * README, config, rpmlint.1, rpmlint.py: Move default user config
+ file to ~/.config/rpmlint per freedesktop.org base dir spec.
+ * config: Comment spelling fix.
+
+2009-05-31 Ville Skyttä <ville.skytta at iki.fi>
+
+ * DistributionCheck.py:
+ s/compressed-wth-lzma/compressed-with-lzma/, thanks to Mark Hall.
+
+2009-05-30 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmdiff: Improve usage message.
+ * rpmdiff: Always append /usr/share/rpmlint to load path.
+ * rpmlint.bash-completion: Add rpmdiff bash completion.
+ * rpmdiff: Sync with koji version by Florian Festi and Mike Bonnet
+ (https://fedorahosted.org/koji/log/hub/rpmdiff)
+
+2009-05-20 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Check for comparison operators in dependency tokens
+ (#174).
+
+2009-04-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Hack around a subsys parsing deficiency,
+ https://bugzilla.redhat.com/498107
+ * InitScriptCheck.py: Print expected init script names in
+ incoherent-init-script-name output.
+
+2009-04-21 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Don't output missing-PT_GNU_STACK-section
+ messages if readelf failed.
+
+2009-04-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Add check for debuginfo packages without sources.
+
+2009-03-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Allow more common files to be non-readable
+ (https://bugzilla.redhat.com/226412).
+ * FilesCheck.py: Revert relative symlink target normalization back
+ to more aggressive.
+ * BinariesCheck.py, Config.py, ConfigCheck.py, FHSCheck.py,
+ FilesCheck.py, I18NCheck.py, MenuCheck.py, Pkg.py, PostCheck.py,
+ TagsCheck.py, rpmdiff: Code cleanups.
+ * FilesCheck.py: Do not check existence of relative link targets on
+ the filesystem for consistency with absolute link target checks,
+ improve dangling-*symlink info messages.
+ * FilesCheck.py, Pkg.py: Be a bit stricter when normalizing link
+ targets, do it earlier.
+ * FilesCheck.py, Pkg.py: Improve link target normalization in
+ symlink checks.
+
+2009-03-19 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: 0.87
+ * rpmlint.py: Avoid importing SpecCheck (and TagsCheck, FilesCheck
+ etc by recursion) too early, causing config settings for them
+ being ignored.
+ * rpmlint.bash-completion: Speed up path based completion.
+ * Makefile: 0.86
+ * Makefile: Make "verify" target more useful.
+ * SpecCheck.py: More spaces vs tabs improvements
+ (https://bugzilla.redhat.com/488146#c6, Mattias Ellert)
+
+2009-03-17 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: Don't treat %verifyscript dependencies as 'pre' ones.
+ * Pkg.py: Drop obsolete backwards compatibility cruft.
+ * Pkg.py, PostCheck.py: Add %verifyscript support.
+
+2009-03-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Improve buildconfig_rpath_regex a bit more.
+ * FilesCheck.py: Try to catch more rpaths in *.pc and *-config.
+ * FilesCheck.py: Add check for rpaths in *.pc and *-config files
+ (https://bugzilla.redhat.com/334414, based on patch from Milos
+ Jakubicek)
+
+2009-03-05 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Improve mixed tabs/spaces indentation check
+ (Mattias Ellert, https://bugzilla.redhat.com/488146).
+
+2009-02-28 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Work around spurious "No such file or dir" errors
+ from rpm when querying specfiles,
+ https://bugzilla.redhat.com/487855
+ * BinariesCheck.py, INSTALL, Pkg.py: Use file magic info from rpm
+ header and amend it with python-magic, include magic info in
+ PkgFile.
+ * Pkg.py: Fix pkgfile.path for files in uninstalled (extracted)
+ binary packages.
+
+2009-02-25 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Fix false positives caused by noarch subpackages,
+ include (sub)package name in libdir-macro-in-noarch-package
+ message (#175).
+ * test/SpecCheck.spec, test/noarch-sub.spec,
+ test/test.SpecCheck.py: Include noarch subpackage test in
+ SpecCheck test.
+ * Pkg.py: Ensure current_linenum is always defined in fake pkgs.
+
+2009-02-19 Ville Skyttä <ville.skytta at iki.fi>
+
+ * test/noarch-sub.spec: Test specfile for noarch subpackages.
+
+2009-02-15 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Dict iteration improvement.
+ * FilesCheck.py: Strip quotes from scriptlets before examining them
+ (#169).
+
+2009-02-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.py: Walk given dirs recursively.
+ * Pkg.py: Improve temp dir creation and temp file/dir naming.
+ * rpmlint.py: Run package cleanup handlers only once.
+
+2009-02-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * DocFilesCheck.py, INSTALL, Pkg.py, PostCheck.py, rpmlint.py: Drop
+ support for rpm < 4.4.
+
+2009-02-04 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Fix undefined variable.
+ * BinariesCheck.py: Make only-non-binary-in-usr-lib false positives
+ less likely, https://bugzilla.redhat.com/483199
+ * BinariesCheck.py: Downgrade only-non-binary-in-usr-lib to a
+ warning, https://bugzilla.redhat.com/483199
+
+2009-02-01 Ville Skyttä <ville.skytta at iki.fi>
+
+ * AbstractCheck.py, BinariesCheck.py, MenuCheck.py: Cleanups.
+ * BinariesCheck.py, DocFilesCheck.py, FilesCheck.py, I18NCheck.py,
+ InitScriptCheck.py, MenuCheck.py, Pkg.py, SourceCheck.py,
+ SpecCheck.py, ZipCheck.py, rpmdiff: Represent files inside
+ packages as PkgFile objects.
+ * rpmdiff: Make rpmdiff usable on installed packages.
+ * Pkg.py: Always raise KeyError (instead of sometimes
+ StopIteration) from InstalledPkg if not found by name.
+ * DocFilesCheck.py, test/test.DocFilesCheck.py: Move testing code
+ out of DocFilesCheck.py.
+ * BinariesCheck.py, DocFilesCheck.py, Pkg.py: Move file
+ dependencies to central files dict in Pkg.
+ * BinariesCheck.py, MenuCheck.py, MenuXDGCheck.py, Pkg.py,
+ rpmlint.py: Unused variable, import cleanup.
+ * FilesCheck.py: Don't reuse variable 'link' in hardlink check,
+ breaks in symlink check.
+ * BinariesCheck.py, FilesCheck.py, Pkg.py: Add cross directory hard
+ link check (#171, Lubomir Rintel)
+ * Pkg.py: Comment/TODO update.
+
+2009-01-30 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Python 2.4 compat fix for no os.SEEK_END (#172,
+ Lubomir Rintel)
+ * Filter.py: Move Config.info check outside of printDescriptions
+ (#171, lkundrak at v3.sk)
+ * BinariesCheck.py, ConfigCheck.py, DistributionCheck.py,
+ DocFilesCheck.py, FHSCheck.py, FilesCheck.py, Filter.py,
+ I18NCheck.py, InitScriptCheck.py, LSBCheck.py, MenuCheck.py,
+ MenuXDGCheck.py, NamingPolicyCheck.py, PamCheck.py, PostCheck.py,
+ RpmFileCheck.py, SignatureCheck.py, SourceCheck.py, SpecCheck.py,
+ TagsCheck.py, ZipCheck.py: Make sure details are available for -i
+ also for early loaded check modules.
+ * README, rpmlint.1: Add note about best check coverage.
+ * SpecCheck.py: Query specfiles using rpm to find syntax and other
+ errors, https://bugzilla.redhat.com/483196
+
+2009-01-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: Docstring update.
+ * I18NCheck.py, TagsCheck.py: Use generators where appropriate.
+ * SpecCheck.py: Compile section regexps only once.
+
+2009-01-28 Ville Skyttä <ville.skytta at iki.fi>
+
+ * INSTALL: Fix indentation.
+
+2009-01-28 Michael Scherer <misc at mandriva.org>
+
+ * INSTALL: - update the requirement in INSTALL
+ * Pkg.py: - fix python 2.6 deprecation warning
+
+2009-01-27 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Cleanup fallout fixes.
+ * BinariesCheck.py, ConfigCheck.py, DistributionCheck.py,
+ DocFilesCheck.py, FHSCheck.py, FilesCheck.py, I18NCheck.py,
+ InitScriptCheck.py, LSBCheck.py, MenuCheck.py, MenuXDGCheck.py,
+ NamingPolicyCheck.py, PamCheck.py, PostCheck.py, RpmFileCheck.py,
+ SignatureCheck.py, SourceCheck.py, SpecCheck.py, TagsCheck.py,
+ ZipCheck.py, rpmlint.py: Import fixes.
+ * InitScriptCheck.py: Downgrade missing-mandatory-lsb-keyword error
+ to missing-lsb-keyword warning, add some recommended keywords
+ (#71).
+ * AbstractCheck.py, DistributionCheck.py, FHSCheck.py,
+ FilesCheck.py, InitScriptCheck.py, MenuCheck.py,
+ NamingPolicyCheck.py, Pkg.py, PostCheck.py, SourceCheck.py,
+ SpecCheck.py, TagsCheck.py, ZipCheck.py, rpmdiff: Dict access
+ cleanups.
+ * BinariesCheck.py, I18NCheck.py, Pkg.py: Combine file langs into
+ regular Pkg files list.
+
+2009-01-26 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Treat *-headers as devel packages.
+ * BinariesCheck.py, Config.py, DocFilesCheck.py, FHSCheck.py,
+ FilesCheck.py, I18NCheck.py, MenuCheck.py, NamingPolicyCheck.py,
+ Pkg.py, PostCheck.py, TagsCheck.py, rpmdiff: PEP 8, pylint,
+ pychecker cleanups.
+ * AbstractCheck.py, BinariesCheck.py, Config.py, ConfigCheck.py,
+ DistributionCheck.py, DocFilesCheck.py, FHSCheck.py,
+ FilesCheck.py, Filter.py, I18NCheck.py, InitScriptCheck.py,
+ LSBCheck.py, MenuCheck.py, MenuXDGCheck.py, NamingPolicyCheck.py,
+ PamCheck.py, Pkg.py, PostCheck.py, RpmFileCheck.py,
+ SignatureCheck.py, SourceCheck.py, SpecCheck.py, TagsCheck.py,
+ ZipCheck.py, rpmlint.py, tools/Testing.py, tools/compile.py:
+ Import cleanup (PEP 8).
+ * Pkg.py, rpmlint.1: Allow PACKAGE argument to be a glob(7)
+ pattern.
+ * rpmlint.1: Document FILE and PACKAGE arguments.
+ * rpmlint.py: Do not run SpecCheck on *.spec if the check is not
+ enabled.
+ * Config.py: Strip .py[co]? from check names to add.
+ * AbstractCheck.py, Config.py, rpmlint.py: Do not reset list of
+ default checks to run if Config.addCheck() is used in config
+ files.
+ * rpmlint.1: Mention that -c may be given multiple times.
+ * rpmlint.py: Improve usage message.
+
+2009-01-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.py: Fix exit status (to 1) when no arguments given.
+ * FilesCheck.py, Filter.py, I18NCheck.py, PostCheck.py, rpmlint.py:
+ pychecker fixes.
+ * AbstractCheck.py, BinariesCheck.py, Config.py, ConfigCheck.py,
+ DistributionCheck.py, DocFilesCheck.py, FHSCheck.py,
+ FilesCheck.py, Filter.py, I18NCheck.py, InitScriptCheck.py,
+ LSBCheck.py, MenuCheck.py, MenuXDGCheck.py, NamingPolicyCheck.py,
+ PamCheck.py, Pkg.py, PostCheck.py, RpmFileCheck.py,
+ SignatureCheck.py, SourceCheck.py, SpecCheck.py, TagsCheck.py,
+ ZipCheck.py, rpmlint.py, test/test.PamCheck.py,
+ test/test.SpecCheck.py, tools/Testing.py, tools/compile.py: Add
+ utf-8 magic comments.
+ * InitScriptCheck.py: Use os.path.basename instead of a regexp.
+ * FilesCheck.py, MenuCheck.py, TagsCheck.py: Drop unused regexps.
+ * FilesCheck.py, SpecCheck.py: *_regex naming consistency fixes.
+ * FilesCheck.py: Recognize bzr internal files.
+ * TagsCheck.py: Add Enhances, Recommends, Suggests, Supplements to
+ tag_regex.
+ * TagsCheck.py: Use os.path.basename instead of
+ filename.split("/").
+ * TagsCheck.py: Sync license list with OSI.
+ * RpmFileCheck.py, ZipCheck.py: Fix spelling of my surname.
+
+2009-01-21 Ville Skyttä <ville.skytta at iki.fi>
+
+ * ZipCheck.py: Use pkg.files() instead of pkg.getFilesInfo(),
+ improve error message.
+ * Pkg.py, SpecCheck.py, TagsCheck.py, Util.py: Move default rpm
+ groups loading to Pkg, call it only once.
+ * rpmlint.1, rpmlint.py: Do not load/initialize checks more than
+ once.
+ * Pkg.py: Simplify file_regex.
+
+2009-01-20 Ville Skyttä <ville.skytta at iki.fi>
+
+ * README.devel: s/nbsp/ /
+ * README.devel, rpmlint.1, rpmlint.py: Remove the -p option, use
+ something like "python -O -u -m cProfile -s cumulative rpmlint.py
+ ..." for profiling instead.
+ * BinariesCheck.py: Speed up reading trailing bytes from binaries.
+ * rpmlint.py: Fix -a with rpm 4.6
+ (https://bugzilla.redhat.com/480664, Panu Matilainen).
+
+2009-01-14 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.bash-completion: Complete on installed packages, dirs and
+ *.rpm, *.spec only.
+ * rpmlint.bash-completion: Add *Emacs, vim and friends mode and
+ indentation settings.
+ * SpecCheck.py: Avoid duplicate non-standard-group warnings when
+ checking source packages (#167).
+ * SpecCheck.py: Match Group: case insensitively.
+ * Filter.py, Makefile, Testing.py, compile.py, test.sh, tools,
+ tools/Testing.py, tools/compile.py: Move compile.py and
+ Testing.py to tools/, do not require installation of Testing.py.
+ * SpecCheck.py, test/SpecCheck.spec, test/test.SpecCheck.py:
+ Improve applied patch detection (#59, dmueller at suse.de), add
+ test case.
+ * Testing.py: Add getTestedSpecPackage().
+ * test.sh: Fix exit code on success.
+
+2009-01-12 Michael Scherer <misc at mandriva.org>
+
+ * SpecCheck.py, TagsCheck.py, Util.py: add patch from akurtakov, to
+ check the Group tag in spec files, close ticket #167
+
+2009-01-03 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Mention symlinks(8) in symlink-should-be-relative
+ info message.
+
+2008-11-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Add name-repeated-in-summary check.
+
+2008-11-09 Ville Skyttä <ville.skytta at iki.fi>
+
+ * AbstractCheck.py, DistributionCheck.py, FilesCheck.py,
+ InitScriptCheck.py, MenuCheck.py, MenuXDGCheck.py,
+ NamingPolicyCheck.py, PamCheck.py, SpecCheck.py, TagsCheck.py,
+ rpmlint.py: Trim trailing whitespace.
+
+2008-10-30 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py, FilesCheck.py, Filter.py, MenuCheck.py: pylint
+ whitespace cleanups.
+ * BinariesCheck.py: Include filename in binaryinfo-readelf-failed
+ and binaryinfo-tail-failed messages.
+
+2008-10-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py, Config.py, ConfigCheck.py,
+ DistributionCheck.py, DocFilesCheck.py, FHSCheck.py,
+ FilesCheck.py, Filter.py, I18NCheck.py, InitScriptCheck.py,
+ LSBCheck.py, MenuCheck.py, MenuXDGCheck.py, NamingPolicyCheck.py,
+ PamCheck.py, Pkg.py, PostCheck.py, SignatureCheck.py,
+ SourceCheck.py, SpecCheck.py, TagsCheck.py, ZipCheck.py, rpmdiff,
+ rpmlint.py: pylint whitespace cleanups.
+ * rpmlint.py: Improve non-package-related error message formatting.
+ * SpecCheck.py: Warn about %setup outside of %prep (#164, akurtakov
+ at gmail.com).
+ * ZipCheck.py: Fix traceback when zip file does not exist (for
+ example L10n jars in firefox with %_install_langs).
+ * BinariesCheck.py, FilesCheck.py, InitScriptCheck.py,
+ MenuCheck.py, Pkg.py, SpecCheck.py, TagsCheck.py: Use string
+ methods instead of deprecated string functions.
+ * AbstractCheck.py: pylint cleanups.
+ * Filter.py, Pkg.py: Drop no longer needed Python < 2.3
+ workarounds.
+ * INSTALL, Pkg.py, PostCheck.py, README, Testing.py, rpmdiff,
+ rpmlint.1, rpmlint.py: Use tempfile.gettempdir() instead of
+ hardcoding /tmp, Python >= 2.3 is now required.
+ * README, rpmlint.1, rpmlint.py: Load all /etc/rpmlint/*config as
+ config files.
+
+2008-10-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 0.85.
+ * README, TagsCheck.py: Allow EVR in changelog without release
+ extension, the extension is often a macro or otherwise dynamic.
+ Also do not automatically add '$' to ReleaseExtension when
+ compiling it as a regex - existing configurations should be
+ reviewed and adjusted if necessary.
+ * INSTALL, Makefile, rpmlint.1, rpmlint.bash-completion,
+ rpmlint.py: Drop defunct policy loading code and options (#163).
+ * FilesCheck.py: Improve file-not-utf8 info message.
+
+2008-10-17 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Remove broken redundant-prefix-tag check (#160).
+ * README: Fix indentation.
+
+2008-10-15 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Make obsolete-not-provided info message more
+ accurate.
+
+2008-10-05 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Make invalid-version description more generic
+ (#161).
+
+2008-09-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Check for executables built with ocaml -custom,
+ https://bugzilla.redhat.com/461434
+
+2008-09-07 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Fix lib package detection (most likely broken since
+ section parsing was implemented).
+ * SpecCheck.py: Do tag related checks only within package sections.
+ * SpecCheck.py: Fix line number off by one in
+ configure-without-libdir-spec, https://bugzilla.redhat.com/461421
+
+2008-09-02 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Rename useless-explicit-provides to
+ useless-provides; "explicit" just adds potential confusion as all
+ Provides are treated the same way.
+ * TagsCheck.py: Variable naming improvements, TODO for
+ https://bugzilla.redhat.com/460872
+
+2008-08-21 Michael Scherer <misc at mandriva.org>
+
+ * SpecCheck.py: - if we have only one spec file wrongly named,
+ rpmlint will trigger "no-spec-file", instead of the proper error,
+ and will not analyse the specfile
+
+2008-08-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Allow interpreters to be in arbitrary deep subdirs
+ of /usr/libexec, /usr/lib(64)/*, /usr/share/*.
+ * TagsCheck.py: Recognize more library packages for the
+ explicit-lib-dependency check.
+
+2008-08-04 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Don't recompute nbsp for each line in spec.
+
+2008-07-30 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Try to detect and warn about shared libraries
+ that call exit() or _exit(), https://bugzilla.redhat.com/450011
+ * SpecCheck.py: Kludge for dependency token parsing within
+ multiline macro definitions, https://bugzilla.redhat.com/456843
+ * SpecCheck.py: Fix false non-break-space positives for some UTF-8
+ specfiles, https://bugzilla.redhat.com/455371
+ * SpecCheck.py, rpmlint.py: Fix enabling of UTF-8 checks for
+ specfile-only checks in UTF-8 mode.
+ * BinariesCheck.py: Reduce duplicate regex searches.
+
+2008-07-30 Michael Scherer <misc at mandriva.org>
+
+ * Pkg.py: - lzma compressed file support
+
+2008-07-26 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 0.84.
+ * FilesCheck.py: Accept interpreters also in /usr/games,
+ /usr/libexec, /usr/lib/*, /usr/lib64/*,
+ https://bugzilla.redhat.com/355861
+
+2008-07-14 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: rpm.org 4.5+ compatibility fix (Panu Matilainen)
+
+2008-07-09 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.1, rpmlint.py: Drop warnings-only exit status 65 (#157).
+ * BinariesCheck.py: Fix syntax error introduced in previous commit
+ (#159), apply #158 also for pentium* and athlon.
+
+2008-07-01 Michael Scherer <misc at mandriva.org>
+
+ * BinariesCheck.py: do not complain for PT_GNU_STACK on non x86
+ section, patch from dmueller, close bug #158
+
+2008-06-29 Michael Scherer <misc at mandriva.org>
+
+ * SpecCheck.py: tabnnany fix
+
+2008-06-21 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Change *.so regex for symlinks to match files also
+ in lib(64) subdirs, and lib*.so only to improve no-dependency-on
+ and dangling-*symlink checks.
+
+2008-06-15 Michael Scherer <misc at mandriva.org>
+
+ * SpecCheck.py: as PreReq is deprecated on almost every rpm version
+ out there ( ie > 4.2 ), we should count this as error to force
+ people to fix their packages ( mandriva patch from thierry
+ vignaud )
+ * Pkg.py: - add patch for rpm5 found in mandriva package, from
+ peroyvind
+
+2008-05-27 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Release 0.83.
+ * FilesCheck.py: Downgrade non-standard-uid/gid errors to warnings,
+ https://bugzilla.redhat.com/430206
+
+2008-05-22 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Make GPL and LGPL versioned in default valid
+ license list (#144, peroyvind at mandriva.org).
+
+2008-05-05 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Allow foo-devel require foo/foo-libs/libfoo (#154,
+ mostly from dan at danny.cz).
+
+2008-04-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Config.py, Filter.py, README, rpmlint.1, rpmlint.py: Add badness
+ scoring support (#70, vast majority of work by dmueller at
+ suse.de).
+ * rpmlint.py: Always print stats.
+ * rpmlint.py: Cosmetic cleanups.
+ * rpmlint.py: Output basic statistics before exiting.
+ * rpmlint.1, rpmlint.py: Exit with non-zero status if
+ errors/warnings printed (#128).
+ * Filter.py: Count number of printed (== non-filtered) messages per
+ category.
+ * rpmlint.py: Output usage errors to stderr, improve messages.
+ * PostCheck.py: Avoid percent-in-%foo warnings for commented out
+ lines (#149).
+ * FilesCheck.py: Don't warn about hidden files in /etc/skel (#151,
+ dmueller at suse.de).
+ * BinariesCheck.py: Fix section header regexps (#152, dmueller at
+ suse.de).
+ * SpecCheck.py: Tighten ifarch and endif regexps.
+ * SpecCheck.py: Check for attributes in %files (#153, most of the
+ work by dan at danny.cz).
+
+2008-04-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: Comment typo fix.
+
+2008-03-24 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Improve non-break-space info message.
+ * FHSCheck.py, FilesCheck.py, Filter.py, TagsCheck.py: Use
+ textwrap.fill() to pretty print all info messages, not just a few
+ special cases.
+ * rpmlint.py: Minor -I output tweak.
+ * SpecCheck.py: Check for %{_libdir} and %{_lib} in noarch
+ packages' %files, https://bugzilla.redhat.com/237204
+
+2008-03-21 Michael Scherer <misc at mandriva.org>
+
+ * SpecCheck.py: add a check for non-break space, asked on mandriva
+ bugzilla 39094
+
+2008-03-04 Ville Skyttä <ville.skytta at iki.fi>
+
+ * ZipCheck.py: Do not issue errors for jars without manifests;
+ META-INF/* are optional in them:
+ http://java.sun.com/j2se/1.4/docs/guide/jar/jar.html
+
+2008-03-01 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py, README: Add SkipDocsRegexp config parameter for
+ skipping end of line and UTF-8 tests for doc files, include RTF,
+ HTML, and OCaml files in it by default,
+ https://bugzilla.redhat.com/434694
+ * FilesCheck.py: Issue end-of-line warnings for Mac style end of
+ line delimiters.
+
+2008-02-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * ZipCheck.py: s/MANIFEST/MANIFEST.MF/ in relevant info messages.
+
+2008-02-26 Michael Scherer <misc at mandriva.org>
+
+ * SpecCheck.py: - check the main specfile ,not the first one, see
+ Mandriva bug 38157
+
+2008-02-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Downgrade configure-without-libdir-spec into a
+ warning and improve info message; not all configure scripts
+ support or need %{_libdir}, https://bugzilla.redhat.com/433783
+ * BinariesCheck.py: Excempt ocaml packages from
+ only-non-binary-in-usr-lib check,
+ https://bugzilla.redhat.com/433783
+
+2008-02-21 Ville Skyttä <ville.skytta at iki.fi>
+
+ * NamingPolicyCheck.py: Spelling fix.
+ * NamingPolicyCheck.py: Improve info message.
+ * NamingPolicyCheck.py: Allow "base" packages to satisfy checks in
+ addition to subpackages.
+ * NamingPolicyCheck.py: Check lib64 dirs in addition to lib.
+
+2008-02-09 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Do changelog version checks for source rpms too.
+
+2008-02-01 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Improve executable-stack explanation.
+
+2008-01-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Check for presence of PT_GNU_STACK and
+ executable stack (https://bugzilla.redhat.com/428096, messages
+ from lintian).
+ * BinariesCheck.py: Code cleanups.
+
+2008-01-25 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Apply license check improvements from Fedora per
+ Mandriva's request, more info:
+ http://fedoraproject.org/wiki/Packaging/LicensingGuidelines
+
+2007-12-23 Michael Scherer <misc at mandriva.org>
+
+ * FilesCheck.py: add lzma to the list of compression extension
+ * DistributionCheck.py: - check manpages compressed with lzma,
+ asked by lzma lover dvalin :) ( and blino@mandriva )
+
+2007-12-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Improve kernel module depmod regex.
+ * Makefile: 0.82
+ * Pkg.py: Don't lose version flags of prereq dependencies (part of
+ #123, dmueller at suse.de)
+
+2007-11-30 Ville Skyttä <ville.skytta at iki.fi>
+
+ * PostCheck.py: Avoid false positives for percent-in-* checks,
+ macro names are at least 3 chars long (#132, anonymous)
+
+2007-11-27 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py, TagsCheck.py: Fix UTF-8 line length calculations,
+ https://bugzilla.redhat.com/399871
+
+2007-11-17 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.1: ...
+ * rpmlint.1: Note that rpmlint does not have a full featured spec
+ file parser.
+
+2007-11-05 Ville Skyttä <ville.skytta at iki.fi>
+
+ * I18NCheck.py: Fix webapp detection (#122, dmueller at suse.de)
+
+2007-10-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Don't think that a service is enabled by
+ default if it contains LSB Default-Start: with empty value.
+
+2007-09-19 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Include both actual and expected filenames in
+ non-coherent-filename message.
+
+2007-09-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Skip end-of-line char and UTF-8 checks for RTF and
+ HTML files.
+
+2007-09-03 Ville Skyttä <ville.skytta at iki.fi>
+
+ * I18NCheck.py: subfile-not-in-%lang fix (pixel at mandriva.com)
+ * Makefile: 0.81
+
+2007-08-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py, INSTALL: Use readelf instead of objdump for
+ binary checks (#83, dmueller at suse.de).
+ * TagsCheck.py: Unbreak info messages for checks added in previous
+ revision.
+ * BinariesCheck.py: Use generator objects when iterating over files
+ (#83, dmueller at suse.de)
+ * TagsCheck.py: Check for percent chars in dependency (#75,
+ dmueller at suse.de), obsoletes, provides and conflicts versions.
+
+2007-08-24 Michael Scherer <misc at mandriva.org>
+
+ * I18NCheck.py: - patch from pixel, for directory marked as %lang
+ when the files or subdirectory are not
+ * test/test.PamCheck.py: fix test
+
+2007-08-20 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.py: Check spec files too when processing directories.
+
+2007-08-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py, PostCheck.py: Do not warn about ghost file non-creation
+ for missingok files (#79, dmueller at suse.de)
+ * BinariesCheck.py: Drop unused dynsyms stuff (#83, dmueller at
+ suse.de)
+ * Pkg.py: grep() speedup (#80, dmueller at suse.de)
+
+2007-08-11 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Symlinking a *.so to another *.so does not make it
+ a devel symlink (#78, dmueller at suse.de)
+ * DistributionCheck.py: Improve man page dir regex (#76)
+
+2007-07-31 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Comment typo fix.
+ * rpmlint.py: 2007
+
+2007-07-24 Michael Scherer <misc at mandriva.org>
+
+ * PostCheck.py: - lua is a valid shell, fix #74, patch from
+ dmueller
+
+2007-07-20 Ville Skyttä <ville.skytta at iki.fi>
+
+ * I18NCheck.py: Add "bn_IN" (#72, sankarshan.mukhopadhyay at
+ gmail.com), "my", and "si" to list of valid locale subdirs.
+
+2007-06-24 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: LSB comment blocks have keywords, not tags.
+
+2007-06-20 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: Tolerate nonexistent current dir,
+ https://bugzilla.redhat.com/244835
+
+2007-06-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Avoid outputting useless line numbers (alcapcom at
+ gmail.com).
+
+2007-06-02 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Ignore filename case when looking for include
+ files.
+ * FilesCheck.py: Improve OCaml devel file handling,
+ https://bugzilla.redhat.com/241471
+
+2007-05-31 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Add filename and info for missing mandatory
+ LSB tag check (#67, dmueller at suse.de)
+ * SpecCheck.py: Don't choke on trailing commas in deptokens() (#65,
+ dmueller at suse.de)
+
+2007-05-26 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Treat *.cxx, *.cpp, *.hxx and *.hpp as include
+ files.
+
+2007-05-14 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Filter.py, Pkg.py, SpecCheck.py, rpmlint.1, rpmlint.py: Add
+ direct specfile checking, error line numbers (#60, alcapcom at
+ gmail.com). Also modify error output format for editor
+ parseability (mostly useful for specfile checks), and include
+ package architectures in output.
+
+ NOTE: locally installed filters should be reviewed against the
+ new output format and ported to it if needed.
+ * BinariesCheck.py, FilesCheck.py, Pkg.py, TagsCheck.py: Carry
+ package arch around in pkg object.
+
+2007-05-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Even more fallout from previous change.
+ * Pkg.py: More fallout from previous change.
+ * InitScriptCheck.py: Fix fallout from previous change.
+ * FilesCheck.py, InitScriptCheck.py, Pkg.py, PostCheck.py,
+ SpecCheck.py, TagsCheck.py: Better I/O error and temporary file
+ handling.
+ * FilesCheck.py: Check doc text files for UTF-8.
+
+2007-05-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.py: Remove unused/deprecated loadFile().
+ * SpecCheck.py: Better file I/O error handling.
+ * SpecCheck.py: Improve patch related regexps.
+ * PostCheck.py: Check %pretrans and %posttrans if supported by
+ installed rpm.
+ * PostCheck.py: Improve percent-in-* check,
+ https://bugzilla.redhat.com/239611
+
+2007-05-07 Ville Skyttä <ville.skytta at iki.fi>
+
+ * MenuXDGCheck.py: Fix path to failing .desktop file in output
+ (#61, dmueller at suse.de)
+ * InitScriptCheck.py: Accept LSB keywords starting with X- (#62,
+ dmueller at suse.de)
+
+2007-04-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: 0.80
+ * BinariesCheck.py: Improve valid *.so regexp.
+
+2007-04-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Don't require soname to start with 'lib' as
+ long as *.so that do not start with 'lib' are checked.
+
+2007-04-05 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Fail gracefully on unreadable files, eg.
+ dangling symlinks.
+
+2007-03-26 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Check %description contents for things that look
+ like tags.
+
+2007-03-25 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Check LSB Default-Start too when checking if
+ service is enabled by default, bugzilla.redhat.com/233795
+
+2007-03-15 Ville Skyttä <ville.skytta at iki.fi>
+
+ * AbstractCheck.py: Autoregister checks only once (by name) so
+ checks (and configs) can import each other without resulting in
+ multiple instances of a particular check being run.
+ * FilesCheck.py: Check for UTF-8 encoded filenames.
+ * INSTALL, Pkg.py, README: Implement is_utf8_str without iconv;
+ Python >= 2.2 is now required.
+
+2007-03-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Change obsolete-not-provided into a warning and
+ improve description.
+
+2007-03-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Allow hardcoded /lib in /lib/firmware.
+ * FilesCheck.py: Fix devel to base package dependency check broken
+ in [1317]
+
+2007-03-07 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Allow tabs and be as relaxed as chkconfig
+ itself with whitespace in chkconfig regex.
+
+2007-03-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Fix variable scoping/indentation issue introduced
+ in [1319].
+
+2007-03-02 Michael Scherer <misc at mandriva.org>
+
+ * FilesCheck.py: oops, fix typo spotted by guillomovitch
+ * FilesCheck.py: - check cron file, see
+ http://qa.mandriva.com/show_bug.cgi?id=23951
+
+2007-02-25 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Treat *-static as devel packages.
+ * TagsCheck.py: Use devel package regexp from FilesCheck.
+
+2007-02-17 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Fix LSB multiline description parsing (#52).
+ * InitScriptCheck.py: unknow-lsb-tag -> unknown-lsb-tag
+ * DocFilesCheck.py: Fix for rpm-python < 4.3.2 (#53).
+
+2007-02-16 Ville Skyttä <ville.skytta at iki.fi>
+
+ * MenuCheck.py, TagsCheck.py: Improve capitalization checks,
+ https://bugzilla.redhat.com/228645
+
+2007-02-05 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Don't warn about missing -q to %setup if there's a
+ -T without -a or -b, https://bugzilla.redhat.com/227389
+
+2007-02-02 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: 0.79.
+ * Makefile: Fix test target.
+
+2007-02-01 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Treat Ocaml *.cmx and *.cmxa as arch dependent
+ binaries, https://bugzilla.redhat.com/226879
+
+2007-01-28 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Check for unused direct shared lib deps,
+ inspired by
+ http://www.redhat.com/archives/fedora-maintainers/2006-June/msg00176.html
+
+2007-01-25 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Avoid spewing both spurious-executable and
+ script-without-shebang warnings for one file.
+
+2007-01-24 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Check for usual spurious executable bits,
+ https://bugzilla.redhat.com/222585
+
+2007-01-02 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Don't do end-of-line checks on RTF files,
+ https://bugzilla.redhat.com/220061
+ * MenuXDGCheck.py: Use Pkg.getstatusoutput instead of
+ subprocess.call for Python < 2.4 compat,
+ http://bugzilla.redhat.com/221116
+
+2006-12-18 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Treat PDF files always as binary,
+ https://bugzilla.redhat.com/220061
+ * FilesCheck.py: Use floating point division in istextfile().
+
+2006-12-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Check for > 1 %changelog sections,
+ http://bugzilla.redhat.com/219068
+
+2006-12-04 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Spelling fixes (Bernard Johnson,
+ https://bugzilla.redhat.com/218250)
+
+2006-12-03 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Improve PreReq and broken context marked dep syntax
+ regexps and descriptions.
+
+2006-11-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * README: Don't hardcode InvalidRequires default, fix up
+ indentation.
+ * README: InvalidRequires is a list of regexps, not strings.
+
+2006-11-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Add Creative Commons licenses,
+ https://bugzilla.redhat.com/211417
+
+2006-11-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * PostCheck.py: Fine tune forbidden-selinux-command info message.
+ * PostCheck.py: Check for forbidden SELinux related commands (Steve
+ Grubb, https://bugzilla.redhat.com/214605)
+ * PostCheck.py: Drop some duplicate code.
+
+2006-10-28 Ville Skyttä <ville.skytta at iki.fi>
+
+ * I18NCheck.py: Allow 3-letter language codes in locale subdirs
+ (Dave Lehman, https://bugzilla.redhat.com/212491).
+
+2006-10-19 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Skip non-PIC check if objdump failed.
+
+2006-10-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Don't run ldd on ar archives,
+ https://bugzilla.redhat.com/210110
+
+2006-10-11 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: Try to avoid infinite recursion in shell variable
+ expansion https://bugzilla.redhat.com/210261
+ * Pkg.py: Add start boundary to shell variable assignment regexp
+ https://bugzilla.redhat.com/210261
+ * InitScriptCheck.py: Fix regression introduced in [1242]: shell
+ variable expansion needs the whole file to be passed to it, not
+ just the current line being examined
+ https://bugzilla.redhat.com/210261
+
+2006-10-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Don't crash in istextfile() if reading fails, eg.
+ https://bugzilla.redhat.com/209876
+ * BinariesCheck.py, I18NCheck.py, TagsCheck.py: Don't assume all
+ packages have a source rpm or i18n table, eg. gpg-pubkeys don't;
+ https://bugzilla.redhat.com/209889
+ * rpmlint.py: Only try to check rpm files when given a directory.
+ * rpmlint.py: Don't run main() when importing rpmlint.py, makes eg.
+ pychecker happier (#48).
+
+2006-09-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * MenuXDGCheck.py, PamCheck.py, Testing.py: Add editor indentation
+ settings.
+
+2006-09-23 Michael Scherer <misc at mandriva.org>
+
+ * Makefile: tag 0.78 in Makefile
+ * SpecCheck.py: - some refactoring
+
+2006-09-22 Michael Scherer <misc at mandriva.org>
+
+ * test/test.PamCheck.py: - fix test
+
+2006-09-14 Ville Skyttä <ville.skytta at iki.fi>
+
+ * INSTALL: Python >= 2.0 required for popen4.
+ * BinariesCheck.py, MenuCheck.py, Pkg.py, PostCheck.py: Use
+ sequence based command invocation more; avoids shell
+ escaping/whitespace issues eg. like
+ https://bugzilla.redhat.com/206383
+ * I18NCheck.py: sw is valid for Swahili, don't treat is as
+ misspelled Swedish.
+
+2006-09-09 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Take escaping into account when finding buildroot
+ strings, fixes #47.
+ * SpecCheck.py: Say which section lacks cleanup in
+ no-cleaning-of-buildroot.
+
+2006-09-03 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Recognize GIT and Mercurial "SCM internal" files.
+ * FilesCheck.py, InitScriptCheck.py, PamCheck.py: More literal dot
+ in regexp escaping.
+ * DistributionCheck.py: Drop unused regexps.
+ * SpecCheck.py: Check for unversioned Provides/Obsoletes in
+ specfiles.
+
+2006-08-30 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Distinguish between sourced and executed scripts
+ (Guillaume Rousse, #17).
+ * FilesCheck.py: Add comment about *.pm shebang (initially from
+ #17)
+ * FilesCheck.py: s/shellbang/shebang/ (part of #17)
+
+2006-08-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Escape literal dots in regexps.
+ * FilesCheck.py: Don't demand executable scripts in /etc/profile.d;
+ cf. https://bugzilla.redhat.com/35714
+
+2006-08-26 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Allow init scripts with package name + 'd'.
+ * PamCheck.py: Use Pkg.grep, output line numbers; fixes #38.
+ * BinariesCheck.py: Improve invalid dir reference regexp, compile
+ it only once.
+ * BinariesCheck.py, MenuCheck.py, Pkg.py: Make Pkg.grep more
+ robust, change to instance method, return matching line numbers.
+
+2006-08-25 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Tone down invalid-license message a bit.
+ * TagsCheck.py: Add no-url-tag description (duh).
+
+2006-08-24 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Improve broken scriptlet dep regex.
+ * SpecCheck.py: Check broken scriptlet dep syntax only in the
+ "package" section (Pascal Terjan, #42).
+
+2006-07-31 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Check for world writable files and dirs.
+
+2006-07-25 Ville Skyttä <ville.skytta at iki.fi>
+
+ * PostCheck.py, SpecCheck.py: Add some TODO comments.
+
+2006-07-14 Michael Scherer <misc at mandriva.org>
+
+ * PostCheck.py: - add Xdg menu directory to %updates-menus
+ detection
+ * MenuXDGCheck.py: - really fix the call to desktop-file-validate
+ * MenuXDGCheck.py: - fix the check
+
+2006-07-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Catch macroized %{__make} etc in make check regexp,
+ and ignore all of it in %description too.
+ * TagsCheck.py: Use zlib/libpng, not zlib for that license to
+ follow the OSI list (bugzilla.redhat.com/198616).
+
+2006-07-11 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.py: Improve error messages, fix checking dirs containing
+ packages.
+
+2006-07-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Config.py, README: Add removeFilter() for removing filters from
+ the config, useful eg. in per-user config files for restoring
+ filters added in system config files.
+
+2006-07-09 Michael Scherer <misc at mandriva.org>
+
+ * SpecCheck.py: do not complaint if %changelog contains a line like
+ : - move the make test to %%check bug reported by vincent danen
+
+2006-07-07 Michael Scherer <misc at mandriva.org>
+
+ * MenuXDGCheck.py: add non-utf8-desktopfile check, for [30]
+ * ., InitScriptCheck.py: r1045@crovax: misc | 2006-07-07 00:37:01
+ +0200 Lsb tags checking
+ * ., InitScriptCheck.py: r1044@crovax: misc | 2006-07-06 23:48:30
+ +0200 - check the script line by line, needed to check Lsb init
+ tags
+
+2006-07-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Allow EVR also in the first line of the changelog
+ text, based on a patch from Thomas Vander Stichele (#23).
+
+2006-07-06 Michael Scherer <misc at mandriva.org>
+
+ * ., I18NCheck.py, MenuXDGCheck.py, SourceCheck.py, SpecCheck.py:
+ merge
+ * ., TagsCheck.py: r1034@crovax: misc | 2006-07-06 16:41:03 +0200 -
+ replace regexp by simple search
+ * ., DistributionCheck.py: r1033@crovax: misc | 2006-07-06 16:37:03
+ +0200 - remove regexp, use simple search
+ * ., MenuCheck.py: r1032@crovax: misc | 2006-07-06 16:33:22 +0200 -
+ remove uneeded import
+ * ., FilesCheck.py: r1031@crovax: misc | 2006-07-06 16:32:38 +0200
+ - do not use regexp when we can use a single search
+ * .:
+ * ., FHSCheck.py: r1019@mallarme: misc | 2006-07-06 01:23:02 +0200
+ - do not hardcode standard subdir in the detailed message, better
+ to take them from the check itself
+ * ., FHSCheck.py: r1018@mallarme: misc | 2006-07-06 01:14:07 +0200
+ - do not use regexp when we can avoid them, in order to use less
+ memory and be faster.
+ * ., Config.py, MenuXDGCheck.py: r1017@mallarme: misc | 2006-07-06
+ 01:02:24 +0200 - start MenuXDGCheck, for now, it just run
+ desktop-file-validate on every file, maybe i should restrict more
+ * ., Pkg.py: r1016@mallarme: misc | 2006-07-06 00:06:39 +0200 -
+ pychecker fix, do not mask builtins
+ * ., FilesCheck.py, MenuCheck.py: r1015@mallarme: misc | 2006-07-06
+ 00:00:31 +0200 - pychecker fix
+ * ., test.sh: r1014@mallarme: misc | 2006-07-05 23:57:46 +0200 -
+ improve test.sh, exit when a test fail, and try to launch rpmlint
+ ( basic syntax checking )
+ * ., FilesCheck.py: r1013@mallarme: misc | 2006-07-05 23:20:12
+ +0200 - pychecker fix, erase idx as this is also used in
+ Filter.py
+ * ., InitScriptCheck.py: r1012@mallarme: misc | 2006-07-05 23:17:56
+ +0200 - better variable name, fix pychecker warning
+
+2006-07-05 Michael Scherer <misc at mandriva.org>
+
+ * NamingPolicyCheck.py: - revert [910], fix bug #18
+ * PamCheck.py: - fix bug #33, by removing comment from pam files
+ * Filter.py, Makefile, Testing.py, test, test.sh,
+ test/PamCheck-0.1-1.i586.rpm, test/test.PamCheck.py: - start of
+ testing infrastructure
+
+2006-06-30 Ville Skyttä <ville.skytta at iki.fi>
+
+ * PostCheck.py: Improve home regex.
+ * PostCheck.py: Improve dangerous command regexp.
+
+2006-06-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Add line numbers to mixed-use-of-spaces-and-tabs
+ warning (#28).
+ * TagsCheck.py: Don't fail if the GROUPS file is not found (eg. rpm
+ installed with --excludedocs).
+ * TagsCheck.py: Improve incoherent-version-in-changelog
+ description.
+ * SpecCheck.py: Check that %prep, %build and %install exist, even
+ if empty: https://bugzilla.redhat.com/192422
+
+2006-06-28 Michael Scherer <misc at mandriva.org>
+
+ * Makefile: preparing for release
+
+2006-06-28 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.py: Fix error message output when loading a system config
+ file fails.
+
+2006-06-28 Michael Scherer <misc at mandriva.org>
+
+ * rpmlint.py: - give error message about config file loading
+ failure, and give the correct file that failed.
+ * PostCheck.py, TagsCheck.py, rpmlint.py: - pychecker fix
+
+2006-06-28 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Use default standard users/groups from LSB.
+ Distributors may want to customize StandardUsers/StandardGroups.
+ * Config.py, setuplist.py: Remove dead code.
+ * FilesCheck.py: Don't hardcode lists of standard users/groups in
+ error messages, use configured ones instead.
+ * README: Typo fixes.
+
+2006-06-26 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Remove single-percent-in-changelog; it's already
+ covered by macro-in-%changelog (#27).
+
+2006-06-22 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py, README: Always prefer relative symlinks by default
+ (#25), the new configuration option UseRelativeSymlinks can be
+ set to 0 to restore the old behaviour. Also add a tiny
+ description for symlink-should-be-relative.
+ * FilesCheck.py: Sort list of standard dirs, add /, /media,
+ /selinux, /srv, /sys, and /usr/local/share*.
+ * SpecCheck.py: Add check for mixed use of spaces and tabs in
+ specfiles (#19).
+
+2006-06-22 Michael Scherer <misc at mandriva.org>
+
+ * AbstractCheck.py, BinariesCheck.py, Config.py, DocFilesCheck.py,
+ FHSCheck.py, I18NCheck.py, MenuCheck.py, NamingPolicyCheck.py,
+ PostCheck.py, ZipCheck.py: - remove pychecker errors: - unused
+ variable - variable that shadow builtins - unused expression (
+ usually used to trigger a exception ) - do not raise string as
+ exception
+ * PamCheck.py, RpmFileCheck.py, SpecCheck.py: - fix pychecker
+ warning, notably a missing regexp in commit [1201]
+
+2006-06-21 Michael Scherer <misc at mandriva.org>
+
+ * MenuCheck.py: add non-xdg-migrated-menu, patch from frederic
+ crozat, closes #21
+ * SpecCheck.py: add a check for macro in %changelog
+
+2006-06-20 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py: Adapt rpm >= 4.2 and source package detection to rpm
+ 4.4.x (x=6?) versions which no longer have RPMTAG_SOURCEPACKAGE.
+
+2006-06-15 Ville Skyttä <ville.skytta at iki.fi>
+
+ * DocFilesCheck.py: Revert previous change; the order of filenames
+ and their deps needs to be in sync.
+ * SpecCheck.py: Warn if __find_(provides|requires) is defined
+ without disabling the internal dependency generator.
+ * DistributionCheck.py, DocFilesCheck.py, FilesCheck.py,
+ I18NCheck.py, InitScriptCheck.py, LSBCheck.py, MenuCheck.py,
+ NamingPolicyCheck.py, Pkg.py, SpecCheck.py, TagsCheck.py:
+ Cleanups.
+ * BinariesCheck.py: Include filename in ldd failure warning.
+
+2006-06-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Make name of file system base package a bit less
+ hardcoded, allow multiple.
+
+2006-06-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Improve subsys regexp, fixes
+ bugzilla.redhat.com/194466.
+
+2006-06-06 Ville Skyttä <ville.skytta at iki.fi>
+
+ * PostCheck.py: Fix fallout from [1183].
+
+2006-06-05 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Pkg.py, rpmlint.py: Check all matching installed packages, not
+ only the first (for multiarch systems and/or multi-version
+ packages).
+ * rpmlint.py: Fix setting ExtractDir in config files.
+ * rpmlint.py: 2006.
+ * rpmlint.py: Use execfile() for loading conf files, fixes #20.
+ * SpecCheck.py: Really bypass checks on section markers.
+ * SpecCheck.py: Catch redundant prefix specified as %_prefix
+ (without curlies).
+ * SpecCheck.py: Add check for macros in %changelog.
+ * SpecCheck.py: Don't bother running checks against section marker
+ lines.
+ * SpecCheck.py: Tighten section marker regexp.
+ * PostCheck.py: Make sure that the correct interpreter is used for
+ scriptlet syntax checks.
+ * FilesCheck.py: Base doc file decisions on whether a file is
+ marked as doc, not a regexp heuristic.
+
+2006-06-04 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Do not hardcode lib*-devel no-documentation
+ warning suppression. It would work only for specific package
+ naming schemes, and even in the cases where it works, it can be
+ argued that it's of dubious usefulness. Configurable filters
+ according to distro policies are a better way to handle cases
+ like this.
+
+2006-05-19 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Check that debuginfo packages contain files.
+
+2006-05-16 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmdiff: Print what's wrong if getopt fails.
+ * rpmdiff: Add -h/--help.
+ * rpmdiff: Add option to ignore file timestamps, thanks to Matt
+ Domsch.
+
+2006-05-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Tolerate macroized "rm" in buildroot cleaning
+ check.
+ * SpecCheck.py: Wrap info message lines at < 80 chars.
+ * SpecCheck.py: Fix build root regexp.
+
+2006-05-13 Michael Scherer <misc at mandriva.org>
+
+ * SpecCheck.py: add make-check-outside-check-section, close #14.
+ * SpecCheck.py: add rpm-buildroot-usage, close ticket #15.
+ * SpecCheck.py: - add no-cleaning-of-buildroot check, to be sure
+ that $BUILDROOT is cleaned, as this will leave old files, and
+ break in funny way when files are removed from package, etc.
+
+2006-05-13 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Check for undefined non-weak symbols (shared
+ libs in installed pkgs only).
+
+2006-05-12 Michael Scherer <misc at mandriva.org>
+
+ * SpecCheck.py: add setup-not-quiet, to check if %setup -q is used
+ * SpecCheck.py: - use a variable to hold the current esection, as
+ it will help to detect if some macros are misused, and as it
+ unify the handling of the section
+ * SpecCheck.py: avoid using useless temporary variables if not
+ needed
+ * SpecCheck.py: - style fix, i cannot really stand foo=bar as this
+ is less readable than foo = bar, but a real tool should be used
+ to do that.
+
+2006-05-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Drop unused la-file-with-invalid-dir-reference
+ info message.
+ * TagsCheck.py: Add Development/Debug into default groups, many
+ distros use it in -debug(info) packages but do not have it in
+ GROUPS. While at it, sort the list and close the GROUPS file
+ explicitly.
+ * TagsCheck.py: OPL is ambiguous, do not use it.
+ * TagsCheck.py: Bring list of licenses up to date with OSI, add a
+ couple of other common (mainly non-software) licenses.
+
+2006-05-04 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Make *.so regexp stricter.
+ * BinariesCheck.py: Update ld.so filename regexp for x86_64.
+
+2006-05-03 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py, FilesCheck.py: Don't tell people to bother
+ flepied about distro config issues.
+
+2006-04-28 Michael Scherer <misc at mandriva.org>
+
+ * AbstractCheck.py, PamCheck.py: - create a new class
+ AbstractFilesCheck to factorise the code that filter and extract
+ files based on a regexp. this class is now used for PamCheck.py,
+ and other similar check will be converted soon.
+ * RpmFileCheck.py: - fix error caused by unterminated string.
+
+2006-04-15 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Allow bundling depmod arguments in kernel module
+ post(un)install scriptlets.
+
+2006-04-14 Michael Scherer <misc at mandriva.org>
+
+ * RpmFileCheck.py: add svn keywords and copyright notice to
+ RpmFileCheck.py
+ * FilesCheck.py: remove setuid-gid-binary error, as we already have
+ setuid-binary and setgid-binary, and remove Fred email address
+ from the detailed explication, as this is no longer used, and not
+ generic enough.
+ * FilesCheck.py, I18NCheck.py: remove all reference to
+ /etc/httpd/webapps.d, fix bug #8
+
+2006-04-14 Ville Skyttä <ville.skytta at iki.fi>
+
+ * RpmFileCheck.py: Make info message wrap with < 80 char lines.
+
+2006-04-13 Michael Scherer <misc at mandriva.org>
+
+ * Config.py, README, RpmFileCheck.py: add RpmFileCheck, to test the
+ rpm file itself, for the moment, only check the length of the
+ filename, close ticket #9
+
+2006-04-10 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.1: Bump month.
+ * rpmlint.1: Mention /usr/share/rpmlint/config.
+ * rpmlint.1: Mention AUTHORS.
+ * FilesCheck.py: Treat *-debug(info) as devel packages.
+ * Makefile: Produce prettier and more concise ChangeLog.
+ * Makefile: Fix AUTHORS and ChangeLog up to date checks.
+ * TagsCheck.py: List valid groups in non-standard-group info.
+ * TagsCheck.py: Don't hardcode valid license list in
+ invalid-license info.
+ * TagsCheck.py: Avoid empty strings in list from
+ get_default_valid_rpmgroups().
+
+2006-04-10 Michael Scherer <misc at mandriva.org>
+
+ * Makefile: for some reason, svn messages do not have space at the
+ same place for french locales and C locales :
+
+ [misc@takara rpmlint] $ svn info . | grep URL URL :
+ svn+ssh://rpmlint.zarb.org/home/projects/rpmlint/svn/trunk
+ [misc@takara rpmlint] $ LC_ALL=C svn info . | grep URL URL:
+ svn+ssh://rpmlint.zarb.org/home/projects/rpmlint/svn/trunk
+
+ (note 'URL:' vs 'URL :')
+
+ so, running make tag do not work on my computer, using sed
+ instead of cut did the trick.
+
+2006-04-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Clean up AUTHORS too.
+ * ., AUTHORS, Makefile, authors.xsl: Generate AUTHORS from
+ authors.xml.
+ * Makefile: More target dependency fixes and cleanups.
+ * Makefile: Fix localcopy/tar dependencies.
+ * Makefile: Don't require trailing slash for ETCDIR.
+
+2006-04-05 Michael Scherer <misc at mandriva.org>
+
+ * check-install.py: Not used by rpmlint, broken on latest rpm
+ python binding
+ * Config.py, PamCheck.py: add use-old-pam-stack check
+ * README: update default values for configuration option
+ * Config.py: automatically transform filter regexp using () into
+ regexp without named group, to prevent overflow, as it seems to
+ be limited to 100 for this version of python. It may also reduce
+ memory usage.
+ * FilesCheck.py, README: add a option StandardUsers and
+ StandardGroups, for uid checking
+ * MenuCheck.py: place DEFAULT_LAUNCHERS here, as it was removed by
+ last Config.py cleaning
+
+2006-04-04 Michael Scherer <misc at mandriva.org>
+
+ * Config.py: remove all exceptions, they should be placed in
+ another file ( it's up to each packager to provides it )
+ * Makefile: use do not override ETCDIR in Makefile, as it misplaces
+ bash completion, and make package relocation difficult.
+ * Makefile: shebang line of ./compile.py was broken on my computer
+ : [misc@n1 rpmlint-0.76] $ make ./compile.py
+ "/usr/share/rpmlint/" [A-Z]*.py Unknown option: - usage:
+ /usr/bin/python [option] ... [-c cmd | -m mod | file | -] [arg]
+ ... Try `python -h' for more information. make: *** [all] Erreur
+ 2
+ * rpmlint.py: add another configuration file for packager
+ /usr/share/rpmlint/config is used by packager, to set package
+ wide policy, etc, and should not be modified by users
+ /etc/rpmlint/config is used to set a configuration for a specific
+ host, and should be marked as %config(noreplace) in rpm
+ * Makefile: tar target requires dir target, as it use the directory
+ created before
+ * TagsCheck.py: do not trigger description-use-invalid-word and
+ summary-use-invalid-word if ForbiddenWords is empty ( default
+ value )
+ * TagsCheck.py: use rpm package default group, taken from file
+ GROUPS
+
+2006-04-03 Michael Scherer <misc at mandriva.org>
+
+ * TagsCheck.py: do not hardcode "mandrake" forbidden word in code
+ * TagsCheck.py: oops, seems i forget to remove last occurence of
+ DEFAULT_PACKAGER
+
+2006-04-02 Michael Scherer <misc at mandriva.org>
+
+ * DistributionCheck.py, TagsCheck.py: use default empty value for
+ Distribution and Vendor
+ * TagsCheck.py: remove mandriva domain from check details
+ * TagsCheck.py: no default value for release extension
+ * TagsCheck.py: remove mandriva default value for
+ valid_buildhost_regex
+ * TagsCheck.py: remove useless constant DEFAULT_PACKAGER, as we now
+ have default empty value for getOption
+ * TagsCheck.py: do not hardcode mandrake forbidden word in url
+ * Config.py: add default empty value for getOption
+ * TagsCheck.py: do not hardcode mandriva email address in code
+
+2006-04-01 Michael Scherer <misc at mandriva.org>
+
+ * TagsCheck.py: change requires-on-release from error to warning,
+ close ticket #5
+
+2006-04-01 Ville Skyttä <ville.skytta at iki.fi>
+
+ * AbstractCheck.py, BinariesCheck.py, Config.py, ConfigCheck.py,
+ DistributionCheck.py, DocFilesCheck.py, FHSCheck.py,
+ FilesCheck.py, Filter.py, I18NCheck.py, InitScriptCheck.py,
+ LSBCheck.py, MenuCheck.py, NamingPolicyCheck.py, Pkg.py,
+ PostCheck.py, SignatureCheck.py, SourceCheck.py, SpecCheck.py,
+ TagsCheck.py, ZipCheck.py, check-install.py, compile.py, rpmdiff,
+ rpmlint, rpmlint.py: Add indentation settings for vi* and *Emacs,
+ use -tt, untabify.
+
+2006-03-31 Michael Scherer <misc at mandriva.org>
+
+ * DistributionCheck.py, README: - Do not use mandriva defaut value
+ for DistributionCheck - Do not warn for invalid distribution and
+ vendor if the option not set
+ * FilesCheck.py, README: Remove unused CrossCompilation option, the
+ code was commented since it was commited
+
+2006-03-30 Michael Scherer <misc at mandriva.org>
+
+ * TagsCheck.py: add explanation for requires-on-release
+
+2006-03-29 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Drop removed rpmlint.spec from FILES.
+
+2006-03-29 Michael Scherer <misc at mandriva.org>
+
+ * AbstractCheck.py: - raise not implemented when
+ AbstractCheck.check is not implemented instead of silently
+ passing
+ * Makefile, rpmlint.spec: - remove spec and associated Makefile
+ target\n- rename rename localdist to dist
+
+2006-03-28 Michael Scherer <misc at mandriva.org>
+
+ * Config.py: - do not report
+ shared-lib-without-dependency-information and
+ library-not-linked-against-libc for glibc on x86_64, reported and
+ patched by Dirk Mueller, close ticket #2
+
+2006-03-27 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Makefile: Try to detect if a tag already exists.
+ * Makefile: Set root:root ownership inside tarball.
+ * Makefile: Drop superfluous dependency on changelog from tar.
+ * Makefile: Comment out rpm target for now.
+ * Makefile: Adapt (cvs)tag target for svn.
+ * Makefile: Adapt export target to svn.
+
+2006-03-24 Ville Skyttä <ville.skytta at iki.fi>
+
+ * rpmlint.spec: Fix querying the specfile on setups without the
+ %mkrel macro.
+ * .cvsignore: No longer needed.
+
+2006-03-24 Michael Scherer <misc at mandriva.org>
+
+ * authors.xml: add my new uid to authors.xml
+
+2006-03-24 Ville Skyttä <ville.skytta at iki.fi>
+
+ * README.CVS, README.devel: Update anon checkout instructions.
+
+2006-03-23 Ville Skyttä <ville.skytta at iki.fi>
+
+ * ., ChangeLog, Makefile, authors.xml: Generate ChangeLog with
+ svn2cl, remove it from svn.
+ * Makefile, rpmlint.1, rpmlint.spec: Add man page.
+ * rpmlint.spec: Update project URL.
+ * README: Update contact info.
+
+2006-03-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Don't use -T to objdump for *.debug files.
+ * BinariesCheck.py: Take lib64 archs into account in reference and
+ usr_lib_exception regexps.
+
+2006-03-08 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Sync code and info messages for "*-too-long"
+ checks.
+
+2006-03-02 Ville Skyttä <ville.skytta at iki.fi>
+
+ * FilesCheck.py: Treat *-config as "build config" files only if
+ they are in .../bin/
+
+2006-03-02 Michael Scherer <misc at mandriva.org>
+
+ * I18NCheck.py: add Khmer language code ( thanks Dirk Mueller from
+ suse for the patch )
+
+2006-02-17 Ville Skyttä <ville.skytta at iki.fi>
+
+ * TagsCheck.py: Take Epoch always into account when checking -devel
+ deps to main package.
+
+2006-02-12 Ville Skyttä <ville.skytta at iki.fi>
+
+ * SpecCheck.py: Improve (Build)PreReq messages and descriptions.
+ * FilesCheck.py: Don't warn about shebang-less executable *.la in
+ lib paths; libtool does them that way.
+ * FilesCheck.py: Don't print an error if a script included in docs
+ is not executable.
+ * FilesCheck.py: Avoid using potentially stale "line" and "res"
+ values in text file check.
+
+2006-02-10 Guillaume Rousse <guillomovitch at mandriva.org>
+
+ * Makefile: install bash completion file
+ * FilesCheck.py: let perl module keep shellbang for their own use
+
+2006-02-06 Michael Scherer <misc at mandriva.org>
+
+ * ChangeLog: Generated by cvs2cl the 06_Feb
+ * rpmlint.spec: - release 0.75-1mdk
+ * Makefile: - Remove spurious spaces after setting the locales
+ * Makefile: - force C locales in the shell subcommand, as it will
+ otherwise includes lowercase filenames in the check for print
+ statement in Makefile
+ * ChangeLog: Generated by cvs2cl the 06_f�v
+ * README: Add MetaPackageRegexp option in README
+
+2006-02-06 Rafael Garcia-Suarez <rgarciasuarez at mandriva.com>
+
+ * FilesCheck.py: Fix english spelling in new warning
+
+2006-02-06 Michael Scherer <misc at mandriva.org>
+
+ * ChangeLog: Generated by cvs2cl the 06_f�v
+ * FilesCheck.py: Add file-in-meta-package check, to detect if a
+ meta package contains files ( rgs request )
+
+2006-02-02 Rafael Garcia-Suarez <rgarciasuarez at mandriva.com>
+
+ * SpecCheck.py: Patch by Pascal Terjan to warn against the faulty
+ Requires(pre,post) syntax in spec files.
+
+2006-01-31 Rafael Garcia-Suarez <rgarciasuarez at mandriva.com>
+
+ * TagsCheck.py: Add Development/PHP to the list of default allowed
+ rpm groups
+
+2006-01-30 Michael Scherer <misc at mandriva.org>
+
+ * TagsCheck.py: - add the Mozart Licence ( bug #16416 ), thanks to
+ guillaume rousse.
+
+2006-01-15 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py, README: Add option for warning services being
+ on by default after chkconfig --add.
+ * Pkg.py: Remove accidentally included debug outputs.
+ * Config.py, FilesCheck.py, Pkg.py, README, SpecCheck.py,
+ TagsCheck.py: Add optional UTF-8 checks for docs, specfiles and
+ header field values.
+ * FilesCheck.py: Recognize docs in /usr/X11R6 too.
+
+2006-01-09 Michael Scherer <misc at mandriva.org>
+
+ * Pkg.py: Fix backtrace when there is a missing tag ( bug #20518 on
+ mdv bugzilla )
+
+2005-12-20 Frédéric Lepied <flepied at mandriva.com>
+
+ * I18NCheck.py: fixed typo making the nso and oc ISO codes not
+ reported as valid (Dirk Mueller).
+
+2005-12-14 Ville Skyttä <ville.skytta at iki.fi>
+
+ * Config.py, DocFilesCheck.py: New check for dependencies caused by
+ %doc files (Enrico Scholz).
+ * BinariesCheck.py: Clean up failed objdump output.
+
+2005-12-09 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 09_Dec
+
+2005-12-09 Ville Skyttä <ville.skytta at iki.fi>
+
+ * BinariesCheck.py: Skip some checks and output better warning
+ message if objdump fails.
+ * BinariesCheck.py, Pkg.py: Ensure English output for commands
+ whose output is parsed.
+
+2005-12-06 Michael Scherer <misc at mandriva.org>
+
+ * rpmlint.spec: - use mkrel macro
+
+2005-11-27 Ville Skyttä <ville.skytta at iki.fi>
+
+ * InitScriptCheck.py: Report incoherent subsys starting with "$" as
+ a warning instead of an error.
+ * AbstractCheck.py, BinariesCheck.py, Config.py, ConfigCheck.py,
+ DistributionCheck.py, FHSCheck.py, FilesCheck.py, Filter.py,
+ I18NCheck.py, InitScriptCheck.py, LSBCheck.py, MenuCheck.py,
+ NamingPolicyCheck.py, Pkg.py, PostCheck.py, SignatureCheck.py,
+ SourceCheck.py, SpecCheck.py, TagsCheck.py, check-install.py,
+ compile.py, rpmdiff, rpmlint, rpmlint.py: Fix indentation,
+ untabify, trim trailing whitespace.
+
+2005-11-24 Ville Skyttä <ville.skytta at iki.fi>
+
+ * ConfigCheck.py: Recognize app-defaults in /usr/share/X11/.
+
+2005-11-22 Ville Skyttä <ville.skytta at iki.fi>
+
+ * COPYING: Update FSF's address, copy current GPL from gnu.org
+ as-is.
+ * FilesCheck.py: Don't warn about dangling symlinks whose target is
+ a file-based dependency.
+ * InitScriptCheck.py: Don't blame the package if our shell
+ expansion for subsys filename fails.
+ * FilesCheck.py: Only non-empty perl *.bs files are unneeded (see
+ DynaLoader.pm), and empty ones are already caught by the empty
+ file check.
+ * FilesCheck.py: Improve accuracy of doc, info, and games path
+ regexps.
+ * rpmlint.py: Improve error message when invoked on non-rpm files.
+ * AUTHORS, README.CVS: Update URLs and addresses.
+ * BinariesCheck.py, DistributionCheck.py, FHSCheck.py,
+ FilesCheck.py, InitScriptCheck.py, MenuCheck.py, SourceCheck.py,
+ SpecCheck.py, TagsCheck.py: Improve and fix spelling of error
+ descriptions.
+
+2005-09-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * Makefile: add a check to avoid releasing with print statements.
+ * Pkg.py: use stderr for error messages.
+ * ChangeLog: Generated by cvs2cl the 11_Sep
+ * FilesCheck.py: added noarch-python-in-64bit-path
+ * MenuCheck.py: added menu-in-wrong-dir
+ * rpmdiff: output details on dependencies
+
+2005-08-17 Thierry Vignaud <tvignaud at mandriva.com>
+
+ * Config.py: add filter rules for harddrake & drakxtools
+
+2005-08-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: check Mandrivalinux in bad words
+ * ChangeLog: Generated by cvs2cl the 10_Aug
+ * rpmlint.spec: real 0.71-1mdk
+ * SpecCheck.py: added prereq-use
+
+2005-08-10 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: added "ang" (anglo-saxon or old-english) to rpmlint
+ * I18NCheck.py: added 'rw'
+
+2005-08-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: 0.71-1mdk
+ * ChangeLog: Generated by cvs2cl the 10_Aug
+ * Pkg.py: handle the new Requires(pre) and co syntax. fixed broken
+ code that was extracting info from headers multiple times.
+ * SpecCheck.py: allow to do a symlink to a configure file without
+ having the configure-without-libdir-spec error. (reported by Hans
+ de Goede)
+ * I18NCheck.py: fixed uninitialized variable (reported by Dan
+ Kegel).
+ * Config.py: added exception for uucp (Bruno Cornec)
+ * InitScriptCheck.py: make the "incoherent subsys" check work
+ properly with trailing " or '. (Ville Skytta)
+ * I18NCheck.py: typo (Ville Skytta)
+ * FilesCheck.py: Brings the verbose message about non-config files
+ in /etc up to date wrt. executables. (Ville Skytta)
+ * ChangeLog: Generated by cvs2cl the 10_Aug
+
+2005-07-06 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: new languages to be recognized as valid: 'se' (was
+ wrongly assumed to be an error for 'sv'), 'yo', 'pa_IN'
+
+2005-07-01 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: added the CECILL licence (requested by S�bastion
+ Savarin).
+
+2005-06-21 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: kernel-uml
+
+2005-06-18 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 18_Jun
+ * rpmlint.spec: 0.70-1mdk
+ * ChangeLog: Generated by cvs2cl the 18_Jun
+ * Pkg.py: use RPMSENSE_SCRIPT_PRE if available to detect prereq.
+
+2005-06-17 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: fix script detection (Guillaume Rousse, bug
+ #15152).
+ * TagsCheck.py: added LaTeX Project Public License (Guillaume
+ Rousse, bug #15928).
+ * PostCheck.py: adds userdel and groupdel to the list of
+ "dangerous" commands. (Ville Skytt�)
+ * BinariesCheck.py, Config.py, FilesCheck.py, README: mandriva
+ * TagsCheck.py: check that the url doesn't contain anymore
+ mandrake.
+ * rpmlint.spec: fixed url
+ * ChangeLog: Generated by cvs2cl the 17_Jun
+
+2005-05-30 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 30_May
+
+2005-05-23 Pixel <pixel at mandriva.com>
+
+ * Config.py: whitelist ash statically-linked-binary /sbin/bsh
+
+2005-04-15 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 15_Apr
+ * rpmlint.spec: 0.69-1mdk
+ * ChangeLog: Generated by cvs2cl the 15_Apr
+ * Config.py, DistributionCheck.py, InitScriptCheck.py, LSBCheck.py,
+ MenuCheck.py, NamingPolicyCheck.py, PostCheck.py, README,
+ TagsCheck.py, check-install.py, rpmdiff, rpmlint.py,
+ rpmlint.spec: Mandriva
+
+2005-04-09 Guillaume Rousse <guillomovitch at mandriva.org>
+
+ * FilesCheck.py: /etc/cron.d is a configuration directory, not a
+ script directory
+
+2005-03-23 Guillaume Rousse <guillomovitch at mandriva.org>
+
+ * FilesCheck.py: intepreters can also live in /sbin or /usr/sbin
+
+2005-03-19 Frédéric Lepied <flepied at mandriva.com>
+
+ * PostCheck.py, README: add non-empty shell check (Ville Skytt�)
+
+2005-03-14 Frédéric Lepied <flepied at mandriva.com>
+
+ * Makefile: don't install compile.pyo (Ville Skytt�).
+
+2005-03-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 10_Mar
+ * rpmlint.spec: fix %prefix
+ * ChangeLog: Generated by cvs2cl the 10_Mar
+ * rpmlint.spec: 0.68-1mdk
+ * ChangeLog: Generated by cvs2cl the 10_Mar
+
+2005-03-04 Thierry Vignaud <tvignaud at mandriva.com>
+
+ * Config.py: add exception for dmraid
+
+2005-03-04 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added recognition of Quechua (qu) language
+
+2005-02-23 Pablo Saratxaga <pablo at mandriva.com>
+
+ * ChangeLog: converted to UTF-8
+
+2005-02-16 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 16_Feb
+ * FilesCheck.py: simplify site_perl regexp (Ville Skytt�).
+
+2005-02-14 Rafael Garcia-Suarez <rgarciasuarez at mandriva.com>
+
+ * Pkg.py: Fix version checking when the epoch is not defined
+ (Michael Scherer)
+
+2005-02-11 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: removed Prefix tag
+ * ChangeLog: Generated by cvs2cl the 11_Feb
+ * rpmlint.spec: 0.67-1mdk
+ * ChangeLog: Generated by cvs2cl the 11_Feb
+ * SpecCheck.py: added hardcoded-packager-tag, hardcoded-prefix-tag
+ and redundant-prefix-tag checks (Guillaume Rousse, bug #12725).
+ * Config.py: added wrong-script-interpreter, non-executable-script,
+ script-without-shellbang, wrong-script-end-of-line-encoding and
+ wrong-file-end-of-line-encoding for 10.2 policy.
+ * FilesCheck.py: added wrong-script-interpreter,
+ non-executable-script, script-without-shellbang,
+ wrong-script-end-of-line-encoding and
+ wrong-file-end-of-line-encoding. (Guillaume Rousse, bug #12725).
+ * TagsCheck.py: added the 'Graphical desktop/Xfce' group (bug
+ #13141).
+ * rpmdiff: filter the provides on name-version-release for the
+ package itself.
+
+2005-02-08 Rafael Garcia-Suarez <rgarciasuarez at mandriva.com>
+
+ * Pkg.py: Make check_versioned_dep ignore epoch when comparing
+ versions (patch by Michael Scherer)
+
+2005-02-08 Thierry Vignaud <tvignaud at mandriva.com>
+
+ * Config.py: do not complain about explicit dependancy on liblua5
+ (else b/c of buggy lua, lua users accepted either lua4 or lua5
+ thus resulting in linkinkg issues at runtime)
+ * Config.py: update drakconf rule
+
+2005-02-07 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: added Design Sciences License (S�bastien Savarin)
+ fixed Lucent Public License
+
+2005-01-30 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: add exceptions for dkms packages
+
+2005-01-25 Gwenole Beauchesne <gbeauchesne at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 25_Jan
+ * rpmlint.spec: 0.66-1mdk
+ * Config.py: exceptions for %multiarch policy
+ * ChangeLog: Generated by cvs2cl the 25_Jan
+ * ChangeLog: Generated by cvs2cl the 25_Jan
+
+2005-01-24 Rafael Garcia-Suarez <rgarciasuarez at mandriva.com>
+
+ * FilesCheck.py: Add a new warning for perl modules installed under
+ site_perl instead of vendor_perl
+ * FilesCheck.py: Perl modules go under vendor_perl, not site_perl
+
+2005-01-21 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 21_Jan
+ * rpmlint.spec: oops put the right version
+ * ChangeLog: Generated by cvs2cl the 21_Jan
+ * rpmlint.spec: 0.65-1mdk
+ * ChangeLog: Generated by cvs2cl the 21_Jan
+
+2005-01-19 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: recognition of some more languages (bug #12216)
+
+2005-01-14 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added language codes (nr, nso, tn, ts) of South
+ Africa that have efforts on creating localizations for them
+ (source: http://www.translate.org.za/ )
+
+2005-01-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: don't report non-conffile-in-etc on executable.
+ * ChangeLog: Generated by cvs2cl the 10_Jan
+
+2005-01-05 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added dir-or-file-in-var-local to mdk10.2 policy
+ * TagsCheck.py: - E:V-R should be consistent in package and
+ changelog regardless if use_epoch is set or not (Ville Skytt�). -
+ Spelling fixes (Ville Skytt�).
+ * Makefile: - Include rpmlint.bash-completion in dist tarball. -
+ Don't install compile.py.
+ * I18NCheck.py: - "se" -> "sv" in I18NCheck (Ville Skytt�).
+ * FilesCheck.py: - Flag installing files to /var/local as an error
+ (Ville Skytt�). - Improved perl temp file regexp (Ville Skytt�).
+ - Extended CVS internal file regexp to cover Subversion and GNU
+ Arch (Ville Skytt�).
+ * Config.py: added missing-menu-command to policy
+ * MenuCheck.py: added missing-menu-command (Michael Scherer)
+
+2004-12-30 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 30_Dec
+ * FilesCheck.py: removed unused variables
+ * rpmlint.spec: 0.64-1mdk
+ * ChangeLog: Generated by cvs2cl the 30_Dec
+ * rpmlint.py: fix double import
+ * FilesCheck.py: Check that pkg-config files and config script are
+ in devel packages (Guillaume Rousse, bug #12662).
+ * FilesCheck.py: added htaccess-file check (Guillaume Rousse, bug
+ #12661).
+ * Config.py: first pass to update load_policy.
+ * FilesCheck.py: added executable-marked-as-config-file check.
+ * TagsCheck.py: added requires-on-release check
+ * Makefile: clean the build
+
+2004-12-19 Rafael Garcia-Suarez <rgarciasuarez at mandriva.com>
+
+ * TagsCheck.py: The Lucent Public Licence (Plan9) is
+ opensource.org-approved.
+
+2004-12-18 Guillaume Rousse <guillomovitch at mandriva.org>
+
+ * rpmlint.bash-completion: initial import
+ * FilesCheck.py: typo
+
+2004-12-06 Rafael Garcia-Suarez <rgarciasuarez at mandriva.com>
+
+ * SpecCheck.py: Clarify the use-of-RPM_SOURCE_DIR message
+ explanation
+
+2004-12-05 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: lookup .cmi files as devel files too (Guillaume
+ Rousse) [bug #12186].
+ * ChangeLog: Generated by cvs2cl the 05_Dec
+ * rpmlint.spec: 0.63-1mdk
+ * ChangeLog: Generated by cvs2cl the 05_Dec
+ * FilesCheck.py: add /usr/lib/menu to STANDARD_DIRS (Michael)
+ * ChangeLog: Generated by cvs2cl the 05_Dec
+ * Config.py: added exceptions for kernel-source.* on
+ devel-file-in-non-devel-package reports.
+ * FilesCheck.py: added dir-or-file-in-usr-local (Michael Scherer).
+ * BinariesCheck.py: allow soname in the form libfoo-X.Y.Z.so too
+ (Guillaume Rousse) [bug #12522].
+ * NamingPolicyCheck.py: make exception to the
+ python/perl/ruby/ocaml naming policy when the package contains
+ executable (Guillaume Rousse) [bug #12521].
+ * I18NCheck.py: Don't tag .mo in webapps (Guillaume Rousse) [bug
+ #12186]
+ * TagsCheck.py: added summary-ended-with-dot (Guillaume Rousse)
+ [bug #12520]
+
+2004-12-04 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: cleanup exceptions (Michael Scherer)
+ * rpmlint.py: exit on C-c (Michael Scherer)
+ * ChangeLog: Generated by cvs2cl the 05_Dec
+
+2004-11-28 Guillaume Rousse <guillomovitch at mandriva.org>
+
+ * NamingPolicyCheck.py: ocaml naming policy
+
+2004-11-23 Frédéric Lepied <flepied at mandriva.com>
+
+ * PostCheck.py: doc for postin-without-ghost-file-creation (Pascal
+ Terjan)
+
+2004-09-22 Michael Scherer <misc at mandriva.org>
+
+ * Config.py: - some code factorisation - fix addCheck ( was not
+ useable since it was a tuple instead of a list )
+
+2004-08-30 Rafael Garcia-Suarez <rgarciasuarez at mandriva.com>
+
+ * FilesCheck.py: Minor nit in the regexp that checks that perl
+ module rpms should come without the source tarball MANIFEST.
+
+2004-08-27 Frederic Crozat <fcrozat at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 27_Aug
+ * rpmlint.spec: Fix missing capitalization
+ * MenuCheck.py: Fix missing capitalization
+
+2004-08-25 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 25_Aug
+ * rpmlint.spec: 0.61-1mdk
+ * ChangeLog: Generated by cvs2cl the 25_Aug
+
+2004-08-19 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added recognition of "pa" (Punjabi) language code
+
+2004-08-17 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 17_Aug
+
+2004-08-17 Frederic Crozat <fcrozat at mandriva.com>
+
+ * MenuCheck.py: Fix menu capitalization
+
+2004-08-03 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: added some more language codes
+
+2004-08-03 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 03_Aug
+ * rpmlint.spec: 0.60-1mdk
+ * ChangeLog: Generated by cvs2cl the 03_Aug
+ * TagsCheck.py: added obsolete-on-name
+
+2004-07-29 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exceptions for perl and dyalog on
+ devel-file-in-non-devel-package
+
+2004-07-27 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 27_Jul
+
+2004-07-13 Rafael Garcia-Suarez <rgarciasuarez at mandriva.com>
+
+ * FilesCheck.py: Add a warning for MANIFEST* files in perl modules
+
+2004-07-08 Frédéric Lepied <flepied at mandriva.com>
+
+ * BinariesCheck.py: add ruby exceptions like perl and python.
+ * ChangeLog: Generated by cvs2cl the 08_jui
+
+2004-07-05 Michael Scherer <misc at mandriva.org>
+
+ * TagsCheck.py: - added useless-explicit-provides ( check if there
+ is 2 times the same provides )
+
+2004-05-17 Michael Scherer <misc at mandriva.org>
+
+ * rpmlint.py: - fix -I, rpmlint didn't work f run without the
+ option.
+
+2004-05-07 Michael Scherer <misc at mandriva.org>
+
+ * rpmlint.py: added option -I, to print description of the error
+ passed on commandline
+
+2004-05-03 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added Furlan language code (fur)
+
+2004-04-30 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 30_Apr
+ * rpmlint.spec: 0.59-1mdk
+ * ChangeLog: Generated by cvs2cl the 30_Apr
+
+2004-04-30 Michael Scherer <misc at mandriva.org>
+
+ * TagsCheck.py: check if a package requires a interpreter in
+ /usr/local/bin/
+
+2004-04-19 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 19_Apr
+ * Config.py, DistributionCheck.py, InitScriptCheck.py, LSBCheck.py,
+ NamingPolicyCheck.py, PostCheck.py, README, TagsCheck.py,
+ check-install.py: Mandrakelinux (Robert Vojta)
+
+2004-03-22 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: some more languages recognized
+
+2004-03-19 Frédéric Lepied <flepied at mandriva.com>
+
+ * InitScriptCheck.py: chack that the initscript is executable
+ (Michael Scherer)
+
+2004-03-12 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 12_Mar
+ * rpmlint.spec: 0.58-1mdk
+ * ChangeLog: Generated by cvs2cl the 12_Mar
+ * TagsCheck.py: The Mandrake word is forbidden alone.
+
+2004-03-09 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: consolehelper is in usermode-consoleonly
+ * MenuCheck.py: fixed missing comma
+
+2004-03-01 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: fixed missing comma (Michael Scherer)
+
+2004-02-20 David Baudens <baudens at mandriva.com>
+
+ * MenuCheck.py: Add mission "More applications/Other" section
+
+2004-02-12 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 12_Feb
+ * rpmlint.spec: 0.57.1-1mdk
+ * PostCheck.py: removed buggy trigger code
+ * ChangeLog: Generated by cvs2cl the 12_Feb
+ * rpmlint.spec: 0.57-1mdk
+ * ChangeLog: Generated by cvs2cl the 12_Feb
+ * FilesCheck.py: no-dependancy-on => no-dependency-on fixed perl
+ check (Michael Scherer)
+ * Config.py: dependancy => dependency
+ * TagsCheck.py: added Education as a valid group
+ * PostCheck.py: makes postcheck not to whine about ghost files that
+ are created by %triggerin scripts in addition to %pre and %post.
+ (Ville Skytt�)
+
+ Additionally, somewhat improved documentation on
+ one-line-commands, using "should" instead of "must" since AFAICS
+ %post -p <command> cannot be used if <command> needs parameters.
+ (Ville Skytt�)
+
+2004-02-11 David Baudens <baudens at mandriva.com>
+
+ * MenuCheck.py: Add missing entries
+
+2004-02-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 10_Feb
+ * rpmlint.spec: 0.56-1mdk
+ * ChangeLog: Generated by cvs2cl the 10_Feb
+
+2004-02-10 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: changed uz@Latn locale
+
+2004-02-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * MenuCheck.py: added missing ',' (Michael Scherer)
+
+2004-02-09 Frédéric Lepied <flepied at mandriva.com>
+
+ * NamingPolicyCheck.py: Better error message. Fixed bug when no
+ files are present. (Michael Scherer)
+ * rpmlint.py: more robust processing when scanning a directory
+ (Michael Scherer)
+
+ force to have / in directory names to allow to have directory
+ with the same name as an installed package (Michael Scherer)
+
+2004-01-28 Frédéric Lepied <flepied at mandriva.com>
+
+ * MenuCheck.py: removed duplicated Physics entry
+
+2004-01-27 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 27_Jan
+ * rpmlint.spec: 0.55-1mdk
+ * MenuCheck.py: final menu structure
+
+2004-01-23 David Baudens <baudens at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 23_Jan
+ * ChangeLog, MenuCheck.py, rpmlint.spec: Replace old menu structure
+ by new menu structure
+
+2004-01-19 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 19_Jan
+ * rpmlint.spec: 0.54-1mdk
+ * ChangeLog: Generated by cvs2cl the 19_Jan
+ * rpmlint.py: fixed main loop for argument testing on files and
+ directories
+ * Pkg.py: in __getitem__ return None instead of [] (change in rpm
+ 4.2.2)
+
+2004-01-15 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.py: updated directory support (Michael Scherer)
+
+2004-01-14 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.py: Allow to pass a directory as an argument (Michael
+ Scherer)
+
+2004-01-13 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added new language codes
+
+2003-12-31 Frédéric Lepied <flepied at mandriva.com>
+
+ * PostCheck.py: allow the [[:space:]] construction (bug #6466)
+ (Luca Berra)
+ * Config.py: added exceptions for postfix (Luca Berra)
+ * BinariesCheck.py: in only-non-binary-in-usr-lib don't report
+ directories (Michael Scherer)
+
+2003-12-26 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: mispelled-macro check (Michael Scherer)
+
+2003-12-22 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 22_Dec
+ * rpmlint.spec: 0.53-1mdk
+ * TagsCheck.py: added *@mandrake.org as a correct packager tag.
+ * ChangeLog: Generated by cvs2cl the 22_Dec
+ * ZipCheck.py: handle exception while reading zip file
+ * FilesCheck.py: log-files-without-logrotate (Michael Scherer)
+ * Config.py, README, ZipCheck.py: new check ZipCheck (Ville Skytt�)
+ * SpecCheck.py: check lib packages only they start by lib (Pixel)
+ * FilesCheck.py, InitScriptCheck.py, LSBCheck.py, MenuCheck.py,
+ TagsCheck.py: spelling fixes (Ville Skytt�)
+ * README: KernelModuleRPMsOK defaults to 1
+ * README, config: added KernelModuleRPMsOK
+ * FilesCheck.py: non-standard-executable-perm was never run (Ville
+ Skytt�)
+ * FilesCheck.py: added checks for correct depmod calls in
+ scriptlets (Eric Sandeen)
+
+2003-12-11 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exception for busybox
+
+2003-11-20 Nicolas Planel <nplanel at mandriva.com>
+
+ * Config.py, rpmlint.spec: add policycoreutils exception
+
+2003-11-19 Nicolas Planel <nplanel at mandriva.com>
+
+ * Config.py, rpmlint.spec: added statically-linked-binary exception
+ for udev
+
+2003-10-01 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exceptions for kernel-source
+ * InitScriptCheck.py: allow multiple spaces before chkconfig (Eric
+ Sandeen)
+
+2003-09-05 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 05_Sep
+ * rpmlint.spec: 0.52-1mdk
+ * ChangeLog: Generated by cvs2cl the 05_Sep
+ * TagsCheck.py: added explicit-lib-dependency check
+ * Config.py: added exceptions for explicit-lib-dependency and
+ invalid-build-requires do not report errors on debug packages
+
+2003-09-04 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: added invalid-build-requires
+
+2003-08-05 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: don't depend on rpm-devel anymore
+ * ChangeLog: Generated by cvs2cl the 05_Aug
+ * rpmlint.spec: 0.51.1-1mdk
+ * ChangeLog: Generated by cvs2cl the 05_Aug
+ * TagsCheck.py: don't check devel-dependency on source package
+ * rpmlint.py: 2003
+ * NamingPolicyCheck.py: corrected info reports
+
+2003-08-05 Gwenole Beauchesne <gbeauchesne at mandriva.com>
+
+ * SpecCheck.py: Add /usr/lib/hotplug to hardcoded-library-path
+ exceptions
+
+2003-08-04 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 04_Aug
+ * rpmlint.spec: 0.51-1mdk
+ * ChangeLog: Generated by cvs2cl the 04_Aug
+ * TagsCheck.py: added devel-dependency check
+ * Config.py: escape ++ in addFilter correct load_policy
+ * Config.py: corrected load_policy
+ * Config.py: added devel-dependancy wrong warnings
+ * Config.py: added exceptios for ppp (Guillaume Rousse)
+
+2003-07-30 Pablo Saratxaga <pablo at mandriva.com>
+
+ * TagsCheck.py: fixed English typo
+
+2003-07-22 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 22_Jul
+ * SpecCheck.py: allow the following form for a patch instruction:
+ %patch -P 1 (request from Stephan Kulow)
+ * NamingPolicyCheck.py: first version from Michael Scherer
+ * Pkg.py: in shell_var_value escape the var name to avoid a
+ backtrace (Ville Skytt�)
+ * Config.py: don't warn on -debug packages (Ville Skytt�)
+ * InitScriptCheck.py: added init-script-name-with-dot check
+ (Michael Scherer)
+
+2003-06-30 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added 'mn' to list of languages
+
+2003-06-29 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added some more languages
+
+2003-05-09 Gwenole Beauchesne <gbeauchesne at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 09_May
+ * rpmlint.spec: 0.50-1mdk
+ * ChangeLog: Generated by cvs2cl the 09_mai
+ * SpecCheck.py: Make %ifarch-applied-patch a warning
+
+2003-05-09 Frédéric Lepied <flepied at mandriva.com>
+
+ * Makefile: install rpmdiff in /usr/bin
+ * rpmdiff: load Pkg from /usr/share/rpmlint to be able to be
+ installed anywhere
+
+2003-05-08 Gwenole Beauchesne <gbeauchesne at mandriva.com>
+
+ * SpecCheck.py: Hanle %ifnarch in ifarch_regex too. Aka. don't
+ suddenly think about rpmlint prior to taking a bath.
+ * ChangeLog: Generated by cvs2cl the 08_mai
+ * SpecCheck.py: check for hardcoded-library-path exceptions only on
+ the actual suspected hardcoded library path
+ * SpecCheck.py: add %ifarch-applied-patch check
+ * SpecCheck.py: Add hardcoded-library-path exceptions
+
+2003-05-07 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: changed Serbian lang codes to match what is now
+ used in Gnome and KDE
+
+2003-05-05 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: corrected epoch tests (Ville Skytt�)
+ * FilesCheck.py: allow empty __init__.py (St�fane Fermigier)
+ * TagsCheck.py: added Zope Public License
+ * Config.py: added exceptions for hidden-file-or-dir check (Michael
+ Scherer)
+ * FilesCheck.py: added hidden-file-or-dir check (Michael Scherer)
+
+2003-04-30 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added an exception for gconf schemas
+
+2003-04-29 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 29_Apr
+ * rpmlint.spec: 0.49-1mdk
+ * ChangeLog: Generated by cvs2cl the 29_Apr
+ * Config.py: added an exception for tpctl
+
+2003-04-23 Frédéric Lepied <flepied at mandriva.com>
+
+ * Pkg.py, SignatureCheck.py, rpmlint.py: rpm 4.2 support (Ville
+ Skytt�)
+
+2003-03-25 Frédéric Lepied <flepied at mandriva.com>
+
+ * README: Spelling fixes, new options: UseEpoch, ValidSrcPerms
+ (Ville Skytt�).
+ * TagsCheck.py: Handle nosrc packages properly, add required Epoch
+ functionality (Ville Skytt�).
+ * SourceCheck.py: Made valid source permissions configurable (Ville
+ Skytt�).
+ * Pkg.py: Added isNoSource()
+
+2003-02-19 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Fixed Maori ('mi', was wrongly coded as 'ma'),
+ Added various Indic languages that have Gnome/KDE translations,
+ Added Xhosa (xh), changed Ganda code lug -> lg (we standardize on
+ two letter codes)
+
+2003-01-31 Frédéric Lepied <flepied at mandriva.com>
+
+ * Pkg.py: added support for rpm 4.2
+
+2003-01-17 Gwenole Beauchesne <gbeauchesne at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 17_Jan
+ * rpmlint.spec: 0.48-2mdk
+ * ChangeLog: Generated by cvs2cl the 17_jan
+ * FilesCheck.py: Errour out about outside-libdir-files only if it
+ concerns a library package. This is heuristically determined on
+ the package name as '^(lib|.+-libs)'.
+ * BinariesCheck.py: Add lib64 paths
+
+2003-01-16 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 17_Jan
+ * rpmlint.spec: 0.48-1mdk
+ * ChangeLog: Generated by cvs2cl the 16_Jan
+ * rpmlint.py: added a way to load an alternative config file.
+ * SpecCheck.py: added lib-package-without-%mklibname
+ * FilesCheck.py: added outside-libdir-files
+
+2003-01-09 Chmouel Boudjnah
+
+ * Config.py: Add modutils rules.
+
+2003-01-07 Chmouel Boudjnah
+
+ * Config.py: fix pcmcia-cs regexp.
+
+2002-12-19 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: exception for wvdial
+
+2002-12-06 Frédéric Lepied <flepied at mandriva.com>
+
+ * SpecCheck.py: don't parse changelog section to find errors and
+ correct source_dir_regex.
+
+2002-11-19 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added 'en_US' as valid locale name
+
+2002-11-07 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exceptions for extipl, ocamltk and drakconf
+
+2002-10-14 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added "lug" (Luganda) language as a valid code for
+ translations
+
+2002-08-21 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added recognition of some more language codes
+ (Gnome includes some translations in those languages now)
+
+2002-08-08 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 08_Aug
+ * rpmlint.spec: 0.47-1mdk
+ * ChangeLog: Generated by cvs2cl the 08_Aug
+ * setuplist.py: split old and new users/groups.
+ * PostCheck.py: check rpm-helper prereq.
+ * MenuCheck.py: add default values from Config.
+ * InitScriptCheck.py: allow to add/del service with rpm-helper
+ scripts.
+ * FilesCheck.py: use default values from Config.
+ * Config.py: added handling of default values.
+ * BinariesCheck.py: added /usr/lib/bonobo to no binary in /usr/lib
+ exceptions
+
+2002-07-23 Gwenole Beauchesne <gbeauchesne at mandriva.com>
+
+ * FilesCheck.py: Add lib64 directories
+ * FHSCheck.py: Add lib64 as standard subdir in /usr (that's the
+ /lib<qual> part of FHS)
+
+2002-07-12 Frédéric Lepied <flepied at mandriva.com>
+
+ * setuplist.py: Sync with version 2.2.0-28mdk of setup package
+
+2002-07-11 Frédéric Lepied <flepied at mandriva.com>
+
+ * PostCheck.py: added perl to dangerous command check trigger
+ scripts too
+ * BinariesCheck.py: corrected wrong loop for /usr/lib check
+
+2002-06-19 Pablo Saratxaga <pablo at mandriva.com>
+
+ * ChangeLog, I18NCheck.py: Added 'zh_HK' recognition
+
+2002-06-14 Frédéric Lepied <flepied at mandriva.com>
+
+ * PostCheck.py: corrected prereq test
+
+2002-06-07 Gwenole Beauchesne <gbeauchesne at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 07_jun
+ * SpecCheck.py: Also check for %{?_prefix}?/lib references
+
+2002-06-04 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 04_Jun
+ * Makefile: remove cvs commit from cvstag target
+ * ChangeLog: Generated by cvs2cl the 04_Jun
+ * rpmlint.spec: 0.46-1mdk
+ * ChangeLog: Generated by cvs2cl the 04_Jun
+ * Config.py: added exceptions for no-binary
+ * README: added UsrLibBinaryException
+ * BinariesCheck.py: added UsrLibBinaryException option and
+ exception no-binary for multiple packages.
+ * Pkg.py: make all extracted files accessible.
+ * BinariesCheck.py: added no-binary and only-non-binary-in-usr-lib
+
+2002-06-03 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 03_Jun
+ * rpmlint.spec: 0.45-1mdk
+ * Config.py: added new check from Gwenole in mdk9.0 policy
+ * ChangeLog: Generated by cvs2cl the 03_Jun
+
+2002-06-01 Gwenole Beauchesne <gbeauchesne at mandriva.com>
+
+ * SpecCheck.py: - Add configure-without-libdir-spec check - Fix
+ typos in previous hardcoded-library-path description
+ * Config.py: Revert last change
+ * Config.py: Add hardcoded-library-path to mdk9.0 policy
+ * SpecCheck.py: Add hardcoded-library-path check
+
+2002-05-29 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 29_May
+ * rpmlint.spec: 0.44-1mdk
+ * ChangeLog: Generated by cvs2cl the 29_May
+ * Config.py: added
+ non-root-user-log-file|non-root-group-log-file|non-ghost-file for
+ mdk9.0 policy.
+ * FilesCheck.py: added non-ghost-file check
+
+2002-05-14 Frédéric Lepied <flepied at mandriva.com>
+
+ * Makefile: added AUTHORS
+ * AUTHORS: first version
+
+2002-05-02 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: added non-root-user-log-file and
+ non-root-group-log-file.
+
+2002-05-01 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 01_May
+ * TagsCheck.py: remove debug trace
+ * rpmlint.spec: 0.43-1mdk
+ * ChangeLog: Generated by cvs2cl the 01_May
+ * Config.py: added no-prereq-on for mdk9.0 policy
+ * TagsCheck.py: change non-coherent-filename to check all the
+ filename
+ * PostCheck.py: added no-prereq-on
+
+2002-04-24 Frédéric Lepied <flepied at mandriva.com>
+
+ * BinariesCheck.py: corrected bad report on libgimp1.2_1
+ * INSTALL: described policy
+ * Config.py: added load_policy
+ * Makefile: retrieve the version and release in a more generic way.
+
+ put the version and policy at install time.
+ * TagsCheck.py: check that the architecture is coherent with the
+ file name.
+ * rpmlint.py: added a --policy option.
+
+ the version is set at install time.
+ * Pkg.py: create a fake filename to satisfy some checks on the
+ filename for InstalledPkg objects.
+
+2002-04-17 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: build one regex from all the exceptions
+
+ added exception for avifile-samples
+
+2002-03-08 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: 0.42-2mdk
+ * Makefile: install rpmdiff in LIBDIR
+
+2002-03-04 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 04_Mar
+ * Makefile: added rpmdiff
+ * rpmlint.py, rpmlint.spec: 0.42
+ * ChangeLog: Generated by cvs2cl the 03_Mar
+ * README: added PerlVersionTrick
+ * BinariesCheck.py: handle new file attributes
+ * FilesCheck.py: allow perl and python dependencies to be on
+ perl-base and python-base.
+
+ manage Mandrake perl versionning.
+ * Pkg.py: added md5, mtime and rdev to file attributes
+
+2002-02-26 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added 'mt' to recognized locales
+
+2002-02-23 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmdiff: first version
+
+2002-02-20 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exceptions for shorewall and DansGuardian
+
+2002-02-13 Frédéric Lepied <flepied at mandriva.com>
+
+ * PostCheck.py: add descriptions for spurious-bracket-in-.
+
+2002-02-11 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exception for logrotate entry for hylafax.
+
+2002-02-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 09_Feb
+ * setuplist.py: Sync with version 2.2.0-23mdk of setup package
+ * rpmlint.py, rpmlint.spec: 0.41
+ * Makefile: pychecker.sh => pychecker
+
+2002-02-07 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 07_Feb
+ * Pkg.py: added check_versioned_dep
+ * FilesCheck.py: check dependency on the right version of the
+ interpreter for python and perl modules.
+ * Config.py: exceptions for perl, python and nut.
+
+2002-02-01 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exception for fetchmail
+
+2002-01-29 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: added W3C Licence
+ * Config.py: add exception for no-%clean-section'
+
+2002-01-25 Frédéric Lepied <flepied at mandriva.com>
+
+ * SpecCheck.py: report missing %clean section.
+
+2002-01-14 Frédéric Lepied <flepied at mandriva.com>
+
+ * setuplist.py: Sync with version 2.2.0-18mdk of setup package
+
+2002-01-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 10_Jan
+ * rpmlint.py, rpmlint.spec: 0.40-1mdk
+ * ChangeLog: Generated by cvs2cl the 10_Jan
+ * Config.py, MenuCheck.py, README: icons for menu are now png
+ * TagsCheck.py: added libsafe.so as an invalid Requires.
+ * Makefile: compile.py takes an extra argument now.
+
+2002-01-07 Chmouel Boudjnah
+
+ * setuplist.py: Sync with version 2.2.0-18mdk of setup package
+ * Config.py: Add exceptions for wine
+
+2002-01-03 Frédéric Lepied <flepied at mandriva.com>
+
+ * compile.py: pass the destination directory to avoid change when
+ the byte compilation is checked.
+
+2001-12-11 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exception for kdebase.
+
+2001-12-04 Chmouel Boudjnah
+
+ * setuplist.py: Sync with version 2.2.0-16mdk of setup package
+
+2001-11-30 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 30_Nov
+ * rpmlint.spec: 0.39-2mdk
+ * ChangeLog: Generated by cvs2cl the 30_Nov
+ * BinariesCheck.py: search references to home or tmp in
+ /usr/lib/pkgconfig/ files.
+ * FilesCheck.py: .nosearch files are allowed to have a zero length.
+ * Config.py: added an exception for use-of-RPM_SOURCE_DIR in the
+ kernel package.
+
+2001-11-28 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: exceptions for ;getty
+
+2001-11-27 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: group "Development/KDE and QT" renamed
+ "Development/KDE and Qt"
+
+2001-11-27 Chmouel Boudjnah
+
+ * Config.py: Add some exceptions for zero-lenght files in setup
+ packages.
+ * setuplist.py: Sync with version of setup package
+ * Config.py: mandrake_consmap doen't have a status and reload entry
+ which is normal.
+
+2001-11-26 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: exception for libsane
+
+2001-11-25 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exceptions for zapping.
+ * ChangeLog: Generated by cvs2cl the 25_nov
+ * rpmlint.py, rpmlint.spec: 0.39-1mdk
+ * ChangeLog: Generated by cvs2cl the 25_nov
+ * LSBCheck.py, PostCheck.py, check-install.py: Linux-Mandrake =>
+ Mandrake Linux
+ * TagsCheck.py: corrected regexp to check devel provides.
+ * InitScriptCheck.py: added incoherent-init-script-name check.
+
+ expand shell variable in incoherent-subsys check.
+ * Pkg.py: added substitute_shell_vars and shell_var_value
+ functions.
+
+2001-11-23 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: use list imported from the setup package for users
+ and groups (from setuplist.py).
+ * README: added InvalidRequires to the list of options.
+ * TagsCheck.py: added the new check invalid-dependency.
+
+2001-11-22 Frédéric Lepied <flepied at mandriva.com>
+
+ * setuplist.py: setup 2.2.0-13mdk
+
+2001-11-21 Chmouel Boudjnah
+
+ * ChangeLog: Generated by cvs2cl the 21_Nov
+ * PostCheck.py: Don't print error about percent if post-script has
+ a %%.
+
+2001-11-19 Frédéric Lepied <flepied at mandriva.com>
+
+ * SpecCheck.py: check also %_sourcedir.
+
+2001-11-16 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 16_Nov
+
+2001-11-14 Frédéric Lepied <flepied at mandriva.com>
+
+ * AbstractCheck.py, BinariesCheck.py, ConfigCheck.py,
+ DistributionCheck.py, FHSCheck.py, FilesCheck.py, I18NCheck.py,
+ InitScriptCheck.py, LSBCheck.py, MenuCheck.py, Pkg.py,
+ PostCheck.py, README, SignatureCheck.py, SourceCheck.py,
+ SpecCheck.py, TagsCheck.py, check-install.py, rpmlint.py:
+ corrected warnings reported by pychecker
+ * Makefile: added a verify target to use pychecker.
+ * SpecCheck.py: Check that the BuildRoot tag doesn't contain a
+ hardcoded path
+
+2001-11-13 Frédéric Lepied <flepied at mandriva.com>
+
+ * BinariesCheck.py: check if .la files contain tmp or home
+ references.
+
+2001-11-13 Chmouel Boudjnah
+
+ * Config.py: Fix regexp with emacs.*el
+ * Config.py: Don't do dependences on locale-el on all emacs and
+ xemacs el package.
+
+2001-11-09 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exceptions for iptable and old menu files.
+ * PostCheck.py: check that RPM_BUILD_ROOT or RPM_BUILD_DIR isn't
+ used
+
+2001-10-30 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 30_Oct
+ * rpmlint.py, rpmlint.spec: 0.38-1mdk
+ * ChangeLog: Generated by cvs2cl the 30_Oct
+ * Config.py: added incoherent-version-in-name exceptions.
+
+2001-10-29 Frédéric Lepied <flepied at mandriva.com>
+
+ * README, TagsCheck.py: added mandrake.org as a valid build host.
+ * BinariesCheck.py: check that major version is present in package
+ name.
+
+2001-10-27 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: check that regular files haven't a zero size.
+ * Pkg.py: add the size to the record about a file
+
+2001-10-25 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: exceptions for Mesa libification
+ * I18NCheck.py: only check binary packages
+
+2001-10-24 Frédéric Lepied <flepied at mandriva.com>
+
+ * SpecCheck.py: don't allow space before tag name
+
+2001-10-23 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: added mandrakeexpert url as a valid Packager field.
+
+2001-10-19 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: allow space after the release in a changelog entry.
+
+2001-10-18 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: updated list from opensource.org and added non
+ opensource ones.
+
+2001-10-17 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: report a warning if no url tag is defined.
+ * Config.py: exception for fetchmail-daemon.
+
+2001-10-17 Pixel <pixel at mandriva.com>
+
+ * Config.py: - add ocaml-lablgtk and camlp4 in devel packages - fix
+ the setuid filter for /usr/bin/sperl5.6.1
+
+2001-10-16 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 16_Oct
+ * README: added descriptions for ForbiddenWords and ValidBuildHost.
+ * SourceCheck.py: correct boolean expression for strange-permission
+ * ChangeLog: Generated by cvs2cl the 16_Oct
+ * rpmlint.py, rpmlint.spec: 0.37-1mdk
+ * ChangeLog: Generated by cvs2cl the 16_Oct
+ * SourceCheck.py: allow 0755 as a valid mode for source.
+ * Config.py: various exceptions
+ * ChangeLog: Generated by cvs2cl the 16_Oct
+ * TagsCheck.py: added invalid-word check in description and
+ summary. added invalid-buildhost check.
+ * FilesCheck.py: added .cvsignore to the list of cvs-internal-file.
+ * BinariesCheck.py: check for new style of pic sections.
+
+2001-10-11 Chmouel Boudjnah
+
+ * ChangeLog: Generated by cvs2cl the 11_Oct
+
+2001-10-10 Chmouel Boudjnah
+
+ * FilesCheck.py: Check if kernel modules are in the kernel package.
+
+2001-10-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * PostCheck.py: track command with full path too.
+
+2001-10-09 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: exceptions for hylafax
+ * Config.py: exceptions for mkinitrd automake gettext chromium
+ webmin methane apache-suexec
+
+2001-10-05 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: added squid group and user.
+ * BinariesCheck.py: Warn for man pages without version in library
+ packages.
+
+2001-10-02 Chmouel Boudjnah
+
+ * DistributionCheck.py: More explicit path regexp check for info
+ files.
+
+2001-09-29 Chmouel Boudjnah
+
+ * Config.py: execptions for shadow-utils package.
+
+2001-09-28 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 28_Sep
+ * rpmlint.spec: 0.36-1mdk
+ * ChangeLog: Generated by cvs2cl the 28_Sep
+ * MenuCheck.py: check if a menu file is executable.
+ * PostCheck.py: check if /tmp or /var/tmp is used.
+
+ check if update-menus is called without a menu file.
+ * Config.py: don't make exception if no_exception is set.
+ * rpmlint.py: added -n/--noexception option to display all the
+ errors/warnings without exceptions from Config.
+ * TagsCheck.py: added the https address as a valid one.
+
+2001-09-28 Chmouel Boudjnah
+
+ * Config.py: ipsec.secrets is normal to be not readable.
+ * Config.py: mandrake_consmap like mandrake_(fistime|everytime)
+ * Config.py: Add exeptions for traceoute6 and ping6 setuid.
+ * Config.py: Add nfs-utils execptions.
+
+2001-09-14 Frédéric Lepied <flepied at mandriva.com>
+
+ * PostCheck.py: ghost-files-without-postun =>
+ ghost-files-without-postin
+
+2001-09-13 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exceptions for portsentry.
+
+2001-09-04 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: added /etc/logrotate.d entry check.
+
+2001-08-24 Chmouel Boudjnah
+
+ * Config.py: Add execpt for initscripts.
+
+2001-08-23 Chmouel Boudjnah
+
+ * Config.py: iputils setuid ping6/tracroute6, safe as they drop it
+ VERY early.
+
+2001-08-21 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 21_Aug
+ * rpmlint.spec: 0.35-1mdk
+ * rpmlint.py: 0.35
+ * ChangeLog: Generated by cvs2cl the 21_Aug
+ * BinariesCheck.py: Make libraries not linked against libc errors
+ and not warnings. (Bill Nottingham)
+
+ libc doesn't need to be linked against libc, and the dynamic
+ linker doesn't need dependeny information. (Bill Nottingham)
+
+ Fix some of the library checks to be more correct. (Bill
+ Nottingham)
+ * TagsCheck.py: added a check on obsoleted packages not provided.
+ * Pkg.py: factorize code for obsoletes/provides/requires/prereq.
+
+2001-08-20 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: check non readable files.
+ * PostCheck.py: check ~/ instead of ~ to allow awk scripts not to
+ give false reports.
+ * MenuCheck.py: added a check for / in menu titles.
+
+2001-08-13 Chmouel Boudjnah
+
+ * FilesCheck.py: Add wine groups.
+
+2001-08-11 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: Added 'bs' as a valid language code name
+
+2001-08-04 Chmouel Boudjnah
+
+ * Config.py: Add a filter for reiserfsprogs
+ dangling-relative-symlink /sbin/fsck.reiserfs ../bin/true
+
+2001-07-18 Frederic Crozat <fcrozat at mandriva.com>
+
+ * MenuCheck.py: Add missing menu entries
+
+2001-07-15 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 15_Jul
+ * Config.py: added exceptions for egcs.
+ * rpmlint.spec: 0.34-1mdk
+ * rpmlint.py: added -a option to check all the installed packages.
+
+ bumped the version to 0.34.
+ * TagsCheck.py: added missing descriptions.
+
+ corrected the -devel warning to handle the libbzip2_1-devel case.
+ * BinariesCheck.py, DistributionCheck.py, FilesCheck.py,
+ InitScriptCheck.py, MenuCheck.py, SignatureCheck.py,
+ SourceCheck.py, SpecCheck.py: added missing descriptions.
+ * Pkg.py: authorize to pass an rpm header to the InstalledPkg
+ constructor.
+ * Filter.py: don't print the description if the error/warning is
+ filtered.
+
+2001-07-11 Frederic Crozat <fcrozat at mandriva.com>
+
+ * Config.py: userhelper (from usermode) is authorized to be setuid
+
+2001-07-06 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: added two more language codes
+
+2001-07-06 Christian Belisle
+
+ * rpmlint.spec: 0.33-2mdk
+ * ChangeLog: Generated by cvs2cl the 06_Jul
+ * rpmlint.spec: Version 0.33-2mdk, Added descriptions
+ * ChangeLog: Generated by cvs2cl the 06_Jul
+ * TagsCheck.py: Added descriptions
+
+2001-07-05 Christian Belisle
+
+ * TagsCheck.py: Added descriptions.
+ * TagsCheck.py: Added entries for descriptions.
+ * SpecCheck.py: Added descriptions.
+
+2001-07-04 Pablo Saratxaga <pablo at mandriva.com>
+
+ * ChangeLog, I18NCheck.py: updated I18NCheck.py file
+
+2001-07-04 Frédéric Lepied <flepied at mandriva.com>
+
+ * I18NCheck.py: added nn as a valid subdir of /usr/share/local.
+
+2001-07-03 Christian Belisle
+
+ * SpecCheck.py: Added entries for the descriptions
+ * MenuCheck.py: Added entries to put descriptions.
+ * SourceCheck.py: Added descriptions.
+ * SignatureCheck.py: Added a description.
+ * LSBCheck.py: Added descriptions
+
+2001-07-02 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 02_Jul
+ * rpmlint.spec: 0.33-1mdk
+ * rpmlint.py: 0.33
+ * ChangeLog: Generated by cvs2cl the 02_Jul
+ * Config.py: added library policy exceptions
+ * BinariesCheck.py: removed debug trace
+
+2001-06-27 Christian Belisle
+
+ * InitScriptCheck.py: Added descriptions.
+ * FilesCheck.py: Added descriptions.
+
+2001-06-26 Christian Belisle
+
+ * ConfigCheck.py, DistributionCheck.py, FHSCheck.py: Added
+ descriptions.
+
+2001-06-25 Frédéric Lepied <flepied at mandriva.com>
+
+ * BinariesCheck.py: new check for files which can cause upgrade
+ problems in the library packages.
+ * TagsCheck.py: try to check alpha/beta/pre version use.
+
+2001-06-20 Frédéric Lepied <flepied at mandriva.com>
+
+ * Filter.py: print description only if they aren't empty.
+ * TagsCheck.py: added a check for invalid version.
+ * SpecCheck.py: added a check for obsolete tags.
+ * PostCheck.py: described one-line-command-in warnings.
+
+2001-06-19 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: added named user and group to the exception list.
+
+2001-06-19 Christian Belisle
+
+ * FHSCheck.py, I18NCheck.py, InitScriptCheck.py, LSBCheck.py,
+ rpmlint.spec: Added few descriptions
+ * ChangeLog: Generated by cvs2cl the 19_Jun
+ * DistributionCheck.py: Added few descriptions
+
+2001-06-18 Christian Belisle
+
+ * DistributionCheck.py: Added few descriptions
+ * ConfigCheck.py: Added few descriptions
+ * FilesCheck.py: Added few descriptions
+
+2001-06-15 Chmouel Boudjnah
+
+ * Config.py: Add more filesystem exclude.
+
+2001-06-14 Chmouel Boudjnah
+
+ * Config.py: /var/run/usb as 700 is normal.
+
+2001-06-13 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 13_Jun
+ * rpmlint.spec: 0.32-1mdk
+ * README: changed Linux-Mandrake => Mandrake Linux as default
+ Distribution tag.
+ * ChangeLog: Generated by cvs2cl the 13_Jun
+ * rpmlint.py: corrected copyright statement
+ * DistributionCheck.py: changed Linux-Mandrake => Mandrake Linux as
+ default Distribution tag.
+ * MenuCheck.py: added new Office sub menus.
+
+2001-06-12 Chmouel Boudjnah
+
+ * ChangeLog: Generated by cvs2cl the 12_Jun
+ * FilesCheck.py: Add /etc/profile.d/.
+
+2001-06-06 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.py: If the file given on the command line doesn't exist,
+ try to use the name as an installed package to check.
+ * TagsCheck.py: add error desccriptions only when -i is given on
+ the command line.
+ * FilesCheck.py: added /usr/X11R6/man subdirs to the list of
+ STANDARD_DIRS.
+
+ warn for .so file only if they are in a lib dir.
+
+ warn for source files in a non devel package only if they are not
+ a doc file.
+ * BinariesCheck.py: corrected soname regexp.
+
+ document errors.
+ * SignatureCheck.py: use checkSignature from the Pkg class to avoid
+ calling rpm directly to support the installed packages.
+ * Pkg.py: created InstalledPkg class to access already installed
+ packages.
+
+2001-05-25 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: added an example of details use.
+ * rpmlint.py: added -i/--info command line option to print details
+ of warings/errors.
+ * Filter.py: added functions to print/store details of
+ warnings/errors.
+ * Config.py: added info global variable.
+
+2001-05-22 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: corrected description-line-too-long check.
+
+2001-05-20 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: add the rpm user and group per request of Jeff
+ Johnson for the future version of rpm.
+
+2001-05-18 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 18_May
+ * rpmlint.py, rpmlint.spec: 0.31
+ * Makefile: added rules to build test and release rpms.
+ * Config.py: exceptions for XFree86
+ * Config.py: added various exceptions
+ * ChangeLog: Generated by cvs2cl the 18_May
+ * PostCheck.py: check that a script isn't a oneliner.
+ * PostCheck.py: check postin and prein instead of postun and preun
+ for ghost files creation.
+ * MenuCheck.py: don't check NO_XALF in menu command
+ * check-install.py: factorized checks
+ * ChangeLog: Generated by cvs2cl the 18_May
+
+2001-04-01 Chmouel Boudjnah
+
+ * ChangeLog: Generated by cvs2cl the 01_Apr
+ * FilesCheck.py: Add rpcuser
+
+2001-03-15 Chmouel Boudjnah
+
+ * Config.py: Expections for ldconfig.
+ * Config.py: Some more exeptions for initscripts.
+ * Config.py: Add some Execptions for initscripts.
+
+2001-02-28 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: check length of summary and description lines
+
+2001-02-21 Chmouel Boudjnah
+
+ * Config.py: netkit-base and iputils is the same for Filter.
+
+2001-02-16 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: 0.30-1mdk
+ * ChangeLog: Generated by cvs2cl the 16_Feb
+ * rpmlint.py: 0.30
+ * Config.py: exception for autoconf and libclanlib0-gl.
+ * InitScriptCheck.py: check if runlevels are set
+ * LSBCheck.py: also check source packages.
+ * MenuCheck.py: added support to check launchers.
+ * Pkg.py: added req_names to retrieve the list of packages names
+ (requires+prereq).
+ * TagsCheck.py: changed Window Maker to WindowMaker
+
+2001-02-13 Frédéric Lepied <flepied at mandriva.com>
+
+ * I18NCheck.py: check subdirs of /sur/share/man.
+
+2001-02-02 Frédéric Lepied <flepied at mandriva.com>
+
+ * PostCheck.py: check that the postun creates the ghost files
+ * PostCheck.py: added install to dangerous commands
+ * LSBCheck.py: first version
+
+2001-01-23 Chmouel Boudjnah
+
+ * ChangeLog: Generated by cvs2cl the 23_Jan
+ * TagsCheck.py: Add https as valid url.
+
+2000-12-13 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: used list of licenses from
+ www.opensource.org/licenses
+
+2000-12-07 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: check the full license before splitting in it
+ multiple parts.
+ * rpmlint.py, rpmlint.spec: 0.29
+
+2000-12-07 Chmouel Boudjnah
+
+ * ChangeLog: Generated by cvs2cl the 07_Dec
+ * PostCheck.py: Add /sbin/sash as VALID_SHELLS.
+
+2000-12-06 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: added exceptions for dev.
+ * FilesCheck.py: check dangling-symlink in the file index too to
+ avoid missing special files that aren't created when extracted as
+ a user.
+ * FilesCheck.py: removed trace.
+ * Config.py: cleaned header.
+ * README: added description of DanglingSymlinkExceptions.
+ * FilesCheck.py: added a generic way to avoid dangling-symlink
+ warnings.
+ * TagsCheck.py: for devel packages, check dependency on lib package
+ only when a .so file is present.
+
+2000-11-29 Chmouel Boudjnah
+
+ * Config.py: addFilter W: dev86-devel no-provides dev8-devel on
+ this warning.
+ * Config.py: add some execptions for pam (0750 dir for /etc/default
+ is normal as weel to have gpasswd and chage as suid).
+ * Config.py: the dangling symlink in dev are not dangled they are
+ relatives !!!
+ * Config.py: Don't check info-file-with-install-info for bash since
+ it's by default in the dir file.
+
+2000-11-24 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: 0.28-1mdk
+ * ChangeLog: Generated by cvs2cl the 24_Nov
+ * rpmlint.py: 0.28
+ * TagsCheck.py: check -devel package naming scheme only on binary
+ packages.
+ * SourceCheck.py: only check compression on tar or diff files.
+ * Config.py: various exceptions added.
+ * TagsCheck.py: report a warning if a -devel package comes with no
+ major in its name. added python licence and public domain. check
+ syntax of url tag.
+ * BinariesCheck.py: report the file location on objdump errors. new
+ error: executable in library package.
+
+2000-11-23 Frédéric Lepied <flepied at mandriva.com>
+
+ * I18NCheck.py: fuzzy check on packages without dependency on
+ locales
+ * FilesCheck.py: check if a package provides sources.
+ * PostCheck.py: force a separator before dangerous command.
+
+2000-11-13 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: 0.27-1mdk
+ * ChangeLog: Generated by cvs2cl the 13_Nov
+ * rpmlint.py: 0.27
+ * FilesCheck.py: don't warn if a games is setgid games.
+ * README: RpmGamesGroup added to the list of available options.
+ * Config.py: added axception for xman.
+ * BinariesCheck.py: check ldconfig symlinks.
+
+2000-11-11 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: don't check no-version-in-changelog for source rpm.
+
+2000-11-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: 0.26
+ * ChangeLog: Generated by cvs2cl the 10_Nov
+ * rpmlint.py: 0.26
+ * Config.py: added various exceptions.
+ * TagsCheck.py: allow multiple licenses.
+
+ don't report anymore the package-provides-itself warning because
+ it's the default in rpm 4.
+
+ try to not report incoherent-version-in-changelog for
+ sub-packages.
+ * MenuCheck.py: correct the non-transparent-xpm check.
+ * FilesCheck.py: don't report buggy length-symlink anymore.
+
+2000-10-25 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: don't check if package provides itself because rpm
+ 4.0 always does it.
+
+2000-10-17 Chmouel Boudjnah
+
+ * Config.py: Fix exception for glibc.
+
+2000-10-16 Frédéric Lepied <flepied at mandriva.com>
+
+ * check-install.py: first version.
+ * Pkg.py: added the possibility to create a Pkg object directly
+ from an rpm header.
+
+2000-10-12 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: corrected changelog
+ * rpmlint.spec: corrected changelog
+ * rpmlint.spec: 0.25-1mdk
+ * ChangeLog: Generated by cvs2cl the 12_Oct
+ * rpmlint.py: 0.25
+ * Config.py: added exception for sympa, rpm and bcast.
+ * TagsCheck.py: check that devel package depends on the base
+ package with the same version. check that summary begins with a
+ capital letter.
+ * PostCheck.py: check dangerous commands. check reference to ~ or
+ $HOME.
+ * SourceCheck.py: cleanup.
+ * MenuCheck.py: check that titles and longtitles begin by a capital
+ letter. check that no version is included in title and longtitle.
+ * FilesCheck.py: check package owning system dirs.
+
+2000-10-11 Frédéric Lepied <flepied at mandriva.com>
+
+ * SpecCheck.py: check name of spec file.
+ * README: added description of SpecCheck.
+ * Config.py: added SpecCheck to DEFAULT_CHECKS.
+ * SpecCheck.py: check use of $RPM_SOURCE_DIR.
+ * SpecCheck.py: first version
+
+2000-10-10 Chmouel Boudjnah
+
+ * MenuCheck.py: /lib/cpp errors to /dev/null for new cpp.
+
+2000-10-02 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.py, rpmlint.spec: 0.24
+ * ChangeLog: Generated by cvs2cl the 02_Oct
+ * FilesCheck.py: added apache and postgres to standard groups.
+ * TagsCheck.py: spell check a la Debian.
+
+2000-09-29 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.py, rpmlint.spec: 0.23
+ * ChangeLog: Generated by cvs2cl the 29_Sep
+ * MenuCheck.py: added Applications/Accessibility. check that menu
+ file are readable by everyone.
+ * Config.py: * removed exception for /home. * added exceptions for
+ vixie-cron.
+
+2000-09-25 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: check cvs internal files.
+
+2000-09-12 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: 0.22
+ * ChangeLog: Generated by cvs2cl the 12_Sep
+ * rpmlint.py: 0.22
+ * PostCheck.py: print a warning on empty script.
+ * FilesCheck.py: added postgres and apache to default users.
+ * Config.py: added libwmf and doxygen as dev packages.
+
+ info/dir exception for info-install package.
+ * README, TagsCheck.py: added bugs@linux-mandrake.com as a valid
+ packager address.
+
+2000-09-06 Pixel <pixel at mandriva.com>
+
+ * ChangeLog: *** empty log message ***
+ * I18NCheck.py: check *.mo for file-not-in-%lang, not only in
+ /usr/share/locale
+
+2000-09-05 Frédéric Lepied <flepied at mandriva.com>
+
+ * MenuCheck.py, TagsCheck.py: replaced Networking/ICQ group with
+ Networking/Instant messaging.
+
+2000-08-31 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: 0.21
+ * ChangeLog: Generated by cvs2cl the 31_Aug
+ * rpmlint.py: 0.21
+ * TagsCheck.py: check packager field compliance to a regexp.
+ * README: added description of the Packager option.
+ * Config.py: added exception for libwmf.
+ * ChangeLog: Generated by cvs2cl the 31_Aug
+ * README: removed XpmIconPath.
+ * Config.py: imported default exceptions.
+ * config: move standard exceptions to Config.py.
+ * TagsCheck.py: added Apache License, PHP Licence and BSD-Style.
+ * MenuCheck.py: check hardcoded path in icon field and large, mini,
+ normal icon files.
+
+2000-08-28 Chmouel Boudjnah
+
+ * ChangeLog: Generated by cvs2cl the 28_Aug
+ * PostCheck.py: Fix typo in check of /usr/bin/perl.
+ * ChangeLog: Generated by cvs2cl the 28_Aug
+ * PostCheck.py: Check perl script like we do for bash script.
+
+2000-08-28 Pablo Saratxaga <pablo at mandriva.com>
+
+ * I18NCheck.py: updated locales list
+
+2000-08-26 Chmouel Boudjnah
+
+ * ChangeLog: Generated by cvs2cl the 26_Aug
+ * FilesCheck.py: Only check perl_temp_file in a /perl/ directory.
+
+2000-08-25 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.py, rpmlint.spec: 0.20
+ * ChangeLog: Generated by cvs2cl the 25_Aug
+ * Config.py: added InitScriptCheck.
+ * InitScriptCheck.py: first version.
+ * config: added exceptions for InitScriptCheck.
+ * README: added InitScriptCheck description.
+ * PostCheck.py: check where a script is present that the shell is
+ valid.
+ * FilesCheck.py: moved /etc/rc.d/init.d checks to InitScriptCheck.
+ * ConfigCheck.py: report warnings for app-defaults only in
+ /usr/X11R6/lib/X11/app-defaults.
+ * BinariesCheck.py: report the rpath warning if directory isn't a
+ sub-directory of /usr/lib/.
+
+2000-08-18 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: I18NCheck is back.
+ * ChangeLog, rpmlint.spec: 0.19
+ * rpmlint.py: 0.19
+ * README: added SystemLibPaths mention.
+ * BinariesCheck.py: check rpath only on system lib paths (ie /lib,
+ /usr/lib and /usr/X11R6/lib). This can be configured with the
+ SystemLibPaths option.
+ * Pkg.py: added fileLang to retrieve the lang associated to a file.
+
+2000-08-17 Frédéric Lepied <flepied at mandriva.com>
+
+ * I18NCheck.py: warn if .mo is not registered in %lang.
+ * MenuCheck.py: protected kdesu check.
+ * FilesCheck.py: check perl temporary files.
+
+2000-08-16 Frédéric Lepied <flepied at mandriva.com>
+
+ * README, rpmlint.py: added ExtractDir option usable in the config
+ file.
+ * PostCheck.py: check ] in if statement. report warning for a
+ percent.
+
+2000-08-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: Generated by cvs2cl the 10_Aug
+ * rpmlint.spec: 0.18-1mdk
+ * TagsCheck.py: check for valid licence.
+ * README: added ValidLicenses.
+ * rpmlint.py: 0.18
+ * ChangeLog: Generated by cvs2cl the 10_Aug
+ * ConfigCheck.py: check files without no-replace flag.
+ * Pkg.py: added noreplaceFiles()
+ * MenuCheck.py: allow depency on kdesu to point directly to
+ /usr/bin/kdesu.
+
+2000-08-08 Frédéric Lepied <flepied at mandriva.com>
+
+ * FHSCheck.py: allow ftp and www in var (from upcoming FHS 2.2).
+ * rpmlint.py: 0.17
+ * ChangeLog: Generated by cvs2cl the 08_Aug
+ * rpmlint.spec: 0.17-1mdk
+ * FilesCheck.py: corrected check for install_info to avoid
+ backtrace on empty postun or preun.
+ * rpmlint.spec: * 0.17
+
+2000-08-07 Frédéric Lepied <flepied at mandriva.com>
+
+ * ScriptCheck.py: * replaced by PostCheck.py
+
+2000-08-03 Chmouel Boudjnah
+
+ * config: Add few filters for util-linux
+
+2000-08-01 Chmouel Boudjnah
+
+ * config: Correct pam setuid-binary execptions.
+ * config: mount and umount are suid binary.
+
+2000-07-31 Chmouel Boudjnah
+
+ * ChangeLog: Generated by cvs2cl the 31_Jul
+
+2000-07-31 Frédéric Lepied <flepied at mandriva.com>
+
+ * Config.py: * (DEFAULT_CHECKS): removed ScriptCheck.
+ * PostCheck.py: * merged ScriptCheck.py and corrected shell script
+ check.
+ * FilesCheck.py: * allow install-info call in preun. * check
+ chkconfig calls for package that comes with an
+ /etc/rc.d/init.d/script.
+
+2000-07-29 Chmouel Boudjnah
+
+ * ChangeLog: Generated by cvs2cl the 29_Jul
+ * MenuCheck.py: If we use kdesu check in it present in Requires:
+ Prereq:
+ * MenuCheck.py: Fix again kdesu (i hate python indentation :-()
+ * MenuCheck.py: Get kdesu check to work :-\
+ * ChangeLog: Generated by cvs2cl the 29_Jul
+ * Makefile: Add a changelog rules to be used with cvs2cl.
+ * Config.py, ScriptCheck.py: check syntax of (post|pre)(un)?install
+ script, currently only bash is supported.
+
+2000-07-28 Chmouel Boudjnah
+
+ * FilesCheck.py: install-info can't be RPMTAG_POSTUNPROG or
+ RPMTAG_POSTPROG
+
+2000-07-27 Chmouel Boudjnah
+
+ * config: Add e2fsprogs changes.
+
+2000-07-25 Pixel <pixel at mandriva.com>
+
+ * ChangeLog: no_comment
+ * config: add exception for some devel packages, updated the filter
+ for setuid perl's
+
+2000-07-21 Chmouel Boudjnah
+
+ * config: Add some glibc exceptions.
+
+2000-07-20 Chmouel Boudjnah
+
+ * FilesCheck.py: if there is info files check to see if we have a
+ install-info in the %post(un)?
+
+2000-07-19 Chmouel Boudjnah
+
+ * config: filter me the "shadow-utils dangling-symlink /usr/bin/sg
+ newgrp"
+ * config: static binaries for ldconfig is permit :-)
+ * config: Add some exeptions for kernel packages.
+ * config: /bin/ping as suid binary is correct.
+
+2000-07-19 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog, rpmlint.spec: * 0.16
+ * rpmlint.py: * 0.16
+ * TagsCheck.py: * (DEFAULT_VALID_GROUPS): sawmill => sawfish.
+ * Config.py: * added FHSCheck by default.
+ * config: * added exception for wall.
+ * FHSCheck.py: * corrected check to not match substrings.
+ * FilesCheck.py: * added check dandling symlinks. * check the
+ presence of /usr(/share)/info/dir
+
+2000-07-19 Chmouel Boudjnah
+
+ * config: Exception for pam package.
+
+2000-07-19 Frédéric Lepied <flepied at mandriva.com>
+
+ * README.CVS: * give command line example.
+
+2000-07-19 Chmouel Boudjnah
+
+ * MenuCheck.py: If the menu_command contain a kdesu -c "", check
+ instead for the command instead of kdesu.
+ * FilesCheck.py: By default {doc,man,info} in /usr/share, product
+ an error when the package use /usr/.
+ * config: su is suid and it normal !!
+
+2000-07-05 Frédéric Lepied <flepied at mandriva.com>
+
+ * README: * added description for PostCheck.
+ * Config.py: * added PostCheck
+ * PostCheck.py: * first version.
+
+2000-07-01 Chmouel Boudjnah
+
+ * config: Remove lftp .So warning (should be fixed in rpmlint), add
+ expeption for slocate.
+
+2000-06-30 Chmouel Boudjnah
+
+ * config: Add sudo exception.
+
+2000-06-27 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog, rpmlint.spec: * 0.15
+ * rpmlint.py: * 0.15
+ * README: * added XpmIconPath.
+ * MenuCheck.py: * check non transparent pixmaps in icon path.
+ * config: * added man for sgid exception. * corrected the regex for
+ /var/catman and /usr/man.
+ * BinariesCheck.py: * added a check for soname.
+ * TagsCheck.py: * removed trace.
+ * TagsCheck.py: * added a warning for packages that provide
+ themselves (for Pixel).
+ * Pkg.py: * (_gatherDepInfo) corrected the conflicts and provides
+ acquisition.
+
+2000-06-16 Chmouel Boudjnah
+
+ * config: add W: lftp shared-lib-without-dependency-information
+ /usr/lib/lftp/.*.so
+
+2000-06-15 Frédéric Lepied <flepied at mandriva.com>
+
+ * Pkg.py: * added a builtin grep.
+ * config: * removed stange-needs filters.
+ * README: * added ExtraMenuNeeds
+ * MenuCheck.py: * added a list of valid needs.
+
+2000-06-15 Chmouel Boudjnah
+
+ * config: add execption for "I: iceconf strange-needs icewm
+ /usr/lib/menu/iceconf"
+
+2000-04-17 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog, rpmlint.py, rpmlint.spec: * 0.14
+ * FilesCheck.py, MenuCheck.py: * corrected check of %post, %postun
+ to avoid comments.
+ * MenuCheck.py: * check old menu entries for KDE and GNOME. * allow
+ entries for sections.
+ * config: * added exceptions for urpmi, sash, octave, ghc,
+ procmail, rsh.
+
+2000-04-12 Frédéric Lepied <flepied at mandriva.com>
+
+ * Pkg.py: * (Pkg._extract): extract in dir
+ <tmppath>/<pkgname>.<pid>
+
+2000-04-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog, rpmlint.py, rpmlint.spec: * 0.13
+ * config: * added exception for XFree86 4.0 .a modules.
+ * ChangeLog: * 0.13
+ * MenuCheck.py: * use POSTINPROG if no POSTIN. * use POSTUNPROG if
+ no POSTUN.
+ * FilesCheck.py: * check ldconfig in %post and %postun. * added
+ urpmi to default group list.
+
+2000-04-07 Chmouel Boudjnah
+
+ * rpmlint.spec: Use %{_tmppath}
+ * ChangeLog: "Seethechangelog"
+ * MenuCheck.py: Add check on icons, if no icon print a warning, if
+ icon specified (with a long path, not with relative path) is not
+ here print a Error.
+ * FilesCheck.py: package -source are also devel package.
+
+2000-04-05 Chmouel Boudjnah
+
+ * config: traceroute need to be setuid-binary
+ * config: Add exception rules for initscripts.
+ * ChangeLog: "Seethechangelog"
+ * MenuCheck.py: Move Applications/Terminals to Terminals
+
+2000-04-04 Chmouel Boudjnah
+
+ * ChangeLog: *** empty log message ***
+ * MenuCheck.py: Add Session/Windowmanagers in List of good
+ window-managers.
+
+2000-04-03 Chmouel Boudjnah
+
+ * config: Add hackkernel* like kernel* for the
+ devel-file-in-non-devel-package stuff.
+
+2000-03-31 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog, rpmlint.py: * 0.12
+ * rpmlint.spec: * 1.12
+ * MenuCheck.py: * check update-menus in %post and %postun if a menu
+ is present.
+ * config: * avoid I: strange-needs kde for package beginning by k.
+ * MenuCheck.py: * corrected default menu (thanks to DindinX).
+
+2000-03-30 Chmouel Boudjnah
+
+ * config: add /mnt/disk like /mnt/(floppy|cdrom) and clean up
+ regex.
+
+2000-03-29 Frédéric Lepied <flepied at mandriva.com>
+
+ * MenuCheck.py: * check that the command is present in the menu.
+ * BinariesCheck.py: * check for non sparc32 binaries in sparc rpms.
+
+2000-03-27 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog, rpmlint.py, rpmlint.spec: * 0.11
+ * README: * added MenuCheck.
+ * MenuCheck.py: * valid sections are now a configuration variable.
+ * config: * added an exception for MenuCheck.
+
+2000-03-23 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: * commented out non-empty-dir-listed.
+ * Config.py: * (DEFAULT_CHECKS): added MenuCheck.
+
+2000-03-23 Chmouel Boudjnah
+
+ * config: don't check devel-file-in-non-devel-package for
+ alsa-source
+ * config: it's allow to have a tmpdir as 700 in etcskel and
+ rootfile.
+ * config: Don't check for config files on /root/*.
+
+2000-03-20 Frédéric Lepied <flepied at mandriva.com>
+
+ * MenuCheck.py: * first version.
+
+2000-03-14 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: * corrected Group header.
+ * config: * added exceptions for devel-file-in-non-devel-package
+ and dir-or-file-in-home.
+ * rpmlint.spec: * 1.10.
+ * ChangeLog: * 1.10
+ * rpmlint.py: * 0.10.
+
+2000-03-13 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: * corrected new group list.
+ * FilesCheck.py: * check files on /home.
+
+2000-03-09 Chmouel Boudjnah
+
+ * FilesCheck.py: Correct cdwriters to cdwriter Add x10 group.
+
+2000-03-06 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: * changed the list of standard groups.
+ * FilesCheck.py: * (FilesCheck.check): added a check for .h and .a
+ files and symbolic link .so in non devel packages.
+
+2000-02-28 Frédéric Lepied <flepied at mandriva.com>
+
+ * Pkg.py: * real correction for rpm 3.0.4 (I hope).
+ * rpmlint.py: * 0.9.2.
+ * ChangeLog, rpmlint.spec: * 0.9.2
+ * Pkg.py: * corrected rpm 3.0.4 support.
+
+2000-02-23 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: * 0.9.1.
+ * rpmlint.spec: * 0.9.1-1mdk.
+ * Makefile: * added README.CVS.
+ * rpmlint.py: * 0.9.1 * changed copyright year.
+ * Pkg.py: * added support for the rpm 3.0.4 way to store file
+ names.
+ * README.CVS: * first version.
+
+2000-02-10 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: * 0.9.
+ * ChangeLog: * forgot comment about SignatureCheck.
+ * SignatureCheck.py: * added gpg to correct signatures.
+ * ChangeLog: * 0.9.
+ * rpmlint.py: * 0.9.
+ * README: * added description of new options: ValidGroups,
+ ReleaseExtension and UseVersionInChangelog.
+ * config: * added commented examples for ReleaseExtension and
+ ValidGroups.
+ * TagsCheck.py: * (check): check release extension. * (check):
+ added configuration option for version on changelog and release
+ extension.
+ * DistributionCheck.py: * first version.
+ * Makefile: * install .py files too.
+ * Config.py: * change MandrakeCheck to DistributionCheck.
+ * MandrakeCheck.py: * renamed in DistributionCheck.
+
+2000-01-24 Frédéric Lepied <flepied at mandriva.com>
+
+ * FilesCheck.py: * added a check on non executable in bin
+ directories.
+
+1999-12-30 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: * 0.8-1mdk.
+ * ChangeLog, rpmlint.py: * 0.8.
+ * README: * change email. * added a line about i18n checks.
+ * config: * added exception for sympa, postgresql-test and
+ filesystem.
+ * MandrakeCheck.py: * change default distribution to
+ Linux-Mandrake.
+ * TagsCheck.py: * (TagsCheck.check): added check on version in the
+ first line of the changelog.
+ * FilesCheck.py: * change severity of reports.
+ * BinariesCheck.py: * binaries not stripped is dowgraded to
+ warning.
+
+1999-12-14 Frédéric Lepied <flepied at mandriva.com>
+
+ * I18NCheck.py: * added a check on dirs containing LC_MESSAGES
+ catalogs.
+
+1999-11-30 Frédéric Lepied <flepied at mandriva.com>
+
+ * I18NCheck.py: * correct the locales subdir regex to capture only
+ correct names.
+
+1999-11-25 Frédéric Lepied <flepied at mandriva.com>
+
+ * I18NCheck.py: * initial release.
+
+1999-11-18 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: * real v 0.7
+ * MandrakeCheck.py: * don't warn about info/dir not compressed
+
+1999-11-16 Frédéric Lepied <flepied at mandriva.com>
+
+ * README: * updated to reflect the change to addFilter. * corrected
+ the description of the checks.
+
+1999-11-15 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog, rpmlint.spec: * 0.7
+ * Filter.py: * test a string against the filters.
+ * Pkg.py: * (cleanup): change access rights before removing the
+ package to prevent very bad packages from making rpmlint abort.
+ * TagsCheck.py: * peform all the check on source package too.
+ * config: * added /var/catman exceptions.
+ * Config.py: * filters are regexp now.
+ * rpmlint.py: * 0.7 * output through Filter.
+
+1999-10-27 Frédéric Lepied <flepied at mandriva.com>
+
+ * SourceCheck.py: * oops: added Filter import.
+ * TagsCheck.py: * (TagsCheck.check): verify the name tag and the
+ file name coherence.
+ * Config.py: * (DEFAULT_CHECKS): added SourceCheck.
+ * SourceCheck.py: * first version.
+
+1999-10-23 Frédéric Lepied <flepied at mandriva.com>
+
+ * ., .cvsignore: * added .flog and .bz2.
+ * rpmlint.spec: * 0.6.1.
+ * ChangeLog: * 0.6.1
+ * Makefile: * (all): use compile.py to byte compile files.
+ * compile.py: * first version.
+ * rpmlint.py: * 0.6.1
+ * rpmlint.spec: * 0.6.
+ * ChangeLog: * O.6.
+ * BinariesCheck.py, ConfigCheck.py, FHSCheck.py, MandrakeCheck.py,
+ SignatureCheck.py: * output via Filter.
+ * README: * added description of addFilter.
+ * Config.py: * don't use FHS check by default because rpm doesn't
+ put the doc files under /usr/share/doc => too much reports.
+ * rpmlint.py: * version 0.6.
+ * config: * added an example of addFilter.
+ * TagsCheck.py: * output via Filter. * (TagsCheck.check): checks if
+ the summary is on multiple lines.
+ * FilesCheck.py: * output via Filter. * added documentation checks.
+ * Filter.py: * first version.
+ * Config.py: * (addFilter isFiltered): new funtions for output
+ filters.
+
+1999-10-16 Frédéric Lepied <flepied at mandriva.com>
+
+ * ChangeLog: * 0.5.
+ * rpmlint.spec: * 0.5.
+ * rpmlint.py: * (version): 0.5. * uses Config to get the list of
+ checks.
+ * README: * added description of config files and options.
+ * MandrakeCheck.py: * uses Config options for vendor, ditribution
+ and compression.
+ * Makefile: * install the config file in /etc/rpmlint
+ * AllChecks.py: no more needed
+ * Config.py, FHSCheck.py, config: * first version.
+
+1999-10-12 Frédéric Lepied <flepied at mandriva.com>
+
+ * TagsCheck.py: * (TagsCheck.check): corrected the message for
+ non-standard-group to display the package name.
+ * Pkg.py: * (Pkg._gatherFilesInfo): gather ghost files.
+ * FilesCheck.py: * (FilesCheck.check): avoid reporting
+ non-conffile-in-etc for a ghost file. * (FilesCheck.check): added
+ a check of non standard users and groups.
+
+1999-10-11 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: * corrected typo.
+
+1999-10-08 Chmouel Boudjnah
+
+ * rpmlint.spec: *** empty log message ***
+
+1999-10-07 Frédéric Lepied <flepied at mandriva.com>
+
+ * rpmlint.spec: * (Requires): added cpio. * 0.4.
+ * rpmlint.py: * (version): 0.4.
+ * TagsCheck.py: * added a check for valid group name.
+ * README: * pgp check impemented.
+ * FilesCheck.py: * (FilesCheck.check): check only binary package.
+ * ConfigCheck.py: * (ConfigCheck.check): check only binary package.
+ * ChangeLog: * 0.4.
+ * BinariesCheck.py: * corrected error message when there is a
+ problem with objdump.
+ * AllChecks.py: * added SignatureCheck.
+ * SignatureCheck.py: * first version.
+
+1999-10-06 Frédéric Lepied <flepied at mandriva.com>
+
+ * PermissionsCheck.py: removed
+ * rpmlint.spec: * 0.3-1. * added version of needed dependencies.
+ * rpmlint.py: * changed the exception handling to have all the
+ traceback.
+ * rpmlint: * launch python with unbuffered output.
+ * README: * added name of check on the implemented part. * added
+ FileCheck to the implemented part.
+ * Pkg.py: * added comments. * extract all file listing in one place
+ (_gatherFilesInfo).
+ * Makefile: * install only .pyo files. * new target ndist to make a
+ tar ball without the version number in the directory name.
+ * INSTALL: * added cpio to the list of dependencies.
+ * ChangeLog: * 0.3.
+ * AllChecks.py: * added FilesCheck.
+ * ., .cvsignore, FilesCheck.py: * first version.
+ * rpmlint.spec: * added header. * 0.2-1.
+ * README: * changed configuration check comment from planed to
+ implemented.
+ * Pkg.py: (configFiles): new method to return the list of
+ configuration files.
+ * Makefile: * added ChangeLog to distribution files. * bzip2 in a
+ separate command to make the dist target run on system without
+ the y option of tar.
+ * AllChecks.py: * added ConfigCheck.
+ * ChangeLog, ConfigCheck.py: * first version.
+ * rpmlint.py: * 0.2
+
+1999-10-01 Chmouel Boudjnah
+
+ * AbstractCheck.py, AllChecks.py, BinariesCheck.py, COPYING,
+ INSTALL, Makefile, MandrakeCheck.py, PermissionsCheck.py, Pkg.py,
+ README, TagsCheck.py, rpmlint, rpmlint.py, rpmlint.spec: Initial
+ revision
+
+1999-10-01
+
+ * .: New repository initialized by cvs2svn.
+
diff --git a/Config.py b/Config.py
new file mode 100644
index 0000000..29b124c
--- /dev/null
+++ b/Config.py
@@ -0,0 +1,161 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : Config.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Fri Oct 15 20:04:25 1999
+# Version : $Id: Config.py 1871 2011-06-18 09:40:52Z scop $
+# Purpose : handle configuration options. To be used from config files.
+#############################################################################
+
+import locale
+import os.path
+import re
+
+try:
+ from __version__ import __version__
+except ImportError:
+ __version__ = 'devel'
+
+DEFAULT_CHECKS = ("DistributionCheck",
+ "TagsCheck",
+ "BinariesCheck",
+ "ConfigCheck",
+ "FilesCheck",
+ "DocFilesCheck",
+ "FHSCheck",
+ "SignatureCheck",
+ "I18NCheck",
+ "MenuCheck",
+ "PostCheck",
+ "InitScriptCheck",
+ "SourceCheck",
+ "SpecCheck",
+ "NamingPolicyCheck",
+ "ZipCheck",
+ "PamCheck",
+ "RpmFileCheck",
+ "MenuXDGCheck",
+ )
+
+USEUTF8_DEFAULT = False
+try:
+ if locale.getpreferredencoding() == 'UTF-8':
+ USEUTF8_DEFAULT = True
+except:
+ try:
+ if re.match('utf', locale.getdefaultlocale()[1], re.I):
+ USEUTF8_DEFAULT = True
+ except:
+ pass
+
+info = False
+no_exception = False
+
+# handle the list of checks to load
+_checks = []
+_checks.extend(DEFAULT_CHECKS)
+
+def addCheck(check):
+ check = re.sub('\.py[co]?$', '', check)
+ if check not in _checks:
+ _checks.append(check)
+
+def allChecks():
+ if _checks == []:
+ defaultChecks()
+ return _checks
+
+def defaultChecks():
+ resetChecks()
+ _checks.extend(DEFAULT_CHECKS)
+
+def resetChecks():
+ global _checks
+
+ _checks = []
+
+# handle the list of directories to look for checks
+
+_dirs = ["/usr/share/rpmlint"]
+
+def addCheckDir(dir):
+ d = os.path.expanduser(dir)
+ if d not in _dirs:
+ _dirs.insert(0, d)
+
+def checkDirs():
+ return _dirs
+
+# handle options
+
+_options = {}
+
+def setOption(name, value):
+ _options[name] = value
+
+def getOption(name, default = ""):
+ try:
+ return _options[name]
+ except:
+ return default
+
+# List of filters
+_filters = []
+_filters_re = None
+
+def addFilter(s):
+ global _filters_re
+
+ _filters.append(s)
+ _filters_re = None
+
+def removeFilter(s):
+ global _filters_re
+
+ try:
+ _filters.remove(s)
+ except:
+ pass
+ else:
+ _filters_re = None
+
+_scoring = {}
+
+def setBadness(s, score):
+ _scoring[s] = score
+
+def badness(s):
+ return _scoring.get(s, 0)
+
+_non_named_group_re = re.compile('[^\\](\()[^:]')
+def isFiltered(s):
+ global _filters_re
+
+ if _filters_re == None:
+ # no filter
+ if len(_filters) == 0:
+ return False
+ _filters_re = '(?:' + _filters[0] + ')'
+
+ for idx in range(1, len(_filters)):
+ # to prevent named group overflow that happen when there is too
+ # many () in a single regexp: AssertionError: sorry, but this
+ # version only supports 100 named groups
+ if '(' in _filters[idx]:
+ _non_named_group_re.subn('(:?', _filters[idx])
+ _filters_re = _filters_re + '|(?:' + _filters[idx] +')'
+ _filters_re = re.compile(_filters_re)
+
+ if not no_exception:
+ if _filters_re.search(s):
+ return True
+ return False
+
+# Config.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/ConfigCheck.py b/ConfigCheck.py
new file mode 100644
index 0000000..42296d6
--- /dev/null
+++ b/ConfigCheck.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : ConfigCheck.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Sun Oct 3 21:48:20 1999
+# Version : $Id: ConfigCheck.py 1774 2010-04-20 20:07:10Z scop $
+# Purpose :
+#############################################################################
+
+from Filter import addDetails, printError, printWarning
+import AbstractCheck
+
+
+class ConfigCheck(AbstractCheck.AbstractCheck):
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, "ConfigCheck")
+
+ def check(self, pkg):
+ # Check only binary package
+ if pkg.isSource():
+ return
+
+ config_files = pkg.configFiles()
+ noreplace_files = pkg.noreplaceFiles()
+
+ for c in config_files:
+ if c.startswith("/var/lib/games/"):
+ printError(pkg, "score-file-must-not-be-conffile", c)
+ elif not c.startswith("/etc/") and not c.startswith("/var/"):
+ printWarning(pkg, "non-etc-or-var-file-marked-as-conffile", c)
+
+ if c not in noreplace_files:
+ printWarning(pkg, "conffile-without-noreplace-flag", c)
+
+# Create an object to enable the auto registration of the test
+check = ConfigCheck()
+
+# Add information about checks
+addDetails(
+'score-file-must-not-be-conffile',
+"""A file in /var/lib/games/ is a configuration file. Store your conf
+files in /etc instead.""",
+
+'non-etc-or-var-file-marked-as-conffile',
+"""A file not in /etc or /var is marked as being a configuration file.
+Please put your conf files in /etc or /var.""",
+
+'conffile-without-noreplace-flag',
+"""A configuration file is stored in your package without the noreplace flag.
+A way to resolve this is to put the following in your SPEC file:
+
+%config(noreplace) /etc/your_config_file_here
+""",
+
+)
+
+# ConfigCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/DistributionCheck.py b/DistributionCheck.py
new file mode 100644
index 0000000..05f8f29
--- /dev/null
+++ b/DistributionCheck.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : DistributionCheck.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Tue Sep 28 00:05:33 1999
+# Version : $Id: DistributionCheck.py 1732 2010-02-21 11:28:42Z scop $
+# Purpose : check the Distribution specificities in a binary rpm package.
+#############################################################################
+
+import re
+
+import rpm
+
+from Filter import addDetails, printWarning
+import AbstractCheck
+import Config
+
+
+man_regex = re.compile("/man(?:\d[px]?|n)/")
+info_regex = re.compile("(/usr/share|/usr)/info/")
+vendor = Config.getOption("Vendor")
+distribution = Config.getOption("Distribution")
+compress_ext = Config.getOption("CompressExtension", "bz2")
+
+class DistributionCheck(AbstractCheck.AbstractCheck):
+
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, "DistributionCheck")
+
+ def check(self, pkg):
+ # Check only binary package
+ if pkg.isSource():
+ return
+
+ if vendor and pkg[rpm.RPMTAG_VENDOR] != vendor:
+ printWarning(pkg, "invalid-vendor", pkg[rpm.RPMTAG_VENDOR])
+
+ if distribution and pkg[rpm.RPMTAG_DISTRIBUTION] != distribution:
+ printWarning(pkg, "invalid-distribution",
+ pkg[rpm.RPMTAG_DISTRIBUTION])
+
+ if compress_ext:
+ for fname in pkg.files():
+ if man_regex.search(fname):
+ if not fname.endswith(compress_ext):
+ printWarning(pkg, 'manpage-not-compressed',
+ compress_ext, fname)
+ elif info_regex.search(fname) and \
+ not fname.endswith("/info/dir"):
+ if not fname.endswith(compress_ext):
+ printWarning(pkg, 'infopage-not-compressed',
+ compress_ext, fname)
+
+
+# Create an object to enable the auto registration of the test
+check = DistributionCheck()
+
+addDetails(
+'invalid-vendor',
+'''In the "%s" distribution, vendor should be "%s".''' % (distribution, vendor),
+
+'invalid-distribution',
+'The distribution value should be "' + distribution + '".',
+
+'manpage-not-compressed',
+'''This manual page is not compressed with the %s compression method
+(does not have the %s extension). If the compression does not happen
+automatically when the package is rebuilt, make sure that you have the
+appropriate rpm helper and/or config packages for your target distribution
+installed and try rebuilding again; if it still does not happen automatically,
+you can compress this file in the %%install section of the spec file.''' \
+% (compress_ext, compress_ext),
+
+'infopage-not-compressed',
+'''This info page is not compressed with the %s compression method
+(does not have the %s extension). If the compression does not happen
+automatically when the package is rebuilt, make sure that you have the
+appropriate rpm helper and/or config packages for your target distribution
+installed and try rebuilding again; if it still does not happen automatically,
+you can compress this file in the %%install section of the spec file.''' \
+% (compress_ext, compress_ext),
+)
+
+# DistributionCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/DocFilesCheck.py b/DocFilesCheck.py
new file mode 100644
index 0000000..4463579
--- /dev/null
+++ b/DocFilesCheck.py
@@ -0,0 +1,105 @@
+# -*- coding: utf-8 -*-
+# Copyright (C) 2005 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
+#
+# 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; version 2 of the License.
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import rpm
+
+from Filter import addDetails, printWarning
+import AbstractCheck
+
+
+class DocFilesCheck(AbstractCheck.AbstractCheck):
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, 'DocFilesCheck')
+
+ def __checkRequirements(self, pkg):
+
+ doc_files = pkg.docFiles()
+ files = pkg.files()
+
+ reqs = {}
+ for fname, pkgfile in files.items():
+ reqs[fname] = [x[0] for x in pkgfile.requires]
+
+ core_reqs = {} # dependencies of non-doc files
+ doc_reqs = {} # dependencies of doc files
+
+ for dep in pkg.header.dsFromHeader():
+ # skip deps which were found by find-requires
+ if dep.Flags() & rpm.RPMSENSE_FIND_REQUIRES != 0:
+ continue
+ core_reqs[dep.N()] = []
+
+ # register things which are provided by the package
+ for i in pkg.header[rpm.RPMTAG_PROVIDES] + files.keys():
+ core_reqs[i] = []
+
+ for i in files:
+ if not reqs[i]:
+ continue # skip empty dependencies
+ if i in doc_files:
+ target = doc_reqs
+ else:
+ target = core_reqs
+
+ for r in reqs[i]:
+ if r not in target:
+ target[r] = []
+ target[r].append(i)
+
+ # go through the calculated requirements of the %doc files
+ for (dep, req_files) in doc_reqs.items():
+ if dep not in core_reqs:
+ for f in req_files:
+ printWarning(pkg, "doc-file-dependency", f, dep)
+
+ def __checkUnwantedFiles(self, pkg):
+
+ for docfile in pkg.docFiles():
+ if docfile.endswith("/INSTALL"):
+ printWarning(pkg, "install-file-in-docs", docfile)
+
+ def check(self, pkg):
+
+ if pkg.isSource() or not pkg.docFiles():
+ return
+
+ self.__checkRequirements(pkg)
+ self.__checkUnwantedFiles(pkg)
+
+
+check = DocFilesCheck()
+
+addDetails(
+'doc-file-dependency',
+'''An included file marked as %doc creates a possible additional dependency in
+the package. Usually, this is not wanted and may be caused by eg. example
+scripts with executable bits set included in the package's documentation.''',
+
+'install-file-in-docs',
+'''A file whose name suggests that it contains installation instructions is
+included in the package. Such instructions are often not relevant for already
+installed packages; if this is the case for this file and it does not contain
+any information that is of interest after the package has been built and
+installed, do not include the file in the binary package.''',
+)
+
+# DocFilesCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/FHSCheck.py b/FHSCheck.py
new file mode 100644
index 0000000..56bc65b
--- /dev/null
+++ b/FHSCheck.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : FHSCheck.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Fri Oct 15 17:40:32 1999
+# Version : $Id: FHSCheck.py 1578 2009-03-23 18:47:03Z scop $
+# Purpose : check FHS conformity
+#############################################################################
+
+import re
+
+from Filter import addDetails, printWarning
+import AbstractCheck
+
+
+class FHSCheck(AbstractCheck.AbstractCheck):
+ usr_regex = re.compile("^/usr/([^/]+)/")
+ usr_subdir = ('X11R6', 'X386', 'bin', 'games', 'include', 'lib', 'lib64',
+ 'local', 'sbin', 'share', 'src', 'spool', 'tmp')
+ var_regex = re.compile("^/var/([^/]+)/")
+ var_fsstnd = ('adm', 'catman', 'local', 'named', 'nis', 'preserve')
+ var_subdir = ('account', 'lib', 'cache', 'crash', 'games', 'lock', 'log',
+ 'opt', 'run', 'spool', 'state', 'tmp', 'yp', 'www', 'ftp')
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, "FHSCheck")
+
+ def check(self, pkg):
+ # Check only binary package
+ if pkg.isSource():
+ return
+
+ var_list = []
+ usr_list = []
+
+ for fname in pkg.files():
+ s = FHSCheck.usr_regex.search(fname)
+ if s:
+ d = s.group(1)
+ if d not in FHSCheck.usr_subdir and d not in usr_list:
+ printWarning(pkg, "non-standard-dir-in-usr", d)
+ usr_list.append(d)
+ else:
+ s = FHSCheck.var_regex.search(fname)
+ if s:
+ d = s.group(1)
+ if d in var_list:
+ continue
+ if d in FHSCheck.var_fsstnd:
+ printWarning(pkg, "FSSTND-dir-in-var", fname)
+ var_list.append(d)
+ elif d not in FHSCheck.var_subdir:
+ printWarning(pkg, "non-standard-dir-in-var", d)
+ var_list.append(d)
+
+# Create an object to enable the auto registration of the test
+check = FHSCheck()
+
+addDetails(
+'non-standard-dir-in-usr',
+"""Your package is creating a non-standard subdirectory in /usr. The standard
+directories are:
+%s.""" % ", ".join(FHSCheck.usr_subdir),
+
+'FSSTND-dir-in-var',
+"""Your package is creating an illegal directory in /var. The FSSTND (illegal)
+ones are:
+%s.""" % ", ".join(FHSCheck.var_fsstnd),
+
+'non-standard-dir-in-var',
+"""Your package is creating a non-standard subdirectory in /var. The standard
+directories are:
+%s.""" % ", ".join(FHSCheck.var_subdir),
+)
+
+# FHSCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/FilesCheck.py b/FilesCheck.py
new file mode 100644
index 0000000..eb314e0
--- /dev/null
+++ b/FilesCheck.py
@@ -0,0 +1,1341 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : FilesCheck.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Mon Oct 4 19:32:49 1999
+# Version : $Id: FilesCheck.py 1894 2011-11-24 21:56:18Z scop $
+# Purpose : test various aspects on files: locations, owner, groups,
+# permission, setuid, setgid...
+#############################################################################
+
+from datetime import datetime
+import commands
+import os
+import re
+import stat
+import string
+
+import rpm
+
+from Filter import addDetails, printError, printWarning
+from Pkg import catcmd, is_utf8, is_utf8_str
+import AbstractCheck
+import Config
+
+
+# must be kept in sync with the filesystem package
+STANDARD_DIRS = (
+ '/',
+ '/bin',
+ '/boot',
+ '/etc',
+ '/etc/X11',
+ '/etc/opt',
+ '/etc/profile.d',
+ '/etc/skel',
+ '/etc/xinetd.d',
+ '/home',
+ '/lib',
+ '/lib/modules',
+ '/lib64',
+ '/media',
+ '/mnt',
+ '/mnt/cdrom',
+ '/mnt/disk',
+ '/mnt/floppy',
+ '/opt',
+ '/proc',
+ '/root',
+ '/sbin',
+ '/selinux',
+ '/srv',
+ '/sys',
+ '/tmp',
+ '/usr',
+ '/usr/X11R6',
+ '/usr/X11R6/bin',
+ '/usr/X11R6/doc',
+ '/usr/X11R6/include',
+ '/usr/X11R6/lib',
+ '/usr/X11R6/lib64',
+ '/usr/X11R6/man',
+ '/usr/X11R6/man/man1',
+ '/usr/X11R6/man/man2',
+ '/usr/X11R6/man/man3',
+ '/usr/X11R6/man/man4',
+ '/usr/X11R6/man/man5',
+ '/usr/X11R6/man/man6',
+ '/usr/X11R6/man/man7',
+ '/usr/X11R6/man/man8',
+ '/usr/X11R6/man/man9',
+ '/usr/X11R6/man/mann',
+ '/usr/bin',
+ '/usr/bin/X11',
+ '/usr/etc',
+ '/usr/games',
+ '/usr/include',
+ '/usr/lib',
+ '/usr/lib/X11',
+ '/usr/lib/games',
+ '/usr/lib/gcc-lib',
+ '/usr/lib/menu',
+ '/usr/lib64',
+ '/usr/lib64/gcc-lib',
+ '/usr/local',
+ '/usr/local/bin',
+ '/usr/local/doc',
+ '/usr/local/etc',
+ '/usr/local/games',
+ '/usr/local/info',
+ '/usr/local/lib',
+ '/usr/local/lib64',
+ '/usr/local/man',
+ '/usr/local/man/man1',
+ '/usr/local/man/man2',
+ '/usr/local/man/man3',
+ '/usr/local/man/man4',
+ '/usr/local/man/man5',
+ '/usr/local/man/man6',
+ '/usr/local/man/man7',
+ '/usr/local/man/man8',
+ '/usr/local/man/man9',
+ '/usr/local/man/mann',
+ '/usr/local/sbin',
+ '/usr/local/share',
+ '/usr/local/share/man',
+ '/usr/local/share/man/man1',
+ '/usr/local/share/man/man2',
+ '/usr/local/share/man/man3',
+ '/usr/local/share/man/man4',
+ '/usr/local/share/man/man5',
+ '/usr/local/share/man/man6',
+ '/usr/local/share/man/man7',
+ '/usr/local/share/man/man8',
+ '/usr/local/share/man/man9',
+ '/usr/local/share/man/mann',
+ '/usr/local/src',
+ '/usr/sbin',
+ '/usr/share',
+ '/usr/share/dict',
+ '/usr/share/doc',
+ '/usr/share/icons',
+ '/usr/share/info',
+ '/usr/share/man',
+ '/usr/share/man/man1',
+ '/usr/share/man/man2',
+ '/usr/share/man/man3',
+ '/usr/share/man/man4',
+ '/usr/share/man/man5',
+ '/usr/share/man/man6',
+ '/usr/share/man/man7',
+ '/usr/share/man/man8',
+ '/usr/share/man/man9',
+ '/usr/share/man/mann',
+ '/usr/share/misc',
+ '/usr/src',
+ '/usr/tmp',
+ '/var',
+ '/var/cache',
+ '/var/db',
+ '/var/lib',
+ '/var/lib/games',
+ '/var/lib/misc',
+ '/var/lib/rpm',
+ '/var/local',
+ '/var/lock',
+ '/var/lock/subsys',
+ '/var/log',
+ '/var/mail',
+ '/var/nis',
+ '/var/opt',
+ '/var/preserve',
+ '/var/run',
+ '/var/spool',
+ '/var/spool/mail',
+ '/var/tmp',
+ )
+
+DEFAULT_GAMES_GROUPS = 'Games'
+
+DEFAULT_DANGLING_EXCEPTIONS = (['consolehelper$', 'usermode-consoleonly'],
+ )
+
+# Standard users and groups from LSB Core 4.0.0: 21.2 User & Group Names
+DEFAULT_STANDARD_USERS = ('root', 'bin', 'daemon', 'adm', 'lp', 'sync',
+ 'shutdown', 'halt', 'mail', 'news', 'uucp',
+ 'operator', 'man', 'nobody',)
+DEFAULT_STANDARD_GROUPS = ('root', 'bin', 'daemon', 'adm', 'lp', 'sync',
+ 'shutdown', 'halt', 'mail', 'news', 'uucp',
+ 'man', 'nobody',)
+
+tmp_regex = re.compile('^/tmp/|^(/var|/usr)/tmp/')
+sub_bin_regex = re.compile('^(/usr)?/s?bin/\S+/')
+backup_regex = re.compile('(~|\#[^/]+\#|\.orig|\.rej)$')
+compr_regex = re.compile('\.(gz|z|Z|zip|bz2|lzma|xz)$')
+absolute_regex = re.compile('^/([^/]+)')
+absolute2_regex = re.compile('^/?([^/]+)')
+points_regex = re.compile('^\.\./(.*)')
+doc_regex = re.compile('^/usr(/share|/X11R6)?/(doc|man|info)/')
+bin_regex = re.compile('^/(?:usr/(?:s?bin|games)|s?bin)/(.*)')
+includefile_regex = re.compile('\.(c|h)(pp|xx)?$', re.IGNORECASE)
+develfile_regex = re.compile('\.(a|cmxa?|mli?)$')
+buildconfigfile_regex = re.compile('(\.pc|/bin/.+-config)$')
+# room for improvement with catching more -R, but also for false positives...
+buildconfig_rpath_regex = re.compile('(?:-rpath|Wl,-R)\\b')
+sofile_regex = re.compile('/lib(64)?/(.+/)?lib[^/]+\.so$')
+devel_regex = re.compile('(.*)-(debug(info)?|devel|headers|source|static)$')
+debuginfo_package_regex = re.compile('-debug(info)?$')
+lib_regex = re.compile('lib(64)?/lib[^/]*\.so\..*')
+ldconfig_regex = re.compile('^[^#]*ldconfig', re.MULTILINE)
+depmod_regex = re.compile('^[^#]*depmod', re.MULTILINE)
+install_info_regex = re.compile('^[^#]*install-info', re.MULTILINE)
+perl_temp_file_regex = re.compile('.*perl.*/(\.packlist|perllocal\.pod)$')
+scm_regex = re.compile('/CVS/[^/]+$|/\.(bzr|cvs|git|hg)ignore$|/\.hgtags$|/\.(bzr|git|hg|svn)/|/(\.arch-ids|{arch})/')
+games_path_regex = re.compile('^/usr(/lib(64)?)?/games/')
+games_group_regex = re.compile(Config.getOption('RpmGamesGroups', DEFAULT_GAMES_GROUPS))
+dangling_exceptions = Config.getOption('DanglingSymlinkExceptions', DEFAULT_DANGLING_EXCEPTIONS)
+logrotate_regex = re.compile('^/etc/logrotate\.d/(.*)')
+module_rpms_ok = Config.getOption('KernelModuleRPMsOK', True)
+kernel_modules_regex = re.compile('^/lib/modules/(2\.[23456]\.[0-9]+[^/]*?)/')
+kernel_package_regex = re.compile('^kernel(22)?(-)?(smp|enterprise|bigmem|secure|BOOT|i686-up-4GB|p3-smp-64GB)?')
+normal_zero_length_regex = re.compile('^/etc/security/console\.apps/|/\.nosearch$|/__init__\.py$')
+perl_regex = re.compile('^/usr/lib/perl5/(?:vendor_perl/)?([0-9]+\.[0-9]+)\.([0-9]+)/')
+python_regex = re.compile('^/usr/lib(?:64)?/python([.0-9]+)/')
+python_bytecode_regex_pep3147 = re.compile('^(.*)/__pycache__/(.*)\.(.*)(\.py[oc])$')
+python_bytecode_regex = re.compile('^(.*)(\.py[oc])$')
+python_default_version = Config.getOption('PythonDefaultVersion', None)
+perl_version_trick = Config.getOption('PerlVersionTrick', True)
+log_regex = re.compile('^/var/log/[^/]+$')
+lib_path_regex = re.compile('^(/usr(/X11R6)?)?/lib(64)?')
+lib_package_regex = re.compile('^(lib|.+-libs)')
+hidden_file_regex = re.compile('/\.[^/]*$')
+manifest_perl_regex = re.compile('^/usr/share/doc/perl-.*/MANIFEST(\.SKIP)?$')
+shebang_regex = re.compile('^#!\s*(\S+)')
+interpreter_regex = re.compile('^/(usr/)?(s?bin|games|libexec(/.+)?|(lib(64)?|share)/.+)/[^/]+$')
+script_regex = re.compile('^/((usr/)?s?bin|etc/(rc\.d/init\.d|X11/xinit\.d|cron\.(hourly|daily|monthly|weekly)))/')
+sourced_script_regex = re.compile('^/etc/(bash_completion\.d|profile\.d)/')
+use_utf8 = Config.getOption('UseUTF8', Config.USEUTF8_DEFAULT)
+skipdocs_regex = re.compile(Config.getOption('SkipDocsRegexp', '\.(?:rtf|x?html?|svg|ml[ily]?)$'), re.IGNORECASE)
+meta_package_regex = re.compile(Config.getOption('MetaPackageRegexp', '^(bundle|task)-'))
+filesys_packages = ['filesystem'] # TODO: make configurable?
+quotes_regex = re.compile('[\'"]+')
+
+for idx in range(0, len(dangling_exceptions)):
+ dangling_exceptions[idx][0] = re.compile(dangling_exceptions[idx][0])
+del idx
+
+use_relative_symlinks = Config.getOption("UseRelativeSymlinks", True)
+
+standard_groups = Config.getOption('StandardGroups', DEFAULT_STANDARD_GROUPS)
+standard_users = Config.getOption('StandardUsers', DEFAULT_STANDARD_USERS)
+
+non_readable_regexs = (re.compile('^/var/log/'),
+ re.compile('^/etc/(g?shadow-?|securetty)$'))
+
+man_base_regex = re.compile(r'^/usr(?:/share)?/man/man[^/]+/(.+)\.[1-9n]')
+man_warn_regex = re.compile(r'^([^:]+:)\d+:\s*')
+man_nowarn_regex = re.compile(
+ # From Lintian: ignore common undefined macros from pod2man << Perl 5.10
+ r'\`(Tr|IX)\' not defined|'
+ # .so entries won't resolve as we're dealing with stdin
+ r'No such file or directory|'
+ # TODO, better handling for these (see e.g. Lintian)
+ r'(can\'t break|cannot adjust) line')
+man_warn_category = Config.getOption('ManWarningCategory', 'mac')
+
+fsf_license_regex = re.compile('(GNU((\s+(Library|Lesser|Affero))?(\s+General)?\s+Public|\s+Free\s+Documentation)\s+Licen[cs]e|(GP|FD)L)', re.IGNORECASE)
+fsf_wrong_address_regex = re.compile('(675\s+Mass\s+Ave|59\s+Temple\s+Place|Franklin\s+Steet|02139|02111-1307)', re.IGNORECASE)
+
+# loosely inspired from Python Cookbook
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/173220
+text_characters = "".join(map(chr, range(32, 127)) + list("\n\r\t\b"))
+_null_trans = string.maketrans("", "")
+
+def peek(filename, pkg, length=1024):
+ """Peek into a file, return a chunk from its beginning and a flag if it
+ seems to be a text file."""
+ fobj = None
+ chunk = None
+ try:
+ fobj = open(filename, 'rb')
+ chunk = fobj.read(length)
+ fobj.close()
+ except Exception, e: # eg. https://bugzilla.redhat.com/209876
+ printWarning(pkg, 'read-error', e)
+ if fobj:
+ fobj.close()
+ return (chunk, False)
+
+ if "\0" in chunk:
+ return (chunk, False)
+
+ if not chunk: # Empty files are considered text
+ return (chunk, True)
+
+ # PDF's are binary but often detected as text by the algorithm below
+ if filename.lower().endswith('.pdf') and chunk.startswith('%PDF-'):
+ return (chunk, False)
+
+ # Get the non-text characters (maps a character to itself then
+ # use the 'remove' option to get rid of the text characters.)
+ t = chunk.translate(_null_trans, text_characters)
+
+ # If more than 30% non-text characters, then consider it a binary file
+ istext = float(len(t))/len(chunk) <= 0.30
+ return (chunk, istext)
+
+# See Python/import.c (in the trunk and py3k branches) for a full list of
+# the values here.
+_python_magic_values = {
+ '2.2': 60717,
+ '2.3': 62011,
+ '2.4': 62061,
+ '2.5': 62131,
+ '2.6': 62161,
+ '2.7': 62211,
+ '3.0': 3130,
+ '3.1': 3150,
+ '3.2': 3180,
+ '3.3': 3190,
+ }
+
+def get_expected_pyc_magic(path):
+ """.pyc/.pyo files embed a 4-byte magic value identifying which version of
+ the python bytecode ABI they are for. Given a path to a .pyc/.pyo file,
+ return a (magic ABI value, python version) tuple. For example,
+ '/usr/lib/python3.1/foo.pyc' should return (3151, '3.1').
+ The first value will be None if the python version was not resolved
+ from the given pathname and the PythonDefaultVersion configuration
+ variable is not set, or if we don't know the magic ABI value for the
+ python version (no matter from which source the version came from).
+ The second value will be None if a python version could not be resolved
+ from the given pathname."""
+
+ ver_from_path = None
+ m = python_regex.search(path)
+ if m:
+ ver_from_path = m.group(1)
+
+ expected_version = ver_from_path or python_default_version
+ expected_magic_value = _python_magic_values.get(expected_version)
+
+ if not expected_magic_value:
+ return (None, ver_from_path)
+
+ # In Python 2, if Py_UnicodeFlag is set, Python's import code uses a value
+ # one higher, but this is off by default. In Python 3.0 and 3.1 (but no
+ # longer in 3.2), it always uses the value one higher:
+ if expected_version[:3] in ('3.0', '3.1'):
+ expected_magic_value += 1
+
+ return (expected_magic_value, ver_from_path)
+
+def py_demarshal_long(b):
+ """Counterpart to Python's PyMarshal_ReadLongFromFile, operating on the
+ bytes in a string."""
+ return (ord(b[0])
+ + (ord(b[1]) << 8)
+ + (ord(b[2]) << 16)
+ + (ord(b[3]) << 24))
+
+def python_bytecode_to_script(path):
+ """Given a python bytecode path, give the path of the .py file
+ (or None if not python bytecode)."""
+
+ res = python_bytecode_regex_pep3147.search(path)
+ if res:
+ return res.group(1) + '/' + res.group(2) + '.py'
+
+ res = python_bytecode_regex.search(path)
+ if res:
+ return res.group(1) + '.py'
+
+ return None
+
+class FilesCheck(AbstractCheck.AbstractCheck):
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, 'FilesCheck')
+
+ def check(self, pkg):
+
+ files = pkg.files()
+
+ if use_utf8:
+ for filename in files:
+ if not is_utf8_str(filename):
+ printError(pkg, 'filename-not-utf8', filename)
+
+ # Rest of the checks are for binary packages only
+ if pkg.isSource():
+ return
+
+ # Check if the package is a development package
+ devel_pkg = devel_regex.search(pkg.name)
+
+ config_files = pkg.configFiles()
+ ghost_files = pkg.ghostFiles()
+ doc_files = pkg.docFiles()
+ req_names = pkg.req_names()
+ lib_package = lib_package_regex.search(pkg.name)
+ is_kernel_package = kernel_package_regex.search(pkg.name)
+ debuginfo_package = debuginfo_package_regex.search(pkg.name)
+
+ # report these errors only once
+ perl_dep_error = False
+ python_dep_error = False
+ lib_file = False
+ non_lib_file = None
+ log_file = None
+ logrotate_file = False
+ debuginfo_srcs = False
+ debuginfo_debugs = False
+
+ if not doc_files:
+ printWarning(pkg, 'no-documentation')
+
+ if files:
+ if meta_package_regex.search(pkg.name):
+ printWarning(pkg, 'file-in-meta-package')
+ elif debuginfo_package:
+ printError(pkg, 'empty-debuginfo-package')
+
+ # Unique (rdev, inode) combinations
+ hardlinks = {}
+
+ # All executable files from standard bin dirs (basename => [paths])
+ # Hack: basenames with empty paths links are symlinks (not subject
+ # to duplicate binary check, but yes for man page existence check)
+ bindir_exes = {}
+
+ # All man page "base" names (without section etc extensions)
+ man_basenames = set()
+
+ for f, pkgfile in files.items():
+ mode = pkgfile.mode
+ user = pkgfile.user
+ group = pkgfile.group
+ link = pkgfile.linkto
+ size = pkgfile.size
+ rdev = pkgfile.rdev
+ inode = pkgfile.inode
+ is_doc = f in doc_files
+ nonexec_file = False
+
+ for match in AbstractCheck.macro_regex.findall(f):
+ printWarning(pkg, 'unexpanded-macro', f, match)
+ if standard_users and user not in standard_users:
+ printWarning(pkg, 'non-standard-uid', f, user)
+ if standard_groups and group not in standard_groups:
+ printWarning(pkg, 'non-standard-gid', f, group)
+
+ if not module_rpms_ok and kernel_modules_regex.search(f) and not \
+ is_kernel_package:
+ printError(pkg, "kernel-modules-not-in-kernel-packages", f)
+
+ if tmp_regex.search(f):
+ printError(pkg, 'dir-or-file-in-tmp', f)
+ elif f.startswith('/mnt/'):
+ printError(pkg, 'dir-or-file-in-mnt', f)
+ elif f.startswith('/opt/'):
+ printError(pkg, 'dir-or-file-in-opt', f)
+ elif f.startswith('/usr/local/'):
+ printError(pkg, 'dir-or-file-in-usr-local', f)
+ elif f.startswith('/var/local/'):
+ printError(pkg, 'dir-or-file-in-var-local', f)
+ elif f.startswith('/var/run/'):
+ if f not in ghost_files:
+ printWarning(pkg, 'non-ghost-in-var-run', f)
+ elif f.startswith('/var/lock/'):
+ if f not in ghost_files:
+ printWarning(pkg, 'non-ghost-in-var-lock', f)
+ elif sub_bin_regex.search(f):
+ printError(pkg, 'subdir-in-bin', f)
+ elif f.startswith('/home/'):
+ printError(pkg, 'dir-or-file-in-home', f)
+ elif '/site_perl/' in f:
+ printWarning(pkg, 'siteperl-in-perl-module', f)
+
+ if backup_regex.search(f):
+ printError(pkg, 'backup-file-in-package', f)
+ elif scm_regex.search(f):
+ printError(pkg, 'version-control-internal-file', f)
+ elif f.endswith('/.htaccess'):
+ printError(pkg, 'htaccess-file', f)
+ elif hidden_file_regex.search(f) and not f.startswith("/etc/skel/"):
+ printWarning(pkg, 'hidden-file-or-dir', f)
+ elif manifest_perl_regex.search(f):
+ printWarning(pkg, 'manifest-in-perl-module', f)
+ elif f == '/usr/info/dir' or f == '/usr/share/info/dir':
+ printError(pkg, 'info-dir-file', f)
+
+ res = logrotate_regex.search(f)
+ if res:
+ logrotate_file = True
+ if res.group(1) != pkg.name:
+ printError(pkg, 'incoherent-logrotate-file', f)
+
+ if link != '':
+ ext = compr_regex.search(link)
+ if ext:
+ if not re.compile('\.' + ext.group(1) + '$').search(f):
+ printError(pkg, 'compressed-symlink-with-wrong-ext',
+ f, link)
+
+ perm = mode & 07777
+
+ if log_regex.search(f):
+ log_file = f
+
+ # Hardlink check
+ hardlink = hardlinks.get((rdev, inode))
+ if hardlink and os.path.dirname(hardlink) != os.path.dirname(f):
+ printWarning(pkg, 'cross-directory-hard-link', f, hardlink)
+ hardlinks[(rdev, inode)] = f
+
+ # normal file check
+ if stat.S_ISREG(mode):
+
+ # set[ug]id bit check
+ if stat.S_ISGID & mode or stat.S_ISUID & mode:
+ if stat.S_ISUID & mode:
+ printError(pkg, 'setuid-binary', f, user, oct(perm))
+ if stat.S_ISGID & mode:
+ if not (group == 'games' and
+ (games_path_regex.search(f) or
+ games_group_regex.search(
+ pkg[rpm.RPMTAG_GROUP]))):
+ printError(pkg, 'setgid-binary', f, group,
+ oct(perm))
+ if mode & 0777 != 0755:
+ printError(pkg, 'non-standard-executable-perm', f,
+ oct(perm))
+
+ # Prefetch scriptlets, strip quotes from them (#169)
+ postin = pkg[rpm.RPMTAG_POSTIN] or \
+ pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
+ if postin:
+ postin = quotes_regex.sub('', postin)
+ postun = pkg[rpm.RPMTAG_POSTUN] or \
+ pkg.scriptprog(rpm.RPMTAG_POSTUNPROG)
+ if postun:
+ postun = quotes_regex.sub('', postun)
+
+ if not devel_pkg:
+ if lib_path_regex.search(f):
+ lib_file = True
+ elif not is_doc:
+ non_lib_file = f
+
+ if log_regex.search(f):
+ nonexec_file = True
+ if user != 'root':
+ printError(pkg, 'non-root-user-log-file', f, user)
+ if group != 'root':
+ printError(pkg, 'non-root-group-log-file', f, group)
+ if f not in ghost_files:
+ printError(pkg, 'non-ghost-file', f)
+
+ chunk = None
+ istext = False
+ if os.access(pkgfile.path, os.R_OK):
+ (chunk, istext) = peek(pkgfile.path, pkg)
+
+ interpreter = None
+ if chunk:
+ res = shebang_regex.search(chunk)
+ if res:
+ interpreter = res.group(1)
+
+ if doc_regex.search(f):
+ if not interpreter:
+ nonexec_file = True
+ if not is_doc:
+ printError(pkg, 'not-listed-as-documentation', f)
+
+ # check ldconfig call in %post and %postun
+ if lib_regex.search(f):
+ if not postin:
+ printError(pkg, 'library-without-ldconfig-postin', f)
+ else:
+ if not ldconfig_regex.search(postin):
+ printError(pkg, 'postin-without-ldconfig', f)
+
+ if not postun:
+ printError(pkg, 'library-without-ldconfig-postun', f)
+ else:
+ if not ldconfig_regex.search(postun):
+ printError(pkg, 'postun-without-ldconfig', f)
+
+ # check depmod call in %post and %postun
+ res = not is_kernel_package and kernel_modules_regex.search(f)
+ if res:
+ kernel_version = res.group(1)
+ kernel_version_regex = re.compile(
+ '\\bdepmod\s+-a.*F\s+/boot/System\.map-' +
+ re.escape(kernel_version) + '\\b.*\\b' +
+ re.escape(kernel_version) + '\\b',
+ re.MULTILINE | re.DOTALL)
+
+ if not postin or not depmod_regex.search(postin):
+ printError(pkg, 'module-without-depmod-postin', f)
+ # check that we run depmod on the right kernel
+ elif not kernel_version_regex.search(postin):
+ printError(pkg, 'postin-with-wrong-depmod', f)
+
+ if not postun or not depmod_regex.search(postun):
+ printError(pkg, 'module-without-depmod-postun', f)
+ # check that we run depmod on the right kernel
+ elif not kernel_version_regex.search(postun):
+ printError(pkg, 'postun-with-wrong-depmod', f)
+
+ # check install-info call in %post and %postun
+ if f.startswith('/usr/share/info/'):
+ if not postin:
+ printError(pkg,
+ 'info-files-without-install-info-postin', f)
+ elif not install_info_regex.search(postin):
+ printError(pkg, 'postin-without-install-info', f)
+
+ preun = pkg[rpm.RPMTAG_PREUN] or \
+ pkg.scriptprog(rpm.RPMTAG_PREUNPROG)
+ if not postun and not preun:
+ printError(pkg,
+ 'info-files-without-install-info-postun', f)
+ elif (not postun or
+ not install_info_regex.search(postun)) and \
+ (not preun or not install_info_regex.search(preun)):
+ printError(pkg, 'postin-without-install-info', f)
+
+ # check perl temp file
+ if perl_temp_file_regex.search(f):
+ printWarning(pkg, 'perl-temp-file', f)
+
+ is_buildconfig = buildconfigfile_regex.search(f) and True
+
+ # check rpaths in buildconfig files
+ if is_buildconfig:
+ ln = pkg.grep(buildconfig_rpath_regex, f)
+ if ln:
+ printError(pkg, 'rpath-in-buildconfig', f, 'lines', ln)
+
+ res = bin_regex.search(f)
+ if res:
+ if mode & 0111 == 0:
+ printWarning(pkg, 'non-executable-in-bin', f, oct(perm))
+ else:
+ exe = res.group(1)
+ if "/" not in exe:
+ bindir_exes.setdefault(exe, []).append(f)
+
+ if not devel_pkg and not is_doc and \
+ (includefile_regex.search(f) or \
+ develfile_regex.search(f) or is_buildconfig):
+ printWarning(pkg, 'devel-file-in-non-devel-package', f)
+ if mode & 0444 != 0444 and perm & 07000 == 0:
+ ok_nonreadable = False
+ for regex in non_readable_regexs:
+ if regex.search(f):
+ ok_nonreadable = True
+ break
+ if not ok_nonreadable:
+ printError(pkg, 'non-readable', f, oct(perm))
+ if size == 0 and not normal_zero_length_regex.search(f) and \
+ f not in ghost_files:
+ printError(pkg, 'zero-length', f)
+
+ if mode & 0002 != 0:
+ printError(pkg, 'world-writable', f, oct(perm))
+
+ if not perl_dep_error:
+ res = perl_regex.search(f)
+ if res:
+ if perl_version_trick:
+ vers = res.group(1) + '.' + res.group(2)
+ else:
+ vers = res.group(1) + res.group(2)
+ if not (pkg.check_versioned_dep('perl-base', vers) or
+ pkg.check_versioned_dep('perl', vers)):
+ printError(pkg, 'no-dependency-on',
+ 'perl-base', vers)
+ perl_dep_error = True
+
+ if not python_dep_error:
+ res = python_regex.search(f)
+ if res and not (pkg.check_versioned_dep('python-base',
+ res.group(1)) or
+ pkg.check_versioned_dep('python',
+ res.group(1))):
+ printError(pkg, 'no-dependency-on', 'python-base',
+ res.group(1))
+ python_dep_error = True
+
+ source_file = python_bytecode_to_script(f)
+ if source_file:
+ if source_file in files:
+ if chunk:
+ # Verify that the magic ABI value embedded in the
+ # .pyc header is correct
+ found_magic = py_demarshal_long(chunk[:4]) & 0xffff
+ exp_magic, exp_version = get_expected_pyc_magic(f)
+ if exp_magic and found_magic != exp_magic:
+ found_version = 'unknown'
+ for (pv, pm) in _python_magic_values.items():
+ if pm == found_magic:
+ found_version = pv
+ break
+ # If expected version was from the file path,
+ # issue # an error, otherwise a warning.
+ msg = (pkg, 'python-bytecode-wrong-magic-value',
+ f, "expected %d (%s), found %d (%s)" %
+ (exp_magic,
+ exp_version or python_default_version,
+ found_magic, found_version))
+ if exp_version is not None:
+ printError(*msg)
+ else:
+ printWarning(*msg)
+
+ # Verify that the timestamp embedded in the .pyc
+ # header matches the mtime of the .py file:
+ pyc_timestamp = py_demarshal_long(chunk[4:8])
+ # If it's a symlink, check target file mtime.
+ srcfile = pkg.readlink(files[source_file])
+ if not srcfile:
+ printWarning(
+ pkg, 'python-bytecode-without-source', f)
+ elif pyc_timestamp != srcfile.mtime:
+ cts = datetime.fromtimestamp(
+ pyc_timestamp).isoformat()
+ sts = datetime.fromtimestamp(
+ srcfile.mtime).isoformat()
+ printError(pkg,
+ 'python-bytecode-inconsistent-mtime',
+ f, cts, srcfile.name, sts)
+ else:
+ printWarning(pkg, 'python-bytecode-without-source', f)
+
+ # normal executable check
+ if mode & stat.S_IXUSR and perm != 0755:
+ printError(pkg, 'non-standard-executable-perm',
+ f, oct(perm))
+ if mode & 0111 != 0:
+ if f in config_files:
+ printError(pkg, 'executable-marked-as-config-file', f)
+ if not nonexec_file:
+ # doc_regex and log_regex checked earlier, no match,
+ # check rest of usual cases here. Sourced scripts have
+ # their own check, so disregard them here.
+ nonexec_file = f.endswith('.pc') or \
+ compr_regex.search(f) or \
+ includefile_regex.search(f) or \
+ develfile_regex.search(f) or \
+ logrotate_regex.search(f)
+ if nonexec_file:
+ printWarning(pkg, 'spurious-executable-perm', f)
+ elif f.startswith('/etc/') and f not in config_files and \
+ f not in ghost_files:
+ printWarning(pkg, 'non-conffile-in-etc', f)
+
+ if pkg.arch == 'noarch' and f.startswith('/usr/lib64/python'):
+ printError(pkg, 'noarch-python-in-64bit-path', f)
+
+ if debuginfo_package:
+ if f.endswith('.debug'):
+ debuginfo_debugs = True
+ else:
+ debuginfo_srcs = True
+
+ res = man_base_regex.search(f)
+ if res:
+ man_basenames.add(res.group(1))
+ if use_utf8 and chunk:
+ # TODO: better shell escaping or seq based invocation
+ cmd = commands.getstatusoutput(
+ 'env LC_ALL=C %s "%s" | gtbl | '
+ 'env LC_ALL=en_US.UTF-8 groff -mtty-char -Tutf8 '
+ '-P-c -mandoc -w%s >/dev/null' %
+ (catcmd(f), pkgfile.path, man_warn_category))
+ for line in cmd[1].split("\n"):
+ res = man_warn_regex.search(line)
+ if not res or man_nowarn_regex.search(line):
+ continue
+ printWarning(pkg, "manual-page-warning", f,
+ line[res.end(1):])
+
+ # text file checks
+ if istext:
+ # ignore perl module shebang -- TODO: disputed...
+ if f.endswith('.pm'):
+ interpreter = None
+ # sourced scripts should not be executable
+ if sourced_script_regex.search(f):
+ if interpreter:
+ printError(pkg,
+ 'sourced-script-with-shebang', f,
+ interpreter)
+ if mode & 0111 != 0:
+ printError(pkg, 'executable-sourced-script',
+ f, oct(perm))
+ # ...but executed ones should
+ elif interpreter or mode & 0111 != 0 or \
+ script_regex.search(f):
+ if interpreter:
+ if not interpreter_regex.search(interpreter):
+ printError(pkg, 'wrong-script-interpreter',
+ f, interpreter)
+ elif not nonexec_file and not \
+ (lib_path_regex.search(f) and
+ f.endswith('.la')):
+ printError(pkg, 'script-without-shebang', f)
+
+ if mode & 0111 == 0 and not is_doc:
+ printError(pkg, 'non-executable-script', f,
+ oct(perm), interpreter)
+ if '\r' in chunk:
+ printError(
+ pkg, 'wrong-script-end-of-line-encoding', f)
+ elif is_doc and not skipdocs_regex.search(f):
+ if '\r' in chunk:
+ printWarning(
+ pkg, 'wrong-file-end-of-line-encoding', f)
+ # We check only doc text files for UTF-8-ness;
+ # checking everything may be slow and can generate
+ # lots of unwanted noise.
+ if use_utf8 and not is_utf8(pkgfile.path):
+ printWarning(pkg, 'file-not-utf8', f)
+ if fsf_license_regex.search(chunk) and \
+ fsf_wrong_address_regex.search(chunk):
+ printError(pkg, 'incorrect-fsf-address', f)
+
+ elif is_doc and chunk and compr_regex.search(f):
+ ff = compr_regex.sub('', f)
+ if not skipdocs_regex.search(ff):
+ # compressed docs, eg. info and man files etc
+ if use_utf8 and not is_utf8(pkgfile.path):
+ printWarning(pkg, 'file-not-utf8', f)
+
+ # normal dir check
+ elif stat.S_ISDIR(mode):
+ if mode & 01002 == 2: # world writable without sticky bit
+ printError(pkg, 'world-writable', f, oct(perm))
+ if perm != 0755:
+ printError(pkg, 'non-standard-dir-perm', f, oct(perm))
+ if pkg.name not in filesys_packages and f in STANDARD_DIRS:
+ printError(pkg, 'standard-dir-owned-by-package', f)
+ if hidden_file_regex.search(f):
+ printWarning(pkg, 'hidden-file-or-dir', f)
+
+
+ # symbolic link check
+ elif stat.S_ISLNK(mode):
+
+ is_so = sofile_regex.search(f)
+ if not devel_pkg and is_so and not link.endswith('.so'):
+ printWarning(pkg, 'devel-file-in-non-devel-package', f)
+
+ res = man_base_regex.search(f)
+ if res:
+ man_basenames.add(res.group(1))
+ else:
+ res = bin_regex.search(f)
+ if res:
+ exe = res.group(1)
+ if "/" not in exe:
+ bindir_exes.setdefault(exe, [])
+
+ # absolute link
+ r = absolute_regex.search(link)
+ if r:
+ if not is_so and link not in files and \
+ link not in req_names:
+ is_exception = False
+ for e in dangling_exceptions:
+ if e[0].search(link):
+ is_exception = e[1]
+ break
+ if is_exception:
+ if is_exception not in req_names:
+ printWarning(pkg, 'no-dependency-on',
+ is_exception)
+ else:
+ printWarning(pkg, 'dangling-symlink', f, link)
+ linktop = r.group(1)
+ r = absolute_regex.search(f)
+ if r:
+ filetop = r.group(1)
+ if filetop == linktop or use_relative_symlinks:
+ printWarning(pkg, 'symlink-should-be-relative',
+ f, link)
+ # relative link
+ else:
+ if not is_so:
+ abslink = '%s/%s' % (os.path.dirname(f), link)
+ abslink = os.path.normpath(abslink)
+ if abslink not in files and abslink not in req_names:
+ is_exception = False
+ for e in dangling_exceptions:
+ if e[0].search(link):
+ is_exception = e[1]
+ break
+ if is_exception:
+ if is_exception not in req_names:
+ printWarning(pkg, 'no-dependency-on',
+ is_exception)
+ else:
+ printWarning(pkg, 'dangling-relative-symlink',
+ f, link)
+ pathcomponents = f.split('/')[1:]
+ r = points_regex.search(link)
+ lastpop = None
+ mylink = None
+
+ while r:
+ mylink = r.group(1)
+ if len(pathcomponents) == 0:
+ printError(pkg, 'symlink-has-too-many-up-segments',
+ f, link)
+ break
+ else:
+ lastpop = pathcomponents[0]
+ pathcomponents = pathcomponents[1:]
+ r = points_regex.search(mylink)
+
+ if mylink and lastpop:
+ r = absolute2_regex.search(mylink)
+ linktop = r.group(1)
+
+ # does the link go up and then down into the same
+ # directory?
+ #if linktop == lastpop:
+ # printWarning(pkg, 'lengthy-symlink', f, link)
+
+ # have we reached the root directory?
+ if len(pathcomponents) == 0 and linktop != lastpop \
+ and not use_relative_symlinks:
+ # relative link into other toplevel directory
+ printWarning(pkg, 'symlink-should-be-absolute', f,
+ link)
+ # check additional segments for mistakes like
+ # `foo/../bar/'
+ for linksegment in mylink.split('/'):
+ if linksegment == '..':
+ printError(
+ pkg,
+ 'symlink-contains-up-and-down-segments',
+ f, link)
+
+ if f.startswith('/etc/cron.d/'):
+ if stat.S_ISLNK(mode):
+ printError(pkg, 'symlink-crontab-file', f)
+
+ if mode & 0111:
+ printError(pkg, 'executable-crontab-file', f)
+
+ if stat.S_IWGRP & mode or stat.S_IWOTH & mode:
+ printError(pkg, 'non-owner-writeable-only-crontab-file', f)
+
+ if log_file and not logrotate_file:
+ printWarning(pkg, 'log-files-without-logrotate', log_file)
+
+ if lib_package and lib_file and non_lib_file:
+ printError(pkg, 'outside-libdir-files', non_lib_file)
+
+ if debuginfo_package and debuginfo_debugs and not debuginfo_srcs:
+ printError(pkg, 'debuginfo-without-sources')
+
+ for exe, paths in bindir_exes.items():
+ if len(paths) > 1:
+ printWarning(pkg, "duplicate-executable", exe, paths)
+ if exe not in man_basenames:
+ printWarning(pkg, "no-manual-page-for-binary", exe)
+
+# Create an object to enable the auto registration of the test
+check = FilesCheck()
+
+addDetails(
+'no-documentation',
+'''The package contains no documentation (README, doc, etc).
+You have to include documentation files.''',
+
+'not-listed-as-documentation',
+'''The documentation files of this package are not listed with
+the standard %doc tag.''',
+
+'non-standard-uid',
+'''A file in this package is owned by a non standard user.
+Standard users are:
+%s.''' % ", ".join(standard_users),
+
+'non-standard-gid',
+'''A file in this package is owned by a non standard group.
+Standard groups are:
+%s.''' % ", ".join(standard_groups),
+
+'library-without-ldconfig-postin',
+'''This package contains a library and provides no %post scriptlet containing
+a call to ldconfig.''',
+
+'postin-without-ldconfig',
+'''This package contains a library and its %post scriptlet doesn't call
+ldconfig.''',
+
+'library-without-ldconfig-postun',
+'''This package contains a library and provides no %postun scriptlet containing
+a call to ldconfig.''',
+
+'postun-without-ldconfig',
+'''This package contains a library and its %postun doesn't call ldconfig.''',
+
+'info-files-without-install-info-postin',
+'''This package contains info files and provides no %post scriptlet containing
+a call to install-info.''',
+
+'postin-without-install-info',
+'''This package contains info files and its %post doesn't call install-info.''',
+
+'info-files-without-install-info-postun',
+'''This package contains info files and provides no %postun scriptlet containing
+a call to install-info.''',
+
+'postun-without-install-info',
+'''This package contains info files and its %postun doesn't call
+install-info.''',
+
+'perl-temp-file',
+'''You have a perl temporary file in your package. Usually, this
+file is beginning with a dot (.) and contain "perl" in its name.''',
+
+'dir-or-file-in-tmp',
+'''A file in the package is located in /tmp. It's not permitted
+for packages to install files in this directory.''',
+
+'dir-or-file-in-mnt',
+'''A file in the package is located in /mnt. It's not permitted
+for packages to install files in this directory.''',
+
+'dir-or-file-in-opt',
+'''A file in the package is located in /opt. It's not permitted
+for packages to install files in this directory.''',
+
+'dir-or-file-in-usr-local',
+'''A file in the package is located in /usr/local. It's not permitted
+for packages to install files in this directory.''',
+
+'dir-or-file-in-var-local',
+'''A file in the package is located in /var/local. It's not permitted
+for packages to install files in this directory.''',
+
+'non-ghost-in-var-run',
+'''A file or directory in the package is located in /var/run. Files installed
+in this directory should be marked as %ghost and created at runtime to work
+properly in tmpfs /var/run setups.''',
+
+'non-ghost-in-var-lock',
+'''A file or directory in the package is located in /var/lock. Files installed
+in this directory should be marked as %ghost and created at runtime to work
+properly in tmpfs /var/lock setups.''',
+
+'subdir-in-bin',
+'''The package contains a subdirectory in /usr/bin. It's not permitted to
+create a subdir there. Create it in /usr/lib/ instead.''',
+
+'backup-file-in-package',
+'''You have a file whose name looks like one for backup files, usually created
+by an editor or resulting from applying unclean (fuzzy, or ones with line
+offsets) patches.''',
+
+'dir-or-file-in-home',
+'''A file in the package is located in /home. It's not permitted
+for packages to install files in this directory.''',
+
+'version-control-internal-file',
+'''You have included file(s) internally used by a version control system
+in the package. Move these files out of the package and rebuild it.''',
+
+'htaccess-file',
+'''You have individual apache configuration .htaccess file(s) in your package.
+Replace them by a central configuration file in /etc/, according to the web
+application packaging policy for your distribution.''',
+
+'info-dir-file',
+'''You have /usr/info/dir or /usr/share/info/dir in your package. It will cause
+conflicts with other packages and thus is not allowed. Please remove it and
+rebuild your package.''',
+
+'non-conffile-in-etc',
+'''A non-executable file in your package is being installed in /etc, but is not
+a configuration file. All non-executable files in /etc should be configuration
+files. Mark the file as %config in the spec file.''',
+
+'compressed-symlink-with-wrong-ext',
+'''The symlink points to a compressed file but doesn't use the same
+extension.''',
+
+'setuid-binary',
+'''The file is setuid; this may be dangerous, especially if this
+file is setuid root. Sometimes file capabilities can be used instead of
+setuid bits.''',
+
+'setgid-binary',
+'''The file is setgid. Usually this is a packaging bug. If this is a game,
+then, you should use the proper rpm group, or location.''',
+
+'non-standard-executable-perm',
+'''A standard executable should have permission set to 0755. If you get this
+message, it means that you have a wrong executable permissions in some files
+included in your package.''',
+
+'non-executable-in-bin',
+'''A file is being installed in /usr/bin, but is not an executable. Be sure
+that the file is an executable or that it has executable permissions.''',
+
+'devel-file-in-non-devel-package',
+'''A development file (usually source code) is located in a non-devel
+package. If you want to include source code in your package, be sure to
+create a development package.''',
+
+'non-standard-dir-perm',
+'''A standard directory should have permission set to 0755. If you get this
+message, it means that you have wrong directory permissions in some dirs
+included in your package.''',
+
+'spurious-executable-perm',
+'''The file is installed with executable permissions, but was identified as one
+that probably should not be executable. Verify if the executable bits are
+desired, and remove if not.''',
+
+'world-writable',
+'''A file or directory in the package is installed with world writable
+permissions, which is most likely a security issue.''',
+
+'standard-dir-owned-by-package',
+'''This package owns a directory that is part of the standard hierarchy, which
+can lead to default directory permissions or ownerships being changed to
+something non-standard.''',
+
+'no-dependency-on',
+'''
+''',
+
+'cross-directory-hard-link',
+'''File is hard linked across directories. This can cause problems in
+installations where the directories are located on different devices.''',
+
+'dangling-symlink',
+'''The target of the symbolic link does not exist within this package or its
+file based dependencies. Verify spelling of the link target and that the
+target is included in a package in this package's dependency chain.''',
+
+'symlink-should-be-relative',
+'''Absolute symlinks are problematic eg. when working with chroot environments.
+symlinks(8) is a tool that can be useful for creating/dealing with relative
+symlinks at package build time.''',
+
+'dangling-relative-symlink',
+'''The target of the symbolic link does not exist within this package or its
+file based dependencies. Verify spelling of the link target and that the
+target is included in a package in this package's dependency chain.''',
+
+'symlink-has-too-many-up-segments',
+'''
+''',
+
+'symlink-should-be-absolute',
+'''
+''',
+
+'symlink-contains-up-and-down-segments',
+'''
+''',
+
+'non-readable',
+'''The file can't be read by everybody. If this is expected (for security
+reasons), contact your rpmlint distributor to get it added to the list of
+exceptions for your distro (or add it to your local configuration if you
+installed rpmlint from the source tarball).''',
+
+'incoherent-logrotate-file',
+'''Your logrotate file should be named /etc/logrotate.d/<package name>.''',
+
+'non-root-user-log-file',
+'''If you need log files owned by a non-root user, just create a subdir in
+/var/log and put your log files in it.''',
+
+'non-root-group-log-file',
+'''If you need log files owned by a non-root group, just create a subdir in
+/var/log and put your log files in it.''',
+
+'non-ghost-file',
+'''File should be tagged %ghost.''',
+
+'outside-libdir-files',
+'''This library package must not contain non library files to allow 64
+and 32 bits versions of the package to coexist.''',
+
+'hidden-file-or-dir',
+'''The file or directory is hidden. You should see if this is normal,
+and delete it from the package if not.''',
+
+'module-without-depmod-postin',
+'''This package contains a kernel module but provides no call to depmod in the
+%post scriptlet.''',
+
+'postin-with-wrong-depmod',
+'''This package contains a kernel module but its %post scriptlet calls depmod
+for the wrong kernel.''',
+
+'module-without-depmod-postun',
+'''This package contains a kernel module but provides no call to depmod in the
+%postun scriptlet.''',
+
+'postun-with-wrong-depmod',
+'''This package contains a kernel module but its %postun scriptlet calls depmod
+for the wrong kernel.''',
+
+'log-files-without-logrotate',
+'''This package contains files in /var/log/ without adding logrotate
+configuration for them.''',
+
+'unexpanded-macro',
+'''This package contains a file whose path contains something that looks like
+an unexpanded macro; this is often the sign of a misspelling. Please check your
+specfile.''',
+
+'manifest-in-perl-module',
+'''This perl module package contains a MANIFEST or a MANIFEST.SKIP file
+in the documentation directory.''',
+
+'siteperl-in-perl-module',
+'''This perl module package installs files under the subdirectory site_perl,
+while they must appear under vendor_perl.''',
+
+'executable-marked-as-config-file',
+'''Executables must not be marked as config files because that may
+prevent upgrades from working correctly. If you need to be able to
+customize an executable, make it for example read a config file in
+/etc/sysconfig.''',
+
+'sourced-script-with-shebang',
+'''This text file contains a shebang, but is meant to be sourced, not
+executed.''',
+
+'executable-sourced-script',
+'''This text file has executable bit set, but is meant to be sourced, not
+executed.''',
+
+'wrong-script-interpreter',
+'''This script uses an incorrect interpreter.''',
+
+'non-executable-script',
+'''This text file contains a shebang or is located in a path dedicated for
+executables, but lacks the executable bits and cannot thus be executed. If
+the file is meant to be an executable script, add the executable bits,
+otherwise remove the shebang or move the file elsewhere.''',
+
+'script-without-shebang',
+'''This text file has executable bits set or is located in a path dedicated
+for executables, but lacks a shebang and cannot thus be executed. If the file
+is meant to be an executable script, add the shebang, otherwise remove the
+executable bits or move the file elsewhere.''',
+
+'wrong-script-end-of-line-encoding',
+'''This script has wrong end-of-line encoding, usually caused by creation or
+modification on a non-Unix system. It will prevent its execution.''',
+
+'wrong-file-end-of-line-encoding',
+'''This file has wrong end-of-line encoding, usually caused by creation or
+modification on a non-Unix system. It could prevent it from being displayed
+correctly in some circumstances.''',
+
+'file-not-utf8',
+'''The character encoding of this file is not UTF-8. Consider converting it
+in the specfile's %prep section for example using iconv(1).''',
+
+'filename-not-utf8',
+'''The character encoding of the name of this file is not UTF-8.
+Rename it.''',
+
+'file-in-meta-package',
+'''This package seems to be a meta-package (an empty package used to require
+other packages), but it is not empty. You should remove or rename it, see the
+option MetaPackageRegexp.''',
+
+'empty-debuginfo-package',
+'''This debuginfo package contains no files. This is often a sign of binaries
+being unexpectedly stripped too early during the build, rpmbuild not being able
+to strip the binaries, the package actually being a noarch one but erratically
+packaged as arch dependent, or something else. Verify what the case is, and
+if there's no way to produce useful debuginfo out of it, disable creation of
+the debuginfo package.''',
+
+'debuginfo-without-sources',
+'''This debuginfo package appears to contain debug symbols but no source files.
+This is often a sign of binaries being unexpectedly stripped too early during
+the build, or being compiled without compiler debug flags (which again often
+is a sign of distro's default compiler flags ignored which might have security
+consequences), or other compiler flags which result in rpmbuild's debuginfo
+extraction not working as expected. Verify that the binaries are not
+unexpectedly stripped and that the intended compiler flags are used.''',
+
+'read-error',
+'''This file could not be read. A reason for this could be that the info about
+it in the rpm header indicates that it is supposed to be a readable normal file
+but it actually is not in the filesystem. Because of this, some checks will
+be skipped.''',
+
+'executable-crontab-file',
+'''This crontab file has executable bit set, which is refused by newer version
+of cron''',
+
+'non-owner-writeable-only-crontab-file',
+'''This crontab file is writeable by other users as its owner, which is refused
+by newer version of cron and insecure''',
+
+'symlink-crontab-file',
+'''This crontab file is a symbolic link, which is insecure and refused by newer
+version of cron''',
+
+'rpath-in-buildconfig',
+'''This build configuration file contains rpaths which will be introduced into
+dependent packages.''',
+
+'python-bytecode-wrong-magic-value',
+'''The "magic" ABI version embedded in this python bytecode file isn't equal
+to that of the corresponding runtime, which will force the interpreter to
+recompile the .py source every time, ignoring the saved bytecode.''',
+
+'python-bytecode-inconsistent-mtime',
+'''The timestamp embedded in this python bytecode file isn't equal to the mtime
+of the original source file, which will force the interpreter to recompile the
+.py source every time, ignoring the saved bytecode.''',
+
+'python-bytecode-without-source',
+'''This python bytecode file (.pyo/.pyc) is not accompanied by its original
+source file (.py)''',
+
+'duplicate-executable',
+'''This executable file exists in more than one standard binary directories.
+It can cause problems when dirs in $PATH are reordered.''',
+
+'no-manual-page-for-binary',
+'''Each executable in standard binary directories should have a man page.''',
+
+'manual-page-warning',
+'''This man page may contain problems that can cause it not to be formatted
+as intended.''',
+
+'incorrect-fsf-address',
+'''The Free Software Foundation address in this file seems to be outdated or
+misspelled. Ask upstream to update the address, or if this is a license file,
+possibly the entire file with a new copy available from the FSF.''',
+)
+
+# FilesCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/Filter.py b/Filter.py
new file mode 100644
index 0000000..0900fe7
--- /dev/null
+++ b/Filter.py
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : Filter.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Sat Oct 23 15:52:27 1999
+# Version : $Id: Filter.py 1871 2011-06-18 09:40:52Z scop $
+# Purpose : filter the output of rpmlint to allow exceptions.
+#############################################################################
+
+import locale
+import sys
+import textwrap
+
+import Config
+try:
+ import Testing
+except ImportError:
+ Testing = None
+
+
+_rawout = None
+_diagnostic = list()
+_badness_score = 0
+printed_messages = { "I": 0, "W": 0, "E": 0 }
+
+if sys.stdout.isatty():
+ def __print(s):
+ print(s)
+else:
+ def __print(s):
+ print(s.encode(locale.getpreferredencoding(), "replace"))
+
+def printInfo(pkg, reason, *details):
+ _print("I", pkg, reason, details)
+
+def printWarning(pkg, reason, *details):
+ _print("W", pkg, reason, details)
+
+def printError(pkg, reason, *details):
+ _print("E", pkg, reason, details)
+
+def _print(msgtype, pkg, reason, details):
+ global _badness_score
+
+ threshold = badnessThreshold()
+
+ badness = 0
+ if threshold >= 0:
+ badness = Config.badness(reason)
+ # anything with badness is an error
+ if badness:
+ msgtype = 'E'
+ # errors without badness become warnings
+ elif msgtype == 'E':
+ msgtype = 'W'
+
+ ln = ""
+ if pkg.current_linenum is not None:
+ ln = "%s:" % pkg.current_linenum
+ arch = ""
+ if pkg.arch is not None:
+ arch = ".%s" % pkg.arch
+ s = "%s%s:%s %s: %s" % (pkg.name, arch, ln, msgtype, reason)
+ if badness:
+ s = s + " (Badness: %d)" % badness
+ for d in details:
+ s = s + " %s" % d
+ if Testing and Testing.isTest():
+ Testing.addOutput(s)
+ else:
+ if _rawout:
+ print >>_rawout, s.encode(locale.getpreferredencoding(), "replace")
+ if not Config.isFiltered(s):
+ printed_messages[msgtype] += 1
+ _badness_score += badness
+ if threshold >= 0:
+ _diagnostic.append(s + "\n")
+ else:
+ __print(s)
+ if Config.info:
+ printDescriptions(reason)
+ return True
+
+ return False
+
+def printDescriptions(reason):
+ try:
+ d = _details[reason]
+ if d and d != '' and d != "\n":
+ __print(textwrap.fill(d, 78))
+ __print("")
+ except KeyError:
+ pass
+
+def _diag_sortkey(x):
+ xs = x.split()
+ return (xs[2], xs[1])
+
+def printAllReasons():
+ threshold = badnessThreshold()
+ if threshold < 0:
+ return False
+
+ global _diagnostic
+ _diagnostic.sort(key = _diag_sortkey, reverse = True)
+ last_reason = ''
+ for diag in _diagnostic:
+ if Config.info:
+ reason = diag.split()[2]
+ if reason != last_reason:
+ if len(last_reason):
+ printDescriptions(last_reason)
+ last_reason = reason
+ __print(diag)
+ if Config.info and len(last_reason):
+ printDescriptions(last_reason)
+ _diagnostic = list()
+ return _badness_score > threshold
+
+
+_details = {}
+
+def addDetails(*details):
+ for idx in range(len(details)/2):
+ if not details[idx*2] in _details:
+ _details[details[idx*2]] = details[idx*2+1]
+
+def badnessScore():
+ return _badness_score
+
+def badnessThreshold():
+ return Config.getOption("BadnessThreshold", -1)
+
+def setRawOut(file):
+ global _rawout
+ if _rawout:
+ _rawout.close()
+ _rawout = open(file, "w")
+
+# Filter.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/I18NCheck.py b/I18NCheck.py
new file mode 100644
index 0000000..f94064b
--- /dev/null
+++ b/I18NCheck.py
@@ -0,0 +1,206 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : I18NCheck.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Mon Nov 22 20:02:56 1999
+# Version : $Id: I18NCheck.py 1798 2010-06-23 19:47:26Z scop $
+# Purpose : checks i18n bugs.
+#############################################################################
+
+import re
+
+import rpm
+
+from Filter import addDetails, printError, printWarning
+from __isocodes__ import COUNTRIES, LANGUAGES
+import AbstractCheck
+
+
+# Associative array of invalid value => correct value
+INCORRECT_LOCALES = {
+ 'in': 'id',
+ 'in_ID': 'id_ID',
+ 'iw': 'he',
+ 'iw_IL': 'he_IL',
+ 'gr': 'el',
+ 'gr_GR': 'el_GR',
+ 'cz': 'cs',
+ 'cz_CZ': 'cs_CZ',
+ 'lug': 'lg', # 'lug' is valid, but we standardize on 2 letter codes
+ 'en_UK': 'en_GB'}
+
+package_regex = re.compile('-(' + '|'.join(LANGUAGES) + ')$')
+locale_regex = re.compile('^(/usr/share/locale/([^/]+))/')
+correct_subdir_regex = re.compile('^(([a-z][a-z]([a-z])?(_[A-Z][A-Z])?)([.@].*$)?)$')
+lc_messages_regex = re.compile('/usr/share/locale/([^/]+)/LC_MESSAGES/.*(mo|po)$')
+man_regex = re.compile('/usr(?:/share)?/man/([^/]+)/man[0-9n][^/]*/[^/]+$')
+
+# list of exceptions
+#
+# note: ISO-8859-9E is non standard, ISO-8859-{6,8} are of limited use
+# as locales (since all modern handling of bidi is based on utf-8 anyway),
+# so they should be removed once UTF-8 is deployed)
+EXCEPTION_DIRS = ('C', 'POSIX', 'CP1251', 'CP1255', 'CP1256',
+'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4', 'ISO-8859-5',
+'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9', 'ISO-8859-9E',
+'ISO-8859-10', 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15',
+'KOI8-R', 'KOI8-U', 'UTF-8', 'default')
+
+def is_valid_lang(lang):
+ # TODO: @Foo and charset handling
+ lang = re.sub("[@.].*$", "", lang)
+
+ if lang in LANGUAGES:
+ return True
+
+ ix = lang.find("_")
+ if ix == -1:
+ return False
+
+ # TODO: don't accept all lang_COUNTRY combinations
+
+ country = lang[ix+1:]
+ if country not in COUNTRIES:
+ return False
+
+ lang = lang[0:ix]
+ if lang not in LANGUAGES:
+ return False
+
+ return True
+
+class I18NCheck(AbstractCheck.AbstractCheck):
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, 'I18NCheck')
+
+ def check(self, pkg):
+
+ if pkg.isSource():
+ return
+
+ files = pkg.files().keys()
+ files.sort()
+ locales = [] # list of locales for this packages
+ webapp = False
+
+ i18n_tags = pkg[rpm.RPMTAG_HEADERI18NTABLE] or ()
+
+ for i in i18n_tags:
+ try:
+ correct = INCORRECT_LOCALES[i]
+ printError(pkg, 'incorrect-i18n-tag-' + correct, i)
+ except KeyError:
+ pass
+
+ # as some webapps have their files under /var/www/html, and
+ # others in /usr/share or /usr/lib, the only reliable way
+ # sofar to detect them is to look for an apache configuration file
+ for f in files:
+ if f.startswith('/etc/apache2/') or \
+ f.startswith('/etc/httpd/conf.d/'):
+ webapp = True
+
+ for f in files:
+ res = locale_regex.search(f)
+ if res:
+ locale = res.group(2)
+ # checks the same locale only once
+ if locale not in locales:
+ locales.append(locale)
+ res2 = correct_subdir_regex.search(locale)
+ if not res2:
+ if locale not in EXCEPTION_DIRS:
+ printError(pkg, 'incorrect-locale-subdir', f)
+ else:
+ locale_name = res2.group(2)
+ try:
+ correct = INCORRECT_LOCALES[locale_name]
+ printError(pkg, 'incorrect-locale-' + correct, f)
+ except KeyError:
+ pass
+ res = lc_messages_regex.search(f)
+ subdir = None
+ if res:
+ subdir = res.group(1)
+ if not is_valid_lang(subdir):
+ printError(pkg, 'invalid-lc-messages-dir', f)
+ else:
+ res = man_regex.search(f)
+ if res:
+ subdir = res.group(1)
+ if is_valid_lang(subdir):
+ subdir = None
+ else:
+ printError(pkg, 'invalid-locale-man-dir', f)
+
+ if f.endswith('.mo') or subdir:
+ if pkg.files()[f].lang == '' and not webapp:
+ printWarning(pkg, 'file-not-in-%lang', f)
+
+ main_dir, main_lang = ("", "")
+ for f in files:
+ lang = pkg.files()[f].lang
+ if main_lang and lang == "" and is_prefix(main_dir + '/', f):
+ printError(pkg, 'subfile-not-in-%lang', f)
+ if main_lang != lang:
+ main_dir, main_lang = f, lang
+
+ name = pkg.name
+ res = package_regex.search(name)
+ if res:
+ locales = 'locales-' + res.group(1)
+ if locales != name:
+ if locales not in (x[0] for x in pkg.requires()):
+ printError(pkg, 'no-dependency-on', locales)
+
+def is_prefix(p, s):
+ return len(p) <= len(s) and p == s[:len(p)]
+
+# Create an object to enable the auto registration of the test
+check = I18NCheck()
+
+addDetails(
+# Need to add a function to list all the locales
+'incorrect-i18n-tag-',
+"""
+""",
+
+'incorrect-locale-subdir',
+"""
+""",
+
+'incorrect-locale-',
+"""
+""",
+
+'invalid-lc-messages-dir',
+"""
+""",
+
+'invalid-locale-man-dir',
+"""
+""",
+
+'file-not-in-lang',
+"""
+""",
+
+'no-dependency-on',
+"""
+""",
+
+'subfile-not-in-%lang',
+""" If /foo/bar is not tagged %lang(XX) whereas /foo is, the package won't be
+installable if XX is not in %_install_langs.""",
+
+)
+
+# I18NCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..bc607a3
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,16 @@
+You need the following utilities to run rpmlint:
+
+ o python 2.x (x >= 4)
+ o rpm 4.4.2.2 or newer and its python bindings
+ o libmagic and its python bindings (optional)
+ o readelf
+ o cpio
+ o desktop-file-validate
+ o gzip, bzip2 and xz
+ o enchant and its python bindings (optional)
+
+and the following programs to install it:
+
+ o python 2.x (x >= 4)
+ o rpm python bindings 4.4 or newer
+ o sed
diff --git a/InitScriptCheck.py b/InitScriptCheck.py
new file mode 100644
index 0000000..c77adbc
--- /dev/null
+++ b/InitScriptCheck.py
@@ -0,0 +1,286 @@
+# -*- coding: utf-8 -*-
+#---------------------------------------------------------------
+# Project : Mandriva Linux
+# Module : rpmlint
+# File : InitScriptCheck.py
+# Version : $Id: InitScriptCheck.py 1893 2011-11-23 20:24:32Z scop $
+# Author : Frederic Lepied
+# Created On : Fri Aug 25 09:26:37 2000
+# Purpose : check init scripts (files in /etc/rc.d/init.d)
+#---------------------------------------------------------------
+
+import os
+import re
+
+import rpm
+
+from Filter import addDetails, printError, printWarning
+import AbstractCheck
+import Config
+import Pkg
+
+
+chkconfig_content_regex = re.compile('^\s*#\s*chkconfig:\s*([-0-9]+)\s+[-0-9]+\s+[-0-9]+')
+subsys_regex = re.compile('/var/lock/subsys/([^/"\'\n\s;&|]+)', re.MULTILINE)
+chkconfig_regex = re.compile('^[^#]*(chkconfig|add-service|del-service)', re.MULTILINE)
+status_regex = re.compile('^[^#]*status', re.MULTILINE)
+reload_regex = re.compile('^[^#]*reload', re.MULTILINE)
+use_deflevels = Config.getOption('UseDefaultRunlevels', True)
+lsb_tags_regex = re.compile('^# ([\w-]+):\s*(.*?)\s*$')
+lsb_cont_regex = re.compile('^#(?:\t| )(.*?)\s*$')
+use_subsys = Config.getOption('UseVarLockSubsys', True)
+
+LSB_KEYWORDS = ('Provides', 'Required-Start', 'Required-Stop', 'Should-Start',
+ 'Should-Stop', 'Default-Start', 'Default-Stop',
+ 'Short-Description', 'Description')
+RECOMMENDED_LSB_KEYWORDS = ('Provides', 'Required-Start', 'Required-Stop',
+ 'Default-Stop', 'Short-Description')
+
+class InitScriptCheck(AbstractCheck.AbstractCheck):
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, 'InitScriptCheck')
+
+ def check(self, pkg):
+ # Check only binary package
+ if pkg.isSource():
+ return
+
+ initscript_list = []
+ for fname, pkgfile in pkg.files().items():
+
+ if not fname.startswith('/etc/init.d/') and \
+ not fname.startswith('/etc/rc.d/init.d/'):
+ continue
+
+ basename = os.path.basename(fname)
+ initscript_list.append(basename)
+ if pkgfile.mode & 0500 != 0500:
+ printError(pkg, 'init-script-non-executable', fname)
+
+ if "." in basename:
+ printError(pkg, 'init-script-name-with-dot', fname)
+
+ # check chkconfig call in %post and %preun
+ postin = pkg[rpm.RPMTAG_POSTIN] or \
+ pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
+ if not postin:
+ printError(pkg, 'init-script-without-chkconfig-postin', fname)
+ elif not chkconfig_regex.search(postin):
+ printError(pkg, 'postin-without-chkconfig', fname)
+
+ preun = pkg[rpm.RPMTAG_PREUN] or \
+ pkg.scriptprog(rpm.RPMTAG_PREUNPROG)
+ if not preun:
+ printError(pkg, 'init-script-without-chkconfig-preun', fname)
+ elif not chkconfig_regex.search(preun):
+ printError(pkg, 'preun-without-chkconfig', fname)
+
+ status_found = False
+ reload_found = False
+ chkconfig_content_found = False
+ subsys_regex_found = False
+ in_lsb_tag = False
+ in_lsb_description = False
+ lastline = ''
+ lsb_tags = {}
+ # check common error in file content
+ content = None
+ try:
+ content = Pkg.readlines(pkgfile.path)
+ except Exception, e:
+ printWarning(pkg, 'read-error', e)
+ continue
+ content_str = "".join(content)
+ for line in content:
+ line = line[:-1] # chomp
+ # TODO check if there is only one line like this
+ if line.startswith('### BEGIN INIT INFO'):
+ in_lsb_tag = True
+ continue
+ if line.endswith('### END INIT INFO'):
+ in_lsb_tag = False
+ for kw, vals in lsb_tags.items():
+ if len(vals) != 1:
+ printError(pkg, 'redundant-lsb-keyword', kw)
+
+ for kw in RECOMMENDED_LSB_KEYWORDS:
+ if kw not in lsb_tags:
+ printWarning(pkg, 'missing-lsb-keyword',
+ "%s in %s" % (kw, fname))
+ if in_lsb_tag:
+ # TODO maybe we do not have to handle this ?
+ if lastline.endswith('\\'):
+ line = lastline + line
+ else:
+ res = lsb_tags_regex.search(line)
+ if not res:
+ cres = lsb_cont_regex.search(line)
+ if not (in_lsb_description and cres):
+ in_lsb_description = False
+ printError(pkg,
+ 'malformed-line-in-lsb-comment-block',
+ line)
+ else:
+ lsb_tags["Description"][-1] += \
+ " " + cres.group(1)
+ else:
+ tag = res.group(1)
+ if not tag.startswith('X-') and \
+ tag not in LSB_KEYWORDS:
+ printError(pkg, 'unknown-lsb-keyword', line)
+ else:
+ in_lsb_description = (tag == 'Description')
+ if tag not in lsb_tags:
+ lsb_tags[tag] = []
+ lsb_tags[tag].append(res.group(2))
+ lastline = line
+
+ if not status_found and status_regex.search(line):
+ status_found = True
+
+ if not reload_found and reload_regex.search(line):
+ reload_found = True
+
+ res = chkconfig_content_regex.search(line)
+ if res:
+ chkconfig_content_found = True
+ if use_deflevels:
+ if res.group(1) == '-':
+ printWarning(pkg, 'no-default-runlevel', fname)
+ elif res.group(1) != '-':
+ printWarning(pkg, 'service-default-enabled', fname)
+
+ res = subsys_regex.search(line)
+ if res:
+ subsys_regex_found = True
+ name = res.group(1)
+ if use_subsys and name != basename:
+ error = True
+ if name[0] == '$':
+ value = Pkg.substitute_shell_vars(name, content_str)
+ if value == basename:
+ error = False
+ else:
+ i = name.find('}')
+ if i != -1:
+ name = name[0:i]
+ error = name != basename
+ if error and len(name):
+ if name[0] == '$':
+ printWarning(pkg, 'incoherent-subsys', fname,
+ name)
+ else:
+ printError(pkg, 'incoherent-subsys', fname,
+ name)
+
+ if "Default-Start" in lsb_tags:
+ if "".join(lsb_tags["Default-Start"]):
+ printWarning(pkg, 'service-default-enabled', fname)
+
+ if not status_found:
+ printError(pkg, 'no-status-entry', fname)
+ if not reload_found:
+ printWarning(pkg, 'no-reload-entry', fname)
+ if not chkconfig_content_found:
+ printError(pkg, 'no-chkconfig-line', fname)
+ if not subsys_regex_found and use_subsys:
+ printError(pkg, 'subsys-not-used', fname)
+ elif subsys_regex_found and not use_subsys:
+ printError(pkg, 'subsys-unsupported', fname)
+
+ if len(initscript_list) == 1:
+ pkgname = re.sub("-sysvinit$", "", pkg.name.lower())
+ goodnames = (pkgname, pkgname + 'd')
+ if initscript_list[0] not in goodnames:
+ printWarning(pkg, 'incoherent-init-script-name',
+ initscript_list[0], str(goodnames))
+
+
+# Create an object to enable the auto registration of the test
+check = InitScriptCheck()
+
+addDetails(
+'init-script-without-chkconfig-postin',
+'''The package contains an init script but doesn't contain a %post with
+a call to chkconfig.''',
+
+'postin-without-chkconfig',
+'''The package contains an init script but doesn't call chkconfig in its
+%post script.''',
+
+'init-script-without-chkconfig-preun',
+'''The package contains an init script but doesn't contain a %preun with
+a call to chkconfig.''',
+
+'preun-without-chkconfig',
+'''The package contains an init script but doesn't call chkconfig in its
+%preun script.''',
+
+'missing-lsb-keyword',
+'''The package contains an init script that does not contain one of the LSB
+init script comment block convention keywords that are recommendable for all
+init scripts. If there is nothing to add to a keyword's value, include the
+keyword in the script with an empty value. Note that as of version 3.2, the
+LSB specification does not mandate presence of any keywords.''',
+
+'no-status-entry',
+'''In your init script (/etc/rc.d/init.d/your_file), you don't
+have a 'status' entry, which is necessary for good functionality.''',
+
+'no-reload-entry',
+'''In your init script (/etc/rc.d/init.d/your_file), you don't
+have a 'reload' entry, which is necessary for good functionality.''',
+
+'no-chkconfig-line',
+'''The init script doesn't contain a chkconfig line to specify the runlevels
+at which to start and stop it.''',
+
+'no-default-runlevel',
+'''The default runlevel isn't specified in the init script.''',
+
+'service-default-enabled',
+'''The service is enabled by default after "chkconfig --add"; for security
+reasons, most services should not be. Use "-" as the default runlevel in the
+init script's "chkconfig:" line and/or remove the "Default-Start:" LSB keyword
+to fix this if appropriate for this service.''',
+
+'subsys-unsupported',
+'''The init script uses /var/lock/subsys which is not supported by
+this distribution.''',
+
+'subsys-not-used',
+'''While your daemon is running, you have to put a lock file in
+/var/lock/subsys/. To see an example, look at this directory on your
+machine and examine the corresponding init scripts.''',
+
+'incoherent-subsys',
+'''The filename of your lock file in /var/lock/subsys/ is incoherent
+with your actual init script name. For example, if your script name
+is httpd, you have to use 'httpd' as the filename in your subsys directory.
+It is also possible that rpmlint gets this wrong, especially if the init
+script contains nontrivial shell variables and/or assignments. These
+cases usually manifest themselves when rpmlint reports that the subsys name
+starts a with '$'; in these cases a warning instead of an error is reported
+and you should check the script manually.''',
+
+'incoherent-init-script-name',
+'''The init script name should be the same as the package name in lower case,
+or one with 'd' appended if it invokes a process by that name.''',
+
+'init-script-name-with-dot',
+'''The init script name should not contain a dot in its name. Some versions
+of chkconfig don't work as expected with init script names like that.''',
+
+'init-script-non-executable',
+'''The init script should have at least the execution bit set for root
+in order for it to run at boot time.''',
+)
+
+# InitScriptCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/LSBCheck.py b/LSBCheck.py
new file mode 100644
index 0000000..d88a8e0
--- /dev/null
+++ b/LSBCheck.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+#---------------------------------------------------------------
+# Project : Mandriva Linux
+# Module : rpmlint
+# File : LSBCheck.py
+# Version : $Id: LSBCheck.py 1532 2009-01-30 22:01:50Z scop $
+# Author : Frederic Lepied
+# Created On : Tue Jan 30 14:44:37 2001
+# Purpose : LSB non compliance checks
+#---------------------------------------------------------------
+
+import re
+
+import rpm
+
+from Filter import addDetails, printError
+import AbstractCheck
+
+
+version_regex = re.compile('^[a-zA-Z0-9.+]+$')
+name_regex = re.compile('^[a-z0-9.+-]+$')
+
+class LSBCheck(AbstractCheck.AbstractCheck):
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, "LSBCheck")
+
+ def check(self, pkg):
+
+ name = pkg.name
+ if name and not name_regex.search(name):
+ printError(pkg, 'non-lsb-compliant-package-name', name)
+
+ version = pkg[rpm.RPMTAG_VERSION]
+ if version and not version_regex.search(version):
+ printError(pkg, 'non-lsb-compliant-version', version)
+
+ release = pkg[rpm.RPMTAG_RELEASE]
+ if release and not version_regex.search(release):
+ printError(pkg, 'non-lsb-compliant-release', release)
+
+# Create an object to enable the auto registration of the test
+check = LSBCheck()
+
+addDetails(
+'non-lsb-compliant-package-name',
+"""Your package name contains an illegal character. Use only
+alphanumeric symbols in your package name.""",
+
+'non-lsb-compliant-version',
+"""Your version number contains an illegal character. Use only
+lowercase letters and/or numbers.""",
+
+'non-lsb-compliant-release',
+"""Your version number contains an illegal character. Use only
+lowercase letters and/or numbers.""",
+
+)
+
+# LSBCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..c55482e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,110 @@
+#############################################################################
+# File : Makefile
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Mon Sep 30 13:20:18 1999
+# Version : $Id: Makefile 1895 2011-12-04 16:23:10Z scop $
+# Purpose : rules to manage the files.
+#############################################################################
+
+BINDIR=/usr/bin
+LIBDIR=/usr/share/rpmlint
+ETCDIR=/etc
+MANDIR=/usr/share/man
+
+FILES = rpmlint *.py INSTALL README README.devel COPYING tools/*.py \
+ Makefile config rpmdiff rpmlint.bash-completion rpmlint.1 \
+ test.sh test/*.rpm test/*.spec test/*.py
+GENERATED = AUTHORS ChangeLog __version__.py
+
+PACKAGE = rpmlint
+PYTHON = python
+
+# update this variable to create a new release
+VERSION := 1.4
+TAG := $(shell echo "V$(VERSION)" | tr -- '-.' '__')
+SVNBASE = $(shell svn info . | grep URL | sed -e 's/[^:]*:\s*//' -e 's,/\(trunk\|tags/.\+\)$$,,')
+
+# for the [A-Z]* part
+LC_ALL:=C
+export LC_ALL
+
+all: __version__.py __isocodes__.py
+ if [ "x${COMPILE_PYC}" = "x1" ] ; then \
+ $(PYTHON) -m py_compile [A-Z]*.py __*__.py ; \
+ fi
+ $(PYTHON) -O -m py_compile [A-Z]*.py __*__.py
+
+clean:
+ rm -f *~ *.pyc *.pyo $(GENERATED)
+
+install: all
+ mkdir -p $(DESTDIR)$(LIBDIR) $(DESTDIR)$(BINDIR) $(DESTDIR)$(ETCDIR)/$(PACKAGE) $(DESTDIR)$(MANDIR)/man1
+ -cp -p *.pyc $(DESTDIR)$(LIBDIR)
+ cp -p *.py *.pyo $(DESTDIR)$(LIBDIR)
+ cp -p rpmlint rpmdiff $(DESTDIR)$(BINDIR)
+ cp -p config $(DESTDIR)$(ETCDIR)/$(PACKAGE)
+ compdir=`pkg-config --variable=completionsdir bash-completion 2>/dev/null` ; \
+ if [ "x$$compdir" = "x" ] ; then \
+ mkdir -p $(DESTDIR)$(ETCDIR)/bash_completion.d ; \
+ cp -p rpmlint.bash-completion $(DESTDIR)$(ETCDIR)/bash_completion.d/rpmlint ; \
+ else \
+ mkdir -p $(DESTDIR)$$compdir ; \
+ cp -p rpmlint.bash-completion $(DESTDIR)$$compdir/rpmlint ; \
+ ln -s rpmlint $(DESTDIR)$$compdir/rpmdiff ; \
+ fi
+ cp -p rpmlint.1 $(DESTDIR)$(MANDIR)/man1/rpmlint.1
+
+verify:
+ pychecker --limit=100 [A-Z]*.py __*__.py
+
+.PHONY: check
+
+check:
+ ./test.sh
+
+version:
+ @echo "$(VERSION)"
+
+
+dist: cleandist localcopy tar
+
+cleandist:
+ rm -rf $(PACKAGE)-$(VERSION) $(PACKAGE)-$(VERSION).tar.xz
+
+localcopy: $(FILES) $(GENERATED)
+ mkdir $(PACKAGE)-$(VERSION)
+ cp -p --parents $(FILES) $(GENERATED) $(PACKAGE)-$(VERSION)
+
+tar: localcopy
+ tar cv --owner=root --group=root -f $(PACKAGE)-$(VERSION).tar $(PACKAGE)-$(VERSION)
+ xz -9evf $(PACKAGE)-$(VERSION).tar
+ rm -rf $(PACKAGE)-$(VERSION)
+
+export:
+ svn export $(SVNBASE)/tags/$(TAG) $(PACKAGE)-$(VERSION)
+
+tag:
+ @if svn list $(SVNBASE)/tags/$(TAG) &>/dev/null ; then \
+ echo "ERROR: tag \"$(TAG)\" probably already exists" ; \
+ exit 1 ; \
+ else \
+ echo 'svn copy -m "Tag $(TAG)." . $(SVNBASE)/tags/$(TAG)' ; \
+ svn copy -m "Tag $(TAG)." . $(SVNBASE)/tags/$(TAG) ; \
+ fi
+
+AUTHORS: authors.xml authors.xsl
+ xsltproc authors.xsl authors.xml | sort -u > $@
+
+ChangeLog: $(FILES) authors.xml
+ svn2cl --authors=authors.xml --group-by-day --reparagraph \
+ --strip-prefix=trunk
+
+__version__.py: Makefile
+ echo "# Automatically generated, do not edit" > $@
+ echo "__version__ = '$(VERSION)'" >> $@
+
+__isocodes__.py:
+ tools/generate-isocodes.py > $@
+
+# Makefile ends here
diff --git a/MenuCheck.py b/MenuCheck.py
new file mode 100644
index 0000000..0dd865a
--- /dev/null
+++ b/MenuCheck.py
@@ -0,0 +1,460 @@
+# -*- coding: utf-8 -*-
+#---------------------------------------------------------------
+# Project : Mandriva Linux
+# Module : rpmlint
+# File : MenuCheck.py
+# Version : $Id: MenuCheck.py 1885 2011-09-13 18:15:29Z scop $
+# Author : Frederic Lepied
+# Created On : Mon Mar 20 07:43:37 2000
+#---------------------------------------------------------------
+
+import re
+import stat
+
+import rpm
+
+from Filter import addDetails, printError, printInfo, printWarning
+import AbstractCheck
+import Config
+import Pkg
+
+
+DEFAULT_VALID_SECTIONS = (
+ 'Office/Accessories',
+ 'Office/Address Books',
+ 'Office/Communications/Fax',
+ 'Office/Communications/PDA',
+ 'Office/Communications/Phone',
+ 'Office/Communications/Other',
+ 'Office/Drawing',
+ 'Office/Graphs',
+ 'Office/Presentations',
+ 'Office/Publishing',
+ 'Office/Spreadsheets',
+ 'Office/Tasks Management',
+ 'Office/Time Management',
+ 'Office/Wordprocessors',
+ 'Office/Other',
+ 'Internet/Chat',
+ 'Internet/File Transfer',
+ 'Internet/Instant Messaging',
+ 'Internet/Mail',
+ 'Internet/News',
+ 'Internet/Remote Access',
+ 'Internet/Video Conference',
+ 'Internet/Web Browsers',
+ 'Internet/Web Editors',
+ 'Internet/Other',
+ 'Multimedia/Graphics',
+ 'Multimedia/Sound',
+ 'Multimedia/Video',
+ 'Multimedia/Other',
+ 'System/Archiving/Backup',
+ 'System/Archiving/CD Burning',
+ 'System/Archiving/Compression',
+ 'System/Archiving/Other',
+ 'System/Configuration/Boot and Init',
+ 'System/Configuration/GNOME',
+ 'System/Configuration/Hardware',
+ 'System/Configuration/KDE',
+ 'System/Configuration/Networking',
+ 'System/Configuration/Packaging',
+ 'System/Configuration/Printing',
+ 'System/Configuration/Users',
+ 'System/Configuration/Other',
+ 'System/File Tools',
+ 'System/Monitoring',
+ 'System/Session/Windowmanagers',
+ 'System/Terminals',
+ 'System/Text Tools',
+ 'System/Other',
+ 'More Applications/Accessibility',
+ 'More Applications/Communications',
+ 'More Applications/Databases',
+ 'More Applications/Development/Code Generators',
+ 'More Applications/Development/Development Environments',
+ 'More Applications/Development/Interpreters',
+ 'More Applications/Development/Tools',
+ 'More Applications/Development/Other',
+ 'More Applications/Documentation',
+ 'More Applications/Editors',
+ 'More Applications/Education/Economy',
+ 'More Applications/Education/Geography',
+ 'More Applications/Education/History',
+ 'More Applications/Education/Languages',
+ 'More Applications/Education/Literature',
+ 'More Applications/Education/Sciences',
+ 'More Applications/Education/Sports',
+ 'More Applications/Education/Other',
+ 'More Applications/Emulators',
+ 'More Applications/Finances',
+ 'More Applications/Games/Adventure',
+ 'More Applications/Games/Arcade',
+ 'More Applications/Games/Boards',
+ 'More Applications/Games/Cards',
+ 'More Applications/Games/Puzzles',
+ 'More Applications/Games/Sports',
+ 'More Applications/Games/Strategy',
+ 'More Applications/Games/Toys',
+ 'More Applications/Games/Other',
+ 'More Applications/Sciences/Artificial Intelligence',
+ 'More Applications/Sciences/Astronomy',
+ 'More Applications/Sciences/Biology',
+ 'More Applications/Sciences/Chemistry',
+ 'More Applications/Sciences/Computer Science',
+ 'More Applications/Sciences/Data visualization',
+ 'More Applications/Sciences/Electricity',
+ 'More Applications/Sciences/Geosciences',
+ 'More Applications/Sciences/Image Processing',
+ 'More Applications/Sciences/Mathematics',
+ 'More Applications/Sciences/Numerical Analysis',
+ 'More Applications/Sciences/Parallel Computing',
+ 'More Applications/Sciences/Physics',
+ 'More Applications/Sciences/Robotics',
+ 'More Applications/Sciences/Other',
+ 'More Applications/Other',
+ )
+
+DEFAULT_EXTRA_MENU_NEEDS = (
+ 'gnome',
+ 'icewm',
+ 'kde',
+ 'wmaker',
+ )
+
+DEFAULT_ICON_PATH = (('/usr/share/icons/', 'normal'),
+ ('/usr/share/icons/mini/', 'mini'),
+ ('/usr/share/icons/large/', 'large'))
+
+DEFAULT_LAUNCHERS = (['(?:/usr/bin/)?kdesu', ('/usr/bin/kdesu', 'kdesu')],
+ ['(?:/usr/bin/)?launch_x11_clanapp', ('/usr/bin/launch_x11_clanapp', 'clanlib', 'libclanlib0')],
+ ['(?:/usr/bin/)?soundwrapper', None],
+ )
+
+menu_file_regex = re.compile('^/usr/lib/menu/([^/]+)$')
+old_menu_file_regex = re.compile('^/usr/share/(gnome/apps|applnk)/([^/]+)$')
+package_regex = re.compile('\?package\((.*)\):')
+needs_regex = re.compile('needs=(\"([^\"]+)\"|([^ \t\"]+))')
+section_regex = re.compile('section=(\"([^\"]+)\"|([^ \t\"]+))')
+title_regex = re.compile('[\"\s]title=(\"([^\"]+)\"|([^ \t\"]+))')
+longtitle_regex = re.compile('longtitle=(\"([^\"]+)\"|([^ \t\"]+))')
+command_regex = re.compile('command=(?:\"([^\"]+)\"|([^ \t\"]+))')
+icon_regex = re.compile('icon=\"?([^\" ]+)')
+valid_sections = Config.getOption('ValidMenuSections', DEFAULT_VALID_SECTIONS)
+update_menus_regex = re.compile('^[^#]*update-menus', re.MULTILINE)
+standard_needs = Config.getOption('ExtraMenuNeeds', DEFAULT_EXTRA_MENU_NEEDS)
+icon_paths = Config.getOption('IconPath', DEFAULT_ICON_PATH)
+xpm_ext_regex = re.compile('/usr/share/icons/(mini/|large/).*\.xpm$')
+icon_ext_regex = re.compile(Config.getOption('IconFilename', '.*\.png$'))
+version_regex = re.compile('([0-9.][0-9.]+)($|\s)')
+launchers = Config.getOption('MenuLaunchers', DEFAULT_LAUNCHERS)
+xdg_migrated_regex = re.compile('xdg=\"?([^\" ]+)')
+
+# compile regexps
+for l in launchers:
+ l[0] = re.compile(l[0])
+del l
+
+class MenuCheck(AbstractCheck.AbstractCheck):
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, 'MenuCheck')
+
+ def check(self, pkg):
+ # Check only binary package
+ if pkg.isSource():
+ return
+
+ files = pkg.files()
+ menus = []
+
+ for fname, pkgfile in files.items():
+ # Check menu files
+ res = menu_file_regex.search(fname)
+ mode = pkgfile.mode
+ if res:
+ basename = res.group(1)
+ if not stat.S_ISREG(mode):
+ printError(pkg, 'non-file-in-menu-dir', fname)
+ else:
+ if basename != pkg.name:
+ printWarning(pkg, 'non-coherent-menu-filename', fname)
+ if mode & 0444 != 0444:
+ printError(pkg, 'non-readable-menu-file', fname)
+ if mode & 0111 != 0:
+ printError(pkg, 'executable-menu-file', fname)
+ menus.append(fname)
+ else:
+ # Check old menus from KDE and GNOME
+ res = old_menu_file_regex.search(fname)
+ if res:
+ if stat.S_ISREG(mode):
+ printError(pkg, 'old-menu-entry', fname)
+ else:
+ # Check non transparent xpm files
+ res = xpm_ext_regex.search(fname)
+ if res:
+ if stat.S_ISREG(mode) and not pkg.grep('None",', fname):
+ printWarning(pkg, 'non-transparent-xpm', fname)
+ if fname.startswith('/usr/lib64/menu'):
+ printError(pkg, 'menu-in-wrong-dir', fname)
+
+ if menus:
+ postin = pkg[rpm.RPMTAG_POSTIN] or \
+ pkg.scriptprog(rpm.RPMTAG_POSTINPROG)
+ if not postin:
+ printError(pkg, 'menu-without-postin')
+ elif not update_menus_regex.search(postin):
+ printError(pkg, 'postin-without-update-menus')
+
+ postun = pkg[rpm.RPMTAG_POSTUN] or \
+ pkg.scriptprog(rpm.RPMTAG_POSTUNPROG)
+ if not postun:
+ printError(pkg, 'menu-without-postun')
+ elif not update_menus_regex.search(postun):
+ printError(pkg, 'postun-without-update-menus')
+
+ directory = pkg.dirName()
+ for f in menus:
+ # remove comments and handle cpp continuation lines
+ cmd = Pkg.getstatusoutput(('/lib/cpp', directory + f), True)[1]
+
+ for line in cmd.splitlines():
+ if not line.startswith('?'):
+ continue
+ res = package_regex.search(line)
+ if res:
+ package = res.group(1)
+ if package != pkg.name:
+ printWarning(pkg,
+ 'incoherent-package-value-in-menu',
+ package, f)
+ else:
+ printInfo(pkg, 'unable-to-parse-menu-entry', line)
+
+ command = True
+ res = command_regex.search(line)
+ if res:
+ command_line = (res.group(1) or res.group(2)).split()
+ command = command_line[0]
+ for launcher in launchers:
+ if not launcher[0].search(command):
+ continue
+ found = False
+ if launcher[1]:
+ if ('/bin/' + command_line[0] in files or
+ '/usr/bin/' + command_line[0] in files
+ or '/usr/X11R6/bin/' + command_line[0]
+ in files):
+ found = True
+ else:
+ for l in launcher[1]:
+ if l in pkg.req_names():
+ found = True
+ break
+ if not found:
+ printError(pkg,
+ 'use-of-launcher-in-menu-but-no-requires-on',
+ launcher[1][0])
+ command = command_line[1]
+ break
+
+ if command[0] == '/':
+ if command not in files:
+ printWarning(pkg, 'menu-command-not-in-package',
+ command)
+ elif not ('/bin/' + command in files or
+ '/usr/bin/' + command in files or
+ '/usr/X11R6/bin/' + command in files):
+ printWarning(pkg, 'menu-command-not-in-package',
+ command)
+ else:
+ printWarning(pkg, 'missing-menu-command')
+ command = False
+
+ res = longtitle_regex.search(line)
+ if res:
+ grp = res.groups()
+ title = grp[1] or grp[2]
+ if title[0] != title[0].upper():
+ printWarning(pkg, 'menu-longtitle-not-capitalized',
+ title)
+ res = version_regex.search(title)
+ if res:
+ printWarning(pkg, 'version-in-menu-longtitle',
+ title)
+ else:
+ printError(pkg, 'no-longtitle-in-menu', f)
+ title = None
+
+ res = title_regex.search(line)
+ if res:
+ grp = res.groups()
+ title = grp[1] or grp[2]
+ if title[0] != title[0].upper():
+ printWarning(pkg, 'menu-title-not-capitalized',
+ title)
+ res = version_regex.search(title)
+ if res:
+ printWarning(pkg, 'version-in-menu-title', title)
+ if '/' in title:
+ printError(pkg, 'invalid-title', title)
+ else:
+ printError(pkg, 'no-title-in-menu', f)
+ title = None
+
+ res = needs_regex.search(line)
+ if res:
+ grp = res.groups()
+ needs = (grp[1] or grp[2]).lower()
+ if needs in ('x11', 'text' ,'wm'):
+ res = section_regex.search(line)
+ if res:
+ grp = res.groups()
+ section = grp[1] or grp[2]
+ # don't warn entries for sections
+ if command and section not in valid_sections:
+ printError(pkg, 'invalid-menu-section',
+ section, f)
+ else:
+ printInfo(pkg, 'unable-to-parse-menu-section',
+ line)
+ elif needs not in standard_needs:
+ printInfo(pkg, 'strange-needs', needs, f)
+ else:
+ printInfo(pkg, 'unable-to-parse-menu-needs', line)
+
+ res = icon_regex.search(line)
+ if res:
+ icon = res.group(1)
+ if not icon_ext_regex.search(icon):
+ printWarning(pkg, 'invalid-menu-icon-type', icon)
+ if icon[0] == '/' and needs == 'x11':
+ printWarning(pkg, 'hardcoded-path-in-menu-icon',
+ icon)
+ else:
+ for path in icon_paths:
+ if (path[0] + icon) not in files:
+ printError(pkg,
+ path[1] + '-icon-not-in-package',
+ icon, f)
+ else:
+ printWarning(pkg, 'no-icon-in-menu', title)
+
+ res = xdg_migrated_regex.search(line)
+ if res:
+ if not res.group(1).lower() == "true":
+ printError(pkg, 'non-xdg-migrated-menu')
+ else:
+ printError(pkg, 'non-xdg-migrated-menu')
+
+
+# Create an object to enable the auto registration of the test
+check = MenuCheck()
+
+addDetails(
+'non-file-in-menu-dir',
+'''/usr/lib/menu must not contain anything else than normal files.''',
+
+'non-coherent-menu-filename',
+'''The menu file name should be /usr/lib/menu/<package>.''',
+
+'non-readable-menu-file',
+'''The menu file isn't readable. Check the permissions.''',
+
+'old-menu-entry',
+'''
+''',
+
+'non-transparent-xpm',
+'''xpm icon should be transparent for use in menus.''',
+
+'menu-without-postin',
+'''A menu file exists in the package but no %post scriptlet is present to call
+update-menus.''',
+
+'postin-without-update-menus',
+'''A menu file exists in the package but its %post scriptlet doesn't call
+update-menus.''',
+
+'menu-without-postun',
+'''A menu file exists in the package but no %postun scriptlet is present to call
+update-menus.''',
+
+'postun-without-update-menus',
+'''A menu file exists in the package but its %postun scriptlet doesn't call
+update-menus.''',
+
+'incoherent-package-value-in-menu',
+'''The package field of the menu entry isn't the same as the package name.''',
+
+'use-of-launcher-in-menu-but-no-requires-on',
+'''The menu command uses a launcher but there is no dependency in the package
+that contains it.''',
+
+'menu-command-not-in-package',
+'''The command used in the menu isn't included in the package.''',
+
+'menu-longtitle-not-capitalized',
+'''The longtitle field of the menu doesn't start with a capital letter.''',
+
+'version-in-menu-longtitle',
+'''The longtitle filed of the menu entry contains a version. This is bad
+because it will be prone to error when the version of the package changes.''',
+
+'no-longtitle-in-menu',
+'''The longtitle field isn't present in the menu entry.''',
+
+'menu-title-not-capitalized',
+'''The title field of the menu entry doesn't start with a capital letter.''',
+
+'version-in-menu-title',
+'''The title filed of the menu entry contains a version. This is bad
+because it will be prone to error when the version of the package changes.''',
+
+'no-title-in-menu',
+'''The title field isn't present in the menu entry.''',
+
+'invalid-menu-section',
+'''The section field of the menu entry isn't standard.''',
+
+'unable-to-parse-menu-section',
+'''rpmlint wasn't able to parse the menu section. Please report.''',
+
+'hardcoded-path-in-menu-icon',
+'''The path of the icon is hardcoded in the menu entry. This prevent multiple
+sizes of the icon from being found.''',
+
+'normal-icon-not-in-package',
+'''The normal icon isn't present in the package.''',
+
+'mini-icon-not-in-package',
+'''The mini icon isn't present in the package.''',
+
+'large-icon-not-in-package',
+'''The large icon isn't present in the package.''',
+
+'no-icon-in-menu',
+'''The menu entry doesn't contain an icon field.''',
+
+'invalid-title',
+'''The menu title contains invalid characters like /.''',
+
+'missing-menu-command',
+'''The menu file doesn't contain a command.''',
+
+'menu-in-wrong-directory',
+'''The menu files must be under /usr/lib/menu.''',
+
+'non-xdg-migrated-menu',
+'''The menu file has not been migrated to new XDG menu system.''',
+
+)
+
+# MenuCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/MenuXDGCheck.py b/MenuXDGCheck.py
new file mode 100644
index 0000000..7fb9e5b
--- /dev/null
+++ b/MenuXDGCheck.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+# Version : $Id$
+
+#
+# check xdg file format violation
+#
+# http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html
+#
+
+from Filter import addDetails, printError
+from Pkg import getstatusoutput, is_utf8
+import AbstractCheck
+
+
+class MenuXDGCheck(AbstractCheck.AbstractFilesCheck):
+ def __init__(self):
+ # desktop file need to be in $XDG_DATA_DIRS
+ # $ echo $XDG_DATA_DIRS/applications
+ # /var/lib/menu-xdg:/usr/share
+ AbstractCheck.AbstractFilesCheck.__init__(
+ self, "MenuXDGCheck", "/usr/share/applications/.*\.desktop$")
+
+ def check_file(self, pkg, filename):
+ f = pkg.dirName() + filename
+ st = getstatusoutput(('desktop-file-validate', f), True)
+ if st[0]:
+ error_printed = False
+ for line in st[1].splitlines():
+ if 'error: ' in line:
+ printError(pkg, 'invalid-desktopfile', filename,
+ line.split('error: ')[1])
+ error_printed = True
+ if not error_printed:
+ printError(pkg, 'invalid-desktopfile', filename)
+ if not is_utf8(f):
+ printError(pkg, 'non-utf8-desktopfile', filename)
+
+
+check = MenuXDGCheck()
+
+addDetails(
+'invalid-desktopfile',
+'''.desktop file is not valid, check with desktop-file-validate''',
+
+'non-utf8-desktopfile',
+'''.desktop file is not encoded in UTF-8''',
+)
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/NamingPolicyCheck.py b/NamingPolicyCheck.py
new file mode 100644
index 0000000..8f22483
--- /dev/null
+++ b/NamingPolicyCheck.py
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+#---------------------------------------------------------------
+# Project : Mandriva Linux
+# Module : rpmlint
+# File : NamingPolicyCheck.py
+# Version : $Id: NamingPolicyCheck.py 1732 2010-02-21 11:28:42Z scop $
+# Author : Michael Scherer
+# Created On : Mon May 19 11:25:37 2003
+# Purpose : Check package names according to their content.
+#---------------------------------------------------------------
+
+import re
+
+from Filter import addDetails, printWarning
+import AbstractCheck
+
+
+# could be added.
+#
+# zope
+# abiword2
+# alsaplayer-plugin-input
+# emacs
+# gstreamer
+# nautilus
+# vlc-plugin
+# XFree
+# xine
+
+simple_naming_policy_re = re.compile('\^[a-zA-Z1-9-_]*$')
+
+class NamingPolicyNotAppliedException(Exception):
+ pass
+
+class NamingPolicyCheck(AbstractCheck.AbstractCheck):
+ checks_ = []
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, "NamingPolicyCheck")
+
+ def add_check(self, pkg_name, name_re, file_re):
+ c = {}
+ c['pkg_name'] = pkg_name
+ c['name_re'] = re.compile(name_re)
+ c['file_re'] = re.compile(file_re)
+ self.checks_.append(c)
+
+ if simple_naming_policy_re.search(name_re):
+ details = "Its name should begin with " + name_re[1:]
+ else:
+ details = "Its name should match the regular expression " + name_re
+
+ addDetails(pkg_name + '-naming-policy-not-applied',
+ "This package doesn't respect the naming policy for %s "
+ "packages.\n%s." % (pkg_name, details))
+
+ def check(self, pkg):
+ if pkg.isSource():
+ return
+ files = pkg.files()
+ if not files:
+ return
+ try:
+ # check for files then
+ for c in self.checks_:
+ for f in files:
+ if c['file_re'].search(f) and \
+ not c['name_re'].search(pkg.name):
+ raise NamingPolicyNotAppliedException
+ except NamingPolicyNotAppliedException:
+ printWarning(pkg, c['pkg_name'] + '-naming-policy-not-applied', f)
+
+check = NamingPolicyCheck()
+
+#
+# these are the check currently implemented.
+#
+# first argument is the name of the check, printed by the warning.
+# ex : xmms.
+#
+# secund argument is the regular expression of the naming policy.
+# ex: xmms plugin should be named xmms-name_of_plugin.
+#
+# third is the path of the file that should contains a package to be related to
+# the naming scheme.
+# ex: xmms plugin are put under /usr/lib/xmms/
+#
+# the module is far from being perfect since you need to check this file for
+# the naming file.
+# if somone as a elegant solution, I will be happy to implement and test it.
+
+
+check.add_check('xmms', '^xmms(-|$)', '^/usr/lib(64)?/xmms/')
+check.add_check('python', '^python(-|$)', '^/usr/lib(64)?/python[1-9](-[1-9])?')
+check.add_check('perl5', '^perl(-|$)', '^/usr/lib(64)?/perl5/vendor_perl')
+check.add_check('apache2', '^apache2-mod_', '^/usr/lib(64)?/apache2-')
+check.add_check('fortune', '^fortune(-|$)', '^/usr/share/games/fortunes/')
+check.add_check('php', '^php(-|$)', '/usr/lib(64)?/php/extensions/')
+check.add_check('ruby', '^ruby(-|$)', '/usr/lib(64)?/ruby/[1-9](-[1-9])?/')
+check.add_check('ocaml', '^ocaml(-|$)', '/usr/lib(64)?/ocaml/')
+
+# these exception should be added
+# apache2 => apache2-devel
+# apache2-modules
+# ruby => apache2-mod_ruby
+# ruby
+
+# NamingPolicyCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/PamCheck.py b/PamCheck.py
new file mode 100644
index 0000000..43cd35f
--- /dev/null
+++ b/PamCheck.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+#---------------------------------------------------------------
+# Project : Mandriva Linux
+# Module : rpmlint
+# File : PamCheck.py
+# Version : $Id: /local/personal/PamCheck.py 4977 2006-01-31T09:29:13.664059Z misc $
+# Author : Michael Scherer
+# Created On : 031/01/2006
+# Purpose : Apply pam policy
+#---------------------------------------------------------------
+
+import re
+
+from Filter import addDetails, printError
+import AbstractCheck
+
+
+pam_stack_re = re.compile('^\s*[^#].*pam_stack\.so\s*service')
+
+class PamCheck(AbstractCheck.AbstractFilesCheck):
+ def __init__(self):
+ AbstractCheck.AbstractFilesCheck.__init__(self, "PamCheck",
+ "/etc/pam\.d/.*")
+
+ def check_file(self, pkg, filename):
+ lines = pkg.grep(pam_stack_re, filename)
+ if lines:
+ printError(pkg, 'use-old-pam-stack', filename,
+ '(line %s)' % ", ".join(lines))
+
+check = PamCheck()
+
+addDetails(
+'use-old-pam-stack',
+'''Update pam file to use include instead of pam_stack.''',
+)
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/Pkg.py b/Pkg.py
new file mode 100644
index 0000000..14118aa
--- /dev/null
+++ b/Pkg.py
@@ -0,0 +1,865 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : Pkg.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Tue Sep 28 07:18:06 1999
+# Version : $Id: Pkg.py 1892 2011-11-23 20:21:05Z scop $
+# Purpose : provide an API to handle a rpm package either by accessing
+# the rpm file or by accessing the files contained inside.
+#############################################################################
+
+import commands
+import os
+import re
+import stat
+import subprocess
+import sys
+import tempfile
+import types
+import urlparse
+
+try:
+ import magic
+ # TODO: magic.MAGIC_COMPRESS when PkgFile gets decompress support.
+ _magic = magic.open(magic.MAGIC_NONE)
+ _magic.load()
+except:
+ _magic = None
+import rpm
+
+import Filter
+
+# Python 2/3 compatibility/convenience wrapper for printing to stderr with
+# less concerns of UnicodeErrors than plain sys.stderr.write.
+if sys.version_info[0] > 2:
+ # Blows up with Python < 3 without the exec() hack
+ exec('def warn(s): print (s, file=sys.stderr)')
+else:
+ def warn(s): print >> sys.stderr, s
+
+
+# utilities
+
+# 64: RPMSENSE_PREREQ is 0 with recent rpm versions, we want 64 here in order
+# to do the right thing with packages built with older rpm versions
+PREREQ_FLAG = (rpm.RPMSENSE_PREREQ or 64) | \
+ rpm.RPMSENSE_SCRIPT_PRE | \
+ rpm.RPMSENSE_SCRIPT_POST | \
+ rpm.RPMSENSE_SCRIPT_PREUN | \
+ rpm.RPMSENSE_SCRIPT_POSTUN
+
+var_regex = re.compile('^(.*)\${?(\w+)}?(.*)$')
+
+def shell_var_value(var, script):
+ assign_regex = re.compile('\\b' + re.escape(var) + '\s*=\s*(.+)\s*(#.*)*$',
+ re.MULTILINE)
+ res = assign_regex.search(script)
+ if res:
+ res2 = var_regex.search(res.group(1))
+ if res2:
+ if res2.group(2) == var: # infinite loop
+ return None
+ return substitute_shell_vars(res.group(1), script)
+ else:
+ return None
+
+def substitute_shell_vars(val, script):
+ res = var_regex.search(val)
+ if res:
+ value = shell_var_value(res.group(2), script)
+ if not value:
+ value = ''
+ return res.group(1) + value + \
+ substitute_shell_vars(res.group(3), script)
+ else:
+ return val
+
+def getstatusoutput(cmd, stdoutonly = False):
+ '''A version of commands.getstatusoutput() which can take cmd as a
+ sequence, thus making it potentially more secure.'''
+ if stdoutonly:
+ proc = subprocess.Popen(cmd, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE, close_fds=True)
+ else:
+ proc = subprocess.Popen(cmd, stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT, close_fds=True)
+ proc.stdin.close()
+ text = proc.stdout.read()
+ sts = proc.wait()
+ if sts is None:
+ sts = 0
+ if text.endswith('\n'):
+ text = text[:-1]
+ return sts, text
+
+bz2_regex = re.compile('\.t?bz2?$')
+xz_regex = re.compile('\.(t[xl]z|xz|lzma)$')
+
+def catcmd(fname):
+ """Get a 'cat' command that handles possibly compressed files."""
+ cat = 'gzip -dcf'
+ if bz2_regex.search(fname):
+ cat = 'bzip2 -dcf'
+ elif xz_regex.search(fname):
+ cat = 'xz -dc'
+ return cat
+
+def is_utf8(fname):
+ (sts, text) = getstatusoutput(catcmd(fname).split() + [fname])
+ return not sts and is_utf8_str(text)
+
+def is_utf8_str(s):
+ try:
+ s.decode('UTF-8')
+ except:
+ return False
+ return True
+
+def to_utf8(string):
+ if string is None:
+ return ''
+ elif isinstance(string, unicode):
+ return string
+ try:
+ x = unicode(string, 'ascii')
+ return string
+ except UnicodeError:
+ encodings = ['utf-8', 'iso-8859-1', 'iso-8859-15', 'iso-8859-2']
+ for enc in encodings:
+ try:
+ x = unicode(string, enc)
+ except UnicodeError:
+ pass
+ else:
+ if x.encode(enc) == string:
+ return x.encode('utf-8')
+ newstring = ''
+ for char in string:
+ if ord(char) > 127:
+ newstring = newstring + '?'
+ else:
+ newstring = newstring + char
+ return newstring
+
+def readlines(path):
+ fobj = open(path, "r")
+ try:
+ return fobj.readlines()
+ finally:
+ fobj.close()
+
+def mktemp():
+ tmpfd, tmpname = tempfile.mkstemp(prefix = 'rpmlint.')
+ tmpfile = os.fdopen(tmpfd, 'w')
+ return tmpfile, tmpname
+
+slash_regex = re.compile('/+')
+slashdot_regex = re.compile('/(\.(/|$))+')
+slashend_regex = re.compile('([^/])/+$')
+
+def safe_normpath(path):
+ """Like os.path.normpath but normalizes less aggressively thus being
+ potentially safer for paths containing symlinks."""
+ ret = slash_regex.sub('/', path)
+ ret = slashdot_regex.sub('\\2', ret)
+ ret = slashend_regex.sub('\\1', ret)
+ return ret
+
+def get_default_valid_rpmgroups(filename = None):
+ """Get default rpm groups from filename, or try to look them up from
+ the rpm package (if installed) if no filename is given"""
+ groups = []
+ if not filename:
+ try:
+ p = InstalledPkg('rpm')
+ except:
+ pass
+ else:
+ groupsfiles = [x for x in p.files() if x.endswith('/GROUPS')]
+ if groupsfiles:
+ filename = groupsfiles[0]
+ if filename and os.path.exists(filename):
+ fobj = open(filename)
+ try:
+ groups = fobj.read().strip().splitlines()
+ finally:
+ fobj.close()
+ if 'Development/Debug' not in groups:
+ groups.append('Development/Debug')
+ if 'Unspecified' not in groups: # auto-added by rpm >= 4.6.0
+ groups.append('Unspecified')
+ groups.sort()
+ return groups
+
+# from yum 3.2.27, rpmUtils.miscutils, with rpmlint modifications
+def compareEVR((e1, v1, r1), (e2, v2, r2)):
+ # return 1: a is newer than b
+ # 0: a and b are the same version
+ # -1: b is newer than a
+ # rpmlint mod: don't stringify None epochs to 'None' strings
+ if e1 is not None:
+ e1 = str(e1)
+ v1 = str(v1)
+ r1 = str(r1)
+ if e2 is not None:
+ e2 = str(e2)
+ v2 = str(v2)
+ r2 = str(r2)
+ rc = rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
+ return rc
+
+# from yum 3.2.27, rpmUtils.miscutils, with rpmlint modifications
+def rangeCompare(reqtuple, provtuple):
+ """returns true if provtuple satisfies reqtuple"""
+ (reqn, reqf, (reqe, reqv, reqr)) = reqtuple
+ (n, f, (e, v, r)) = provtuple
+ if reqn != n:
+ return 0
+
+ # unversioned satisfies everything
+ if not f or not reqf:
+ return 1
+
+ # and you thought we were done having fun
+ # if the requested release is left out then we have
+ # to remove release from the package prco to make sure the match
+ # is a success - ie: if the request is EQ foo 1:3.0.0 and we have
+ # foo 1:3.0.0-15 then we have to drop the 15 so we can match
+ if reqr is None:
+ r = None
+ # rpmlint mod: don't mess with provided Epoch, doing so breaks e.g.
+ # "Requires: foo < 1.0" should not be satisfied by "Provides: foo = 1:0.5"
+ #if reqe is None:
+ # e = None
+ if reqv is None: # just for the record if ver is None then we're going to segfault
+ v = None
+
+ # if we just require foo-version, then foo-version-* will match
+ if r is None:
+ reqr = None
+
+ rc = compareEVR((e, v, r), (reqe, reqv, reqr))
+
+ # does not match unless
+ if rc >= 1:
+ if reqf in ['GT', 'GE', 4, 12]:
+ return 1
+ if reqf in ['EQ', 8]:
+ if f in ['LE', 10, 'LT', 2]:
+ return 1
+ if reqf in ['LE', 'LT', 'EQ', 10, 2, 8]:
+ if f in ['LE', 'LT', 10, 2]:
+ return 1
+
+ if rc == 0:
+ if reqf in ['GT', 4]:
+ if f in ['GT', 'GE', 4, 12]:
+ return 1
+ if reqf in ['GE', 12]:
+ if f in ['GT', 'GE', 'EQ', 'LE', 4, 12, 8, 10]:
+ return 1
+ if reqf in ['EQ', 8]:
+ if f in ['EQ', 'GE', 'LE', 8, 12, 10]:
+ return 1
+ if reqf in ['LE', 10]:
+ if f in ['EQ', 'LE', 'LT', 'GE', 8, 10, 2, 12]:
+ return 1
+ if reqf in ['LT', 2]:
+ if f in ['LE', 'LT', 10, 2]:
+ return 1
+ if rc <= -1:
+ if reqf in ['GT', 'GE', 'EQ', 4, 12, 8]:
+ if f in ['GT', 'GE', 4, 12]:
+ return 1
+ if reqf in ['LE', 'LT', 10, 2]:
+ return 1
+# if rc >= 1:
+# if reqf in ['GT', 'GE', 4, 12]:
+# return 1
+# if rc == 0:
+# if reqf in ['GE', 'LE', 'EQ', 8, 10, 12]:
+# return 1
+# if rc <= -1:
+# if reqf in ['LT', 'LE', 2, 10]:
+# return 1
+
+ return 0
+
+# from yum 3.2.23, rpmUtils.miscutils, with rpmlint modifications
+def formatRequire(name, flags, evr):
+ s = name
+
+ if flags:
+ if flags & (rpm.RPMSENSE_LESS | rpm.RPMSENSE_GREATER |
+ rpm.RPMSENSE_EQUAL):
+ s = s + " "
+ if flags & rpm.RPMSENSE_LESS:
+ s = s + "<"
+ if flags & rpm.RPMSENSE_GREATER:
+ s = s + ">"
+ if flags & rpm.RPMSENSE_EQUAL:
+ s = s + "="
+ s = "%s %s" % (s, versionToString(evr))
+ return s
+
+def versionToString(evr):
+ if not isinstance(evr, tuple) and not isinstance(evr, list):
+ # assume string
+ return evr
+ ret = ""
+ if evr[0] is not None and evr[0] != "":
+ ret += str(evr[0]) + ":"
+ if evr[1] is not None:
+ ret += evr[1]
+ if evr[2] is not None and evr[2] != "":
+ ret += "-" + evr[2]
+ return ret
+
+# from yum 3.2.23, rpmUtils.miscutils, with some rpmlint modifications
+def stringToVersion(verstring):
+ if verstring in (None, ''):
+ return (None, None, None)
+ epoch = None
+ i = verstring.find(':')
+ if i != -1:
+ try:
+ epoch = str(long(verstring[:i]))
+ except ValueError:
+ # garbage in epoch, ignore it
+ pass
+ i += 1
+ j = verstring.find('-', i)
+ if j != -1:
+ if verstring[i:j] == '':
+ version = None
+ else:
+ version = verstring[i:j]
+ release = verstring[j+1:]
+ else:
+ if verstring[i:] == '':
+ version = None
+ else:
+ version = verstring[i:]
+ release = None
+ return (epoch, version, release)
+
+def parse_deps(line):
+ '''Parse provides/requires/conflicts/obsoletes line to list of
+ (name, flags, (epoch, version, release)) tuples.'''
+
+ prcos = []
+ tokens = re.split('[\s,]+', line.strip())
+
+ # Drop line continuation backslash in multiline macro definition (for
+ # spec file parsing), e.g.
+ # [...] \
+ # Obsoletes: foo-%1 <= 1.0.0 \
+ # [...] \
+ # (yes, this is an ugly hack and we probably have other problems with
+ # multiline macro definitions elsewhere...)
+ if tokens[-1] == '\\':
+ del tokens[-1]
+
+ prco = []
+ while tokens:
+ token = tokens.pop(0)
+ if not token:
+ # skip empty tokens
+ continue
+
+ plen = len(prco)
+
+ if plen == 0:
+ prco.append(token)
+
+ elif plen == 1:
+ flags = 0
+ if token[0] in ("=", "<", "<=", ">", ">="):
+ # versioned, flags
+ if "=" in token:
+ flags |= rpm.RPMSENSE_EQUAL
+ if "<" in token:
+ flags |= rpm.RPMSENSE_LESS
+ if ">" in token:
+ flags |= rpm.RPMSENSE_GREATER
+ prco.append(flags)
+ else:
+ # no flags following name, treat as unversioned, add and reset
+ prco.extend((flags, (None, None, None)))
+ prcos.append(tuple(prco))
+ prco = [token]
+
+ elif plen == 2:
+ # last token of versioned one, add and reset
+ prco.append(stringToVersion(token))
+ prcos.append(tuple(prco))
+ prco = []
+
+ plen = len(prco)
+ if plen:
+ if plen == 1:
+ prco.extend((0, (None, None, None)))
+ elif plen == 2:
+ prco.append((None, None, None))
+ prcos.append(tuple(prco))
+
+ return prcos
+
+
+# classes representing package
+
+class Pkg:
+
+ _magic_from_compressed_re = re.compile('\([^)]+\s+compressed\s+data\\b')
+
+ def __init__(self, filename, dirname, header = None, is_source = False):
+ self.filename = filename
+ self.extracted = False
+ self.dirname = dirname
+ self.current_linenum = None
+ self._config_files = None
+ self._doc_files = None
+ self._noreplace_files = None
+ self._ghost_files = None
+ self._missingok_files = None
+ self._files = None
+ self._requires = None
+ self._req_names = -1
+
+ if header:
+ self.header = header
+ self.is_source = is_source
+ else:
+ # Create a package object from the file name
+ ts = rpm.TransactionSet()
+ # Don't check signatures here...
+ ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
+ fd = os.open(filename, os.O_RDONLY)
+ try:
+ self.header = ts.hdrFromFdno(fd)
+ finally:
+ os.close(fd)
+ self.is_source = not self.header[rpm.RPMTAG_SOURCERPM]
+
+ self.name = self.header[rpm.RPMTAG_NAME]
+ if self.isNoSource():
+ self.arch = 'nosrc'
+ elif self.isSource():
+ self.arch = 'src'
+ else:
+ self.arch = self.header[rpm.RPMTAG_ARCH]
+
+ # Return true if the package is a source package
+ def isSource(self):
+ return self.is_source
+
+ # Return true if the package is a nosource package.
+ # NoSource files are ghosts in source packages.
+ def isNoSource(self):
+ return self.is_source and self.ghostFiles()
+
+ # access the tags like an array
+ def __getitem__(self, key):
+ try:
+ val = self.header[key]
+ except:
+ val = []
+ if val == []:
+ return None
+ else:
+ return val
+
+ # return the name of the directory where the package is extracted
+ def dirName(self):
+ if not self.extracted:
+ self._extract()
+ return self.dirname
+
+ # extract rpm contents
+ def _extract(self):
+ s = os.stat(self.dirname)
+ if not stat.S_ISDIR(s[stat.ST_MODE]):
+ warn('Unable to access dir %s' % self.dirname)
+ return None
+ else:
+ self.dirname = tempfile.mkdtemp(
+ prefix = 'rpmlint.%s.' % os.path.basename(self.filename),
+ dir = self.dirname)
+ # TODO: better shell escaping or sequence based command invocation
+ command_str = \
+ 'rpm2cpio "%s" | (cd "%s"; cpio -id); chmod -R +rX "%s"' % \
+ (self.filename, self.dirname, self.dirname)
+ cmd = commands.getstatusoutput(command_str)
+ self.extracted = True
+ return cmd
+
+ def checkSignature(self):
+ return getstatusoutput(('env', 'LC_ALL=C', 'rpm', '-K', self.filename))
+
+ # remove the extracted files from the package
+ def cleanup(self):
+ if self.extracted and self.dirname:
+ getstatusoutput(('rm', '-rf', self.dirname))
+
+ def grep(self, regex, filename):
+ """Grep regex from a file, return matching line numbers."""
+ ret = []
+ lineno = 0
+ in_file = None
+ try:
+ try:
+ in_file = open(self.dirName() + '/' + filename)
+ for line in in_file:
+ lineno += 1
+ if regex.search(line):
+ ret.append(str(lineno))
+ break
+ except Exception, e:
+ Filter.printWarning(self, 'read-error', filename, e)
+ finally:
+ if in_file:
+ in_file.close()
+ return ret
+
+ def langtag(self, tag, lang):
+ """Get value of tag in the given language."""
+ # LANGUAGE trumps other env vars per GNU gettext docs, see also #166
+ orig = os.environ.get('LANGUAGE')
+ os.environ['LANGUAGE'] = lang
+ ret = self[tag]
+ if orig is not None:
+ os.environ['LANGUAGE'] = orig
+ return ret
+
+ # return the associative array indexed on file names with
+ # the values as: (file perm, file owner, file group, file link to)
+ def files(self):
+ if self._files is not None:
+ return self._files
+
+ self._gatherFilesInfo()
+ return self._files
+
+ # return the list of config files
+ def configFiles(self):
+ if self._config_files is not None:
+ return self._config_files
+
+ self._config_files = [x.name for x in self.files().values()
+ if x.is_config]
+ return self._config_files
+
+ # return the list of noreplace files
+ def noreplaceFiles(self):
+ if self._noreplace_files is not None:
+ return self._noreplace_files
+
+ self._noreplace_files = [x.name for x in self.files().values()
+ if x.is_noreplace]
+ return self._noreplace_files
+
+ # return the list of documentation files
+ def docFiles(self):
+ if self._doc_files is not None:
+ return self._doc_files
+
+ self._doc_files = [x.name for x in self.files().values() if x.is_doc]
+ return self._doc_files
+
+ # return the list of ghost files
+ def ghostFiles(self):
+ if self._ghost_files is not None:
+ return self._ghost_files
+
+ self._ghost_files = [x.name for x in self.files().values()
+ if x.is_ghost]
+ return self._ghost_files
+
+ def missingOkFiles(self):
+ if self._missingok_files is not None:
+ return self._missingok_files
+
+ self._missingok_files = [x.name for x in self.files().values()
+ if x.is_missingok]
+ return self._missingok_files
+
+ # extract information about the files
+ def _gatherFilesInfo(self):
+
+ self._files = {}
+ flags = self.header[rpm.RPMTAG_FILEFLAGS]
+ modes = self.header[rpm.RPMTAG_FILEMODES]
+ users = self.header[rpm.RPMTAG_FILEUSERNAME]
+ groups = self.header[rpm.RPMTAG_FILEGROUPNAME]
+ links = self.header[rpm.RPMTAG_FILELINKTOS]
+ sizes = self.header[rpm.RPMTAG_FILESIZES]
+ md5s = self.header[rpm.RPMTAG_FILEMD5S]
+ mtimes = self.header[rpm.RPMTAG_FILEMTIMES]
+ rdevs = self.header[rpm.RPMTAG_FILERDEVS]
+ langs = self.header[rpm.RPMTAG_FILELANGS]
+ inodes = self.header[rpm.RPMTAG_FILEINODES]
+ requires = self.header[rpm.RPMTAG_FILEREQUIRE]
+ provides = self.header[rpm.RPMTAG_FILEPROVIDE]
+ files = self.header[rpm.RPMTAG_FILENAMES]
+ magics = self.header[rpm.RPMTAG_FILECLASS]
+ try: # rpm >= 4.7.0
+ filecaps = self.header[rpm.RPMTAG_FILECAPS]
+ except:
+ filecaps = None
+
+ # rpm-python < 4.6 does not return a list for this (or FILEDEVICES,
+ # FWIW) for packages containing exactly one file
+ if not isinstance(inodes, types.ListType):
+ inodes = [inodes]
+
+ if files:
+ for idx in range(0, len(files)):
+ pkgfile = PkgFile(files[idx])
+ # Do not use os.path.join here, pkgfile.name can start with a
+ # / which would result in self.dirName being ignored
+ pkgfile.path = os.path.normpath(
+ self.dirName() + '/' + pkgfile.name)
+ pkgfile.flags = flags[idx]
+ pkgfile.mode = modes[idx]
+ pkgfile.user = users[idx]
+ pkgfile.group = groups[idx]
+ pkgfile.linkto = links[idx] and safe_normpath(links[idx])
+ pkgfile.size = sizes[idx]
+ pkgfile.md5 = md5s[idx]
+ pkgfile.mtime = mtimes[idx]
+ pkgfile.rdev = rdevs[idx]
+ pkgfile.inode = inodes[idx]
+ pkgfile.requires = parse_deps(requires[idx])
+ pkgfile.provides = parse_deps(provides[idx])
+ pkgfile.lang = langs[idx]
+ pkgfile.magic = magics[idx]
+ if not pkgfile.magic and _magic:
+ pkgfile.magic = _magic.file(pkgfile.path)
+ if pkgfile.magic is None:
+ pkgfile.magic = ''
+ elif Pkg._magic_from_compressed_re.search(pkgfile.magic):
+ # Discard magic from inside compressed files ("file -z")
+ # until PkgFile gets decompression support. We may get
+ # such magic strings from package headers already now;
+ # for example Fedora's rpmbuild as of F-11's 4.7.1 is
+ # patched so it generates them.
+ pkgfile.magic = ''
+ if filecaps:
+ pkgfile.filecaps = filecaps[idx]
+ self._files[pkgfile.name] = pkgfile
+
+ def readlink(self, pkgfile):
+ """Resolve symlinks for the given PkgFile, return the dereferenced
+ PkgFile if it is found in this package, None if not."""
+ result = pkgfile
+ while result and result.linkto:
+ linkpath = urlparse.urljoin(result.name, result.linkto)
+ linkpath = safe_normpath(linkpath)
+ result = self.files().get(linkpath)
+ return result
+
+ # API to access dependency information
+ def obsoletes(self):
+ """Get package Obsoletes as list of
+ (name, flags, (epoch, version, release)) tuples."""
+ self._gatherDepInfo()
+ return self._obsoletes
+
+ def requires(self):
+ """Get package Requires as list of
+ (name, flags, (epoch, version, release)) tuples."""
+ self._gatherDepInfo()
+ return self._requires
+
+ def prereq(self):
+ """Get package PreReqs as list of
+ (name, flags, (epoch, version, release)) tuples."""
+ self._gatherDepInfo()
+ return self._prereq
+
+ def req_names(self):
+ if self._req_names == -1:
+ self._req_names = [x[0] for x in self.requires() + self.prereq()]
+ return self._req_names
+
+ def check_versioned_dep(self, name, version):
+ # try to match name%_isa as well (e.g. "foo(x86-64)", "foo(x86-32)")
+ name_re = re.compile('^%s(\(\w+-\d+\))?$' % re.escape(name))
+ for d in self.requires() + self.prereq():
+ if name_re.match(d[0]):
+ if d[1] & rpm.RPMSENSE_EQUAL != rpm.RPMSENSE_EQUAL \
+ or d[2][1] != version:
+ return False
+ return True
+ return False
+
+ def conflicts(self):
+ """Get package Conflicts as list of
+ (name, flags, (epoch, version, release)) tuples."""
+ self._gatherDepInfo()
+ return self._conflicts
+
+ def provides(self):
+ """Get package Provides as list of
+ (name, flags, (epoch, version, release)) tuples."""
+ self._gatherDepInfo()
+ return self._provides
+
+ # internal function to gather dependency info used by the above ones
+ def _gather_aux(self, header, list, nametag, flagstag, versiontag,
+ prereq = None):
+ names = header[nametag]
+ flags = header[flagstag]
+ versions = header[versiontag]
+
+ if versions:
+ for loop in range(len(versions)):
+ evr = stringToVersion(versions[loop])
+ if prereq is not None and flags[loop] & PREREQ_FLAG:
+ prereq.append((names[loop], flags[loop] & (~PREREQ_FLAG),
+ evr))
+ else:
+ list.append((names[loop], flags[loop], evr))
+
+ def _gatherDepInfo(self):
+ if self._requires is None:
+ self._requires = []
+ self._prereq = []
+ self._provides = []
+ self._conflicts = []
+ self._obsoletes = []
+
+ self._gather_aux(self.header, self._requires,
+ rpm.RPMTAG_REQUIRENAME,
+ rpm.RPMTAG_REQUIREFLAGS,
+ rpm.RPMTAG_REQUIREVERSION,
+ self._prereq)
+ self._gather_aux(self.header, self._conflicts,
+ rpm.RPMTAG_CONFLICTNAME,
+ rpm.RPMTAG_CONFLICTFLAGS,
+ rpm.RPMTAG_CONFLICTVERSION)
+ self._gather_aux(self.header, self._provides,
+ rpm.RPMTAG_PROVIDENAME,
+ rpm.RPMTAG_PROVIDEFLAGS,
+ rpm.RPMTAG_PROVIDEVERSION)
+ self._gather_aux(self.header, self._obsoletes,
+ rpm.RPMTAG_OBSOLETENAME,
+ rpm.RPMTAG_OBSOLETEFLAGS,
+ rpm.RPMTAG_OBSOLETEVERSION)
+
+ def scriptprog(self, which):
+ """Get the specified script interpreter as a string.
+ Depending on rpm-python version, the string may or may not include
+ interpreter arguments, if any."""
+ prog = self[which]
+ if prog is None:
+ prog = ""
+ elif not isinstance(prog, basestring):
+ # http://rpm.org/ticket/847#comment:2
+ prog = " ".join(prog)
+ return prog
+
+def getInstalledPkgs(name):
+ """Get list of installed package objects by name."""
+
+ pkgs = []
+ ts = rpm.TransactionSet()
+ if re.search('[?*]|\[.+\]', name):
+ mi = ts.dbMatch()
+ mi.pattern("name", rpm.RPMMIRE_GLOB, name)
+ else:
+ mi = ts.dbMatch("name", name)
+
+ for hdr in mi:
+ pkgs.append(InstalledPkg(name, hdr))
+
+ return pkgs
+
+# Class to provide an API to an installed package
+class InstalledPkg(Pkg):
+ def __init__(self, name, hdr = None):
+ if not hdr:
+ ts = rpm.TransactionSet()
+ mi = ts.dbMatch('name', name)
+ if not mi:
+ raise KeyError(name)
+ try:
+ hdr = mi.next()
+ except StopIteration:
+ raise KeyError(name)
+
+ Pkg.__init__(self, name, '/', hdr)
+
+ self.extracted = True
+ # create a fake filename to satisfy some checks on the filename
+ self.filename = '%s-%s-%s.%s.rpm' % \
+ (self.name, self[rpm.RPMTAG_VERSION], self[rpm.RPMTAG_RELEASE],
+ self[rpm.RPMTAG_ARCH])
+
+ def cleanup(self):
+ pass
+
+ def checkSignature(self):
+ return (0, 'fake: pgp md5 OK')
+
+# Class to provide an API to a "fake" package, eg. for specfile-only checks
+class FakePkg:
+ def __init__(self, name):
+ self.name = name
+ self.arch = None
+ self.current_linenum = None
+
+ def cleanup(self):
+ pass
+
+# Class for files in packages
+class PkgFile(object):
+
+ def __init__(self, name):
+ self.name = name
+ # Real path to the file (taking extract dir into account)
+ self.path = name
+ self.flags = 0
+ self.mode = 0
+ self.user = None
+ self.group = None
+ self.linkto = ''
+ self.size = None
+ self.md5 = None
+ self.mtime = 0
+ self.rdev = ''
+ self.inode = 0
+ self.requires = []
+ self.provides = []
+ self.lang = ''
+ self.magic = ''
+ self.filecaps = None
+
+ # TODO: decompression support
+
+ is_config = property(lambda self: self.flags & rpm.RPMFILE_CONFIG)
+ is_doc = property(lambda self: self.flags & rpm.RPMFILE_DOC)
+ is_noreplace = property(lambda self: self.flags & rpm.RPMFILE_NOREPLACE)
+ is_ghost = property(lambda self: self.flags & rpm.RPMFILE_GHOST)
+ is_missingok = property(lambda self: self.flags & rpm.RPMFILE_MISSINGOK)
+
+
+if __name__ == '__main__':
+ for p in sys.argv[1:]:
+ pkg = Pkg(sys.argv[1], tempfile.gettempdir())
+ print ('Requires: %s' % pkg.requires())
+ print ('Prereq: %s' % pkg.prereq())
+ print ('Conflicts: %s' % pkg.conflicts())
+ print ('Provides: %s' % pkg.provides())
+ print ('Obsoletes: %s' % pkg.obsoletes())
+ pkg.cleanup()
+
+# Pkg.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/PostCheck.py b/PostCheck.py
new file mode 100644
index 0000000..c72bbe1
--- /dev/null
+++ b/PostCheck.py
@@ -0,0 +1,257 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# Project : Mandriva Linux
+# Module : rpmlint
+# File : PostCheck.py
+# Version : $Id: PostCheck.py 1885 2011-09-13 18:15:29Z scop $
+# Author : Frederic Lepied
+# Created On : Wed Jul 5 13:30:17 2000
+# Purpose : Check post/pre scripts
+#############################################################################
+
+import os
+import re
+import types
+
+import rpm
+
+from Filter import addDetails, printError, printWarning
+import AbstractCheck
+import Config
+import Pkg
+
+
+DEFAULT_VALID_SHELLS = ('<lua>',
+ '/bin/sh',
+ '/bin/bash',
+ '/sbin/sash',
+ '/usr/bin/perl',
+ '/sbin/ldconfig',
+ )
+
+DEFAULT_EMPTY_SHELLS = ('/sbin/ldconfig',
+ )
+
+valid_shells = Config.getOption('ValidShells', DEFAULT_VALID_SHELLS)
+empty_shells = Config.getOption('ValidEmptyShells', DEFAULT_EMPTY_SHELLS)
+# shells that grok the -n switch for debugging
+syntaxcheck_shells = ('/bin/sh', '/bin/bash')
+
+percent_regex = re.compile('^[^#]*%{?\w{3,}', re.MULTILINE)
+bracket_regex = re.compile('^[^#]*if.*[^ :\]]\]', re.MULTILINE)
+home_regex = re.compile('[^a-zA-Z]+~/|\${?HOME(\W|$)', re.MULTILINE)
+dangerous_command_regex = re.compile("(^|[;\|`]|&&|$\()\s*(?:\S*/s?bin/)?(cp|mv|ln|tar|rpm|chmod|chown|rm|cpio|install|perl|userdel|groupdel)\s", re.MULTILINE)
+selinux_regex = re.compile("(^|[;\|`]|&&|$\()\s*(?:\S*/s?bin/)?(chcon|runcon)\s", re.MULTILINE)
+single_command_regex = re.compile("^[ \n]*([^ \n]+)[ \n]*$")
+tmp_regex = re.compile('^[^#]*\s(/var)?/tmp', re.MULTILINE)
+menu_regex = re.compile('^/usr/lib/menu/|^/etc/menu-methods/|^/usr/share/applications/')
+bogus_var_regex = re.compile('(\${?RPM_BUILD_(ROOT|DIR)}?)')
+
+prereq_assoc = (
+# ['chkconfig', ('chkconfig', '/sbin/chkconfig')],
+ ['chkfontpath', ('chkfontpath', '/usr/sbin/chkfontpath')],
+ ['rpm-helper', ('rpm-helper',)],
+ )
+
+for p in prereq_assoc:
+ p[0] = re.compile('^[^#]+' + p[0], re.MULTILINE)
+
+# pychecker fix
+del p
+
+script_tags = [
+ (rpm.RPMTAG_PREIN, rpm.RPMTAG_PREINPROG, '%pre'),
+ (rpm.RPMTAG_POSTIN, rpm.RPMTAG_POSTINPROG, '%post'),
+ (rpm.RPMTAG_PREUN, rpm.RPMTAG_PREUNPROG, '%preun'),
+ (rpm.RPMTAG_POSTUN, rpm.RPMTAG_POSTUNPROG, '%postun'),
+ (rpm.RPMTAG_TRIGGERSCRIPTS, rpm.RPMTAG_TRIGGERSCRIPTPROG, '%trigger'),
+ (rpm.RPMTAG_PRETRANS, rpm.RPMTAG_PRETRANSPROG, '%pretrans'),
+ (rpm.RPMTAG_POSTTRANS, rpm.RPMTAG_POSTTRANSPROG, '%posttrans'),
+ (rpm.RPMTAG_VERIFYSCRIPT, rpm.RPMTAG_VERIFYSCRIPTPROG, '%verifyscript'),
+ ]
+
+def incorrect_shell_script(prog, shellscript):
+ if not shellscript:
+ return False
+ # TODO: test that "prog" is available/executable
+ tmpfile, tmpname = Pkg.mktemp()
+ try:
+ tmpfile.write(shellscript)
+ tmpfile.close()
+ ret = Pkg.getstatusoutput((prog, '-n', tmpname))
+ finally:
+ tmpfile.close()
+ os.remove(tmpname)
+ return ret[0]
+
+def incorrect_perl_script(prog, perlscript):
+ if not perlscript:
+ return False
+ # TODO: test that "prog" is available/executable
+ tmpfile, tmpname = Pkg.mktemp()
+ try:
+ tmpfile.write(perlscript)
+ tmpfile.close()
+ ret = Pkg.getstatusoutput((prog, '-wc', tmpname))
+ finally:
+ tmpfile.close()
+ os.remove(tmpname)
+ return ret[0]
+
+class PostCheck(AbstractCheck.AbstractCheck):
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, 'PostCheck')
+
+ def check(self, pkg):
+ # Check only binary package
+ if pkg.isSource():
+ return
+
+ prereq = [x[0] for x in pkg.prereq()]
+ files = pkg.files()
+
+ for tag in script_tags:
+ script = pkg[tag[0]]
+
+ if not isinstance(script, types.ListType):
+ prog = pkg.scriptprog(tag[1])
+ if prog:
+ prog = prog.split()[0]
+ self.check_aux(pkg, files, prog, script, tag[2], prereq)
+ else:
+ prog = pkg[tag[1]]
+ for idx in range(0, len(prog)):
+ self.check_aux(
+ pkg, files, prog[idx], script[idx], tag[2], prereq)
+
+ ghost_files = pkg.ghostFiles()
+ if ghost_files:
+ postin = pkg[rpm.RPMTAG_POSTIN]
+ prein = pkg[rpm.RPMTAG_PREIN]
+ if not postin and not prein:
+ printWarning(pkg, 'ghost-files-without-postin')
+ else:
+ for f in ghost_files:
+ if (not postin or f not in postin) and \
+ (not prein or f not in prein) and \
+ f not in pkg.missingOkFiles():
+ printWarning(pkg,
+ 'postin-without-ghost-file-creation', f)
+
+ def check_aux(self, pkg, files, prog, script, tag, prereq):
+ if script:
+ if prog:
+ if prog not in valid_shells:
+ printError(pkg, 'invalid-shell-in-' + tag, prog)
+ if prog in empty_shells:
+ printError(pkg, 'non-empty-' + tag, prog)
+ if prog in syntaxcheck_shells or prog == '/usr/bin/perl':
+ if percent_regex.search(script):
+ printWarning(pkg, 'percent-in-' + tag)
+ if bracket_regex.search(script):
+ printWarning(pkg, 'spurious-bracket-in-' + tag)
+ res = dangerous_command_regex.search(script)
+ if res:
+ printWarning(pkg, 'dangerous-command-in-' + tag,
+ res.group(2))
+ res = selinux_regex.search(script)
+ if res:
+ printError(pkg, 'forbidden-selinux-command-in-' + tag,
+ res.group(2))
+
+ if 'update-menus' in script:
+ menu_error = True
+ for f in files:
+ if menu_regex.search(f):
+ menu_error = False
+ break
+ if menu_error:
+ printError(pkg, 'update-menus-without-menu-file-in-' +
+ tag)
+ if tmp_regex.search(script):
+ printError(pkg, 'use-tmp-in-' + tag)
+ for c in prereq_assoc:
+ if c[0].search(script):
+ found = False
+ for p in c[1]:
+ if p in prereq or p in files:
+ found = True
+ break
+ if not found:
+ printError(pkg, 'no-prereq-on', c[1][0])
+
+ if prog in syntaxcheck_shells:
+ if incorrect_shell_script(prog, script):
+ printError(pkg, 'shell-syntax-error-in-' + tag)
+ if home_regex.search(script):
+ printError(pkg, 'use-of-home-in-' + tag)
+ res = bogus_var_regex.search(script)
+ if res:
+ printWarning(pkg, 'bogus-variable-use-in-' + tag,
+ res.group(1))
+
+ if prog == '/usr/bin/perl':
+ if incorrect_perl_script(prog, script):
+ printError(pkg, 'perl-syntax-error-in-' + tag)
+ elif prog.endswith('sh'):
+ res = single_command_regex.search(script)
+ if res:
+ printWarning(pkg, 'one-line-command-in-' + tag,
+ res.group(1))
+
+ elif prog not in empty_shells and prog in valid_shells:
+ printWarning(pkg, 'empty-' + tag)
+
+# Create an object to enable the auto registration of the test
+check = PostCheck()
+
+# Add information about checks
+addDetails(
+'postin-without-ghost-file-creation',
+'''A file tagged as ghost is not created during %prein nor during %postin.''',
+)
+for scriptlet in (
+ '%pre', '%post', '%preun', '%postun', '%pretrans', '%posttrans',
+ '%trigger', '%triggerin', '%triggerprein', '%triggerun', '%triggerpostun',
+ '%verifyscript'):
+ addDetails(
+'one-line-command-in-%s' % scriptlet,
+'''You should use %s -p <command> instead of using:
+
+%s
+<command>
+
+It will avoid the fork of a shell interpreter to execute your command as
+well as allows rpm to automatically mark the dependency on your command
+for the excecution of the scriptlet.''' % (scriptlet, scriptlet),
+
+'percent-in-%s' % scriptlet,
+'''The %s scriptlet contains a "%%" in a context which might indicate it being
+fallout from an rpm macro/variable which was not expanded during build.
+Investigate whether this is the case and fix if appropriate.''' % scriptlet,
+
+'spurious-bracket-in-%s' % scriptlet,
+'''The %s scriptlet contains an "if []" construct without a space before
+the "]".''' % scriptlet,
+
+'forbidden-selinux-command-in-%s' % scriptlet,
+'''A command which requires intimate knowledge about a specific SELinux
+policy type was found in the scriptlet. These types are subject to change
+on a policy version upgrade. Use the restorecon command which queries the
+currently loaded policy for the correct type instead.''',
+
+'non-empty-%s' % scriptlet,
+'''Scriptlets for the interpreter mentioned in the message should be empty.
+One common case where they are unintentionally not is when the specfile
+contains comments after the scriptlet and before the next section. Review
+and clean up the scriptlet contents if appropriate.''',
+)
+
+# PostCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/README b/README
new file mode 100644
index 0000000..6b8e352
--- /dev/null
+++ b/README
@@ -0,0 +1,67 @@
+rpmlint is a tool for checking common errors in rpm packages. rpmlint
+can be used to test individual packages before uploading or to check
+an entire distribution. By default all applicable checks are
+performed but specific checks can be performed by using command line
+parameters.
+
+rpmlint can check binary rpms (files and installed ones), source rpms,
+and plain specfiles, but all checks do not apply to all argument
+types. For best check coverage, run rpmlint on source rpms instead of
+plain specfiles, and installed binary rpms instead of uninstalled
+binary rpm files.
+
+The idea for rpmlint is from the lintian tool of the Debian project.
+
+Comments and new checks welcome. See the project home page at
+http://rpmlint.zarb.org/ for mailing list information, bug tracking
+system and other project resources.
+
+Implemented checks:
+
+ o Tag checks (TagsCheck).
+ o Distribution specific checks (MandrakeCheck).
+ o Binary checks (BinaryCheck).
+ o Configuration file checks (ConfigCheck).
+ o Location, permission, group and owner checks (FileCheck).
+ o suid warnings (FileCheck).
+ o Signature checks (SignatureCheck).
+ o FHS checks (FHSCheck).
+ o Source specific checks (SourceCheck).
+ o i18n checks (I18NCheck).
+ o Menu system checks (MenuCheck).
+ o %post; %pre, %postun and %preun script checks (PostCheck).
+ o /etc/rc.d/init.d checks (InitScriptCheck).
+ o Spec file checks (SpecCheck).
+ o Zip/Jar file checks (ZipCheck).
+ o Pam configuration file checks (PamCheck).
+ o Rpm file checks (RpmFileCheck).
+
+If you want to change configuration options or the list of checks, use
+the global configuration files /etc/rpmlint/*config or the user
+configuration file $XDG_CONFIG_HOME/rpmlint (~/.config/rpmlint if
+$XDG_CONFIG_HOME is empty or not set).
+
+Configuration files are Python source files and should begin with the
+following line:
+
+from Config import *
+
+to load configuration functions.
+
+Configuration functions:
+
+resetChecks() resets the list of checks.
+
+addCheck(check) adds the check to the list of checks to try.
+
+addCheckDir(path) adds a path to look for checks.
+
+setOption(name, value) sets the value of the configuration option.
+See below for the list of available options.
+
+addFilter(regexp) adds a filter to remove the output of a check, and
+removeFilter(regexp) removes one (for use eg. in per-user configuration
+files to remove filters added in system config files).
+
+See the file "config" shipped with rpmlint for examples, available
+options and their default values.
diff --git a/README.devel b/README.devel
new file mode 100644
index 0000000..81657d1
--- /dev/null
+++ b/README.devel
@@ -0,0 +1,15 @@
+The latest development version can be retrieved from anonymous Subversion:
+
+$ svn checkout http://rpmlint.zarb.org/svn/trunk/
+
+To run rpmlint from the working tree or in place from an unpacked tarball:
+
+$ ./rpmlint -C . <rpms>
+
+...and to run only a single check, for example to test only FHSCheck:
+
+$ ./rpmlint -C . -c FHSCheck <rpms>
+
+For profiling, use something like:
+
+$ python -O -u -m cProfile -s cumulative rpmlint -C . [...]
diff --git a/RpmFileCheck.py b/RpmFileCheck.py
new file mode 100644
index 0000000..d4ffb19
--- /dev/null
+++ b/RpmFileCheck.py
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+# check the rpm file for various errors.
+# $Id: RpmFileCheck.py 1732 2010-02-21 11:28:42Z scop $
+
+# Copyright (C) 2006 Michael Scherer <misc@zarb.org>
+# Ville Skyttä <ville.skytta@iki.fi>
+#
+# 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; version 2 of the License.
+#
+# 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+import os
+
+from Filter import addDetails, printWarning
+import AbstractCheck
+
+
+class RpmFileCheck(AbstractCheck.AbstractCheck):
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, "RpmFileCheck")
+
+ def check(self, pkg):
+ # http://en.wikipedia.org/wiki/Joliet_(file_system)
+ rpmfile_name = os.path.basename(pkg.filename)
+ if len(rpmfile_name) > 64:
+ printWarning(pkg, 'filename-too-long-for-joliet', rpmfile_name)
+
+check = RpmFileCheck()
+
+addDetails(
+'filename-too-long-for-joliet',
+'''This filename is too long to fit on a joliet filesystem (limit is 64 unicode
+chars).''',
+)
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/SignatureCheck.py b/SignatureCheck.py
new file mode 100644
index 0000000..fd09812
--- /dev/null
+++ b/SignatureCheck.py
@@ -0,0 +1,60 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : SignatureCheck.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Thu Oct 7 17:06:14 1999
+# Version : $Id: SignatureCheck.py 1732 2010-02-21 11:28:42Z scop $
+# Purpose : check the presence of a PGP signature.
+#############################################################################
+
+import re
+
+from Filter import addDetails, printError
+import AbstractCheck
+import Pkg
+
+
+class SignatureCheck(AbstractCheck.AbstractCheck):
+ pgp_regex = re.compile("pgp|gpg", re.IGNORECASE)
+ unknown_key_regex = re.compile("\(MISSING KEYS:(?:\([^)]+\))?\s+([^\)]+)\)")
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, "SignatureCheck")
+
+ def check(self, pkg):
+ res = pkg.checkSignature()
+ if not res or res[0] != 0:
+ if res and res[1]:
+ kres = SignatureCheck.unknown_key_regex.search(res[1])
+ else:
+ kres = None
+ if kres:
+ printError(pkg, "unknown-key", kres.group(1))
+ else:
+ Pkg.warn("Error checking signature of %s: %s" %
+ (pkg.filename, res[1]))
+ else:
+ if not SignatureCheck.pgp_regex.search(res[1]):
+ printError(pkg, "no-signature")
+
+# Create an object to enable the auto registration of the test
+check = SignatureCheck()
+
+addDetails(
+'no-signature',
+'''You have to include your pgp or gpg signature in your package.
+For more information on signatures, please refer to www.gnupg.org.''',
+
+'unknown-key',
+'''The package was signed, but with an unknown key.
+See the rpm --import option for more information.''',
+)
+
+# SignatureCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/SourceCheck.py b/SourceCheck.py
new file mode 100644
index 0000000..b93d5ba
--- /dev/null
+++ b/SourceCheck.py
@@ -0,0 +1,76 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : SourceCheck.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Wed Oct 27 21:17:03 1999
+# Version : $Id: SourceCheck.py 1754 2010-03-30 20:34:48Z scop $
+# Purpose : verify source package correctness.
+#############################################################################
+
+import re
+
+from Filter import addDetails, printError, printWarning
+import AbstractCheck
+import Config
+
+
+DEFAULT_VALID_SRC_PERMS = (0644, 0755)
+
+source_regex = re.compile('\\.(tar|patch|tgz|diff)$')
+compress_ext = Config.getOption("CompressExtension", "bz2")
+valid_src_perms = Config.getOption("ValidSrcPerms", DEFAULT_VALID_SRC_PERMS)
+
+class SourceCheck(AbstractCheck.AbstractCheck):
+
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, 'SourceCheck')
+
+ def check(self, pkg):
+ # Check only source package
+ if not pkg.isSource():
+ return
+
+ # process file list
+ spec_file = None
+ for fname, pkgfile in pkg.files().items():
+ if fname.endswith('.spec'):
+ if spec_file:
+ printError(pkg, 'multiple-specfiles', spec_file, fname)
+ else:
+ spec_file = fname
+ elif source_regex.search(fname) and compress_ext and \
+ not fname.endswith(compress_ext):
+ printWarning(pkg, 'source-or-patch-not-compressed',
+ compress_ext, fname)
+ perm = pkgfile.mode & 07777
+ if perm not in valid_src_perms:
+ printWarning(pkg, 'strange-permission', fname, oct(perm))
+
+check = SourceCheck()
+
+addDetails(
+'multiple-specfiles',
+'''Your package contains multiple spec files. To build a
+correct package, you need to have only one spec file containing
+all your RPM information.''',
+
+'source-or-patch-not-compressed',
+'''A source archive or file in your package is not compressed using the %s
+compression method (doesn't have the %s extension).''' %
+(compress_ext, compress_ext),
+
+'strange-permission',
+'''A file that you listed to include in your package has strange
+permissions. Usually, a file should have 0644 permissions.''',
+
+)
+
+# SourceCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/SpecCheck.py b/SpecCheck.py
new file mode 100644
index 0000000..97e1da6
--- /dev/null
+++ b/SpecCheck.py
@@ -0,0 +1,812 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : SpecCheck.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Thu Oct 7 17:06:14 1999
+# Version : $Id: SpecCheck.py 1892 2011-11-23 20:21:05Z scop $
+# Purpose : check the spec file of a source rpm.
+#############################################################################
+
+import re
+try:
+ from urlparse import urlparse
+except ImportError: # Python 3
+ from urllib.parse import urlparse
+
+import rpm
+
+from Filter import addDetails, printError, printWarning
+from TagsCheck import VALID_GROUPS
+import AbstractCheck
+import Config
+import Pkg
+
+
+# Don't check for hardcoded library paths in biarch packages
+DEFAULT_BIARCH_PACKAGES = '^(gcc|glibc)'
+
+# Don't check for hardcoded library paths in packages which can have
+# their noarch files in /usr/lib/<package>/*, or packages that can't
+# be installed on biarch systems
+DEFAULT_HARDCODED_LIB_PATH_EXCEPTIONS = '/lib/(modules|cpp|perl5|rpm|hotplug|firmware)($|[\s/,])'
+patch_regex = re.compile("^Patch(\d*)\s*:\s*(\S+)", re.IGNORECASE)
+applied_patch_regex = re.compile("^%patch(\d*)")
+applied_patch_p_regex = re.compile("\s-P\s+(\d+)\\b")
+applied_patch_pipe_regex = re.compile(r'\s%\{PATCH(\d+)\}\s*\|\s*(%\{?__)?patch\b')
+source_dir_regex = re.compile("^[^#]*(\$RPM_SOURCE_DIR|%{?_sourcedir}?)")
+obsolete_tags_regex = re.compile("^(Copyright|Serial)\s*:\s*(\S+)")
+buildroot_regex = re.compile('^BuildRoot\s*:\s*(\S+)', re.IGNORECASE)
+prefix_regex = re.compile('^Prefix\s*:\s*(\S+)', re.IGNORECASE)
+packager_regex = re.compile('^Packager\s*:\s*(\S+)', re.IGNORECASE)
+buildarch_regex = re.compile('^BuildArch(itectures)?\s*:\s*(.+?)\s*$', re.IGNORECASE)
+make_check_regex = re.compile('(^|\s|%{?__)make}?\s+(check|test)')
+rm_regex = re.compile('(^|\s)((.*/)?rm|%{?__rm}?) ')
+rpm_buildroot_regex = re.compile('^[^#]*(?:(\\\*)\${?RPM_BUILD_ROOT}?|(%+){?buildroot}?)')
+configure_libdir_spec_regex = re.compile('ln |\./configure[^#]*--libdir=(\S+)[^#]*')
+lib_package_regex = re.compile('^%package.*\Wlib')
+ifarch_regex = re.compile('^\s*%ifn?arch\s')
+if_regex = re.compile('^\s*%if\s')
+endif_regex = re.compile('^\s*%endif\\b')
+biarch_package_regex = re.compile(DEFAULT_BIARCH_PACKAGES)
+hardcoded_lib_path_exceptions_regex = re.compile(Config.getOption('HardcodedLibPathExceptions', DEFAULT_HARDCODED_LIB_PATH_EXCEPTIONS))
+prereq_regex = re.compile('^PreReq(\(.*\))?:\s*(.+?)\s*$', re.IGNORECASE)
+buildprereq_regex = re.compile('^BuildPreReq:\s*(.+?)\s*$', re.IGNORECASE)
+use_utf8 = Config.getOption('UseUTF8', Config.USEUTF8_DEFAULT)
+libdir_regex = re.compile('%{?_lib(?:dir)?\}?\\b')
+comment_or_empty_regex = re.compile('^\s*(#|$)')
+defattr_regex = re.compile('^\s*%defattr\\b')
+attr_regex = re.compile('^\s*%attr\\b')
+section_regexs = dict(
+ ([x, re.compile('^%' + x + '(?:\s|$)')]
+ for x in ('build', 'changelog', 'check', 'clean', 'description', 'files',
+ 'install', 'package', 'prep', 'pre', 'post', 'preun', 'postun',
+ 'trigger', 'triggerin', 'triggerun', 'triggerprein',
+ 'triggerpostun', 'pretrans', 'posttrans')))
+deprecated_grep_regex = re.compile(r'\b[ef]grep\b')
+
+# Only check for /lib, /usr/lib, /usr/X11R6/lib
+# TODO: better handling of X libraries and modules.
+hardcoded_library_paths = '(/lib|/usr/lib|/usr/X11R6/lib/(?!([^/]+/)+)[^/]*\\.([oa]|la|so[0-9.]*))'
+hardcoded_library_path_regex = re.compile('^[^#]*((^|\s+|\.\./\.\.|\${?RPM_BUILD_ROOT}?|%{?buildroot}?|%{?_prefix}?)' + hardcoded_library_paths + '(?=[\s;/])([^\s,;]*))')
+
+# Requires(pre,post) is broken in some rpm versions, see
+# https://bugzilla.redhat.com/118780 and bugs linked to that one.
+scriptlet_requires_regex = re.compile('^(PreReq|Requires)\([^\)]*,', re.IGNORECASE)
+
+depscript_override_regex = re.compile('(^|\s)%(define|global)\s+__find_(requires|provides)\s')
+depgen_disable_regex = re.compile('(^|\s)%(define|global)\s+_use_internal_dependency_generator\s+0')
+
+# See https://bugzilla.redhat.com/488146 for details
+indent_spaces_regex = re.compile('( \t|(^|\t)([^\t]{8})*[^\t]{4}[^\t]?([^\t][^\t.!?]|[^\t]?[.!?] ) )')
+
+requires_regex = re.compile('^(?:Build)?(?:Pre)?Req(?:uires)?(?:\([^\)]+\))?:\s*(.*)', re.IGNORECASE)
+provides_regex = re.compile('^Provides(?:\([^\)]+\))?:\s*(.*)', re.IGNORECASE)
+obsoletes_regex = re.compile('^Obsoletes:\s*(.*)', re.IGNORECASE)
+conflicts_regex = re.compile('^(?:Build)?Conflicts:\s*(.*)', re.IGNORECASE)
+
+compop_regex = re.compile('[<>=]')
+
+setup_q_regex = re.compile(' -[A-Za-z]*q')
+setup_t_regex = re.compile(' -[A-Za-z]*T')
+setup_ab_regex = re.compile(' -[A-Za-z]*[ab]')
+
+filelist_regex = re.compile('\s+-f\s+\S+')
+pkgname_regex = re.compile('\s+(?:-n\s+)?(\S+)')
+tarball_regex = re.compile('\.(?:t(?:ar|[glx]z|bz2?)|zip)\\b', re.IGNORECASE)
+
+
+def unversioned(deps):
+ '''Yield unversioned dependency names from the given list.'''
+ for dep in deps:
+ if not dep[1]:
+ yield dep[0]
+
+def contains_buildroot(line):
+ '''Check if the given line contains use of rpm buildroot.'''
+ res = rpm_buildroot_regex.search(line)
+ if res and \
+ (not res.group(1) or len(res.group(1)) % 2 == 0) and \
+ (not res.group(2) or len(res.group(2)) % 2 != 0):
+ return True
+ return False
+
+
+class SpecCheck(AbstractCheck.AbstractCheck):
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, "SpecCheck")
+ self._spec_file = None
+
+ def check(self, pkg):
+ if not pkg.isSource():
+ return
+
+ wrong_spec = False
+
+ # lookup spec file
+ for fname, pkgfile in pkg.files().items():
+ if fname.endswith('.spec'):
+ self._spec_file = pkgfile.path
+ if fname == pkg.name + ".spec":
+ wrong_spec = False
+ break
+ else:
+ wrong_spec = True
+ if not self._spec_file:
+ printError(pkg, "no-spec-file")
+ else:
+ if wrong_spec:
+ printError(pkg, "invalid-spec-name")
+
+ # check content of spec file
+ self.check_spec(pkg, self._spec_file)
+
+ def check_spec(self, pkg, spec_file, spec_lines=[]):
+ self._spec_file = spec_file
+ spec_only = isinstance(pkg, Pkg.FakePkg)
+ if not spec_lines:
+ spec_lines = Pkg.readlines(spec_file)
+ patches = {}
+ applied_patches = []
+ applied_patches_ifarch = []
+ source_dir = False
+ buildroot = False
+ configure_linenum = None
+ configure_cmdline = ""
+ mklibname = False
+ is_lib_pkg = False
+ if_depth = 0
+ ifarch_depth = -1
+ current_section = 'package'
+ buildroot_clean = {'clean': False, 'install' : False}
+ depscript_override = False
+ depgen_disabled = False
+ indent_spaces = 0
+ indent_tabs = 0
+ files_has_defattr = False
+ section = {}
+ # None == main package
+ current_package = None
+ package_noarch = {}
+
+ is_utf8 = False
+ if self._spec_file and use_utf8:
+ if Pkg.is_utf8(self._spec_file):
+ is_utf8 = True
+ else:
+ printError(pkg, "non-utf8-spec-file", self._spec_file)
+
+ # gather info from spec lines
+
+ pkg.current_linenum = 0
+
+ nbsp = chr(0xA0)
+ if is_utf8:
+ nbsp = unichr(0xA0)
+
+ for line in spec_lines:
+
+ pkg.current_linenum += 1
+
+ if is_utf8:
+ line = unicode(line, "utf-8", "replace")
+
+ char = line.find(nbsp)
+ if char != -1:
+ printWarning(pkg, "non-break-space", "line %s, char %d" %
+ (pkg.current_linenum, char))
+
+ section_marker = False
+ for sec, regex in section_regexs.items():
+ res = regex.search(line)
+ if res:
+ current_section = sec
+ section_marker = True
+ section[sec] = section.get(sec, 0) + 1
+ if sec in ('package', 'files'):
+ rest = filelist_regex.sub('', line[res.end()-1:])
+ res = pkgname_regex.search(rest)
+ if res:
+ current_package = res.group(1)
+ else:
+ current_package = None
+ break
+
+ if section_marker:
+
+ if current_section == 'files':
+ files_has_defattr = False
+
+ if not is_lib_pkg and lib_package_regex.search(line):
+ is_lib_pkg = True
+
+ continue
+
+ if current_section in ('prep', 'build') and \
+ contains_buildroot(line):
+ printWarning(pkg, 'rpm-buildroot-usage', '%' + current_section,
+ line[:-1].strip())
+
+ if make_check_regex.search(line) and current_section not in \
+ ('check', 'changelog', 'package', 'description'):
+ printWarning(pkg, 'make-check-outside-check-section', line[:-1])
+
+ if current_section in buildroot_clean and \
+ not buildroot_clean[current_section] and \
+ contains_buildroot(line) and rm_regex.search(line):
+ buildroot_clean[current_section] = True
+
+ if ifarch_regex.search(line):
+ if_depth = if_depth + 1
+ ifarch_depth = if_depth
+
+ if if_regex.search(line):
+ if_depth = if_depth + 1
+
+ if line.startswith('%setup'):
+ if not setup_q_regex.search(line):
+ # Don't warn if there's a -T without -a or -b
+ if setup_t_regex.search(line):
+ if setup_ab_regex.search(line):
+ printWarning(pkg, 'setup-not-quiet')
+ else:
+ printWarning(pkg, 'setup-not-quiet')
+ if current_section != 'prep':
+ printWarning(pkg, 'setup-not-in-prep')
+
+ if endif_regex.search(line):
+ if ifarch_depth == if_depth:
+ ifarch_depth = -1
+ if_depth = if_depth - 1
+
+ res = applied_patch_regex.search(line)
+ if res:
+ pnum = res.group(1) or 0
+ for tmp in applied_patch_p_regex.findall(line) or [pnum]:
+ pnum = int(tmp)
+ applied_patches.append(pnum)
+ if ifarch_depth > 0:
+ applied_patches_ifarch.append(pnum)
+ else:
+ res = applied_patch_pipe_regex.search(line)
+ if res:
+ pnum = int(res.group(1))
+ applied_patches.append(pnum)
+ if ifarch_depth > 0:
+ applied_patches_ifarch.append(pnum)
+ if not res and not source_dir:
+ res = source_dir_regex.search(line)
+ if res:
+ source_dir = True
+ printError(pkg, "use-of-RPM_SOURCE_DIR")
+
+ if configure_linenum:
+ if configure_cmdline[-1] == "\\":
+ configure_cmdline = configure_cmdline[:-1] + line.strip()
+ else:
+ res = configure_libdir_spec_regex.search(configure_cmdline)
+ if not res:
+ # Hack to get the correct (start of ./configure) line
+ # number displayed:
+ real_linenum = pkg.current_linenum
+ pkg.current_linenum = configure_linenum
+ printWarning(pkg, "configure-without-libdir-spec")
+ pkg.current_linenum = real_linenum
+ elif res.group(1):
+ res = re.match(hardcoded_library_paths, res.group(1))
+ if res:
+ printError(pkg, "hardcoded-library-path",
+ res.group(1), "in configure options")
+ configure_linenum = None
+
+ hashPos = line.find("#")
+
+ if current_section != 'changelog':
+ cfgPos = line.find('./configure')
+ if cfgPos != -1 and (hashPos == -1 or hashPos > cfgPos):
+ # store line where it started
+ configure_linenum = pkg.current_linenum
+ configure_cmdline = line.strip()
+
+ res = hardcoded_library_path_regex.search(line)
+ if current_section != 'changelog' and res and not \
+ (biarch_package_regex.match(pkg.name) or
+ hardcoded_lib_path_exceptions_regex.search(
+ res.group(1).lstrip())):
+ printError(pkg, "hardcoded-library-path", "in",
+ res.group(1).lstrip())
+
+ if '%mklibname' in line:
+ mklibname = True
+
+ if current_section == 'package':
+
+ # Would be cleaner to get sources and patches from the
+ # specfile parsed in Python (see below), but we want to
+ # catch %ifarch'd etc ones as well, and also catch these when
+ # the specfile is not parseable.
+
+ res = patch_regex.search(line)
+ if res:
+ pnum = int(res.group(1) or 0)
+ patches[pnum] = res.group(2)
+
+ res = obsolete_tags_regex.search(line)
+ if res:
+ printWarning(pkg, "obsolete-tag", res.group(1))
+
+ res = buildroot_regex.search(line)
+ if res:
+ buildroot = True
+ if res.group(1).startswith('/'):
+ printWarning(pkg, 'hardcoded-path-in-buildroot-tag',
+ res.group(1))
+
+ res = buildarch_regex.search(line)
+ if res:
+ if res.group(2) != "noarch":
+ printError(pkg, 'buildarch-instead-of-exclusivearch-tag', res.group(2))
+ else:
+ package_noarch[current_package] = True
+
+ res = packager_regex.search(line)
+ if res:
+ printWarning(pkg, 'hardcoded-packager-tag', res.group(1))
+
+ res = prefix_regex.search(line)
+ if res:
+ if not res.group(1).startswith('%'):
+ printWarning(pkg, 'hardcoded-prefix-tag', res.group(1))
+
+ res = prereq_regex.search(line)
+ if res:
+ printError(pkg, 'prereq-use', res.group(2))
+
+ res = buildprereq_regex.search(line)
+ if res:
+ printError(pkg, 'buildprereq-use', res.group(1))
+
+ if scriptlet_requires_regex.search(line):
+ printError(pkg, 'broken-syntax-in-scriptlet-requires',
+ line.strip())
+
+ res = requires_regex.search(line)
+ if res:
+ reqs = Pkg.parse_deps(res.group(1))
+ for req in unversioned(reqs):
+ if compop_regex.search(req):
+ printWarning(pkg, 'comparison-operator-in-deptoken',
+ req)
+
+ res = provides_regex.search(line)
+ if res:
+ provs = Pkg.parse_deps(res.group(1))
+ for prov in unversioned(provs):
+ printWarning(pkg, 'unversioned-explicit-provides', prov)
+ if compop_regex.search(prov):
+ printWarning(pkg, 'comparison-operator-in-deptoken',
+ prov)
+
+ res = obsoletes_regex.search(line)
+ if res:
+ obses = Pkg.parse_deps(res.group(1))
+ for obs in unversioned(obses):
+ printWarning(pkg, 'unversioned-explicit-obsoletes', obs)
+ if compop_regex.search(obs):
+ printWarning(pkg, 'comparison-operator-in-deptoken',
+ obs)
+
+ res = conflicts_regex.search(line)
+ if res:
+ confs = Pkg.parse_deps(res.group(1))
+ for conf in unversioned(confs):
+ if compop_regex.search(conf):
+ printWarning(pkg, 'comparison-operator-in-deptoken',
+ conf)
+
+ if current_section == 'changelog':
+ for match in AbstractCheck.macro_regex.findall(line):
+ res = re.match('%+', match)
+ if len(res.group(0)) % 2:
+ printWarning(pkg, 'macro-in-%changelog', match)
+ else:
+ if not depscript_override:
+ depscript_override = \
+ depscript_override_regex.search(line) is not None
+ if not depgen_disabled:
+ depgen_disabled = \
+ depgen_disable_regex.search(line) is not None
+
+ if current_section == 'files':
+
+ if not comment_or_empty_regex.search(line) and not \
+ (ifarch_regex.search(line) or if_regex.search(line) or
+ endif_regex.search(line)):
+ if defattr_regex.search(line):
+ files_has_defattr = True
+ elif not (files_has_defattr or attr_regex.search(line)):
+ printWarning(pkg, 'files-attr-not-set')
+
+ # TODO: check scriptlets for these too?
+ if package_noarch.get(current_package) or \
+ (current_package not in package_noarch and
+ package_noarch.get(None)):
+ res = libdir_regex.search(line)
+ if res:
+ pkgname = current_package
+ if pkgname is None:
+ pkgname = '(main package)'
+ printWarning(pkg, 'libdir-macro-in-noarch-package',
+ pkgname, line.rstrip())
+
+ if not indent_tabs and '\t' in line:
+ indent_tabs = pkg.current_linenum
+ if not indent_spaces and indent_spaces_regex.search(line):
+ indent_spaces = pkg.current_linenum
+
+ # Check if egrep or fgrep is used
+ if current_section not in \
+ ('package', 'changelog', 'description', 'files'):
+ greps = deprecated_grep_regex.findall(line)
+ if greps:
+ printWarning(pkg, "deprecated-grep", greps)
+
+ # If not checking spec file only, we're checking one inside a
+ # SRPM -> skip this check to avoid duplicate warnings (#167)
+ if spec_only and VALID_GROUPS and \
+ line.lower().startswith("group:"):
+ group = line[6:].strip()
+ if group not in VALID_GROUPS:
+ printWarning(pkg, 'non-standard-group', group)
+
+ # Test if there are macros in comments
+ if hashPos != -1 and \
+ (hashPos == 0 or line[hashPos-1] in (" ", "\t")):
+ for match in AbstractCheck.macro_regex.findall(
+ line[hashPos+1:]):
+ res = re.match('%+', match)
+ if len(res.group(0)) % 2:
+ printWarning(pkg, 'macro-in-comment', match)
+
+ # Last line read is not useful after this point
+ pkg.current_linenum = None
+
+ for sect in (x for x in buildroot_clean if not buildroot_clean[x]):
+ printWarning(pkg, 'no-cleaning-of-buildroot', '%' + sect)
+
+ if not buildroot:
+ printWarning(pkg, 'no-buildroot-tag')
+
+ for sec in ('prep', 'build', 'install', 'clean'):
+ if not section.get(sec):
+ printWarning(pkg, 'no-%%%s-section' % sec)
+ for sec in ('changelog',):
+ # prep, build, install, clean, check prevented by rpmbuild 4.4
+ if section.get(sec, 0) > 1:
+ printWarning(pkg, 'more-than-one-%%%s-section' % sec)
+
+ if is_lib_pkg and not mklibname:
+ printError(pkg, 'lib-package-without-%mklibname')
+
+ if depscript_override and not depgen_disabled:
+ printWarning(pkg, 'depscript-without-disabling-depgen')
+
+ if indent_spaces and indent_tabs:
+ pkg.current_linenum = max(indent_spaces, indent_tabs)
+ printWarning(pkg, 'mixed-use-of-spaces-and-tabs',
+ '(spaces: line %d, tab: line %d)' %
+ (indent_spaces, indent_tabs))
+ pkg.current_linenum = None
+
+ # process gathered info
+ for pnum, pfile in patches.items():
+ if pnum in applied_patches_ifarch:
+ printWarning(pkg, "%ifarch-applied-patch", "Patch%d:" % pnum,
+ pfile)
+ if pnum not in applied_patches:
+ printWarning(pkg, "patch-not-applied", "Patch%d:" % pnum,
+ pfile)
+
+ # Rest of the checks require a real spec file
+ if not self._spec_file:
+ return
+
+ # We'd like to parse the specfile only once using python bindings,
+ # but it seems errors from rpmlib get logged to stderr and we can't
+ # capture and print them nicely, so we do it once each way :P
+
+ out = Pkg.getstatusoutput(('env', 'LC_ALL=C', 'rpm', '-q',
+ '--qf=', '--specfile', self._spec_file))
+ parse_error = False
+ for line in out[1].splitlines():
+ # No such file or dir hack: https://bugzilla.redhat.com/487855
+ if 'No such file or directory' not in line:
+ parse_error = True
+ printError(pkg, 'specfile-error', line)
+
+ if not parse_error:
+ # grab sources and patches from parsed spec object to get
+ # them with macros expanded for URL checking
+
+ spec_obj = None
+ try:
+ ts = rpm.TransactionSet()
+ spec_obj = ts.parseSpec(self._spec_file)
+ except:
+ # errors logged above already
+ pass
+ if spec_obj:
+ try:
+ # rpm < 4.8.0
+ sources = spec_obj.sources()
+ except TypeError:
+ # rpm >= 4.8.0
+ sources = spec_obj.sources
+ for src in sources:
+ (url, num, flags) = src
+ (scheme, netloc) = urlparse(url)[0:2]
+ if flags & 1: # rpmspec.h, rpm.org ticket #123
+ srctype = "Source"
+ else:
+ srctype = "Patch"
+ tag = '%s%s' % (srctype, num)
+ if scheme and netloc:
+ info = self.check_url(pkg, tag, url)
+ if not info or not hasattr(pkg, 'files'):
+ continue
+ clen = info.get("Content-Length")
+ if clen is not None:
+ clen = int(clen)
+ cmd5 = info.get("Content-MD5")
+ if cmd5 is not None:
+ cmd5 = cmd5.lower()
+ if clen is not None or cmd5 is not None:
+ # Not using path from urlparse results to match how
+ # rpm itself parses the basename.
+ pkgfile = pkg.files()[url.split("/")[-1]]
+ if pkgfile:
+ if clen is not None and pkgfile.size != clen:
+ printWarning(pkg, 'file-size-mismatch',
+ '%s = %s, %s = %s' %
+ (pkgfile.name, pkgfile.size,
+ url, clen))
+ # pkgfile.md5 could be some other digest than
+ # MD5, treat as MD5 only if it's 32 chars long
+ if cmd5 and len(pkgfile.md5) == 32 \
+ and pkgfile.md5 != cmd5:
+ printWarning(pkg, 'file-md5-mismatch',
+ '%s = %s, %s = %s' %
+ (pkgfile.name, pkgfile.md5,
+ url, cmd5))
+ elif srctype == "Source" and tarball_regex.search(url):
+ printWarning(pkg, 'invalid-url', '%s:' % tag, url)
+
+# Create an object to enable the auto registration of the test
+check = SpecCheck()
+
+# Add information about checks
+addDetails(
+'no-spec-file',
+'''No spec file was specified in your RPM building. Please specify a valid
+SPEC file to build a valid RPM package.''',
+
+'invalid-spec-name',
+'''Your spec filename must end with '.spec'. If it's not the case, rename your
+file and rebuild your package.''',
+
+'non-utf8-spec-file',
+'''The character encoding of the spec file is not UTF-8. Convert it for
+example using iconv(1).''',
+
+'use-of-RPM_SOURCE_DIR',
+'''You use $RPM_SOURCE_DIR or %{_sourcedir} in your spec file. If you have to
+use a directory for building, use $RPM_BUILD_ROOT instead.''',
+
+'patch-not-applied',
+'''A patch is included in your package but was not applied. Refer to the patches
+documentation to see what's wrong.''',
+
+'obsolete-tag',
+'''The following tags are obsolete: Copyright and Serial. They must
+be replaced by License and Epoch respectively.''',
+
+'deprecated-grep',
+'''Direct use of grep as egrep or fgrep is deprecated in GNU grep and
+historical in POSIX, use grep -E and grep -F instead.''',
+
+'no-buildroot-tag',
+'''The BuildRoot tag isn't used in your spec. It must be used in order to
+allow building the package as non root on some systems. For some rpm versions
+(e.g. rpm.org >= 4.6) the BuildRoot tag is not necessary in specfiles and is
+ignored by rpmbuild; if your package is only going to be built with such rpm
+versions you can ignore this warning.''',
+
+'hardcoded-path-in-buildroot-tag',
+'''A path is hardcoded in your Buildroot tag. It should be replaced
+by something like %{_tmppath}/%name-root.''',
+
+'hardcoded-packager-tag',
+'''The Packager tag is hardcoded in your spec file. It should be removed, so
+as to use rebuilder's own defaults.''',
+
+'buildarch-instead-of-exclusivearch-tag',
+'''Use ExclusiveArch instead of BuildArch (or BuildArchitectures)
+to restrict build on some specific architectures.
+Only use BuildArch with noarch''',
+
+'hardcoded-prefix-tag',
+'''The Prefix tag is hardcoded in your spec file. It should be removed, so as
+to allow package relocation.''',
+
+'hardcoded-library-path',
+'''A library path is hardcoded to one of the following paths: /lib,
+/usr/lib. It should be replaced by something like /%{_lib} or %{_libdir}.''',
+
+'configure-without-libdir-spec',
+'''A configure script is run without specifying the libdir. configure
+options must be augmented with something like --libdir=%{_libdir} whenever
+the script supports it.''',
+
+'no-%prep-section',
+'''The spec file does not contain a %prep section. Even if some packages don't
+directly need it, section markers may be overridden in rpm's configuration
+to provide additional "under the hood" functionality. Add the section, even
+if empty.''',
+
+'no-%build-section',
+'''The spec file does not contain a %build section. Even if some packages
+don't directly need it, section markers may be overridden in rpm's
+configuration to provide additional "under the hood" functionality, such as
+injection of automatic -debuginfo subpackages. Add the section, even if
+empty.''',
+
+'no-%install-section',
+'''The spec file does not contain an %install section. Even if some packages
+don't directly need it, section markers may be overridden in rpm's
+configuration to provide additional "under the hood" functionality. Add the
+section, even if empty.''',
+
+'no-%clean-section',
+'''The spec file doesn't contain a %clean section to remove the files installed
+by the %install section.''',
+
+'more-than-one-%changelog-section',
+'''The spec file unnecessarily contains more than one %changelog section;
+remove the extra ones.''',
+
+'lib-package-without-%mklibname',
+'''The package name must be built using %mklibname to allow lib64 and lib32
+coexistence.''',
+
+'%ifarch-applied-patch',
+'''A patch is applied inside an %ifarch block. Patches must be applied
+on all architectures and may contain necessary configure and/or code
+patch to be effective only on a given arch.''',
+
+'prereq-use',
+'''The use of PreReq is deprecated. In the majority of cases, a plain Requires
+is enough and the right thing to do. Sometimes Requires(pre), Requires(post),
+Requires(preun) and/or Requires(postun) can also be used instead of PreReq.''',
+
+'buildprereq-use',
+'''The use of BuildPreReq is deprecated, build dependencies are always required
+before a package can be built. Use plain BuildRequires instead.''',
+
+'broken-syntax-in-scriptlet-requires',
+'''Comma separated context marked dependencies are silently broken in some
+versions of rpm. One way to work around it is to split them into several ones,
+eg. replace "Requires(post,preun): foo" with "Requires(post): foo" and
+"Requires(preun): foo".''',
+
+'setup-not-in-prep',
+'''The %setup macro should only be used within the %prep section because it may
+not expand to anything outside of it and can break the build in unpredictable
+ways.''',
+
+'setup-not-quiet',
+'''Use the -q option to the %setup macro to avoid useless build output from
+unpacking the sources.''',
+
+'no-cleaning-of-buildroot',
+'''You should clean $RPM_BUILD_ROOT in the %clean section and in the beginning
+of the %install section. Use "rm -rf $RPM_BUILD_ROOT". Some rpm configurations
+do this automatically; if your package is only going to be built in such
+configurations, you can ignore this warning for the section(s) where your rpm
+takes care of it.''',
+
+'rpm-buildroot-usage',
+'''$RPM_BUILD_ROOT should not be touched during %build or %prep stage, as it
+may break short circuit builds.''',
+
+'make-check-outside-check-section',
+'''Make check or other automated regression test should be run in %check, as
+they can be disabled with a rpm macro for short circuiting purposes.''',
+
+'macro-in-%changelog',
+'''Macros are expanded in %changelog too, which can in unfortunate cases lead
+to the package not building at all, or other subtle unexpected conditions that
+affect the build. Even when that doesn\'t happen, the expansion results in
+possibly "rewriting history" on subsequent package revisions and generally
+odd entries eg. in source rpms, which is rarely wanted. Avoid use of macros
+in %changelog altogether, or use two '%'s to escape them, like '%%foo'.''',
+
+'depscript-without-disabling-depgen',
+'''In some common rpm configurations/versions, defining __find_provides and/or
+__find_requires has no effect if rpm's internal dependency generator has not
+been disabled for the build. %define _use_internal_dependency_generator to 0
+to disable it in the specfile, or don't define __find_provides/requires.''',
+
+'mixed-use-of-spaces-and-tabs',
+'''The specfile mixes use of spaces and tabs for indentation, which is a
+cosmetic annoyance. Use either spaces or tabs for indentation, not both.''',
+
+'unversioned-explicit-provides',
+'''The specfile contains an unversioned Provides: token, which will match all
+older, equal, and newer versions of the provided thing. This may cause
+update problems and will make versioned dependencies, obsoletions and conflicts
+on the provided thing useless -- make the Provides versioned if possible.''',
+
+'unversioned-explicit-obsoletes',
+'''The specfile contains an unversioned Obsoletes: token, which will match all
+older, equal and newer versions of the obsoleted thing. This may cause update
+problems, restrict future package/provides naming, and may match something it
+was originally not inteded to match -- make the Obsoletes versioned if
+possible.''',
+
+'libdir-macro-in-noarch-package',
+'''The %{_libdir} or %{_lib} macro was found in a noarch package in a section
+that gets included in binary packages. This is most likely an error because
+these macros are expanded on the build host and their values vary between
+architectures, probably resulting in a package that does not work properly
+on all architectures at runtime. Investigate whether the package is really
+architecture independent or if some other dir/macro should be instead.''',
+
+'non-break-space',
+'''The spec file contains a non-break space, which looks like a regular space
+in some editors but can lead to obscure errors. It should be replaced by a
+regular space.''',
+
+'files-attr-not-set',
+'''A file or a directory entry in a %files section does not have attributes
+set which may result in unexpected file permissions and thus security issues
+in the resulting binary package depending on the build environment and rpmbuild
+version (typically < 4.4). Add default attributes using %defattr before it in
+the %files section, or use per entry %attr's.''',
+
+'non-standard-group',
+'''The value of the Group tag in the package is not valid. Valid groups are:
+"%s".''' % '", "'.join(VALID_GROUPS),
+
+'specfile-error',
+'''This error occurred when rpmlint used rpm to query the specfile. The error
+is output by rpm and the message should contain more information.''',
+
+'comparison-operator-in-deptoken',
+'''This dependency token contains a comparison operator (<, > or =). This is
+usually not intended and may be caused by missing whitespace between the token's
+name, the comparison operator and the version string.''',
+
+'macro-in-comment',
+'''There is a unescaped macro after a shell style comment in the specfile.
+Macros are expanded everywhere, so check if it can cause a problem in this
+case and escape the macro with another leading % if appropriate.''',
+
+'file-size-mismatch',
+'''The size of the file in the package does not match the size indicated by
+peeking at its URL. Verify that the file in the package has the intended
+contents.''',
+
+'file-md5-mismatch',
+'''The MD5 hash of the file in the package does not match the MD5 hash
+indicated by peeking at its URL. Verify that the file in the package has the
+intended contents.''',
+)
+
+# SpecCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/TagsCheck.py b/TagsCheck.py
new file mode 100644
index 0000000..1194660
--- /dev/null
+++ b/TagsCheck.py
@@ -0,0 +1,1138 @@
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : TagsCheck.py
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Tue Sep 28 00:03:24 1999
+# Version : $Id: TagsCheck.py 1892 2011-11-23 20:21:05Z scop $
+# Purpose : Check a package to see if some rpm tags are present
+#############################################################################
+
+import calendar
+import os
+import re
+import time
+try:
+ from urlparse import urlparse
+except ImportError: # Python 3
+ from urllib.parse import urlparse
+
+import rpm
+
+from Filter import addDetails, printError, printInfo, printWarning
+import AbstractCheck
+import Config
+import FilesCheck
+import Pkg
+
+_use_enchant = Config.getOption("UseEnchant", None)
+if _use_enchant or _use_enchant is None:
+ try:
+ import enchant
+ import enchant.checker
+ except ImportError:
+ enchant = None
+else:
+ enchant = None
+del _use_enchant
+
+DEFAULT_VALID_LICENSES = (
+ # OSI approved licenses, http://www.opensource.org/licenses/ (unversioned,
+ # trailing "license" dropped based on fuzzy logic, and in well-known cases,
+ # the abbreviation used instead of the full name, but list kept sorted by
+ # the full name). Updated 2010-02-01.
+ 'Academic Free License',
+ 'Adaptive Public License',
+ 'AGPLv3', # Affero GNU Public License
+ 'AGPLv3+', # Affero GNU Public License
+ 'Apache License',
+ 'Apache Software License',
+ 'Apple Public Source License',
+ 'Artistic',
+ 'Attribution Assurance License',
+ 'BSD',
+ 'Boost Software License',
+ 'Computer Associates Trusted Open Source License',
+ 'CDDL', # Common Development and Distribution License
+ 'Common Public Attribution License',
+ 'CUA Office Public License',
+ 'EU DataGrid Software License',
+ 'Eclipse Public License',
+ 'Educational Community License',
+ 'Eiffel Forum License',
+ 'Entessa Public License',
+ 'European Union Public License',
+ 'Fair License',
+ 'Frameworx License',
+ 'GPLv1',
+ 'GPLv1+',
+ 'GPLv2',
+ 'GPLv2+',
+ 'GPLv3',
+ 'GPLv3+',
+ 'LGPLv2',
+ 'LGPLv2+',
+ 'LGPLv3',
+ 'LGPLv3+',
+ 'Historical Permission Notice and Disclaimer',
+ 'IBM Public License',
+ 'IPA Font License',
+ 'ISC License',
+ 'Lucent Public License',
+ 'Microsoft Public License',
+ 'Microsoft Reciprocal License',
+ 'MirOS License',
+ 'MIT',
+ 'Motosoto License',
+ 'MPL', # Mozilla Public License
+ 'Multics License',
+ 'NASA Open Source Agreement',
+ 'Naumen Public License',
+ 'Nethack General Public License',
+ 'Nokia Open Source License',
+ 'Non-profit Open Software License',
+ 'NTP License',
+ 'OCLC Research Public License',
+ 'OFL', # Open Font License
+ 'Open Group Test Suite License',
+ 'Open Software License',
+ 'PHP License',
+ 'Python license', # CNRI Python License
+ 'Python Software Foundation License',
+ 'QPL', # Qt Public License
+ 'RealNetworks Public Source License',
+ 'Reciprocal Public License',
+ 'Ricoh Source Code Public License',
+ 'Simple Public License',
+ 'Sleepycat License',
+ 'Sun Public License',
+ 'Sybase Open Watcom Public License',
+ 'University of Illinois/NCSA Open Source License',
+ 'Vovida Software License',
+ 'W3C License',
+ 'wxWindows Library License',
+ 'X.Net License',
+ 'Zope Public License',
+ 'zlib/libpng License',
+ # Creative commons licenses, http://creativecommons.org/licenses/:
+ 'Creative Commons Attribution',
+ 'Creative Commons Attribution-NoDerivs',
+ 'Creative Commons Attribution-NonCommercial-NoDerivs',
+ 'Creative Commons Attribution-NonCommercial',
+ 'Creative Commons Attribution-NonCommercial-ShareAlike',
+ 'Creative Commons Attribution-ShareAlike',
+ # Others:
+ 'Design Public License', # ???
+ 'GFDL', # GNU Free Documentation License
+ 'LaTeX Project Public License',
+ 'OpenContent License',
+ 'Open Publication License',
+ 'Public Domain',
+ 'Ruby License',
+ 'SIL Open Font License',
+ # Non open source licences:
+ 'Charityware',
+ 'Commercial',
+ 'Distributable',
+ 'Freeware',
+ 'Non-distributable',
+ 'Proprietary',
+ 'Shareware',
+ )
+
+BAD_WORDS = {
+ 'alot': 'a lot',
+ 'accesnt': 'accent',
+ 'accelleration': 'acceleration',
+ 'accessable': 'accessible',
+ 'accomodate': 'accommodate',
+ 'acess': 'access',
+ 'acording': 'according',
+ 'additionaly': 'additionally',
+ 'adress': 'address',
+ 'adresses': 'addresses',
+ 'adviced': 'advised',
+ 'albumns': 'albums',
+ 'alegorical': 'allegorical',
+ 'algorith': 'algorithm',
+ 'allpication': 'application',
+ 'altough': 'although',
+ 'alows': 'allows',
+ 'amoung': 'among',
+ 'amout': 'amount',
+ 'analysator': 'analyzer',
+ 'ang': 'and',
+ 'appropiate': 'appropriate',
+ 'arraival': 'arrival',
+ 'artifical': 'artificial',
+ 'artillary': 'artillery',
+ 'attemps': 'attempts',
+ 'automatize': 'automate',
+ 'automatized': 'automated',
+ 'automatizes': 'automates',
+ 'auxilliary': 'auxiliary',
+ 'availavility': 'availability',
+ 'availble': 'available',
+ 'avaliable': 'available',
+ 'availiable': 'available',
+ 'backgroud': 'background',
+ 'baloons': 'balloons',
+ 'becomming': 'becoming',
+ 'becuase': 'because',
+ 'cariage': 'carriage',
+ 'challanges': 'challenges',
+ 'changable': 'changeable',
+ 'charachters': 'characters',
+ 'charcter': 'character',
+ 'choosen': 'chosen',
+ 'colorfull': 'colorful',
+ 'comand': 'command',
+ 'commerical': 'commercial',
+ 'comminucation': 'communication',
+ 'commoditiy': 'commodity',
+ 'compability': 'compatibility',
+ 'compatability': 'compatibility',
+ 'compatable': 'compatible',
+ 'compatibiliy': 'compatibility',
+ 'compatibilty': 'compatibility',
+ 'compleatly': 'completely',
+ 'complient': 'compliant',
+ 'compres': 'compress',
+ 'containes': 'contains',
+ 'containts': 'contains',
+ 'contence': 'contents',
+ 'continous': 'continuous',
+ 'contraints': 'constraints',
+ 'convertor': 'converter',
+ 'convinient': 'convenient',
+ 'cryptocraphic': 'cryptographic',
+ 'deamon': 'daemon',
+ 'debians': 'Debian\'s',
+ 'decompres': 'decompress',
+ 'definate': 'definite',
+ 'definately': 'definitely',
+ 'dependancies': 'dependencies',
+ 'dependancy': 'dependency',
+ 'dependant': 'dependent',
+ 'developement': 'development',
+ 'developped': 'developed',
+ 'deveolpment': 'development',
+ 'devided': 'divided',
+ 'dictionnary': 'dictionary',
+ 'diplay': 'display',
+ 'disapeared': 'disappeared',
+ 'dissapears': 'disappears',
+ 'documentaion': 'documentation',
+ 'docuentation': 'documentation',
+ 'documantation': 'documentation',
+ 'dont': 'don\'t',
+ 'easilly': 'easily',
+ 'ecspecially': 'especially',
+ 'edditable': 'editable',
+ 'editting': 'editing',
+ 'eletronic': 'electronic',
+ 'enchanced': 'enhanced',
+ 'encorporating': 'incorporating',
+ 'enlightnment': 'enlightenment',
+ 'enterily': 'entirely',
+ 'enviroiment': 'environment',
+ 'environement': 'environment',
+ 'excellant': 'excellent',
+ 'exlcude': 'exclude',
+ 'exprimental': 'experimental',
+ 'extention': 'extension',
+ 'failuer': 'failure',
+ 'familar': 'familiar',
+ 'fatser': 'faster',
+ 'fetaures': 'features',
+ 'forse': 'force',
+ 'fortan': 'fortran',
+ 'framwork': 'framework',
+ 'fuction': 'function',
+ 'fuctions': 'functions',
+ 'functionnality': 'functionality',
+ 'functonality': 'functionality',
+ 'functionaly': 'functionally',
+ 'futhermore': 'furthermore',
+ 'generiously': 'generously',
+ 'grahical': 'graphical',
+ 'grahpical': 'graphical',
+ 'grapic': 'graphic',
+ 'guage': 'gauge',
+ 'halfs': 'halves',
+ 'heirarchically': 'hierarchically',
+ 'helpfull': 'helpful',
+ 'hierachy': 'hierarchy',
+ 'hierarchie': 'hierarchy',
+ 'howver': 'however',
+ 'implemantation': 'implementation',
+ 'incomming': 'incoming',
+ 'incompatabilities': 'incompatibilities',
+ 'indended': 'intended',
+ 'indendation': 'indentation',
+ 'independant': 'independent',
+ 'informatiom': 'information',
+ 'initalize': 'initialize',
+ 'inofficial': 'unofficial',
+ 'integreated': 'integrated',
+ 'integrety': 'integrity',
+ 'integrey': 'integrity',
+ 'intendet': 'intended',
+ 'interchangable': 'interchangeable',
+ 'intermittant': 'intermittent',
+ 'jave': 'java',
+ 'langage': 'language',
+ 'langauage': 'language',
+ 'langugage': 'language',
+ 'lauch': 'launch',
+ 'lesstiff': 'lesstif',
+ 'libaries': 'libraries',
+ 'licenceing': 'licencing',
+ 'loggin': 'login',
+ 'logile': 'logfile',
+ 'loggging': 'logging',
+ 'mandrivalinux': 'Mandriva Linux',
+ 'maintainance': 'maintenance',
+ 'maintainence': 'maintenance',
+ 'makeing': 'making',
+ 'managable': 'manageable',
+ 'manoeuvering': 'maneuvering',
+ 'ment': 'meant',
+ 'modulues': 'modules',
+ 'monochromo': 'monochrome',
+ 'multidimensionnal': 'multidimensional',
+ 'navagating': 'navigating',
+ 'nead': 'need',
+ 'neccesary': 'necessary',
+ 'neccessary': 'necessary',
+ 'necesary': 'necessary',
+ 'nescessary': 'necessary',
+ 'noticable': 'noticeable',
+ 'optionnal': 'optional',
+ 'orientied': 'oriented',
+ 'pacakge': 'package',
+ 'pachage': 'package',
+ 'packacge': 'package',
+ 'packege': 'package',
+ 'packge': 'package',
+ 'pakage': 'package',
+ 'particularily': 'particularly',
+ 'persistant': 'persistent',
+ 'plattform': 'platform',
+ 'ploting': 'plotting',
+ 'posible': 'possible',
+ 'powerfull': 'powerful',
+ 'prefered': 'preferred',
+ 'prefferably': 'preferably',
+ 'prepaired': 'prepared',
+ 'princliple': 'principle',
+ 'priorty': 'priority',
+ 'proccesors': 'processors',
+ 'proces': 'process',
+ 'processsing': 'processing',
+ 'processessing': 'processing',
+ 'progams': 'programs',
+ 'programers': 'programmers',
+ 'programm': 'program',
+ 'programms': 'programs',
+ 'promps': 'prompts',
+ 'pronnounced': 'pronounced',
+ 'prononciation': 'pronunciation',
+ 'pronouce': 'pronounce',
+ 'protcol': 'protocol',
+ 'protocoll': 'protocol',
+ 'recieve': 'receive',
+ 'recieved': 'received',
+ 'redircet': 'redirect',
+ 'regulamentations': 'regulations',
+ 'remoote': 'remote',
+ 'repectively': 'respectively',
+ 'replacments': 'replacements',
+ 'requiere': 'require',
+ 'runnning': 'running',
+ 'safly': 'safely',
+ 'savable': 'saveable',
+ 'searchs': 'searches',
+ 'separatly': 'separately',
+ 'seperate': 'separate',
+ 'seperately': 'separately',
+ 'seperatly': 'separately',
+ 'serveral': 'several',
+ 'setts': 'sets',
+ 'similiar': 'similar',
+ 'simliar': 'similar',
+ 'speach': 'speech',
+ 'standart': 'standard',
+ 'staically': 'statically',
+ 'staticly': 'statically',
+ 'succesful': 'successful',
+ 'succesfully': 'successfully',
+ 'suplied': 'supplied',
+ 'suport': 'support',
+ 'suppport': 'support',
+ 'supportin': 'supporting',
+ 'synchonized': 'synchronized',
+ 'syncronize': 'synchronize',
+ 'syncronizing': 'synchronizing',
+ 'syncronus': 'synchronous',
+ 'syste': 'system',
+ 'sythesis': 'synthesis',
+ 'taht': 'that',
+ 'throught': 'through',
+ 'useable': 'usable',
+ 'usefull': 'useful',
+ 'usera': 'users',
+ 'usetnet': 'Usenet',
+ 'utilites': 'utilities',
+ 'utillities': 'utilities',
+ 'utilties': 'utilities',
+ 'utiltity': 'utility',
+ 'utitlty': 'utility',
+ 'variantions': 'variations',
+ 'varient': 'variant',
+ 'verson': 'version',
+ 'vicefersa': 'vice-versa',
+ 'yur': 'your',
+ 'wheter': 'whether',
+ 'wierd': 'weird',
+ 'xwindows': 'X'
+ }
+
+DEFAULT_INVALID_REQUIRES = ('^is$', '^not$', '^owned$', '^by$', '^any$', '^package$', '^libsafe\.so\.')
+
+VALID_GROUPS = Config.getOption('ValidGroups', None)
+if VALID_GROUPS is None: # get defaults from rpm package only if it's not set
+ VALID_GROUPS = Pkg.get_default_valid_rpmgroups()
+VALID_LICENSES = Config.getOption('ValidLicenses', DEFAULT_VALID_LICENSES)
+INVALID_REQUIRES = map(re.compile, Config.getOption('InvalidRequires', DEFAULT_INVALID_REQUIRES))
+packager_regex = re.compile(Config.getOption('Packager'))
+changelog_version_regex = re.compile('[^>]([^ >]+)\s*$')
+changelog_text_version_regex = re.compile('^\s*-\s*((\d+:)?[\w\.]+-[\w\.]+)')
+release_ext = Config.getOption('ReleaseExtension')
+extension_regex = release_ext and re.compile(release_ext)
+use_version_in_changelog = Config.getOption('UseVersionInChangelog', True)
+devel_number_regex = re.compile('(.*?)([0-9.]+)(_[0-9.]+)?-devel')
+lib_devel_number_regex = re.compile('^lib(.*?)([0-9.]+)(_[0-9.]+)?-devel')
+invalid_url_regex = re.compile(Config.getOption('InvalidURL'), re.IGNORECASE)
+lib_package_regex = re.compile('(?:^(?:compat-)?lib.*?(\.so.*)?|libs?[\d-]*)$', re.IGNORECASE)
+leading_space_regex = re.compile('^\s+')
+license_regex = re.compile('\(([^)]+)\)|\s(?:and|or)\s')
+invalid_version_regex = re.compile('([0-9](?:rc|alpha|beta|pre).*)', re.IGNORECASE)
+# () are here for grouping purpose in the regexp
+forbidden_words_regex = re.compile('(' + Config.getOption('ForbiddenWords') + ')', re.IGNORECASE)
+valid_buildhost_regex = re.compile(Config.getOption('ValidBuildHost'))
+use_epoch = Config.getOption('UseEpoch', False)
+use_utf8 = Config.getOption('UseUTF8', Config.USEUTF8_DEFAULT)
+max_line_len = Config.getOption('MaxLineLength', 79)
+tag_regex = re.compile('^((?:Auto(?:Req|Prov|ReqProv)|Build(?:Arch(?:itectures)?|Root)|(?:Build)?Conflicts|(?:Build)?(?:Pre)?Requires|Copyright|(?:CVS|SVN)Id|Dist(?:ribution|Tag|URL)|DocDir|(?:Build)?Enhances|Epoch|Exclu(?:de|sive)(?:Arch|OS)|Group|Icon|License|Name|No(?:Patch|Source)|Obsoletes|Packager|Patch\d*|Prefix(?:es)?|Provides|(?:Build)?Recommends|Release|RHNPlatform|Serial|Source\d*|(?:Build)?Suggests|Summary|(?:Build)?Supplements|(?:Bug)?URL|Vendor|Version)(?:\([^)]+\))?:)\s*\S', re.IGNORECASE)
+punct = '.,:;!?'
+sentence_break_regex = re.compile(r'(^|[.:;!?])\s*$')
+so_dep_regex = re.compile(r'\.so(\.[0-9a-zA-z]+)*(\([^)]*\))*$')
+# we assume that no rpm packages existed before rpm itself existed...
+oldest_changelog_timestamp = calendar.timegm(time.strptime("1995-01-01", "%Y-%m-%d"))
+
+private_so_paths = set()
+for path in ('%perl_archlib', '%perl_vendorarch', '%perl_sitearch',
+ '%python_sitearch', '%ruby_sitearch', '%php_extdir'):
+ epath = rpm.expandMacro(path)
+ if epath != path:
+ private_so_paths.add(epath)
+ private_so_paths.add(re.sub(r'/lib64(?=/|$)', '/lib', epath))
+ private_so_paths.add(re.sub(r'/lib(?=/|$)', '/lib64', epath))
+
+_enchant_checkers = {}
+def spell_check(pkg, str, fmt, lang, ignored):
+
+ dict_found = True
+ warned = set()
+ if enchant:
+ if lang == 'C':
+ lang = 'en_US'
+
+ checker = _enchant_checkers.get(lang)
+ if not checker and lang not in _enchant_checkers:
+ try:
+ checker = enchant.checker.SpellChecker(
+ lang, filters = [ enchant.tokenize.EmailFilter,
+ enchant.tokenize.URLFilter,
+ enchant.tokenize.WikiWordFilter ])
+ except enchant.DictNotFoundError:
+ printInfo(pkg, 'enchant-dictionary-not-found', lang)
+ pass
+ _enchant_checkers[lang] = checker
+
+ if checker:
+ # squeeze whitespace to ease leading context check
+ checker.set_text(re.sub(r'\s+', ' ', str))
+ uppername = pkg.name.upper()
+ upperparts = uppername.split('-')
+ if lang.startswith('en'):
+ ups = [x + "'S" for x in upperparts]
+ upperparts.extend(ups)
+ for err in checker:
+
+ # Skip already warned and ignored words
+ if err.word in warned or err.word in ignored:
+ continue
+
+ # Skip all capitalized words that do not start a sentence
+ if err.word[0].isupper() and not \
+ sentence_break_regex.search(checker.leading_context(3)):
+ continue
+
+ upperword = err.word.upper()
+
+ # Skip all uppercase words
+ if err.word == upperword:
+ continue
+
+ # Skip errors containing package name or equal to a
+ # "component" of it, case insensitively
+ if uppername in upperword or upperword in upperparts:
+ continue
+
+ # Work around enchant's digit tokenizing behavior:
+ # http://github.com/rfk/pyenchant/issues/issue/3
+ if checker.leading_context(1).isdigit() or \
+ checker.trailing_context(1).isdigit():
+ continue
+
+ # Warn and suggest
+ sug = ', '.join(checker.suggest()[:3])
+ if sug:
+ sug = '-> %s' % sug
+ printWarning(pkg, 'spelling-error', fmt % lang, err.word, sug)
+ warned.add(err.word)
+
+ else:
+ dict_found = False
+
+ if not enchant or not dict_found:
+ for seq in str.split():
+ for word in re.split('[^a-z]+', seq.lower()):
+ if len(word) == 0:
+ continue
+ correct = BAD_WORDS.get(word)
+ if not correct:
+ continue
+ if word[0] == '\'':
+ word = word[1:]
+ if word[-1] == '\'':
+ word = word[:-1]
+ if word in warned or word in ignored:
+ continue
+ printWarning(pkg, 'spelling-error', fmt % lang, word, '->',
+ correct)
+ warned.add(word)
+
+
+class TagsCheck(AbstractCheck.AbstractCheck):
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, 'TagsCheck')
+
+ def _unexpanded_macros(self, pkg, tagname, value, is_url=False):
+ if not value:
+ return
+ for match in AbstractCheck.macro_regex.findall(str(value)):
+ # Do not warn about %XX URL escapes
+ if is_url and re.match('^%[0-9A-F][0-9A-F]$', match, re.I):
+ continue
+ printWarning(pkg, 'unexpanded-macro', tagname, match)
+
+ def check(self, pkg):
+
+ packager = pkg[rpm.RPMTAG_PACKAGER]
+ self._unexpanded_macros(pkg, 'Packager', packager)
+ if not packager:
+ printError(pkg, 'no-packager-tag')
+ elif Config.getOption('Packager') and \
+ not packager_regex.search(packager):
+ printWarning(pkg, 'invalid-packager', packager)
+
+ version = pkg[rpm.RPMTAG_VERSION]
+ self._unexpanded_macros(pkg, 'Version', version)
+ if not version:
+ printError(pkg, 'no-version-tag')
+ else:
+ res = invalid_version_regex.search(version)
+ if res:
+ printError(pkg, 'invalid-version', version)
+
+ release = pkg[rpm.RPMTAG_RELEASE]
+ self._unexpanded_macros(pkg, 'Release', release)
+ if not release:
+ printError(pkg, 'no-release-tag')
+ elif release_ext and not extension_regex.search(release):
+ printWarning(pkg, 'not-standard-release-extension', release)
+
+ epoch = pkg[rpm.RPMTAG_EPOCH]
+ if epoch is None:
+ if use_epoch:
+ printError(pkg, 'no-epoch-tag')
+ else:
+ if epoch > 99:
+ printWarning(pkg, 'unreasonable-epoch', epoch)
+ epoch = str(epoch)
+
+ if use_epoch:
+ for o in (x for x in pkg.obsoletes() if x[1] and x[2][0] is None):
+ printWarning(pkg, 'no-epoch-in-obsoletes',
+ apply(Pkg.formatRequire, o))
+ for c in (x for x in pkg.conflicts() if x[1] and x[2][0] is None):
+ printWarning(pkg, 'no-epoch-in-conflicts',
+ apply(Pkg.formatRequire, c))
+ for p in (x for x in pkg.provides() if x[1] and x[2][0] is None):
+ printWarning(pkg, 'no-epoch-in-provides',
+ apply(Pkg.formatRequire, p))
+
+ name = pkg.name
+ deps = pkg.requires() + pkg.prereq()
+ devel_depend = False
+ is_devel = FilesCheck.devel_regex.search(name)
+ is_source = pkg.isSource()
+ for d in deps:
+ value = apply(Pkg.formatRequire, d)
+ if use_epoch and d[1] and d[2][0] is None \
+ and d[0][0:7] != 'rpmlib(':
+ printWarning(pkg, 'no-epoch-in-dependency', value)
+ for r in INVALID_REQUIRES:
+ if r.search(d[0]):
+ printError(pkg, 'invalid-dependency', d[0])
+
+ if d[0].startswith('/usr/local/'):
+ printError(pkg, 'invalid-dependency', d[0])
+
+ if not devel_depend and not is_devel and not is_source and \
+ FilesCheck.devel_regex.search(d[0]):
+ printError(pkg, 'devel-dependency', d[0])
+ devel_depend = True
+ if is_source and lib_devel_number_regex.search(d[0]):
+ printError(pkg, 'invalid-build-requires', d[0])
+ if not is_source and not is_devel:
+ res = lib_package_regex.search(d[0])
+ if res and not res.group(1) and not d[1]:
+ printError(pkg, 'explicit-lib-dependency', d[0])
+ if d[1] == rpm.RPMSENSE_EQUAL and d[2][2] is not None:
+ printWarning(pkg, 'requires-on-release', value)
+ self._unexpanded_macros(pkg, 'dependency %s' % (value,), value)
+
+ self._unexpanded_macros(pkg, 'Name', name)
+ if not name:
+ printError(pkg, 'no-name-tag')
+ else:
+ if is_devel and not is_source:
+ base = is_devel.group(1)
+ dep = None
+ has_so = False
+ for fname in pkg.files():
+ if fname.endswith('.so'):
+ has_so = True
+ break
+ if has_so:
+ base_or_libs = base + '/' + base + '-libs/lib' + base
+ # try to match *%_isa as well (e.g. "(x86-64)", "(x86-32)")
+ base_or_libs_re = re.compile(
+ '^(lib)?%s(-libs)?(\(\w+-\d+\))?$' % re.escape(base))
+ for d in deps:
+ if base_or_libs_re.match(d[0]):
+ dep = d
+ break
+ if not dep:
+ printWarning(pkg, 'no-dependency-on', base_or_libs)
+ elif version:
+ exp = (epoch, version, None)
+ sexp = Pkg.versionToString(exp)
+ if not dep[1]:
+ printWarning(pkg, 'no-version-dependency-on',
+ base_or_libs, sexp)
+ elif dep[2][:2] != exp[:2]:
+ printWarning(pkg,
+ 'incoherent-version-dependency-on',
+ base_or_libs,
+ Pkg.versionToString((dep[2][0],
+ dep[2][1], None)),
+ sexp)
+ res = devel_number_regex.search(name)
+ if not res:
+ printWarning(pkg, 'no-major-in-name', name)
+ else:
+ if res.group(3):
+ prov = res.group(1) + res.group(2) + '-devel'
+ else:
+ prov = res.group(1) + '-devel'
+
+ if prov not in (x[0] for x in pkg.provides()):
+ printWarning(pkg, 'no-provides', prov)
+
+ # List of words to ignore in spell check
+ ignored_words = set()
+ for pf in pkg.files():
+ ignored_words.update(pf.split('/'))
+ ignored_words.update((x[0] for x in pkg.provides()))
+ ignored_words.update((x[0] for x in pkg.requires()))
+ ignored_words.update((x[0] for x in pkg.conflicts()))
+ ignored_words.update((x[0] for x in pkg.obsoletes()))
+
+ summary = pkg[rpm.RPMTAG_SUMMARY]
+ if not summary:
+ printError(pkg, 'no-summary-tag')
+ else:
+ if not pkg[rpm.RPMTAG_HEADERI18NTABLE]:
+ self._unexpanded_macros(pkg, 'Summary', summary)
+ else:
+ for lang in pkg[rpm.RPMTAG_HEADERI18NTABLE]:
+ self.check_summary(pkg, lang, ignored_words)
+
+ description = pkg[rpm.RPMTAG_DESCRIPTION]
+ if not description:
+ printError(pkg, 'no-description-tag')
+ else:
+ if not pkg[rpm.RPMTAG_HEADERI18NTABLE]:
+ self._unexpanded_macros(pkg, '%description', description)
+ else:
+ for lang in pkg[rpm.RPMTAG_HEADERI18NTABLE]:
+ self.check_description(pkg, lang, ignored_words)
+
+ group = pkg[rpm.RPMTAG_GROUP]
+ self._unexpanded_macros(pkg, 'Group', group)
+ if not group:
+ printError(pkg, 'no-group-tag')
+ elif VALID_GROUPS and group not in VALID_GROUPS:
+ printWarning(pkg, 'non-standard-group', group)
+
+ buildhost = pkg[rpm.RPMTAG_BUILDHOST]
+ self._unexpanded_macros(pkg, 'BuildHost', buildhost)
+ if not buildhost:
+ printError(pkg, 'no-buildhost-tag')
+ elif Config.getOption('ValidBuildHost') and \
+ not valid_buildhost_regex.search(buildhost):
+ printWarning(pkg, 'invalid-buildhost', buildhost)
+
+ changelog = pkg[rpm.RPMTAG_CHANGELOGNAME]
+ if not changelog:
+ printError(pkg, 'no-changelogname-tag')
+ else:
+ clt = pkg[rpm.RPMTAG_CHANGELOGTEXT]
+ if use_version_in_changelog:
+ ret = changelog_version_regex.search(changelog[0])
+ if not ret and clt:
+ # we also allow the version specified as the first
+ # thing on the first line of the text
+ ret = changelog_text_version_regex.search(clt[0])
+ if not ret:
+ printWarning(pkg, 'no-version-in-last-changelog')
+ elif version and release:
+ srpm = pkg[rpm.RPMTAG_SOURCERPM] or ''
+ # only check when source name correspond to name
+ if srpm[0:-8] == '%s-%s-%s' % (name, version, release):
+ expected = [version + '-' + release]
+ if epoch is not None: # regardless of use_epoch
+ expected[0] = str(epoch) + ':' + expected[0]
+ # Allow EVR in changelog without release extension,
+ # the extension is often a macro or otherwise dynamic.
+ if release_ext:
+ expected.append(
+ extension_regex.sub('', expected[0]))
+ if ret.group(1) not in expected:
+ if len(expected) == 1:
+ expected = expected[0]
+ printWarning(pkg, 'incoherent-version-in-changelog',
+ ret.group(1), expected)
+
+ if clt:
+ changelog = changelog + clt
+ if use_utf8 and not Pkg.is_utf8_str(' '.join(changelog)):
+ printError(pkg, 'tag-not-utf8', '%changelog')
+
+ clt = pkg[rpm.RPMTAG_CHANGELOGTIME][0]
+ if clt:
+ clt -= clt % (24*3600) # roll back to 00:00:00, see #246
+ if clt < oldest_changelog_timestamp:
+ printWarning(pkg, 'changelog-time-overflow',
+ time.strftime("%Y-%m-%d", time.gmtime(clt)))
+ elif clt > time.time():
+ printError(pkg, 'changelog-time-in-future',
+ time.strftime("%Y-%m-%d", time.gmtime(clt)))
+
+# for provide_name in (x[0] for x in pkg.provides()):
+# if name == provide_name:
+# printWarning(pkg, 'package-provides-itself')
+# break
+
+ def split_license(license):
+ return (x.strip() for x in
+ (l for l in license_regex.split(license) if l))
+
+ rpm_license = pkg[rpm.RPMTAG_LICENSE]
+ if not rpm_license:
+ printError(pkg, 'no-license')
+ else:
+ valid_license = True
+ if rpm_license not in VALID_LICENSES:
+ for l1 in split_license(rpm_license):
+ if l1 in VALID_LICENSES:
+ continue
+ for l2 in split_license(l1):
+ if l2 not in VALID_LICENSES:
+ printWarning(pkg, 'invalid-license', l2)
+ valid_license = False
+ if not valid_license:
+ self._unexpanded_macros(pkg, 'License', rpm_license)
+
+ for tag in ('URL', 'DistURL', 'BugURL'):
+ if hasattr(rpm, 'RPMTAG_%s' % tag.upper()):
+ url = pkg[getattr(rpm, 'RPMTAG_%s' % tag.upper())]
+ self._unexpanded_macros(pkg, tag, url, is_url = True)
+ if url:
+ (scheme, netloc) = urlparse(url)[0:2]
+ if not scheme or not netloc or "." not in netloc or \
+ scheme not in ('http', 'https', 'ftp') or \
+ (Config.getOption('InvalidURL') and \
+ invalid_url_regex.search(url)):
+ printWarning(pkg, 'invalid-url', tag, url)
+ else:
+ self.check_url(pkg, tag, url)
+ elif tag == 'URL':
+ printWarning(pkg, 'no-url-tag')
+
+ obs_names = [x[0] for x in pkg.obsoletes()]
+ prov_names = [x[0] for x in pkg.provides()]
+
+ for o in (x for x in obs_names if x not in prov_names):
+ printWarning(pkg, 'obsolete-not-provided', o)
+ for o in pkg.obsoletes():
+ value = apply(Pkg.formatRequire, o)
+ self._unexpanded_macros(pkg, 'Obsoletes %s' % (value,), value)
+
+ # TODO: should take versions, <, <=, =, >=, > into account here
+ # https://bugzilla.redhat.com/460872
+ useless_provides = []
+ for p in prov_names:
+ if prov_names.count(p) != 1 and p not in useless_provides:
+ useless_provides.append(p)
+ for p in useless_provides:
+ printError(pkg, 'useless-provides', p)
+
+ for p in pkg.provides():
+ value = apply(Pkg.formatRequire, p)
+ self._unexpanded_macros(pkg, 'Provides %s' % (value,), value)
+
+ for c in pkg.conflicts():
+ value = apply(Pkg.formatRequire, c)
+ self._unexpanded_macros(pkg, 'Conflicts %s' % (value,), value)
+
+ obss = pkg.obsoletes()
+ if obss:
+ provs = pkg.provides()
+ for prov in provs:
+ for obs in obss:
+ if Pkg.rangeCompare(obs, prov):
+ printWarning(pkg, 'self-obsoletion', '%s obsoletes %s' %
+ (apply(Pkg.formatRequire, obs),
+ apply(Pkg.formatRequire, prov)))
+
+ expfmt = rpm.expandMacro("%{_build_name_fmt}")
+ if pkg.isSource():
+ # _build_name_fmt often (always?) ends up not outputting src/nosrc
+ # as arch for source packages, do it ourselves
+ expfmt = re.sub(r'(?i)%\{?ARCH\b\}?', pkg.arch, expfmt)
+ expected = pkg.header.sprintf(expfmt).split("/")[-1]
+ basename = os.path.basename(pkg.filename)
+ if basename != expected:
+ printWarning(pkg, 'non-coherent-filename', basename, expected)
+
+ for tag in ('Distribution', 'DistTag', 'ExcludeArch', 'ExcludeOS',
+ 'Vendor'):
+ if hasattr(rpm, 'RPMTAG_%s' % tag.upper()):
+ self._unexpanded_macros(
+ pkg, tag, pkg[getattr(rpm, 'RPMTAG_%s' % tag.upper())])
+
+ for path in private_so_paths:
+ for fname, pkgfile in pkg.files().items():
+ if fname.startswith(path):
+ for prov in pkgfile.provides:
+ if so_dep_regex.search(prov[0]):
+ printWarning(pkg, "private-shared-object-provides",
+ fname, apply(Pkg.formatRequire, prov))
+
+
+ def check_description(self, pkg, lang, ignored_words):
+ description = pkg.langtag(rpm.RPMTAG_DESCRIPTION, lang)
+ self._unexpanded_macros(pkg, '%%description -l %s' % lang, description)
+ utf8desc = description
+ if use_utf8:
+ utf8desc = Pkg.to_utf8(description).decode('utf-8')
+ spell_check(pkg, utf8desc, '%%description -l %s', lang, ignored_words)
+ for l in utf8desc.splitlines():
+ if len(l) > max_line_len:
+ printError(pkg, 'description-line-too-long', lang, l)
+ res = forbidden_words_regex.search(l)
+ if res and Config.getOption('ForbiddenWords'):
+ printWarning(pkg, 'description-use-invalid-word', lang,
+ res.group(1))
+ res = tag_regex.search(l)
+ if res:
+ printWarning(pkg, 'tag-in-description', lang, res.group(1))
+ if use_utf8 and not Pkg.is_utf8_str(description):
+ printError(pkg, 'tag-not-utf8', '%description', lang)
+
+ def check_summary(self, pkg, lang, ignored_words):
+ summary = pkg.langtag(rpm.RPMTAG_SUMMARY, lang)
+ self._unexpanded_macros(pkg, 'Summary(%s)' % lang, summary)
+ utf8summary = summary
+ if use_utf8:
+ utf8summary = Pkg.to_utf8(summary).decode('utf-8')
+ spell_check(pkg, utf8summary, 'Summary(%s)', lang, ignored_words)
+ if '\n' in summary:
+ printError(pkg, 'summary-on-multiple-lines', lang)
+ if summary[0] != summary[0].upper():
+ printWarning(pkg, 'summary-not-capitalized', lang, summary)
+ if summary[-1] == '.':
+ printWarning(pkg, 'summary-ended-with-dot', lang, summary)
+ if len(utf8summary) > max_line_len:
+ printError(pkg, 'summary-too-long', lang, summary)
+ if leading_space_regex.search(summary):
+ printError(pkg, 'summary-has-leading-spaces', lang, summary)
+ res = forbidden_words_regex.search(summary)
+ if res and Config.getOption('ForbiddenWords'):
+ printWarning(pkg, 'summary-use-invalid-word', lang, res.group(1))
+ if pkg.name:
+ sepchars = '[\s' + punct + ']'
+ res = re.search('(?:^|\s)(%s)(?:%s|$)' %
+ (re.escape(pkg.name), sepchars),
+ summary, re.IGNORECASE | re.UNICODE)
+ if res:
+ printWarning(pkg, 'name-repeated-in-summary', lang,
+ res.group(1))
+ if use_utf8 and not Pkg.is_utf8_str(summary):
+ printError(pkg, 'tag-not-utf8', 'Summary', lang)
+
+
+# Create an object to enable the auto registration of the test
+check = TagsCheck()
+
+# Add information about checks
+addDetails(
+'summary-too-long',
+'The "Summary:" must not exceed %d characters.' % max_line_len,
+
+'invalid-version',
+'''The version string must not contain the pre, alpha, beta or rc suffixes
+because when the final version will be out, you will have to use an Epoch tag
+to make the package upgradable. Instead put it in the release tag, prefixed
+with something you have control over.''',
+
+'spelling-error',
+'''The value of this tag appears to be misspelled. Please double-check.''',
+
+'no-packager-tag',
+'''There is no Packager tag in your package. You have to specify a packager
+using the Packager tag. Ex: Packager: John Doe <john.doe@example.com>.''',
+
+'invalid-packager',
+'''The packager email must end with an email compatible with the Packager
+option of rpmlint. Please change it and rebuild your package.''',
+
+'no-version-tag',
+'''There is no Version tag in your package. You have to specify a version using
+the Version tag.''',
+
+'no-release-tag',
+'''There is no Release tag in your package. You have to specify a release using
+the Release tag.''',
+
+'not-standard-release-extension',
+'Your release tag must match the regular expression ' + release_ext + '.',
+
+'no-name-tag',
+'''There is no Name tag in your package. You have to specify a name using the
+Name tag.''',
+
+'non-coherent-filename',
+'''The file which contains the package should be named
+<NAME>-<VERSION>-<RELEASE>.<ARCH>.rpm.''',
+
+'no-dependency-on',
+'''
+''',
+
+'incoherent-version-dependency-on',
+'''
+''',
+
+'no-version-dependency-on',
+'''
+''',
+
+'no-major-in-name',
+'''The major number of the library isn't included in the package's name.
+''',
+
+'no-provides',
+'''Your library package doesn't provide the -devel name without the major
+version included.''',
+
+'no-summary-tag',
+'''There is no Summary tag in your package. You have to describe your package
+using this tag. To insert it, just insert a tag 'Summary'.''',
+
+'summary-on-multiple-lines',
+'''Your summary must fit on one line. Please make it shorter and rebuild the
+package.''',
+
+'summary-not-capitalized',
+'''Summary doesn't begin with a capital letter.''',
+
+'summary-ended-with-dot',
+'''Summary ends with a dot.''',
+
+'summary-has-leading-spaces',
+'''Summary begins with whitespace which will waste space when displayed.''',
+
+'no-description-tag',
+'''The description of the package is empty or missing. To add it, insert a
+%description section in your spec file, add a textual description of the
+package after it, and rebuild the package.''',
+
+'description-line-too-long',
+'''Your description lines must not exceed %d characters. If a line is exceeding
+this number, cut it to fit in two lines.''' % max_line_len,
+
+'tag-in-description',
+'''Something that looks like a tag was found in the package's description.
+This may indicate a problem where the tag was not actually parsed as a tag
+but just textual description content, thus being a no-op. Verify if this is
+the case, and move the tag to a place in the specfile where %description
+won't fool the specfile parser, and rebuild the package.''',
+
+'no-group-tag',
+'''There is no Group tag in your package. You have to specify a valid group
+in your spec file using the Group tag.''',
+
+'non-standard-group',
+'''The value of the Group tag in the package is not valid. Valid groups are:
+"%s".''' % '", "'.join(VALID_GROUPS),
+
+'no-changelogname-tag',
+'''There is no %changelog tag in your spec file. To insert it, just insert a
+'%changelog' in your spec file and rebuild it.''',
+
+'no-version-in-last-changelog',
+'''The latest changelog entry doesn't contain a version. Please insert the
+version that is coherent with the version of the package and rebuild it.''',
+
+'incoherent-version-in-changelog',
+'''The latest entry in %changelog contains a version identifier that is not
+coherent with the epoch:version-release tuple of the package.''',
+
+'changelog-time-overflow',
+'''The timestamp of the latest entry in %changelog is suspiciously far away in
+the past; it is possible that it is actually so much in the future that it
+has overflowed rpm's timestamp representation.''',
+
+'changelog-time-in-future',
+'''The timestamp of the latest entry in %changelog is in the future.''',
+
+'no-license',
+'''There is no License tag in your spec file. You have to specify one license
+for your program (eg. GPL). To insert this tag, just insert a 'License' in
+your specfile.''',
+
+'invalid-license',
+'''The value of the License tag was not recognized. Known values are:
+"%s".''' % '", "'.join(VALID_LICENSES),
+
+'obsolete-not-provided',
+'''If a package is obsoleted by a compatible replacement, the obsoleted package
+should also be provided in order to not cause unnecessary dependency breakage.
+If the obsoleting package is not a compatible replacement for the old one,
+leave out the Provides.''',
+
+'invalid-dependency',
+'''An invalid dependency has been detected. It usually means that the build of
+the package was buggy.''',
+
+'no-epoch-tag',
+'''There is no Epoch tag in your package. You have to specify an epoch using the
+Epoch tag.''',
+
+'unreasonable-epoch',
+'''The value of your Epoch tag is unreasonably large (> 99).''',
+
+'no-epoch-in-obsoletes',
+'''Your package contains a versioned Obsoletes entry without an Epoch.''',
+
+'no-epoch-in-conflicts',
+'''Your package contains a versioned Conflicts entry without an Epoch.''',
+
+'no-epoch-in-provides',
+'''Your package contains a versioned Provides entry without an Epoch.''',
+
+'no-epoch-in-dependency',
+'''Your package contains a versioned dependency without an Epoch.''',
+
+'devel-dependency',
+'''Your package has a dependency on a devel package but it's not a devel
+package itself.''',
+
+'invalid-build-requires',
+'''Your source package contains a dependency not compliant with the lib64
+naming. This BuildRequires dependency will not be resolved on lib64 platforms
+(eg. amd64).''',
+
+'explicit-lib-dependency',
+'''You must let rpm find the library dependencies by itself. Do not put unneeded
+explicit Requires: tags.''',
+
+'useless-provides',
+'''This package provides 2 times the same capacity. It should only provide it
+once.''',
+
+'tag-not-utf8',
+'''The character encoding of the value of this tag is not UTF-8.''',
+
+'requires-on-release',
+'''This rpm requires a specific release of another package.''',
+
+'no-url-tag',
+'''The URL tag is missing.''',
+
+'name-repeated-in-summary',
+'''The name of the package is repeated in its summary. This is often redundant
+information and looks silly in various programs' output. Make the summary
+brief and to the point without including redundant information in it.''',
+
+'enchant-dictionary-not-found',
+'''A dictionary for the Enchant spell checking library is not available for
+the language given in the info message. Spell checking will proceed with
+rpmlint's built-in implementation for localized tags in this language.
+For better spell checking results in this language, install the appropriate
+dictionary that Enchant will use for this language, often for example
+hunspell-* or aspell-*.''',
+
+'self-obsoletion',
+'''The package obsoletes itself. This is known to cause errors in various
+tools and should thus be avoided, usually by using appropriately versioned
+Obsoletes and/or Provides and avoiding unversioned ones.''',
+
+'unexpanded-macro',
+'''This tag contains something that looks like an unexpanded macro; this is
+often the sign of a misspelling. Please check your specfile.''',
+
+'private-shared-object-provides',
+'''A shared object soname provides is provided by a file in a path from which
+other packages should not directly load shared objects from. Such shared
+objects should thus not be depended on and they should not result in provides
+in the containing package. Get rid of the provides if appropriate, for example
+by filtering it out during build. Note that in some cases this may require
+disabling rpmbuild's internal dependency generator.''',
+)
+
+# TagsCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/ZipCheck.py b/ZipCheck.py
new file mode 100644
index 0000000..ce8f2a3
--- /dev/null
+++ b/ZipCheck.py
@@ -0,0 +1,110 @@
+# -*- coding: utf-8 -*-
+#------------------------------------------------------------------------------
+# File : ZipCheck.py
+# Package : rpmlint
+# Author : Ville Skyttä
+# Created on : Thu Oct 30 00:14:45 EET 2003
+# Version : $Id: ZipCheck.py 1732 2010-02-21 11:28:42Z scop $
+# Purpose : Verify Zip/Jar file correctness
+#------------------------------------------------------------------------------
+
+import os
+import re
+import stat
+import sys
+import zipfile
+
+from Filter import addDetails, printError, printWarning
+import AbstractCheck
+import Config
+
+
+zip_regex = re.compile('\.(zip|[ewj]ar)$')
+jar_regex = re.compile('\.[ewj]ar$')
+classpath_regex = re.compile('^\s*Class-Path\s*:', re.M | re.I)
+
+want_indexed_jars = Config.getOption('UseIndexedJars', True)
+
+class ZipCheck(AbstractCheck.AbstractCheck):
+
+ def __init__(self):
+ AbstractCheck.AbstractCheck.__init__(self, "ZipCheck")
+
+ def check(self, pkg):
+ for fname, pkgfile in pkg.files().items():
+ path = pkgfile.path
+ if zip_regex.search(fname) and os.path.exists(path) and \
+ stat.S_ISREG(os.lstat(path)[stat.ST_MODE]) and \
+ zipfile.is_zipfile(path):
+ z = None
+ try:
+ z = zipfile.ZipFile(path, 'r')
+ badcrc = z.testzip()
+ if badcrc:
+ printError(pkg, 'bad-crc-in-zip', badcrc, fname)
+ compressed = False
+ for zinfo in z.infolist():
+ if zinfo.compress_type != zipfile.ZIP_STORED:
+ compressed = True
+ break
+ if not compressed:
+ printWarning(pkg, 'uncompressed-zip', fname)
+
+ # additional jar checks
+ if jar_regex.search(fname):
+ try:
+ mf = z.read('META-INF/MANIFEST.MF')
+ if classpath_regex.search(mf):
+ printWarning(pkg,
+ 'class-path-in-manifest', fname)
+ except KeyError:
+ # META-INF/* are optional:
+ # http://java.sun.com/j2se/1.4/docs/guide/jar/jar.html
+ pass
+ try:
+ zinfo = z.getinfo('META-INF/INDEX.LIST')
+ if not want_indexed_jars:
+ printWarning(pkg, 'jar-indexed', fname)
+ except KeyError:
+ if want_indexed_jars:
+ printWarning(pkg, 'jar-not-indexed', fname)
+ pass
+ except:
+ printWarning(pkg, 'unable-to-read-zip', '%s: %s' %
+ (fname, sys.exc_info()[1]))
+
+ z and z.close()
+
+
+check = ZipCheck()
+
+addDetails(
+'bad-crc-in-zip',
+'''The reported file in the zip fails the CRC check. Usually this is a
+sign of a corrupt zip file.''',
+
+'uncompressed-zip',
+'''The zip file is not compressed.''',
+
+'class-path-in-manifest',
+'''The META-INF/MANIFEST.MF file in the jar contains a hardcoded Class-Path.
+These entries do not work with older Java versions and even if they do work,
+they are inflexible and usually cause nasty surprises.''',
+
+'jar-indexed',
+'''The jar file is indexed, ie. it contains the META-INF/INDEX.LIST file.
+These files are known to cause problems with some older Java versions.''',
+
+'jar-not-indexed',
+'''The jar file is not indexed, ie. it does not contain the META-INF/INDEX.LIST
+file. Indexed jars speed up the class searching process of classloaders
+in some situations.''',
+)
+
+# ZipCheck.py ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/__isocodes__.py b/__isocodes__.py
new file mode 100644
index 0000000..b3459a1
--- /dev/null
+++ b/__isocodes__.py
@@ -0,0 +1,7965 @@
+# Generated with tools/generate-isocodes.py
+
+LANGUAGES = set((
+ "aa",
+ "aaa",
+ "aab",
+ "aac",
+ "aad",
+ "aae",
+ "aaf",
+ "aag",
+ "aah",
+ "aai",
+ "aak",
+ "aal",
+ "aam",
+ "aan",
+ "aao",
+ "aap",
+ "aaq",
+ "aas",
+ "aat",
+ "aau",
+ "aaw",
+ "aax",
+ "aaz",
+ "ab",
+ "aba",
+ "abb",
+ "abc",
+ "abd",
+ "abe",
+ "abf",
+ "abg",
+ "abh",
+ "abi",
+ "abj",
+ "abl",
+ "abm",
+ "abn",
+ "abo",
+ "abp",
+ "abq",
+ "abr",
+ "abs",
+ "abt",
+ "abu",
+ "abv",
+ "abw",
+ "abx",
+ "aby",
+ "abz",
+ "aca",
+ "acb",
+ "acd",
+ "ace",
+ "acf",
+ "ach",
+ "aci",
+ "ack",
+ "acl",
+ "acm",
+ "acn",
+ "acp",
+ "acq",
+ "acr",
+ "acs",
+ "act",
+ "acu",
+ "acv",
+ "acw",
+ "acx",
+ "acy",
+ "acz",
+ "ada",
+ "adb",
+ "add",
+ "ade",
+ "adf",
+ "adg",
+ "adh",
+ "adi",
+ "adj",
+ "adl",
+ "adn",
+ "ado",
+ "adp",
+ "adq",
+ "adr",
+ "ads",
+ "adt",
+ "adu",
+ "adw",
+ "adx",
+ "ady",
+ "adz",
+ "ae",
+ "aea",
+ "aeb",
+ "aec",
+ "aed",
+ "aee",
+ "aek",
+ "ael",
+ "aem",
+ "aen",
+ "aeq",
+ "aer",
+ "aes",
+ "aeu",
+ "aew",
+ "aey",
+ "aez",
+ "af",
+ "afb",
+ "afd",
+ "afe",
+ "afg",
+ "afh",
+ "afi",
+ "afk",
+ "afn",
+ "afo",
+ "afp",
+ "afs",
+ "aft",
+ "afu",
+ "afz",
+ "aga",
+ "agb",
+ "agc",
+ "agd",
+ "age",
+ "agf",
+ "agg",
+ "agh",
+ "agi",
+ "agj",
+ "agk",
+ "agl",
+ "agm",
+ "agn",
+ "ago",
+ "agq",
+ "agr",
+ "ags",
+ "agt",
+ "agu",
+ "agv",
+ "agw",
+ "agx",
+ "agy",
+ "agz",
+ "aha",
+ "ahb",
+ "ahg",
+ "ahh",
+ "ahi",
+ "ahk",
+ "ahl",
+ "ahm",
+ "ahn",
+ "aho",
+ "ahp",
+ "ahr",
+ "ahs",
+ "aht",
+ "aia",
+ "aib",
+ "aic",
+ "aid",
+ "aie",
+ "aif",
+ "aig",
+ "aih",
+ "aii",
+ "aij",
+ "aik",
+ "ail",
+ "aim",
+ "ain",
+ "aio",
+ "aip",
+ "aiq",
+ "air",
+ "ais",
+ "ait",
+ "aiw",
+ "aix",
+ "aiy",
+ "aja",
+ "ajg",
+ "aji",
+ "ajp",
+ "ajt",
+ "aju",
+ "ajw",
+ "ajz",
+ "ak",
+ "akb",
+ "akc",
+ "akd",
+ "ake",
+ "akf",
+ "akg",
+ "akh",
+ "aki",
+ "akj",
+ "akk",
+ "akl",
+ "akm",
+ "ako",
+ "akp",
+ "akq",
+ "akr",
+ "aks",
+ "akt",
+ "aku",
+ "akv",
+ "akw",
+ "akx",
+ "aky",
+ "akz",
+ "ala",
+ "alc",
+ "ald",
+ "ale",
+ "alf",
+ "alh",
+ "ali",
+ "alj",
+ "alk",
+ "all",
+ "alm",
+ "aln",
+ "alo",
+ "alp",
+ "alq",
+ "alr",
+ "als",
+ "alt",
+ "alu",
+ "alw",
+ "alx",
+ "aly",
+ "alz",
+ "am",
+ "ama",
+ "amb",
+ "amc",
+ "ame",
+ "amf",
+ "amg",
+ "ami",
+ "amj",
+ "amk",
+ "aml",
+ "amm",
+ "amn",
+ "amo",
+ "amp",
+ "amq",
+ "amr",
+ "ams",
+ "amt",
+ "amu",
+ "amv",
+ "amw",
+ "amx",
+ "amy",
+ "amz",
+ "an",
+ "ana",
+ "anb",
+ "anc",
+ "and",
+ "ane",
+ "anf",
+ "ang",
+ "anh",
+ "ani",
+ "anj",
+ "ank",
+ "anl",
+ "anm",
+ "ann",
+ "ano",
+ "anp",
+ "anq",
+ "anr",
+ "ans",
+ "ant",
+ "anu",
+ "anv",
+ "anw",
+ "anx",
+ "any",
+ "anz",
+ "aoa",
+ "aob",
+ "aoc",
+ "aod",
+ "aoe",
+ "aof",
+ "aog",
+ "aoh",
+ "aoi",
+ "aoj",
+ "aok",
+ "aol",
+ "aom",
+ "aon",
+ "aor",
+ "aos",
+ "aot",
+ "aox",
+ "aoz",
+ "apb",
+ "apc",
+ "apd",
+ "ape",
+ "apf",
+ "apg",
+ "aph",
+ "api",
+ "apj",
+ "apk",
+ "apl",
+ "apm",
+ "apn",
+ "apo",
+ "app",
+ "apq",
+ "apr",
+ "aps",
+ "apt",
+ "apu",
+ "apv",
+ "apw",
+ "apx",
+ "apy",
+ "apz",
+ "aqc",
+ "aqd",
+ "aqg",
+ "aqm",
+ "aqn",
+ "aqp",
+ "aqr",
+ "aqz",
+ "ar",
+ "arb",
+ "arc",
+ "ard",
+ "are",
+ "arh",
+ "ari",
+ "arj",
+ "ark",
+ "arl",
+ "arn",
+ "aro",
+ "arp",
+ "arq",
+ "arr",
+ "ars",
+ "aru",
+ "arv",
+ "arw",
+ "arx",
+ "ary",
+ "arz",
+ "as",
+ "asa",
+ "asb",
+ "asc",
+ "asd",
+ "ase",
+ "asf",
+ "asg",
+ "ash",
+ "asi",
+ "asj",
+ "ask",
+ "asl",
+ "asn",
+ "aso",
+ "asp",
+ "asq",
+ "asr",
+ "ass",
+ "ast",
+ "asu",
+ "asv",
+ "asw",
+ "asx",
+ "asy",
+ "asz",
+ "ata",
+ "atb",
+ "atc",
+ "atd",
+ "ate",
+ "atg",
+ "ati",
+ "atj",
+ "atk",
+ "atl",
+ "atm",
+ "atn",
+ "ato",
+ "atp",
+ "atq",
+ "atr",
+ "ats",
+ "att",
+ "atu",
+ "atv",
+ "atw",
+ "atx",
+ "aty",
+ "atz",
+ "aua",
+ "aub",
+ "auc",
+ "aud",
+ "aue",
+ "aug",
+ "auh",
+ "aui",
+ "auj",
+ "auk",
+ "aul",
+ "aum",
+ "aun",
+ "auo",
+ "aup",
+ "auq",
+ "aur",
+ "aut",
+ "auu",
+ "auw",
+ "aux",
+ "auy",
+ "auz",
+ "av",
+ "avb",
+ "avd",
+ "avi",
+ "avk",
+ "avl",
+ "avn",
+ "avo",
+ "avs",
+ "avt",
+ "avu",
+ "avv",
+ "awa",
+ "awb",
+ "awc",
+ "awe",
+ "awh",
+ "awi",
+ "awk",
+ "awm",
+ "awn",
+ "awo",
+ "awr",
+ "aws",
+ "awt",
+ "awu",
+ "awv",
+ "aww",
+ "awx",
+ "awy",
+ "axb",
+ "axg",
+ "axk",
+ "axm",
+ "axx",
+ "ay",
+ "aya",
+ "ayb",
+ "ayc",
+ "ayd",
+ "aye",
+ "ayg",
+ "ayh",
+ "ayi",
+ "ayk",
+ "ayl",
+ "ayn",
+ "ayo",
+ "ayp",
+ "ayq",
+ "ayr",
+ "ays",
+ "ayt",
+ "ayu",
+ "ayy",
+ "ayz",
+ "az",
+ "aza",
+ "azb",
+ "azg",
+ "azj",
+ "azm",
+ "azo",
+ "azt",
+ "azz",
+ "ba",
+ "baa",
+ "bab",
+ "bac",
+ "bae",
+ "baf",
+ "bag",
+ "bah",
+ "baj",
+ "bal",
+ "ban",
+ "bao",
+ "bap",
+ "bar",
+ "bas",
+ "bau",
+ "bav",
+ "baw",
+ "bax",
+ "bay",
+ "baz",
+ "bba",
+ "bbb",
+ "bbc",
+ "bbd",
+ "bbe",
+ "bbf",
+ "bbg",
+ "bbh",
+ "bbi",
+ "bbj",
+ "bbk",
+ "bbl",
+ "bbm",
+ "bbn",
+ "bbo",
+ "bbp",
+ "bbq",
+ "bbr",
+ "bbs",
+ "bbt",
+ "bbu",
+ "bbv",
+ "bbw",
+ "bbx",
+ "bby",
+ "bbz",
+ "bca",
+ "bcb",
+ "bcc",
+ "bcd",
+ "bce",
+ "bcf",
+ "bcg",
+ "bch",
+ "bci",
+ "bcj",
+ "bck",
+ "bcl",
+ "bcm",
+ "bcn",
+ "bco",
+ "bcp",
+ "bcq",
+ "bcr",
+ "bcs",
+ "bct",
+ "bcu",
+ "bcv",
+ "bcw",
+ "bcy",
+ "bcz",
+ "bda",
+ "bdb",
+ "bdc",
+ "bdd",
+ "bde",
+ "bdf",
+ "bdg",
+ "bdh",
+ "bdi",
+ "bdj",
+ "bdk",
+ "bdl",
+ "bdm",
+ "bdn",
+ "bdo",
+ "bdp",
+ "bdq",
+ "bdr",
+ "bds",
+ "bdt",
+ "bdu",
+ "bdv",
+ "bdw",
+ "bdx",
+ "bdy",
+ "bdz",
+ "be",
+ "bea",
+ "beb",
+ "bec",
+ "bed",
+ "bee",
+ "bef",
+ "beg",
+ "beh",
+ "bei",
+ "bej",
+ "bek",
+ "bem",
+ "beo",
+ "bep",
+ "beq",
+ "bes",
+ "bet",
+ "beu",
+ "bev",
+ "bew",
+ "bex",
+ "bey",
+ "bez",
+ "bfa",
+ "bfb",
+ "bfc",
+ "bfd",
+ "bfe",
+ "bff",
+ "bfg",
+ "bfh",
+ "bfi",
+ "bfj",
+ "bfk",
+ "bfl",
+ "bfm",
+ "bfn",
+ "bfo",
+ "bfp",
+ "bfq",
+ "bfr",
+ "bfs",
+ "bft",
+ "bfu",
+ "bfw",
+ "bfx",
+ "bfy",
+ "bfz",
+ "bg",
+ "bga",
+ "bgb",
+ "bgc",
+ "bgd",
+ "bge",
+ "bgf",
+ "bgg",
+ "bgi",
+ "bgj",
+ "bgk",
+ "bgl",
+ "bgm",
+ "bgn",
+ "bgo",
+ "bgp",
+ "bgq",
+ "bgr",
+ "bgs",
+ "bgt",
+ "bgu",
+ "bgv",
+ "bgw",
+ "bgx",
+ "bgy",
+ "bgz",
+ "bh",
+ "bha",
+ "bhb",
+ "bhc",
+ "bhd",
+ "bhe",
+ "bhf",
+ "bhg",
+ "bhh",
+ "bhi",
+ "bhj",
+ "bhl",
+ "bhm",
+ "bhn",
+ "bho",
+ "bhp",
+ "bhq",
+ "bhr",
+ "bhs",
+ "bht",
+ "bhu",
+ "bhv",
+ "bhw",
+ "bhx",
+ "bhy",
+ "bhz",
+ "bi",
+ "bia",
+ "bib",
+ "bic",
+ "bid",
+ "bie",
+ "bif",
+ "big",
+ "bij",
+ "bik",
+ "bil",
+ "bim",
+ "bin",
+ "bio",
+ "bip",
+ "biq",
+ "bir",
+ "bit",
+ "biu",
+ "biv",
+ "biw",
+ "bix",
+ "biy",
+ "biz",
+ "bja",
+ "bjb",
+ "bjc",
+ "bjd",
+ "bje",
+ "bjf",
+ "bjg",
+ "bjh",
+ "bji",
+ "bjj",
+ "bjk",
+ "bjl",
+ "bjm",
+ "bjn",
+ "bjo",
+ "bjr",
+ "bjs",
+ "bjt",
+ "bju",
+ "bjv",
+ "bjw",
+ "bjx",
+ "bjy",
+ "bjz",
+ "bka",
+ "bkc",
+ "bkd",
+ "bkf",
+ "bkg",
+ "bkh",
+ "bki",
+ "bkj",
+ "bkk",
+ "bkl",
+ "bkm",
+ "bkn",
+ "bko",
+ "bkp",
+ "bkq",
+ "bkr",
+ "bks",
+ "bkt",
+ "bku",
+ "bkv",
+ "bkw",
+ "bkx",
+ "bky",
+ "bkz",
+ "bla",
+ "blb",
+ "blc",
+ "bld",
+ "ble",
+ "blf",
+ "blg",
+ "blh",
+ "bli",
+ "blj",
+ "blk",
+ "bll",
+ "blm",
+ "bln",
+ "blo",
+ "blp",
+ "blq",
+ "blr",
+ "bls",
+ "blt",
+ "blv",
+ "blw",
+ "blx",
+ "bly",
+ "blz",
+ "bm",
+ "bma",
+ "bmb",
+ "bmc",
+ "bmd",
+ "bme",
+ "bmf",
+ "bmg",
+ "bmh",
+ "bmi",
+ "bmj",
+ "bmk",
+ "bml",
+ "bmm",
+ "bmn",
+ "bmo",
+ "bmp",
+ "bmq",
+ "bmr",
+ "bms",
+ "bmt",
+ "bmu",
+ "bmv",
+ "bmw",
+ "bmx",
+ "bmy",
+ "bmz",
+ "bn",
+ "bna",
+ "bnb",
+ "bnc",
+ "bnd",
+ "bne",
+ "bnf",
+ "bng",
+ "bni",
+ "bnj",
+ "bnk",
+ "bnl",
+ "bnm",
+ "bnn",
+ "bno",
+ "bnp",
+ "bnq",
+ "bnr",
+ "bns",
+ "bnu",
+ "bnv",
+ "bnw",
+ "bnx",
+ "bny",
+ "bnz",
+ "bo",
+ "boa",
+ "bob",
+ "boe",
+ "bof",
+ "bog",
+ "boh",
+ "boi",
+ "boj",
+ "bok",
+ "bol",
+ "bom",
+ "bon",
+ "boo",
+ "bop",
+ "boq",
+ "bor",
+ "bot",
+ "bou",
+ "bov",
+ "bow",
+ "box",
+ "boy",
+ "boz",
+ "bpa",
+ "bpb",
+ "bpd",
+ "bpg",
+ "bph",
+ "bpi",
+ "bpj",
+ "bpk",
+ "bpl",
+ "bpm",
+ "bpn",
+ "bpo",
+ "bpp",
+ "bpq",
+ "bpr",
+ "bps",
+ "bpt",
+ "bpu",
+ "bpv",
+ "bpw",
+ "bpx",
+ "bpy",
+ "bpz",
+ "bqa",
+ "bqb",
+ "bqc",
+ "bqd",
+ "bqf",
+ "bqg",
+ "bqh",
+ "bqi",
+ "bqj",
+ "bqk",
+ "bql",
+ "bqm",
+ "bqn",
+ "bqo",
+ "bqp",
+ "bqq",
+ "bqr",
+ "bqs",
+ "bqt",
+ "bqu",
+ "bqv",
+ "bqw",
+ "bqx",
+ "bqy",
+ "bqz",
+ "br",
+ "bra",
+ "brb",
+ "brc",
+ "brd",
+ "brf",
+ "brg",
+ "brh",
+ "bri",
+ "brj",
+ "brk",
+ "brl",
+ "brm",
+ "brn",
+ "bro",
+ "brp",
+ "brq",
+ "brr",
+ "brs",
+ "brt",
+ "bru",
+ "brv",
+ "brw",
+ "brx",
+ "bry",
+ "brz",
+ "bs",
+ "bsa",
+ "bsb",
+ "bsc",
+ "bse",
+ "bsf",
+ "bsg",
+ "bsh",
+ "bsi",
+ "bsj",
+ "bsk",
+ "bsl",
+ "bsm",
+ "bsn",
+ "bso",
+ "bsp",
+ "bsq",
+ "bsr",
+ "bss",
+ "bst",
+ "bsu",
+ "bsv",
+ "bsw",
+ "bsx",
+ "bsy",
+ "bta",
+ "btc",
+ "btd",
+ "bte",
+ "btf",
+ "btg",
+ "bth",
+ "bti",
+ "btj",
+ "btl",
+ "btm",
+ "btn",
+ "bto",
+ "btp",
+ "btq",
+ "btr",
+ "bts",
+ "btt",
+ "btu",
+ "btv",
+ "btw",
+ "btx",
+ "bty",
+ "btz",
+ "bua",
+ "bub",
+ "buc",
+ "bud",
+ "bue",
+ "buf",
+ "bug",
+ "buh",
+ "bui",
+ "buj",
+ "buk",
+ "bum",
+ "bun",
+ "buo",
+ "bup",
+ "buq",
+ "bus",
+ "but",
+ "buu",
+ "buv",
+ "buw",
+ "bux",
+ "buy",
+ "buz",
+ "bva",
+ "bvb",
+ "bvc",
+ "bvd",
+ "bve",
+ "bvf",
+ "bvg",
+ "bvh",
+ "bvi",
+ "bvj",
+ "bvk",
+ "bvl",
+ "bvm",
+ "bvn",
+ "bvo",
+ "bvq",
+ "bvr",
+ "bvt",
+ "bvu",
+ "bvv",
+ "bvw",
+ "bvx",
+ "bvy",
+ "bvz",
+ "bwa",
+ "bwb",
+ "bwc",
+ "bwd",
+ "bwe",
+ "bwf",
+ "bwg",
+ "bwh",
+ "bwi",
+ "bwj",
+ "bwk",
+ "bwl",
+ "bwm",
+ "bwn",
+ "bwo",
+ "bwp",
+ "bwq",
+ "bwr",
+ "bws",
+ "bwt",
+ "bwu",
+ "bww",
+ "bwx",
+ "bwy",
+ "bwz",
+ "bxa",
+ "bxb",
+ "bxc",
+ "bxd",
+ "bxe",
+ "bxf",
+ "bxg",
+ "bxh",
+ "bxi",
+ "bxj",
+ "bxk",
+ "bxl",
+ "bxm",
+ "bxn",
+ "bxo",
+ "bxp",
+ "bxq",
+ "bxr",
+ "bxs",
+ "bxu",
+ "bxv",
+ "bxw",
+ "bxx",
+ "bxz",
+ "bya",
+ "byb",
+ "byc",
+ "byd",
+ "bye",
+ "byf",
+ "byg",
+ "byh",
+ "byi",
+ "byj",
+ "byk",
+ "byl",
+ "bym",
+ "byn",
+ "byo",
+ "byp",
+ "byq",
+ "byr",
+ "bys",
+ "byt",
+ "byv",
+ "byw",
+ "byx",
+ "byy",
+ "byz",
+ "bza",
+ "bzb",
+ "bzc",
+ "bzd",
+ "bze",
+ "bzf",
+ "bzg",
+ "bzh",
+ "bzi",
+ "bzj",
+ "bzk",
+ "bzl",
+ "bzm",
+ "bzn",
+ "bzo",
+ "bzp",
+ "bzq",
+ "bzr",
+ "bzs",
+ "bzt",
+ "bzu",
+ "bzv",
+ "bzw",
+ "bzx",
+ "bzy",
+ "bzz",
+ "ca",
+ "caa",
+ "cab",
+ "cac",
+ "cad",
+ "cae",
+ "caf",
+ "cag",
+ "cah",
+ "caj",
+ "cak",
+ "cal",
+ "cam",
+ "can",
+ "cao",
+ "cap",
+ "caq",
+ "car",
+ "cas",
+ "cav",
+ "caw",
+ "cax",
+ "cay",
+ "caz",
+ "cbb",
+ "cbc",
+ "cbd",
+ "cbe",
+ "cbg",
+ "cbh",
+ "cbi",
+ "cbj",
+ "cbk",
+ "cbl",
+ "cbn",
+ "cbo",
+ "cbr",
+ "cbs",
+ "cbt",
+ "cbu",
+ "cbv",
+ "cbw",
+ "cby",
+ "cca",
+ "ccc",
+ "ccd",
+ "cce",
+ "ccg",
+ "cch",
+ "ccj",
+ "ccl",
+ "ccm",
+ "cco",
+ "ccp",
+ "ccq",
+ "ccr",
+ "cda",
+ "cde",
+ "cdf",
+ "cdg",
+ "cdh",
+ "cdi",
+ "cdj",
+ "cdm",
+ "cdn",
+ "cdo",
+ "cdr",
+ "cds",
+ "cdy",
+ "cdz",
+ "ce",
+ "cea",
+ "ceb",
+ "ceg",
+ "cen",
+ "cet",
+ "cfa",
+ "cfd",
+ "cfg",
+ "cfm",
+ "cga",
+ "cgc",
+ "cgg",
+ "cgk",
+ "ch",
+ "chb",
+ "chc",
+ "chd",
+ "chf",
+ "chg",
+ "chh",
+ "chj",
+ "chk",
+ "chl",
+ "chm",
+ "chn",
+ "cho",
+ "chp",
+ "chq",
+ "chr",
+ "cht",
+ "chw",
+ "chx",
+ "chy",
+ "chz",
+ "cia",
+ "cib",
+ "cic",
+ "cid",
+ "cie",
+ "cih",
+ "cik",
+ "cim",
+ "cin",
+ "cip",
+ "cir",
+ "ciw",
+ "ciy",
+ "cja",
+ "cje",
+ "cjh",
+ "cji",
+ "cjk",
+ "cjm",
+ "cjn",
+ "cjo",
+ "cjp",
+ "cjs",
+ "cjv",
+ "cjy",
+ "cka",
+ "ckb",
+ "ckh",
+ "ckl",
+ "cko",
+ "ckq",
+ "ckr",
+ "cks",
+ "ckt",
+ "cku",
+ "ckv",
+ "ckx",
+ "cky",
+ "ckz",
+ "cla",
+ "clc",
+ "cld",
+ "cle",
+ "clh",
+ "cli",
+ "clk",
+ "cll",
+ "clm",
+ "clo",
+ "clu",
+ "clw",
+ "cly",
+ "cma",
+ "cme",
+ "cmg",
+ "cmi",
+ "cml",
+ "cmm",
+ "cmn",
+ "cmo",
+ "cmr",
+ "cms",
+ "cmt",
+ "cna",
+ "cnb",
+ "cnc",
+ "cng",
+ "cnh",
+ "cni",
+ "cnk",
+ "cnl",
+ "cno",
+ "cns",
+ "cnt",
+ "cnu",
+ "cnw",
+ "cnx",
+ "co",
+ "coa",
+ "cob",
+ "coc",
+ "cod",
+ "coe",
+ "cof",
+ "cog",
+ "coh",
+ "coj",
+ "cok",
+ "col",
+ "com",
+ "con",
+ "coo",
+ "cop",
+ "coq",
+ "cot",
+ "cou",
+ "cov",
+ "cow",
+ "cox",
+ "coy",
+ "coz",
+ "cpa",
+ "cpb",
+ "cpc",
+ "cpg",
+ "cpi",
+ "cpn",
+ "cps",
+ "cpu",
+ "cpx",
+ "cpy",
+ "cqd",
+ "cqu",
+ "cr",
+ "cra",
+ "crb",
+ "crc",
+ "crd",
+ "crf",
+ "crg",
+ "crh",
+ "cri",
+ "crj",
+ "crk",
+ "crl",
+ "crm",
+ "crn",
+ "cro",
+ "crq",
+ "crr",
+ "crs",
+ "crt",
+ "crv",
+ "crw",
+ "crx",
+ "cry",
+ "crz",
+ "cs",
+ "csa",
+ "csb",
+ "csc",
+ "csd",
+ "cse",
+ "csf",
+ "csg",
+ "csh",
+ "csi",
+ "csk",
+ "csl",
+ "csm",
+ "csn",
+ "cso",
+ "csq",
+ "csr",
+ "css",
+ "cst",
+ "csw",
+ "csy",
+ "csz",
+ "cta",
+ "ctc",
+ "ctd",
+ "cte",
+ "ctg",
+ "ctl",
+ "ctm",
+ "ctn",
+ "cto",
+ "ctp",
+ "cts",
+ "ctt",
+ "ctu",
+ "ctz",
+ "cu",
+ "cua",
+ "cub",
+ "cuc",
+ "cug",
+ "cuh",
+ "cui",
+ "cuj",
+ "cuk",
+ "cul",
+ "cum",
+ "cuo",
+ "cup",
+ "cuq",
+ "cur",
+ "cut",
+ "cuu",
+ "cuv",
+ "cuw",
+ "cux",
+ "cv",
+ "cvg",
+ "cvn",
+ "cwa",
+ "cwb",
+ "cwd",
+ "cwe",
+ "cwg",
+ "cwt",
+ "cy",
+ "cya",
+ "cyb",
+ "cyo",
+ "czh",
+ "czk",
+ "czn",
+ "czo",
+ "czt",
+ "da",
+ "daa",
+ "dac",
+ "dad",
+ "dae",
+ "daf",
+ "dag",
+ "dah",
+ "dai",
+ "daj",
+ "dak",
+ "dal",
+ "dam",
+ "dao",
+ "dap",
+ "daq",
+ "dar",
+ "das",
+ "dau",
+ "dav",
+ "daw",
+ "dax",
+ "daz",
+ "dba",
+ "dbb",
+ "dbd",
+ "dbe",
+ "dbf",
+ "dbg",
+ "dbi",
+ "dbj",
+ "dbl",
+ "dbm",
+ "dbn",
+ "dbo",
+ "dbp",
+ "dbq",
+ "dbr",
+ "dbu",
+ "dbv",
+ "dby",
+ "dcc",
+ "dcr",
+ "ddd",
+ "dde",
+ "ddg",
+ "ddi",
+ "ddj",
+ "ddn",
+ "ddo",
+ "dds",
+ "ddw",
+ "de",
+ "dec",
+ "ded",
+ "dee",
+ "def",
+ "deg",
+ "deh",
+ "dei",
+ "dek",
+ "del",
+ "dem",
+ "den",
+ "dep",
+ "deq",
+ "der",
+ "des",
+ "dev",
+ "dez",
+ "dga",
+ "dgb",
+ "dgc",
+ "dgd",
+ "dge",
+ "dgg",
+ "dgh",
+ "dgi",
+ "dgk",
+ "dgn",
+ "dgo",
+ "dgr",
+ "dgs",
+ "dgu",
+ "dgx",
+ "dgz",
+ "dhd",
+ "dhg",
+ "dhi",
+ "dhl",
+ "dhm",
+ "dhn",
+ "dho",
+ "dhr",
+ "dhs",
+ "dhu",
+ "dhv",
+ "dhw",
+ "dia",
+ "dib",
+ "dic",
+ "did",
+ "dif",
+ "dig",
+ "dih",
+ "dii",
+ "dij",
+ "dik",
+ "dil",
+ "dim",
+ "din",
+ "dio",
+ "dip",
+ "diq",
+ "dir",
+ "dis",
+ "dit",
+ "diu",
+ "diw",
+ "dix",
+ "diy",
+ "diz",
+ "djb",
+ "djc",
+ "djd",
+ "dje",
+ "djf",
+ "dji",
+ "djj",
+ "djk",
+ "djl",
+ "djm",
+ "djn",
+ "djo",
+ "djr",
+ "dju",
+ "djw",
+ "dka",
+ "dkk",
+ "dkr",
+ "dks",
+ "dkx",
+ "dlg",
+ "dlm",
+ "dln",
+ "dma",
+ "dmb",
+ "dmc",
+ "dme",
+ "dmg",
+ "dmk",
+ "dml",
+ "dmm",
+ "dmo",
+ "dmr",
+ "dms",
+ "dmu",
+ "dmv",
+ "dmx",
+ "dmy",
+ "dna",
+ "dnd",
+ "dne",
+ "dng",
+ "dni",
+ "dnk",
+ "dnn",
+ "dnr",
+ "dnt",
+ "dnu",
+ "dnw",
+ "dny",
+ "doa",
+ "dob",
+ "doc",
+ "doe",
+ "dof",
+ "doh",
+ "doi",
+ "dok",
+ "dol",
+ "don",
+ "doo",
+ "dop",
+ "doq",
+ "dor",
+ "dos",
+ "dot",
+ "dov",
+ "dow",
+ "dox",
+ "doy",
+ "doz",
+ "dpp",
+ "drb",
+ "drc",
+ "drd",
+ "dre",
+ "drg",
+ "dri",
+ "drl",
+ "drn",
+ "dro",
+ "drq",
+ "drr",
+ "drs",
+ "drt",
+ "dru",
+ "dry",
+ "dsb",
+ "dse",
+ "dsh",
+ "dsi",
+ "dsl",
+ "dsn",
+ "dso",
+ "dsq",
+ "dta",
+ "dtb",
+ "dtd",
+ "dti",
+ "dtk",
+ "dtm",
+ "dtp",
+ "dtr",
+ "dts",
+ "dtt",
+ "dtu",
+ "dua",
+ "dub",
+ "duc",
+ "dud",
+ "due",
+ "duf",
+ "dug",
+ "duh",
+ "dui",
+ "duj",
+ "duk",
+ "dul",
+ "dum",
+ "dun",
+ "duo",
+ "dup",
+ "duq",
+ "dur",
+ "dus",
+ "duu",
+ "duv",
+ "duw",
+ "dux",
+ "duy",
+ "duz",
+ "dv",
+ "dva",
+ "dwa",
+ "dwl",
+ "dwr",
+ "dws",
+ "dww",
+ "dya",
+ "dyb",
+ "dyd",
+ "dyg",
+ "dyi",
+ "dym",
+ "dyn",
+ "dyo",
+ "dyu",
+ "dyy",
+ "dz",
+ "dza",
+ "dzd",
+ "dzg",
+ "dzl",
+ "dzn",
+ "ebg",
+ "ebk",
+ "ebo",
+ "ebr",
+ "ebu",
+ "ecr",
+ "ecs",
+ "ecy",
+ "ee",
+ "eee",
+ "efa",
+ "efe",
+ "efi",
+ "ega",
+ "egl",
+ "ego",
+ "egy",
+ "ehu",
+ "eip",
+ "eit",
+ "eiv",
+ "eja",
+ "eka",
+ "eke",
+ "ekg",
+ "eki",
+ "ekk",
+ "ekl",
+ "ekm",
+ "eko",
+ "ekp",
+ "ekr",
+ "eky",
+ "el",
+ "ele",
+ "elh",
+ "eli",
+ "elk",
+ "elm",
+ "elo",
+ "elp",
+ "elu",
+ "elx",
+ "ema",
+ "emb",
+ "eme",
+ "emg",
+ "emi",
+ "emk",
+ "emm",
+ "emn",
+ "emo",
+ "emp",
+ "ems",
+ "emu",
+ "emw",
+ "emx",
+ "emy",
+ "en",
+ "ena",
+ "enb",
+ "enc",
+ "end",
+ "enf",
+ "enh",
+ "enm",
+ "enn",
+ "eno",
+ "enq",
+ "enr",
+ "enu",
+ "env",
+ "enw",
+ "eo",
+ "eot",
+ "epi",
+ "era",
+ "erg",
+ "erh",
+ "eri",
+ "erk",
+ "ero",
+ "err",
+ "ers",
+ "ert",
+ "erw",
+ "es",
+ "ese",
+ "esh",
+ "esi",
+ "esk",
+ "esl",
+ "esm",
+ "esn",
+ "eso",
+ "esq",
+ "ess",
+ "esu",
+ "et",
+ "etb",
+ "etc",
+ "eth",
+ "etn",
+ "eto",
+ "etr",
+ "ets",
+ "ett",
+ "etu",
+ "etx",
+ "etz",
+ "eu",
+ "eve",
+ "evh",
+ "evn",
+ "ewo",
+ "ext",
+ "eya",
+ "eyo",
+ "eze",
+ "fa",
+ "faa",
+ "fab",
+ "fad",
+ "faf",
+ "fag",
+ "fah",
+ "fai",
+ "faj",
+ "fak",
+ "fal",
+ "fam",
+ "fan",
+ "fap",
+ "far",
+ "fat",
+ "fau",
+ "fax",
+ "fay",
+ "faz",
+ "fbl",
+ "fcs",
+ "fer",
+ "ff",
+ "ffi",
+ "ffm",
+ "fgr",
+ "fi",
+ "fia",
+ "fie",
+ "fil",
+ "fip",
+ "fir",
+ "fit",
+ "fiw",
+ "fj",
+ "fkv",
+ "fla",
+ "flh",
+ "fli",
+ "fll",
+ "fln",
+ "flr",
+ "fly",
+ "fmp",
+ "fmu",
+ "fng",
+ "fni",
+ "fo",
+ "fod",
+ "foi",
+ "fom",
+ "fon",
+ "for",
+ "fos",
+ "fpe",
+ "fqs",
+ "fr",
+ "frc",
+ "frd",
+ "frk",
+ "frm",
+ "fro",
+ "frp",
+ "frq",
+ "frr",
+ "frs",
+ "frt",
+ "fse",
+ "fsl",
+ "fss",
+ "fub",
+ "fuc",
+ "fud",
+ "fue",
+ "fuf",
+ "fuh",
+ "fui",
+ "fuj",
+ "fum",
+ "fun",
+ "fuq",
+ "fur",
+ "fut",
+ "fuu",
+ "fuv",
+ "fuy",
+ "fvr",
+ "fwa",
+ "fwe",
+ "fy",
+ "ga",
+ "gaa",
+ "gab",
+ "gac",
+ "gad",
+ "gae",
+ "gaf",
+ "gag",
+ "gah",
+ "gai",
+ "gaj",
+ "gak",
+ "gal",
+ "gam",
+ "gan",
+ "gao",
+ "gap",
+ "gaq",
+ "gar",
+ "gas",
+ "gat",
+ "gau",
+ "gaw",
+ "gax",
+ "gay",
+ "gaz",
+ "gba",
+ "gbb",
+ "gbc",
+ "gbd",
+ "gbe",
+ "gbf",
+ "gbg",
+ "gbh",
+ "gbi",
+ "gbj",
+ "gbk",
+ "gbl",
+ "gbm",
+ "gbn",
+ "gbo",
+ "gbp",
+ "gbq",
+ "gbr",
+ "gbs",
+ "gbu",
+ "gbv",
+ "gbx",
+ "gby",
+ "gbz",
+ "gcc",
+ "gcd",
+ "gce",
+ "gcf",
+ "gcl",
+ "gcn",
+ "gcr",
+ "gct",
+ "gd",
+ "gda",
+ "gdb",
+ "gdc",
+ "gdd",
+ "gde",
+ "gdf",
+ "gdg",
+ "gdh",
+ "gdi",
+ "gdj",
+ "gdk",
+ "gdl",
+ "gdm",
+ "gdn",
+ "gdo",
+ "gdq",
+ "gdr",
+ "gdu",
+ "gdx",
+ "gea",
+ "geb",
+ "gec",
+ "ged",
+ "geg",
+ "geh",
+ "gei",
+ "gej",
+ "gek",
+ "gel",
+ "geq",
+ "ges",
+ "gew",
+ "gex",
+ "gey",
+ "gez",
+ "gfk",
+ "gft",
+ "gga",
+ "ggb",
+ "ggd",
+ "gge",
+ "ggg",
+ "ggk",
+ "ggl",
+ "ggn",
+ "ggo",
+ "ggr",
+ "ggt",
+ "ggu",
+ "ggw",
+ "gha",
+ "ghc",
+ "ghe",
+ "ghh",
+ "ghk",
+ "ghl",
+ "ghn",
+ "gho",
+ "ghr",
+ "ghs",
+ "ght",
+ "gia",
+ "gib",
+ "gic",
+ "gid",
+ "gig",
+ "gil",
+ "gim",
+ "gin",
+ "gio",
+ "gip",
+ "giq",
+ "gir",
+ "gis",
+ "git",
+ "giw",
+ "gix",
+ "giy",
+ "giz",
+ "gji",
+ "gjk",
+ "gjn",
+ "gju",
+ "gka",
+ "gke",
+ "gkn",
+ "gkp",
+ "gl",
+ "glc",
+ "gld",
+ "glh",
+ "gli",
+ "glj",
+ "glk",
+ "glo",
+ "glr",
+ "glu",
+ "glw",
+ "gly",
+ "gma",
+ "gmb",
+ "gmd",
+ "gmh",
+ "gml",
+ "gmm",
+ "gmn",
+ "gmu",
+ "gmv",
+ "gmx",
+ "gmy",
+ "gn",
+ "gna",
+ "gnb",
+ "gnc",
+ "gnd",
+ "gne",
+ "gng",
+ "gnh",
+ "gni",
+ "gnk",
+ "gnl",
+ "gnm",
+ "gnn",
+ "gno",
+ "gnq",
+ "gnr",
+ "gnt",
+ "gnu",
+ "gnw",
+ "gnz",
+ "goa",
+ "gob",
+ "goc",
+ "god",
+ "goe",
+ "gof",
+ "gog",
+ "goh",
+ "goi",
+ "goj",
+ "gok",
+ "gol",
+ "gom",
+ "gon",
+ "goo",
+ "gop",
+ "goq",
+ "gor",
+ "gos",
+ "got",
+ "gou",
+ "gow",
+ "gox",
+ "goy",
+ "goz",
+ "gpa",
+ "gpn",
+ "gqa",
+ "gqi",
+ "gqn",
+ "gqr",
+ "gra",
+ "grb",
+ "grc",
+ "grd",
+ "grg",
+ "grh",
+ "gri",
+ "grj",
+ "grm",
+ "gro",
+ "grq",
+ "grr",
+ "grs",
+ "grt",
+ "gru",
+ "grv",
+ "grw",
+ "grx",
+ "gry",
+ "grz",
+ "gse",
+ "gsg",
+ "gsl",
+ "gsm",
+ "gsn",
+ "gso",
+ "gsp",
+ "gss",
+ "gsw",
+ "gta",
+ "gti",
+ "gu",
+ "gua",
+ "gub",
+ "guc",
+ "gud",
+ "gue",
+ "guf",
+ "gug",
+ "guh",
+ "gui",
+ "guk",
+ "gul",
+ "gum",
+ "gun",
+ "guo",
+ "gup",
+ "guq",
+ "gur",
+ "gus",
+ "gut",
+ "guu",
+ "guv",
+ "guw",
+ "gux",
+ "guz",
+ "gv",
+ "gva",
+ "gvc",
+ "gve",
+ "gvf",
+ "gvj",
+ "gvl",
+ "gvm",
+ "gvn",
+ "gvo",
+ "gvp",
+ "gvr",
+ "gvs",
+ "gvy",
+ "gwa",
+ "gwb",
+ "gwc",
+ "gwd",
+ "gwe",
+ "gwf",
+ "gwg",
+ "gwi",
+ "gwj",
+ "gwn",
+ "gwr",
+ "gwt",
+ "gwu",
+ "gww",
+ "gwx",
+ "gxx",
+ "gya",
+ "gyb",
+ "gyd",
+ "gye",
+ "gyf",
+ "gyg",
+ "gyi",
+ "gyl",
+ "gym",
+ "gyn",
+ "gyr",
+ "gyy",
+ "gza",
+ "gzi",
+ "gzn",
+ "ha",
+ "haa",
+ "hab",
+ "hac",
+ "had",
+ "hae",
+ "haf",
+ "hag",
+ "hah",
+ "hai",
+ "haj",
+ "hak",
+ "hal",
+ "ham",
+ "han",
+ "hao",
+ "hap",
+ "haq",
+ "har",
+ "has",
+ "hav",
+ "haw",
+ "hax",
+ "hay",
+ "haz",
+ "hba",
+ "hbb",
+ "hbn",
+ "hbo",
+ "hbu",
+ "hca",
+ "hch",
+ "hdn",
+ "hds",
+ "hdy",
+ "he",
+ "hea",
+ "hed",
+ "heg",
+ "heh",
+ "hei",
+ "hem",
+ "hgm",
+ "hgw",
+ "hhi",
+ "hhr",
+ "hhy",
+ "hi",
+ "hia",
+ "hib",
+ "hid",
+ "hif",
+ "hig",
+ "hih",
+ "hii",
+ "hij",
+ "hik",
+ "hil",
+ "hio",
+ "hir",
+ "hit",
+ "hiw",
+ "hix",
+ "hji",
+ "hka",
+ "hke",
+ "hkk",
+ "hks",
+ "hla",
+ "hlb",
+ "hld",
+ "hle",
+ "hlt",
+ "hlu",
+ "hma",
+ "hmb",
+ "hmc",
+ "hmd",
+ "hme",
+ "hmf",
+ "hmg",
+ "hmh",
+ "hmi",
+ "hmj",
+ "hmk",
+ "hml",
+ "hmm",
+ "hmn",
+ "hmp",
+ "hmq",
+ "hmr",
+ "hms",
+ "hmt",
+ "hmu",
+ "hmv",
+ "hmw",
+ "hmy",
+ "hmz",
+ "hna",
+ "hnd",
+ "hne",
+ "hnh",
+ "hni",
+ "hnj",
+ "hnn",
+ "hno",
+ "hns",
+ "hnu",
+ "ho",
+ "hoa",
+ "hob",
+ "hoc",
+ "hod",
+ "hoe",
+ "hoh",
+ "hoi",
+ "hoj",
+ "hol",
+ "hom",
+ "hoo",
+ "hop",
+ "hor",
+ "hos",
+ "hot",
+ "hov",
+ "how",
+ "hoy",
+ "hoz",
+ "hpo",
+ "hps",
+ "hr",
+ "hra",
+ "hre",
+ "hrk",
+ "hrm",
+ "hro",
+ "hrr",
+ "hrt",
+ "hru",
+ "hrx",
+ "hrz",
+ "hsb",
+ "hsh",
+ "hsl",
+ "hsn",
+ "hss",
+ "ht",
+ "hti",
+ "hto",
+ "hts",
+ "htu",
+ "htx",
+ "hu",
+ "hub",
+ "huc",
+ "hud",
+ "hue",
+ "huf",
+ "hug",
+ "huh",
+ "hui",
+ "huj",
+ "huk",
+ "hul",
+ "hum",
+ "huo",
+ "hup",
+ "huq",
+ "hur",
+ "hus",
+ "hut",
+ "huu",
+ "huv",
+ "huw",
+ "hux",
+ "huy",
+ "huz",
+ "hvc",
+ "hve",
+ "hvk",
+ "hvn",
+ "hvv",
+ "hwa",
+ "hwc",
+ "hwo",
+ "hy",
+ "hya",
+ "hz",
+ "ia",
+ "iai",
+ "ian",
+ "iap",
+ "iar",
+ "iba",
+ "ibb",
+ "ibd",
+ "ibe",
+ "ibg",
+ "ibi",
+ "ibl",
+ "ibm",
+ "ibn",
+ "ibr",
+ "ibu",
+ "iby",
+ "ica",
+ "ich",
+ "icl",
+ "icr",
+ "id",
+ "ida",
+ "idb",
+ "idc",
+ "idd",
+ "ide",
+ "idi",
+ "idr",
+ "ids",
+ "idt",
+ "idu",
+ "ie",
+ "ifa",
+ "ifb",
+ "ife",
+ "iff",
+ "ifk",
+ "ifm",
+ "ifu",
+ "ify",
+ "ig",
+ "igb",
+ "ige",
+ "igg",
+ "igl",
+ "igm",
+ "ign",
+ "igo",
+ "igs",
+ "igw",
+ "ihb",
+ "ihi",
+ "ihp",
+ "ii",
+ "ijc",
+ "ije",
+ "ijj",
+ "ijn",
+ "ijs",
+ "ik",
+ "ike",
+ "iki",
+ "ikk",
+ "ikl",
+ "iko",
+ "ikp",
+ "ikt",
+ "ikv",
+ "ikw",
+ "ikx",
+ "ikz",
+ "ila",
+ "ilb",
+ "ilg",
+ "ili",
+ "ilk",
+ "ill",
+ "ilo",
+ "ils",
+ "ilu",
+ "ilv",
+ "ilw",
+ "ima",
+ "ime",
+ "imi",
+ "iml",
+ "imn",
+ "imo",
+ "imr",
+ "ims",
+ "imy",
+ "inb",
+ "ing",
+ "inh",
+ "inj",
+ "inl",
+ "inm",
+ "inn",
+ "ino",
+ "inp",
+ "ins",
+ "int",
+ "inz",
+ "io",
+ "ior",
+ "iou",
+ "iow",
+ "ipi",
+ "ipo",
+ "iqu",
+ "ire",
+ "irh",
+ "iri",
+ "irk",
+ "irn",
+ "irr",
+ "iru",
+ "irx",
+ "iry",
+ "is",
+ "isa",
+ "isc",
+ "isd",
+ "ise",
+ "isg",
+ "ish",
+ "isi",
+ "isk",
+ "ism",
+ "isn",
+ "iso",
+ "isr",
+ "ist",
+ "isu",
+ "it",
+ "itb",
+ "ite",
+ "iti",
+ "itk",
+ "itl",
+ "itm",
+ "ito",
+ "itr",
+ "its",
+ "itt",
+ "itv",
+ "itw",
+ "itx",
+ "ity",
+ "itz",
+ "iu",
+ "ium",
+ "ivb",
+ "ivv",
+ "iwk",
+ "iwm",
+ "iwo",
+ "iws",
+ "ixc",
+ "ixl",
+ "iya",
+ "iyo",
+ "iyx",
+ "izh",
+ "izi",
+ "izr",
+ "ja",
+ "jaa",
+ "jab",
+ "jac",
+ "jad",
+ "jae",
+ "jaf",
+ "jah",
+ "jaj",
+ "jak",
+ "jal",
+ "jam",
+ "jao",
+ "jaq",
+ "jar",
+ "jas",
+ "jat",
+ "jau",
+ "jax",
+ "jay",
+ "jaz",
+ "jbe",
+ "jbj",
+ "jbn",
+ "jbo",
+ "jbr",
+ "jbt",
+ "jbu",
+ "jcs",
+ "jct",
+ "jda",
+ "jdg",
+ "jdt",
+ "jeb",
+ "jee",
+ "jeg",
+ "jeh",
+ "jei",
+ "jek",
+ "jel",
+ "jen",
+ "jer",
+ "jet",
+ "jeu",
+ "jgb",
+ "jge",
+ "jgo",
+ "jhi",
+ "jhs",
+ "jia",
+ "jib",
+ "jic",
+ "jid",
+ "jie",
+ "jig",
+ "jih",
+ "jii",
+ "jil",
+ "jim",
+ "jio",
+ "jiq",
+ "jit",
+ "jiu",
+ "jiv",
+ "jiy",
+ "jko",
+ "jku",
+ "jle",
+ "jls",
+ "jma",
+ "jmb",
+ "jmc",
+ "jmd",
+ "jmi",
+ "jml",
+ "jmn",
+ "jmr",
+ "jms",
+ "jmx",
+ "jna",
+ "jnd",
+ "jng",
+ "jni",
+ "jnj",
+ "jnl",
+ "jns",
+ "job",
+ "jod",
+ "jor",
+ "jos",
+ "jow",
+ "jpa",
+ "jpr",
+ "jqr",
+ "jra",
+ "jrb",
+ "jrr",
+ "jrt",
+ "jru",
+ "jsl",
+ "jua",
+ "jub",
+ "juc",
+ "jud",
+ "juh",
+ "juk",
+ "jul",
+ "jum",
+ "jun",
+ "juo",
+ "jup",
+ "jur",
+ "jus",
+ "jut",
+ "juu",
+ "juw",
+ "juy",
+ "jv",
+ "jvd",
+ "jvn",
+ "jwi",
+ "jya",
+ "jye",
+ "jyy",
+ "ka",
+ "kaa",
+ "kab",
+ "kac",
+ "kad",
+ "kae",
+ "kaf",
+ "kag",
+ "kah",
+ "kai",
+ "kaj",
+ "kak",
+ "kam",
+ "kao",
+ "kap",
+ "kaq",
+ "kav",
+ "kaw",
+ "kax",
+ "kay",
+ "kba",
+ "kbb",
+ "kbc",
+ "kbd",
+ "kbe",
+ "kbf",
+ "kbg",
+ "kbh",
+ "kbi",
+ "kbj",
+ "kbk",
+ "kbl",
+ "kbm",
+ "kbn",
+ "kbo",
+ "kbp",
+ "kbq",
+ "kbr",
+ "kbs",
+ "kbt",
+ "kbu",
+ "kbv",
+ "kbw",
+ "kbx",
+ "kby",
+ "kbz",
+ "kca",
+ "kcb",
+ "kcc",
+ "kcd",
+ "kce",
+ "kcf",
+ "kcg",
+ "kch",
+ "kci",
+ "kcj",
+ "kck",
+ "kcl",
+ "kcm",
+ "kcn",
+ "kco",
+ "kcp",
+ "kcq",
+ "kcr",
+ "kcs",
+ "kct",
+ "kcu",
+ "kcv",
+ "kcw",
+ "kcx",
+ "kcy",
+ "kcz",
+ "kda",
+ "kdc",
+ "kdd",
+ "kde",
+ "kdf",
+ "kdg",
+ "kdh",
+ "kdi",
+ "kdj",
+ "kdk",
+ "kdl",
+ "kdm",
+ "kdn",
+ "kdp",
+ "kdq",
+ "kdr",
+ "kdt",
+ "kdu",
+ "kdv",
+ "kdw",
+ "kdx",
+ "kdy",
+ "kdz",
+ "kea",
+ "keb",
+ "kec",
+ "ked",
+ "kee",
+ "kef",
+ "keg",
+ "keh",
+ "kei",
+ "kej",
+ "kek",
+ "kel",
+ "kem",
+ "ken",
+ "keo",
+ "kep",
+ "keq",
+ "ker",
+ "kes",
+ "ket",
+ "keu",
+ "kev",
+ "kew",
+ "kex",
+ "key",
+ "kez",
+ "kfa",
+ "kfb",
+ "kfc",
+ "kfd",
+ "kfe",
+ "kff",
+ "kfg",
+ "kfh",
+ "kfi",
+ "kfj",
+ "kfk",
+ "kfl",
+ "kfm",
+ "kfn",
+ "kfo",
+ "kfp",
+ "kfq",
+ "kfr",
+ "kfs",
+ "kft",
+ "kfu",
+ "kfv",
+ "kfw",
+ "kfx",
+ "kfy",
+ "kfz",
+ "kg",
+ "kga",
+ "kgb",
+ "kgc",
+ "kgd",
+ "kge",
+ "kgf",
+ "kgg",
+ "kgh",
+ "kgi",
+ "kgj",
+ "kgk",
+ "kgl",
+ "kgm",
+ "kgn",
+ "kgo",
+ "kgp",
+ "kgq",
+ "kgr",
+ "kgs",
+ "kgt",
+ "kgu",
+ "kgv",
+ "kgw",
+ "kgx",
+ "kgy",
+ "kha",
+ "khb",
+ "khc",
+ "khd",
+ "khe",
+ "khf",
+ "khg",
+ "khh",
+ "khj",
+ "khk",
+ "khl",
+ "khn",
+ "kho",
+ "khp",
+ "khq",
+ "khr",
+ "khs",
+ "kht",
+ "khu",
+ "khv",
+ "khw",
+ "khx",
+ "khy",
+ "khz",
+ "ki",
+ "kia",
+ "kib",
+ "kic",
+ "kid",
+ "kie",
+ "kif",
+ "kig",
+ "kih",
+ "kii",
+ "kij",
+ "kil",
+ "kim",
+ "kio",
+ "kip",
+ "kiq",
+ "kis",
+ "kit",
+ "kiu",
+ "kiv",
+ "kiw",
+ "kix",
+ "kiy",
+ "kiz",
+ "kj",
+ "kja",
+ "kjb",
+ "kjc",
+ "kjd",
+ "kje",
+ "kjf",
+ "kjg",
+ "kjh",
+ "kji",
+ "kjj",
+ "kjk",
+ "kjl",
+ "kjm",
+ "kjn",
+ "kjo",
+ "kjp",
+ "kjq",
+ "kjr",
+ "kjs",
+ "kjt",
+ "kju",
+ "kjx",
+ "kjy",
+ "kjz",
+ "kk",
+ "kka",
+ "kkb",
+ "kkc",
+ "kkd",
+ "kke",
+ "kkf",
+ "kkg",
+ "kkh",
+ "kki",
+ "kkj",
+ "kkk",
+ "kkl",
+ "kkm",
+ "kkn",
+ "kko",
+ "kkp",
+ "kkq",
+ "kkr",
+ "kks",
+ "kkt",
+ "kku",
+ "kkv",
+ "kkw",
+ "kkx",
+ "kky",
+ "kkz",
+ "kl",
+ "kla",
+ "klb",
+ "klc",
+ "kld",
+ "kle",
+ "klf",
+ "klg",
+ "klh",
+ "kli",
+ "klj",
+ "klk",
+ "kll",
+ "klm",
+ "kln",
+ "klo",
+ "klp",
+ "klq",
+ "klr",
+ "kls",
+ "klt",
+ "klu",
+ "klv",
+ "klw",
+ "klx",
+ "kly",
+ "klz",
+ "km",
+ "kma",
+ "kmb",
+ "kmc",
+ "kmd",
+ "kme",
+ "kmf",
+ "kmg",
+ "kmh",
+ "kmi",
+ "kmj",
+ "kmk",
+ "kml",
+ "kmm",
+ "kmn",
+ "kmo",
+ "kmp",
+ "kmq",
+ "kmr",
+ "kms",
+ "kmt",
+ "kmu",
+ "kmv",
+ "kmw",
+ "kmx",
+ "kmy",
+ "kmz",
+ "kn",
+ "kna",
+ "knb",
+ "knc",
+ "knd",
+ "kne",
+ "knf",
+ "kng",
+ "kni",
+ "knj",
+ "knk",
+ "knl",
+ "knm",
+ "knn",
+ "kno",
+ "knp",
+ "knq",
+ "knr",
+ "kns",
+ "knt",
+ "knu",
+ "knv",
+ "knw",
+ "knx",
+ "kny",
+ "knz",
+ "ko",
+ "koa",
+ "koc",
+ "kod",
+ "koe",
+ "kof",
+ "kog",
+ "koh",
+ "koi",
+ "koj",
+ "kok",
+ "kol",
+ "koo",
+ "kop",
+ "koq",
+ "kos",
+ "kot",
+ "kou",
+ "kov",
+ "kow",
+ "kox",
+ "koy",
+ "koz",
+ "kpa",
+ "kpb",
+ "kpc",
+ "kpd",
+ "kpe",
+ "kpf",
+ "kpg",
+ "kph",
+ "kpi",
+ "kpj",
+ "kpk",
+ "kpl",
+ "kpm",
+ "kpn",
+ "kpo",
+ "kpp",
+ "kpq",
+ "kpr",
+ "kps",
+ "kpt",
+ "kpu",
+ "kpv",
+ "kpw",
+ "kpx",
+ "kpy",
+ "kpz",
+ "kqa",
+ "kqb",
+ "kqc",
+ "kqd",
+ "kqe",
+ "kqf",
+ "kqg",
+ "kqh",
+ "kqi",
+ "kqj",
+ "kqk",
+ "kql",
+ "kqm",
+ "kqn",
+ "kqo",
+ "kqp",
+ "kqq",
+ "kqr",
+ "kqs",
+ "kqt",
+ "kqu",
+ "kqv",
+ "kqw",
+ "kqx",
+ "kqy",
+ "kqz",
+ "kr",
+ "kra",
+ "krb",
+ "krc",
+ "krd",
+ "kre",
+ "krf",
+ "krh",
+ "kri",
+ "krj",
+ "krk",
+ "krl",
+ "krm",
+ "krn",
+ "krp",
+ "krr",
+ "krs",
+ "krt",
+ "kru",
+ "krv",
+ "krw",
+ "krx",
+ "kry",
+ "krz",
+ "ks",
+ "ksa",
+ "ksb",
+ "ksc",
+ "ksd",
+ "kse",
+ "ksf",
+ "ksg",
+ "ksh",
+ "ksi",
+ "ksj",
+ "ksk",
+ "ksl",
+ "ksm",
+ "ksn",
+ "kso",
+ "ksp",
+ "ksq",
+ "ksr",
+ "kss",
+ "kst",
+ "ksu",
+ "ksv",
+ "ksw",
+ "ksx",
+ "ksy",
+ "ksz",
+ "kta",
+ "ktb",
+ "ktc",
+ "ktd",
+ "kte",
+ "ktf",
+ "ktg",
+ "kth",
+ "kti",
+ "ktj",
+ "ktk",
+ "ktl",
+ "ktm",
+ "ktn",
+ "kto",
+ "ktp",
+ "ktq",
+ "ktr",
+ "kts",
+ "ktt",
+ "ktu",
+ "ktv",
+ "ktw",
+ "ktx",
+ "kty",
+ "ktz",
+ "ku",
+ "kub",
+ "kuc",
+ "kud",
+ "kue",
+ "kuf",
+ "kug",
+ "kuh",
+ "kui",
+ "kuj",
+ "kuk",
+ "kul",
+ "kum",
+ "kun",
+ "kuo",
+ "kup",
+ "kuq",
+ "kus",
+ "kut",
+ "kuu",
+ "kuv",
+ "kuw",
+ "kux",
+ "kuy",
+ "kuz",
+ "kv",
+ "kva",
+ "kvb",
+ "kvc",
+ "kvd",
+ "kve",
+ "kvf",
+ "kvg",
+ "kvh",
+ "kvi",
+ "kvj",
+ "kvk",
+ "kvl",
+ "kvm",
+ "kvn",
+ "kvo",
+ "kvp",
+ "kvq",
+ "kvr",
+ "kvs",
+ "kvt",
+ "kvu",
+ "kvv",
+ "kvw",
+ "kvx",
+ "kvy",
+ "kvz",
+ "kw",
+ "kwa",
+ "kwb",
+ "kwc",
+ "kwd",
+ "kwe",
+ "kwf",
+ "kwg",
+ "kwh",
+ "kwi",
+ "kwj",
+ "kwk",
+ "kwl",
+ "kwm",
+ "kwn",
+ "kwo",
+ "kwp",
+ "kwq",
+ "kwr",
+ "kws",
+ "kwt",
+ "kwu",
+ "kwv",
+ "kww",
+ "kwx",
+ "kwy",
+ "kwz",
+ "kxa",
+ "kxb",
+ "kxc",
+ "kxd",
+ "kxe",
+ "kxf",
+ "kxh",
+ "kxi",
+ "kxj",
+ "kxk",
+ "kxl",
+ "kxm",
+ "kxn",
+ "kxo",
+ "kxp",
+ "kxq",
+ "kxr",
+ "kxs",
+ "kxt",
+ "kxu",
+ "kxv",
+ "kxw",
+ "kxx",
+ "kxy",
+ "kxz",
+ "ky",
+ "kya",
+ "kyb",
+ "kyc",
+ "kyd",
+ "kye",
+ "kyf",
+ "kyg",
+ "kyh",
+ "kyi",
+ "kyj",
+ "kyk",
+ "kyl",
+ "kym",
+ "kyn",
+ "kyo",
+ "kyp",
+ "kyq",
+ "kyr",
+ "kys",
+ "kyt",
+ "kyu",
+ "kyv",
+ "kyw",
+ "kyx",
+ "kyy",
+ "kyz",
+ "kza",
+ "kzb",
+ "kzc",
+ "kzd",
+ "kze",
+ "kzf",
+ "kzg",
+ "kzh",
+ "kzi",
+ "kzj",
+ "kzk",
+ "kzl",
+ "kzm",
+ "kzn",
+ "kzo",
+ "kzp",
+ "kzq",
+ "kzr",
+ "kzs",
+ "kzt",
+ "kzu",
+ "kzv",
+ "kzw",
+ "kzx",
+ "kzy",
+ "kzz",
+ "la",
+ "laa",
+ "lab",
+ "lac",
+ "lad",
+ "lae",
+ "laf",
+ "lag",
+ "lah",
+ "lai",
+ "laj",
+ "lak",
+ "lal",
+ "lam",
+ "lan",
+ "lap",
+ "laq",
+ "lar",
+ "las",
+ "lau",
+ "law",
+ "lax",
+ "lay",
+ "laz",
+ "lb",
+ "lba",
+ "lbb",
+ "lbc",
+ "lbe",
+ "lbf",
+ "lbg",
+ "lbi",
+ "lbj",
+ "lbk",
+ "lbl",
+ "lbm",
+ "lbn",
+ "lbo",
+ "lbq",
+ "lbr",
+ "lbs",
+ "lbt",
+ "lbu",
+ "lbv",
+ "lbw",
+ "lbx",
+ "lby",
+ "lbz",
+ "lcc",
+ "lcd",
+ "lce",
+ "lcf",
+ "lch",
+ "lcl",
+ "lcm",
+ "lcp",
+ "lcq",
+ "lcs",
+ "ldb",
+ "ldd",
+ "ldg",
+ "ldh",
+ "ldi",
+ "ldj",
+ "ldk",
+ "ldl",
+ "ldm",
+ "ldn",
+ "ldo",
+ "ldp",
+ "ldq",
+ "lea",
+ "leb",
+ "lec",
+ "led",
+ "lee",
+ "lef",
+ "leg",
+ "leh",
+ "lei",
+ "lej",
+ "lek",
+ "lel",
+ "lem",
+ "len",
+ "leo",
+ "lep",
+ "leq",
+ "ler",
+ "les",
+ "let",
+ "leu",
+ "lev",
+ "lew",
+ "lex",
+ "ley",
+ "lez",
+ "lfa",
+ "lfn",
+ "lg",
+ "lga",
+ "lgb",
+ "lgg",
+ "lgh",
+ "lgi",
+ "lgk",
+ "lgl",
+ "lgm",
+ "lgn",
+ "lgq",
+ "lgr",
+ "lgt",
+ "lgu",
+ "lgz",
+ "lha",
+ "lhh",
+ "lhi",
+ "lhl",
+ "lhm",
+ "lhn",
+ "lhp",
+ "lhs",
+ "lht",
+ "lhu",
+ "li",
+ "lia",
+ "lib",
+ "lic",
+ "lid",
+ "lie",
+ "lif",
+ "lig",
+ "lih",
+ "lii",
+ "lij",
+ "lik",
+ "lil",
+ "lio",
+ "lip",
+ "liq",
+ "lir",
+ "lis",
+ "liu",
+ "liv",
+ "liw",
+ "lix",
+ "liy",
+ "liz",
+ "lje",
+ "lji",
+ "ljl",
+ "ljp",
+ "lka",
+ "lkb",
+ "lkc",
+ "lkd",
+ "lke",
+ "lkh",
+ "lki",
+ "lkj",
+ "lkl",
+ "lkn",
+ "lko",
+ "lkr",
+ "lks",
+ "lkt",
+ "lky",
+ "lla",
+ "llb",
+ "llc",
+ "lld",
+ "lle",
+ "llf",
+ "llg",
+ "llh",
+ "lli",
+ "llk",
+ "lll",
+ "llm",
+ "lln",
+ "llo",
+ "llp",
+ "llq",
+ "lls",
+ "llu",
+ "llx",
+ "lma",
+ "lmb",
+ "lmc",
+ "lmd",
+ "lme",
+ "lmf",
+ "lmg",
+ "lmh",
+ "lmi",
+ "lmj",
+ "lmk",
+ "lml",
+ "lmm",
+ "lmn",
+ "lmo",
+ "lmp",
+ "lmq",
+ "lmr",
+ "lmu",
+ "lmv",
+ "lmw",
+ "lmx",
+ "lmy",
+ "lmz",
+ "ln",
+ "lna",
+ "lnb",
+ "lnd",
+ "lng",
+ "lnh",
+ "lni",
+ "lnj",
+ "lnl",
+ "lnm",
+ "lnn",
+ "lno",
+ "lns",
+ "lnu",
+ "lnz",
+ "lo",
+ "loa",
+ "lob",
+ "loc",
+ "loe",
+ "lof",
+ "log",
+ "loh",
+ "loi",
+ "loj",
+ "lok",
+ "lol",
+ "lom",
+ "lon",
+ "loo",
+ "lop",
+ "loq",
+ "lor",
+ "los",
+ "lot",
+ "lou",
+ "lov",
+ "low",
+ "lox",
+ "loy",
+ "loz",
+ "lpa",
+ "lpe",
+ "lpn",
+ "lpo",
+ "lpx",
+ "lra",
+ "lrc",
+ "lre",
+ "lrg",
+ "lri",
+ "lrk",
+ "lrl",
+ "lrm",
+ "lrn",
+ "lro",
+ "lrr",
+ "lrt",
+ "lrv",
+ "lrz",
+ "lsa",
+ "lsd",
+ "lse",
+ "lsg",
+ "lsh",
+ "lsi",
+ "lsl",
+ "lsm",
+ "lso",
+ "lsp",
+ "lsr",
+ "lss",
+ "lst",
+ "lsy",
+ "lt",
+ "ltc",
+ "ltg",
+ "lti",
+ "ltn",
+ "lto",
+ "lts",
+ "ltu",
+ "lu",
+ "lua",
+ "luc",
+ "lud",
+ "lue",
+ "luf",
+ "lui",
+ "luj",
+ "luk",
+ "lul",
+ "lum",
+ "lun",
+ "luo",
+ "lup",
+ "luq",
+ "lur",
+ "lus",
+ "lut",
+ "luu",
+ "luv",
+ "luw",
+ "luy",
+ "luz",
+ "lv",
+ "lva",
+ "lvk",
+ "lvs",
+ "lvu",
+ "lwa",
+ "lwe",
+ "lwg",
+ "lwh",
+ "lwl",
+ "lwm",
+ "lwo",
+ "lwt",
+ "lww",
+ "lya",
+ "lyg",
+ "lyn",
+ "lzh",
+ "lzl",
+ "lzn",
+ "lzz",
+ "maa",
+ "mab",
+ "mad",
+ "mae",
+ "maf",
+ "mag",
+ "mai",
+ "maj",
+ "mak",
+ "mam",
+ "man",
+ "maq",
+ "mas",
+ "mat",
+ "mau",
+ "mav",
+ "maw",
+ "max",
+ "maz",
+ "mba",
+ "mbb",
+ "mbc",
+ "mbd",
+ "mbe",
+ "mbf",
+ "mbh",
+ "mbi",
+ "mbj",
+ "mbk",
+ "mbl",
+ "mbm",
+ "mbn",
+ "mbo",
+ "mbp",
+ "mbq",
+ "mbr",
+ "mbs",
+ "mbt",
+ "mbu",
+ "mbv",
+ "mbw",
+ "mbx",
+ "mby",
+ "mbz",
+ "mca",
+ "mcb",
+ "mcc",
+ "mcd",
+ "mce",
+ "mcf",
+ "mcg",
+ "mch",
+ "mci",
+ "mcj",
+ "mck",
+ "mcl",
+ "mcm",
+ "mcn",
+ "mco",
+ "mcp",
+ "mcq",
+ "mcr",
+ "mcs",
+ "mct",
+ "mcu",
+ "mcv",
+ "mcw",
+ "mcx",
+ "mcy",
+ "mcz",
+ "mda",
+ "mdb",
+ "mdc",
+ "mdd",
+ "mde",
+ "mdf",
+ "mdg",
+ "mdh",
+ "mdi",
+ "mdj",
+ "mdk",
+ "mdl",
+ "mdm",
+ "mdn",
+ "mdp",
+ "mdq",
+ "mdr",
+ "mds",
+ "mdt",
+ "mdu",
+ "mdv",
+ "mdw",
+ "mdx",
+ "mdy",
+ "mdz",
+ "mea",
+ "meb",
+ "mec",
+ "med",
+ "mee",
+ "mef",
+ "meg",
+ "meh",
+ "mei",
+ "mej",
+ "mek",
+ "mel",
+ "mem",
+ "men",
+ "meo",
+ "mep",
+ "meq",
+ "mer",
+ "mes",
+ "met",
+ "meu",
+ "mev",
+ "mew",
+ "mey",
+ "mez",
+ "mfa",
+ "mfb",
+ "mfc",
+ "mfd",
+ "mfe",
+ "mff",
+ "mfg",
+ "mfh",
+ "mfi",
+ "mfj",
+ "mfk",
+ "mfl",
+ "mfm",
+ "mfn",
+ "mfo",
+ "mfp",
+ "mfq",
+ "mfr",
+ "mfs",
+ "mft",
+ "mfu",
+ "mfv",
+ "mfw",
+ "mfx",
+ "mfy",
+ "mfz",
+ "mg",
+ "mga",
+ "mgb",
+ "mgc",
+ "mgd",
+ "mge",
+ "mgf",
+ "mgg",
+ "mgh",
+ "mgi",
+ "mgj",
+ "mgk",
+ "mgl",
+ "mgm",
+ "mgn",
+ "mgo",
+ "mgp",
+ "mgq",
+ "mgr",
+ "mgs",
+ "mgt",
+ "mgu",
+ "mgv",
+ "mgw",
+ "mgx",
+ "mgy",
+ "mgz",
+ "mh",
+ "mha",
+ "mhb",
+ "mhc",
+ "mhd",
+ "mhe",
+ "mhf",
+ "mhg",
+ "mhh",
+ "mhi",
+ "mhj",
+ "mhk",
+ "mhl",
+ "mhm",
+ "mhn",
+ "mho",
+ "mhp",
+ "mhq",
+ "mhr",
+ "mhs",
+ "mht",
+ "mhu",
+ "mhw",
+ "mhx",
+ "mhy",
+ "mhz",
+ "mi",
+ "mia",
+ "mib",
+ "mic",
+ "mid",
+ "mie",
+ "mif",
+ "mig",
+ "mih",
+ "mii",
+ "mij",
+ "mik",
+ "mil",
+ "mim",
+ "min",
+ "mio",
+ "mip",
+ "miq",
+ "mir",
+ "mis",
+ "mit",
+ "miu",
+ "miw",
+ "mix",
+ "miy",
+ "miz",
+ "mjc",
+ "mjd",
+ "mje",
+ "mjg",
+ "mjh",
+ "mji",
+ "mjj",
+ "mjk",
+ "mjl",
+ "mjm",
+ "mjn",
+ "mjo",
+ "mjp",
+ "mjq",
+ "mjr",
+ "mjs",
+ "mjt",
+ "mju",
+ "mjv",
+ "mjw",
+ "mjx",
+ "mjy",
+ "mjz",
+ "mk",
+ "mka",
+ "mkb",
+ "mkc",
+ "mke",
+ "mkf",
+ "mkg",
+ "mki",
+ "mkj",
+ "mkk",
+ "mkl",
+ "mkm",
+ "mkn",
+ "mko",
+ "mkp",
+ "mkq",
+ "mkr",
+ "mks",
+ "mkt",
+ "mku",
+ "mkv",
+ "mkw",
+ "mkx",
+ "mky",
+ "mkz",
+ "ml",
+ "mla",
+ "mlb",
+ "mlc",
+ "mld",
+ "mle",
+ "mlf",
+ "mlh",
+ "mli",
+ "mlj",
+ "mlk",
+ "mll",
+ "mlm",
+ "mln",
+ "mlo",
+ "mlp",
+ "mlq",
+ "mlr",
+ "mls",
+ "mlu",
+ "mlv",
+ "mlw",
+ "mlx",
+ "mlz",
+ "mma",
+ "mmb",
+ "mmc",
+ "mmd",
+ "mme",
+ "mmf",
+ "mmg",
+ "mmh",
+ "mmi",
+ "mmj",
+ "mmk",
+ "mml",
+ "mmm",
+ "mmn",
+ "mmo",
+ "mmp",
+ "mmq",
+ "mmr",
+ "mmt",
+ "mmu",
+ "mmv",
+ "mmw",
+ "mmx",
+ "mmy",
+ "mmz",
+ "mn",
+ "mna",
+ "mnb",
+ "mnc",
+ "mnd",
+ "mne",
+ "mnf",
+ "mng",
+ "mnh",
+ "mni",
+ "mnj",
+ "mnk",
+ "mnl",
+ "mnm",
+ "mnn",
+ "mnp",
+ "mnq",
+ "mnr",
+ "mns",
+ "mnt",
+ "mnu",
+ "mnv",
+ "mnw",
+ "mnx",
+ "mny",
+ "mnz",
+ "mo",
+ "moa",
+ "moc",
+ "mod",
+ "moe",
+ "mog",
+ "moh",
+ "moi",
+ "moj",
+ "mok",
+ "mom",
+ "moo",
+ "mop",
+ "moq",
+ "mor",
+ "mos",
+ "mot",
+ "mou",
+ "mov",
+ "mow",
+ "mox",
+ "moy",
+ "moz",
+ "mpa",
+ "mpb",
+ "mpc",
+ "mpd",
+ "mpe",
+ "mpg",
+ "mph",
+ "mpi",
+ "mpj",
+ "mpk",
+ "mpl",
+ "mpm",
+ "mpn",
+ "mpo",
+ "mpp",
+ "mpq",
+ "mpr",
+ "mps",
+ "mpt",
+ "mpu",
+ "mpv",
+ "mpw",
+ "mpx",
+ "mpy",
+ "mpz",
+ "mqa",
+ "mqb",
+ "mqc",
+ "mqe",
+ "mqf",
+ "mqg",
+ "mqh",
+ "mqi",
+ "mqj",
+ "mqk",
+ "mql",
+ "mqm",
+ "mqn",
+ "mqo",
+ "mqp",
+ "mqq",
+ "mqr",
+ "mqs",
+ "mqt",
+ "mqu",
+ "mqv",
+ "mqw",
+ "mqx",
+ "mqy",
+ "mqz",
+ "mr",
+ "mra",
+ "mrb",
+ "mrc",
+ "mrd",
+ "mre",
+ "mrf",
+ "mrg",
+ "mrh",
+ "mrj",
+ "mrk",
+ "mrl",
+ "mrm",
+ "mrn",
+ "mro",
+ "mrp",
+ "mrq",
+ "mrr",
+ "mrs",
+ "mrt",
+ "mru",
+ "mrv",
+ "mrw",
+ "mrx",
+ "mry",
+ "mrz",
+ "ms",
+ "msb",
+ "msc",
+ "msd",
+ "mse",
+ "msf",
+ "msg",
+ "msh",
+ "msi",
+ "msj",
+ "msk",
+ "msl",
+ "msm",
+ "msn",
+ "mso",
+ "msp",
+ "msq",
+ "msr",
+ "mss",
+ "msu",
+ "msv",
+ "msw",
+ "msx",
+ "msy",
+ "msz",
+ "mt",
+ "mta",
+ "mtb",
+ "mtc",
+ "mtd",
+ "mte",
+ "mtf",
+ "mtg",
+ "mth",
+ "mti",
+ "mtj",
+ "mtk",
+ "mtl",
+ "mtm",
+ "mtn",
+ "mto",
+ "mtp",
+ "mtq",
+ "mtr",
+ "mts",
+ "mtt",
+ "mtu",
+ "mtv",
+ "mtw",
+ "mtx",
+ "mty",
+ "mua",
+ "mub",
+ "muc",
+ "mud",
+ "mue",
+ "mug",
+ "muh",
+ "mui",
+ "muj",
+ "muk",
+ "mul",
+ "mum",
+ "muo",
+ "mup",
+ "muq",
+ "mur",
+ "mus",
+ "mut",
+ "muu",
+ "muv",
+ "mux",
+ "muy",
+ "muz",
+ "mva",
+ "mvb",
+ "mvd",
+ "mve",
+ "mvf",
+ "mvg",
+ "mvh",
+ "mvi",
+ "mvk",
+ "mvl",
+ "mvm",
+ "mvn",
+ "mvo",
+ "mvp",
+ "mvq",
+ "mvr",
+ "mvs",
+ "mvt",
+ "mvu",
+ "mvv",
+ "mvw",
+ "mvx",
+ "mvy",
+ "mvz",
+ "mwa",
+ "mwb",
+ "mwc",
+ "mwd",
+ "mwe",
+ "mwf",
+ "mwg",
+ "mwh",
+ "mwi",
+ "mwj",
+ "mwk",
+ "mwl",
+ "mwm",
+ "mwn",
+ "mwo",
+ "mwp",
+ "mwq",
+ "mwr",
+ "mws",
+ "mwt",
+ "mwu",
+ "mwv",
+ "mww",
+ "mwx",
+ "mwy",
+ "mwz",
+ "mxa",
+ "mxb",
+ "mxc",
+ "mxd",
+ "mxe",
+ "mxf",
+ "mxg",
+ "mxh",
+ "mxi",
+ "mxj",
+ "mxk",
+ "mxl",
+ "mxm",
+ "mxn",
+ "mxo",
+ "mxp",
+ "mxq",
+ "mxr",
+ "mxs",
+ "mxt",
+ "mxu",
+ "mxv",
+ "mxw",
+ "mxx",
+ "mxy",
+ "mxz",
+ "my",
+ "myb",
+ "myc",
+ "myd",
+ "mye",
+ "myf",
+ "myg",
+ "myh",
+ "myi",
+ "myj",
+ "myk",
+ "myl",
+ "mym",
+ "myo",
+ "myp",
+ "myq",
+ "myr",
+ "mys",
+ "myu",
+ "myv",
+ "myw",
+ "myx",
+ "myy",
+ "myz",
+ "mza",
+ "mzb",
+ "mzc",
+ "mzd",
+ "mze",
+ "mzg",
+ "mzh",
+ "mzi",
+ "mzj",
+ "mzk",
+ "mzl",
+ "mzm",
+ "mzn",
+ "mzo",
+ "mzp",
+ "mzq",
+ "mzr",
+ "mzs",
+ "mzt",
+ "mzu",
+ "mzv",
+ "mzw",
+ "mzx",
+ "mzy",
+ "mzz",
+ "na",
+ "naa",
+ "nab",
+ "nac",
+ "nad",
+ "nae",
+ "naf",
+ "nag",
+ "naj",
+ "nak",
+ "nal",
+ "nam",
+ "nan",
+ "nao",
+ "nap",
+ "naq",
+ "nar",
+ "nas",
+ "nat",
+ "naw",
+ "nax",
+ "nay",
+ "naz",
+ "nb",
+ "nba",
+ "nbb",
+ "nbc",
+ "nbd",
+ "nbe",
+ "nbg",
+ "nbh",
+ "nbi",
+ "nbj",
+ "nbk",
+ "nbm",
+ "nbn",
+ "nbo",
+ "nbp",
+ "nbq",
+ "nbr",
+ "nbs",
+ "nbt",
+ "nbu",
+ "nbv",
+ "nbw",
+ "nbx",
+ "nby",
+ "nca",
+ "ncb",
+ "ncc",
+ "ncd",
+ "nce",
+ "ncf",
+ "ncg",
+ "nch",
+ "nci",
+ "ncj",
+ "nck",
+ "ncl",
+ "ncm",
+ "ncn",
+ "nco",
+ "ncp",
+ "ncr",
+ "ncs",
+ "nct",
+ "ncu",
+ "ncx",
+ "ncz",
+ "nd",
+ "nda",
+ "ndb",
+ "ndc",
+ "ndd",
+ "ndf",
+ "ndg",
+ "ndh",
+ "ndi",
+ "ndj",
+ "ndk",
+ "ndl",
+ "ndm",
+ "ndn",
+ "ndp",
+ "ndq",
+ "ndr",
+ "nds",
+ "ndt",
+ "ndu",
+ "ndv",
+ "ndw",
+ "ndx",
+ "ndy",
+ "ndz",
+ "ne",
+ "nea",
+ "neb",
+ "nec",
+ "ned",
+ "nee",
+ "nef",
+ "neg",
+ "neh",
+ "nei",
+ "nej",
+ "nek",
+ "nem",
+ "nen",
+ "neo",
+ "neq",
+ "ner",
+ "nes",
+ "net",
+ "nev",
+ "new",
+ "nex",
+ "ney",
+ "nez",
+ "nfa",
+ "nfd",
+ "nfl",
+ "nfr",
+ "nfu",
+ "ng",
+ "nga",
+ "ngb",
+ "ngc",
+ "ngd",
+ "nge",
+ "ngg",
+ "ngh",
+ "ngi",
+ "ngj",
+ "ngk",
+ "ngl",
+ "ngm",
+ "ngn",
+ "ngo",
+ "ngp",
+ "ngq",
+ "ngr",
+ "ngs",
+ "ngt",
+ "ngu",
+ "ngv",
+ "ngw",
+ "ngx",
+ "ngy",
+ "ngz",
+ "nha",
+ "nhb",
+ "nhc",
+ "nhd",
+ "nhe",
+ "nhf",
+ "nhg",
+ "nhh",
+ "nhi",
+ "nhk",
+ "nhm",
+ "nhn",
+ "nho",
+ "nhp",
+ "nhq",
+ "nhr",
+ "nht",
+ "nhu",
+ "nhv",
+ "nhw",
+ "nhx",
+ "nhy",
+ "nhz",
+ "nia",
+ "nib",
+ "nid",
+ "nie",
+ "nif",
+ "nig",
+ "nih",
+ "nii",
+ "nij",
+ "nik",
+ "nil",
+ "nim",
+ "nin",
+ "nio",
+ "niq",
+ "nir",
+ "nis",
+ "nit",
+ "niu",
+ "niv",
+ "niw",
+ "nix",
+ "niy",
+ "niz",
+ "nja",
+ "njb",
+ "njd",
+ "njh",
+ "nji",
+ "njj",
+ "njl",
+ "njm",
+ "njn",
+ "njo",
+ "njr",
+ "njs",
+ "njt",
+ "nju",
+ "njx",
+ "njy",
+ "nka",
+ "nkb",
+ "nkc",
+ "nkd",
+ "nke",
+ "nkf",
+ "nkg",
+ "nkh",
+ "nki",
+ "nkj",
+ "nkk",
+ "nkm",
+ "nkn",
+ "nko",
+ "nkp",
+ "nkq",
+ "nkr",
+ "nks",
+ "nkt",
+ "nku",
+ "nkv",
+ "nkw",
+ "nkx",
+ "nkz",
+ "nl",
+ "nla",
+ "nlc",
+ "nle",
+ "nlg",
+ "nli",
+ "nlj",
+ "nlk",
+ "nll",
+ "nln",
+ "nlo",
+ "nlr",
+ "nlu",
+ "nlv",
+ "nlx",
+ "nly",
+ "nlz",
+ "nma",
+ "nmb",
+ "nmc",
+ "nmd",
+ "nme",
+ "nmf",
+ "nmg",
+ "nmh",
+ "nmi",
+ "nmj",
+ "nmk",
+ "nml",
+ "nmm",
+ "nmn",
+ "nmo",
+ "nmp",
+ "nmq",
+ "nmr",
+ "nms",
+ "nmt",
+ "nmu",
+ "nmv",
+ "nmw",
+ "nmx",
+ "nmy",
+ "nmz",
+ "nn",
+ "nna",
+ "nnb",
+ "nnc",
+ "nnd",
+ "nne",
+ "nnf",
+ "nng",
+ "nnh",
+ "nni",
+ "nnj",
+ "nnk",
+ "nnl",
+ "nnm",
+ "nnn",
+ "nnp",
+ "nnq",
+ "nnr",
+ "nns",
+ "nnt",
+ "nnu",
+ "nnv",
+ "nnw",
+ "nnx",
+ "nny",
+ "nnz",
+ "no",
+ "noa",
+ "noc",
+ "nod",
+ "noe",
+ "nof",
+ "nog",
+ "noh",
+ "noi",
+ "noj",
+ "nok",
+ "nom",
+ "non",
+ "nop",
+ "noq",
+ "nos",
+ "not",
+ "nou",
+ "nov",
+ "now",
+ "noy",
+ "noz",
+ "npa",
+ "npb",
+ "nph",
+ "npl",
+ "npn",
+ "npo",
+ "nps",
+ "npu",
+ "npy",
+ "nqg",
+ "nqk",
+ "nqm",
+ "nqn",
+ "nqo",
+ "nr",
+ "nra",
+ "nrb",
+ "nrc",
+ "nre",
+ "nrg",
+ "nri",
+ "nrl",
+ "nrm",
+ "nrn",
+ "nrp",
+ "nrr",
+ "nrt",
+ "nru",
+ "nrx",
+ "nrz",
+ "nsa",
+ "nsc",
+ "nsd",
+ "nse",
+ "nsg",
+ "nsh",
+ "nsi",
+ "nsk",
+ "nsl",
+ "nsm",
+ "nsn",
+ "nso",
+ "nsp",
+ "nsq",
+ "nsr",
+ "nss",
+ "nst",
+ "nsu",
+ "nsv",
+ "nsw",
+ "nsx",
+ "nsy",
+ "nsz",
+ "nte",
+ "nti",
+ "ntj",
+ "ntk",
+ "ntm",
+ "nto",
+ "ntp",
+ "ntr",
+ "nts",
+ "ntu",
+ "ntw",
+ "nty",
+ "ntz",
+ "nua",
+ "nuc",
+ "nud",
+ "nue",
+ "nuf",
+ "nug",
+ "nuh",
+ "nui",
+ "nuj",
+ "nuk",
+ "nul",
+ "num",
+ "nun",
+ "nuo",
+ "nup",
+ "nuq",
+ "nur",
+ "nus",
+ "nut",
+ "nuu",
+ "nuv",
+ "nuw",
+ "nux",
+ "nuy",
+ "nuz",
+ "nv",
+ "nvh",
+ "nvm",
+ "nwa",
+ "nwb",
+ "nwc",
+ "nwe",
+ "nwi",
+ "nwm",
+ "nwr",
+ "nwx",
+ "nwy",
+ "nxa",
+ "nxd",
+ "nxe",
+ "nxg",
+ "nxi",
+ "nxl",
+ "nxm",
+ "nxn",
+ "nxq",
+ "nxr",
+ "nxu",
+ "nxx",
+ "ny",
+ "nyb",
+ "nyc",
+ "nyd",
+ "nye",
+ "nyf",
+ "nyg",
+ "nyh",
+ "nyi",
+ "nyj",
+ "nyk",
+ "nyl",
+ "nym",
+ "nyn",
+ "nyo",
+ "nyp",
+ "nyq",
+ "nyr",
+ "nys",
+ "nyt",
+ "nyu",
+ "nyv",
+ "nyw",
+ "nyx",
+ "nyy",
+ "nza",
+ "nzb",
+ "nzi",
+ "nzk",
+ "nzm",
+ "nzs",
+ "nzu",
+ "nzy",
+ "nzz",
+ "oaa",
+ "oac",
+ "oar",
+ "oav",
+ "obi",
+ "obk",
+ "obl",
+ "obm",
+ "obo",
+ "obr",
+ "obt",
+ "obu",
+ "oc",
+ "oca",
+ "och",
+ "oco",
+ "ocu",
+ "oda",
+ "odk",
+ "odt",
+ "odu",
+ "ofo",
+ "ofs",
+ "ofu",
+ "ogb",
+ "ogc",
+ "oge",
+ "ogg",
+ "ogo",
+ "ogu",
+ "oht",
+ "ohu",
+ "oia",
+ "oin",
+ "oj",
+ "ojb",
+ "ojc",
+ "ojg",
+ "ojp",
+ "ojs",
+ "ojv",
+ "ojw",
+ "oka",
+ "okb",
+ "okd",
+ "oke",
+ "okh",
+ "oki",
+ "okj",
+ "okk",
+ "okl",
+ "okm",
+ "okn",
+ "oko",
+ "okr",
+ "oks",
+ "oku",
+ "okv",
+ "okx",
+ "ola",
+ "old",
+ "ole",
+ "olm",
+ "olo",
+ "olr",
+ "om",
+ "oma",
+ "omb",
+ "omc",
+ "ome",
+ "omg",
+ "omi",
+ "omk",
+ "oml",
+ "omn",
+ "omo",
+ "omp",
+ "omr",
+ "omt",
+ "omu",
+ "omw",
+ "omx",
+ "ona",
+ "onb",
+ "one",
+ "ong",
+ "oni",
+ "onj",
+ "onk",
+ "onn",
+ "ono",
+ "onp",
+ "onr",
+ "ons",
+ "ont",
+ "onu",
+ "onw",
+ "onx",
+ "ood",
+ "oog",
+ "oon",
+ "oor",
+ "oos",
+ "opa",
+ "opk",
+ "opm",
+ "opo",
+ "opt",
+ "opy",
+ "or",
+ "ora",
+ "orc",
+ "ore",
+ "org",
+ "orh",
+ "orn",
+ "oro",
+ "orr",
+ "ors",
+ "ort",
+ "oru",
+ "orv",
+ "orw",
+ "orx",
+ "orz",
+ "os",
+ "osa",
+ "osc",
+ "osi",
+ "oso",
+ "osp",
+ "ost",
+ "osu",
+ "osx",
+ "ota",
+ "otb",
+ "otd",
+ "ote",
+ "oti",
+ "otk",
+ "otl",
+ "otm",
+ "otn",
+ "otq",
+ "otr",
+ "ots",
+ "ott",
+ "otu",
+ "otw",
+ "otx",
+ "oty",
+ "otz",
+ "oua",
+ "oub",
+ "oue",
+ "oui",
+ "oum",
+ "oun",
+ "owi",
+ "owl",
+ "oyb",
+ "oyd",
+ "oym",
+ "oyy",
+ "ozm",
+ "pa",
+ "pab",
+ "pac",
+ "pad",
+ "pae",
+ "paf",
+ "pag",
+ "pah",
+ "pai",
+ "pak",
+ "pal",
+ "pam",
+ "pao",
+ "pap",
+ "paq",
+ "par",
+ "pas",
+ "pat",
+ "pau",
+ "pav",
+ "paw",
+ "pax",
+ "pay",
+ "paz",
+ "pbb",
+ "pbc",
+ "pbe",
+ "pbf",
+ "pbg",
+ "pbh",
+ "pbi",
+ "pbl",
+ "pbn",
+ "pbo",
+ "pbp",
+ "pbr",
+ "pbs",
+ "pbt",
+ "pbu",
+ "pbv",
+ "pby",
+ "pbz",
+ "pca",
+ "pcb",
+ "pcc",
+ "pcd",
+ "pce",
+ "pcf",
+ "pcg",
+ "pch",
+ "pci",
+ "pcj",
+ "pck",
+ "pcl",
+ "pcm",
+ "pcn",
+ "pcp",
+ "pcr",
+ "pcw",
+ "pda",
+ "pdc",
+ "pdi",
+ "pdn",
+ "pdo",
+ "pdt",
+ "pdu",
+ "pea",
+ "peb",
+ "ped",
+ "pee",
+ "pef",
+ "peg",
+ "peh",
+ "pei",
+ "pej",
+ "pek",
+ "pel",
+ "pem",
+ "peo",
+ "pep",
+ "peq",
+ "pes",
+ "pev",
+ "pex",
+ "pey",
+ "pez",
+ "pfa",
+ "pfe",
+ "pfl",
+ "pga",
+ "pgg",
+ "pgi",
+ "pgk",
+ "pgl",
+ "pgn",
+ "pgs",
+ "pgu",
+ "pgy",
+ "pha",
+ "phd",
+ "phg",
+ "phh",
+ "phk",
+ "phl",
+ "phm",
+ "phn",
+ "pho",
+ "phq",
+ "phr",
+ "pht",
+ "phu",
+ "phv",
+ "phw",
+ "pi",
+ "pia",
+ "pib",
+ "pic",
+ "pid",
+ "pie",
+ "pif",
+ "pig",
+ "pih",
+ "pii",
+ "pij",
+ "pil",
+ "pim",
+ "pin",
+ "pio",
+ "pip",
+ "pir",
+ "pis",
+ "pit",
+ "piu",
+ "piv",
+ "piw",
+ "pix",
+ "piy",
+ "piz",
+ "pjt",
+ "pka",
+ "pkb",
+ "pkc",
+ "pkg",
+ "pkh",
+ "pkn",
+ "pko",
+ "pkp",
+ "pkr",
+ "pks",
+ "pkt",
+ "pku",
+ "pl",
+ "pla",
+ "plb",
+ "plc",
+ "pld",
+ "ple",
+ "plg",
+ "plh",
+ "plj",
+ "plk",
+ "pll",
+ "pln",
+ "plo",
+ "plp",
+ "plq",
+ "plr",
+ "pls",
+ "plt",
+ "plu",
+ "plv",
+ "plw",
+ "ply",
+ "plz",
+ "pma",
+ "pmb",
+ "pmc",
+ "pme",
+ "pmf",
+ "pmh",
+ "pmi",
+ "pmj",
+ "pmk",
+ "pml",
+ "pmm",
+ "pmn",
+ "pmo",
+ "pmq",
+ "pmr",
+ "pms",
+ "pmt",
+ "pmu",
+ "pmw",
+ "pmx",
+ "pmy",
+ "pmz",
+ "pna",
+ "pnb",
+ "pnc",
+ "pne",
+ "png",
+ "pnh",
+ "pni",
+ "pnm",
+ "pnn",
+ "pno",
+ "pnp",
+ "pnq",
+ "pnr",
+ "pns",
+ "pnt",
+ "pnu",
+ "pnv",
+ "pnw",
+ "pnx",
+ "pny",
+ "pnz",
+ "poc",
+ "pod",
+ "poe",
+ "pof",
+ "pog",
+ "poh",
+ "poi",
+ "pok",
+ "pom",
+ "pon",
+ "poo",
+ "pop",
+ "poq",
+ "pos",
+ "pot",
+ "pov",
+ "pow",
+ "pox",
+ "poy",
+ "ppa",
+ "ppe",
+ "ppi",
+ "ppk",
+ "ppl",
+ "ppm",
+ "ppn",
+ "ppo",
+ "ppp",
+ "ppq",
+ "ppr",
+ "pps",
+ "ppt",
+ "ppu",
+ "pqa",
+ "pqm",
+ "prb",
+ "prc",
+ "prd",
+ "pre",
+ "prf",
+ "prg",
+ "prh",
+ "pri",
+ "prk",
+ "prl",
+ "prm",
+ "prn",
+ "pro",
+ "prp",
+ "prq",
+ "prr",
+ "prs",
+ "prt",
+ "pru",
+ "prw",
+ "prx",
+ "pry",
+ "prz",
+ "ps",
+ "psa",
+ "psc",
+ "psd",
+ "pse",
+ "psg",
+ "psh",
+ "psi",
+ "psl",
+ "psm",
+ "psn",
+ "pso",
+ "psp",
+ "psq",
+ "psr",
+ "pss",
+ "pst",
+ "psu",
+ "psw",
+ "psy",
+ "pt",
+ "pta",
+ "pth",
+ "pti",
+ "ptn",
+ "pto",
+ "ptp",
+ "ptr",
+ "ptt",
+ "ptu",
+ "ptv",
+ "ptw",
+ "pty",
+ "pua",
+ "pub",
+ "puc",
+ "pud",
+ "pue",
+ "puf",
+ "pug",
+ "pui",
+ "puj",
+ "puk",
+ "pum",
+ "puo",
+ "pup",
+ "puq",
+ "pur",
+ "put",
+ "puu",
+ "puw",
+ "pux",
+ "puy",
+ "puz",
+ "pwa",
+ "pwb",
+ "pwg",
+ "pwm",
+ "pwn",
+ "pwo",
+ "pwr",
+ "pww",
+ "pxm",
+ "pye",
+ "pym",
+ "pyn",
+ "pys",
+ "pyu",
+ "pyx",
+ "pyy",
+ "pzn",
+ "qu",
+ "qua",
+ "qub",
+ "quc",
+ "qud",
+ "quf",
+ "qug",
+ "quh",
+ "qui",
+ "quk",
+ "qul",
+ "qum",
+ "qun",
+ "qup",
+ "quq",
+ "qur",
+ "qus",
+ "quv",
+ "quw",
+ "qux",
+ "quy",
+ "quz",
+ "qva",
+ "qvc",
+ "qve",
+ "qvh",
+ "qvi",
+ "qvj",
+ "qvl",
+ "qvm",
+ "qvn",
+ "qvo",
+ "qvp",
+ "qvs",
+ "qvw",
+ "qvy",
+ "qvz",
+ "qwa",
+ "qwc",
+ "qwh",
+ "qwm",
+ "qws",
+ "qwt",
+ "qxa",
+ "qxc",
+ "qxh",
+ "qxl",
+ "qxn",
+ "qxo",
+ "qxp",
+ "qxq",
+ "qxr",
+ "qxs",
+ "qxt",
+ "qxu",
+ "qxw",
+ "qya",
+ "qyp",
+ "raa",
+ "rab",
+ "rac",
+ "rad",
+ "raf",
+ "rag",
+ "rah",
+ "rai",
+ "raj",
+ "rak",
+ "ral",
+ "ram",
+ "ran",
+ "rao",
+ "rap",
+ "raq",
+ "rar",
+ "ras",
+ "rat",
+ "rau",
+ "rav",
+ "raw",
+ "rax",
+ "ray",
+ "raz",
+ "rbb",
+ "rbk",
+ "rbl",
+ "rcf",
+ "rdb",
+ "rea",
+ "reb",
+ "ree",
+ "reg",
+ "rei",
+ "rej",
+ "rel",
+ "rem",
+ "ren",
+ "rer",
+ "res",
+ "ret",
+ "rey",
+ "rga",
+ "rge",
+ "rgk",
+ "rgn",
+ "rgr",
+ "rgs",
+ "rgu",
+ "rhg",
+ "rhp",
+ "ria",
+ "rie",
+ "rif",
+ "ril",
+ "rim",
+ "rin",
+ "rir",
+ "rit",
+ "riu",
+ "rjg",
+ "rji",
+ "rjs",
+ "rka",
+ "rkb",
+ "rkh",
+ "rki",
+ "rkm",
+ "rkt",
+ "rm",
+ "rma",
+ "rmb",
+ "rmc",
+ "rmd",
+ "rme",
+ "rmf",
+ "rmg",
+ "rmh",
+ "rmi",
+ "rmk",
+ "rml",
+ "rmm",
+ "rmn",
+ "rmo",
+ "rmp",
+ "rmq",
+ "rms",
+ "rmt",
+ "rmu",
+ "rmv",
+ "rmw",
+ "rmx",
+ "rmy",
+ "rmz",
+ "rn",
+ "rna",
+ "rnd",
+ "rng",
+ "rnl",
+ "rnn",
+ "rnp",
+ "rnw",
+ "ro",
+ "rob",
+ "roc",
+ "rod",
+ "roe",
+ "rof",
+ "rog",
+ "rol",
+ "rom",
+ "roo",
+ "rop",
+ "ror",
+ "rou",
+ "row",
+ "rpn",
+ "rpt",
+ "rri",
+ "rro",
+ "rsb",
+ "rsi",
+ "rsl",
+ "rth",
+ "rtm",
+ "rtw",
+ "ru",
+ "rub",
+ "ruc",
+ "rue",
+ "ruf",
+ "rug",
+ "ruh",
+ "rui",
+ "ruk",
+ "ruo",
+ "rup",
+ "ruq",
+ "rut",
+ "ruu",
+ "ruy",
+ "ruz",
+ "rw",
+ "rwa",
+ "rwk",
+ "rwm",
+ "rwo",
+ "rwr",
+ "ryn",
+ "rys",
+ "ryu",
+ "sa",
+ "saa",
+ "sab",
+ "sac",
+ "sad",
+ "sae",
+ "saf",
+ "sah",
+ "saj",
+ "sak",
+ "sam",
+ "sao",
+ "sap",
+ "saq",
+ "sar",
+ "sas",
+ "sat",
+ "sau",
+ "sav",
+ "saw",
+ "sax",
+ "say",
+ "saz",
+ "sba",
+ "sbb",
+ "sbc",
+ "sbd",
+ "sbe",
+ "sbf",
+ "sbg",
+ "sbh",
+ "sbi",
+ "sbj",
+ "sbk",
+ "sbl",
+ "sbm",
+ "sbn",
+ "sbo",
+ "sbp",
+ "sbq",
+ "sbr",
+ "sbs",
+ "sbt",
+ "sbu",
+ "sbv",
+ "sbw",
+ "sbx",
+ "sby",
+ "sbz",
+ "sc",
+ "sca",
+ "scb",
+ "sce",
+ "scf",
+ "scg",
+ "sch",
+ "sci",
+ "sck",
+ "scl",
+ "scn",
+ "sco",
+ "scp",
+ "scq",
+ "scs",
+ "scu",
+ "scv",
+ "scw",
+ "scx",
+ "sd",
+ "sda",
+ "sdb",
+ "sdc",
+ "sde",
+ "sdf",
+ "sdg",
+ "sdh",
+ "sdj",
+ "sdk",
+ "sdl",
+ "sdm",
+ "sdn",
+ "sdo",
+ "sdp",
+ "sdr",
+ "sds",
+ "sdt",
+ "sdu",
+ "sdx",
+ "sdz",
+ "se",
+ "sea",
+ "seb",
+ "sec",
+ "sed",
+ "see",
+ "sef",
+ "seg",
+ "seh",
+ "sei",
+ "sej",
+ "sek",
+ "sel",
+ "sen",
+ "seo",
+ "sep",
+ "seq",
+ "ser",
+ "ses",
+ "set",
+ "seu",
+ "sev",
+ "sew",
+ "sey",
+ "sez",
+ "sfb",
+ "sfm",
+ "sfs",
+ "sfw",
+ "sg",
+ "sga",
+ "sgb",
+ "sgc",
+ "sgd",
+ "sge",
+ "sgg",
+ "sgh",
+ "sgi",
+ "sgk",
+ "sgm",
+ "sgo",
+ "sgp",
+ "sgr",
+ "sgs",
+ "sgt",
+ "sgu",
+ "sgw",
+ "sgx",
+ "sgy",
+ "sgz",
+ "sh",
+ "sha",
+ "shb",
+ "shc",
+ "shd",
+ "she",
+ "shg",
+ "shh",
+ "shi",
+ "shj",
+ "shk",
+ "shl",
+ "shm",
+ "shn",
+ "sho",
+ "shp",
+ "shq",
+ "shr",
+ "shs",
+ "sht",
+ "shu",
+ "shv",
+ "shw",
+ "shx",
+ "shy",
+ "shz",
+ "si",
+ "sia",
+ "sib",
+ "sid",
+ "sie",
+ "sif",
+ "sig",
+ "sih",
+ "sii",
+ "sij",
+ "sik",
+ "sil",
+ "sim",
+ "sip",
+ "siq",
+ "sir",
+ "sis",
+ "siu",
+ "siv",
+ "siw",
+ "six",
+ "siy",
+ "siz",
+ "sja",
+ "sjb",
+ "sjd",
+ "sje",
+ "sjg",
+ "sjk",
+ "sjl",
+ "sjm",
+ "sjn",
+ "sjo",
+ "sjp",
+ "sjr",
+ "sjs",
+ "sjt",
+ "sju",
+ "sjw",
+ "sk",
+ "ska",
+ "skb",
+ "skc",
+ "skd",
+ "ske",
+ "skf",
+ "skg",
+ "skh",
+ "ski",
+ "skj",
+ "skk",
+ "skm",
+ "skn",
+ "sko",
+ "skp",
+ "skq",
+ "skr",
+ "sks",
+ "skt",
+ "sku",
+ "skv",
+ "skw",
+ "skx",
+ "sky",
+ "skz",
+ "sl",
+ "slc",
+ "sld",
+ "sle",
+ "slf",
+ "slg",
+ "slh",
+ "sli",
+ "slj",
+ "sll",
+ "slm",
+ "sln",
+ "slp",
+ "slq",
+ "slr",
+ "sls",
+ "slt",
+ "slu",
+ "slw",
+ "slx",
+ "sly",
+ "slz",
+ "sm",
+ "sma",
+ "smb",
+ "smc",
+ "smd",
+ "smf",
+ "smg",
+ "smh",
+ "smj",
+ "smk",
+ "sml",
+ "smm",
+ "smn",
+ "smp",
+ "smq",
+ "smr",
+ "sms",
+ "smt",
+ "smu",
+ "smv",
+ "smw",
+ "smx",
+ "smy",
+ "smz",
+ "sn",
+ "snb",
+ "snc",
+ "sne",
+ "snf",
+ "sng",
+ "snh",
+ "sni",
+ "snj",
+ "snk",
+ "snl",
+ "snm",
+ "snn",
+ "sno",
+ "snp",
+ "snq",
+ "snr",
+ "sns",
+ "snu",
+ "snv",
+ "snw",
+ "snx",
+ "sny",
+ "snz",
+ "so",
+ "soa",
+ "sob",
+ "soc",
+ "sod",
+ "soe",
+ "sog",
+ "soh",
+ "soi",
+ "soj",
+ "sok",
+ "sol",
+ "soo",
+ "sop",
+ "soq",
+ "sor",
+ "sos",
+ "sou",
+ "sov",
+ "sow",
+ "sox",
+ "soy",
+ "soz",
+ "spb",
+ "spc",
+ "spd",
+ "spe",
+ "spg",
+ "spi",
+ "spk",
+ "spl",
+ "spm",
+ "spo",
+ "spp",
+ "spq",
+ "spr",
+ "sps",
+ "spt",
+ "spu",
+ "spx",
+ "spy",
+ "sq",
+ "sqa",
+ "sqh",
+ "sqm",
+ "sqn",
+ "sqo",
+ "sqq",
+ "sqr",
+ "sqs",
+ "sqt",
+ "squ",
+ "sr",
+ "sra",
+ "srb",
+ "src",
+ "sre",
+ "srf",
+ "srg",
+ "srh",
+ "sri",
+ "srk",
+ "srl",
+ "srm",
+ "srn",
+ "sro",
+ "srq",
+ "srr",
+ "srs",
+ "srt",
+ "sru",
+ "srv",
+ "srw",
+ "srx",
+ "sry",
+ "srz",
+ "ss",
+ "ssb",
+ "ssc",
+ "ssd",
+ "sse",
+ "ssf",
+ "ssg",
+ "ssh",
+ "ssi",
+ "ssj",
+ "ssk",
+ "ssl",
+ "ssm",
+ "ssn",
+ "sso",
+ "ssp",
+ "ssq",
+ "ssr",
+ "sss",
+ "sst",
+ "ssu",
+ "ssv",
+ "ssx",
+ "ssy",
+ "ssz",
+ "st",
+ "sta",
+ "stb",
+ "std",
+ "ste",
+ "stf",
+ "stg",
+ "sth",
+ "sti",
+ "stj",
+ "stk",
+ "stl",
+ "stm",
+ "stn",
+ "sto",
+ "stp",
+ "stq",
+ "str",
+ "sts",
+ "stt",
+ "stu",
+ "stv",
+ "stw",
+ "su",
+ "sua",
+ "sub",
+ "suc",
+ "sue",
+ "sug",
+ "sui",
+ "suj",
+ "suk",
+ "suq",
+ "sur",
+ "sus",
+ "sut",
+ "suv",
+ "suw",
+ "sux",
+ "suy",
+ "suz",
+ "sv",
+ "sva",
+ "svb",
+ "svc",
+ "sve",
+ "svk",
+ "svr",
+ "svs",
+ "svx",
+ "sw",
+ "swb",
+ "swc",
+ "swf",
+ "swg",
+ "swh",
+ "swi",
+ "swj",
+ "swk",
+ "swl",
+ "swm",
+ "swn",
+ "swo",
+ "swp",
+ "swq",
+ "swr",
+ "sws",
+ "swt",
+ "swu",
+ "swv",
+ "sww",
+ "swx",
+ "swy",
+ "sxb",
+ "sxc",
+ "sxe",
+ "sxg",
+ "sxk",
+ "sxl",
+ "sxm",
+ "sxn",
+ "sxo",
+ "sxr",
+ "sxs",
+ "sxu",
+ "sxw",
+ "sya",
+ "syb",
+ "syc",
+ "syi",
+ "syk",
+ "syl",
+ "sym",
+ "syn",
+ "syo",
+ "syr",
+ "sys",
+ "syw",
+ "syy",
+ "sza",
+ "szb",
+ "szc",
+ "szd",
+ "sze",
+ "szg",
+ "szl",
+ "szn",
+ "szp",
+ "szv",
+ "szw",
+ "ta",
+ "taa",
+ "tab",
+ "tac",
+ "tad",
+ "tae",
+ "taf",
+ "tag",
+ "taj",
+ "tak",
+ "tal",
+ "tan",
+ "tao",
+ "tap",
+ "taq",
+ "tar",
+ "tas",
+ "tau",
+ "tav",
+ "taw",
+ "tax",
+ "tay",
+ "taz",
+ "tba",
+ "tbb",
+ "tbc",
+ "tbd",
+ "tbe",
+ "tbf",
+ "tbg",
+ "tbh",
+ "tbi",
+ "tbj",
+ "tbk",
+ "tbl",
+ "tbm",
+ "tbn",
+ "tbo",
+ "tbp",
+ "tbr",
+ "tbs",
+ "tbt",
+ "tbu",
+ "tbv",
+ "tbw",
+ "tbx",
+ "tby",
+ "tbz",
+ "tca",
+ "tcb",
+ "tcc",
+ "tcd",
+ "tce",
+ "tcf",
+ "tcg",
+ "tch",
+ "tci",
+ "tck",
+ "tcl",
+ "tcm",
+ "tcn",
+ "tco",
+ "tcp",
+ "tcq",
+ "tcs",
+ "tct",
+ "tcu",
+ "tcw",
+ "tcx",
+ "tcy",
+ "tcz",
+ "tda",
+ "tdb",
+ "tdc",
+ "tdd",
+ "tde",
+ "tdf",
+ "tdg",
+ "tdh",
+ "tdi",
+ "tdj",
+ "tdk",
+ "tdl",
+ "tdn",
+ "tdo",
+ "tdq",
+ "tdr",
+ "tds",
+ "tdt",
+ "tdu",
+ "tdv",
+ "tdx",
+ "tdy",
+ "te",
+ "tea",
+ "teb",
+ "tec",
+ "ted",
+ "tee",
+ "tef",
+ "teg",
+ "teh",
+ "tei",
+ "tek",
+ "tem",
+ "ten",
+ "teo",
+ "tep",
+ "teq",
+ "ter",
+ "tes",
+ "tet",
+ "teu",
+ "tev",
+ "tew",
+ "tex",
+ "tey",
+ "tfi",
+ "tfn",
+ "tfo",
+ "tfr",
+ "tft",
+ "tg",
+ "tga",
+ "tgb",
+ "tgc",
+ "tgd",
+ "tge",
+ "tgf",
+ "tgg",
+ "tgh",
+ "tgi",
+ "tgn",
+ "tgo",
+ "tgp",
+ "tgq",
+ "tgr",
+ "tgs",
+ "tgt",
+ "tgu",
+ "tgv",
+ "tgw",
+ "tgx",
+ "tgy",
+ "th",
+ "thc",
+ "thd",
+ "the",
+ "thf",
+ "thh",
+ "thi",
+ "thk",
+ "thl",
+ "thm",
+ "thn",
+ "thp",
+ "thq",
+ "thr",
+ "ths",
+ "tht",
+ "thu",
+ "thv",
+ "thw",
+ "thx",
+ "thy",
+ "thz",
+ "ti",
+ "tia",
+ "tic",
+ "tid",
+ "tif",
+ "tig",
+ "tih",
+ "tii",
+ "tij",
+ "tik",
+ "til",
+ "tim",
+ "tin",
+ "tio",
+ "tip",
+ "tiq",
+ "tis",
+ "tit",
+ "tiu",
+ "tiv",
+ "tiw",
+ "tix",
+ "tiy",
+ "tiz",
+ "tja",
+ "tjg",
+ "tji",
+ "tjm",
+ "tjn",
+ "tjo",
+ "tjs",
+ "tju",
+ "tk",
+ "tka",
+ "tkb",
+ "tkd",
+ "tke",
+ "tkf",
+ "tkg",
+ "tkl",
+ "tkm",
+ "tkn",
+ "tkp",
+ "tkq",
+ "tkr",
+ "tks",
+ "tkt",
+ "tku",
+ "tkw",
+ "tkx",
+ "tkz",
+ "tl",
+ "tla",
+ "tlb",
+ "tlc",
+ "tld",
+ "tlf",
+ "tlg",
+ "tlh",
+ "tli",
+ "tlj",
+ "tlk",
+ "tll",
+ "tlm",
+ "tln",
+ "tlo",
+ "tlp",
+ "tlq",
+ "tlr",
+ "tls",
+ "tlt",
+ "tlu",
+ "tlv",
+ "tlw",
+ "tlx",
+ "tly",
+ "tma",
+ "tmb",
+ "tmc",
+ "tmd",
+ "tme",
+ "tmf",
+ "tmg",
+ "tmh",
+ "tmi",
+ "tmj",
+ "tmk",
+ "tml",
+ "tmm",
+ "tmn",
+ "tmo",
+ "tmp",
+ "tmq",
+ "tmr",
+ "tms",
+ "tmt",
+ "tmu",
+ "tmv",
+ "tmw",
+ "tmy",
+ "tmz",
+ "tn",
+ "tna",
+ "tnb",
+ "tnc",
+ "tnd",
+ "tne",
+ "tng",
+ "tnh",
+ "tni",
+ "tnk",
+ "tnl",
+ "tnm",
+ "tnn",
+ "tno",
+ "tnp",
+ "tnq",
+ "tnr",
+ "tns",
+ "tnt",
+ "tnu",
+ "tnv",
+ "tnw",
+ "tnx",
+ "tny",
+ "tnz",
+ "to",
+ "tob",
+ "toc",
+ "tod",
+ "toe",
+ "tof",
+ "tog",
+ "toh",
+ "toi",
+ "toj",
+ "tol",
+ "tom",
+ "too",
+ "top",
+ "toq",
+ "tor",
+ "tos",
+ "tou",
+ "tov",
+ "tow",
+ "tox",
+ "toy",
+ "toz",
+ "tpa",
+ "tpc",
+ "tpe",
+ "tpf",
+ "tpg",
+ "tpi",
+ "tpj",
+ "tpk",
+ "tpl",
+ "tpm",
+ "tpn",
+ "tpo",
+ "tpp",
+ "tpq",
+ "tpr",
+ "tpt",
+ "tpu",
+ "tpv",
+ "tpw",
+ "tpx",
+ "tpy",
+ "tpz",
+ "tqb",
+ "tql",
+ "tqm",
+ "tqn",
+ "tqo",
+ "tqp",
+ "tqq",
+ "tqr",
+ "tqt",
+ "tqu",
+ "tqw",
+ "tr",
+ "tra",
+ "trb",
+ "trc",
+ "trd",
+ "tre",
+ "trf",
+ "trg",
+ "trh",
+ "tri",
+ "trj",
+ "trl",
+ "trm",
+ "trn",
+ "tro",
+ "trp",
+ "trq",
+ "trr",
+ "trs",
+ "trt",
+ "tru",
+ "trv",
+ "trw",
+ "trx",
+ "try",
+ "trz",
+ "ts",
+ "tsa",
+ "tsb",
+ "tsc",
+ "tsd",
+ "tse",
+ "tsf",
+ "tsg",
+ "tsh",
+ "tsi",
+ "tsj",
+ "tsk",
+ "tsl",
+ "tsm",
+ "tsp",
+ "tsq",
+ "tsr",
+ "tss",
+ "tst",
+ "tsu",
+ "tsv",
+ "tsw",
+ "tsx",
+ "tsy",
+ "tsz",
+ "tt",
+ "tta",
+ "ttb",
+ "ttc",
+ "ttd",
+ "tte",
+ "ttf",
+ "ttg",
+ "tth",
+ "tti",
+ "ttj",
+ "ttk",
+ "ttl",
+ "ttm",
+ "ttn",
+ "tto",
+ "ttp",
+ "ttq",
+ "ttr",
+ "tts",
+ "ttt",
+ "ttu",
+ "ttv",
+ "ttw",
+ "tty",
+ "ttz",
+ "tua",
+ "tub",
+ "tuc",
+ "tud",
+ "tue",
+ "tuf",
+ "tug",
+ "tuh",
+ "tui",
+ "tuj",
+ "tul",
+ "tum",
+ "tun",
+ "tuo",
+ "tuq",
+ "tus",
+ "tuu",
+ "tuv",
+ "tux",
+ "tuy",
+ "tuz",
+ "tva",
+ "tvd",
+ "tve",
+ "tvk",
+ "tvl",
+ "tvm",
+ "tvn",
+ "tvo",
+ "tvs",
+ "tvt",
+ "tvw",
+ "tvy",
+ "tw",
+ "twa",
+ "twb",
+ "twc",
+ "twd",
+ "twe",
+ "twf",
+ "twg",
+ "twh",
+ "twl",
+ "twm",
+ "twn",
+ "two",
+ "twp",
+ "twq",
+ "twr",
+ "twt",
+ "twu",
+ "tww",
+ "twx",
+ "twy",
+ "txa",
+ "txb",
+ "txc",
+ "txe",
+ "txg",
+ "txh",
+ "txi",
+ "txm",
+ "txn",
+ "txo",
+ "txq",
+ "txr",
+ "txs",
+ "txt",
+ "txu",
+ "txx",
+ "txy",
+ "ty",
+ "tya",
+ "tye",
+ "tyh",
+ "tyi",
+ "tyj",
+ "tyl",
+ "tyn",
+ "typ",
+ "tyr",
+ "tys",
+ "tyt",
+ "tyu",
+ "tyv",
+ "tyx",
+ "tyz",
+ "tza",
+ "tzh",
+ "tzj",
+ "tzm",
+ "tzn",
+ "tzo",
+ "tzx",
+ "uam",
+ "uan",
+ "uar",
+ "uba",
+ "ubi",
+ "ubl",
+ "ubr",
+ "ubu",
+ "uby",
+ "uda",
+ "ude",
+ "udg",
+ "udi",
+ "udj",
+ "udl",
+ "udm",
+ "udu",
+ "ues",
+ "ufi",
+ "ug",
+ "uga",
+ "ugb",
+ "uge",
+ "ugn",
+ "ugo",
+ "ugy",
+ "uha",
+ "uhn",
+ "uis",
+ "uiv",
+ "uji",
+ "uk",
+ "uka",
+ "ukg",
+ "ukh",
+ "ukl",
+ "ukp",
+ "ukq",
+ "uks",
+ "uku",
+ "ukw",
+ "ula",
+ "ulb",
+ "ulc",
+ "ulf",
+ "uli",
+ "ulk",
+ "ull",
+ "ulm",
+ "uln",
+ "ulu",
+ "ulw",
+ "uma",
+ "umb",
+ "umc",
+ "umd",
+ "umg",
+ "umi",
+ "umm",
+ "umn",
+ "umo",
+ "ump",
+ "umr",
+ "ums",
+ "umu",
+ "una",
+ "und",
+ "une",
+ "ung",
+ "unk",
+ "unm",
+ "unp",
+ "unr",
+ "unx",
+ "unz",
+ "uok",
+ "upi",
+ "upv",
+ "ur",
+ "ura",
+ "urb",
+ "urc",
+ "ure",
+ "urf",
+ "urg",
+ "urh",
+ "uri",
+ "urk",
+ "url",
+ "urm",
+ "urn",
+ "uro",
+ "urp",
+ "urr",
+ "urt",
+ "uru",
+ "urv",
+ "urw",
+ "urx",
+ "ury",
+ "urz",
+ "usa",
+ "ush",
+ "usi",
+ "usk",
+ "usp",
+ "usu",
+ "uta",
+ "ute",
+ "utp",
+ "utr",
+ "utu",
+ "uum",
+ "uun",
+ "uur",
+ "uuu",
+ "uve",
+ "uvh",
+ "uvl",
+ "uwa",
+ "uya",
+ "uz",
+ "uzn",
+ "uzs",
+ "vaa",
+ "vae",
+ "vaf",
+ "vag",
+ "vah",
+ "vai",
+ "vaj",
+ "val",
+ "vam",
+ "van",
+ "vao",
+ "vap",
+ "var",
+ "vas",
+ "vau",
+ "vav",
+ "vay",
+ "vbb",
+ "vbk",
+ "ve",
+ "vec",
+ "ved",
+ "vel",
+ "vem",
+ "veo",
+ "vep",
+ "ver",
+ "vgr",
+ "vgt",
+ "vi",
+ "vic",
+ "vid",
+ "vif",
+ "vig",
+ "vil",
+ "vin",
+ "vis",
+ "vit",
+ "viv",
+ "vka",
+ "vki",
+ "vkj",
+ "vkk",
+ "vkl",
+ "vkm",
+ "vko",
+ "vkp",
+ "vkt",
+ "vku",
+ "vlp",
+ "vls",
+ "vma",
+ "vmb",
+ "vmc",
+ "vmd",
+ "vme",
+ "vmf",
+ "vmg",
+ "vmh",
+ "vmi",
+ "vmj",
+ "vmk",
+ "vml",
+ "vmm",
+ "vmp",
+ "vmq",
+ "vmr",
+ "vms",
+ "vmu",
+ "vmv",
+ "vmw",
+ "vmx",
+ "vmy",
+ "vmz",
+ "vnk",
+ "vnm",
+ "vnp",
+ "vo",
+ "vor",
+ "vot",
+ "vra",
+ "vro",
+ "vrs",
+ "vrt",
+ "vsi",
+ "vsl",
+ "vsv",
+ "vto",
+ "vum",
+ "vun",
+ "vut",
+ "vwa",
+ "wa",
+ "waa",
+ "wab",
+ "wac",
+ "wad",
+ "wae",
+ "waf",
+ "wag",
+ "wah",
+ "wai",
+ "waj",
+ "wal",
+ "wam",
+ "wan",
+ "wao",
+ "wap",
+ "waq",
+ "war",
+ "was",
+ "wat",
+ "wau",
+ "wav",
+ "waw",
+ "wax",
+ "way",
+ "waz",
+ "wba",
+ "wbb",
+ "wbe",
+ "wbf",
+ "wbh",
+ "wbi",
+ "wbj",
+ "wbk",
+ "wbl",
+ "wbm",
+ "wbp",
+ "wbq",
+ "wbr",
+ "wbt",
+ "wbv",
+ "wbw",
+ "wca",
+ "wci",
+ "wdd",
+ "wdg",
+ "wdj",
+ "wdu",
+ "wea",
+ "wec",
+ "wed",
+ "weh",
+ "wei",
+ "wem",
+ "weo",
+ "wep",
+ "wer",
+ "wes",
+ "wet",
+ "weu",
+ "wew",
+ "wfg",
+ "wga",
+ "wgb",
+ "wgg",
+ "wgi",
+ "wgo",
+ "wgy",
+ "wha",
+ "whg",
+ "whk",
+ "whu",
+ "wib",
+ "wic",
+ "wie",
+ "wif",
+ "wig",
+ "wih",
+ "wii",
+ "wij",
+ "wik",
+ "wil",
+ "wim",
+ "win",
+ "wir",
+ "wit",
+ "wiu",
+ "wiv",
+ "wiw",
+ "wiy",
+ "wja",
+ "wji",
+ "wka",
+ "wkb",
+ "wkd",
+ "wkl",
+ "wku",
+ "wkw",
+ "wla",
+ "wlc",
+ "wle",
+ "wlg",
+ "wli",
+ "wlk",
+ "wll",
+ "wlm",
+ "wlo",
+ "wlr",
+ "wls",
+ "wlu",
+ "wlv",
+ "wlw",
+ "wlx",
+ "wly",
+ "wma",
+ "wmb",
+ "wmc",
+ "wmd",
+ "wme",
+ "wmh",
+ "wmi",
+ "wmm",
+ "wmn",
+ "wmo",
+ "wms",
+ "wmt",
+ "wmw",
+ "wmx",
+ "wnb",
+ "wnc",
+ "wnd",
+ "wne",
+ "wng",
+ "wni",
+ "wnk",
+ "wnm",
+ "wno",
+ "wnp",
+ "wnu",
+ "wo",
+ "woa",
+ "wob",
+ "woc",
+ "wod",
+ "woe",
+ "wof",
+ "wog",
+ "woi",
+ "wok",
+ "wom",
+ "won",
+ "woo",
+ "wor",
+ "wos",
+ "wow",
+ "woy",
+ "wpc",
+ "wra",
+ "wrb",
+ "wrd",
+ "wrg",
+ "wrh",
+ "wri",
+ "wrl",
+ "wrm",
+ "wrn",
+ "wrp",
+ "wrr",
+ "wrs",
+ "wru",
+ "wrv",
+ "wrw",
+ "wrx",
+ "wry",
+ "wrz",
+ "wsa",
+ "wsi",
+ "wsk",
+ "wsr",
+ "wss",
+ "wsu",
+ "wsv",
+ "wtf",
+ "wti",
+ "wtk",
+ "wtm",
+ "wtw",
+ "wua",
+ "wub",
+ "wud",
+ "wuh",
+ "wul",
+ "wum",
+ "wun",
+ "wur",
+ "wut",
+ "wuu",
+ "wuv",
+ "wux",
+ "wuy",
+ "wwa",
+ "wwo",
+ "wwr",
+ "www",
+ "wxa",
+ "wya",
+ "wyb",
+ "wym",
+ "wyr",
+ "wyy",
+ "xaa",
+ "xab",
+ "xac",
+ "xad",
+ "xae",
+ "xag",
+ "xai",
+ "xal",
+ "xam",
+ "xan",
+ "xao",
+ "xap",
+ "xaq",
+ "xar",
+ "xas",
+ "xat",
+ "xau",
+ "xav",
+ "xaw",
+ "xay",
+ "xba",
+ "xbb",
+ "xbc",
+ "xbi",
+ "xbm",
+ "xbn",
+ "xbo",
+ "xbr",
+ "xbw",
+ "xbx",
+ "xcb",
+ "xcc",
+ "xce",
+ "xcg",
+ "xch",
+ "xcl",
+ "xcm",
+ "xcn",
+ "xco",
+ "xcr",
+ "xct",
+ "xcu",
+ "xcv",
+ "xcw",
+ "xcy",
+ "xdc",
+ "xdm",
+ "xdy",
+ "xeb",
+ "xed",
+ "xeg",
+ "xel",
+ "xem",
+ "xep",
+ "xer",
+ "xes",
+ "xet",
+ "xeu",
+ "xfa",
+ "xga",
+ "xgf",
+ "xgl",
+ "xgr",
+ "xh",
+ "xha",
+ "xhc",
+ "xhd",
+ "xhe",
+ "xhr",
+ "xht",
+ "xhu",
+ "xhv",
+ "xia",
+ "xib",
+ "xii",
+ "xil",
+ "xin",
+ "xip",
+ "xir",
+ "xiv",
+ "xiy",
+ "xka",
+ "xkb",
+ "xkc",
+ "xkd",
+ "xke",
+ "xkf",
+ "xkg",
+ "xkh",
+ "xki",
+ "xkj",
+ "xkk",
+ "xkl",
+ "xkn",
+ "xko",
+ "xkp",
+ "xkq",
+ "xkr",
+ "xks",
+ "xkt",
+ "xku",
+ "xkv",
+ "xkw",
+ "xkx",
+ "xky",
+ "xkz",
+ "xla",
+ "xlb",
+ "xlc",
+ "xld",
+ "xle",
+ "xlg",
+ "xli",
+ "xln",
+ "xlo",
+ "xlp",
+ "xls",
+ "xlu",
+ "xly",
+ "xma",
+ "xmb",
+ "xmc",
+ "xmd",
+ "xme",
+ "xmf",
+ "xmg",
+ "xmh",
+ "xmj",
+ "xmk",
+ "xml",
+ "xmm",
+ "xmn",
+ "xmo",
+ "xmp",
+ "xmq",
+ "xmr",
+ "xms",
+ "xmt",
+ "xmu",
+ "xmv",
+ "xmw",
+ "xmx",
+ "xmy",
+ "xmz",
+ "xna",
+ "xnb",
+ "xng",
+ "xnh",
+ "xnn",
+ "xno",
+ "xnr",
+ "xns",
+ "xnt",
+ "xoc",
+ "xod",
+ "xog",
+ "xoi",
+ "xok",
+ "xom",
+ "xon",
+ "xoo",
+ "xop",
+ "xor",
+ "xow",
+ "xpc",
+ "xpe",
+ "xpg",
+ "xpi",
+ "xpk",
+ "xpm",
+ "xpn",
+ "xpo",
+ "xpp",
+ "xpq",
+ "xpr",
+ "xps",
+ "xpu",
+ "xpy",
+ "xqa",
+ "xqt",
+ "xra",
+ "xrb",
+ "xre",
+ "xri",
+ "xrm",
+ "xrn",
+ "xrr",
+ "xrt",
+ "xru",
+ "xrw",
+ "xsa",
+ "xsb",
+ "xsc",
+ "xsd",
+ "xse",
+ "xsh",
+ "xsi",
+ "xsj",
+ "xsl",
+ "xsm",
+ "xsn",
+ "xso",
+ "xsp",
+ "xsq",
+ "xsr",
+ "xss",
+ "xsu",
+ "xsv",
+ "xsy",
+ "xta",
+ "xtb",
+ "xtc",
+ "xtd",
+ "xte",
+ "xtg",
+ "xti",
+ "xtj",
+ "xtl",
+ "xtm",
+ "xtn",
+ "xto",
+ "xtp",
+ "xtq",
+ "xtr",
+ "xts",
+ "xtt",
+ "xtu",
+ "xtw",
+ "xty",
+ "xtz",
+ "xua",
+ "xub",
+ "xug",
+ "xuj",
+ "xum",
+ "xuo",
+ "xup",
+ "xur",
+ "xut",
+ "xuu",
+ "xve",
+ "xvi",
+ "xvn",
+ "xvo",
+ "xvs",
+ "xwa",
+ "xwc",
+ "xwe",
+ "xwg",
+ "xwl",
+ "xwo",
+ "xwr",
+ "xxb",
+ "xxk",
+ "xxr",
+ "xxt",
+ "xyl",
+ "xzh",
+ "xzm",
+ "xzp",
+ "yaa",
+ "yab",
+ "yac",
+ "yad",
+ "yae",
+ "yaf",
+ "yag",
+ "yah",
+ "yai",
+ "yaj",
+ "yak",
+ "yal",
+ "yam",
+ "yan",
+ "yao",
+ "yap",
+ "yaq",
+ "yar",
+ "yas",
+ "yat",
+ "yau",
+ "yav",
+ "yaw",
+ "yax",
+ "yay",
+ "yaz",
+ "yba",
+ "ybb",
+ "ybd",
+ "ybe",
+ "ybh",
+ "ybi",
+ "ybj",
+ "ybk",
+ "ybl",
+ "ybm",
+ "ybn",
+ "ybo",
+ "ybx",
+ "yby",
+ "ych",
+ "ycl",
+ "ycn",
+ "ycp",
+ "ydd",
+ "yde",
+ "ydg",
+ "ydk",
+ "yds",
+ "yea",
+ "yec",
+ "yee",
+ "yei",
+ "yej",
+ "yel",
+ "yen",
+ "yer",
+ "yes",
+ "yet",
+ "yeu",
+ "yev",
+ "yey",
+ "ygl",
+ "ygm",
+ "ygp",
+ "ygr",
+ "ygw",
+ "yha",
+ "yhd",
+ "yhl",
+ "yi",
+ "yia",
+ "yif",
+ "yig",
+ "yih",
+ "yii",
+ "yij",
+ "yik",
+ "yil",
+ "yim",
+ "yin",
+ "yip",
+ "yiq",
+ "yir",
+ "yis",
+ "yit",
+ "yiu",
+ "yiv",
+ "yix",
+ "yiy",
+ "yiz",
+ "yka",
+ "ykg",
+ "yki",
+ "ykk",
+ "ykl",
+ "ykm",
+ "yko",
+ "ykr",
+ "ykt",
+ "yky",
+ "yla",
+ "ylb",
+ "yle",
+ "ylg",
+ "yli",
+ "yll",
+ "ylm",
+ "yln",
+ "ylo",
+ "ylr",
+ "ylu",
+ "yly",
+ "yma",
+ "ymb",
+ "ymc",
+ "ymd",
+ "yme",
+ "ymg",
+ "ymh",
+ "ymi",
+ "ymk",
+ "yml",
+ "ymm",
+ "ymn",
+ "ymo",
+ "ymp",
+ "ymq",
+ "ymr",
+ "yms",
+ "ymt",
+ "ymx",
+ "ymz",
+ "yna",
+ "ynd",
+ "yne",
+ "yng",
+ "ynh",
+ "ynk",
+ "ynl",
+ "ynn",
+ "yno",
+ "yns",
+ "ynu",
+ "yo",
+ "yob",
+ "yog",
+ "yoi",
+ "yok",
+ "yol",
+ "yom",
+ "yon",
+ "yos",
+ "yox",
+ "yoy",
+ "ypa",
+ "ypb",
+ "ypg",
+ "yph",
+ "ypm",
+ "ypn",
+ "ypo",
+ "ypp",
+ "ypz",
+ "yra",
+ "yrb",
+ "yre",
+ "yri",
+ "yrk",
+ "yrl",
+ "yrn",
+ "yrs",
+ "yrw",
+ "ysc",
+ "ysd",
+ "ysl",
+ "ysn",
+ "yso",
+ "ysp",
+ "ysr",
+ "yss",
+ "ysy",
+ "yta",
+ "ytl",
+ "ytp",
+ "ytw",
+ "yua",
+ "yub",
+ "yuc",
+ "yud",
+ "yue",
+ "yuf",
+ "yug",
+ "yui",
+ "yuj",
+ "yuk",
+ "yul",
+ "yum",
+ "yun",
+ "yup",
+ "yuq",
+ "yur",
+ "yut",
+ "yuu",
+ "yuw",
+ "yux",
+ "yuy",
+ "yuz",
+ "yva",
+ "yvt",
+ "ywa",
+ "ywl",
+ "ywn",
+ "ywq",
+ "ywr",
+ "ywt",
+ "ywu",
+ "yww",
+ "yyu",
+ "yyz",
+ "yzg",
+ "yzk",
+ "za",
+ "zaa",
+ "zab",
+ "zac",
+ "zad",
+ "zae",
+ "zaf",
+ "zag",
+ "zah",
+ "zai",
+ "zaj",
+ "zak",
+ "zal",
+ "zam",
+ "zao",
+ "zap",
+ "zaq",
+ "zar",
+ "zas",
+ "zat",
+ "zau",
+ "zav",
+ "zaw",
+ "zax",
+ "zay",
+ "zaz",
+ "zbc",
+ "zbe",
+ "zbl",
+ "zbt",
+ "zbw",
+ "zca",
+ "zch",
+ "zdj",
+ "zea",
+ "zeg",
+ "zeh",
+ "zen",
+ "zga",
+ "zgb",
+ "zgm",
+ "zgn",
+ "zgr",
+ "zh",
+ "zhb",
+ "zhd",
+ "zhi",
+ "zhn",
+ "zhw",
+ "zia",
+ "zib",
+ "zik",
+ "zil",
+ "zim",
+ "zin",
+ "zir",
+ "ziw",
+ "ziz",
+ "zka",
+ "zkb",
+ "zkg",
+ "zkh",
+ "zkk",
+ "zko",
+ "zkp",
+ "zkr",
+ "zkt",
+ "zku",
+ "zkv",
+ "zkz",
+ "zlj",
+ "zlm",
+ "zln",
+ "zlq",
+ "zma",
+ "zmb",
+ "zmc",
+ "zmd",
+ "zme",
+ "zmf",
+ "zmg",
+ "zmh",
+ "zmi",
+ "zmj",
+ "zmk",
+ "zml",
+ "zmm",
+ "zmn",
+ "zmo",
+ "zmp",
+ "zmq",
+ "zmr",
+ "zms",
+ "zmt",
+ "zmu",
+ "zmv",
+ "zmw",
+ "zmx",
+ "zmy",
+ "zmz",
+ "zna",
+ "zne",
+ "zng",
+ "znk",
+ "zns",
+ "zoc",
+ "zoh",
+ "zom",
+ "zoo",
+ "zoq",
+ "zor",
+ "zos",
+ "zpa",
+ "zpb",
+ "zpc",
+ "zpd",
+ "zpe",
+ "zpf",
+ "zpg",
+ "zph",
+ "zpi",
+ "zpj",
+ "zpk",
+ "zpl",
+ "zpm",
+ "zpn",
+ "zpo",
+ "zpp",
+ "zpq",
+ "zpr",
+ "zps",
+ "zpt",
+ "zpu",
+ "zpv",
+ "zpw",
+ "zpx",
+ "zpy",
+ "zpz",
+ "zqe",
+ "zra",
+ "zrg",
+ "zrn",
+ "zro",
+ "zrp",
+ "zrs",
+ "zsa",
+ "zsk",
+ "zsl",
+ "zsm",
+ "zsr",
+ "zsu",
+ "zte",
+ "ztg",
+ "ztl",
+ "ztm",
+ "ztn",
+ "ztp",
+ "ztq",
+ "zts",
+ "ztt",
+ "ztu",
+ "ztx",
+ "zty",
+ "zu",
+ "zua",
+ "zuh",
+ "zum",
+ "zun",
+ "zuy",
+ "zwa",
+ "zxx",
+ "zyb",
+ "zyg",
+ "zyj",
+ "zyn",
+ "zyp",
+ "zza",
+ "zzj",
+))
+
+COUNTRIES = set((
+ "AD",
+ "AE",
+ "AF",
+ "AG",
+ "AI",
+ "AL",
+ "AM",
+ "AO",
+ "AQ",
+ "AR",
+ "AS",
+ "AT",
+ "AU",
+ "AW",
+ "AX",
+ "AZ",
+ "BA",
+ "BB",
+ "BD",
+ "BE",
+ "BF",
+ "BG",
+ "BH",
+ "BI",
+ "BJ",
+ "BL",
+ "BM",
+ "BN",
+ "BO",
+ "BQ",
+ "BR",
+ "BS",
+ "BT",
+ "BV",
+ "BW",
+ "BY",
+ "BZ",
+ "CA",
+ "CC",
+ "CD",
+ "CF",
+ "CG",
+ "CH",
+ "CI",
+ "CK",
+ "CL",
+ "CM",
+ "CN",
+ "CO",
+ "CR",
+ "CU",
+ "CV",
+ "CW",
+ "CX",
+ "CY",
+ "CZ",
+ "DE",
+ "DJ",
+ "DK",
+ "DM",
+ "DO",
+ "DZ",
+ "EC",
+ "EE",
+ "EG",
+ "EH",
+ "ER",
+ "ES",
+ "ET",
+ "FI",
+ "FJ",
+ "FK",
+ "FM",
+ "FO",
+ "FR",
+ "GA",
+ "GB",
+ "GD",
+ "GE",
+ "GF",
+ "GG",
+ "GH",
+ "GI",
+ "GL",
+ "GM",
+ "GN",
+ "GP",
+ "GQ",
+ "GR",
+ "GS",
+ "GT",
+ "GU",
+ "GW",
+ "GY",
+ "HK",
+ "HM",
+ "HN",
+ "HR",
+ "HT",
+ "HU",
+ "ID",
+ "IE",
+ "IL",
+ "IM",
+ "IN",
+ "IO",
+ "IQ",
+ "IR",
+ "IS",
+ "IT",
+ "JE",
+ "JM",
+ "JO",
+ "JP",
+ "KE",
+ "KG",
+ "KH",
+ "KI",
+ "KM",
+ "KN",
+ "KP",
+ "KR",
+ "KW",
+ "KY",
+ "KZ",
+ "LA",
+ "LB",
+ "LC",
+ "LI",
+ "LK",
+ "LR",
+ "LS",
+ "LT",
+ "LU",
+ "LV",
+ "LY",
+ "MA",
+ "MC",
+ "MD",
+ "ME",
+ "MF",
+ "MG",
+ "MH",
+ "MK",
+ "ML",
+ "MM",
+ "MN",
+ "MO",
+ "MP",
+ "MQ",
+ "MR",
+ "MS",
+ "MT",
+ "MU",
+ "MV",
+ "MW",
+ "MX",
+ "MY",
+ "MZ",
+ "NA",
+ "NC",
+ "NE",
+ "NF",
+ "NG",
+ "NI",
+ "NL",
+ "NO",
+ "NP",
+ "NR",
+ "NU",
+ "NZ",
+ "OM",
+ "PA",
+ "PE",
+ "PF",
+ "PG",
+ "PH",
+ "PK",
+ "PL",
+ "PM",
+ "PN",
+ "PR",
+ "PS",
+ "PT",
+ "PW",
+ "PY",
+ "QA",
+ "RE",
+ "RO",
+ "RS",
+ "RU",
+ "RW",
+ "SA",
+ "SB",
+ "SC",
+ "SD",
+ "SE",
+ "SG",
+ "SH",
+ "SI",
+ "SJ",
+ "SK",
+ "SL",
+ "SM",
+ "SN",
+ "SO",
+ "SR",
+ "SS",
+ "ST",
+ "SV",
+ "SX",
+ "SY",
+ "SZ",
+ "TC",
+ "TD",
+ "TF",
+ "TG",
+ "TH",
+ "TJ",
+ "TK",
+ "TL",
+ "TM",
+ "TN",
+ "TO",
+ "TR",
+ "TT",
+ "TV",
+ "TW",
+ "TZ",
+ "UA",
+ "UG",
+ "UM",
+ "US",
+ "UY",
+ "UZ",
+ "VA",
+ "VC",
+ "VE",
+ "VG",
+ "VI",
+ "VN",
+ "VU",
+ "WF",
+ "WS",
+ "YE",
+ "YT",
+ "ZA",
+ "ZM",
+ "ZW",
+))
diff --git a/__version__.py b/__version__.py
new file mode 100644
index 0000000..92d92ba
--- /dev/null
+++ b/__version__.py
@@ -0,0 +1,2 @@
+# Automatically generated, do not edit
+__version__ = '1.4'
diff --git a/config b/config
new file mode 100644
index 0000000..5631260
--- /dev/null
+++ b/config
@@ -0,0 +1,214 @@
+# -*- python -*-
+
+# Example configuration file for rpmlint.
+
+# This line is mandatory to access the configuration functions
+from Config import *
+
+# Additional paths to look for checks.
+# ------------------------------------
+
+#addCheckDir("~/mandrake/rpmlint")
+
+# Configure the checks if you don't want the default ones.
+# --------------------------------------------------------
+
+#addCheck("FHSCheck")
+#addCheck("BinariesCheck")
+
+# Configuration options used by the checks shipped with rpmlint.
+# The values in the commented out setOption() calls represent default
+# or typical example values for the option.
+# -------------------------------------------------------------------
+
+# Type: integer, default: -1 (less than 0 means disabled)
+#setOption("BadnessThreshold", -1)
+
+# When checking that various files that should be compressed are
+# indeed compressed, look for this filename extension (no dot here).
+# Type: string, default: "bz2"
+#setOption("CompressExtension", "bz2")
+
+# Exception list for dangling symlink checks. The first in each pair
+# is a regexp, and the second the package in which the target of the
+# dangling symlink is shipped.
+# Type: tuple of lists, default: (['consolehelper$', 'usermode-consoleonly'])
+#setOption("DanglingSymlinkExceptions", ())
+
+# Value for the Distribution tag.
+# Type: string, default: "" (the empty string disables checking)
+#setOption("Distribution", "")
+
+# Base directory where to extract uninstalled packages while checking.
+# Type: string, default: tempfile.gettempdir()
+#setOption("ExtractDir", "/tmp")
+
+# Standard "needs" values for non-XDG legacy menu items.
+# Type: tuple of strings, default: ('gnome', 'icewm', 'kde', 'wmaker')
+#setOption("ExtraMenuNeeds", ('gnome', 'icewm', 'kde', 'wmaker'))
+
+# Words that must not exist in various tag values.
+# Type: regexp, default: '' ('' effectively disables this check)
+#setOption("ForbiddenWords", '')
+
+# Exceptions for hardcoded library paths.
+# Type: regexp, default: see DEFAULT_HARDCODED_LIB_PATH_EXCEPTIONS in SpecCheck
+#setOption("HardcodedLibPathExceptions", '/lib/modules/')
+
+# Accepted non-XDG legacy icon filenames.
+# Type: regexp, default: '.*\.png$'
+#setOption("IconFilename", '.*\.png$')
+
+# Paths in which non-XDG legacy icons should be installed. The first
+# item in the tuples is a filesystem path, and the second the icon type.
+# Type: tuple of string tuples, default: see DEFAULT_ICON_PATH in MenuCheck
+#setOption("IconPath", ())
+
+# Disallowed dependencies.
+# Type: tuple of regexps, default: see DEFAULT_INVALID_REQUIRES in TagsCheck
+#setOption("InvalidRequires", ())
+
+# Strings to disallow in various URL tags.
+# Type: regexp, default: '' ('' effectively disables this check)
+#setOption("InvalidURL", '')
+
+# Whether to allow packaging kernel modules in non-kernel packages.
+# Type: boolean, default: True
+#setOption("KernelModuleRPMsOK", True)
+
+# Maximum line length for summaries and descriptions.
+# Type: integer, default: 79
+#setOption("MaxLineLength", 79)
+
+# Type: tuple of string,tuple lists, default: see DEFAULT_LAUNCHERS in MenuCheck
+#setOption("MenuLaunchers", ())
+
+# Names of packages to treat as "meta" ones.
+# Type: regexp, default: '^(bundle|task)-'
+#setOption("MetaPackageRegexp", '^(bundle|task)-')
+
+# Whether to enable checks that require networking.
+# Type: boolean, default: False
+#setOption("NetworkEnabled", False)
+
+# Timeout for network operations in seconds.
+# Type: integer, default: 10
+#setOption("NetworkTimeout", 10)
+
+# Value for the Packager tag.
+# Type: regexp, default: '' ('' effectively disables this check)
+#setOption("Packager", '')
+
+# Type: boolean, default: True
+#setOption("PerlVersionTrick", True)
+
+# Assumed default version of Python if one cannot be determined from files.
+# Type: string, default: None
+#setOption("PythonDefaultVersion", None)
+
+# Expected suffix in Release tags.
+# Type: regexp, default: '' ('' effectively disables this check)
+#setOption("ReleaseExtension", '')
+
+# Group tag for games.
+# Type: regexp, default: 'Games'
+#setOption("RpmGamesGroup", 'Games')
+
+# Doc files to which end of line and UTF-8 checks should not be applied.
+# Type: regexp, default: \.(?:rtf|x?html?|svg|ml[ily]?)$'
+#setOption("SkipDocsRegexp", '\.(?:rtf|x?html?|svg|ml[ily]?)$')
+
+# Standard OS groups.
+# Type: tuple of strings, default: see DEFAULT_STANDARD_GROUPS in FilesCheck
+#setOption("StandardGroups", ())
+
+# Standard OS users.
+# Type: tuple of strings, see DEFAULT_STANDARD_USERS in FilesCheck
+#setOption("StandardUsers", ())
+
+# List of directories considered to be system default library search paths.
+# Type: tuple of strings, default: see DEFAULT_SYSTEM_LIB_PATHS in BinariesCheck
+#setOption("SystemLibPaths", ('/lib', '/lib64', '/usr/lib', '/usr/lib64'))
+
+# Executables that must be compiled as position independent.
+# Type: regex, default: None
+#setOption("PieExecutables", '^/bin/(ping6?|su)$')
+
+# Whether to want default start/stop runlevels specified in init scripts.
+# Type: boolean, default: True
+#setOption("UseDefaultRunlevels", True)
+
+# Whether to use the Enchant spell checker (if available) for spell checking.
+# Type: boolean, default: True
+#setOption("UseEnchant", True)
+
+# Whether an explicit Epoch should always be specified.
+# Type: boolean, default: False
+#setOption("UseEpoch", False)
+
+# Whether jars should be indexed.
+# Type: boolean, default: True
+#setOption("UseIndexedJars", True)
+
+# Whether symlinks between directories should be relative.
+# Type: boolean, default: True
+#setOption("UseRelativeSymlinks", True)
+
+# Whether the UTF-8 character encoding should be used where applicable.
+# Type: boolean, default: autodetected from environment
+#setOption("UseUTF8", True)
+
+# Whether %changelog entries should contain a version.
+# Type: boolean, default: True
+#setOption("UseVersionInChangelog", True)
+
+# Whether init scripts must use /var/lock/subsys
+# Type: boolean, default: True
+#setOption("UseVarLockSubsys", True)
+
+# Architecture dependent paths in which packages are allowed to install files
+# even if they are all non-binary.
+# Type: regexp, default: see BinariesCheck
+#setOption("UsrLibBinaryException", '^/usr/lib(64)?/(perl|python|ruby)')
+
+# Value for the BuildHost tag.
+# Type: regexp, default '' ('' effectively disables this check)
+#setOption("ValidBuildHost", '')
+
+# Interpreters whose scriptlets are allowed to be empty.
+# Type: tuple of strings, default: ('/sbin/ldconfig',)
+#setOption("ValidEmptyShells", ('/sbin/ldconfig',))
+
+# Values for the Group tag.
+# Type: list of strings, default: extracted from GROUPS file shipped with rpm
+#setOption("ValidGroups", [])
+
+# Values for the License tag.
+# Type: tuple of strings, default: see DEFAULT_VALID_LICENSES in TagsCheck
+#setOption("ValidLicenses", ())
+
+# Values for non-XDG legacy menu item sections.
+# Type: tuple of strings, default: see DEFAULT_VALID_SECTIONS in MenuCheck
+#setOption("ValidMenuSections", ())
+
+# Package scriptlet interpreters.
+# Type: tuple of strings, default: see DEFAULT_VALID_SHELLS in PostCheck
+#setOption("ValidShells", ('/bin/sh', '/bin/bash'))
+
+# Permissions for files in source packages.
+# Type: tuple of modes, default: (0644, 0755)
+#setOption("ValidSrcPerms", (0644, 0755))
+
+# Value for the Vendor tag.
+# Type: string, default: "" ("" effectively disables this check)
+#setOption("Vendor", "")
+
+# Man page warning category, passed to groff -w while checking man pages.
+# See the groff(1) or troff(1) man pages for available categories.
+# Type: string, default: 'mac'
+#SetOption("ManWarningCategory", 'mac')
+
+# Output filters.
+# ---------------
+
+#addFilter("E: .* no-signature")
diff --git a/rpmdiff b/rpmdiff
new file mode 100755
index 0000000..00c6787
--- /dev/null
+++ b/rpmdiff
@@ -0,0 +1,294 @@
+#!/usr/bin/python -tt
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2006 Mandriva; 2009 Red Hat, Inc.; 2009 Ville Skyttä
+# Authors: Frederic Lepied, Florian Festi
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU Library General Public License as published by
+# the Free Software Foundation; version 2 only
+#
+# 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 Library General Public License for more details.
+#
+# You should have received a copy of the GNU Library General Public License
+# along with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import getopt
+import itertools
+import os
+import site
+import stat
+import sys
+import tempfile
+
+import rpm
+
+if os.path.isdir("/usr/share/rpmlint"):
+ site.addsitedir("/usr/share/rpmlint")
+import Pkg
+
+
+class Rpmdiff:
+
+ # constants
+
+ TAGS = ( rpm.RPMTAG_NAME, rpm.RPMTAG_SUMMARY,
+ rpm.RPMTAG_DESCRIPTION, rpm.RPMTAG_GROUP,
+ rpm.RPMTAG_LICENSE, rpm.RPMTAG_URL,
+ rpm.RPMTAG_PREIN, rpm.RPMTAG_POSTIN,
+ rpm.RPMTAG_PREUN, rpm.RPMTAG_POSTUN,
+ rpm.RPMTAG_PRETRANS, rpm.RPMTAG_POSTTRANS)
+
+ PRCO = ( 'REQUIRES', 'PROVIDES', 'CONFLICTS', 'OBSOLETES')
+
+ #{fname : (size, mode, mtime, flags, dev, inode,
+ # nlink, state, vflags, user, group, digest)}
+ __FILEIDX = [ ['S', 0],
+ ['M', 1],
+ ['5', 11],
+ ['D', 4],
+ ['N', 6],
+ ['L', 7],
+ ['V', 8],
+ ['U', 9],
+ ['G', 10],
+ ['F', 3],
+ ['T', 2] ]
+
+ DEPFORMAT = '%-12s%s %s %s %s'
+ FORMAT = '%-12s%s'
+
+ ADDED = 'added'
+ REMOVED = 'removed'
+
+ # code starts here
+
+ def __init__(self, old, new, ignore=None):
+ self.result = []
+ self.ignore = ignore
+ if self.ignore is None:
+ self.ignore = []
+
+ FILEIDX = self.__FILEIDX
+ for tag in self.ignore:
+ for entry in FILEIDX:
+ if tag == entry[0]:
+ entry[1] = None
+ break
+
+ try:
+ old = self.__load_pkg(old).header
+ new = self.__load_pkg(new).header
+ except KeyError, e:
+ Pkg.warn(str(e))
+ sys.exit(2)
+
+ # Compare single tags
+ for tag in self.TAGS:
+ old_tag = old[tag]
+ new_tag = new[tag]
+ if old_tag != new_tag:
+ tagname = rpm.tagnames[tag]
+ if old_tag == None:
+ self.__add(self.FORMAT, (self.ADDED, tagname))
+ elif new_tag == None:
+ self.__add(self.FORMAT, (self.REMOVED, tagname))
+ else:
+ self.__add(self.FORMAT, ('S.5.....', tagname))
+
+ # compare Provides, Requires, ...
+ for tag in self.PRCO:
+ self.__comparePRCOs(old, new, tag)
+
+ # compare the files
+
+ old_files_dict = self.__fileIteratorToDict(old.fiFromHeader())
+ new_files_dict = self.__fileIteratorToDict(new.fiFromHeader())
+ files = list(set(itertools.chain(old_files_dict.iterkeys(),
+ new_files_dict.iterkeys())))
+ files.sort()
+
+ for f in files:
+ diff = False
+
+ old_file = old_files_dict.get(f)
+ new_file = new_files_dict.get(f)
+
+ if not old_file:
+ self.__add(self.FORMAT, (self.ADDED, f))
+ elif not new_file:
+ self.__add(self.FORMAT, (self.REMOVED, f))
+ else:
+ format = ''
+ for entry in FILEIDX:
+ if entry[1] != None and \
+ old_file[entry[1]] != new_file[entry[1]]:
+ format = format + entry[0]
+ diff = True
+ else:
+ format = format + '.'
+ if diff:
+ self.__add(self.FORMAT, (format, f))
+
+ # return a report of the differences
+ def textdiff(self):
+ return '\n'.join((format % data for format, data in self.result))
+
+ # do the two rpms differ
+ def differs(self):
+ return bool(self.result)
+
+ # add one differing item
+ def __add(self, format, data):
+ self.result.append((format, data))
+
+ # load a package from a file or from the installed ones
+ def __load_pkg(self, name, tmpdir = tempfile.gettempdir()):
+ try:
+ st = os.stat(name)
+ if stat.S_ISREG(st[stat.ST_MODE]):
+ return Pkg.Pkg(name, tmpdir)
+ except (OSError, TypeError):
+ pass
+ inst = Pkg.getInstalledPkgs(name)
+ if not inst:
+ raise KeyError("No installed packages by name %s" % name)
+ if len(inst) > 1:
+ raise KeyError("More than one installed packages by name %s" % name)
+ return inst[0]
+
+ # output the right string according to RPMSENSE_* const
+ def sense2str(self, sense):
+ s = ""
+ for tag, char in ((rpm.RPMSENSE_LESS, "<"),
+ (rpm.RPMSENSE_GREATER, ">"),
+ (rpm.RPMSENSE_EQUAL, "=")):
+ if sense & tag:
+ s += char
+ return s
+
+ # output the right requires string according to RPMSENSE_* const
+ def req2str(self, req):
+ s = "REQUIRES"
+ # we want to use 64 even with rpm versions that define RPMSENSE_PREREQ
+ # as 0 to get sane results when comparing packages built with an old
+ # (64) version and a new (0) one
+ if req & (rpm.RPMSENSE_PREREQ or 64):
+ s = "PREREQ"
+
+ ss = []
+ if req & rpm.RPMSENSE_SCRIPT_PRE:
+ ss.append("pre")
+ elif req & rpm.RPMSENSE_SCRIPT_POST:
+ ss.append("post")
+ elif req & rpm.RPMSENSE_SCRIPT_PREUN:
+ ss.append("preun")
+ elif req & rpm.RPMSENSE_SCRIPT_POSTUN:
+ ss.append("postun")
+ elif req & getattr(rpm, "RPMSENSE_PRETRANS", 1 << 7): # rpm >= 4.9.0
+ ss.append("pretrans")
+ elif req & getattr(rpm, "RPMSENSE_POSTTRANS", 1 << 5): # rpm >= 4.9.0
+ ss.append("posttrans")
+ if ss:
+ s += "(%s)" % ",".join(ss)
+
+ return s
+
+ # compare Provides, Requires, Conflicts, Obsoletes
+ def __comparePRCOs(self, old, new, name):
+ oldflags = old[name[:-1]+'FLAGS']
+ newflags = new[name[:-1]+'FLAGS']
+ # fix buggy rpm binding not returning list for single entries
+ if not isinstance(oldflags, list): oldflags = [ oldflags ]
+ if not isinstance(newflags, list): newflags = [ newflags ]
+
+ o = zip(old[name], oldflags, old[name[:-1]+'VERSION'])
+ n = zip(new[name], newflags, new[name[:-1]+'VERSION'])
+
+ # filter self provides, TODO: self %name(%_isa) as well
+ if name == 'PROVIDES':
+ oldE = old['epoch'] is not None and str(old['epoch'])+":" or ""
+ oldNV = (old['name'], rpm.RPMSENSE_EQUAL,
+ "%s%s-%s" % (oldE, old['version'], old['release']))
+ newE = new['epoch'] is not None and str(new['epoch'])+":" or ""
+ newNV = (new['name'], rpm.RPMSENSE_EQUAL,
+ "%s%s-%s" % (newE, new['version'], new['release']))
+ o = [entry for entry in o if entry != oldNV]
+ n = [entry for entry in n if entry != newNV]
+
+ for oldentry in o:
+ if not oldentry in n:
+ namestr = name
+ if namestr == 'REQUIRES':
+ namestr = self.req2str(oldentry[1])
+ self.__add(self.DEPFORMAT,
+ (self.REMOVED, namestr, oldentry[0],
+ self.sense2str(oldentry[1]), oldentry[2]))
+ for newentry in n:
+ if not newentry in o:
+ namestr = name
+ if namestr == 'REQUIRES':
+ namestr = self.req2str(newentry[1])
+ self.__add(self.DEPFORMAT,
+ (self.ADDED, namestr, newentry[0],
+ self.sense2str(newentry[1]), newentry[2]))
+
+ def __fileIteratorToDict(self, fi):
+ result = {}
+ for filedata in fi:
+ result[filedata[0]] = filedata[1:]
+ return result
+
+def _usage(exit=1):
+ print ('''Usage: %s [<options>] <old package> <new package>
+Options:
+ -h, --help Output this message and exit
+ -i, --ignore File property to ignore when calculating differences (may be
+ used multiple times); valid values are: S (size), M (mode),
+ 5 (checksum), D (device), N (inode), L (number of links),
+ V (vflags), U (user), G (group), F (digest), T (time)''' \
+ % sys.argv[0])
+ sys.exit(exit)
+
+def main():
+
+ ignore_tags = []
+ try:
+ opts, args = getopt.getopt(sys.argv[1:],
+ "hti:", ["help", "ignore-times", "ignore="])
+ except getopt.GetoptError, e:
+ Pkg.warn("Error: %s" % e)
+ _usage()
+
+ for option, argument in opts:
+ if option in ("-h", "--help"):
+ _usage(0)
+ if option in ("-t", "--ignore-times"):
+ # deprecated; --ignore=T should be used instead
+ ignore_tags.append("T")
+ if option in ("-i", "--ignore"):
+ ignore_tags.append(argument)
+
+ if len(args) != 2:
+ _usage()
+
+ d = Rpmdiff(args[0], args[1], ignore=ignore_tags)
+ textdiff = d.textdiff()
+ if textdiff:
+ print (textdiff)
+ sys.exit(int(d.differs()))
+
+if __name__ == '__main__':
+ main()
+
+# rpmdiff ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/rpmlint b/rpmlint
new file mode 100755
index 0000000..4838014
--- /dev/null
+++ b/rpmlint
@@ -0,0 +1,387 @@
+#!/usr/bin/python -ttOu
+# -*- coding: utf-8 -*-
+#############################################################################
+# File : rpmlint
+# Package : rpmlint
+# Author : Frederic Lepied
+# Created on : Mon Sep 27 19:20:18 1999
+# Version : $Id: rpmlint 1870 2011-06-18 09:19:24Z scop $
+# Purpose : main entry point: process options, load the checks and run
+# the checks.
+#############################################################################
+
+import getopt
+import glob
+import imp
+import locale
+import os
+import re
+import stat
+import sys
+import tempfile
+
+# 1 instead of 0 here because we want the script dir to be looked up first,
+# e.g. for in-place from tarball or SCM checkout
+sys.path.insert(1, '/usr/share/rpmlint')
+
+# Do not import anything that initializes its global variables from
+# Config at load time here (or anything that imports such a thing),
+# that results in those variables initialized before config files are
+# loaded which is too early - settings from config files won't take
+# place for those variables.
+
+from Filter import badnessScore, badnessThreshold, printAllReasons, \
+ printDescriptions, printInfo, printed_messages, setRawOut
+import AbstractCheck
+import Config
+import Pkg
+
+
+_default_user_conf = '%s/rpmlint' % \
+ (os.environ.get('XDG_CONFIG_HOME') or '~/.config')
+
+# Print usage information
+def usage(name):
+ print ('''usage: %s [<options>] <rpm files|installed packages|specfiles|dirs>
+ options:
+\t[-i|--info]
+\t[-I|--explain <messageid>]
+\t[-c|--check <check>]
+\t[-a|--all]
+\t[-C|--checkdir <checkdir>]
+\t[-h|--help]
+\t[-v|--verbose]
+\t[-E|--extractdir <dir>]
+\t[-V|--version]
+\t[-n|--noexception]
+\t[ --rawout <file>]
+\t[-f|--file <user config file to use instead of %s]
+\t[-o|--option <key value>]''' \
+ % (name, _default_user_conf))
+
+# Print version information
+def printVersion():
+ print ('rpmlint version %s Copyright (C) 1999-2007 Frederic Lepied, Mandriva' % Config.__version__)
+
+def loadCheck(name):
+ '''Load a (check) module by its name, unless it is already loaded.'''
+ # Avoid loading more than once (initialization costs)
+ loaded = sys.modules.get(name)
+ if loaded:
+ return loaded
+ (fobj, pathname, description) = imp.find_module(name)
+ try:
+ imp.load_module(name, fobj, pathname, description)
+ finally:
+ fobj.close()
+
+#############################################################################
+# main program
+#############################################################################
+def main():
+
+ locale.setlocale(locale.LC_COLLATE, '')
+
+ # Add check dirs to the front of load path
+ sys.path[0:0] = Config.checkDirs()
+
+ # Load all checks
+ for c in Config.allChecks():
+ loadCheck(c)
+
+ packages_checked = 0
+ specfiles_checked = 0
+ do_spec_check = 'SpecCheck' in Config.allChecks()
+ if do_spec_check:
+ # See comments in "top level import section" for why this isn't
+ # imported earlier.
+ import SpecCheck
+
+ try:
+ # Loop over all file names given in arguments
+ dirs = []
+ for arg in args:
+ pkgs = []
+ isfile = False
+ try:
+ if arg == "-":
+ arg = "(standard input)"
+ # Short-circuit stdin spec file check
+ if do_spec_check:
+ stdin = sys.stdin.readlines()
+ if not stdin:
+ continue
+ pkg = Pkg.FakePkg(arg)
+ check = SpecCheck.SpecCheck()
+ check.verbose = verbose
+ check.check_spec(pkg, None, spec_lines=stdin)
+ pkg.cleanup()
+ specfiles_checked += 1
+ continue
+
+ try:
+ st = os.stat(arg)
+ isfile = True
+ if stat.S_ISREG(st[stat.ST_MODE]):
+ if arg.endswith(".spec"):
+ if do_spec_check:
+ # Short-circuit spec file checks
+ pkg = Pkg.FakePkg(arg)
+ check = SpecCheck.SpecCheck()
+ check.verbose = verbose
+ check.check_spec(pkg, arg)
+ pkg.cleanup()
+ specfiles_checked += 1
+ elif "/" in arg or arg.endswith(".rpm") or \
+ arg.endswith(".spm"):
+ pkgs.append(Pkg.Pkg(arg, extract_dir))
+ else:
+ raise OSError
+
+ elif stat.S_ISDIR(st[stat.ST_MODE]):
+ dirs.append(arg)
+ continue
+ else:
+ raise OSError
+ except OSError:
+ ipkgs = Pkg.getInstalledPkgs(arg)
+ if not ipkgs:
+ Pkg.warn(
+ '(none): E: no installed packages by name %s' % arg)
+ else:
+ ipkgs.sort(key = lambda x: locale.strxfrm(
+ x.header.sprintf("%{NAME}.%{ARCH}")))
+ pkgs.extend(ipkgs)
+ except KeyboardInterrupt:
+ if isfile:
+ arg = os.path.abspath(arg)
+ Pkg.warn(
+ '(none): E: interrupted, exiting while reading %s' % arg)
+ sys.exit(2)
+ except Exception, e:
+ if isfile:
+ arg = os.path.abspath(arg)
+ Pkg.warn('(none): E: error while reading %s: %s' % (arg, e))
+ pkgs = []
+ continue
+
+ for pkg in pkgs:
+ runChecks(pkg)
+ packages_checked += 1
+
+ for dname in dirs:
+ try:
+ for path, dirs, files in os.walk(dname):
+ for fname in files:
+ fname = os.path.abspath(os.path.join(path, fname))
+ try:
+ if fname.endswith('.rpm') or \
+ fname.endswith('.spm'):
+ pkg = Pkg.Pkg(fname, extract_dir)
+ runChecks(pkg)
+ packages_checked += 1
+
+ elif do_spec_check and fname.endswith('.spec'):
+ pkg = Pkg.FakePkg(fname)
+ check = SpecCheck.SpecCheck()
+ check.verbose = verbose
+ check.check_spec(pkg, fname)
+ pkg.cleanup()
+ specfiles_checked += 1
+
+ except KeyboardInterrupt:
+ Pkg.warn('(none): E: interrupted, exiting while ' +
+ 'reading %s' % fname)
+ sys.exit(2)
+ except Exception, e:
+ Pkg.warn(
+ '(none): E: while reading %s: %s' % (fname, e))
+ continue
+ except Exception, e:
+ Pkg.warn(
+ '(none): E: error while reading dir %s: %s' % (dname, e))
+ continue
+
+ if printAllReasons():
+ Pkg.warn('(none): E: badness %d exceeds threshold %d, aborting.' %
+ (badnessScore(), badnessThreshold()))
+ sys.exit(66)
+
+ finally:
+ print "%d packages and %d specfiles checked; %d errors, %d warnings." \
+ % (packages_checked, specfiles_checked,
+ printed_messages["E"], printed_messages["W"])
+
+ if printed_messages["E"] > 0:
+ sys.exit(64)
+ sys.exit(0)
+
+def runChecks(pkg):
+
+ try:
+ if verbose:
+ printInfo(pkg, 'checking')
+
+ for name in Config.allChecks():
+ check = AbstractCheck.AbstractCheck.known_checks.get(name)
+ if check:
+ check.verbose = verbose
+ check.check(pkg)
+ else:
+ Pkg.warn('(none): W: unknown check %s, skipping' % name)
+ finally:
+ pkg.cleanup()
+
+#############################################################################
+#
+#############################################################################
+
+sys.argv[0] = os.path.basename(sys.argv[0])
+
+# parse options
+try:
+ (opt, args) = getopt.getopt(sys.argv[1:],
+ 'iI:c:C:hVvanE:f:o:',
+ ['info',
+ 'explain=',
+ 'check=',
+ 'checkdir=',
+ 'help',
+ 'version',
+ 'verbose',
+ 'all',
+ 'noexception',
+ 'extractdir=',
+ 'file=',
+ 'option=',
+ 'rawout=',
+ ])
+except getopt.GetoptError, e:
+ Pkg.warn("%s: %s" % (sys.argv[0], e))
+ usage(sys.argv[0])
+ sys.exit(1)
+
+# process options
+checkdir = '/usr/share/rpmlint'
+checks = []
+verbose = False
+extract_dir = None
+conf_file = _default_user_conf
+if not os.path.exists(os.path.expanduser(conf_file)):
+ # deprecated backwards compatibility with < 0.88
+ conf_file = '~/.rpmlintrc'
+info_error = set()
+
+# load global config files
+configs = glob.glob('/etc/rpmlint/*config')
+configs.sort()
+
+# Was rpmlint invoked as a prefixed variant?
+m = re.match(r"(?P<prefix>[\w-]+)-rpmlint(\.py)?", sys.argv[0])
+if m:
+ # Okay, we're a prefixed variant. Look for the variant config.
+ # If we find it, use it. If not, fallback to the default.
+ prefix = m.group('prefix')
+ if os.path.isfile('/usr/share/rpmlint/config.%s' % prefix):
+ configs.insert(0, '/usr/share/rpmlint/config.%s' % prefix)
+ else:
+ configs.insert(0, '/usr/share/rpmlint/config')
+else:
+ configs.insert(0, '/usr/share/rpmlint/config')
+
+for f in configs:
+ try:
+ execfile(f)
+ except IOError:
+ pass
+ except Exception, E:
+ Pkg.warn('(none): W: error loading %s, skipping: %s' % (f, E))
+# pychecker fix
+del f
+
+config_overrides = {}
+
+# process command line options
+for o in opt:
+ if o[0] in ('-c', '--check'):
+ checks.append(o[1])
+ elif o[0] in ('-i', '--info'):
+ Config.info = True
+ elif o[0] in ('-I', '--explain'):
+ # split by comma for deprecated backwards compatibility with < 1.2
+ info_error.update(o[1].split(','))
+ elif o[0] in ('-h', '--help'):
+ usage(sys.argv[0])
+ sys.exit(0)
+ elif o[0] in ('-C', '--checkdir'):
+ Config.addCheckDir(o[1])
+ elif o[0] in ('-v', '--verbose'):
+ verbose = True
+ elif o[0] in ('-V', '--version'):
+ printVersion()
+ sys.exit(0)
+ elif o[0] in ('-E', '--extractdir'):
+ extract_dir = o[1]
+ Config.setOption('ExtractDir', extract_dir)
+ elif o[0] in ('-n', '--noexception'):
+ Config.no_exception = True
+ elif o[0] in ('-a', '--all'):
+ if '*' not in args:
+ args.append('*')
+ elif o[0] in ('-f', '--file'):
+ conf_file = o[1]
+ elif o[0] in ('-o', '--option'):
+ kv = o[1].split(None, 1)
+ if len(kv) == 1:
+ config_overrides[kv[0]] = None
+ else:
+ config_overrides[kv[0]] = eval(kv[1])
+ elif o[0] in ('--rawout',):
+ setRawOut(o[1])
+
+# load user config file
+try:
+ execfile(os.path.expanduser(conf_file))
+except IOError:
+ pass
+except Exception,E:
+ Pkg.warn('(none): W: error loading %s, skipping: %s' % (conf_file, E))
+
+# apply config overrides
+for key, value in config_overrides.items():
+ Config.setOption(key, value)
+
+if not extract_dir:
+ extract_dir = Config.getOption('ExtractDir', tempfile.gettempdir())
+
+if info_error:
+ Config.info = True
+ sys.path[0:0] = Config.checkDirs()
+ for c in checks:
+ Config.addCheck(c)
+ for c in Config.allChecks():
+ loadCheck(c)
+ for e in sorted(info_error):
+ print "%s:" % e
+ printDescriptions(e)
+ sys.exit(0)
+
+# if no argument print usage
+if not args:
+ usage(sys.argv[0])
+ sys.exit(1)
+
+if __name__ == '__main__':
+ if checks:
+ Config.resetChecks()
+ for check in checks:
+ Config.addCheck(check)
+ main()
+
+# rpmlint ends here
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/rpmlint.1 b/rpmlint.1
new file mode 100644
index 0000000..7e7794c
--- /dev/null
+++ b/rpmlint.1
@@ -0,0 +1,115 @@
+.TH RPMLINT "1" "April 2011" "rpmlint" "User Commands"
+.SH NAME
+rpmlint \- check common problems in rpm packages
+.SH SYNOPSIS
+\fBrpmlint\fR [\fIOPTION\fR]... [\fIFILE\fR|\fIPACKAGE\fR]...
+.SH DESCRIPTION
+\fBrpmlint\fR is a tool for checking common errors in rpm packages.
+It can be used to test individual packages and spec files before
+uploading or to check an entire distribution. By default all
+applicable checks are processed but specific checks can be performed
+by using command line parameters.
+
+\fIFILE\fR can be a rpm package file, a spec file, or a directory. In
+case of a directory, it is recursively searched for rpm and spec files
+to check. The special value \fB\-\fR results in standard input being
+read and treated as (single) spec file content.
+\fIPACKAGE\fR is the name of an installed package or a
+.BR glob (7)
+pattern to match installed packages, unless a file by that name exists.
+.TP
+\fB\-i\fR, \fB\-\-info\fR
+Display explanations for reported messages.
+.TP
+\fB-I\fR, \fB\-\-explain\fR=\fImessageid\fR
+Display explanations for the specified message identifiers and exit.
+This option may be given multiple times.
+.TP
+\fB\-c\fR, \fB\-\-check\fR=\fIcheck\fR
+Run only the specified check. This option may be given multiple times
+to specify multiple checks to run. \fIcheck\fR is the name of the Python
+module (as it would be given to Python's import statement) containing the
+check.
+.TP
+\fB\-a\fR, \fB\-\-all\fR
+Check all installed packages.
+.TP
+\fB\-C\fR, \fB\-\-checkdir\fR=\fIdir\fR
+Insert \fIdir\fR to the front of the list of paths to load checks
+from, unless it is already in the list. The default list of check
+dirs typically contains only /usr/share/rpmlint. Directories in the
+check dirs list are also inserted to the front of the list of paths to
+load Python modules from when the check process begins.
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Display summary of command line options and exit.
+.TP
+\fB\-v\fR, \fB\-\-verbose\fR
+Operate in verbose mode.
+.TP
+\fB\-E\fR, \fB\-\-extractdir\fR=\fIdir\fR
+Base directory for extracted temporary files, default is what Python's
+tempfile.gettempdir() returns.
+.TP
+\fB\-V\fR, \fB\-\-version\fR
+Display version information and exit.
+.TP
+\fB\-n\fR, \fB\-\-noexception\fR
+Ignore output filters.
+.TP
+\fB\-\-rawout\fR=\fIfile\fR
+Write unfiltered output to \fIfile\fR.
+.TP
+\fB\-f\fR, \fB\-\-file\fR=\fIconffile\fR
+Load user configuration from the specified file, default is
+$XDG_CONFIG_HOME/rpmlint (~/.config/rpmlint if $XDG_CONFIG_HOME is
+empty or not set).
+.TP
+\fB\-o\fR, \fB\-\-option\fR=\fIvalue\fR
+Override a configuration option. \fIvalue\fR is a whitespace separated string,
+first word of which is the option name to set, and the Python eval() return
+value for the rest is set as the value for the option. Passing only an option
+name is treated as if None was passed as its value. See the file "config"
+shipped with rpmlint for the list of configuration options and their types.
+For example:
+ \-o "NetworkEnabled True"
+ \-o "Distribution \(aqMy favorite distro\(aq"
+ \-o "MaxLineLength 80"
+ \-o "ValidShells (\(aq/bin/sh\(aq, \(aq/bin/bash\(aq)"
+.SH CAVEATS
+All checks do not apply to all argument types. For best check
+coverage, run rpmlint on all source and binary packages your build
+produces. The set of checks rpmlint runs on source packages is a
+superset of the one for plain specfiles, the set of checks run for
+installed binary packages is a superset of the one for uninstalled
+binary package files, and the source and binary package check sets are
+quite different.
+.SH FILES
+.TP
+\fB/usr/share/rpmlint/config\fR, \fB/usr/share/rpmlint/config.*\fR
+Built-in configuration. When invoked as \fIsomeprefix\fR-rpmlint,
+/usr/share/rpmlint/config.\fIsomeprefix\fR is used if it exists,
+otherwise /usr/share/rpmlint/config.
+.TP
+\fB/etc/rpmlint/*config\fR
+System wide configuration.
+.TP
+\fB$XDG_CONFIG_HOME/rpmlint\fR or \fB~/.config/rpmlint\fR
+User configuration.
+.SH EXIT CODES
+.IP 0
+No errors.
+.IP 1
+Unspecified error.
+.IP 2
+Interrupted.
+.IP 64
+One or more error message printed.
+.IP 66
+Badness threshold exceeded.
+.SH AUTHOR
+Originally written by Frédéric Lepied, see the file AUTHORS for (probably
+incomplete) list of additional contributors.
+.SH COPYRIGHT
+This program is licensed under the GNU General Public License, see the
+file COPYING included in the distribution archive.
diff --git a/rpmlint.bash-completion b/rpmlint.bash-completion
new file mode 100644
index 0000000..189582f
--- /dev/null
+++ b/rpmlint.bash-completion
@@ -0,0 +1,94 @@
+# bash-completion add-on for rpmlint
+# http://bash-completion.alioth.debian.org/
+
+_rpmlint_installed_packages()
+{
+ if declare -F _rpm_installed_packages &>/dev/null ; then
+ _rpm_installed_packages
+ elif declare -F _xfunc &>/dev/null ; then
+ # bash-completion 1.90+ dynamic loading
+ _xfunc rpm _rpm_installed_packages
+ fi
+}
+
+_rpmlint()
+{
+ COMPREPLY=()
+ local cur=$2 # for _rpm_installed_packages, _filedir
+
+ case $3 in
+ -C|--checkdir|-E|--extractdir)
+ _filedir -d
+ return 0
+ ;;
+ -f|--file|--rawout)
+ _filedir
+ return 0
+ ;;
+ -c|--check)
+ # should take --checkdir, python path, inheritance into account...
+ COMPREPLY=( $( compgen -W \
+ "$( command ls /usr/share/rpmlint/*Check.py* 2>/dev/null |
+ sed -e '/^AbstractCheck/d' \
+ -e 's|.*/\([^/.]*\)\.py.*|\1|' )" -- "$cur" ) )
+ return 0
+ ;;
+ -I|--explain)
+ # should take --checkdir into account...
+ COMPREPLY=( $( compgen -W "$( sed -e '1,/^addDetails/d' \
+ -ne "s/^'\([^'[:space:]]\{1,\}\)',\$/\1/p" \
+ /usr/share/rpmlint/*Check.py 2>/dev/null )" -- "$cur" ) ) #'
+ return 0
+ ;;
+ -o|--option)
+ # argument required but no completions available
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--info --explain --check --all --checkdir
+ --help --verbose --extractdir --version --noexception
+ --file --option' -- "$cur" ) )
+ else
+ # Installed packages completion is potentially slow, do it only if $cur
+ # does not look like a path.
+ [[ $cur != */* && $cur != [.~]* ]] && _rpmlint_installed_packages
+ _filedir @([rs]pm|spec)
+ fi
+}
+complete -F _rpmlint -o filenames rpmlint
+
+_rpmdiff()
+{
+ COMPREPLY=()
+ local cur=$2 # for _rpm_installed_packages, _filedir
+
+ case $3 in
+ -i|--ignore)
+ COMPREPLY=( $( compgen -W 'S M 5 D N L V U G F T' -- "$cur" ) )
+ return 0
+ ;;
+ -h|--help)
+ return 0
+ ;;
+ esac
+
+ if [[ "$cur" == -* ]]; then
+ COMPREPLY=( $( compgen -W '--help --ignore' -- "$cur" ) )
+ else
+ # Installed packages completion is potentially slow, do it only if $cur
+ # does not look like a path.
+ [[ $cur != */* && $cur != [.~]* ]] && _rpmlint_installed_packages
+ _filedir [rs]pm
+ fi
+}
+complete -F _rpmdiff -o filenames rpmdiff
+
+# Local variables:
+# mode: shell-script
+# sh-basic-offset: 4
+# sh-indent-comment: t
+# indent-tabs-mode: nil
+# End:
+# ex: ts=4 sw=4 et filetype=sh
diff --git a/test.sh b/test.sh
new file mode 100755
index 0000000..0c0d202
--- /dev/null
+++ b/test.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+export PYTHONPATH=$(pwd)/tools:$(pwd)
+export TESTPATH="$(pwd)/test/"
+
+for i in $TESTPATH/test.*.py; do
+ python $i
+ RET=$?
+ if [ $RET -ne 0 ]; then
+ exit $RET
+ fi
+done
+
+echo "Check that rpmlint executes with no unexpected errors"
+python ./rpmlint -C $(pwd) test/*.rpm test/*.spec >/dev/null
+rc=$?
+test $rc -eq 0 -o $rc -eq 64
diff --git a/test/PamCheck-0.1-1.i586.rpm b/test/PamCheck-0.1-1.i586.rpm
new file mode 100644
index 0000000..2606df9
--- /dev/null
+++ b/test/PamCheck-0.1-1.i586.rpm
Binary files differ
diff --git a/test/SpecCheck.spec b/test/SpecCheck.spec
new file mode 100644
index 0000000..14f4e6f
--- /dev/null
+++ b/test/SpecCheck.spec
@@ -0,0 +1,62 @@
+Name: SpecCheck
+Version: 0
+Release: 0
+Summary: None here
+
+Group: Undefined
+License: GPLv2
+URL: http://rpmlint.zarb.org/#%{name}
+Source0: Source0.tar.gz
+Patch: Patch.patch
+Patch1: Patch1.patch
+Patch2: Patch2.patch
+Patch3: Patch3.patch
+Patch4: Patch4.patch
+Patch5: Patch5.patch
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+Provides: unversioned-provides, versioned-provides = 1.0
+Obsoletes: versioned-obsoletes < 2.0
+Obsoletes: unversioned-obsoletes
+
+%description
+SpecCheck test.
+
+%package noarch-sub
+Summary: Noarch subpackage
+Group: Undefined
+BuildArch: noarch
+
+%description noarch-sub
+Noarch subpackage test.
+
+
+%prep
+%setup -q
+%patch1
+%patch
+%patch -P 2 -P 4
+sed -e s/foo/bar/ %{PATCH5} | %{__patch} -p1
+
+
+%build
+# %configure
+# %%%
+
+
+%install
+rm -rf $RPM_BUILD_ROOT
+
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+
+%files
+%defattr(-,root,root,-)
+%{_libdir}/foo
+
+%files noarch-sub
+%defattr(-,root,root,-)
+
+
+%changelog
diff --git a/test/test.PamCheck.py b/test/test.PamCheck.py
new file mode 100644
index 0000000..08d1cff
--- /dev/null
+++ b/test/test.PamCheck.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+
+import unittest
+import Testing
+import PamCheck
+# FIXME harcode
+
+class TestPamCheck(unittest.TestCase):
+ def setUp(self):
+ self.pkg = Testing.getTestedPackage('PamCheck')
+ Testing.startTest()
+ def tearDown(self):
+ self.pkg.cleanup()
+ def testcheck(self):
+ PamCheck.check.check(self.pkg)
+ self.assertEqual( Testing.getOutput(), ['PamCheck.i586: E: use-old-pam-stack /etc/pam.d/PamCheck (line 1)'])
+
+# enjoy \o/
+if __name__ == '__main__':
+ unittest.main()
+
+#print Testing.getOutput()
diff --git a/test/test.Pkg.py b/test/test.Pkg.py
new file mode 100644
index 0000000..6d061d4
--- /dev/null
+++ b/test/test.Pkg.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+
+import rpm
+import unittest
+import Testing
+import Pkg
+
+class TestPkg(unittest.TestCase):
+ def setUp(self):
+ Testing.startTest()
+
+ def test_parse_deps(self):
+ for (arg, exp) in (
+ ("a, b < 1.0 c = 5:2.0-3 d",
+ [("a", 0, (None, None, None)),
+ ("b", rpm.RPMSENSE_LESS, (None, "1.0", None)),
+ ("c", rpm.RPMSENSE_EQUAL, ("5", "2.0", "3")),
+ ("d", 0, (None, None, None))]),
+ ):
+ self.assertEqual(Pkg.parse_deps(arg), exp)
+
+ def test_rangeCompare(self):
+ for (req, prov) in (
+ (("foo", rpm.RPMSENSE_LESS, (None, "1.0", None)),
+ ("foo", rpm.RPMSENSE_EQUAL, ("1", "0.5", None))),
+ ):
+ self.assertFalse(Pkg.rangeCompare(req, prov))
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/test/test.SpecCheck.py b/test/test.SpecCheck.py
new file mode 100644
index 0000000..fadf970
--- /dev/null
+++ b/test/test.SpecCheck.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+
+import re
+import unittest
+import Testing
+import SpecCheck
+
+class TestSpecCheck(unittest.TestCase):
+ def setUp(self):
+ self.pkg = Testing.getTestedSpecPackage('SpecCheck')
+ Testing.startTest()
+ def testcheck(self):
+ SpecCheck.check.check_spec(self.pkg, self.pkg.name)
+ out = "\n".join(Testing.getOutput())
+ self.assertTrue("patch-not-applied Patch3" in out)
+ self.assertFalse(re.search("patch-not-applied Patch\\b", out))
+ self.assertFalse(re.search("patch-not-applied Patch[01245]", out))
+ self.assertTrue("libdir-macro-in-noarch-package" not in out)
+ self.assertTrue(len(re.findall("macro-in-comment", out)) == 1)
+ self.assertTrue("unversioned-explicit-provides unversioned-provides"
+ in out)
+ self.assertTrue("unversioned-explicit-provides versioned-provides"
+ not in out)
+ self.assertTrue("unversioned-explicit-obsoletes unversioned-obsoletes"
+ in out)
+ self.assertTrue("unversioned-explicit-obsoletes versioned-obsoletes"
+ not in out)
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/tools/Testing.py b/tools/Testing.py
new file mode 100644
index 0000000..f111597
--- /dev/null
+++ b/tools/Testing.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+
+import glob
+import os
+import tempfile
+
+import Pkg
+
+
+currently_testing = 0
+output = []
+
+def isTest():
+ return currently_testing
+
+def startTest():
+ global currently_testing
+ global output
+ output = []
+ currently_testing = 1
+
+def addOutput(s):
+ global output
+ output.append(s)
+
+def getOutput():
+ global output
+ return output
+
+def getTestedPackage(name):
+ pkg_path = glob.glob(os.environ['TESTPATH'] + '/' + name + '-*.rpm')[0]
+ return Pkg.Pkg(pkg_path, tempfile.gettempdir())
+
+def getTestedSpecPackage(name):
+ pkg_path = glob.glob(os.environ['TESTPATH'] + '/' + name + '.spec')[0]
+ return Pkg.FakePkg(pkg_path)
+
+# Local variables:
+# indent-tabs-mode: nil
+# py-indent-offset: 4
+# End:
+# ex: ts=4 sw=4 et
diff --git a/tools/generate-isocodes.py b/tools/generate-isocodes.py
new file mode 100755
index 0000000..97b5422
--- /dev/null
+++ b/tools/generate-isocodes.py
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+
+# Generate ISO codes for use with e.g. locale subdir checks
+# http://alioth.debian.org/projects/pkg-isocodes/
+
+import sys
+from xml.etree.ElementTree import ElementTree
+
+
+langs = set()
+countries = set()
+
+# 2-letter country codes
+tree = ElementTree(file = "/usr/share/xml/iso-codes/iso_3166.xml")
+for entry in tree.findall("iso_3166_entry"):
+ countries.add(entry.get("alpha_2_code"))
+
+# 2-letter codes
+tree = ElementTree(file = "/usr/share/xml/iso-codes/iso_639.xml")
+for entry in tree.findall("iso_639_entry"):
+ langs.add(entry.get("iso_639_1_code"))
+
+# Remaining 2-letter codes plus 3-letter ones for which we have no 2-letter one
+tree = ElementTree(file = "/usr/share/xml/iso-codes/iso_639_3.xml")
+for entry in tree.findall("iso_639_3_entry"):
+ code = entry.get("part1_code")
+ if code:
+ langs.add(code)
+ if not code:
+ langs.add(entry.get("id"))
+
+print "# Generated with %s" % sys.argv[0]
+print ""
+print "LANGUAGES = set(("
+for code in sorted(langs):
+ if code:
+ print "\t\"%s\"," % code
+print "))"
+print ""
+print "COUNTRIES = set(("
+for code in sorted(countries):
+ if code:
+ print "\t\"%s\"," % code
+print "))"