<?php if (!defined('PmWiki')) exit();

/*
 * This recipe searches for pages using find and grep.  The search is much
 * faster than using the original search mechanism in pmwiki.  The larger
 * your site, the greater the improvement you will see.
 *
 * To use this:
 *
 *  -find and grep must be in your path (probably also a sh on windows)
 *  -Comment out the MakePageList function from pagelist.php.
 *
 * Created by: Martin Fick
 * Version:    1.3
 * Date:       8/18/2006
 */


function SearchPages($pat=NULL,$inclp) {
  global $WikiLibDirs;

  foreach((array)$WikiLibDirs as $dir)
    $out = array_unique(array_merge(PageStoreSearch($dir,$pat,$inclp),(array)@$out));
  return $out;
}
/*
 * This function started out as a the ls function from PageStore.
 * Comments should indicate where modifications have been made
 */
//Modified:	function ls($pats=NULL,) {
function PageStoreSearch($pageStore,$pats=NULL,$inclp) {

    global $GroupPattern, $NamePattern;

//Modified:    StopWatch("PageStore::ls begin {$this->dir}");
    StopWatch("PageStoreSearch begin {$pageStore->dir}");

    $pats=(array)$pats;
    array_push($pats, "/^$GroupPattern\.$NamePattern$/");

//Modified:     $dir = $this->pagefile('$Group.$Name');
  $dir = $pageStore->pagefile('$Group.$Name');

  $dirlist = array(preg_replace('!/*[^/]*\\$.*$!','',$dir));
    $dirlist = array(preg_replace('!/*[^/]*\\$.*$!','',$dir));
    $out = array();

// Added begin:
  $ginclp = "";
  foreach($inclp as $ip) {
    if (preg_match('/\$[^\$]*\$i/', $ip)) $ginclp .= "-i ";
    $ip = escapeshellarg(preg_replace('/\$([^\$]*)\$i/', '$1', $ip));
    $ginclp .= "-e $ip ";
  }
  if ($ginclp == "") return $pageStore->ls($pats);
// Added end:

    while (count($dirlist)>0) {
      $dir = array_shift($dirlist);
      $dfp = @opendir($dir); if (!$dfp) { continue; }
      $o = array();

// Added begin:
      $files = array();
      $files = shell_exec("cd $dir; F=`find . -type f |grep -v '^\./\.'`; grep -l $ginclp \$F |sed -es'|^.*/||g'");

      $o = explode("\n", $files);
// Added end:

//Deleted begin:
#      while ( ($pagefile = readdir($dfp)) !== false) {
#        if ($pagefile{0} == '.') continue;
#        if (is_dir("$dir/$pagefile"))
#          { array_push($dirlist,"$dir/$pagefile"); continue; }
#        $o[] = $pagefile;
#      }
//Deleted end:

      closedir($dfp);

//Modified:      StopWatch("PageStore::ls merge {$this->dir}");
    StopWatch("PageStoreSearch merge {$pageStore->dir}");

      $out = array_merge($out, MatchPageNames($o, $pats));
    }

//Modified:    StopWatch("PageStore::ls end {$this->dir}");
    StopWatch("PageStoreSearch end {$pageStore->dir}");

    return $out;
  }

/*
 * This function is a modified version of MakePageList from pagelist.php.
 * The original function must be commented out to use this one.
 * Comments should indicate where modifications have been made to this function
 */

## MakePageList generates a list of pages using the specifications given
## by $opt.
function MakePageList($pagename, $opt, $retpages = 1) {
  global $MakePageListOpt, $SearchPatterns, $EnablePageListProtect, $PCache,
    $FmtV;
  StopWatch('MakePageList begin');
  SDVA($MakePageListOpt, array('list' => 'default'));

  $opt = array_merge((array)$MakePageListOpt, $opt);
  $readf = @$opt['readf'];
  # we have to read the page if order= is anything but name
  $order = @$opt['order'];
  $readf |= $order && ($order!='name') && ($order!='-name');

  $pats = @(array)$SearchPatterns[$opt['list']];
  if (@$opt['group']) $pats[] = FixGlob($opt['group'], '$1$2.*');
  if (@$opt['name']) $pats[] = FixGlob($opt['name'], '$1*.$2');

  # inclp/exclp contain words to be included/excluded.
  $incl = array(); $inclp = array(); $inclx = false;
  $excl = array(); $exclp = '';
  foreach((array)@$opt[''] as $i) { $incl[] = $i; }
  foreach((array)@$opt['+'] as $i) { $incl[] = $i; }
  foreach((array)@$opt['-'] as $i) { $excl[] = $i; }
  foreach($incl as $i) {
    $inclp[] = '$'.preg_quote($i).'$i';
    $inclx |= preg_match('[^\\w\\x80-\\xff]', $i);
  }
  if ($excl) $exclp = '$'.implode('|', array_map('preg_quote', $excl)).'$i';

  $searchterms = count($incl) + count($excl);
  $readf += $searchterms;                         # forced read if incl/excl

// Added:
  $sinclp = $inclp;

  if (@$opt['trail']) {
    $trail = ReadTrail($pagename, $opt['trail']);
    $list = array();
    foreach($trail as $tstop) {
      $pn = $tstop['pagename'];
      $list[] = $pn;
      $tstop['parentnames'] = array();
      PCache($pn, $tstop);
    }
    foreach($trail as $tstop)
      $PCache[$tstop['pagename']]['parentnames'][] =
        @$trail[$tstop['parent']]['pagename'];

//Modified:  } else $list = ListPages($pats);
  } else $list = SearchPages($pats, $sinclp);


 if (IsEnabled($EnablePageListProtect, 1)) $readf = 1000;
  $matches = array();
  $FmtV['$MatchSearched'] = count($list);

  $terms = ($incl) ? PageIndexTerms($incl) : array();
  if (@$opt['link']) {
    $link = MakePageName($pagename, $opt['link']);
    $linkp = "/(^|,)$link(,|$)/i";
    $terms[] = " $link ";
    $readf++;
  }

  if ($terms) {
    $xlist = PageIndexGrep($terms, true);
    $a = count($list);
    $list = array_diff($list, $xlist);
    $a -= count($list);
    StopWatch("MakePageList: PageIndex filtered $a pages");
  }

  $xlist = array();

  StopWatch('MakePageList scanning '.count($list)." pages, readf=$readf");
  foreach((array)$list as $pn) {
    if ($readf) {
      $page = ($readf >= 1000)
              ? RetrieveAuthPage($pn, 'read', false, READPAGE_CURRENT)
              : ReadPage($pn, READPAGE_CURRENT);
      if (!$page) continue;
      if (@$linkp && !preg_match($linkp, @$page['targets']))
        { $xlist[] = $pn; continue; }
      if ($searchterms) {
        $text = $pn."\n".@$page['targets']."\n".@$page['text'];
        if ($exclp && preg_match($exclp, $text)) continue;
        foreach($inclp as $i)
          if (!preg_match($i, $text))
            { if ($inclx) $xlist[] = $pn; continue 2; }
      }
      $page['size'] = strlen(@$page['text']);
    } else $page = array();
    $page['pagename'] = $page['name'] = $pn;
    PCache($pn, $page);
    $matches[] = $pn;
  }
  StopWatch('MakePageList sort');
  if ($order) SortPageList($matches, $order);
  if ($xlist) {
    register_shutdown_function('flush');
    register_shutdown_function('PageIndexUpdate', $xlist, getcwd());
  }
  StopWatch('MakePageList end');
  if ($retpages)
    for($i=0; $i<count($matches); $i++)
      $matches[$i] = &$PCache[$matches[$i]];
  return $matches;
}