#!/usr/bin/perl -T # # Copyright (c) 1996-2024 Wolfram Schneider # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # ports.cgi - search engine for FreeBSD ports use POSIX qw(strftime); use Time::Local; require "./cgi-style.pl"; $t_style = qq` `; # No unlimited result set. A HTML page with 1000 results can be 10MB big. my $max_hits = 1000; my $max_hits_default = 250; my $max; my $debug = 1; sub init_variables { $localPrefix = '/usr/ports'; # ports prefix # Directory of the up-to-date INDEX* $portsDatabaseHeadDir = "/usr/local/www/ports"; # Ports database file to use if ( -f "$portsDatabaseHeadDir/INDEX-14" ) { $ports_database = 'INDEX-14'; } elsif ( -f "$portsDatabaseHeadDir/INDEX-13" ) { $ports_database = 'INDEX-13'; } else { $ports_database = 'INDEX'; } # URL of ports tree for browsing $remotePrefixFtp = 'ports'; # Web interface for the Ports tree $remotePrefixRepo = 'https://cgit.FreeBSD.org/ports'; # visible E-Mail address, plain text $mailto = 'www@FreeBSD.org'; # Mailinglist for FreeBSD Ports $mailtoList = 'ports@FreeBSD.org'; # use mailto:email?subject $mailtoAdvanced = 'yes'; # the URL if you click at the E-Mail address (see below) $mailtoURL = "mailto:$mailto" if !$mailtoURL; # security $ENV{'PATH'} = '/bin:/usr/bin'; } sub packages_exist { local ( $file, *p ) = @_; open( P, $file ) || do { warn "open $file: $!\n"; warn "Cannot create packages links\n"; return 1; }; while (

) { chop; $p{$_} = 1; } close P; return 0; } # return the date of the last ports database update sub last_update { local ($file) = "$portsDatabaseHeadDir/$ports_database"; local ( $modtime, $modtimestr ); $modtime = ( stat($file) )[9]; if ( defined($modtime) && $modtime > 0 ) { $modtimestr = strftime( "%Y-%m-%d %H:%M:%S UTC", gmtime($modtime) ); } else { $modtimestr = "Unknown"; } return $modtimestr; } sub last_update_message { return "

Last database update: @{[ &last_update ]}

\n"; } sub dec { local ($_) = @_; s/\+/ /g; # '+' -> space s/%(..)/pack("c",hex($1))/ge; # '%ab' -> char ab return ($_); } # $indent is a bit of optional data processing I put in for # formatting the data nicely when you are emailing it. # This is derived from code by Denis Howe # and Thomas A Fine sub decode_form { local ( $form, *data, $indent, $key, $_ ) = @_; foreach $_ ( split( /&/, $form ) ) { ( $key, $_ ) = split( /=/, $_, 2 ); $_ =~ s/\+/ /g; # + -> space $key =~ s/\+/ /g; # + -> space $_ =~ s/%([\da-f]{1,2})/pack(C,hex($1))/eig; # undo % escapes $key =~ s/%([\da-f]{1,2})/pack(C,hex($1))/eig; # undo % escapes $_ =~ s/[\r\n]+/\n\t/g if defined($indent); # indent data after \n $data{$key} = $_; } } sub escapeHTML { my $toencode = shift; return "" unless defined($toencode); $toencode =~ s{&}{&}gso; $toencode =~ s{<}{<}gso; $toencode =~ s{>}{>}gso; $toencode =~ s{"}{"}gso; return $toencode; } # encode unknown data for use in a URL sub encode_url { local ($_) = @_; s/([\000-\032\;\/\?\:\@\&\=\%\'\"\`\<\>\177-\377 ])/sprintf('%%%02x',ord($1))/eg; # s/%20/+/g; $_; } sub warn { print "$_[0]" } sub env { defined( $ENV{ $_[0] } ) ? $ENV{ $_[0] } : undef; } sub exit { exit 0 } sub readindex { local ( *var, *msec ) = @_; local ($localportsdb) = "$portsDatabaseHeadDir/$ports_database"; local ( @tmp, @s ); open( C, $localportsdb ) || do { warn "Cannot open ports database $localportsdb: $!\n"; &exit; }; while () { next if $query && !/$query/oi; chop; @tmp = split(/\|/); $var{"$tmp[0]"} = $_; @s = split( /\s+/, $tmp[6] ); foreach (@s) { $msec{"$tmp[1],$_"} = 1; } } close C; } # extract sub collections sub readcoll { local ( @a, @b, %key ); local ($file) = '../ports/categories'; local ($localportsdb) = "$portsDatabaseHeadDir/$ports_database"; if ( -r $file && open( C, $file ) ) { while () { chop; if (/^\s*([^,]+),\s*"([^"]+)",\s*([A-Z]+)/) { @b = split( /\s+/, $1 ); foreach (@b) { if ( !defined( $key{$_} ) ) { $key{$_} = 1; } } } } } else { if ( -r $localportsdb ) { open( C, $localportsdb ) || do { warn "Cannot open ports database $localportsdb: $!\n"; &exit; } } while () { chop; @a = split('\|'); @b = split( /\s+/, $a[6] ); foreach (@b) { if ( !defined( $key{$_} ) ) { $key{$_} = 1; } } } } close C; @a = (); foreach ( sort keys %key ) { push( @a, $_ ); } return @a; } # basic function for HTML output sub out { local ($line) = @_; local ( $version, $path, $local, $comment, $descfile, $email, $sections, $bdepends, $rdepends, @rest ) = split( /\|/, $line ); if ( $path =~ m%^$localPrefix/([^/]+)%o ) { if ( !$out_sec || $1 ne $out_sec ) { print "\n" if $counter > 0; print qq{\n

} . qq{Category $1} . "

