1 <?php include 'search_paths.php'; ?>
4 function search_results()
6 return "Search Results";
9 function matches_text($num)
13 return "Sorry, no documents matching your query.";
17 return "Found <b>1</b> document matching your query.";
21 return "Found <b>$num</b> documents matching your query. Showing best matches first.";
25 function report_matches()
32 if (array_key_exists("query", $_GET))
34 $query=$_GET["query"];
36 echo "<input type=\"text\" name=\"query\" value=\"$query\" size=\"20\" accesskey=\"s\"/>";
39 function readInt($file)
41 $b1 = ord(fgetc($file)); $b2 = ord(fgetc($file));
42 $b3 = ord(fgetc($file)); $b4 = ord(fgetc($file));
43 return ($b1<<24)|($b2<<16)|($b3<<8)|$b4;
46 function readString($file)
49 while (ord($c=fgetc($file))) $result.=$c;
53 function readHeader($file)
55 $header =fgetc($file); $header.=fgetc($file);
56 $header.=fgetc($file); $header.=fgetc($file);
60 function computeIndex($word)
62 // Fast string hashing
63 //$lword = strtolower($word);
64 //$l = strlen($lword);
65 //for ($i=0;$i<$l;$i++)
67 // $c = ord($lword{$i});
68 // $v = (($v & 0xfc00) ^ ($v << 6) ^ $c) & 0xffff;
72 // Simple hashing that allows for substring search
73 if (strlen($word)<2) return -1;
74 // high char of the index
76 if ($hi==0) return -1;
77 // low char of the index
79 if ($lo==0) return -1;
84 function do_search($path, $file,$word,&$statsList)
86 $index = computeIndex($word);
87 if ($index!=-1) // found a valid index
89 fseek($file,$index*4+4); // 4 bytes per entry, skip header
90 $index = readInt($file);
91 if ($index) // found words matching the hash key
93 $start=sizeof($statsList);
96 $w = readString($file);
99 $statIdx = readInt($file);
100 if ($word==substr($w,0,strlen($word)))
101 { // found word that matches (as substring)
102 $statsList[$count++]=array(
106 "full"=>strlen($w)==strlen($word),
110 $w = readString($file);
115 for ($count=$start;$count<sizeof($statsList);$count++)
117 $statInfo = &$statsList[$count];
119 // whole word matches have a double weight
120 if ($statInfo["full"]) $multiplier=2;
121 fseek($file,$statInfo["index"]);
122 $numDocs = readInt($file);
124 // read docs info + occurrence frequency of the word
125 for ($i=0;$i<$numDocs;$i++)
128 $freq=readInt($file);
129 $docInfo[$i]=array("idx" => $idx,
134 if ($freq&1) // word occurs in high priority doc
137 $totalFreqHi+=$freq*$multiplier;
139 else // word occurs in low priority doc
141 $totalFreqLo+=$freq*$multiplier;
144 // read name and url info for the doc
145 for ($i=0;$i<$numDocs;$i++)
147 fseek($file,$docInfo[$i]["idx"]);
148 $docInfo[$i]["name"]=readString($file);
149 $docInfo[$i]["url"]=$path.readString($file);
151 $statInfo["docs"]=$docInfo;
153 $totalFreq=($totalHi+1)*$totalFreqLo + $totalFreqHi;
154 for ($count=$start;$count<sizeof($statsList);$count++)
156 $statInfo = &$statsList[$count];
158 // whole word matches have a double weight
159 if ($statInfo["full"]) $multiplier=2;
160 for ($i=0;$i<sizeof($statInfo["docs"]);$i++)
162 $docInfo = &$statInfo["docs"];
163 // compute frequency rank of the word in each doc
164 $freq=$docInfo[$i]["freq"];
165 if ($docInfo[$i]["hi"])
167 $statInfo["docs"][$i]["rank"]=
168 (float)($freq*$multiplier+$totalFreqLo)/$totalFreq;
172 $statInfo["docs"][$i]["rank"]=
173 (float)($freq*$multiplier)/$totalFreq;
182 function combine_results($results,&$docs)
184 foreach ($results as $wordInfo)
186 $docsList = &$wordInfo["docs"];
187 foreach ($docsList as $di)
191 if (in_array($key, array_keys($docs)))
193 $docs[$key]["rank"]+=$rank;
197 $docs[$key] = array("url"=>$key,
202 $docs[$key]["words"][] = array(
203 "word"=>$wordInfo["word"],
204 "match"=>$wordInfo["match"],
212 function filter_results($docs,&$requiredWords,&$forbiddenWords)
214 $filteredDocs=array();
215 while (list ($key, $val) = each ($docs))
217 $words = &$docs[$key]["words"];
218 $copy=1; // copy entry by default
219 if (sizeof($requiredWords)>0)
221 foreach ($requiredWords as $reqWord)
224 foreach ($words as $wordInfo)
226 $found = $wordInfo["word"]==$reqWord;
231 $copy=0; // document contains none of the required words
236 if (sizeof($forbiddenWords)>0)
238 foreach ($words as $wordInfo)
240 if (in_array($wordInfo["word"],$forbiddenWords))
242 $copy=0; // document contains a forbidden word
247 if ($copy) $filteredDocs[$key]=$docs[$key];
249 return $filteredDocs;
252 function compare_rank($a,$b)
254 if ($a["rank"] == $b["rank"])
258 return ($a["rank"]>$b["rank"]) ? -1 : 1;
261 function sort_results($docs,&$sorted)
264 usort($sorted,"compare_rank");
268 function report_results(&$docs)
270 echo "<table cellspacing=\"2\">\n";
272 echo " <td colspan=\"2\"><h2>".search_results()."</h2></td>\n";
274 $numDocs = sizeof($docs);
278 echo " <td colspan=\"2\">".matches_text(0)."</td>\n";
284 echo " <td colspan=\"2\">".matches_text($numDocs);
289 foreach ($docs as $doc)
292 echo " <td align=\"right\">$num.</td>";
293 echo "<td><a class=\"el\" href=\"".$doc["url"]."\">".$doc["name"]."</a></td>\n";
295 echo " <td></td><td class=\"tiny\">".report_matches()." ";
296 foreach ($doc["words"] as $wordInfo)
298 $word = $wordInfo["word"];
299 $matchRight = substr($wordInfo["match"],strlen($word));
300 echo "<b>$word</b>$matchRight(".$wordInfo["freq"].") ";
312 if(strcmp('4.1.0', phpversion()) > 0)
314 die("Error: PHP version 4.1.0 or above required!");
320 for ($i=0; $i<sizeof($paths); $i++) {
321 if (!($f=@fopen($paths[$i]."search.idx","rb")))
323 die("Error: Search index file could NOT be opened!");
327 if (readHeader($f)!="DOXS")
329 die("Error: Header of index file is invalid!");
334 if (array_key_exists("query", $_GET))
336 $query=$_GET["query"];
339 echo " \n<div class=\"searchresults\">\n";
341 $requiredWords = array();
342 $forbiddenWords = array();
343 $foundWords = array();
344 $word=strtok($query," ");
345 while ($word) // for each word in the search query
347 if (($word{0}=='+')) { $word=substr($word,1); $requiredWords[]=$word; }
348 if (($word{0}=='-')) { $word=substr($word,1); $forbiddenWords[]=$word; }
349 if (!in_array($word,$foundWords))
352 for ($i=0; $i<sizeof($files); $i++) {
353 do_search($paths[$i], $files[$i], strtolower($word), $results);
359 combine_results($results,$docs);
360 // filter out documents with forbidden word or that do not contain
362 $filteredDocs = filter_results($docs,$requiredWords,$forbiddenWords);
363 // sort the results based on rank
365 sort_results($filteredDocs,$sorted);
366 // report results to the user
367 report_results($sorted);
369 foreach ($files as $file) {