# -*- coding: utf-8 -*- import string import os import sys import zlib import zipfile import time,calendar import binascii import struct chaineretour='' GlobalPVersion='2.08' """ 2.07 : filter() devient pfilter() (conflit de nom de la fonction) 2.06 : correction delete, suit changement dû à Python 2.5 2.05 : extractions des sous-répertoires (pas la compression, hein) correction -V si sous-répertoires """ # Here are some struct module formats for reading headers structEndArchive = "<4s4H2lH" # 9 items, end of archive, 22 bytes stringEndArchive = "PK\005\006" # magic number for end of archive record structCentralDir = "<4s4B4HlLL5HLl"# 19 items, central directory, 46 bytes stringCentralDir = "PK\001\002" # magic number for central directory structFileHeader = "<4s2B4HlLL2H" # 12 items, file header record, 30 bytes stringFileHeader = "PK\003\004" # magic number for file header structEndArchive64Locator = "<4slql" # 4 items, locate Zip64 header, 20 bytes stringEndArchive64Locator = "PK\x06\x07" # magic token for locator header structEndArchive64 = "<4sqhhllqqqq" # 10 items, end of archive (Zip64), 56 bytes stringEndArchive64 = "PK\x06\x06" # magic token for Zip64 header # indexes of entries in the central directory structure _CD_SIGNATURE = 0 _CD_CREATE_VERSION = 1 _CD_CREATE_SYSTEM = 2 _CD_EXTRACT_VERSION = 3 _CD_EXTRACT_SYSTEM = 4 # is this meaningful? _CD_FLAG_BITS = 5 _CD_COMPRESS_TYPE = 6 _CD_TIME = 7 _CD_DATE = 8 _CD_CRC = 9 _CD_COMPRESSED_SIZE = 10 _CD_UNCOMPRESSED_SIZE = 11 _CD_FILENAME_LENGTH = 12 _CD_EXTRA_FIELD_LENGTH = 13 _CD_COMMENT_LENGTH = 14 _CD_DISK_NUMBER_START = 15 _CD_INTERNAL_FILE_ATTRIBUTES = 16 _CD_EXTERNAL_FILE_ATTRIBUTES = 17 _CD_LOCAL_HEADER_OFFSET = 18 # indexes of entries in the local file header structure _FH_SIGNATURE = 0 _FH_EXTRACT_VERSION = 1 _FH_EXTRACT_SYSTEM = 2 # is this meaningful? _FH_GENERAL_PURPOSE_FLAG_BITS = 3 _FH_COMPRESSION_METHOD = 4 _FH_LAST_MOD_TIME = 5 _FH_LAST_MOD_DATE = 6 _FH_CRC = 7 _FH_COMPRESSED_SIZE = 8 _FH_UNCOMPRESSED_SIZE = 9 _FH_FILENAME_LENGTH = 10 _FH_EXTRA_FIELD_LENGTH = 11 def vprint(txt): global chaineretour if __name__ == '__main__': print(txt) else: chaineretour+=txt+'\n' def aide(num='1'): if num=='0': print """ MMcc ccMM M cccccccc M M cccccccc M M ccM Mcc M M Mc M M Mc M M cMc M M M M M Mc M M M M M M MccccccM Mcccccc M M M M M M M M M M M M Mc M M Mc M M M M cccccccc M M cccccccc Mcccc ccccc c c c c ccccc c c c cMc c M McM M M cMc c McM M M cc M M Mc M M cM M Mc M M M M M M M M M M M M M M M cMcccccMc cc M Mcccccc cMcccccMc M M cc M M M cM M cc M M M M cMc c M M M ccM cMc c M M cc cc ccccc ccccc c c c ccccc c c ccc http://mclaveau.com """ if num=='1': print """ J'étais certain que vous auriez besoin d'aide ! Bon puisque ce moment est arrivé, voici quelques tuyaux (ne vous attendez pas à des miracles) : Il faut taper quelque chose comme : zipmci.py commande fichier-zip fichiers-ou-patterns 'commande' pouvant être, au choix : -TT test global -T test par fichiers, avec recherche du premier fichier non valide -L liste (triée) des fichiers archivés, sur 2 colonnes -V liste (triée) des fichiers archivés, avec date-time, tailles -PD appelle le printdir() de ZipFile.py -A ajoute (si déjà existant, ajoute une nouvelle occurence) -U 'update' c-a-d ajoute, si le fichier est manquant, ou supprime, puis ajoute, si le fichier est différent (contenu ou date/heure) -E extrait des fichiers, en (re)-affectant le date-time enregistré -D supprime des fichiers dans le fichier-zip -B sans commentaire (c'est une blague ?). -P liste les paramètres utilisés dans l'appel courant de ZipMCI.py -H x Aide (help), avec x = 0, 1, 2, 3, 4 ou 5 N'OUBLIEZ PAS l'espace après 'H' exemple : zipmci.py -H 0 """ elif num=='2': print """ ZipMCI.py est un utilitaire-exemple (d'utilisation) du module zipfile.py. Il est destiné à un usage en ligne (invite) de commande, sous Windows. SGDG, il a été testé avec Python 2.2, sous W2K & WXP. Au départ, c'était pour remplacer PkZip, qui avait quelques problèmes avec les noms longs. Qq trucs : - la commande peut être sous plusieurs formes ; par exemple : -v -V v V sont identiques. - le fichier-zip peut être cité sans l'extension exemple : Toto & Toto.zip sont identiques - lorsque les listes sont triées, il n'y a pas de casse (pas de différence majuscules/munuscules) - les "patterns" peuvent avoir différentes formes : "*.py *.jpg *.gif" & "*.py;*.jpg;*.gif" sont identiques "image.* toto.jpg toto.gif" est accepté (mais peut créer des doublons) les patterns sont gérés de façon minimale. e.g. 'ti*.py' ne marche pas. le fichier-zip courant est éliminé des listes/patterns - la suppression ('-D') utilise des fichiers temporaires, supprimés à la fin (XXXXXXXX.TMP & XXXXXXXX.ZIP ; les droits doivent être suffisants) """ elif num=='3': print """ L'aide numéro 3 n'est pas définie. C'était juste pour frimer, en faisant croire à un logiciel hyper-complet, ce que ZipMCI.py n'est pas. Tant pis pour vous (pour l'instant). """ elif num=='4': print """ Licence : --------- Le logiciel est fourni 'tel quel', sans support, ni assistance, ni garantie, d'aucune sorte. Le code-source est entièrement libre. Toutefois, il serait apprécié de citer/laisser les coordonnées du développeur : Michel Claveau Informatique http://mclaveau.com Contributions : --------------- Si vous désirez laisser une contribution volontaire, contactez-moi. Une facture sera systématiquement envoyée au contributeur. Le montant de toute contribution est libre. Cependant, il est demandé de ne pas envisager de montants inférieurs à 1 000 000 Euros (ou 1.000.000 dollars). En effet, je n'ai pas envie de m'embêter avec la petite monnaie. Prestations : ------------- Je vis des prestations informatiques. Si vous avez un besoin (et un budget), vous pouvez me contacter à mc@mclaveau.com (attention j'ai des filtres anti-spams assez 'durs' ; si vous n'avez pas de réponse, envoyez d'autres messages).""" elif num=='5': print """ Qq. trucs : ----------- Il y a des options secrètes. Par exemple '-EE', '-AA'. Il s'agit simplement d'un autre code, pour une autre façon de réaliser une fonction déjà existante (ouf !). Cela peut être un code historiquement obsolète (je n'ai malheureusement pas développé ce logiciel 'du premier coup', malgré mes désirs algorithmiquement mégalomaniaques). Ce peut aussi être la conséquence d'essais pas vraiment convainquant (malgré...) Dernier cas : cela peut résulter d'un papillonnement dû à un besoin de se changer les idées. Bref, pour tout cela, il est recommandé de parcourir le code. """ pass sys.exit(0) def formatDate(datetime): # à partir d'une liste, on présente le date-time "à l'européenne" sdtime = '%02d-%02d-%04d %02d:%02d.%02d' % (datetime[2], datetime[1], datetime[0], datetime[3], datetime[4], datetime[5]) return sdtime def pfilter(lst, patterns): """ Retourne deux sous-listes de la liste-fichiers LST : - la première est la liste qui respecte la liste PATTERNS - la deuxième est la liste qui ne respecte pas la liste PATTERNS pfilter remplace fnmatch, car sous Windows fnmatch se plante avec '*.*' et les fichiers '*.' """ result=[] antiresult=[] for pattern in patterns: patroot,patext=os.path.splitext(pattern) patroot=string.upper(patroot) patext=string.upper(patext) for item in lst: tmproot,tmpext=os.path.splitext(item) tmproot=string.upper(tmproot) tmpext=string.upper(tmpext) if (tmproot==patroot or patroot=="*") and (tmpext==patext or patext==".*") : result.append(item) for item in lst: if not item in result: antiresult.append(item) result.sort() antiresult.sort() return(result,antiresult) def zipddelete(fichierzip,patterns): """ Ancienne méthode (la première testée, en fait) ; remplacée par '-D' Ici, pour chaque fichier conservé, on le crée sur disque, puis on le recompresse dans le nouveau fichier-zip. A la fin, on supprime l'ancien fichier-zip, et on renomme le nouveau. """ global chaineretour chaineretour='' try: z=zipfile.ZipFile(fichierzip,"r") z2=zipfile.ZipFile("XXXXXXXX.ZIP","w") lst,antilst=pfilter(z.namelist(),patterns) for i in antilst: print i, bytes = z.read(i) f = open("XXXXXXXX.TMP", "wb") f.write(bytes) f.close() info=z.getinfo(i) # on récupère le date-time du fichier, pour ré-affecter la bonne valeur ltmp=list(info.date_time) ltmp.append(0) ltmp.append(0) ltmp.append(0) ltmp[3]=ltmp[3]+(time.timezone/3600) #correction du GMT-TimeZone (France only ?) filtime=time.mktime(ltmp) os.utime("XXXXXXXX.TMP",(filtime,filtime)) # (re)-affectation du date-time archivé z2.write("XXXXXXXX.TMP",i,zipfile.ZIP_DEFLATED) print 'recorded' z.close() z2.close() print"Delete :",lst os.remove("XXXXXXXX.TMP") os.remove(fichierzip) os.rename("XXXXXXXX.ZIP",fichierzip) flag='OK' except: flag='Erreur' vprint(flag) def readcompressed(ssss, name): """Return file bytes 'compressés' """ if not ssss.fp: raise RuntimeError, "Attempt to read ZIP archive that was already closed" zinfo = ssss.getinfo(name) filepos = ssss.fp.tell() ssss.fp.seek(zinfo.header_offset, 0) # Skip the file header: fheader = ssss.fp.read(30) if fheader[0:4] != stringFileHeader: raise BadZipfile, "Bad magic number for file header" fheader = struct.unpack(structFileHeader, fheader) fname = ssss.fp.read(fheader[_FH_FILENAME_LENGTH]) if fheader[_FH_EXTRA_FIELD_LENGTH]: ssss.fp.read(fheader[_FH_EXTRA_FIELD_LENGTH]) if fname != zinfo.orig_filename: raise BadZipfile, 'File name in directory "%s" and header "%s" differ.' % (zinfo.orig_filename, fname) bytes = ssss.fp.read(zinfo.compress_size) return bytes,zinfo def writecompressed(ssss, zinfoparam, bytes): zinfo = zinfoparam zinfo.header_offset = ssss.fp.tell() ssss._writecheck(zinfo) ssss._didModify = True zinfo.header_offset = ssss.fp.tell() # Start of header bytes ssss.fp.write(zinfo.FileHeader()) ssss.fp.write(bytes) ssss.fp.flush() if zinfo.flag_bits & 0x08: # Write CRC and file sizes after the file data ssss.fp.write(struct.pack("2: todelete.append(i) toadd.append(i) vprint(i+"Dates/heures diff."+time.strftime('%d.%m.%y %H:%M:%S',time.localtime(statf[8]))+"&"+time.strftime('%d.%m.%y %H:%M:%S',time.gmtime(filtime))) else: pass #*** """ #*** try: f = open(i, "rb") bbytes=f.read() f.close() except: vprint(i+"Pb d'acces au fichier.") try: todelete.append(i) toadd.append(i) print "Maj",i,"forcee." except: print"Pb DUR avec",i else: if bbytes!=bytes: todelete.append(i) toadd.append(i) vprint(i+" Contenu different.") """ except: vprint("Error not determined.") flag="Ok" z.close() if len(todelete)>0: #print "-"*50,"a supprimer" #print todelete zipdelete(fichierzip,todelete) if len(toadd)>0: #print "="*50,"a ajouter" #print toadd zipappend(fichierzip,toadd) except: print"Erreur ! "*7 #time.sleep(3) flag='Erreur' def zipcompar(fichierzip,patterns=[''],lstfichiers=[]): global chaineretour chaineretour='' vprint("Debut de comparaison") try: z=zipfile.ZipFile(fichierzip,"r") zl = z.infolist() zlst=[] for item in zl: #zlst.append(item.filename) zlst.append(item.filename.upper()) if lstfichiers==[]: # on collecte les fichiers du répertoire courant for i in os.listdir(os.getcwd()): if not os.path.isdir(i): lstfichiers.append(i) if patterns==['']: lst=lstfichiers antilst=[] else: lst,antilst=pfilter(lstfichiers,patterns) ldiff=[] for i in lst: if fichierzip.upper()==i.upper(): loop if i.upper() not in zlst: ldiff.append([i,'manque']) vprint(i+' manque dans le zip') else: info=z.getinfo(i) filtime=calendar.timegm(info.date_time) #bytes = z.read(i) #*** try: statf=os.stat(i) if abs(calendar.timegm(time.localtime(statf[8]))-filtime)>242: ldiff.append([i,'Dates/heures diff.: '+time.strftime('%d.%m.%y %H:%M:%S',time.localtime(statf[8]))+" & "+time.strftime('%d.%m.%y %H:%M:%S',time.gmtime(filtime))]) else: pass #*** """ #*** try: f = open(i, "rb") bbytes=f.read() f.close() except: vprint(i+"Pb d'acces au fichier.") try: ldiff([i,'Pb d'accès ; Maj forcee.']) except: print"Pb DUR avec",i else: if bbytes!=bytes: ldiff([i,'Content different.']) """ except: vprint("Error not determined.") flag="Ok" z.close() print"Differences" for i in ldiff: vprint(i) except: flag='Erreur' def zipextract(fichierzip,patterns): """ exemple : zipextract('mesdata.zip',['*.*']) """ global chaineretour chaineretour='' try: z=zipfile.ZipFile(fichierzip,"r") lst,antilst=pfilter(z.namelist(),patterns) for i in lst: info=z.getinfo(i) print str(info.file_size).rjust(8)," ",formatDate(info.date_time)," ",i if i[-1]=='\\' or i[-1]=='/': if not os.path.isdir(i[:-1]): os.makedirs(i[:-1]) else: # on récupère le date-time du fichier, pour ré-affecter la bonne valeur ltmp=list(info.date_time) ltmp.append(0) ltmp.append(0) ltmp.append(0) ltmp[3]=ltmp[3]+(time.timezone/3600) #correction du GMT-TimeZone (France only ?) filtime=time.mktime(ltmp) bytes = z.read(i) f = open(i, "wb") f.write(bytes) f.close() os.utime(i,(filtime,filtime)) # (re)-affectation du date-time archivé flag="Ok" z.close() except: flag='Erreur' vprint(flag) return flag def zipeextract(fichierzip,patterns): global chaineretour chaineretour='' try: z=zipfile.ZipFile(fichierzip,"r") lst,antilst=pfilter(z.namelist(),patterns) for fichier in lst: zinfo = z.getinfo(fichier) filepos = z.fp.tell() z.fp.seek(zinfo.file_offset, 0) bytes = z.fp.read(zinfo.compress_size) # maintenant, 'bytes' contient l'objet (=fichier) compressé z.fp.seek(filepos, 0) if zinfo.compress_type == zipfile.ZIP_STORED: pass elif zinfo.compress_type == zipfile.ZIP_DEFLATED: dc = zlib.decompressobj(-15) bytes = dc.decompress(bytes) ex = dc.decompress('Z') + dc.flush() if ex: bytes = bytes + ex else: raise TypeError, "Unsupported compression method %d for file %s" % (zinfo.compress_type, name) crc = binascii.crc32(bytes) if crc != zinfo.CRC: raise BadZipfile, "Bad CRC-32 for file %s" % name print str(zinfo.file_size).rjust(8)," ",formatDate(zinfo.date_time)," ",fichier # on récupère le date-time du fichier, pour ré-affecter la bonne valeur ltmp=list(zinfo.date_time) ltmp.append(0) ltmp.append(0) ltmp.append(0) ltmp[3]=ltmp[3]+(time.timezone/3600) #correction du GMT-TimeZone (France only ?) filtime=time.mktime(ltmp) f = open(fichier, "wb") f.write(bytes) f.close() os.utime(fichier,(filtime,filtime)) # (re)-affectation du date-time archivé flag="Ok" z.close() except: flag='Erreur' vprint(flag) def ziplist(fichierzip): global chaineretour chaineretour='' try: lst=zipfile.ZipFile(fichierzip,"r").namelist() lst.sort(lambda x,y : (cmp(string.upper(x),string.upper(y)))) i=0 for item in lst: i+=1 if int(i/2)*2==i: vprint(item) else: vprint(item.ljust(38)) z.close() except: vprint('Pb Global') def zipvisu(fichierzip): global Gsep,chaineretour try: Gsep=Gsep except: Gsep='\t' chaineretour='' try: z=zipfile.ZipFile(fichierzip,"r") except: vprint('Pb Global') sys.exit(0) zl = z.infolist() zl.sort(lambda x,y : (cmp(string.upper(x.filename),string.upper(y.filename)))) for item in zl: try: taux=int(100-item.compress_size*100/item.file_size) except: taux=0 if item.file_size>100000000: taillef=str(item.file_size/1000000).rjust(7)+' Mo' elif item.file_size>100000: taillef=str(item.file_size/1000).rjust(7)+' ko' else: taillef=str(item.file_size).rjust(7)+' ' if item.compress_size>100000000: taillec=str(item.compress_size/1000000).rjust(6)+' Mo' elif item.file_size>100000: taillec=str(item.compress_size/1000).rjust(6)+' ko' else: taillec=str(item.compress_size).rjust(6)+' ' #vprint(formatDate(item.date_time)+taillef+str(taillec)+str(taux).rjust(3)+"%"+item.CRC+item.filename) vprint(formatDate(item.date_time)+Gsep+taillef+Gsep+str(taillec)+Gsep+' '+str(taux).rjust(3)+"% "+Gsep+str(item.CRC).rjust(12)+' '+Gsep+item.filename) z.close() # c'est là que les athéniens s'éteignirent, car on leur avait coupé l'électricité... if __name__ == '__main__': global Gsep Gsep='' patterns=[] if len(sys.argv)<3: aide() #lecture des arguments j=0 for i in sys.argv: if j==0: script=i if j==1: commande=i if j==2: fichierzip=i if j>2: if string.find(i,";"): for k in string.split(i,";"): patterns.append(k) else: patterns.append(i) j=j+1 tmp=os.path.splitext(fichierzip) # extension '.zip' à ajouter ? if string.upper(tmp[1])<>'.ZIP': if tmp[0][:-1]=="\\": fichierzip=tmp[0][:-1]+'.zip' else: fichierzip=tmp[0]+'.zip' lstfichiers=[] # on collecte les fichiers du répertoire courant for i in os.listdir(os.getcwd()): if not os.path.isdir(i): lstfichiers.append(i) if commande=="T" or commande=="t" or commande=="-T" or commande=="-t": ziptest(fichierzip) if commande=="TT" or commande=="tt" or commande=="-TT" or commande=="-tt" : zipttest(fichierzip) if commande=="L" or commande=="l" or commande=="-L" or commande=="-l" : ziplist(fichierzip) if commande=="V" or commande=="v" or commande=="-V" or commande=="-v": zipvisu(fichierzip) if commande=="PD" or commande=="pd" or commande=="-PD" or commande=="-pd" : # utilise le ZipFile.printdir() standard z=zipfile.ZipFile(fichierzip,"r") z.printdir() if commande=="A" or commande=="a" or commande=="-A" or commande=="-a" : zipappend(fichierzip,patterns) print"fini" if commande=="U" or commande=="u" or commande=="-U" or commande=="-u" : zipupdate(fichierzip,patterns) if commande=="E" or commande=="e" or commande=="-E" or commande=="-e" : zipextract(fichierzip,patterns) if commande=="EE" or commande=="ee" or commande=="-EE" or commande=="-ee" : # équivalent à '-E', mais avec lecture directe # (juste pour montrer du code aux 'dévelop-passionnés') zipeextract(fichierzip,patterns) if commande=="B" or commande=="b" or commande=="-B" or commande=="-b" : vprint("""\r\n\r\n\r\nQuelle est la différence entre Napoléon et un arbre ? \r\n ...\r\n\r\n Ils ont tous les deux des feuilles, sauf Napoléon...\r\n """) if commande=="D" or commande=="d" or commande=="-D" or commande=="-d" : zipdelete(fichierzip,patterns) if commande=="DD" or commande=="dd" or commande=="-DD" or commande=="-dd": """ Ancienne méthode (la première testée, en fait) ; remplacée par '-D' Ici, pour chaque fichier conservé, on le crée sur disque, puis on le recompresse dans le nouveau fichier-zip. A la fin, on supprime l'ancien fichier-zip, et on renomme le nouveau. """ zipddelete(fichierzip,patterns) if commande=="P" or commande=="p" or commande=="-P" or commande=="-p" : vprint("Script :"+script) vprint("Commande :"+commande) vprint("Fichier zip :"+fichierzip) vprint("Patterns :"+patterns) lst,antilst=pfilter(lstfichiers,patterns) vprint("\r\nListe répertoire :"+lst) vprint("\r\nanti-Liste répertoire :"+antilst) if os.path.isfile(fichierzip): z=zipfile.ZipFile(fichierzip,"r") lst,antilst=pfilter(z.namelist(),patterns) vprint("\r\n\r\nListe ds fichier zip :"+lst) vprint("\r\nanti-Liste ds fich.zip:"+antilst) if commande=="H" or commande=="h" or commande=="-H" or commande=="-h" : aide(fichierzip[:1]) if commande=="S" or commande=="s" or commande=="-S" or commande=="-s" : pass