\n
\n"; $out_sec = $1; } } $counter++; $pathB = $path; $pathB =~ s/^$localPrefix/ports/o; $path =~ s/^$localPrefix/$remotePrefixFtp/o; $descfile =~ s/^$localPrefix/$remotePrefixFtp/o; $version = &encode_url($version); #$version =~ s/[\+,]/X/g; local ($l) = $path; $l =~ s%^$remotePrefixFtp%$remotePrefixRepo/log%o; local ($t) = $path; $t =~ s%^$remotePrefixFtp%$remotePrefixRepo/tree%o; $descfile =~ s%^$remotePrefixFtp%$remotePrefixRepo/plain%o; print qq{
$version
\n}; print qq{
}, &escapeHTML($comment), qq{
\n}; print qq[Description :\n]; print qq[Changes
\n]; print qq{Maintained by: $email
\n}; local (@s) = split( /\s+/, $sections ); if ( $#s > 0 ) { print qq{Also listed in: }; foreach (@s) { print qq{$_ } if $_ ne $out_sec; } print "
\n"; } if ( $bdepends || $rdepends ) { local ($flag) = 0; local ($last) = ''; print qq{Requires: }; foreach ( sort split( /\s+/, "$bdepends $rdepends" ) ) { # delete double entries next if $_ eq $last; $last = $_; print ", " if $flag; $flag++; print qq{$_}; } print "
\n"; } print qq[
]; # XXX: should be done in a CSS print qq[
 
]; print qq[\n\n]; } # search and output sub search_ports { local (@a) = (); local ( $key, $name, $text ); foreach $key ( sort keys %today ) { next if $today{$key} !~ /$query/oi; next if $counter >= $max; @a = split( /\|/, $today{$key} ); $name = $a[0]; #$name =~ s/(\W)/\\$1/g; $text = $a[3]; #$text =~ s/(\W)/\\$1/g; if ( $section ne "all" ) { next if $a[6] !~ /\b$section\b/; } #warn "$stype:$query: $name $text\n"; if ( $stype eq "name" && $name =~ /$query/o ) { &out( $today{$key} ); } elsif ( $stype eq "text" && $text =~ /$query/oi ) { &out( $today{$key} ); } elsif ( $stype eq "all" && ( $text =~ /$query/oi || $name =~ /$query/io ) ) { &out( $today{$key} ); } elsif ( $stype eq 'maintainer' && $a[5] =~ /$query/io ) { &out( $today{$key} ); } elsif ( $stype eq 'requires' && ( $a[7] =~ /$query/io || $a[8] =~ /$query/io ) ) { &out( $today{$key} ); } } } sub forms { print qq{

