2 rcs_id('$Id: cvs.php,v 1.27 2007-01-04 16:45:49 rurban Exp $');
4 * Backend for handling CVS repository.
6 * ASSUMES: that the shell commands 'cvs', 'grep', 'rm', are all located
7 * ASSUMES: in the path of the server calling this script.
9 * Author: Gerrit Riessen, gerrit.riessen@open-source-consultants.de
12 require_once('lib/WikiDB/backend.php');
13 require_once('lib/ErrorManager.php');
16 * Constants used by the CVS backend
18 // these are the parameters defined in db_params
19 define( 'CVS_DOC_DIR', 'doc_dir' );
20 define( 'CVS_REPOSITORY', 'repository' );
21 define( 'CVS_CHECK_FOR_REPOSITORY', 'check_for_repository' );
22 define( 'CVS_DEBUG_FILE', 'debug_file' );
23 define( 'CVS_PAGE_SOURCE', 'pgsrc' );
24 define( 'CVS_MODULE_NAME', 'module_name' );
26 // these are the things that are defined in the page hash
27 // CMD == Cvs Meta Data
28 define( 'CMD_LAST_MODIFIED', 'lastmodified' );
29 define( 'CMD_CONTENT', '%content');
30 define( 'CMD_CREATED', 'created');
31 define( 'CMD_VERSION', 'version');
32 define( 'CMD_AUTHOR', 'author');
33 define( 'CMD_LINK_ATT', '_links_' );
35 // file names used to store specific information
36 define( 'CVS_MP_FILE', '.most_popular' );
37 define( 'CVS_MR_FILE', '.most_recent' );
39 class WikiDB_backend_cvs
40 extends WikiDB_backend
48 * In the following parameters should be defined in dbparam:
49 * . wiki ==> directory where the pages should be stored
50 * this is not the CVS repository location
51 * . repository ==> local directory where the repository should be
52 * created. This can also be a :pserver: but then
53 * set check_for_repository to false and checkout
54 * the documents beforehand. (This is basically CVSROOT)
55 * . check_for_repository ==> boolean flag to indicate whether the
56 * repository should be created, this only
57 * applies to local directories, for pserver
58 * set this to false and check out the
59 * document base beforehand
60 * . debug_file ==> file name where debug information should be sent.
61 * If file doesn't exist then it's created, if this
62 * is empty, then debugging is turned off.
63 * . pgsrc ==> directory name where the default wiki pages are stored.
64 * This is only required if the backend is to create a
67 * The class also adds a parameter 'module_name' to indicate the name
68 * of the cvs module that is being used to version the documents. The
69 * module_name is assumed to be the base name of directory given in
70 * wiki, e.g. if wiki == '/some/path/to/documents' then module_name
71 * becomes 'documents' and this module will be created in the CVS
72 * repository or assumed to exist. If on the other hand the parameter
73 * already exists, then it is not overwritten.
75 function WikiDB_backend_cvs( $dbparam )
77 // setup all the instance values.
78 $this->_docDir = $dbparam{CVS_DOC_DIR};
79 $this->_repository = $dbparam{CVS_REPOSITORY};
80 if ( ! $dbparam{CVS_MODULE_NAME} ) {
81 $this->_module_name = basename( $this->_docDir );
82 $dbparam{CVS_MODULE_NAME} = $this->_module_name;
84 $this->_module_name = $dbparam{CVS_MODULE_NAME};
86 $this->_debug_file = $dbparam{CVS_DEBUG_FILE};
88 if ( $dbparam{CVS_CHECK_FOR_REPOSITORY}
89 && !( is_dir( $this->_repository )
90 && is_dir( $this->_repository . "/CVSROOT" )
91 && is_dir( $this->_repository . "/" . $this->_module_name ))) {
93 $this->_cvsDebug( sprintf("Creating new repository [%s]", $this->_repository) );
95 // doesn't exist, need to create it and the replace the wiki
96 // document directory.
97 $this->_mkdir( $this->_repository, 0775 );
99 // assume that the repository is a local directory, prefix :local:
100 if ( !ereg( "^:local:", $this->_repository ) ) {
101 $this->_repository = ":local:" . $this->_repository;
104 $cmdLine = sprintf( "cvs -d \"%s\" init", $this->_repository);
105 $this->_execCommand( $cmdLine, $cmdOutput, true );
107 $this->_mkdir( $this->_docDir, 0775 );
108 $cmdLine = sprintf("cd %s; cvs -d \"%s\" import -m no_message "
109 ."%s V R", $this->_docDir, $this->_repository,
110 $this->_module_name );
111 $this->_execCommand( $cmdLine, $cmdOutput, true );
113 // remove the wiki directory and check it out from the
115 $cmdLine = sprintf( "rm -fr %s; cd %s; cvs -d \"%s\" co %s",
116 $this->_docDir, dirname($this->_docDir),
117 $this->_repository, $this->_module_name);
118 $this->_execCommand( $cmdLine, $cmdOutput, true );
120 // add the default pages using the update_pagedata
122 $metaData[$AUTHOR] = "PhpWiki -- CVS Backend";
124 if ( is_dir( $dbparam[CVS_PAGE_SOURCE] ) ) {
125 $d = opendir( $dbparam[CVS_PAGE_SOURCE] );
126 while ( $entry = readdir( $d ) ) {
127 $filename = $dbparam[CVS_PAGE_SOURCE] . "/" . $entry;
128 $this->_cvsDebug( sprintf("Found [%s] in [%s]", $entry, $dbparam[CVS_PAGE_SOURCE]) );
130 if ( is_file( $filename ) ) {
131 $metaData[CMD_CONTENT] = join('',file($filename));
132 $this->update_pagedata( $entry, $metaData );
138 // ensure that the results of the is_dir are cleared
144 * Return: metadata about page
146 function get_pagedata($pagename)
148 // the metadata information about a page is stored in the
149 // CVS directory of the document root in serialized form. The
150 // file always has the name, i.e. '_$pagename'.
151 $metaFile = $this->_docDir . "/CVS/_" . $pagename;
153 if ( file_exists( $metaFile ) ) {
156 unserialize(join( '',$this->_readFileWithPath($metaFile)));
158 $filename = $this->_docDir . "/" . $pagename;
159 if ( file_exists( $filename ) ) {
160 $megaHash[CMD_CONTENT] = $this->_readFileWithPath( $filename );
162 $megaHash[CMD_CONTENT] = "";
165 $this->_updateMostRecent( $pagename );
166 $this->_updateMostPopular( $pagename );
175 * This will create a new page if page being requested does not
178 function update_pagedata($pagename, $newdata = array() )
181 if ( ! is_array( $newdata ) ) {
182 trigger_error("update_pagedata: Argument 'newdata' was not array",
186 // retrieve the meta data
187 $metaData = $this->get_pagedata( $pagename );
190 $this->_cvsDebug("update_pagedata: no meta data found");
191 // this means that the page does not exist, we need to create
195 $metaData[CMD_CREATED] = time();
196 $metaData[CMD_VERSION] = "1";
198 if ( ! isset($newdata[CMD_CONTENT])) {
199 $metaData[CMD_CONTENT] = "";
201 $metaData[CMD_CONTENT] = $newdata[CMD_CONTENT];
204 // create an empty page ...
205 $this->_writePage( $pagename, $metaData[CMD_CONTENT] );
206 $this->_addPage( $pagename );
208 // make sure that the page is written and committed a second time
209 unset( $newdata[CMD_CONTENT] );
210 unset( $metaData[CMD_CONTENT] );
213 // change any meta data information
214 foreach ( $newdata as $key => $value ) {
215 if ( $value == false || empty( $value ) ) {
216 unset( $metaData[$key] );
218 $metaData[$key] = $value;
222 // update the page data, if required. Use newdata because it could
223 // be empty and thus unset($metaData[CMD_CONTENT]).
224 if ( isset( $newdata[CMD_CONTENT] ) ) {
225 $this->_writePage( $pagename, $newdata[CMD_CONTENT] );
228 // remove any content from the meta data before storing it
229 unset( $metaData[CMD_CONTENT] );
230 $metaData[CMD_LAST_MODIFIED] = time();
232 $metaData[CMD_VERSION] = $this->_commitPage( $pagename, $metaData );
233 $this->_writeMetaInfo( $pagename, $metaData );
236 function get_latest_version($pagename)
238 $metaData = $this->get_pagedata( $pagename );
240 // the version number is everything after the '1.'
241 return $metaData[CMD_VERSION];
243 $this->_cvsDebug(sprintf("get_latest_versioned FAILED for [%s]", $pagename));
248 function get_previous_version($pagename, $version)
250 // cvs increments the version numbers, so this is real easy ;-)
251 return ($version > 0 ? $version - 1 : 0);
255 * the version parameter is assumed to be everything after the '1.'
256 * in the CVS versioning system.
258 function get_versiondata($pagename, $version, $want_content = false)
260 $this->_cvsDebug( "get_versiondata: [$pagename] [$version] [$want_content]" );
263 if ( $want_content ) {
264 // retrieve the version from the repository
265 $cmdLine = sprintf("cvs -d \"%s\" co -p -r 1.%d %s/%s 2>&1",
266 $this->_repository, $version,
267 $this->_module_name, $pagename );
268 $this->_execCommand( $cmdLine, $filedata, true );
270 // TODO: DEBUG: 5 is a magic number here, depending on the
271 // TODO: DEBUG: version of cvs used here, 5 might have to
272 // TODO: DEBUG: change. Basically find a more reliable way of
273 // TODO: DEBUG: doing this.
274 // the first 5 lines contain various bits of
275 // administrative information that can be ignored.
276 for ( $i = 0; $i < 5; $i++ ) {
277 array_shift( $filedata );
282 * Now obtain the rest of the pagehash information, this is contained
283 * in the log message for the revision in serialized form.
285 $cmdLine = sprintf("cd %s; cvs log -r1.%d %s", $this->_docDir,
286 $version, $pagename );
287 $this->_execCommand( $cmdLine, $logdata, true );
289 // shift log data until we get to the 'revision X.X' line
290 // FIXME: ensure that we don't enter an endless loop here
291 while ( !ereg( "^revision 1.([0-9]+)$", $logdata[0], $revInfo ) ) {
292 array_shift( $logdata );
295 // serialized hash information now stored in position 2
296 $rVal = unserialize( _unescape( $logdata[2] ) );
298 // version information is incorrect
299 $rVal[CMD_VERSION] = $revInfo[1];
300 $rVal[CMD_CONTENT] = $filedata;
302 foreach ( $rVal as $key => $value ) {
303 $this->_cvsDebug( "$key == [$value]" );
310 * See ADODB for a better delete_page(), which can be undone and is seen in RecentChanges.
313 //function delete_page($pagename) { $this->purge_page($pagename); }
316 * This returns false if page was not deleted or could not be deleted
319 function purge_page($pagename)
321 $this->_cvsDebug( "delete_page [$pagename]") ;
322 $filename = $this->_docDir . "/" . $pagename;
323 $metaFile = $this->_docDir . "/CVS/_" . $pagename;
325 // obtain a write block before deleting the file
326 if ( $this->_deleteFile( $filename ) == false ) {
330 $this->_deleteFile( $metaFile );
332 $this->_removePage( $pagename );
338 * For now delete and create a new one.
340 * This returns false if page was not renamed,
343 function rename_page($pagename, $to)
345 $this->_cvsDebug( "rename_page [$pagename,$to]") ;
346 $data = get_pagedata($pagename);
347 if (isset($data['pagename']))
348 $data['pagename'] = $to;
349 //$version = $this->get_latest_version($pagename);
350 //$vdata = get_versiondata($pagename, $version, 1);
351 //$data[CMD_CONTENT] = $vdata[CMD_CONTENT];
352 $this->delete_page($pagename);
353 $this->update_pagedata($to, $data);
357 function delete_versiondata($pagename, $version)
359 // TODO: Not Implemented.
360 // TODO: This is, for CVS, difficult because it implies removing a
361 // TODO: revision somewhere in the middle of a revision tree, and
362 // TODO: this is basically not possible!
363 trigger_error("delete_versiondata: Not Implemented", E_USER_WARNING);
366 function set_versiondata($pagename, $version, $data)
368 // TODO: Not Implemented.
369 // TODO: requires changing the log(commit) message for a particular
370 // TODO: version and this can't be done??? (You can edit the repository
371 // TODO: file directly but i don't know of a way of doing it via
372 // TODO: the cvs tools).
373 trigger_error("set_versiondata: Not Implemented", E_USER_WARNING);
376 function update_versiondata($pagename, $version, $newdata)
378 // TODO: same problem as set_versiondata
379 trigger_error("set_versiondata: Not Implemented", E_USER_WARNING);
382 function set_links($pagename, $links)
384 // TODO: needs to be tested ....
385 $megaHash = get_pagedata( $pagename );
386 $megaHash[CMD_LINK_ATT] = $links;
387 $this->_writeMetaInfo( $pagename, $megaHash );
390 function get_links($pagename, $reversed=true, $include_empty=false,
391 $sortby='', $limit='', $exclude='')
393 // TODO: ignores the $reversed argument and returns
394 // TODO: the value of _links_ attribute of the meta information
395 // TODO: to implement a reversed version, i guess, we going to
396 // TODO: need to do a grep on all files for the pagename in
397 // TODO: in question and return all those page names that contained
398 // TODO: the required pagename!
399 $megaHash = get_pagedata( $pagename );
400 return $megaHash[CMD_LINK_ATT];
403 /* function get_all_revisions($pagename) {
404 // TODO: should replace this with something more efficient
405 include_once('lib/WikiDB/backend/dumb/AllRevisionsIter.php');
406 return new WikiDB_backend_dumb_AllRevisionsIter($this, $pagename);
409 function get_all_pages($include_empty=false, $sortby='', $limit='')
411 // FIXME: this ignores the parameters.
412 return new Cvs_Backend_Array_Iterator(
413 $this->_getAllFileNamesInDir( $this->_docDir ));
416 function text_search($search, $fullsearch = false, $orderby=false, $limit='', $exclude='')
419 $iter = new Cvs_Backend_Full_Search_Iterator(
420 $this->_getAllFileNamesInDir( $this->_docDir ),
423 $iter->stoplisted =& $search->stoplisted;
426 return new Cvs_Backend_Title_Search_Iterator(
427 $this->_getAllFileNamesInDir( $this->_docDir ),
432 function most_popular($limit, $sortby='') {
433 // TODO: needs to be tested ...
434 $mp = $this->_getMostPopular();
436 asort ($mp, SORT_NUMERIC);
439 arsort( $mp, SORT_NUMERIC );
441 $returnVal = array();
443 while ( (list($key, $val) = each($a)) && $limit > 0 ) {
451 * This only accepts the 'since' and 'limit' attributes, everything
454 function most_recent($params)
456 // TODO: needs to be tested ...
457 // most recent are those pages with the highest time value ...
458 $mr = $this->_getMostRecent();
460 $returnVal = array();
461 if ( isset( $params['limit'] ) ) {
462 $limit = $params['limit'];
466 arsort( $mr, SORT_NUMERIC );
468 asort( $mr, SORT_NUMERIC );
470 if ( isset( $limit ) ) {
471 while ( (list($key, $val) = each($a)) && $limit > 0 ) {
475 } else if ( isset( $params['since'] ) ) {
476 while ( (list($key, $val) = each($a)) ) {
478 if ( $val > $params['since'] ) {
484 return new Cvs_Backend_Array_Iterator( $returnVal );
487 function lock($write_lock = true)
489 // TODO: to be implemented
490 trigger_error("lock: Not Implemented", E_USER_WARNING);
493 function unlock($force = false)
495 // TODO: to be implemented
496 trigger_error("unlock: Not Implemented", E_USER_WARNING);
512 * What we do here is take a listing of the documents directory and
513 * check that each page has metadata file. If not, then a metadata
514 * file is created for the page.
516 * This can happen if rebuild() was called and someone has added
517 * files to the CVS repository not via PhpWiki. These files are
518 * added to the document directory but without any metadata files.
523 // TODO: test this .... i.e. add test to unit test file.
525 $page_names = $this->_getAllFileNamesInDir($this->_docDir);
526 $meta_names = $this->_getAllFileNamesInDir($this->_docDir . "/CVS");
528 array_walk( $meta_names, '_strip_leading_underscore' );
529 reset( $meta_names );
530 $no_meta_files = array_diff( $page_names, $meta_names );
532 array_walk( $no_meta_files, '_create_meta_file', $this );
538 * Do an update of the CVS repository
543 // TODO: test this .... i.e. add test to unit test file.
545 $cmdLine = sprintf( "cd %s; cvs update -d 2>&1", $this->_docDir );
546 $this->_execCommand( $cmdLine, $cmdOutput, true );
551 // ..-.-..-.-..-.-.. .--..-......-.--. --.-....----.....
552 // The rest are all internal methods, not to be used
554 // ..-.-..-.-..-.-.. .--..-......-.--. --.-....----.....
556 function _create_meta_file( $page_name, $key, &$backend )
558 // this is used as part of an array walk and therefore takes
559 // the backend argument
560 $backend->_cvsDebug(sprintf("Creating meta file for [%s]", $page_name));
561 $backend->update_pagedata( $page_name, array() );
564 function _strip_leading_underscore( &$item )
566 $item = ereg_replace( "^_", "", $item );
570 * update the most popular information by incrementing the count
571 * for the following page. If the page was not defined, it is entered
574 function _updateMostPopular( $pagename )
576 $mp = $this->_getMostPopular();
577 if ( isset( $mp[$pagename] ) ) {
582 $this->_writeFileWithPath( $this->_docDir . "/CVS/" . CVS_MP_FILE,
588 * Returns an array containing the most popular information. This
589 * creates the most popular file if it does not exist.
591 function _getMostPopular()
593 $mostPopular = $this->_docDir . "/CVS/" . CVS_MP_FILE;
594 if ( !file_exists( $mostPopular ) ) {
595 $this->_writeFileWithPath( $mostPopular, serialize( array() ) );
597 return unserialize(join( '',$this->_readFileWithPath($mostPopular)));
600 function _getMostRecent()
602 $mostRecent = $this->_docDir . "/CVS/" . CVS_MR_FILE;
603 if ( !file_exists( $mostRecent ) ) {
604 $this->_writeFileWithPath( $mostRecent, serialize( array() ) );
606 return unserialize(join( '',$this->_readFileWithPath($mostRecent)));
609 function _updateMostRecent( $pagename )
611 $mr = $this->_getMostRecent();
612 $mr[$pagename] = time();
613 $this->_writeFileWithPath( $this->_docDir . "/CVS/" . CVS_MR_FILE,
617 function _writeMetaInfo( $pagename, $hashInfo )
619 $this->_writeFileWithPath( $this->_docDir . "/CVS/_" . $pagename,
620 serialize( $hashInfo ) );
622 function _writePage( $pagename, $content )
624 $this->_writeFileWithPath( $this->_docDir . "/". $pagename, $content );
626 function _removePage( $pagename )
628 $cmdLine = sprintf("cd %s; cvs remove %s 2>&1; cvs commit -m '%s' "
629 ."%s 2>&1", $this->_docDir, $pagename,
630 "remove page", $pagename );
632 $this->_execCommand( $cmdLine, $cmdRemoveOutput, true );
636 * this returns the new version number of the file.
638 function _commitPage( $pagename, &$meta_data )
640 $cmdLine = sprintf( "cd %s; cvs commit -m \"%s\" %s 2>&1",
642 escapeshellcmd( serialize( $meta_data ) ),
644 $this->_execCommand( $cmdLine, $cmdOutput, true );
646 $cmdOutput = implode( "\n", $cmdOutput );
648 ereg( "\nnew revision: 1[.]([0-9]+); previous revision: ", $cmdOutput,
651 $this->_cvsDebug( "CP: revInfo 0: $revInfo[0]" );
652 $this->_cvsDebug( "CP: $cmdOutput" );
653 if ( isset( $revInfo[1] ) ) {
654 $this->_cvsDebug( "CP: got revision information" );
657 ereg( "\ninitial revision: 1[.]([0-9]+)", $cmdOutput, $revInfo );
658 if ( isset( $revInfo[1] ) ) {
659 $this->_cvsDebug( "CP: is initial release" );
662 $this->_cvsDebug( "CP: returning old version" );
663 return $meta_data[CMD_VERSION];
666 function _addPage( $pagename )
668 // TODO: need to add a check for the mimetype so that binary
669 // TODO: files are added as binary files
670 $cmdLine = sprintf("cd %s; cvs add %s 2>&1", $this->_docDir,
672 $this->_execCommand( $cmdLine, $cmdAddOutput, true );
676 * Returns an array containing all the names of files contained
677 * in a particular directory. The list is sorted according the
678 * string representation of the filenames.
680 function _getAllFileNamesInDir( $dirName )
683 $d = opendir( $dirName );
684 while ( $entry = readdir( $d ) ) {
685 $namelist[] = $entry;
688 sort( $namelist, SORT_STRING );
693 * Recursively create all directories.
695 function _mkdir( $path, $mode )
697 $directoryName = dirname( $path );
698 if ( $directoryName != "/" && $directoryName != "\\"
699 && !is_dir( $directoryName ) && $directoryName != "" ) {
700 $rVal = $this->_mkdir( $directoryName, $mode );
706 return ($rVal && @mkdir( $path, $mode ) );
710 * Recursively create all directories and then the file.
712 function _createFile( $path, $mode )
714 $this->_mkdir( dirname( $path ), $mode );
716 chmod( $path, $mode );
720 * The lord giveth, and the lord taketh.
722 function _deleteFile( $filename )
724 if( $fd = fopen($filename, 'a') ) {
726 $locked = flock($fd,2); // Exclusive blocking lock
729 $this->_cvsError("Unable to delete file, lock was not obtained.",
730 __LINE__, $filename, EM_NOTICE_ERRORS );
733 if ( ($rVal = unlink( $filename )) != 0 ) {
734 $this->_cvsDebug( "[$filename] --> Unlink returned [$rVal]" );
739 $this->_cvsError( "deleteFile: Unable to open file",
740 __LINE__, $filename, EM_NOTICE_ERRORS );
746 * Called when something happened that causes the CVS backend to
749 function _cvsError( $msg = "no message",
751 $errfile = "lib/WikiDB/backend/cvs.php",
752 $errno = EM_FATAL_ERRORS)
754 $err = new PhpError( $errno, "[CVS(be)]: " . $msg, $errfile, $errline);
755 // send error to the debug routine
756 $this->_cvsDebug( $err->asXML() );
757 // send the error to the error manager
758 $GLOBALS['ErrorManager']->handleError( $err );
762 * Debug function specifically for the CVS database functions.
763 * Can be deactived by setting the WikiDB['debug_file'] to ""
765 function _cvsDebug( $msg )
767 if ( $this->_debug_file == "" ) {
771 if ( !file_exists( $this->_debug_file ) ) {
772 $this->_createFile( $this->_debug_file, 0755 );
775 if ( $fdlock = @fopen( $this->_debug_file, 'a' ) ) {
776 $locked = flock( $fdlock, 2 );
782 $fdappend = @fopen( $this->_debug_file, 'a' );
783 fwrite( $fdappend, ($msg . "\n") );
788 // TODO: this should be replaced ...
789 printf("unable to locate/open [%s], turning debug off\n", $filename);
790 $this->_debug_file = "";
795 * Execute a command and potentially exit if the flag exitOnNonZero is
796 * set to true and the return value was nonZero
798 function _execCommand( $cmdLine, &$cmdOutput, $exitOnNonZero )
800 $this->_cvsDebug( sprintf("Preparing to execute [%s]", $cmdLine) );
801 exec( $cmdLine, $cmdOutput, $cmdReturnVal );
802 if ( $exitOnNonZero && ($cmdReturnVal != 0) ) {
803 $this->_cvsDebug( sprintf("Command failed [%s], Output: ", $cmdLine) ."[".
804 join("\n",$cmdOutput) . "]" );
805 $this->_cvsError( sprintf("Command failed [%s], Return value: %s", $cmdLine, $cmdReturnVal),
808 $this->_cvsDebug( "Done execution [" . join("\n", $cmdOutput) . "]" );
810 return $cmdReturnVal;
814 * Read locks a file, reads it, and returns it contents
816 function _readFileWithPath( $filename )
818 if ( $fd = @fopen( $filename, "r" ) ) {
819 $locked = flock( $fd, 1 ); // read lock
822 $this->_cvsError( "Unable to obtain read lock.", __LINE__ );
825 $content = file( $filename );
829 $this->_cvsError( sprintf("Unable to open file '%s' for reading", $filename),
836 * Either replace the contents of an existing file or create a
837 * new file in the particular store using the page name as the
840 * Nothing is returned, might be useful to return something ;-)
842 function _writeFileWithPath( $filename, $contents )
844 // TODO: $contents should probably be a reference parameter ...
845 if( $fd = fopen($filename, 'a') ) {
846 $locked = flock($fd,2); // Exclusive blocking lock
848 $this->_cvsError( "Timeout while obtaining lock.", __LINE__ );
851 // Second filehandle -- we use this to write the contents
852 $fdsafe = fopen($filename, 'w');
853 fwrite($fdsafe, $contents);
857 $this->_cvsError( sprintf("Could not open file '%s' for writing", $filename),
863 * Copy the contents of the source directory to the destination directory.
865 function _copyFilesFromDirectory( $src, $dest )
867 $this->_cvsDebug( sprintf("Copying from [%s] to [%s]", $src, $dest) );
869 if ( is_dir( $src ) && is_dir( $dest ) ) {
870 $this->_cvsDebug( "Copying " );
871 $d = opendir( $src );
872 while ( $entry = readdir( $d ) ) {
873 if ( is_file( $src . "/" . $entry )
874 && copy( $src . "/" . $entry, $dest . "/" . $entry ) ) {
875 $this->_cvsDebug( sprintf("Copied to [%s]", "$dest/$entry") );
877 $this->_cvsDebug( sprintf("Failed to copy [%s]", "$src/$entry") );
883 $this->_cvsDebug( "Not copying" );
889 * Unescape a string value. Normally this comes from doing an
890 * escapeshellcmd. This converts the following:
896 function _unescape( $val )
898 $val = str_replace( "\\{", "{", $val );
899 $val = str_replace( "\\}", "}", $val );
900 $val = str_replace( "\\;", ";", $val );
901 $val = str_replace( "\\\"", "\"", $val );
907 * Function for removing the newlines from the ends of the
908 * file data returned from file(..). This is used in retrievePage
910 function _strip_newlines( &$item, $key )
912 $item = ereg_replace( "\n$", "", $item );
915 } /* End of WikiDB_backend_cvs class */
918 * Generic iterator for stepping through an array of values.
920 class Cvs_Backend_Array_Iterator
921 extends WikiDB_backend_iterator
925 function Cvs_Backend_Iterator( $arrayValue = Array() )
927 $this->_array = $arrayValue;
932 while ( ($rVal = array_pop( $this->_array )) != NULL ) {
939 return count($this->_array);
944 unset( $this->_array );
948 class Cvs_Backend_Full_Search_Iterator
949 extends Cvs_Backend_Array_Iterator
951 var $_searchString = '';
954 function Cvs_Backend_Title_Search_Iterator( $arrayValue = Array(),
958 $this->Cvs_Backend_Array_Iterator( $arrayValue );
959 $_searchString = $searchString;
960 $_docDir = $documentDir;
966 $pageName = Cvs_Backend_Array_Iterator::next();
967 } while ( !$this->_searchFile( $_searchString,
968 $_docDir . "/" . $pageName ));
974 * Does nothing more than a grep and search the entire contents
975 * of the given file. Returns TRUE of the searchstring was found,
976 * false if the search string wasn't find or the file was a directory
977 * or could not be read.
979 function _searchFile( $searchString, $fileName )
981 // TODO: using grep here, it might make more sense to use
982 // TODO: some sort of inbuilt/language specific method for
983 // TODO: searching files.
984 $cmdLine = sprintf( "grep -E -i '%s' %s > /dev/null 2>&1",
985 $searchString, $fileName );
987 return ( WikiDB_backend_cvs::_execCommand( $cmdLine, $cmdOutput,
993 * Iterator used for doing a title search.
995 class Cvs_Backend_Title_Search_Iterator
996 extends Cvs_Backend_Array_Iterator
998 var $_searchString = '';
1000 function Cvs_Backend_Title_Search_Iterator( $arrayValue = Array(),
1003 $this->Cvs_Backend_Array_Iterator( $arrayValue );
1004 $_searchString = $searchString;
1010 $pageName = Cvs_Backend_Array_Iterator::next();
1011 } while ( !eregi( $this->_searchString, $pageName ) );
1017 // (c-file-style: "gnu")
1021 // c-basic-offset: 4
1022 // c-hanging-comment-ender-p: nil
1023 // indent-tabs-mode: nil