"""
Created on Fri Dec 20 11:53:35 2013
Régénère les liens vers les fichiers archives d'Unison sous Windows.
Optionnellement il détruit les vieux liens.
Détecte les fichiers recréés par Unison et le replace dans le dossier Windows.
Ce script devrait être appelé après chaque modification d'un profil.
Après toute utilisation d'Unison sur Linux, il doit être appelé avant
d'utiliser Unison sous Windows. Typiquement il est exécuté automatiquement
après (voir aussi avant) chaque exécution d'Unison sous Linux.
Voir le fichier `unison` associé.
Attention, s'il y a deux rootalias dans le fichier, il prend juste en compte
le dernier. Soyez prudent avant d'utiliser deux rootalias dans un fichier de
configuration.
@author: Élie Gouzien (une recherche sur Internet vous permet de me contacter)
"""
import os
import codecs
import hashlib
import shutil
import argparse
UNISON_WIN = os.path.normpath("/mnt/OS/Users/Elie/.unison/")
UNISON_LINUX = os.path.normpath("/home/elie/.unison/")
OPTIONS_RECOMMANDEES = {'fat': 'true'}
parser = argparse.ArgumentParser(description="Crée les liens pour les fichiers\
d'archive d'Unison.", epilog="Lire le code \
source pour plus de détails.")
parser.add_argument('-c', '--clean', dest='recreer', action='store_true',
help="Détruit les liens avant de les recréer")
args = parser.parse_args()
def compter(liste, clef, valeur):
"""Compte le nombre de fois que liste[i][clef] == valeur.
liste : liste de dictionnaire
clef : clef de dictionnaire, chaîne
valeur : valeur de dictionnaire, chaîne
"""
nombre = 0
for dico in liste:
if dico[clef] == valeur:
nombre += 1
return nombre
def indice_convenant(liste, clef, valeur):
"""Donne le premier indice i tel que liste[i][clef] == valeur.
liste : liste de dictionnaire
clef : clef de dictionnaire, chaîne
valeur : valeur de dictionnaire, chaîne
"""
indice = 0
while True:
if liste[indice][clef] == valeur:
return indice
indice += 1
def texte_erreur(liste, profile, chemin_local):
"""Renvoie une description d'erreur contenant les caractéristiques des
fichiers d'archive gérant 'chemin_local'.
liste (list): liste de l'index des fichiers d'archive (ARCHIVES)
chemin_local (str): le chemin (adresse) du profil dont on veut afficher
les archives correspondantes.
"""
sortie = "Profile " + profile + " :\n"
sortie += "Attention, il y a plusieurs fichiers d'archive \
associables au profil.\n"
for dico in liste:
if dico['chemin_local'] == chemin_local:
sortie += "Fichier d'archive " + dico['ar_win'] + " :\n"
sortie += "\tVersion archive : " + str(dico['version']) + "\n"
sortie += "\tChemin local (root) : " + dico['chemin_local'] + "\n"
sortie += ("\tChemins canoniques (roots) :"
+ dico['chemins_profile'] + "\n")
sortie += ("\tFichier annexe (si existant) :" + dico['fp_win']
+ "\n")
sortie += "Le plus simple est souvent de supprimer les vieux fichiers."
return sortie
if args.recreer is True:
for fichier in os.listdir(UNISON_LINUX):
fichier = os.path.join(UNISON_LINUX, fichier)
if os.path.islink(fichier):
os.remove(fichier)
ARCHIVES = []
for fichier in os.listdir(UNISON_WIN):
chemin_fichier = os.path.join(UNISON_WIN, fichier)
if not (os.path.isfile(chemin_fichier) and
os.path.splitext(fichier)[1] == "" and fichier.startswith("ar")):
continue
archive = {}
with codecs.open(chemin_fichier, 'r', 'iso-8859-15') as source:
archive['version'] = source.readline().split()[-1]
archive['chemin_local'], _, archive['chemins_profile'] = \
source.readline().strip('\n').partition(" synchronizing roots ")
archive['chemin_local'] = \
archive['chemin_local'].partition("Archive for root ")[2]
TODO
archive_id = (archive['chemin_local'] + ';' + archive['chemins_profile']
+ ';' + archive['version']).encode('ascii')
archive['somme_controle'] = hashlib.md5(archive_id).hexdigest()
archive['ar_win'] = chemin_fichier
archive['fp_win'] = os.path.join(UNISON_WIN,
'fp' + archive['somme_controle'][0:6])
archive['ar_linux'] = os.path.join(UNISON_LINUX,
'ar' + archive['somme_controle'])
archive['fp_linux'] = os.path.join(UNISON_LINUX,
'fp' + archive['somme_controle'])
archive['version'] = int(archive['version'])
assert os.path.basename(archive['ar_linux']).startswith(
os.path.basename(archive['ar_win'])), "Problème de nomage."
ARCHIVES.append(archive)
for fichier in os.listdir(UNISON_LINUX):
chemin_fichier = os.path.join(UNISON_LINUX, fichier)
if not (os.path.isfile(chemin_fichier) and
os.path.splitext(fichier)[1] == ".prf"):
continue
TODO
options = {}
with codecs.open(chemin_fichier, 'r', 'iso-8859-15') as source:
for ligne in source:
ligne = ligne.strip('\n').strip()
if ligne.find('=') != -1:
clef, _, valeur = ligne.partition('=')
clef = clef.strip()
valeur = valeur.strip()
options[clef] = valeur
if 'rootalias' in options:
chemin_local = options['rootalias'].partition('->')[2].strip()
nombre_convenant = compter(ARCHIVES, 'chemin_local', chemin_local)
if nombre_convenant == 0:
print("Profile " + fichier + " :")
print("\tAttention, il n'y a pas d'archive associée au profil. "
"vous devriez utiliser Unison avec Windows au moins une "
"fois.")
continue
elif nombre_convenant > 1:
raise Exception(texte_erreur(ARCHIVES, fichier, chemin_local))
i = indice_convenant(ARCHIVES, 'chemin_local', chemin_local)
if not os.path.islink(ARCHIVES[i]['ar_linux']):
try:
os.symlink(ARCHIVES[i]['ar_win'], ARCHIVES[i]['ar_linux'])
except OSError as erreur:
if erreur.errno != 17:
raise
shutil.copyfile(ARCHIVES[i]['ar_linux'], ARCHIVES[i]['ar_win'])
os.remove(ARCHIVES[i]['ar_linux'])
os.symlink(ARCHIVES[i]['ar_win'], ARCHIVES[i]['ar_linux'])
if not os.path.islink(ARCHIVES[i]['fp_linux']):
try:
os.symlink(ARCHIVES[i]['fp_win'], ARCHIVES[i]['fp_linux'])
except OSError as erreur:
if erreur.errno != 17:
raise
shutil.copyfile(ARCHIVES[i]['fp_linux'], ARCHIVES[i]['fp_win'])
os.remove(ARCHIVES[i]['fp_linux'])
os.symlink(ARCHIVES[i]['fp_win'], ARCHIVES[i]['fp_linux'])
if not all(options.get(key) == val
for key, val in OPTIONS_RECOMMANDEES.items()):
print("Profile " + fichier + " :")
print("Attention, afin de ne pas générer de conflits, nous "
"recommandons d'appliquer les mêmes options que sous "
"Windows :")
print(OPTIONS_RECOMMANDEES)
try:
fichier_log = os.path.basename(options['logfile'])
if not os.path.islink(os.path.join(UNISON_LINUX, fichier_log)):
os.symlink(os.path.join(UNISON_WIN, fichier_log),
os.path.join(UNISON_LINUX, fichier_log))
del fichier_log
except KeyError:
pass