<?php
/**
* $Id$
* $Log$
*/
Class RCS {
/*
La classe RCS sert d'interface avec les programmes (bas niveau) rcs
Attribut:
$_path String virtual path in RCSManager (pwd)
$_realpath String real path in the file system
$_valid Boolean true if RCS archive can be found, i.e.
RCS subdirectory exist and is readable
Method:
RCS($path) Builder of this class
Table getFilesTable() give the table of RCS archives
RCSFile getFile($name) give an RCS object about the file name
Table checkin($argument) do a check in
Table checkout($argument) do a cheak out
*/
/* Serveur path */
var $_path;
/* Real system path */
var $_realpath;
/* Is RealPath/RCS is valid */
var $_valid = false;
/**
* Constructeur de la classe
* @param $path
*/
function RCS($path)
{
$this->_path = $path;
$this->_realpath = BASE_dir . "$this->_path";
if(is_dir("$this->_realpath/RCS"))
if(is_readable("$this->_realpath/RCS"))
$this->_valid = true;
}/* RCS() */
/**
* Réalise un CheckIn du fichier spécifié dans ce système RCS
* @param $argument table des argument du check in
*/
function checkin($argument)
{
if(!is_uploaded_file($argument[fichier]))
return(array(code => false, cmd => "erreur", msg => "no file uploaded!"));
if(!checkTmpDIR())
return(array(code => false, cmd => "erreur", msg => "can't use temporary folder."));
$options = "";
$pattern = array('/\\\/', '/\\$/', '/`/', '/\"/');
$replace = array('\\\\\\', '\\\$', '\`', '\"' );
// Real name of the file
if($argument[rcsname] != "")
$name = "$argument[rcsname]";
else
$name = "$argument[fichier_name]";
$archivename = "$this->_realpath/RCS/$name,v";
if(file_exists("$archivename") && ($argument[type] == "initial"))
return(array(code => false, cmd => "erreur",
msg => "You ask for initial revision but archive name exist."));
if($argument[revision])
$options = "$options -r$argument[revision]";
if($argument[log])
$options = "$options -m\"" . preg_replace($pattern, $replace, $argument[log]) . "\"";
if($argument[description])
$options = "$options -t-\"" . preg_replace($pattern, $replace, $argument[description]) . "\"";
// location of the uploaded file
$fichier = TmpDIR . "/$name";
// making the command line
putenv("PHP_REMOTE_USER_LOGIN=" . REMOTE_LOGIN);
$command = "mv $argument[fichier] \"$fichier\" && chmod 666 \"$fichier\"";
if(!is_dir("$this->_realpath/RCS"))
$command = "$command && " . WRAPPER . " mkdir --mode 0775 \"$this->_realpath/RCS\"";
$command = "$command && " . WRAPPER . " ci $options \"$fichier\" \"$archivename\"";
if($argument[type] == "initial")
$command = "$command && " . WRAPPER . " rcs -a" . BASE_acl . " \"$archivename\"";
exec("( $command ) 2>&1" , $flow, $code);
// prepare results message
$result[code] = $code == 0;
$result[cmd] = preg_replace("/\&\&/", "&&\n", $command);
$result[msg] = "";
while(list($k,$v) = each($flow)) $result[msg] .= "$v\n";
return($result);
}/* checkin()*/
/**
* Réalise un CheckOut du fichier spécifié dans ce système RCS
*/
function checkout($argument)
{
$result = array();
$flow = array();
$options = "";
$name = $argument[file];
if($argument[revision]) $options = "$options -r$argument[revision]";
if($argument[verou] == "yes") $options = "$options -l";
switch($argument[destination])
{
case "download" :
$dir = TmpDIR;
//$tmpfile = "/tmp/phpRCSmanager/" + microtime();
if(!checkTmpDIR())
return(array(msg => "can't use tmp folder: $dir"));
if(is_file("$dir/$name"))
if(!unlink("$dir/$name"))
return(array(msg => "can't delete $dir/$name"));
break;
case "url" :
$dir = $this->_realpath;
if(is_file("$dir/$name"))
$command = WRAPPER . " rm \"$dir/$name\" && ";
break;
}
// making the command line
putenv("PHP_REMOTE_USER_LOGIN=" . REMOTE_LOGIN);
$command = $command . WRAPPER . " co $options \"$dir/$name\" \"$this->_realpath/RCS/$name,v\"";
exec("( $command ) 2>&1" , $flow, $code);
if(code == 0)
$result[id] = $name;
$result[code] = $code == 0;
$result[cmd] = preg_replace("/\&\&/", "&&\n", $command);
$result[msg] = "";
while(list($k,$v) = each($flow)) $result[msg] .= "$v\n";
return($result);
}/* checkout()*/
/**
* Construit la tables des fichiers RCS du répertoire
* @return Table liste d'objet: "RCSFile".
*/
function getFilesTable()
{
// teste de base
if(!$this->_valid) return (array());
exec("cd \"$this->_realpath\"; rlog -t RCS/*", $flow, $code);
if($code != 0) return(array());
$table = $this->parseFilesListing($flow);
$result = array();
reset($table);
while(list($k,$v) = each($table))
$result[$v["Workingfile"]] = new RCSFile($v["Workingfile"], $this->_path ,$v);
return($result);
}/* getFilesTable() */
/**
* Crée un object "RCSFile" avec toutes les infos données par rlog
* @param $name nom du fichier
* @return Object:RCSFile
*/
function getFile($name)
{
// teste de base
if(!$this->_valid) return (array());
exec("cd \"$this->_realpath\"; rlog \"$name\"", $flow, $code);
if($code != 0) return(array());
//$file->addAttribut("logs", $this->parserLogs($flow));
$table = $this->parseFilesListing($flow);
return(new RCSFile($table[0]["Workingfile"], $this->_path ,$table[0]));
}/* logFile() */
/**
* Parse le détail des révisions
* @param $input le flux d'entrée des logs d'un fichier
* @return Table chaque entrée correspondant a une révision, la clé étant son numéro.
*/
function parseLogs($input)
{
$final = array();
do{
// matche une ligne remplie de "-"
if(preg_match("/^\-{23,}$/",$v))
{
list($k,$v) = each($input);
// récupère les infos de la première lignes.
$mask = "/^(revision )([0-9]+(?:\.[0-9]+)+)(?:\t(locked by: )(.*)|)$/";
preg_match($mask, $v, $matches);
$number = $matches[2]; // numéro de révision
$locker = $matches[4]; // Utilisateur vérouillant
$result = "";
while(!preg_match("/(^\={23,}$)|(^\-{23,}$)/", $v))
{
$result = "$result$v\n";
list($k,$v) = each($input);
}
prev($input);
$final[$number] = $result;
}
}while((list($k,$v) = each($input)) && !preg_match("/^\={23,}$/",$v));
return($final);
}/*parseLogs()*/
/**
* Parser du listing généré par "rlog"
* @param $Input le flux d'entrée
* @return Table chaque entrée correspondant à un fichier.
*/
function parseFilesListing($input)
{
reset($input);
$finalResult = array();
$result = array();
//Parcours du tableau
while(list($k,$v) = each($input))
{
// coupe la chaine de caracteres en plusieurs sous chaine
$valeur=explode(":", $v);
$valeur[0]=ereg_replace(" ", "", $valeur[0]);
$tmp = array();
switch($valeur[0])
{
// traitement des champs multiples
case "locks" :
case "accesslist" :
case "symbolicnames":
$champs = trim($valeur[0]);
$tmp["attrib"]=trim($valeur[1]);
list($k,$v) = each($input);
while(preg_match("/^\t/",$v))
{
//$valeur = explode(":",$v);
//$tmp[trim($valeur[0])] = trim($valeur[1]);
$tmp[] = trim($v);
list($k,$v) = each($input);
}
prev($input);
$result[$champs] = $tmp;
break;
// traitement du champs description
case "description" :
list($k,$v) = each($input);
$description = "";
while(!preg_match("/(^\={23,}$)|(^\-{23,}$)/", $v))
{
$description = "$description\n$v";
list($k,$v) = each($input);
}
$result["description"] = $description;
// Y a t'il les logs des révisions ?
if(preg_match("/^\-{23,}$/", $v))
{
prev($input);
$result["logs"] = $this->parseLogs($input);
}else
$result["logs"] = array();
each($input);
// Termine le fichier courrant
$resultFinal[] = $result;
$result = array();
break;
case "":
break;
// Affectation des valeurs a chaque champs du tableau
default:
$result[trim($valeur[0])]=trim($valeur[1]);
}/*switch()*/
}/*while()*/
return($resultFinal);
}/*parseFilesListing()*/
}/*class RCS*/
?>