The FreeBSD Ports and Packages Collection offers a simple way for users and administrators to install applications.

}; print qq{

"Package Name" searches for the name of a port or distribution. "Description" searches case-insensitive in a short comment about the port. "All" searches case-insensitive for the package name and in the description about the port.

Search for:

@{[ &footer_links ]}
}; } sub footer { print < Powered by FreeBSD © 1996-2024 by Wolfram Schneider. All rights reserved.
General questions about FreeBSD ports should be sent to $mailtoList
@{[ &last_update_message ]}

EOF } sub check_query { my ($query, $sourceid) = @_; $query =~ s/"/ /g; $query =~ s/^\s+//; $query =~ s/\s+$//; # XXX: Firefox opensearch autocomplete workarounds if ($sourceid eq 'opensearch') { # remove space before a dot $query =~ s/ \././g; # remove space between double colon $query =~ s/: :/::/g; } return $query; } sub check_input { if ($query) { $stype = "all" if !$stype; if ( !( $stype eq "name" || $stype eq "text" || $stype eq "maintainer" || $stype eq "requires" || $stype eq "all" ) ) { &warn( "unknown search type ``$stype'', use `all', `text', `name', 'requires', or `maintainer'\n" ); &exit(0); } } $max = int($max); if ($max <= 0 || $max > $max_hits) { warn "reset max=$max to $max_hits_default\n"; $max = $max_hits_default; } } sub faq { print <

FreeBSD Ports Search Help

Keywords

Description
A more detailed description (text).
Changes
Read the latest changes via the git repo

Documentation

Handbook: Using the Ports Collection

You may also search the ports manual pages.

Updates

The script ports.cgi use the file $ports_database as database for its operations. $ports_database is updated automatically every two hours.

@{[ &footer_links ]}
EOF } sub footer_links { return < home @{[ $stype eq "faq" ? "" : qq, | help, ]} EOF } # # Main # &init_variables; $query_string = &env('QUERY_STRING'); $path_info = &env('PATH_INFO'); &decode_form( $query_string, *form ); $section = $form{'sektion'}; $section = 'all' if ( !$section ); $query = $form{'query'}; $stype = $form{'stype'}; $sourceid = $form{'sourceid'} // ""; $script_name = &env('SCRIPT_NAME'); $max = $form{'max'} // $max_hits_default; if ( $path_info eq "/source" ) { # XXX print "Content-type: text/plain\n\n"; open( R, $0 ) || do { print "ick!\n"; &exit; }; while () { print } close R; &exit; } if ( $stype eq "faq" ) { print &short_html_header( "FreeBSD Ports Search Help", 1 ); &faq; &footer; print &html_footer; &exit(0); } print &html_header( "FreeBSD Ports Search", 1 ); # allow `/ports.cgi?netscape' where 'netscape' is the query port to search # this make links to this script shorter if ( !$query && $query_string =~ /^([^=&]+)$/ ) { $query = $1; } # automatically read collections, need only 0.2 sec on a pentium @sec = &readcoll; $query = &check_query($query, $sourceid); &forms; if ( $query_string eq "" || !$query ) { &footer; print &html_footer; &exit(0); } &check_input; $counter = 0; # no prefix search for requires supported yet $query =~ s/^\^// if $stype eq 'requires'; # quote non characters $query =~ s/([^\w\^])/\\$1/g; # search if ($query) { &readindex( *today, *msec ); &search_ports; } if ( !$counter ) { print < Sorry, nothing found. You may look for other FreeBSD Search Services

EOF } else { print "
\n"; my $counter_message = $counter; if ($counter >= $max) { $counter_message .= " (max hit limit reached)"; warn "$counter_message: query=$query stype=$stype section=$section\n" if $debug >= 1; } print "

Number of hits: $counter_message\n

\n"; print &footer_links; } print qq{
\n}; &footer; print &html_footer;