#include #include #include #include #include #include #include "projet.h" #include "prjlist.h" #include "projetx.m" /* V‚rifie l'environnement d'ex‚cution et g‚nŠre des messages d'erreur Retourne -1 si environnement pas acceptable. */ static int near projet_chkenv (const char *prog) { int ret = -1; char path[MAXSIZ_PATH]; if (path_validate()==-1){ xconf_error (MSG_U(E_IVLDPATH,"Variable PATH invalide")); }else if (prog_getnum(prog) == -1){ xconf_error (MSG_U(E_UNKNOWNUSER ,"L'usager %s est inconnue\n" "Un numéro d'identification\n" "doit être alloué à cet usager") ,prog); }else if (file_findexe("unzip",path)==-1){ xconf_error (MSG_U(E_NOUNZIP ,"La commande unzip n'est pas disponible.\n" "Cette commande est essentielle à l'opération\n" "de l'archive.")); }else{ const char *preload = getenv ("LD_PRELOAD"); if (preload != NULL && strstr(preload,"projetx-vfs")!=NULL){ xconf_error (MSG_B(E_NOVFS ,"projetX ne peut exécuter correctement en mode virtuel\n" ,"projetX can't execute properly in virtual mode\n")); }else{ ret = 0; } } return ret; } /* S'assure que la version est s‚lectionn‚e (ou force une s‚lection). Retourne != 0 si une version est s‚lectionn‚e. */ static int near projetp_chkver ( USERINFO *user, bool force) { if (user->getversion()==NULL || force){ version_setmain (user,NULL); } return user->getversion() != NULL; } static USERINFO *user; static PREFER_INT minlivreespace ("minlivreespace",10000); /* V‚rifie s'il y a suffisamment d'espace pour livrer Retourne 1 si espace suffisant ou si l'usager livre quand mˆme. */ static int projet_chkspace(USERINFO *user) { /* #Sp‚cification: livraison / espace requis Avant chaque livraison, l'espace disque dans /kit/ombre et /kit/livre (test‚ s‚par‚ment mˆme si c'est dans le mˆme volume) est ‚valu‚. S'il y a moins de 10 megs, alors l'usager doit confirm‚ manuellement qu'il d‚sire vraiment livr‚ quand mˆme. Id‚alement, projetx devrait ‚valuer l'espace requis. Par contre il y a telle de chose qui peut arriver dans le mˆme volume au mˆme moment, qu'il est pr‚f‚rable de jouer prudemment. Le 10 megs est en fait un pr‚f‚rence (en K). La pr‚f‚rence est */ long minesp = minlivreespace.getval(); const char *kitlivre = user->getlivre(); long spclivre = file_getfree (kitlivre); const char *kitombre = user->getombre(); long spcombre = file_getfree (kitombre); int ret = 1; if (spcombre == -1 || spclivre == -1){ char buf[1000]; sprintf (buf,MSG_U(Q_CANTFREESPACE ,"Ne peut déterminer l'espace libre\n" "dans %s et %s\n" "Voulez vous livrer quand même ?") ,kitombre,kitlivre); ret = xconf_yesno ("",buf,help_nil)==MENU_YES; }else if (spcombre < minesp || spclivre < minesp){ char buf[1000]; sprintf (buf,MSG_U(Q_NOFREESPACE ,"Espace disponible insuffisant\n" "%s : %ld K\n" "%s : %ld K\n" "Espace minimum suggéré : %ld K\n" "Voulez vous livrer quand même ?") ,kitombre,spcombre ,kitlivre,spclivre ,minesp); ret = xconf_yesno ("",buf,help_nil) == MENU_YES; } return ret; } static const char *u_version; static const char *u_commande; static const char *u_livraison; static const char *u_menage; static const char *u_fusion_simple; static const char *u_fusion_complexe; static const char *u_fusion_edit; static const char *u_commande_autre; static const char *c_compilation; static const char *c_livraison; static const char *c_fusion_prepare; static const char *c_fusion_edit; static const char *c_commande_autre; static const char *c_diff; static const char *a_initarch; static const char *a_creation; static const char *a_destruction; static const char *a_menage; static const char *a_importation; /* Fonction qui active les principaux dialogues d‚pendant de la s‚lection du menu. */ static void projet_main (const char *key) { bool pasfait = false; if (key == u_livraison){ /* #Sp‚cification: menu / usager / Livraison S‚lectionne une r‚vision dans l'espace usager et livre les sources modifi‚s dans l'archive. L'usager peut livrer s‚lectivement les projets qu'il veut. La livraison introduit une nouvelle r‚vision dans l'archive correspondant … la nouvelle combinaison de sources des diff‚rents projets. Le r‚pertoire racine sera renomm‚ pour confirmer l'association de l'environnement usager avec cette nouvelle r‚vision. L'op‚ration de livraison est non destructive. On peut livrer et continuer … travailler dans la nouvelle r‚vision sans intervention particuliŠre. Mˆme si des fichiers sont effac‚s, ils continuent d'ˆtre "logiquement" disponible. */ char path[MAXSIZ_PATH]; user->gethome(path); if (projet_chkspace(user)==1 && version_setmain (user,path)){ PRJLISTE_LIVRE g; projet_select (user,g); } }else if (key == u_menage){ // Fait le m‚nage de l'environnement usager }else if (key == u_fusion_simple){ /* #Sp‚cification: menu / usager / fusion / prepare simple La pr‚paration du fusion dans l'espace usager consiste en la s‚lection de deux r‚visions pr‚alablement install‚ dans l'espace usager. La premiŠre est la r‚vision temporaire qui contient des sources qui n'ont pas ‚t‚ livr‚s dans l'archive. La seconde est la r‚vision destination qui a ‚t‚ install‚ sans aucune modification. AprŠs la s‚lection, projetx ‚valuera les changements qui ont ‚t‚ fait dans la version temporaire et construira pour chaque r‚pertoire un guide qui controlera l'‚dition de la fusion. La pr‚paration d'un fusion produit une s‚rie de fichier intŠgre.dat dans la r‚vision destination. Elle n'a pas d'effet dans la r‚vision temporaire. Donc c'est un op‚ration non dommageable. */ ufusion_prepare (user); }else if (key == u_version){ projetp_chkver (user,true); }else if (key == u_commande){ if (projetp_chkver (user,false)){ /* #Sp‚cification: menu / usager / commande AprŠs qu'un projet ait ‚t‚ install‚ dans l'environnement commun /kit/build, on peut en installer des parties dans l'environnement de travail de l'usager. On choisit une r‚vision, ensuite les projets sur lesquels on veut travailler. Un sous-r‚pertoire portant le num‚ro de la r‚vision sera cr‚‚ et les diff‚rents projets s‚lectionn‚s seront install‚s. */ PRJLISTE_GET g; projet_select (user,g); } }else if (key == u_commande_autre){ if (projetp_chkver (user,false)){ /* #Sp‚cification: menu / usager / commande autre Lorsqu'on s‚lectionne un projet pour installation dans notre environnement, on peut s‚lectionner les projets qui ne font pas partie de la r‚vision. Cette possibilit‚ permet de ressuciter des projets qui ont ‚t‚ ‚limin‚s. Cette quˆte vers le pass‚ est g‚n‚ralement une source de joie et de bonheur sans fin... */ PRJLISTE_GETALL g; projet_select (user,g); } }else if (key == u_fusion_edit){ /* #Sp‚cification: menu / usager / fusion Cet option pr‚sente une liste de r‚pertoires o— une intervention est requise. Pour chaque r‚pertoire l'usager obtient une liste des fichiers … combiner ou importer dans la r‚vision destination. */ #ifdef MISSING integre_do (user); #endif }else if (key == u_fusion_complexe){ /* #Sp‚cification: menu / usager / fusion / pr‚pare complexe On doit s‚lectionner la r‚vision qui servira de base … l'int‚gration (BASE). Ensuite on s‚lectionnera les r‚visions qui seront fusionn‚s (MODIFs). Pour chaque r‚vision, on devra associ‚ une r‚vision r‚f‚rence (REFs). En gros, la fusion peut ˆtre repr‚sent‚ par l'‚quation. RESULTAT = BASE + (MODIFs - REFs) Le r‚sultat sera plac‚ dans la r‚vision s‚lectionn‚ et l'‚dition se fera la. Il est pr‚f‚rable de livrer pr‚alablement. On pourra plus facilement d‚terminer la provenance d'un changement plus tard. */ bfusion_setup (user,true); }else if (key == c_livraison){ /* #Sp‚cification: menu / commun / livraison AprŠs une fusion dans l'espace commun (/kit/build), on se retrouve g‚n‚ralement avec une s‚rie de changements qu'il faut rappatrier dans l'archive. On s‚lectionnera g‚n‚ralement tous les projets. La r‚vision reste alors disponible dans l'espace commun pour que les usagers prennent une copie de cette r‚vision. Il est pr‚f‚rable de livrer les corrections avant que le premier usager prenne une copie. */ char path[MAXSIZ_PATH]; user->getbld(path); if (projet_chkspace(user)==1 && version_setmain (user,path)){ USERBUILD *build; { VERSION_DAT ver(user); const REVISION *rev = user->getrevdst(); const char *vername = user->getversion(); VERSION_ONE *one = ver.get (rev); if (one->isinteg()){ build = new USERINTG (vername,rev,rev); }else{ build = new USERBUILD (vername,rev); } } PRJLISTE_LIVREB g; projet_select (build,g); delete build; } }else if (key == c_compilation){ if (projetp_chkver(user,1)){ /* #Sp‚cification: menu / Commun / Compilation Avant que les usagers puissent installer une r‚vision dans leur environnement, il faut qu'elle soit install‚e dans /kit/build et rebatie (compil‚e … partir de 0). On s‚lectionne une r‚vision et les sous-projets qu'on veut install‚. Bien qu'on puisse s‚lectionner les projets s‚par‚ment, il est souhaitable de tout installer, parce que l'op‚ration de compilation risque de ne pas fonctionner. */ USERBUILD build(user->getversion(),user->getrevsrc()); PRJLISTE_BUILD g; projet_select (&build,g); } }else if (key == c_commande_autre){ if (projetp_chkver(user,1)){ /* #Sp‚cification: menu / commun / commande autre Lorsqu'on s‚lectionne un projet pour installation dans l'environnement commun, on peut s‚lectionner les projets qui ne font pas partie de la r‚vision. Cette possibilit‚ permet de ressuciter des projets qui ont ‚t‚ ‚limin‚s. Cette quˆte vers le pass‚ est g‚n‚ralement une source de joie et de bonheur sans fin... */ USERBUILD build(user->getversion(),user->getrevsrc()); PRJLISTE_BUILDALL g; projet_select (&build,g); } }else if (key == c_diff){ diff_select (user); }else if (key == c_fusion_prepare){ if (projetp_chkver(user,1)){ /* #Sp‚cification: menu / commun / fusion / pr‚pare On doit s‚lectionner la r‚vision qui servira de base … l'int‚gration (BASE). Ensuite on s‚lectionnera les r‚visions qui seront fusionn‚s (MODIFs). Pour chaque r‚vision, on devra associ‚ une r‚vision r‚f‚rence (REFs). En gros, la fusion peut ˆtre repr‚sent‚ par l'‚quation. RESULTAT = BASE + (MODIFs - REFs) La r‚vision RESULTAT sera automatiquement allou‚e. */ USERBUILD build(user->getversion(),user->getrevsrc()); bfusion_setup (&build,false); } }else if (key == c_fusion_edit){ if (projetp_chkver(user,1)){ // Mˆme sp‚cification que: menu / usager / fusion / ‚dition { USERBUILD build(user->getversion(),user->getrevsrc()); //integre_do (&build); } } }else if (key == a_creation){ /* #Sp‚cification: menu / admin / cr‚ation On peut cr‚er une nouvelle r‚vision simplement en renommant une ancienne. On peut alors cr‚er un nouveau num‚ro ainsi qu'un nouveau nom. On utilise ce m‚canisme lorsqu'on livre un produit aux clients. Soit l'exemple suivant: A partir de la r‚vision 0.0, les programmeurs travaillent durant 1 an et produisent finalement la r‚vision 0.80. Cette version est envoy‚e aux clients. Pendant qu'une partie de l'‚quipe de programmation sera occup‚e … corriger les bugs de derniŠre minutes, l'autre partie continuera … d‚velopper sans r‚serve. Pour ‚viter la confusion on cr‚er deux arbres de d‚veloppement. # 0.80 -- 0.81 ... 0.89 --+-- ... 0.95 -------+ | | +-- 1.15 | +-- 1.06 ... 1.14 --+ | | 1.0 -- 1.01 ... 1.05 --+ # Initialement 1.0 et 0.80 sont ‚quivalent. En principe 0.80 ‚voluera peu en terme du nombre de fichiers sources modifi‚. Par contre, il y aura plusieurs r‚visions cr‚‚es, chacune correspondant … une version officielle envoy‚e aux usager. 1.0 de son cot‚ sera chambard‚e pour incorporer 1,000,000 de nouvelles options. On voit sur le sch‚ma que 1.06 et 1.15 seront des r‚visions qui fusionnent les corrections de bugs de la version 0.80 aux nouveaux d‚veloppement de 1.0. */ { VERSION_DAT version(user); version.creation (user); } }else if (key == a_destruction){ /* #Sp‚cification: menu / admin / destruction Un jour :-), on pourra ‚liminer une s‚rie de r‚vision de l'archive. Ces r‚visions disparaitront, mais pas n‚cessairement les fichiers qui les composent, puisque ces fichiers peuvent appartenir … plusieurs r‚visions diff‚rentes. *** Pas encore op‚rationnel *** */ pasfait = true; }else if (key == a_menage){ /* #Sp‚cification: menu / admin / m‚nage Op‚ration longue et p‚nible qui localisent tous les fichier de l'archive qui ne sont pas associ‚s … aucune r‚visions. Ces fichiers sont simplement effac‚s ou encore une liste est cr‚‚e permettant de faire une copie de s‚curit‚ et ensuite de les effac‚s. *** Pas encore op‚rationnel *** */ pasfait = true; }else if (key == a_importation){ /* #Sp‚cification: menu / admin / Importation Pr‚sente la liste des toutes les r‚visions qui ont ‚t‚ cr‚‚e dans des pays lointain et placer dans /kit/livre. Seule les r‚visions qui ne sont pas d‚j… dans l'archive sont pr‚sent‚e. On les s‚lectionne et elles sont imm‚diatement incorpor‚es. */ if (projet_chkspace(user)==1){ import (user); } }else{ pasfait = true; } if (pasfait){ xconf_error (MSG_U(E_PASFAIT ,"Je suis désolé ... très désolé\n" "mais ce que vous me demandé là\n" "n'est pas encore disponible.\n")); } } #if 0 /* Obtient le path courant de l'usager avant d'appeler projetx. Ce programme (projetp) doit ex‚cuter dans un r‚pertoire sp‚cifique o— les fonts de GEM doivent se trouver. Le programme projetx note le r‚pertoire courant et l'inscrit dans un fichier portant le nom de l'usager avec l'extension "cwd". */ static int near projet_readcwd( const char *prog, char *cwd, int sizecwd) { int ret = -1; char path[MAXSIZ_PATH]; sprintf (path,"%s.cwd",prog); FILE *fin = fopen_err (path,"r",1); cwd[0] = '\0'; if (fgets(cwd,sizecwd-1,fin)!=NULL){ strip_end (cwd); ret = 0; } fclose (fin); return ret; } #endif int main (int argc, char *argv[]) { dialog_parseuioptions(argc, argv); etc_loadmsg(); html_registerpath ("/usr/lib/projetx"); const char *prog = prog_getid (true); if (projet_chkenv(prog)!=-1){ DIALOG_MENU dia; u_version = MSG_U(M_VERSION,"Version"); u_commande = MSG_U(M_COMMANDE,"Commande"); u_livraison = MSG_U(M_LIVRAISON,"Livraison"); u_menage = MSG_U(M_MENAGE,"Menage"); u_fusion_simple = MSG_U(M_FUSIONSIMPLE,"Fusion simple"); u_fusion_complexe = MSG_U(M_FUSIONCOMPLEXE,"Fusion complexe"); u_fusion_edit = MSG_U(M_FUSIONEDIT,"Édition de la fusion"); u_commande_autre = MSG_U(M_COMMANDEAUTRE,"Commande autre"); c_compilation = MSG_U(M_COMPILATION,"Compilation"); c_livraison = MSG_U(M_C_LIVRAISON,"Livraison"); c_fusion_prepare = MSG_U(M_C_FUSION_PREP,"Prépare fusion"); c_fusion_edit = MSG_U(M_C_FUSION_EDIT,"Édite la fusion"); c_commande_autre = MSG_U(M_C_COMMANDE_AUTRE,"Commande autre"); c_diff = MSG_U(M_C_DIFF,"Fichier patch"); a_initarch = MSG_U(M_INITARCH,"Initialise l'archive"); a_creation = MSG_U(M_CREATION,"Création"); a_destruction = MSG_U(M_DESTRUCTION,"Destruction"); a_menage = MSG_U(M_A_MENAGE,"Ménage"); a_importation = MSG_U(M_IMPORT,"Importation"); static const char *opts[]={ "-", MSG_U(M_USAGER,"Usager"), "", u_version, "", u_commande, "", u_livraison, "", u_menage, "", u_fusion_simple, "", u_fusion_complexe, "", u_fusion_edit, "", u_commande_autre, "-", MSG_U(M_COMMUN,"Commun"), "", c_compilation, "", c_livraison, "", c_fusion_prepare, "", c_fusion_edit, "", c_commande_autre, "", c_diff, "-", MSG_U(M_ADMIN,"Admin"), "", a_initarch, "", a_creation, "", a_destruction, "", a_menage, "", a_importation, NULL }; dia.new_menuitems (opts); int nof = 0; while (1){ MENU_STATUS code = dia.editmenu ( MSG_U(T_PROJETX,"ProjetX") ,"" ,help_nil ,nof,0); if (code == MENU_QUIT || code == MENU_ESCAPE){ break; }else{ const char *sel = dia.getmenustr(nof); if (sel == a_initarch){ /* #Sp‚cification: menu / admin / initialise l'archive Ce menu ne sert qu'un fois pour créer une nouvelle archive. Il y aura création des répertoire et du minimum de fichier pour démarrer l'archivage. */ arch_init(); }else{ user = userinfo_findenv (prog); if (user != NULL){ projet_main (sel); delete user; } } } } } return 0; }