diff --git a/www/index.pl b/www/index.pl index 4d4c92a..095f2c7 100755 --- a/www/index.pl +++ b/www/index.pl @@ -2,7 +2,7 @@ use v5.26; use warnings; -use TUWF ':html5_', ':xml'; +use TUWF ':html5_', ':xml', 'uri_escape'; use POSIX 'ceil'; use List::Util 'uniq', 'min'; use SQL::Interp 'sql', 'sql_interp'; @@ -206,14 +206,14 @@ sub framework_ { html_ lang => 'en', sub { head_ sub { - link_ rel => 'stylesheet', type => 'text/css', href => '/man.css?5'; + link_ rel => 'stylesheet', type => 'text/css', href => '/man.css?6'; title_ $o{title}.' - manned.org'; }; body_ sub { header_ sub { a_ href => '/', 'Manned.org'; form_ action => '/browse/search', method => 'get', sub { - input_ type => 'text', name => 'q', id => 'q', tabindex => 1; + input_ type => 'text', name => 'q', id => 'q', placeholder => 'ncdu, btrfs.8, git-*', value => $o{q}, tabindex => 1; input_ type => 'submit', value => 'Search'; } }; @@ -226,7 +226,7 @@ sub framework_ { }; span_ 'all manual pages are copyrighted by their respective authors.'; }; - script_ type => 'text/javascript', src => '/man.js', ''; + script_ type => 'text/javascript', src => '/man.js?2', ''; } }; @@ -592,57 +592,46 @@ TUWF::get '/info/about' => sub { }; -# Very simple (and fast) prefix match. -sub search_man { - my($q, $limit) = @_; - - my $sect = $q =~ s/^([0-9])\s+// || $q =~ s/\(([a-zA-Z0-9]+)\)$// ? $1 : ''; - my $name = $q =~ s/^([a-zA-Z0-9,.:_-]+)// ? $1 : ''; - - return !$name ? [] : tuwf->dbAlli( - 'SELECT name, section FROM mans WHERE', sql_and( - sql('lower(name) LIKE', \(escape_like(lc $name).'%')), - $sect ? sql('section ILIKE', \(escape_like(lc $sect).'%')) : (), - ), 'ORDER BY name, section LIMIT', \$limit, - ); -} - - TUWF::get '/browse/search' => sub { my $q = tuwf->reqGet('q')||''; - my $man = search_man $q, 150; - return tuwf->resRedirect("/$man->[0]{name}.$man->[0]{section}", 'temp') if @$man == 1; - framework_ title => 'Search results for '.$q, mainclass => 'searchres', sub { - h1_ 'Search results for '.$q; - # Package search would also be useful. - p_ 'Note: This is just a simple case-insensitive prefix match on the man names. In the future we\'ll have more powerful search functionality. Hopefully.'; - if(@$man) { + my $name = $q; + my $sect = $name =~ s/^([0-9])\s+// || $name =~ s/\(([a-zA-Z0-9]+)\)$// || $name =~ s/\.([0-9][a-zA-Z0-9]*)$// ? $1 : ''; + ($name,$sect) = ($sect,'') if !length $name; + + # Redirect if we have an exact match + my @sectsql = length $sect ? ('AND section =', \$sect) : (); + my $man = length $name && tuwf->dbRowi('SELECT name, section FROM mans WHERE name =', \$name, @sectsql); + return tuwf->resRedirect("/man/$man->{name}.$man->{section}", 'temp') if length $man->{name}; + + # Otherwise, do case-insensitive glob search + my $nameq = escape_like(lc $name) =~ tr/?*/_%/r; + my $lst = !length $nameq ? [] : tuwf->dbAlli(' + SELECT name, section + FROM mans WHERE lower(name) LIKE', \$nameq, @sectsql, ' + ORDER BY name, section + LIMIT 500'); + + framework_ title => 'Search results for '.$q, mainclass => 'searchres', q => $q, sub { + h1_ 'Search results for '.(length $sect ? "$name in section $sect" : $q); + if(@$lst) { + p_ 'Truncated to the first 500 results.' if @$lst >= 150; ul_ sub { li_ sub { - a_ href => "/$_->{name}.$_->{section}", $_->{name}; + a_ href => "/man/$_->{name}.$_->{section}", $_->{name}; small_ " $_->{section}"; - } for @$man; + } for @$lst; } } else { p_ 'No results :-('; + p_ sub { + a_ href => '?q='.uri_escape($name), 'Try again in other sections?' if length $sect; + }; } }; }; -TUWF::get '/xml/search.xml' => sub { - my $q = tuwf->reqGet('q')||''; - my $man = search_man $q, 20; - - tuwf->resHeader('Content-Type' => 'text/xml; charset=UTF-8'); - xml; - tag 'results', sub { - tag 'item', id => "$_->{name}.$_->{section}", %$_, undef for @$man; - }; -}; - - # Object to represent the various URLs to a man page. # # Parameters: diff --git a/www/man.css b/www/man.css index 66819ee..758ea76 100644 --- a/www/man.css +++ b/www/man.css @@ -20,7 +20,7 @@ thead td { font-weight: bold } header { border-bottom: 3px dotted #ccc; display: flex; justify-content: space-between; align-items: end; flex-wrap: wrap } header a { font: 24px "Arial", serif; font-weight: bold } header form { padding-bottom: 3px } -header input[type=text] { width: 100px } +header input[type=text] { width: 200px } footer { border-top: 3px dotted #ccc; color: #666; font-size: 12px; display: flex; justify-content: space-between; flex-wrap: wrap } diff --git a/www/man.js b/www/man.js index 3a4af2c..c4225be 100644 --- a/www/man.js +++ b/www/man.js @@ -70,20 +70,6 @@ function tag() { } return el; } -function addBody(el) { - if(document.body.appendChild) - document.body.appendChild(el); - else if(document.documentElement.appendChild) - document.documentElement.appendChild(el); - else if(document.appendChild) - document.appendChild(el); -} -function setContent() { - setText(arguments[0], ''); - for(var i=1; i0 ? l[i-1] : l[l.length-1]; - else - sel = l[i+1] ? l[i+1] : l[0]; - } - obj.ds_selectedId = sel.id.substr(7); - } - - // set selected class - for(var i=0; i