/* Op‚ration de livraison d'une s‚rie de projet. La livraison, c'est l'inscription des modifications … un projet dans son archive. La livraison se fait en deux ‚tapes: 1- On doit documenter les changements. Un fichier change.log contient les changements pour tous les fichiers d'un projet. 2- On archive. Cette op‚ration est batch. On minimise le temps d'accŠs … l'archive pour limiter les chances qu'un crash viennent foutre le bordel. */ #include #include #include #include #include "projet.h" /* Lit le contenu du fichier makefile.exc */ static void livre_readexc ( SSTRINGS &tbexc, USERINFO *user, const char *projet) { /* #Specification: livraison / makefile.exc Certains fichiers ne sont pas archivable. Ce sont des fichiers temporaires généralement créé pendant la compilation et conservé tout au long du travail. Il ne sont donc jamais effacé. Par exemple, les fichiers produits par la commande configure sont sans intéret pour l'archivage. Le fichier makefile.exc (exclusion) fournis la liste des fichiers qui ne doivent pas etre archivé. Notez que le fichier makefile.exc est lui meme archivé. */ char path[MAXSIZ_PATH]; user->makusrpath(projet,path); char nameexc[MAXSIZ_PATH]; path_make (path,"makefile.exc",nameexc); FILE *fin = fopen (nameexc,"r"); if (fin == NULL){ MAKEFILE mkf(NULL,1,projet,user); MAKEFILE_FILE *file = mkf.locate ("makefile.exc"); if (file != NULL){ file->getombpath (user,nameexc); fin = fopen (nameexc,"r"); } } if (fin != NULL){ char buf[100]; while (fgets(buf,sizeof(buf)-1,fin)!=NULL){ strip_end (buf); tbexc.add (new SSTRING(buf)); } fclose (fin); } } /* Livraison d'un projet et optionnellement ses sous-r‚pertoires. Le projet "" est la racine de tous les projets. Il est livrable sauf que les sous-projets ne sont pas livr‚s r‚cursivement. Retourne le nombre de fichier livr‚s. -1 si erreur. Si retourne 0, cela signifie qu'il n'y avait rien … livr‚, donc la livraison du makefile.dat devient optionnelle. Ca ‚vite l'archivage inutile du makefile.dat. */ int livre_revision ( USERINFO *user, PRJCTRL *ctrl, PROJET_LOG *log, // Affiche le traitement … l'‚cran const char *projet, const char *comment, // Commentaire à placer dans le version.dat REVISION_TYPE revtype, const char *version, const SSTRINGS &tbnew) // Contiendra la liste des nouveaux fichiers // (jamais archivé) { int ret = 0; char path[MAXSIZ_PATH]; user->makusrpath(projet,path); SSTRINGS tb; int nb = ctrl->dirsource (path,WILD_ALLFILE,tb); /* #Sp‚cification: livraison / critŠres / livraison forc‚e La pr‚sence d'un fichier makefile.old indique que le makefile.dat a ‚t‚ r‚vis‚ soit manuellement, soit via le m‚canisme de fusion-‚dition. Cela implique qu'il faut le livrer mˆme si aucune autre transaction n'a ‚t‚ faite dans le r‚pertoire (pas de fichier corrig‚, aucun ajout, etc ... */ char nameold[MAXSIZ_PATH]; path_make (path,"makefile.old",nameold); if (file_exist (nameold)) ret = 1; SSTRINGS tbexc; // Contriendra la liste des fichiers a exclure livre_readexc (tbexc,user,projet); // Mˆme s'il n'y a aucun fichier … traiter, il y a peut // ˆtre des fichier qui ont ‚t‚ effac‚. { int err = 0; if (projet[0] != '\0'){ // Livraison des sous-r‚pertoires // On livre les sous-r‚pertoires avant pour mettre … jour // le fichier makefile.dat. La valeur de ret indiquera // combien on a livr‚ de sous-r‚pertoires. for (int i=0; iget(); if (ctrl->isdir(fichier) && tbexc.lookup(fichier)==-1){ log->printf ("R‚pertoire :%s:\n",fichier); char sous_projet[MAXSIZ_PATH]; path_make (projet,fichier,sous_projet); path_stripsep (sous_projet,sous_projet); err = livre_revision (user,ctrl,log,sous_projet,comment ,revtype,version,tbnew); if (err != -1) ret += err; /* #Specification: livraison / critŠres Un projet est livrable entre autre si un de ses sous-r‚pertoires a ‚t‚ livr‚s, puisque son makefile.dat a ‚t‚ chang‚. */ } } } if (err != -1){ MAKEFILE mkf(NULL,1,projet,user); CHANGE_DAT change("change.dat"); /* #Specification: livraison / critŠres Un projet est livrable s'il y a des fichiers qui ont chang‚s, des fichiers qui se sont ajout‚s ou effac‚s et des sous-r‚pertoires qui ont ‚t‚ ajout‚s ou ‚limin‚s. */ { // Compte le nombre de fichiers effac‚s. Affiche un message // pour chaque fichier. mkf.setiter(); MAKEFILE_FILE *file; while ((file=mkf.iterdel(WILD_ALLFILE))!=NULL){ // Attention: Un fichier peut avoir ‚t‚ effac‚ // et recr‚‚ ensuite. // Il ne faut pas de message dans ce cas. if (!mkf.locate(file->getnom())){ ret++; // Note qu'il y a eu au moins un changement char fpath[MAXSIZ_PATH]; path_make (path,file->getnom(),fpath); log->printf ("Elimine %s\n",fpath); } } } for (int i=0; iget(); if (!ctrl->isdir(fichier) && tbexc.lookup(fichier)==-1){ const char *doc = change.getdoc (fichier); bool doarch = false; err = mkf.livre (fichier,revtype,doc,tbnew,doarch); if (doarch) ret++; } } if (err != -1 && projet[0] != '\0'){ /* V‚rifie que tout les sous-r‚pertoires mentionn‚s dans dans makefile.dat existe toujours. Ce traitement n'est pas fait dans la racine parce l'usager n'a pas n‚cessairement pris tous les projets. */ mkf.setiter(); MAKEFILE_FILE *file; while ((file=mkf.iter("*/makefile.dat"))!=NULL){ char dir[MAXSIZ_PATH]; path_splitlex (file->getnom(),dir,NULL); path_make(path,dir,dir); /* Désuet Les sous-r‚pertoires h et doc sont ‚limin‚s de makefile.dat, mˆme s'il n'aurait jamais du ˆtre pr‚sent. Ca corrige un bug lors de la cr‚ation de l'archive. || strcmp(dir,"h")==0 || strcmp(dir,"doc")==0 */ if (file_type (dir) != 1){ mkf.delfil(file->getnom()); ret++; // Note au moins un changement // dans makefile.dat log->printf ("Sous-R‚pertoire %s n'existe plus\n",dir); } } } /* #Sp‚cification: archivage / comportement en cas d'erreur. Si un problŠme survient lors de l'archivage d'un projet, le comportement est le suivant: Si au moins une composante du projet a ‚t‚ archiv‚e correctement, le projet sera archive comme si tout ‚tait ok. livre_revision() retourne > 0. Si aucune composante n'a ‚t‚ archiv‚, livre_revision() retourne -1, indiquant un erreur. Ce comportement fait que mˆme un archivage partiel va laisser l'archive et le r‚pertoire usager dans un ‚tat consistent. */ if (ret > 0 || projet[0] == '\0'){ err = mkf.livretoi (revtype,true,version,comment); if (err != -1){ /* #Specification: livraison / succes Le fichier change.dat est renomm‚ … change.old aprŠs une livraison ok. Ce fichier contient la documentation associ‚e … chaque fichier livr‚. Le fichier makefile.old est effac‚. Ca ‚vite que le makefile.dat soit archiv‚ pour rien lors des prochaines livraisons. */ char change_dat[MAXSIZ_PATH]; path_make (path,"change.dat",change_dat); char change_old[MAXSIZ_PATH]; path_make (path,"change.old",change_old); file_unlink (change_old); file_rename (change_dat,change_old); file_unlink (nameold); }else{ /* Impossible d'archiver le makefile.dat. L'archive est maintenant dans un ‚tat inconsistente. On retourne -1 pour limiter les d‚gats. */ ret = -1; /* #Sp‚cification: archivage / RCS / r‚paration manuelle Lorsqu'un archivage a ‚chou‚ complŠtement dans un r‚pertoire, il faut ‚limin‚s les r‚visions de l'archive. Pour une r‚vision X.Y … ‚liminer, les op‚rations suivantes: rcmd rcs -oX.Y %s edition de version.dat pour eliminer l'information associ‚ … la r‚vision X.Y. */ } }else if (ret == 0){ /* #Specification: archivage / fichier non modifié Même si aucun fichier n'est archivé dans un répertoire il y a quand même des fichiers qui seront effacé parce que la version dans l'archive est identique. Donc même une livraison qui ne livre rien, fait du ménage */ mkf.cleanup(); } } if (err == -1 && ret == 0) ret = -1; } return ret; } /* #Sp‚cification: archivage / fichier binaire L'archivage de fichier binaire est pleinement support‚. Les commandes de diff (int‚gration) devront ˆtre "prot‚g‚". Le module ascii.c permet de reconnaitre de fa‡on heuristique les fichiers binaires. */ # /* Test de livraison d'un projet et optionnellement ses sous-r‚pertoires. Le projet "" est la racine de tous les projets. Il est livrable sauf que les sous-projets ne sont pas livr‚s r‚cursivement. Retourne le nombre d'erreur collect‚e. Test que tous les fichiers … archiver ont de la documentation. */ int livre_test ( USERINFO *user, PRJCTRL *ctrl, const char *projet, bool test_comment, // V‚rifie s'il y a de la doc SSTRINGS &tberr, // Contiendra la liste des fichiers // pas accept‚. SSTRINGS &tbold, // Contiendra la liste des anciens fichiers // qui ont été révisés. SSTRINGS &tbnew, // Contiendra la liste des nouveaux fichiers // (jamais archivé) SSTRINGS &tbdel) // Fichier qui ont été effacé { int nberr = 0; char path[MAXSIZ_PATH]; user->makusrpath(projet,path); SSTRINGS tb,tbexc; livre_readexc (tbexc,user,projet); int nb = ctrl->dirsource (path,WILD_ALLFILE,tb); if (nb > 0){ if (projet[0] != '\0'){ // Test de livraison des sous-r‚pertoires for (int i=0; iget(); if (ctrl->isdir(fichier) && tbexc.lookup(fichier)==-1){ char sous_projet[MAXSIZ_PATH]; path_make (projet,fichier,sous_projet); path_stripsep (sous_projet,sous_projet); nberr += livre_test (user,ctrl,sous_projet,test_comment ,tberr,tbold,tbnew,tbdel); } } } MAKEFILE mkf(NULL,true,projet,user); for (int i=0; iget(); if (!ctrl->isdir(fichier) && tbexc.lookup(fichier)==-1){ MAKEFILE_FILE *file = mkf.locate (fichier,true); char fpath[MAXSIZ_PATH]; path_make (projet,fichier,fpath); if (file == NULL){ // Pas dans l'archive tbnew.add (new SSTRING(fpath)); }else if (mkf.livre_test(file)){ // Le fichier a changé, il devra être archivé. tbold.add (new SSTRING(fpath)); } } } // Vérifie les fichiers effacés mkf.setiter(); MAKEFILE_FILE *file; while ((file=mkf.iterdel(WILD_ALLFILE))!=NULL){ // Attention: Un fichier peut avoir ‚t‚ effac‚ // et recr‚‚ ensuite. // Il ne faut pas de message dans ce cas. if (tb.lookup(file->getnom())==-1){ char fpath[MAXSIZ_PATH]; path_make (projet,file->getnom(),fpath); tbdel.add (new SSTRING(fpath)); } } if (test_comment){ CHANGE_DAT change("change.dat"); for (int i=0; iget(); if (!ctrl->isdir(fichier)){ const char *comment = change.getdoc (fichier); if (comment == NULL){ // Pas de documentation char fpath[MAXSIZ_PATH]; path_make (path,fichier,fpath); tberr.add (new SSTRING(fpath)); } } } } } return nberr; }