RCS.php

00001 <?php
00002 /*
00003  * Copyright (C) 2003-2004 Polytechnique.org
00004  * http://opensource.polytechnique.org/
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  */
00020 
00021 
00022 require_once 'VCS/Spool.php';
00023 // dependency on PEAR
00024 require_once 'System.php';
00025 
00028 class Diogenes_VCS_RCS extends Diogenes_VCS_Spool {
00030   var $rcsdir;
00031 
00033   var $login;
00041   function Diogenes_VCS_RCS(&$caller,$alias,$login,$init = false) {
00042     global $globals;
00043     $this->Diogenes_VCS_Spool($caller,$alias);
00044     $this->rcsdir = "{$globals->rcsroot}/$alias";
00045     $this->login = $login;
00046 
00047     // if we were asked to, created directories
00048     if ($init) {
00049       if (!is_dir($this->rcsdir))
00050         mkdir($this->rcsdir, 0700);
00051       if (!is_dir($this->datadir))
00052         mkdir($this->datadir, 0700);
00053     }
00054 
00055     // check RCS directory
00056     if (!is_dir($this->rcsdir) || !is_writable($this->rcsdir))
00057       $this->kill("'{$this->rcsdir}' is not a writable directory");
00058 
00059     // check spool directory
00060     if (!is_dir($this->datadir) || !is_writable($this->datadir))
00061       $this->kill("'{$this->datadir}' is not a writable directory");
00062   }
00063 
00064 
00070   function rcsPath($parent="",$entry="") {
00071     $this->checkPath($parent,$entry);
00072     return $this->rcsdir.($parent ? "/$parent": "") . ($entry ? "/$entry" : "");
00073   }
00074 
00075 
00081   function rcsFile($dir,$file) {
00082     return $this->rcsPath($dir,$file).",v";
00083   }
00084 
00085   
00091   function checkFile($dir, $file) {
00092     return is_file($this->rcsFile($dir, $file));
00093   }
00094 
00095   
00101   function checkDir($dir) {
00102     return is_dir($this->rcsPath($dir))
00103         && is_writable($this->rcsPath($dir))
00104         && is_dir($this->spoolPath($dir))
00105         && is_writable($this->spoolPath($dir));
00106   }
00107 
00108 
00116   function checkout($dir,$file,$rev,$output)
00117   {
00118     $this->info("RCS : checkout out $file ($rev)..");
00119     $rfile = $this->rcsFile($dir,$file);
00120     if ($this->cmdExec("co -q -r".escapeshellarg($rev)." ".escapeshellarg($rfile)." ".escapeshellarg("$output/$file")))
00121     {
00122       $this->info("RCS : Error, checkout failed!");
00123       $this->info($this->cmdStatus());
00124       return;
00125     }         
00126     return "$output/$file";
00127   }
00128   
00129 
00137   function commit($dir,$file,$content,$message="")
00138   {
00139     $this->info("RCS : checking in '$file'..");
00140 
00141     // check directories
00142     if (!$this->checkDir($dir)) {
00143         // error
00144         $this->info("RCS : Error, RCS sanity check for '$dir' failed!");
00145         return false;
00146     }
00147 
00148     // log commit attempt
00149     $this->log("rcs_commit","{$this->alias}:$dir/$file:$message");
00150 
00151     $sfile = $this->spoolPath($dir,$file);
00152     $rfile = $this->rcsFile($dir,$file);
00153 
00154     // if the RCS file does not exist, create it
00155     if (!file_exists($rfile)) {
00156       if ($this->cmdExec("echo '' | rcs -q -i ".escapeshellarg($rfile)))
00157       {
00158         // error
00159         $this->info("RCS : Error, could not initialise RCS file '$rfile'!");
00160         $this->info($this->cmdStatus());
00161         return false;
00162       }
00163     }
00164 
00165     // lock the spool file
00166     if ($this->cmdExec("co -q -l ".escapeshellarg($rfile)." ".escapeshellarg($sfile)))
00167     {
00168         // error
00169         $this->info("RCS : Error, could not get RCS lock on file '$file'!");
00170         $this->info($this->cmdStatus());
00171         return false;
00172     }
00173     if ($fp = fopen($sfile,"w")) {
00174       fwrite($fp,$content);
00175       fclose($fp);
00176       
00177       if ($this->cmdExec("ci -q -w".escapeshellarg($this->login). ($message ? " -m".escapeshellarg($message) : "").
00178              " ". escapeshellarg($sfile). " ". escapeshellarg($rfile)))
00179       {
00180         // error
00181         $this->info("RCS : Error, checkin failed!");
00182         $this->info($this->cmdStatus());        
00183         return false;
00184       }
00185       
00186       if ($this->cmdExec("co -q ".escapeshellarg($rfile)." ".escapeshellarg($sfile)))
00187       {
00188         // error
00189         $this->info("RCS : Error, checkout after checkin failed!");
00190         $this->info($this->cmdStatus());        
00191         return false;
00192       }
00193     }
00194     return true;
00195   }
00196 
00197 
00198   
00206   function copy($sdir,$sfile,$ddir,$dfile)
00207   {    
00208     $this->info("RCS : copying '$sfile' to '$ddir/$dfile'..");
00209     
00210     $spath = $this->spoolPath($sdir, $sfile);
00211     if (!is_file($spath)) {
00212       $this->info("Error: source file '$spath' does not exist!");
00213       return false;
00214     }
00215     if (!$this->checkDir($ddir)) 
00216     {
00217       $this->info("Error: directory '$ddir' does not exist!");
00218       return false;
00219     }
00220     if ($this->checkFile($ddir, $dfile))
00221     {
00222       $this->info("Error: file '$dfile' already exists in '$ddir'!");
00223       return false;    
00224     }
00225     return $this->commit($ddir,$dfile,
00226                         file_get_contents($spath),
00227                         "copied from '$ddir/$sfile'");
00228   }
00229    
00230      
00236   function del($dir,$file) {
00237     $this->info("RCS : deleting '$file'..");
00238     $this->log("rcs_delete","{$this->alias}:$dir/$file");
00239     @unlink($this->spoolPath($dir,$file));
00240     @unlink($this->rcsFile($dir,$file));
00241   }
00242 
00243 
00251   function diff($dir,$file,$r1,$r2)
00252   {
00253     $rfile = $this->rcsFile($dir,$file);
00254     $this->info("RCS : diffing '$file' ($r1 to $r2)..");
00255     $this->cmdExec("rcsdiff  -r".escapeshellarg($r1). " -r".escapeshellarg($r2)." ".escapeshellarg($rfile));
00256     return $this->cmd_output;
00257   }
00258 
00259 
00267   function importWordFile($dir,$htmlfile,$wordfile)
00268   {
00269     global $globals;
00270     
00271     if (!$globals->word_import) {
00272       $this->info("Error : support for word import is disabled!");
00273       return false;
00274     }
00275 
00276     $func = "importWordFile_{$globals->word_import}";
00277     
00278     if (!method_exists($this, $func))
00279     {
00280       $this->info("Error : the utility '$globals->word_import' is not supported!");
00281       return false;
00282     }
00283     
00284     return $this->$func($dir, $htmlfile, $wordfile);
00285   }
00286 
00287 
00295   function importWordFile_wvHtml($dir,$htmlfile,$wordfile)
00296   {
00297     $tmphtmlfile = "importWord.html";
00298     if (($tmpdir = System::mktemp('-d')) == false) {
00299       $this->info("Error : could not create temporary directory!");
00300       return false;
00301     }
00302 
00303     if ($this->cmdExec("wvHtml --targetdir=".escapeshellarg($tmpdir).
00304            " --charset=iso-8859-15 ".
00305            escapeshellarg($wordfile)." ".escapeshellarg($tmphtmlfile)))
00306     {
00307       $this->info("Error : wvHtml returned an error!");
00308       $this->info($this->cmdStatus());
00309       return false;
00310     }
00311 
00312     if (!$dh = opendir($tmpdir)) {
00313       $this->info("Error : could not find temporary directory '$tmpdir'!");
00314       return false;
00315     }
00316     
00317     // process the files generated by wvHtml
00318     $ok = true;
00319     while (($myentry = readdir($dh)) != false) {
00320       if (is_file($myfile = "$tmpdir/$myentry")) {
00321         if ($myentry == $tmphtmlfile) {
00322           $ok = $ok &&
00323           $this->commit($dir,$htmlfile,
00324                         $this->importHtmlString(file_get_contents($myfile)),
00325                         "Word file import");
00326         } else {
00327           $ok = $ok &&
00328           $this->commit($dir,$myentry,file_get_contents($myfile),
00329                         "Word file import");
00330         }
00331       }
00332     }
00333     closedir($dh);
00334 
00335     return $ok;
00336   }
00337 
00338 
00344   function logEntries($dir,$file) {
00345     $rfile = $this->rcsFile($dir,$file);
00346     $this->cmdExec("rlog ".escapeshellarg($rfile));
00347     return $this->cmd_output;
00348   }
00349 
00350 
00356    function logParse($dir,$file)
00357    {
00358     // get the log, drop last 2 lines
00359     $lines = $this->logEntries($dir,$file);
00360     array_pop($lines);
00361     array_pop($lines);
00362 
00363     // split into revision, drop first block
00364     $revs = split("----------------------------\n", implode("\n",$lines));
00365     array_shift($revs);
00366 
00367     // parse info about the revisions
00368     $revinfo = array();
00369     foreach ($revs as $rev) {
00370       $myrev = array();
00371       $lines = explode("\n",$rev);
00372       preg_match("/^revision (.+)$/",array_shift($lines),$res);
00373       $myrev['rev'] = $res[1];
00374       preg_match("/^date: ([^;]+);  author: ([^;]+); .*$/",array_shift($lines),$res);
00375       $myrev['date'] = $res[1];
00376       $myrev['author'] = $res[2];
00377       $myrev['log'] = implode("\n",$lines);
00378       array_push($revinfo,$myrev);
00379     }
00380     return $revinfo;
00381   }
00382 
00383 
00391   function move($sdir,$sfile,$ddir,$dfile)
00392   {    
00393     $this->info("RCS : moving '$sfile' to '$ddir/$dfile'..");
00394     
00395     
00396     // check source files
00397     $spath = $this->spoolPath($sdir, $sfile);
00398     $srpath = $this->rcsFile($sdir, $sfile);    
00399             
00400     if (!is_file($spath)) {
00401       $this->info("Error: source file '$spath' does not exist!");
00402       return false;
00403     }
00404     
00405     if (!is_file($srpath)) {
00406       $this->info("Error: source RCS file '$srpath' does not exist!");
00407       return false;
00408     }
00409     
00410     // check destination
00411     $dpath = $this->spoolPath($ddir, $dfile);
00412     $drpath = $this->rcsFile($ddir, $dfile);    
00413     
00414     if (!$this->checkDir($ddir)) 
00415     {
00416       $this->info("Error: directory '$ddir' does not exist!");
00417       return false;
00418     }
00419     
00420     if (file_exists($dpath)) {
00421       $this->info("Error: file '$dfile' already exists in '$ddir'!");
00422       return false;    
00423     }
00424     
00425     if (file_exists($drpath)) {
00426       $this->info("Error: file '".basename($drpath)."' already exists in '$ddir'!");
00427       return false;    
00428     }
00429     
00430     if (!rename($spath, $dpath))
00431     {
00432       $this->info("Error: failed to move '".basename($spath)."' to '".basename($dpath)."' in '$ddir'!");
00433       return false;        
00434     }
00435     
00436     if (!rename($srpath, $drpath))
00437     {
00438       $this->info("Error: failed to move '".basename($srpath)."' to '".basename($drpath)."' in '$ddir'!");
00439       return false;        
00440     }
00441 
00442         
00443     //$this->log("rcs_move","{$this->alias}:$dir/$file");    
00444     
00445     return true;
00446   }
00447  
00448  
00454   function newdir($parent,$dir)
00455   {
00456     @mkdir($this->rcsPath($parent,$dir),0700);
00457     @mkdir($this->spoolPath($parent,$dir),0700);
00458   }
00459 
00460 
00468   function dispDiff($dir,$file,$r1,$r2)
00469   {
00470     $lns = "[0-9]+|[0-9]+,[0-9]+";
00471 
00472     // get diff, strip any leading comments
00473     $lines = $this->diff($dir,$file,$r1,$r2);    
00474     $line = "";
00475     while (!preg_match("/^($lns)([acd])($lns)/",$line))
00476       $line = array_shift($lines);
00477     array_unshift($lines,$line);
00478     $raw = implode("\n",$lines);
00479 
00480     $blocks = preg_split("/($lns)([acd])($lns)\n/",$raw,-1,PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
00481     $out = array();
00482 
00483     $lold = array_shift($blocks);
00484     while ($lold!='')
00485     {
00486       $type = array_shift($blocks);
00487       $lnew = array_shift($blocks);
00488       $diff = array_shift($blocks);
00489 
00490       switch ($type) {
00491       case 'c':
00492         list($a,$b) = split("---\n",$diff,2);
00493         break;
00494       case 'a':
00495         $a = "";
00496         $b = $diff;
00497         break;
00498       case 'd':
00499         $a = $diff;
00500         $b = "";
00501         break;
00502       }
00503       array_push($out,array($lold,$type,$lnew,$a,$b));
00504       $lold = array_shift($blocks);
00505     }
00506     //array_push($out,array($type,$a,$b));
00507     return $out;
00508   }
00509 
00510 
00517   function dispDir($dir,$loc,$canedit) {
00518     $entries = array();
00519 
00520     if ($pdir = @opendir($this->rcsPath($dir))) {
00521       while ( ($file = readdir($pdir)) !== false) {
00522         if ( ($file != ".") && ($file != "..") )
00523         {
00524           $entry = $this->dispEntry($dir,$loc,$file,$canedit);
00525           if (!empty($entry)) 
00526             array_push($entries, $entry);
00527         }
00528       }
00529       closedir($pdir);
00530     }
00531     return $entries;
00532   }
00533 
00534 
00542   function dispEntry($dir,$loc,$file,$canedit) {
00543     global $globals;
00544 
00545     $view = $edit = $del = $size = $rev = "";
00546 
00547     $myitem = $this->rcsPath($dir,$file);
00548     
00549     // check the RCS entry exists
00550     if (!file_exists($myitem)) {
00551       $this->info("RCS entry '$myitem' does not exist!");
00552       return;
00553     }
00554     
00555     if (is_dir($myitem))
00556     {
00557     // this is a directory, this should not happen!
00558       $this->info("Unexpected directory in RCS, skipping : '$myitem'");
00559       return;
00560     }
00561     else if (substr($file,-2) == ",v")
00562     {
00563     // this is an RCS file
00564       $file = substr($file,0,-2);
00565           
00566       // check we have a working copy of this item
00567       $spoolitem = $this->spoolPath($dir,$file);
00568       if (!is_file($spoolitem)) 
00569       {
00570         $this->info("Could not find working copy '$spoolitem'!");
00571         $size = "";
00572         $icon = "";
00573       } else {      
00574         $size = $this->dispSize(filesize($spoolitem));
00575         $icon = $globals->icons->get_mime_icon($spoolitem);        
00576       }
00577             
00578       // revision info
00579       $myrev = array_shift($tmparr = $this->logParse($dir,$file));
00580       
00581       return array(
00582         "icon" => $icon,
00583         "file" => $file,
00584         "rev" => array($myrev['rev'],$rev),
00585         "date" => $myrev['date'],
00586         "author" => $myrev['author'],
00587         "size" => $size
00588       );
00589       
00590     }
00591     else 
00592     {
00593       $this->info("Unknown RCS entry type : '$myitem'");
00594       return;
00595     }
00596 
00597   }
00598 
00599 
00604   function dispSize($size)
00605   {
00606     if ($size < 1000)
00607       return "$size B";
00608     else if ($size < 1000000)
00609       return floor($size/1000)." kB";
00610     else
00611       return floor($size/1000000)." MB";
00612   }
00613 
00614 }
00615 
00616 ?>

Generated on Fri Jan 11 01:20:08 2008 for Diogenes by  doxygen 1.5.1