#!/usr/bin/env python # -*- coding: utf-8 -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 u"""%(scriptname)s: Afficher la dernière version d'un ensemble de fichiers USAGE %(scriptname)s [/path/to/]basename OPTIONS -r Inverser le rôle de cet utilitaire: afficher toutes les versions des fichiers plutôt que la dernière -s suffix Ajouter suffix à la liste des suffixes qui doivent être enlevés au nom de fichier avant de trouver la version. -n Ne pas utiliser une liste de suffixes par défaut qui convient pour les fichiers d'archives et d'export: (war, jar, ear, zip, tar[.gz|.bz2], t[gz|bz2], sql, ldif) -f INPUT-FILE Au lieu de lister les fichiers, prendre la liste dans INPUT-FILE. -f - signifie lire sur stdin.""" import os, sys, re from os import path from glob import glob from ulib.all import * SUFFIXES = (".war", ".jar", ".ear", ".zip", ".gz", ".bz2", ".tar", ".tgz", ".tbz2", ".sql", ".ldif", ) def strip_suffixes(p, suffixes=SUFFIXES): suffix_striped = True while suffix_striped: suffix_striped = False for suffix in suffixes: if p.endswith(suffix): p = p[:len(p) - len(suffix)] suffix_striped = True return p RE_ARCHIVE = re.compile(r'(?i)\.(war|jar|ear|zip|tar|tar\.gz|tgz|tar\.bz2|tbz2)$') def is_archive(p): return RE_ARCHIVE.search(p) is not None def strip_archive_ext(p): mo = RE_ARCHIVE.search(p) if mo is not None: p = p[:mo.start(0)] return p RE_PATH_VERSION = re.compile(r'(?i)(?:-|_|\.)' # préfixe: - ou . r'(\d+(?:(-|_|\.)\d+(?:\2\d+)*)?)' # version r'(?:(a|alpha|b|beta|rc)(\d*))?' # type r'$' # ) RE_NUMBERS = re.compile(r'\d+') TYPE_MAPPINGS = {'a': 'alpha', 'b': 'beta'} TYPES = ['alpha', 'beta', None, 'rc'] def has_version(p): return RE_PATH_VERSION.search(p) is not None def get_version(p): mo = RE_PATH_VERSION.search(p) if mo is None: return None, None, None else: vns = map(int, RE_NUMBERS.findall(mo.group(1))) type = mo.group(3) if type is not None: type = type.lower() type = TYPE_MAPPINGS.get(type, type) n = mo.group(4) if n is not None: n = int(n) return vns, type, n def strip_version(p): mo = RE_PATH_VERSION.search(p) if mo is not None: p = p[:mo.start(0)] return p class PathVersion: def __init__(self, suffixes=SUFFIXES): self.suffixes = suffixes def cmp(self, p0, p1): p0 = strip_suffixes(p0, self.suffixes) p1 = strip_suffixes(p1, self.suffixes) vns0, type0, n0 = get_version(p0) vns1, type1, n1 = get_version(p1) c = cmp(vns0, vns1) if c == 0: c = cmp(TYPES.index(type0), TYPES.index(type1)) if c == 0: c = cmp(n0, n1) return c def display_help(): uprint(__doc__ % globals()) def run_plver(): all = False reverse = False suffixes = list(SUFFIXES) inf = None opts, argv = get_args(None, 'arns:f:', ['help', 'all', 'reverse', 'no-suffixes', 'suffix=', 'file=']) for opt, value in opts: if opt in ('--help', ): display_help() sys.exit(0) elif opt in ('-a', '--all'): all = True elif opt in ('-r', '--reverse'): reverse = True elif opt in ('-n', '--no-suffixes'): suffixes = [] elif opt in ('-s', '--suffix'): suffixes.append(value) elif opt in ('-f', '--file'): inf = value if inf == '-': inf = sys.stdin basenames = {} if inf is None: # Generer une liste de fichiers en ne gardant que les fichiers archive qui # ont un numero de version. Classer ces fichiers par noms de base if not argv: argv = ['*'] for arg in argv: if path.isdir(arg): arg += '/' if not arg.endswith('*'): arg += '*' for p in glob(arg): n = strip_suffixes(p, suffixes) s = p[len(n):] if has_version(n): b = strip_version(n) + s if b not in basenames: basenames[b] = [] basenames[b].append(p) else: lines = Lines() for p in lines.readlines(inf): n = strip_suffixes(p, suffixes) s = p[len(n):] if has_version(n): b = strip_version(n) + s if b not in basenames: basenames[b] = [] basenames[b].append(p) # Pour chacun des noms de base, classer la liste et retourner la(les) # version(s) demandée(s) pv = PathVersion(suffixes) for basename, paths in basenames.items(): if all: paths.sort(pv.cmp, reverse=reverse) for p in paths: print p else: paths.sort(pv.cmp, reverse=True) if reverse: for p in paths[1:]: print p else: print paths[0] if __name__ == '__main__': run_plver()