3 # Search for manual pages, and display a list of matches or an exact page
5 require './man-lib.pl';
8 $in{'for'} || &error($text{'search_efor'});
10 @for = split(/\s+/, $in{'for'});
11 @howto = split(/\s+/, $config{'howto_dir'});
12 @doc = split(/\s+/, $config{'doc_dir'});
13 foreach $s (split(/\0/, $in{'section'})) {
14 if ($s =~ /^([^:]+):(.*)/) {
22 if ($in{'check'} && $config{'check'}) {
23 @check = split(/\s+/, $config{'check'});
24 foreach $s (keys %section) {
25 delete($section{$s}) if (&indexof($s, @check) < 0);
29 if ($section{'doc'}) {
30 # Look in the system documentation directory (usually /usr/doc)
32 push(@rv, map { [ $text{'search_doc'},
33 "view_doc.cgi?file=".&urlize($_->[0]),
34 substr($_->[0], length($d)+1),
36 &find_contents($d, \@for,
37 $howto[0], 1, $in{'exact'}));
40 if ($section{'custom'}) {
41 # Look in the custom documentation directory
42 push(@rv, map { [ $text{'search_custom'},
43 "view_doc.cgi?file=".&urlize($_->[0]),
44 substr($_->[0], length($config{'custom_dir'})+1),
46 &find_contents($config{'custom_dir'}, \@for,
47 $howto[0], 1, $in{'exact'}));
49 if ($section{'howto'}) {
50 # Look in the HOWTO directory
52 push(@rv, map { [ $text{'search_howto'},
53 "view_howto.cgi?file=".&urlize($_->[0]),
54 $_->[2], $_->[1], 2 ] }
55 &find_contents($h, \@for, undef, 0, $in{'exact'}));
57 @rv = grep { $_->[2] !~ /^index/i } @rv;
59 if ($section{'kernel'}) {
60 # Look in the linux kernel Documentation directory
61 push(@rv, map { [ $text{'search_kernel'},
62 "view_kernel.cgi?file=".&urlize($_->[0]),
63 substr($_->[0], length($config{'kernel_dir'})+1),
65 &find_contents($config{'kernel_dir'}, \@for,
66 undef, 1, $in{'exact'}));
68 if ($section{'kde'}) {
69 # Look in the KDE documentation directory
70 push(@rv, map { [ $text{'search_kde'},
71 "view_kde.cgi?file=".&urlize($_->[0]),
72 substr($_->[0], length($config{'kde_dir'})+1),
74 &find_contents($config{'kde_dir'}, \@for,
75 undef, 1, $in{'exact'}));
77 if ($section{'perl'}) {
79 # Check for an exact module name match
80 local @f = $in{'and'} ? ( $for[0] ) : @for;
82 chop($out = &backquote_command("$perl_doc -l ".quotemeta($f)." 2>/dev/null", 1));
84 local $doc = &parse_perl_module($out);
85 $doc->{'name'} =~ s/^\s*(\S+)\s+-\s+//;
86 push(@rv, [ $text{'search_perl'},
87 "view_perl.cgi?mod=$f",
88 $f, $doc->{'name'}, 1 ]);
93 # Search the text of all perl modules
94 foreach $d ($Config{'sitelib'}, $Config{'privlib'}) {
95 &open_execute_command(FIND, "find $d -name '*.pm' -print", 1, 1);
96 while($path = <FIND>) {
98 local $doc = &parse_perl_module($path);
99 local ($any = 0, $all = 1);
101 if ($doc->{'name'} !~ /$f/i &&
102 $doc->{'description'} !~ /$f/i) {
109 next if (!$all && $in{'and'} || !$any);
111 $doc->{'name'} =~ s/^\s*(\S+)\s+-\s+//;
113 &backquote_command("$perl_doc -l ".quotemeta($doc->{'package'})." 2>/dev/null", 1);
114 if ($doc->{'package'} && $modfile) {
115 push(@rv, [ $text{'search_perl'},
116 "view_perl.cgi?mod=$doc->{'package'}",
117 $doc->{'package'}, $doc->{'name'},
125 if ($section{'help'}) {
126 # Look in the webmin module help pages
127 opendir(DIR, $root_directory);
128 foreach my $m (readdir(DIR)) {
129 # Is this a module with help
130 local $dir = "$root_directory/$m/help";
131 next if (!-d $dir || $m =~ /^\./ || -l "$root_directory/$m");
132 local %minfo = &get_module_info($m);
133 next if (!%minfo || !&check_os_support(\%minfo));
135 # Check the help pages
138 while($f = readdir(DIR2)) {
139 push(@pfx, $1) if ($f =~ /^([^\.]+)\.html$/);
142 HELP: foreach $p (&unique(@pfx)) {
143 local $file = &help_file($m, $p);
145 local @st = stat($file);
146 read(HELP, $help, $st[7]);
148 if ($help =~ /<header>([^<]+)<\/header>/) {
152 $help =~ s/<include\s+(\S+)>/inchelp($1, $m)/ge;
153 $help =~ s/<[^>]+>//g;
158 $matches++ if ($header =~ /\Q$f\E/i);
164 $matches++ if ($help =~ /\Q$f\E/i);
167 if (($in{'and'} && $matches == @for) ||
168 (!$in{'and'} && $matches)) {
169 push(@rv, [ $text{'search_help'},
170 "/help.cgi/$m/$p?x=1",
177 if ($section{'man'}) {
178 # Look in manual pages (searches are never exact)
179 $cmd = $config{'search_cmd'};
180 map { s/\\/\\\\/g; s/'/\\'/g; } @for;
182 local $qm = quotemeta($for[0]);
186 local $fors = join(" ", map { quotemeta($_) } @for);
187 $cmd =~ s/PAGE/$fors/;
189 &set_manpath($opts{'man'});
190 &open_execute_command(MAN, $cmd, 1, 1);
193 if (/(([^,\s]+).*)\s*\((\S+)\)\s+-\s+(.*)/ &&
195 local ($page, $sect, $desc) = ($1, $3, $4);
196 if ($page =~ /^(\S+)\s*\[(.+)\]/) {
199 local @pp = split(/[\s+,]/, $page);
200 map { s/\((\S+)\)//; } @pp;
202 # Keywords must be page name or desc
203 local ($any = 0, $all = 1, $exact);
205 if ($desc !~ /$f/i && $page !~ /$f/i &&
206 &indexof($f, @pp) < 0) {
212 $exact++ if (&indexof($f, @pp) >= 0);
214 next if (!$all && $in{'and'} || !$any);
216 push(@rv, [ $text{'search_man'},
217 "view_man.cgi?page=$pp[0]&sec=$3&opts=".
218 $opts{'man'}, "$pp[0] ($sect)", $desc,
224 if ($section{'google'}) {
225 # Try to call the Google search engine
226 local ($grv, $error);
227 local $j = $in{'and'} ? ' and ' : ' or ';
228 &http_download($google_host, $google_port, "$google_page?q=".
229 &urlize(join($j, @for))."&sourceid=webmin&num=20",
233 while($grv =~ /(<p[^>]*>|<div[^>]*>)<a[^>]+href=([^>]+)>([\000-\377]+?)<\/a>([\000-\377]*)$/i) {
235 local ($url = $2, $desc = $3);
236 $url =~ s/^"(.*)".*$/$1/;
237 $url =~ s/^'(.*)'.*$/$1/;
238 $desc =~ s/<\/?b>//g;
241 $matches++ if ($desc =~ /\Q$f\E/i);
244 ($in{'and'} && $matches == @for) ||
245 (!$in{'and'} && $matches)) {
246 push(@rv, [ $text{'search_google'}, $url, length($url) > 60 ? substr($url, 0, 60)."..." : $url, $desc, 0.5 ]);
252 if (@rv == 1 && !$in{'check'}) {
253 # redirect to the exact page
254 &redirect($in{'exact'} ? $rv[0]->[1]
255 : "$rv[0]->[1]&for=".&urlize($in{'for'}));
259 # Display search results
260 $for = join($in{'and'} ? " and " : " or ", map { "<tt>$_</tt>" } @for);
261 &ui_print_header(&text('search_for', $for), $text{'search_title'}, "");
263 #@rv = sort { $b->[4] <=> $a->[4] } @rv;
264 @rv = sort { &ranking($b) <=> &ranking($a) } @rv;
265 print &ui_columns_start([ $text{'search_file'},
266 $text{'search_type'},
267 $text{'search_desc'} ], 100);
270 if ($r->[1] =~ /^(http|ftp|https):/) {
271 push(@cols, "<a href='$r->[1]'>".
272 &html_escape($r->[2])."</a>");
275 push(@cols, "<a href='$r->[1]&for=".&urlize($in{'for'}).
276 "'>".&html_escape($r->[2])."</a>");
278 push(@cols, $r->[0]);
279 push(@cols, &html_escape($r->[3]));
280 print &ui_columns_row(\@cols, [ undef, "nowrap", undef ]);
282 print &ui_columns_end();
285 print "<p><b>",&text('search_none', "<tt>$in{'for'}</tt>"),"</b><p>\n";
288 &ui_print_footer("", $text{'index_return'});
290 # find_contents(directory, &strings, [exclude], [descend], [nameonly])
291 # Find some string in a directory of files
295 local @f = readdir(DIR);
299 next if ($f =~ /^\./);
300 local $p = "$_[0]/$f";
301 next if ($p eq $_[2]);
303 # go into subdirectory
304 push(@rv, &find_contents($p, $_[1], $_[2], $_[3], $_[4]))
308 # Skip non-text or HTML files
310 $ff =~ s/\.(gz|bz|bz2)$//i;
311 next if ($ff !~ /\.(txt|htm|html|doc)$/ &&
312 $ff =~ /\.[A-Za-z0-9]+$/);
313 next if ($ff =~ /(^makefile$)|(^core$)/i);
316 foreach $s (@{$_[1]}) {
317 $matches++ if ($p =~ /\Q$s\E/i);
320 # just compare filename
321 if ($in{'and'} && $matches == @{$_[1]} ||
322 !$in{'and'} && $matches) {
323 local ($desc, $data) = &read_doc_file($p);
324 if ($desc !~ /^#!/ && $desc !~ /^#\%/) {
325 push(@rv, [ $p, $desc, $f, $matches ]);
330 # compare file contents
331 local ($desc, $data) = &read_doc_file($p);
333 foreach $s (@{$_[1]}) {
334 $dmatches++ if ($data =~ /\Q$s\E/i);
336 if (($in{'and'} && $dmatches == @{$_[1]} ||
337 !$in{'and'} && $dmatches) &&
338 $desc !~ /^#!/ && $desc !~ /^#\%/) {
339 push(@rv, [ $p, $desc, $f, $matches ]);
347 # read_doc_file(filename)
351 local ($two, $first, $title, $data);
354 local $qm = quotemeta($_[0]);
355 if ($two eq "\037\213") {
357 &open_execute_command(FILE, "gunzip -c $qm", 1, 1);
359 elsif ($two eq "BZ") {
361 &open_execute_command(FILE, "bunzip2 -c $qm", 1, 1);
366 if (/[A-Za-z0-9]/ && !/\$\S+:/ && !$first) {
368 $first =~ s/.\010//g;
372 if ($data =~ /<\s*title\s*>([\000-\177]{0,200})<\s*\/\s*title\s*>/i) {
375 return ($title ? $title : $first =~ /<.*>/ ? undef : $first, $data);
378 # parse_perl_module(file)
379 sub parse_perl_module
381 local (%doc, $inside);
384 if (/^\s*package\s+(\S+)\s*;/ && !$doc{'package'}) {
385 $doc{'package'} = $1;
387 elsif (/^=head1\s+(\S+)/i) {
394 $doc{lc($inside)} .= $_;
401 # inchelp(path, module)
405 local $ipath = &help_file($_[1], $_[0]);
406 open(INC, $ipath) || return "<i>".&text('search_einclude', $_[0])."</i><br>\n";
407 local @st = stat(INC);
408 read(INC, $inc, $st[7]);
415 local ($name = 0, $desc = 0);
417 $desc++ if ($_[0]->[3] =~ /$f/i);
418 $name++ if ($_[0]->[1] =~ /$f/i);
420 return $name ? $_[0]->[4] * 10 :