"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "Basic/SourceFilter/NiceSlice.pm" between
PDL-2.079.tar.gz and PDL-2.080.tar.gz

About: PDL (Perl Data Language) aims to turn perl into an efficient numerical language for scientific computing (similar to IDL and MatLab).

NiceSlice.pm  (PDL-2.079):NiceSlice.pm  (PDL-2.080)
skipping to change at line 54 skipping to change at line 54
# "for $var(LIST)" or "foreach $var(LIST)" statement. CED. # "for $var(LIST)" or "foreach $var(LIST)" statement. CED.
# #
# Modified 5-Nov-2007: stop processing if we encounter m/^no\s+PDL\;:\;:NiceSlic e\;\s*$/. # Modified 5-Nov-2007: stop processing if we encounter m/^no\s+PDL\;:\;:NiceSlic e\;\s*$/.
# the next one is largely stolen from Regexp::Common # the next one is largely stolen from Regexp::Common
my $RE_cmt = qr'(?:(?:\#)(?:[^\n]*)(?:\n))'; my $RE_cmt = qr'(?:(?:\#)(?:[^\n]*)(?:\n))';
use Text::Balanced; # used to find parenthesis-delimited blocks use Text::Balanced; # used to find parenthesis-delimited blocks
BEGIN { BEGIN {
# this is purely for performance reasons - patch: https://github.com/steve-m-hay # fix for problem identified by Ingo, also EOP fix that needs propagating back
/Text-Balanced/pull/5
my %ref_not_regex = map +($_=>1), qw(CODE Text::Balanced::Extractor);
sub my_extract_multiple (;$$$$) # ($text, $functions_ref, $max_fields, $i
gnoreunknown)
{
my $textref = defined($_[0]) ? \$_[0] : \$_;
my $posbug = pos;
my ($lastpos, $firstpos);
my @fields = ();
#for ($$textref)
{
my @func = defined $_[1] ? @{$_[1]} : die "in overridden extract_
multiple must supply functions";
my $max = defined $_[2] && $_[2]>0 ? $_[2] : 1_000_000_000;
my $igunk = $_[3];
pos $$textref ||= 0;
unless (wantarray)
{
use Carp;
carp "extract_multiple reset maximal count to 1 in scalar
context"
if $^W && defined($_[2]) && $max > 1;
$max = 1
}
my $unkpos;
my $func;
my $class;
my @class;
foreach $func ( @func )
{
if (ref($func) eq 'HASH')
{
push @class, (keys %$func)[0];
$func = (values %$func)[0];
}
else
{
push @class, undef;
}
$func = qr/\G$func/ if !$ref_not_regex{ref $func};
}
FIELD: while (pos($$textref) < length($$textref))
{
my ($field, $rem);
my @bits;
foreach my $i ( 0..$#func )
{
my $pref;
$func = $func[$i];
$class = $class[$i];
$lastpos = pos $$textref;
if (ref($func) eq 'CODE')
{ ($field,$rem,$pref) = @bits = $func->($
$textref) }
elsif (ref($func) eq 'Text::Balanced::Extractor')
{ @bits = $field = $func->extract($$textr
ef) }
elsif( $$textref =~ m/$func[$i]/gc )
{ @bits = $field = defined($1)
? $1
: substr($$textref, $-[0], $+[0] - $-[0])
}
$pref ||= "";
if (defined($field) && length($field))
{
if (!$igunk) {
$unkpos = $lastpos
if length($pref) && !defi
ned($unkpos);
if (defined $unkpos)
{
push @fields, substr($$te
xtref, $unkpos, $lastpos-$unkpos).$pref;
$firstpos = $unkpos unles
s defined $firstpos;
undef $unkpos;
last FIELD if @fields ==
$max;
}
}
push @fields, $class
? bless (\$field, $class)
: $field;
$firstpos = $lastpos unless defined $firs
tpos;
$lastpos = pos $$textref;
last FIELD if @fields == $max;
next FIELD;
}
}
if ($$textref =~ /\G(.)/gcs)
{
$unkpos = pos($$textref)-1
unless $igunk || defined $unkpos;
}
}
if (defined $unkpos)
{
push @fields, substr($$textref, $unkpos);
$firstpos = $unkpos unless defined $firstpos;
$lastpos = length $$textref;
}
last;
}
pos $$textref = $lastpos;
return @fields if wantarray;
$firstpos ||= 0;
eval { substr($$textref,$firstpos,$lastpos-$firstpos)="";
pos $$textref = $firstpos };
return $fields[0];
}
sub my_match_codeblock($$$$$$$);
sub my_match_codeblock($$$$$$$)
{
my ($textref, $pre, $ldel_outer, $rdel_outer, $ldel_inner, $rdel_inner, $rd)
= @_;
my $startpos = pos($$textref) = pos($$textref) || 0;
unless ($$textref =~ m/$pre/gc)
{
Text::Balanced::_failmsg qq{Did not match prefix /$pre/ at"} .
substr($$textref,pos($$textref),20) .
q{..."},
pos $$textref;
return;
}
my $codepos = pos($$textref);
unless ($$textref =~ m/\G($ldel_outer)/gc) # OUTERMOST DELIMITER
{
Text::Balanced::_failmsg qq{Did not find expected opening bracket at "}
.
substr($$textref,pos($$textref),20) .
q{..."},
pos $$textref;
pos $$textref = $startpos;
return;
}
my $closing = $1;
$closing =~ tr/([<{/)]>}/;
my $matched;
my $patvalid = 1;
while (pos($$textref) < length($$textref))
{
$matched = '';
if ($rd && $$textref =~ m#\G(\Q(?)\E|\Q(s?)\E|\Q(s)\E)#gc)
{
$patvalid = 0;
next;
}
if ($$textref =~ m/\G\s*#.*/gc)
{
next;
}
if ($$textref =~ m/\G\s*($rdel_outer)/gc)
{
unless ($matched = ($closing && $1 eq $closing) )
{
next if $1 eq '>'; # MIGHT BE A "LESS THAN"
Text::Balanced::_failmsg q{Mismatched closing bracket at "} .
substr($$textref,pos($$textref),20) .
qq{...". Expected '$closing'},
pos $$textref;
}
last;
}
if (my_match_variable($textref,qr/\G(\s*)/) ||
my_match_quotelike($textref,'\s*',$patvalid,$patvalid) )
{
$patvalid = 0;
next;
}
# NEED TO COVER MANY MORE CASES HERE!!!
if ($$textref =~ m#\G\s*(?!$ldel_inner)
( [-+*x/%^&|.]=?
| [!=]~
| =(?!>)
| (\*\*|&&|\|\||<<|>>)=?
| split|grep|map|return
| [([]
)#gcx)
{
$patvalid = 1;
next;
}
if ( my_match_codeblock($textref, qr/\G(\s*)/, $ldel_inner, $rdel_inner,
$ldel_inner, $rdel_inner, $rd) )
{
$patvalid = 1;
next;
}
if ($$textref =~ m/\G\s*$ldel_outer/gc)
{
Text::Balanced::_failmsg q{Improperly nested codeblock at "} .
substr($$textref,pos($$textref),20) .
q{..."},
pos $$textref;
last;
}
$patvalid = 0;
$$textref =~ m/\G\s*(\w+|[-=>]>|.|\Z)/gc;
}
continue { $@ = undef }
unless ($matched)
{
Text::Balanced::_failmsg 'No match found for opening bracket', pos $$tex
tref
unless $@;
return;
}
my $endpos = pos($$textref);
return ( $startpos, $codepos-$startpos,
$codepos, $endpos-$codepos,
$endpos, length($$textref)-$endpos,
);
}
sub my_match_variable($$);
sub my_match_variable($$)
{
# $#
# $^
# $$
my ($textref, $pre) = @_;
my $startpos = pos($$textref) = pos($$textref)||0;
unless ($$textref =~ m/$pre/gc)
{
Text::Balanced::_failmsg "Did not find prefix: /$pre/", pos $$textref;
return;
}
my $varpos = pos($$textref);
unless ($$textref =~ m{\G\$\s*(?!::)(\d+|[][&`'+*./|,";%=~:?!\@<>()-]|\^[a-z
]?)}gci)
{
unless ($$textref =~ m/\G((\$#?|[*\@\%]|\\&)+)/gc)
{
Text::Balanced::_failmsg "Did not find leading dereferencer", pos $$
textref;
pos $$textref = $startpos;
return;
}
my $deref = $1;
unless ($$textref =~ m/\G\s*(?:::|')?(?:[_a-z]\w*(?:::|'))*[_a-z]\w*/gci
or my_match_codeblock($textref, qr/\G()/, '\{', '\}', '\{', '\}', 0)
or $deref eq '$#' or $deref eq '$$' )
{
Text::Balanced::_failmsg "Bad identifier after dereferencer", pos $$
textref;
pos $$textref = $startpos;
return;
}
}
while (1)
{
next if $$textref =~ m/\G\s*(?:->)?\s*[{]\w+[}]/gc;
next if my_match_codeblock($textref,
qr/\G(\s*->\s*(?:[_a-zA-Z]\w+\s*)?)/,
qr/[({[]/, qr/[)}\]]/,
qr/[({[]/, qr/[)}\]]/, 0);
next if my_match_codeblock($textref,
qr/\G(\s*)/, qr/[{[]/, qr/[}\]]/,
qr/[{[]/, qr/[}\]]/, 0);
next if my_match_variable($textref,qr/\G(\s*->\s*)/);
next if $$textref =~ m/\G\s*->\s*\w+(?![\{([])/gc;
last;
}
my $endpos = pos($$textref);
return ($startpos, $varpos-$startpos,
$varpos, $endpos-$varpos,
$endpos, length($$textref)-$endpos
);
}
sub my_extract_variable (;$$)
{
my $textref = defined $_[0] ? \$_[0] : \$_;
return ("","","") unless defined $$textref;
my $pre = defined $_[1] ? qr/\G($_[1])/ : qr/\G(\s*)/;
my @match = my_match_variable($textref,$pre);
return Text::Balanced::_fail wantarray, $textref unless @match;
return Text::Balanced::_succeed wantarray, $textref,
@match[2..3,4..5,0..1]; # MATCH, REMAINDER, PREFIX
}
sub my_extract_codeblock (;$$$$$)
{
my $textref = defined $_[0] ? \$_[0] : \$_;
my $wantarray = wantarray;
my $ldel_inner = defined $_[1] ? $_[1] : '{';
my $pre = !defined $_[2] ? qr/\G(\s*)/ : qr/\G($_[2])/;
my $ldel_outer = defined $_[3] ? $_[3] : $ldel_inner;
my $rd = $_[4];
my $rdel_inner = $ldel_inner;
my $rdel_outer = $ldel_outer;
my $posbug = pos;
for ($ldel_inner, $ldel_outer) { tr/[]()<>{}\0-\377/[[((<<{{/ds }
for ($rdel_inner, $rdel_outer) { tr/[]()<>{}\0-\377/]]))>>}}/ds }
for ($ldel_inner, $ldel_outer, $rdel_inner, $rdel_outer)
{
$_ = '('.join('|',map { quotemeta $_ } split('',$_)).')'
}
pos = $posbug;
my @match = my_match_codeblock($textref, $pre,
$ldel_outer, $rdel_outer,
$ldel_inner, $rdel_inner,
$rd);
return Text::Balanced::_fail($wantarray, $textref) unless @match;
return Text::Balanced::_succeed($wantarray, $textref,
@match[2..3,4..5,0..1] # MATCH, REMAINDER, PREFIX
);
}
# fixes false-positive heredoc and false-match {y=>1} in 2.04 - patch: https://g
ithub.com/steve-m-hay/Text-Balanced/pull/6
my %mods = (
'none' => '[cgimsox]*', 'm'=>'[cgimsox]*', 's'=>'[cegimsox]*',
'tr'=> '[cds]*', 'y'=> '[cds]*', 'qq'=> '', 'qx'=> '', 'qw'=> '',
'qr'=> '[imsx]*', 'q'=> '',
);
sub my_match_quotelike($$$$) # ($textref, $prepat, $allow_raw_match)
{
my ($textref, $pre, $rawmatch, $qmark) = @_;
my ($textlen,$startpos,
$oppos,
$preld1pos,$ld1pos,$str1pos,$rd1pos,
$preld2pos,$ld2pos,$str2pos,$rd2pos,
$modpos) = ( length($$textref), pos($$textref) = pos($$textref) || 0 );
unless ($$textref =~ m/\G($pre)/gc)
{
Text::Balanced::_failmsg qq{Did not find prefix /$pre/ at "} .
substr($$textref, pos($$textref), 20) .
q{..."},
pos $$textref;
return;
}
$oppos = pos($$textref);
my $initial = substr($$textref,$oppos,1);
if ($initial && $initial =~ m|^[\"\'\`]|
|| $rawmatch && $initial =~ m|^/|
|| $qmark && $initial =~ m|^\?|)
{
unless ($$textref =~ m/ \Q$initial\E [^\\$initial]* (\\.[^\\$initial]*)*
\Q$initial\E /gcsx)
{
Text::Balanced::_failmsg qq{Did not find closing delimiter to match
'$initial' at "} .
substr($$textref, $oppos, 20) .
q{..."},
pos $$textref;
pos $$textref = $startpos;
return;
}
$modpos= pos($$textref);
$rd1pos = $modpos-1;
if ($initial eq '/' || $initial eq '?')
{
$$textref =~ m/\G$mods{none}/gc
}
my $endpos = pos($$textref);
return (
$startpos, $oppos-$startpos, # PREFIX
$oppos, 0, # NO OPERATOR
$oppos, 1, # LEFT DEL
$oppos+1, $rd1pos-$oppos-1, # STR/PAT
$rd1pos, 1, # RIGHT DEL
$modpos, 0, # NO 2ND LDEL
$modpos, 0, # NO 2ND STR
$modpos, 0, # NO 2ND RDEL
$modpos, $endpos-$modpos, # MODIFIERS
$endpos, $textlen-$endpos, # REMAINDER
);
}
unless ($$textref =~ m{\G(\b(?:m|s|qq|qx|qw|q|qr|tr|y)\b(?=\s*\S)|<<(?=[a-zA
-Z]|\s*['"`;,]))}gc)
{
Text::Balanced::_failmsg q{No quotelike operator found after prefix at "
} .
substr($$textref, pos($$textref), 20) .
q{..."},
pos $$textref;
pos $$textref = $startpos;
return;
}
my $op = $1;
$preld1pos = pos($$textref);
if ($op eq '<<') {
$ld1pos = pos($$textref);
my $label;
if ($$textref =~ m{\G([A-Za-z_]\w*)}gc) {
$label = $1;
}
elsif ($$textref =~ m{ \G ' ([^'\\]* (?:\\.[^'\\]*)*) '
| \G " ([^"\\]* (?:\\.[^"\\]*)*) "
| \G ` ([^`\\]* (?:\\.[^`\\]*)*) `
}gcsx) {
$label = $+;
}
else {
$label = "";
}
my $extrapos = pos($$textref);
$$textref =~ m{.*\n}gc;
$str1pos = pos($$textref)--;
unless ($$textref =~ m{.*?\n(?=\Q$label\E\n)}gc) {
Text::Balanced::_failmsg qq{Missing here doc terminator ('$label') a
fter "} .
substr($$textref, $startpos, 20) .
q{..."},
pos $$textref;
pos $$textref = $startpos;
return;
}
$rd1pos = pos($$textref);
$$textref =~ m{\Q$label\E\n}gc;
$ld2pos = pos($$textref);
return (
$startpos, $oppos-$startpos, # PREFIX
$oppos, length($op), # OPERATOR
$ld1pos, $extrapos-$ld1pos, # LEFT DEL
$str1pos, $rd1pos-$str1pos, # STR/PAT
$rd1pos, $ld2pos-$rd1pos, # RIGHT DEL
$ld2pos, 0, # NO 2ND LDEL
$ld2pos, 0, # NO 2ND STR
$ld2pos, 0, # NO 2ND RDEL
$ld2pos, 0, # NO MODIFIERS
$ld2pos, $textlen-$ld2pos, # REMAINDER
$extrapos, $str1pos-$extrapos, # FILLETED BIT
);
}
$$textref =~ m/\G\s*/gc;
$ld1pos = pos($$textref);
$str1pos = $ld1pos+1;
if ($$textref !~ m/\G(\S)/gc) # SHOULD USE LOOKAHEAD
{
Text::Balanced::_failmsg "No block delimiter found after quotelike $op",
pos $$textref;
pos $$textref = $startpos;
return;
}
elsif (substr($$textref, $ld1pos, 2) eq '=>')
{
Text::Balanced::_failmsg "quotelike $op was actually quoted by '=>'",
pos $$textref;
pos $$textref = $startpos;
return;
}
pos($$textref) = $ld1pos; # HAVE TO DO THIS BECAUSE LOOKAHEAD BROKEN
my ($ldel1, $rdel1) = ("\Q$1","\Q$1");
if ($ldel1 =~ /[[(<\{]/)
{
$rdel1 =~ tr/[({</])}>/;
defined(Text::Balanced::_match_bracketed($textref,"",$ldel1,"","",$rdel1
))
|| do { pos $$textref = $startpos; return };
$ld2pos = pos($$textref);
$rd1pos = $ld2pos-1;
}
else
{
$$textref =~ /\G$ldel1[^\\$ldel1]*(\\.[^\\$ldel1]*)*$ldel1/gcs
|| do { pos $$textref = $startpos; return };
$ld2pos = $rd1pos = pos($$textref)-1;
}
my $second_arg = $op =~ /s|tr|y/ ? 1 : 0;
if ($second_arg)
{
my ($ldel2, $rdel2);
if ($ldel1 =~ /[[(<\{]/)
{
unless ($$textref =~ /\G\s*(\S)/gc) # SHOULD USE LOOKAHEAD
{
Text::Balanced::_failmsg "Missing second block for quotelike $op
",
pos $$textref;
pos $$textref = $startpos;
return;
}
$ldel2 = $rdel2 = "\Q$1";
$rdel2 =~ tr/[({</])}>/;
}
else
{
$ldel2 = $rdel2 = $ldel1;
}
$str2pos = $ld2pos+1;
if ($ldel2 =~ /[[(<\{]/)
{
pos($$textref)--; # OVERCOME BROKEN LOOKAHEAD
defined(Text::Balanced::_match_bracketed($textref,"",$ldel2,"","",$r
del2))
|| do { pos $$textref = $startpos; return };
}
else
{
$$textref =~ /[^\\$ldel2]*(\\.[^\\$ldel2]*)*$ldel2/gcs
|| do { pos $$textref = $startpos; return };
}
$rd2pos = pos($$textref)-1;
}
else
{
$ld2pos = $str2pos = $rd2pos = $rd1pos;
}
$modpos = pos $$textref;
$$textref =~ m/\G($mods{$op})/gc;
my $endpos = pos $$textref;
return (
$startpos, $oppos-$startpos, # PREFIX
$oppos, length($op), # OPERATOR
$ld1pos, 1, # LEFT DEL
$str1pos, $rd1pos-$str1pos, # STR/PAT
$rd1pos, 1, # RIGHT DEL
$ld2pos, $second_arg, # 2ND LDEL (MAYBE)
$str2pos, $rd2pos-$str2pos, # 2ND STR (MAYBE)
$rd2pos, $second_arg, # 2ND RDEL (MAYBE)
$modpos, $endpos-$modpos, # MODIFIERS
$endpos, $textlen-$endpos, # REMAINDER
);
}
sub my_extract_quotelike (;$$)
{
my $textref = $_[0] ? \$_[0] : \$_;
my $wantarray = wantarray;
my $pre = defined $_[1] ? $_[1] : '\s*';
my @match = my_match_quotelike($textref,$pre,0,0); # do not match // al
one as m//
return Text::Balanced::_fail($wantarray, $textref) unless @match;
return Text::Balanced::_succeed($wantarray, $textref,
$match[2], $match[18]-$match[2], # MATCH
@match[18,19], # REMAINDER
@match[0,1], # PREFIX
@match[2..17], # THE BITS
@match[20,21], # ANY FILLET?
);
}
# fix for problem identified by Ingo - no point in submitting patch to p5p until
above Text-Balanced PR is merged and released
my $ncws = qr/\s+/; my $ncws = qr/\s+/;
my $comment = qr/(?<![\$\@%])#.*/; my $comment = qr/(?<![\$\@%])#.*/;
my $id = qr/\b(?!([ysm]|q[rqxw]?|tr)\b)\w+/; my $id = qr/\b(?!([ysm]|q[rqxw]?|tr)\b)\w+/;
my $EOP = qr/\n\n|\Z/; my $EOP = qr/\n\n|\n#|\Z/;
my $CUT = qr/\n=cut.*$EOP/; my $CUT = qr/\n=cut.*$EOP/;
my $pod_or_DATA = qr/ my $pod_or_DATA = qr/
^=(?:head[1-4]|item) .*? $CUT ^=(?:head[1-4]|item) .*? $CUT
| ^=pod .*? $CUT | ^=pod .*? $CUT
| ^=for .*? $CUT | ^=for .*? $CUT
| ^=begin .*? $CUT | ^=begin .*? $CUT
| ^__(DATA|END)__\r?\n.* | ^__(DATA|END)__\r?\n.*
/smx; /smx;
my %extractor_for = ( my %extractor_for = (
code_no_comments code_no_comments
=> [ { DONT_MATCH => $comment }, => [ { DONT_MATCH => $comment },
$ncws, { DONT_MATCH => $pod_or_DATA }, \&my_extract_variable $ncws, { DONT_MATCH => $pod_or_DATA }, \&Text::Balanced::ext
, ract_variable,
$id, { DONT_MATCH => \&my_extract_quotelike } ], $id, { DONT_MATCH => \&Text::Balanced::extract_quotelike }
],
); );
use Filter::Simple (); use Filter::Simple ();
my $orig_gen_std_filter_for = \&Filter::Simple::gen_std_filter_for; my $orig_gen_std_filter_for = \&Filter::Simple::gen_std_filter_for;
sub my_gen_std_filter_for { sub my_gen_std_filter_for {
my ($type, $transform) = @_; my ($type, $transform) = @_;
goto &$orig_gen_std_filter_for if !$extractor_for{$type}; goto &$orig_gen_std_filter_for if !$extractor_for{$type};
return sub { return sub {
my $instr; my $instr;
my @components; my @components;
for (my_extract_multiple($_,$extractor_for{$type})) { for (Text::Balanced::extract_multiple($_,$extractor_for{$type})) {
if (ref()) { push @components, $_; $instr=0 } if (ref()) { push @components, $_; $instr=0 }
elsif ($instr) { $components[-1] .= $_ } elsif ($instr) { $components[-1] .= $_ }
else { push @components, $_; $instr=1 } else { push @components, $_; $instr=1 }
} }
my $count = 0; my $count = 0;
my $extractor = qr/\Q$;\E(.{4})\Q$;\E/s; my $extractor = qr/\Q$;\E(.{4})\Q$;\E/s;
$_ = join "", $_ = join "",
map { ref $_ ? $;.pack('N',$count++).$; : $_ } map { ref $_ ? $;.pack('N',$count++).$; : $_ }
@components; @components;
@components = grep { ref $_ } @components; @components = grep { ref $_ } @components;
$transform->(@_); $transform->(@_);
s/$extractor/${$components[unpack('N',$1)]}/g; s/$extractor/${$components[unpack('N',$1)]}/g;
} }
} }
# override the current extract_quotelike() routine # override the current extract_quotelike() routine
# needed before using Filter::Simple to work around a bug # needed before using Filter::Simple to work around a bug
# between Text::Balanced and Filter::Simple for our purpose. # between Text::Balanced and Filter::Simple for our purpose.
no warnings 'redefine'; no warnings 'redefine';
*Text::Balanced::extract_variable = \&my_extract_variable;
*Text::Balanced::_match_variable = \&my_match_variable;
*Text::Balanced::extract_codeblock = \&my_extract_codeblock;
*Text::Balanced::_match_codeblock = \&my_match_codeblock;
*Text::Balanced::extract_quotelike = \&my_extract_quotelike;
*Text::Balanced::_match_quotelike = \&my_match_quotelike;
*Text::Balanced::extract_multiple = \&my_extract_multiple;
*Filter::Simple::gen_std_filter_for = \&my_gen_std_filter_for; *Filter::Simple::gen_std_filter_for = \&my_gen_std_filter_for;
} }
# a call stack for error processing # a call stack for error processing
my @callstack = ('stackbottom'); my @callstack = ('stackbottom');
sub curarg { sub curarg {
my $arg = $callstack[-1]; # return top element of stack my $arg = $callstack[-1]; # return top element of stack
$arg =~ s/\((.*)\)/$1/s; $arg =~ s/\((.*)\)/$1/s;
return $arg; return $arg;
} }
 End of changes. 5 change blocks. 
544 lines changed or deleted 7 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)