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 __construct($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))
94 $this->_cvsDebug(sprintf("Creating new repository [%s]", $this->_repository));
96 // doesn't exist, need to create it and the replace the wiki
97 // document directory.
98 $this->_mkdir($this->_repository, 0775);
100 // assume that the repository is a local directory, prefix :local:
101 if (!ereg("^:local:", $this->_repository)) {
102 $this->_repository = ":local:" . $this->_repository;
105 $cmdLine = sprintf("cvs -d \"%s\" init", $this->_repository);
106 $this->_execCommand($cmdLine, $cmdOutput, true);
108 $this->_mkdir($this->_docDir, 0775);
109 $cmdLine = sprintf("cd %s; cvs -d \"%s\" import -m no_message "
110 . "%s V R", $this->_docDir, $this->_repository,
111 $this->_module_name);
112 $this->_execCommand($cmdLine, $cmdOutput, true);
114 // remove the wiki directory and check it out from the
116 $cmdLine = sprintf("rm -fr %s; cd %s; cvs -d \"%s\" co %s",
117 $this->_docDir, dirname($this->_docDir),
118 $this->_repository, $this->_module_name);
119 $this->_execCommand($cmdLine, $cmdOutput, true);
121 // add the default pages using the update_pagedata
123 $metaData[$AUTHOR] = "PhpWiki -- CVS Backend";
125 if (is_dir($dbparam[CVS_PAGE_SOURCE])) {
126 $d = opendir($dbparam[CVS_PAGE_SOURCE]);
127 while ($entry = readdir($d)) {
128 $filename = $dbparam[CVS_PAGE_SOURCE] . "/" . $entry;
129 $this->_cvsDebug(sprintf("Found [%s] in [%s]", $entry, $dbparam[CVS_PAGE_SOURCE]));
131 if (is_file($filename)) {
132 $metaData[CMD_CONTENT] = join('', file($filename));
133 $this->update_pagedata($entry, $metaData);
139 // ensure that the results of the is_dir are cleared
145 * Return: metadata about page
147 function get_pagedata($pagename)
149 // the metadata information about a page is stored in the
150 // CVS directory of the document root in serialized form. The
151 // file always has the name, i.e. '_$pagename'.
152 $metaFile = $this->_docDir . "/CVS/_" . $pagename;
154 if (file_exists($metaFile)) {
157 unserialize(join('', $this->_readFileWithPath($metaFile)));
159 $filename = $this->_docDir . "/" . $pagename;
160 if (file_exists($filename)) {
161 $megaHash[CMD_CONTENT] = $this->_readFileWithPath($filename);
163 $megaHash[CMD_CONTENT] = "";
166 $this->_updateMostRecent($pagename);
167 $this->_updateMostPopular($pagename);
176 * This will create a new page if page being requested does not
179 function update_pagedata($pagename, $newdata = array())
182 if (!is_array($newdata)) {
183 trigger_error("update_pagedata: Argument 'newdata' was not array",
187 // retrieve the meta data
188 $metaData = $this->get_pagedata($pagename);
191 $this->_cvsDebug("update_pagedata: no meta data found");
192 // this means that the page does not exist, we need to create
196 $metaData[CMD_CREATED] = time();
197 $metaData[CMD_VERSION] = "1";
199 if (!isset($newdata[CMD_CONTENT])) {
200 $metaData[CMD_CONTENT] = "";
202 $metaData[CMD_CONTENT] = $newdata[CMD_CONTENT];
205 // create an empty page ...
206 $this->_writePage($pagename, $metaData[CMD_CONTENT]);
207 $this->_addPage($pagename);
209 // make sure that the page is written and committed a second time
210 unset($newdata[CMD_CONTENT]);
211 unset($metaData[CMD_CONTENT]);
214 // change any meta data information
215 foreach ($newdata as $key => $value) {
216 if ($value == false || empty($value)) {
217 unset($metaData[$key]);
219 $metaData[$key] = $value;
223 // update the page data, if required. Use newdata because it could
224 // be empty and thus unset($metaData[CMD_CONTENT]).
225 if (isset($newdata[CMD_CONTENT])) {
226 $this->_writePage($pagename, $newdata[CMD_CONTENT]);
229 // remove any content from the meta data before storing it
230 unset($metaData[CMD_CONTENT]);
231 $metaData[CMD_LAST_MODIFIED] = time();
233 $metaData[CMD_VERSION] = $this->_commitPage($pagename, $metaData);
234 $this->_writeMetaInfo($pagename, $metaData);
237 function get_latest_version($pagename)
239 $metaData = $this->get_pagedata($pagename);
241 // the version number is everything after the '1.'
242 return $metaData[CMD_VERSION];
244 $this->_cvsDebug(sprintf("get_latest_versioned FAILED for [%s]", $pagename));
249 function get_previous_version($pagename, $version)
251 // cvs increments the version numbers, so this is real easy ;-)
252 return ($version > 0 ? $version - 1 : 0);
256 * the version parameter is assumed to be everything after the '1.'
257 * in the CVS versioning system.
259 function get_versiondata($pagename, $version, $want_content = false)
261 $this->_cvsDebug("get_versiondata: [$pagename] [$version] [$want_content]");
265 // retrieve the version from the repository
266 $cmdLine = sprintf("cvs -d \"%s\" co -p -r 1.%d %s/%s 2>&1",
267 $this->_repository, $version,
268 $this->_module_name, $pagename);
269 $this->_execCommand($cmdLine, $filedata, true);
271 // TODO: DEBUG: 5 is a magic number here, depending on the
272 // TODO: DEBUG: version of cvs used here, 5 might have to
273 // TODO: DEBUG: change. Basically find a more reliable way of
274 // TODO: DEBUG: doing this.
275 // the first 5 lines contain various bits of
276 // administrative information that can be ignored.
277 for ($i = 0; $i < 5; $i++) {
278 array_shift($filedata);
283 * Now obtain the rest of the pagehash information, this is contained
284 * in the log message for the revision in serialized form.
286 $cmdLine = sprintf("cd %s; cvs log -r1.%d %s", $this->_docDir,
287 $version, $pagename);
288 $this->_execCommand($cmdLine, $logdata, true);
290 // shift log data until we get to the 'revision X.X' line
291 // FIXME: ensure that we don't enter an endless loop here
292 while (!ereg("^revision 1.([0-9]+)$", $logdata[0], $revInfo)) {
293 array_shift($logdata);
296 // serialized hash information now stored in position 2
297 $rVal = unserialize(_unescape($logdata[2]));
299 // version information is incorrect
300 $rVal[CMD_VERSION] = $revInfo[1];
301 $rVal[CMD_CONTENT] = $filedata;
303 foreach ($rVal as $key => $value) {
304 $this->_cvsDebug("$key == [$value]");
311 * See ADODB for a better delete_page(), which can be undone and is seen in RecentChanges.
314 //function delete_page($pagename) { $this->purge_page($pagename); }
317 * This returns false if page was not deleted or could not be deleted
320 function purge_page($pagename)
322 $this->_cvsDebug("delete_page [$pagename]");
323 $filename = $this->_docDir . "/" . $pagename;
324 $metaFile = $this->_docDir . "/CVS/_" . $pagename;
326 // obtain a write block before deleting the file
327 if ($this->_deleteFile($filename) == false) {
331 $this->_deleteFile($metaFile);
333 $this->_removePage($pagename);
339 * For now delete and create a new one.
341 * This returns false if page was not renamed,
344 function rename_page($pagename, $to)
346 $this->_cvsDebug("rename_page [$pagename,$to]");
347 $data = get_pagedata($pagename);
348 if (isset($data['pagename']))
349 $data['pagename'] = $to;
350 //$version = $this->get_latest_version($pagename);
351 //$vdata = get_versiondata($pagename, $version, 1);
352 //$data[CMD_CONTENT] = $vdata[CMD_CONTENT];
353 $this->delete_page($pagename);
354 $this->update_pagedata($to, $data);
358 function delete_versiondata($pagename, $version)
360 // TODO: Not Implemented.
361 // TODO: This is, for CVS, difficult because it implies removing a
362 // TODO: revision somewhere in the middle of a revision tree, and
363 // TODO: this is basically not possible!
364 trigger_error("delete_versiondata: Not Implemented", E_USER_WARNING);
367 function set_versiondata($pagename, $version, $data)
369 // TODO: Not Implemented.
370 // TODO: requires changing the log(commit) message for a particular
371 // TODO: version and this can't be done??? (You can edit the repository
372 // TODO: file directly but i don't know of a way of doing it via
373 // TODO: the cvs tools).
374 trigger_error("set_versiondata: Not Implemented", E_USER_WARNING);
377 function update_versiondata($pagename, $version, $newdata)
379 // TODO: same problem as set_versiondata
380 trigger_error("set_versiondata: Not Implemented", E_USER_WARNING);
383 function set_links($pagename, $links)
385 // TODO: needs to be tested ....
386 $megaHash = get_pagedata($pagename);
387 $megaHash[CMD_LINK_ATT] = $links;
388 $this->_writeMetaInfo($pagename, $megaHash);
391 function get_links($pagename, $reversed = true, $include_empty = false,
392 $sortby = '', $limit = '', $exclude = '')
394 // TODO: ignores the $reversed argument and returns
395 // TODO: the value of _links_ attribute of the meta information
396 // TODO: to implement a reversed version, i guess, we going to
397 // TODO: need to do a grep on all files for the pagename in
398 // TODO: in question and return all those page names that contained
399 // TODO: the required pagename!
400 $megaHash = get_pagedata($pagename);
401 return $megaHash[CMD_LINK_ATT];
404 /* function get_all_revisions($pagename) {
405 // TODO: should replace this with something more efficient
406 include_once 'lib/WikiDB/backend/dumb/AllRevisionsIter.php';
407 return new WikiDB_backend_dumb_AllRevisionsIter($this, $pagename);
410 public function get_all_pages($include_empty = false,
411 $sortby = '', $limit = '', $exclude = '')
413 // FIXME: this ignores the parameters.
414 return new Cvs_Backend_Array_Iterator(
415 $this->_getAllFileNamesInDir($this->_docDir));
418 public function text_search($search, $fulltext = false,
419 $sortby = '', $limit = '', $exclude = '')
422 $iter = new Cvs_Backend_Full_Search_Iterator(
423 $this->_getAllFileNamesInDir($this->_docDir),
426 $iter->stoplisted =& $search->stoplisted;
429 return new Cvs_Backend_Title_Search_Iterator(
430 $this->_getAllFileNamesInDir($this->_docDir),
435 public function most_popular($limit = 20, $sortby = '')
437 // TODO: needs to be tested ...
438 $mp = $this->_getMostPopular();
440 asort($mp, SORT_NUMERIC);
443 arsort($mp, SORT_NUMERIC);
445 $returnVal = array();
447 while ((list($key, $val) = each($a)) && $limit > 0) {
455 * This only accepts the 'since' and 'limit' attributes, everything
458 public function most_recent($params)
460 // TODO: needs to be tested ...
461 // most recent are those pages with the highest time value ...
462 $mr = $this->_getMostRecent();
464 $returnVal = array();
465 if (isset($params['limit'])) {
466 $limit = $params['limit'];
470 arsort($mr, SORT_NUMERIC);
472 asort($mr, SORT_NUMERIC);
475 while ((list($key, $val) = each($a)) && $limit > 0) {
479 } elseif (isset($params['since'])) {
480 while ((list($key, $val) = each($a))) {
482 if ($val > $params['since']) {
488 return new Cvs_Backend_Array_Iterator($returnVal);
491 function lock($tables = array(), $write_lock = true)
493 // TODO: to be implemented
494 trigger_error("lock: Not Implemented", E_USER_WARNING);
497 function unlock($tables = array(), $force = false)
499 // TODO: to be implemented
500 trigger_error("unlock: Not Implemented", E_USER_WARNING);
517 * What we do here is take a listing of the documents directory and
518 * check that each page has metadata file. If not, then a metadata
519 * file is created for the page.
521 * This can happen if rebuild() was called and someone has added
522 * files to the CVS repository not via PhpWiki. These files are
523 * added to the document directory but without any metadata files.
528 // TODO: test this .... i.e. add test to unit test file.
530 $page_names = $this->_getAllFileNamesInDir($this->_docDir);
531 $meta_names = $this->_getAllFileNamesInDir($this->_docDir . "/CVS");
533 array_walk($meta_names, '_strip_leading_underscore');
535 $no_meta_files = array_diff($page_names, $meta_names);
537 array_walk($no_meta_files, '_create_meta_file', $this);
543 * Do an update of the CVS repository
548 // TODO: test this .... i.e. add test to unit test file.
550 $cmdLine = sprintf("cd %s; cvs update -d 2>&1", $this->_docDir);
551 $this->_execCommand($cmdLine, $cmdOutput, true);
556 // ..-.-..-.-..-.-.. .--..-......-.--. --.-....----.....
557 // The rest are all internal methods, not to be used
559 // ..-.-..-.-..-.-.. .--..-......-.--. --.-....----.....
561 function _create_meta_file($page_name, $key, &$backend)
563 // this is used as part of an array walk and therefore takes
564 // the backend argument
565 $backend->_cvsDebug(sprintf("Creating meta file for [%s]", $page_name));
566 $backend->update_pagedata($page_name, array());
569 function _strip_leading_underscore(&$item)
571 $item = ereg_replace("^_", "", $item);
575 * update the most popular information by incrementing the count
576 * for the following page. If the page was not defined, it is entered
579 function _updateMostPopular($pagename)
581 $mp = $this->_getMostPopular();
582 if (isset($mp[$pagename])) {
587 $this->_writeFileWithPath($this->_docDir . "/CVS/" . CVS_MP_FILE,
592 * Returns an array containing the most popular information. This
593 * creates the most popular file if it does not exist.
595 function _getMostPopular()
597 $mostPopular = $this->_docDir . "/CVS/" . CVS_MP_FILE;
598 if (!file_exists($mostPopular)) {
599 $this->_writeFileWithPath($mostPopular, serialize(array()));
601 return unserialize(join('', $this->_readFileWithPath($mostPopular)));
604 function _getMostRecent()
606 $mostRecent = $this->_docDir . "/CVS/" . CVS_MR_FILE;
607 if (!file_exists($mostRecent)) {
608 $this->_writeFileWithPath($mostRecent, serialize(array()));
610 return unserialize(join('', $this->_readFileWithPath($mostRecent)));
613 function _updateMostRecent($pagename)
615 $mr = $this->_getMostRecent();
616 $mr[$pagename] = time();
617 $this->_writeFileWithPath($this->_docDir . "/CVS/" . CVS_MR_FILE,
621 function _writeMetaInfo($pagename, $hashInfo)
623 $this->_writeFileWithPath($this->_docDir . "/CVS/_" . $pagename,
624 serialize($hashInfo));
627 function _writePage($pagename, $content)
629 $this->_writeFileWithPath($this->_docDir . "/" . $pagename, $content);
632 function _removePage($pagename)
634 $cmdLine = sprintf("cd %s; cvs remove %s 2>&1; cvs commit -m '%s' "
635 . "%s 2>&1", $this->_docDir, $pagename,
636 "remove page", $pagename);
638 $this->_execCommand($cmdLine, $cmdRemoveOutput, true);
642 * this returns the new version number of the file.
644 function _commitPage($pagename, &$meta_data)
646 $cmdLine = sprintf("cd %s; cvs commit -m \"%s\" %s 2>&1",
648 escapeshellcmd(serialize($meta_data)),
650 $this->_execCommand($cmdLine, $cmdOutput, true);
652 $cmdOutput = implode("\n", $cmdOutput);
654 ereg("\nnew revision: 1[.]([0-9]+); previous revision: ", $cmdOutput,
657 $this->_cvsDebug("CP: revInfo 0: $revInfo[0]");
658 $this->_cvsDebug("CP: $cmdOutput");
659 if (isset($revInfo[1])) {
660 $this->_cvsDebug("CP: got revision information");
663 ereg("\ninitial revision: 1[.]([0-9]+)", $cmdOutput, $revInfo);
664 if (isset($revInfo[1])) {
665 $this->_cvsDebug("CP: is initial release");
668 $this->_cvsDebug("CP: returning old version");
669 return $meta_data[CMD_VERSION];
673 function _addPage($pagename)
675 // TODO: need to add a check for the mimetype so that binary
676 // TODO: files are added as binary files
677 $cmdLine = sprintf("cd %s; cvs add %s 2>&1", $this->_docDir,
679 $this->_execCommand($cmdLine, $cmdAddOutput, true);
683 * Returns an array containing all the names of files contained
684 * in a particular directory. The list is sorted according the
685 * string representation of the filenames.
687 function _getAllFileNamesInDir($dirName)
690 $d = opendir($dirName);
691 while ($entry = readdir($d)) {
692 $namelist[] = $entry;
695 sort($namelist, SORT_STRING);
700 * Recursively create all directories.
702 function _mkdir($path, $mode)
704 $directoryName = dirname($path);
705 if ($directoryName != "/" && $directoryName != "\\"
706 && !is_dir($directoryName) && $directoryName != ""
708 $rVal = $this->_mkdir($directoryName, $mode);
713 return ($rVal && @mkdir($path, $mode));
717 * Recursively create all directories and then the file.
719 function _createFile($path, $mode)
721 $this->_mkdir(dirname($path), $mode);
727 * The lord giveth, and the lord taketh.
729 function _deleteFile($filename)
731 if ($fd = fopen($filename, 'a')) {
733 $locked = flock($fd, 2); // Exclusive blocking lock
736 $this->_cvsError("Unable to delete file, lock was not obtained.",
737 __LINE__, $filename, EM_NOTICE_ERRORS);
740 if (($rVal = unlink($filename)) != 0) {
741 $this->_cvsDebug("[$filename] --> Unlink returned [$rVal]");
746 $this->_cvsError("deleteFile: Unable to open file",
747 __LINE__, $filename, EM_NOTICE_ERRORS);
753 * Called when something happened that causes the CVS backend to
756 function _cvsError($msg = "no message",
758 $errfile = "lib/WikiDB/backend/cvs.php",
759 $errno = EM_FATAL_ERRORS)
761 $err = new PhpError($errno, "[CVS(be)]: " . $msg, $errfile, $errline);
762 // send error to the debug routine
763 $this->_cvsDebug($err->asXML());
764 // send the error to the error manager
765 $GLOBALS['ErrorManager']->handleError($err);
769 * Debug function specifically for the CVS database functions.
770 * Can be deactived by setting the WikiDB['debug_file'] to ""
772 function _cvsDebug($msg)
774 if ($this->_debug_file == "") {
778 if (!file_exists($this->_debug_file)) {
779 $this->_createFile($this->_debug_file, 0755);
782 if ($fdlock = @fopen($this->_debug_file, 'a')) {
783 $locked = flock($fdlock, 2);
789 $fdappend = @fopen($this->_debug_file, 'a');
790 fwrite($fdappend, ($msg . "\n"));
794 // TODO: this should be replaced ...
795 printf("unable to locate/open [%s], turning debug off\n", $filename);
796 $this->_debug_file = "";
801 * Execute a command and potentially exit if the flag exitOnNonZero is
802 * set to true and the return value was nonZero
804 function _execCommand($cmdLine, &$cmdOutput, $exitOnNonZero)
806 $this->_cvsDebug(sprintf("Preparing to execute [%s]", $cmdLine));
807 exec($cmdLine, $cmdOutput, $cmdReturnVal);
808 if ($exitOnNonZero && ($cmdReturnVal != 0)) {
809 $this->_cvsDebug(sprintf("Command failed [%s], Output: ", $cmdLine) . "[" .
810 join("\n", $cmdOutput) . "]");
811 $this->_cvsError(sprintf("Command failed [%s], Return value: %s", $cmdLine, $cmdReturnVal),
814 $this->_cvsDebug("Done execution [" . join("\n", $cmdOutput) . "]");
816 return $cmdReturnVal;
820 * Read locks a file, reads it, and returns it contents
822 function _readFileWithPath($filename)
824 if ($fd = @fopen($filename, "r")) {
825 $locked = flock($fd, 1); // read lock
828 $this->_cvsError("Unable to obtain read lock.", __LINE__);
831 $content = file($filename);
835 $this->_cvsError(sprintf("Unable to open file '%s' for reading", $filename),
842 * Either replace the contents of an existing file or create a
843 * new file in the particular store using the page name as the
846 * Nothing is returned, might be useful to return something ;-)
848 function _writeFileWithPath($filename, $contents)
850 // TODO: $contents should probably be a reference parameter ...
851 if ($fd = fopen($filename, 'a')) {
852 $locked = flock($fd, 2); // Exclusive blocking lock
854 $this->_cvsError("Timeout while obtaining lock.", __LINE__);
857 // Second filehandle -- we use this to write the contents
858 $fdsafe = fopen($filename, 'w');
859 fwrite($fdsafe, $contents);
863 $this->_cvsError(sprintf("Could not open file '%s' for writing", $filename),
869 * Copy the contents of the source directory to the destination directory.
871 function _copyFilesFromDirectory($src, $dest)
873 $this->_cvsDebug(sprintf("Copying from [%s] to [%s]", $src, $dest));
875 if (is_dir($src) && is_dir($dest)) {
876 $this->_cvsDebug("Copying ");
878 while ($entry = readdir($d)) {
879 if (is_file($src . "/" . $entry)
880 && copy($src . "/" . $entry, $dest . "/" . $entry)
882 $this->_cvsDebug(sprintf("Copied to [%s]", "$dest/$entry"));
884 $this->_cvsDebug(sprintf("Failed to copy [%s]", "$src/$entry"));
890 $this->_cvsDebug("Not copying");
896 * Unescape a string value. Normally this comes from doing an
897 * escapeshellcmd. This converts the following:
903 function _unescape($val)
905 $val = str_replace("\\{", "{", $val);
906 $val = str_replace("\\}", "}", $val);
907 $val = str_replace("\\;", ";", $val);
908 $val = str_replace("\\\"", "\"", $val);
914 * Function for removing the newlines from the ends of the
915 * file data returned from file(..). This is used in retrievePage
917 function _strip_newlines(&$item, $key)
919 $item = ereg_replace("\n$", "", $item);
922 } /* End of WikiDB_backend_cvs class */
925 * Generic iterator for stepping through an array of values.
927 class Cvs_Backend_Array_Iterator
928 extends WikiDB_backend_iterator
932 function Cvs_Backend_Iterator($arrayValue = Array())
934 $this->_array = $arrayValue;
939 while (($rVal = array_pop($this->_array)) != NULL) {
947 return count($this->_array);
952 unset($this->_array);
956 class Cvs_Backend_Full_Search_Iterator
957 extends Cvs_Backend_Array_Iterator
959 public $_searchString = '';
960 public $_docDir = "";
962 function __construct($arrayValue = array(),
966 $this->Cvs_Backend_Array_Iterator($arrayValue);
967 $this->_searchString = $searchString;
968 $this->_docDir = $documentDir;
974 $pageName = Cvs_Backend_Array_Iterator::next();
975 } while (!$this->_searchFile($this->_searchString,
976 $this->_docDir . "/" . $pageName));
982 * Does nothing more than a grep and search the entire contents
983 * of the given file. Returns TRUE of the searchstring was found,
984 * false if the search string wasn't find or the file was a directory
985 * or could not be read.
987 function _searchFile($searchString, $fileName)
989 // TODO: using grep here, it might make more sense to use
990 // TODO: some sort of inbuilt/language specific method for
991 // TODO: searching files.
992 $cmdLine = sprintf("grep -E -i '%s' %s > /dev/null 2>&1",
993 $searchString, $fileName);
995 return (WikiDB_backend_cvs::_execCommand($cmdLine, $cmdOutput,
1001 * Iterator used for doing a title search.
1003 class Cvs_Backend_Title_Search_Iterator
1004 extends Cvs_Backend_Array_Iterator
1006 public $_searchString = '';
1008 function __construct($arrayValue = array(),
1011 parent::__construct($arrayValue);
1012 $this->_searchString = $searchString;
1018 $pageName = Cvs_Backend_Array_Iterator::next();
1019 } while (!eregi($this->_searchString, $pageName));
1028 // c-basic-offset: 4
1029 // c-hanging-comment-ender-p: nil
1030 // indent-tabs-mode: nil