#include #include #include #include #include #include #include "projet.h" #include "projetx.m" /* Sp‚cification d'un r‚vision d'un projet ou sous-projet */ PUBLIC VERSION_ONE::VERSION_ONE( const char *_vername, // Nom symbolique de la version const char *_progname, // Programmeur ou NULL. const char *revstr, // Num‚roration de la r‚vision const char *datestr): rev(revstr) { strcpy (vername,_vername); setprog(_progname); date = date_a2u(datestr); info = NULL; integ = NULL; } /* Sp‚cification d'un r‚vision d'un projet ou sous-projet */ PUBLIC VERSION_ONE::VERSION_ONE( const char *_vername, const char *_progname, const REVISION *_rev, const char *datestr): rev(_rev) { info = NULL; strcpy (vername,_vername); setprog (_progname); date = date_a2u(datestr); integ = NULL; } /* Sp‚cification d'un r‚vision d'un projet ou sous-projet */ PUBLIC VERSION_ONE::VERSION_ONE(const VERSION_ONE *v) :rev (&v->rev) { info = v->info == NULL ? (char*)NULL : strdup_err (v->info); strcpy (vername,v->vername); strcpy (progname,v->progname); date = v->date; integ = NULL; } /* Retourne le num‚ro de r‚vision associ‚ … cet objet. */ PUBLIC const REVISION *VERSION_ONE::getrev() const { return &rev; } /* Format dans une chaine l'information sur une r‚vision */ PUBLIC void VERSION_ONE::format (char *buf1, char *buf2) const { char revstr[20]; rev.format (revstr); char datestr[11]; date_u2a (date,datestr); #ifdef MISSINGOLD /* #Sp‚cification: r‚vision / pr‚sentation Dans une liste, on pr‚sente une r‚vision comme ceci. Nom_version programmeur date numero Dans certain cas, on ajoute la premiere r‚vision qui contient cette r‚vision (via une fusion). Cette r‚vision est identifi‚ entre parenthŠse. Pour a‚rer cette pr‚sentation, le nom de la version est pr‚sent‚ uniquement pour les r‚vision d'int‚gration (r‚vision a deux num‚ros). De plus, le nom du programmeur est pr‚senter uniquement pour la premiŠre livraison (de ce programmeur). */ strcpy (buf1,rev.type() != REVISION_LIVRE ? vername : ""); #else strcpy (buf1,vername); #endif int len = sprintf (buf2,"%s\t%s\t%s" ,rev.getlivre() < 2 ? progname : "" ,datestr,revstr); if (integ != NULL){ integ->getrev()->format(revstr); sprintf (buf2+len," (%s)",revstr); } } /* Retourne le nom de la version. */ PUBLIC const char *VERSION_ONE::getversion() const { return vername; } /* Ajoute une ligne d'information textuelle … une version. Les lignes ajout‚es forment un texte avec un \n entre chaque ligne. Le texte ne doit pas contenir de '\n'. */ PUBLIC void VERSION_ONE::addinfo (const char *ctl,...) { char ligne[200]; va_list list; va_start (list,ctl); int lenligne = vsprintf (ligne,ctl,list); va_end (list); int leninfo = 0; char *oldinfo = NULL; int delinfo = 0; if (info != NULL){ delinfo = 1; oldinfo = info; leninfo = strlen(info); } info = (char*)malloc_err (lenligne+leninfo+3); if (info != NULL){ char *ptadd = stpcpy (info, oldinfo != NULL ? oldinfo : ""); *ptadd++ = ';'; ptadd = stpcpy (ptadd,ligne); *ptadd++ = '\n'; *ptadd++ = '\0'; } if (delinfo) ::free (oldinfo); } /* Enregistre le nom du programmeur associ‚ … une r‚vision. Si _progname == NULL, enregistre -. Cela indique g‚n‚ralement une nouvelle version en int‚gration. */ PUBLIC void VERSION_ONE::setprog (const char *_progname) { if (_progname == NULL) _progname = "-"; strcpy (progname,_progname); } /* Retourne le nom du programmeur associ‚ … une r‚vision. Si aucun programmeur associ‚, retourne NULL. Cela indique g‚n‚ralement une version en int‚gration (non livr‚e). */ PUBLIC const char *VERSION_ONE::getprog () { const char *ret = progname; if (ret[0] == '-' && ret[1] == '\0') ret = NULL; return ret; } /* D‚termine si une r‚vision est en attente de livraison (int‚gration). Cette r‚vision n'existe pas encore, mais a ‚t‚ cr‚‚e dans version.dat pour r‚server automatiquement la num‚ration. */ PUBLIC int VERSION_ONE::isinteg() const { return strcmp(progname,"-")==0; } PUBLIC VERSION_ONE::~VERSION_ONE() { ::free (info); } /* Sauve information sur une r‚vision dans un fichier */ PUBLIC void VERSION_ONE::save(FILE *fout) const { /* #Sp‚cification: version.dat / format Le fichier version.dat est un fichier ASCII. Il est compos‚ de r‚visions dont le format est le suivant: nom_version progammeur num‚ro_de_r‚vision date ; commmentaires ... ; ... nom_version: Nom symbolique d'une version. Plusieurs r‚vision ont le mˆme nom. programmeur: Nom de login du programmeur qui a livr‚ cette r‚vision. num‚ro_de_r‚vision: num‚ro s‚par‚ par des points. date: Date de livraison. */ char datestr[11]; date_u2a(date,datestr); char revstr[MAXSIZ_REVISION+1]; rev.format(revstr); fprintf (fout,"%-30s\t%-30s\t%-15s\t%s\n" ,vername,progname,revstr,datestr); if (info != NULL){ // info contient d‚j… les '\n' fprintf (fout,"%s",info); } } /* Ajoute un fichier au version.dat courant Cette fonction est utilis‚ lors de la lecture et lorsqu'on incorpore un livraison provenant de /kit/livre. */ PUBLIC int VERSION_DAT::merge (const char *fname) { int ret = -1; FILE *fin = fopen (fname,"r"); if (fin != NULL){ char buf[200]; VERSION_ONE *ver=NULL; // Version courante pour ajouter info. ret = 0; while (fgets_strip(buf,sizeof(buf),fin,NULL)!=NULL){ if (buf[0] == ';'){ if (ver == NULL){ xconf_error(MSG_U(E_VERSIONDAT ,"Index des versions invalide\n" "Fichier: %s\nLigne: %s") ,fname); ret = -1; }else{ ver->addinfo("%s",buf+1); } }else if (buf[0] != '\0'){ char vername[30]; char progname[30]; char revstr[MAXSIZ_REVISION]; char date[11]; if (sscanf (buf,"%s %s %s %s",vername,progname,revstr ,date) == 4){ ver = new VERSION_ONE(vername,progname ,revstr,date); if (ver != NULL) list.add (ver); }else{ xconf_error (MSG_R(E_VERSIONDAT),fname); ret = -1; } } } fclose (fin); } /* #Sp‚cification: version.dat / tri / lecture Lors de la lecture du fichier version.dat, un tri est toujours effectu‚. Cela ‚vite des problŠmes au cas ou le fichier serait corrig‚ … la main. */ sort(); return ret; } /* Lecture du fichier version.dat L'absence du fichier n'est pas un erreur. */ PROTECTED void VERSION_DAT::init (const char *fname) { intgstatus = notdone; path = strdup_err (fname); merge (fname); } /* Charge le fichier version.dat qui d‚finie toutes les r‚visions qu'a subi un projet. Il y a un fichier version.dat pour chaque r‚pertoire d'un projet. Il y a aussi un fichier version.dat global a tous les projets. Il permet d'identifier les r‚visions de projet qui vont ensemble. */ PUBLIC VERSION_DAT::VERSION_DAT (const char *fname) { init (fname); } /* #Sp‚cification: version.dat Chaque r‚pertoire de l'archive, incluant la racine, possŠde un fichier version.dat. Ce fichier contient la liste de toutes les r‚visions existentes de ce r‚pertoire. */ /* #Sp‚cification: version.dat / principal C'est … partir du fichier version.dat principal (celui dans /kit/ombre) qu'on peut s‚lectionner une r‚vision. En plus de l'information formatt‚ d‚crivant chaque r‚vision, le fichier version.dat peut contenir des commentaires pour chaque r‚vision. Ces commentaires sont g‚n‚ralement inscrit par projetx lorsque des transactions sont r‚alis‚es. On peut aussi en ajouter manuellement. Toutes lignes d‚butant par un point-virgule est un commentaire. Ces commentaires sont essentiels pour s‚lectionner les diff‚rentes r‚visions dans l'archive. Le r“le de ces commentaires augmentera en importance … mesure que le nombre de r‚vision va augmenter. */ # /* Lecture du fichier version.dat principal. */ PUBLIC VERSION_DAT::VERSION_DAT(const USERINFO *user) { char verpath[MAXSIZ_PATH]; user->makombpath("",verpath); path_make (verpath,"version.dat",verpath); init (verpath); } /* Lecture d'un fichier version.dat d'un projet. */ PUBLIC VERSION_DAT::VERSION_DAT(const USERINFO *user, const char *projet) { char verpath[MAXSIZ_PATH]; user->makombpath(projet,verpath); path_make (verpath,"version.dat",verpath); init (verpath); } PUBLIC VERSION_DAT::~VERSION_DAT() { free (path); } /* Met a jour le fichier version.dat. Preserve l'ancien dans version.old Retourne -1 si erreur. */ PUBLIC int VERSION_DAT::save(const USERINFO *user) const { int ret = -1; char ftmp[MAXSIZ_PATH]; char fwork[MAXSIZ_PATH]; if (filebak_before(path,".old",ftmp,fwork)!= -1){ FILE *fout = fopen_err (fwork,"w",0); if (fout != NULL){ setvbuf (fout,NULL,_IOFBF,8000); int nb = list.getnb(); for (int i=0; isave(fout); } fclose (fout); ret = filebak_ok (path,".old",ftmp,fwork); }else{ filebak_abort (path,".old",ftmp,fwork); user->logerr ("Ne peut ecrire le fichier %s\n",path); } }else{ xconf_error (MSG_U(E_NOVERSIONBAK ,"Ne peut pas préserver une copie\n" "du fichier %s") ,path); user->logerr ("Ne peut preserver un backup du fichier %s\n",path); } return ret; } static int version_dat_cmp(const ARRAY_OBJ *p1, const ARRAY_OBJ *p2) { VERSION_ONE *v1 = (VERSION_ONE*) p1; VERSION_ONE *v2 = (VERSION_ONE*) p2; #if 0 int ret = strcmp(v1->vername,v2->vername); if (ret == 0){ ret = v1->rev.cmp(&v2->rev); } #else /* #Sp‚cification: r‚vision / ordre de pr‚sentation Les r‚visions sont tri‚s par ordre num‚rique. Le nom symbolique n'est pas utilis‚ pour le tri. */ int ret = v1->rev.cmp(&v2->rev); #endif return ret; } /* Tri la liste des versions. Cette fonction existe surtout pour assurer que le fichier version.dat reste tri‚, mˆme si un huluberlu le modifie … la main. */ PUBLIC void VERSION_DAT::sort() { /* #Sp‚cification: version.dat / tri Le fichier version.dat est toujours tri‚ dans un ordre pr‚cis. Par version et ensuite par num‚ro de r‚vision. */ list.sort (version_dat_cmp); } PUBLIC VERSION_ONE* VERSION_LIST::getitem (int no) const { return (VERSION_ONE*)ARRAY::getitem(no); } /* Choisit une version d'un projet la plus … date pour un programmeur. 1-Choisit une version dont le nom symbolique est le mˆme que vername. 2-Choisit la derniŠre version livr‚ par le programmeur ou la derniŠre version integr‚e. Prend la plus r‚cente des deux. Cette fonction assume que la liste est d‚j… tri‚e. Elle d‚pend de la fonction add() et sort(). Retourne NULL si ne trouve pas. */ PUBLIC VERSION_ONE *VERSION_DAT::select( const char *vername, const char *progname) // Peut ˆtre NULL // Choisie alors la plus grande version int‚gr‚e { VERSION_ONE *ret = NULL; int nb = list.getnb(); for (int i=0; ivername)==0){ if (progname == NULL){ if (one->rev.type() <= REVISION_INTEGRE) ret = one; }else if (strcmp(one->progname,progname)==0){ ret = one; } } } return ret; } PUBLIC void VERSION_DAT::setcombo (FIELD_COMBO *comb) { for (int i=0; irev.format (revstr); char datestr[11]; date_u2a(one->date,datestr); char tmp[100]; snprintf (tmp,sizeof(tmp)-1,"%s %s %s",one->vername,one->progname ,datestr); comb->addopt (revstr,tmp); } } /* Enregistre une nouvelle r‚vision dans version.dat. Il se peut que la r‚vision existe d‚j…. Ca arrive seulement dans le cas d'une livraison d'int‚gration. On cherche d'abord que la r‚vision existe. Si c'est le cas, on r‚vise l'information. Sinon, on fait la cr‚ation et l'insertion au bon endroit dans la liste. */ PUBLIC void VERSION_DAT::add ( VERSION_ONE *ver) { int nb = list.getnb(); int posins = nb; const char *vername = ver->vername; const REVISION *newrev = ver->getrev(); for (int i=0; ivername,vername)==0 && one->rev.cmp(newrev) < 0) posins = i+1; } list.insert (posins,ver); } /* Enregistre les sp‚cifiquation d'une nouvelle r‚vision dans version.dat. Il se peut que la r‚vision existe d‚j…. Ca arrive seulement dans le cas d'une livraison d'int‚gration. On cherche d'abord que la r‚vision existe. Si c'est le cas, on r‚vise l'information. Sinon, on fait la cr‚ation et l'insertion au bon endroit dans la liste. On retourne la r‚vision cr‚‚. L'appelant pourra y ajouter de l'information textuelle (VERSION_ONE::addinfo()). */ PUBLIC VERSION_ONE *VERSION_DAT::add ( const char *vername, // Nom de la version symbolique const char *progname, // Nom du programmeur ou NULL const REVISION *newrev) { VERSION_ONE *ver = get (newrev); if (ver != NULL){ ver->setprog (progname); }else{ char datestr[11]; date_u2a(date_currentu(),datestr); ver = new VERSION_ONE (vername,progname,newrev,datestr); if (ver != NULL){ add (ver); } } assert (ver != NULL); return ver; } #ifdef TEST void error_showdef (int , ...) { } struct TEST_VER{ char *vername; char *progname; char *version; }; static void test_select( VERSION_DAT *ver, const char *prog, const char *version) { VERSION_ONE *one = ver->select (version,prog); printf ("Select version %s %s -> ",prog,version); if (one == NULL){ printf ("Ne trouve pas\n"); }else{ char datestr[11]; date_u2a(one->date,datestr); char revstr[MAXSIZ_REVISION+1]; one->rev.format(revstr); printf ("%-30s\t%-30s\t%-15s\t%s\n" ,one->vername,one->progname,revstr,datestr); } } static void test ( const char *fname, TEST_VER tb[]) { VERSION_DAT ver(fname); int i=0; while (tb[i].vername != NULL){ REVISION rev(tb[i].version); ver.add (tb[i].vername,tb[i].progname,&rev); i++; } test_select (&ver,"wolfgang","v1"); test_select (&ver,"wolfgang","v2"); ver.save(); } int main (int argc, char *argv[]) { file_unlink("toto.dat"); static TEST_VER tb1[]={ "v1", "jacques", "1.0.1.0", "v2", "-", "2.1", "v1", "wolfgang", "1.0.2.0", "v1", "wolfgang", "1.0.2.1", "v1", "jacques", "1.0.1.1", "v1", "-", "1.1", NULL }; test ("toto.dat",tb1); static TEST_VER tb2[]={ "v1", "jacques", "1.1.1.0", "v2", "-", "2.2", "v2", "wolfgang", "2.1.2.0", "v1", "wolfgang", "1.1.2.1", "v1", "jacques", "1.1.1.1", "v1", "-", "1.2", NULL }; test ("toto.dat",tb2); return 0; } #endif