lintpksrc: fix parsing of the ':S' modifier in makefilesdiff -r1.57 -r1.58 pkgsrc/pkgtools/lintpkgsrc/files/lintpkgsrc.pl
(rillig)
@@ -1,16 +1,16 @@ | @@ -1,16 +1,16 @@ | |||
1 | #!@PERL5@ | 1 | #!@PERL5@ | |
2 | 2 | |||
3 | # $NetBSD: lintpkgsrc.pl,v 1.57 2022/08/09 18:14:22 rillig Exp $ | 3 | # $NetBSD: lintpkgsrc.pl,v 1.58 2022/08/09 18:35:43 rillig Exp $ | |
4 | 4 | |||
5 | # Written by David Brownlee <abs@netbsd.org>. | 5 | # Written by David Brownlee <abs@netbsd.org>. | |
6 | # | 6 | # | |
7 | # Caveats: | 7 | # Caveats: | |
8 | # The 'Makefile parsing' algorithm used to obtain package versions and | 8 | # The 'Makefile parsing' algorithm used to obtain package versions and | |
9 | # DEPENDS information is geared towards speed rather than perfection, | 9 | # DEPENDS information is geared towards speed rather than perfection, | |
10 | # though it has gotten somewhat better over time, it only parses the | 10 | # though it has gotten somewhat better over time, it only parses the | |
11 | # simpler Makefile conditionals. | 11 | # simpler Makefile conditionals. | |
12 | # | 12 | # | |
13 | # TODO: Handle fun DEPENDS like avifile-devel with | 13 | # TODO: Handle fun DEPENDS like avifile-devel with | |
14 | # {qt2-designer>=2.2.4,qt2-designer-kde>=2.3.1nb1} | 14 | # {qt2-designer>=2.2.4,qt2-designer-kde>=2.3.1nb1} | |
15 | 15 | |||
16 | use locale; | 16 | use locale; | |
@@ -869,68 +869,74 @@ sub list_pkgsrc_pkgdirs($$) { | @@ -869,68 +869,74 @@ sub list_pkgsrc_pkgdirs($$) { | |||
869 | && $_ ne 'pkg' | 869 | && $_ ne 'pkg' | |
870 | && $_ ne 'CVS' | 870 | && $_ ne 'CVS' | |
871 | && substr($_, 0, 1) ne '.', | 871 | && substr($_, 0, 1) ne '.', | |
872 | readdir(CAT)); | 872 | readdir(CAT)); | |
873 | closedir(CAT); | 873 | closedir(CAT); | |
874 | @pkgdirs; | 874 | @pkgdirs; | |
875 | } | 875 | } | |
876 | 876 | |||
877 | # Convert the glob pattern to a regular expression. | 877 | # Convert the glob pattern to a regular expression. | |
878 | # Return '' if the regular expression equals the glob expression. | 878 | # Return '' if the regular expression equals the glob expression. | |
879 | # Return undef on error. | 879 | # Return undef on error. | |
880 | sub glob2regex($) { | 880 | sub glob2regex($) { | |
881 | my ($glob) = @_; | 881 | my ($glob) = @_; | |
882 | my (@chars, $in_alt); | |||
883 | my ($regex); | |||
884 | 882 | |||
885 | @chars = split(//, $glob); | 883 | my @chars = split(//, $glob); | |
884 | my $alternative_depth = 0; | |||
885 | my $regex = ''; | |||
886 | while (defined($_ = shift @chars)) { | 886 | while (defined($_ = shift @chars)) { | |
887 | if ($_ eq '*') { | 887 | if ($_ eq '*') { | |
888 | $regex .= '.*'; | 888 | $regex .= '.*'; | |
889 | } elsif ($_ eq '?') { | 889 | } elsif ($_ eq '?') { | |
890 | $regex .= '.'; | 890 | $regex .= '.'; | |
891 | } elsif ($_ eq '+') { | 891 | } elsif ($_ eq '+') { | |
892 | $regex .= '\\+'; | 892 | $regex .= '\\+'; | |
893 | } elsif ($_ eq '\\') { | 893 | } elsif ($_ eq '\\' && @chars > 0) { | |
894 | $regex .= $_ . shift @chars; | 894 | my $next = shift @chars; | |
895 | $regex .= $next =~ /\w/ ? "$next" : "\\$next"; | |||
895 | } elsif ($_ eq '.' || $_ eq '|') { | 896 | } elsif ($_ eq '.' || $_ eq '|') { | |
896 | $regex .= quotemeta; | 897 | $regex .= quotemeta; | |
897 | } elsif ($_ eq '{') { | 898 | } elsif ($_ eq '{') { | |
898 | $regex .= '('; | 899 | $regex .= '('; | |
899 | ++$in_alt; | 900 | ++$alternative_depth; | |
900 | } elsif ($_ eq '}') { | 901 | } elsif ($_ eq '}') { | |
901 | if (!$in_alt) { | 902 | if ($alternative_depth == 0) { | |
902 | # Error | 903 | # Error | |
903 | return undef; | 904 | return undef; | |
904 | } | 905 | } | |
905 | $regex .= ')'; | 906 | $regex .= ')'; | |
906 | --$in_alt; | 907 | --$alternative_depth; | |
907 | } elsif ($_ eq ',' && $in_alt) { | 908 | } elsif ($_ eq ',' && $alternative_depth) { | |
908 | $regex .= '|'; | 909 | $regex .= '|'; | |
910 | } elsif ($_ eq '[') { | |||
911 | $regex .= '['; | |||
912 | while (defined($_ = shift @chars)) { | |||
913 | $regex .= $_; | |||
914 | if ($_ eq ']') { | |||
915 | last; | |||
916 | } elsif ($_ eq '\\' && @chars > 0) { | |||
917 | $regex .= shift @chars; | |||
918 | } | |||
919 | } | |||
920 | return undef if $_ ne ']'; | |||
909 | } else { | 921 | } else { | |
910 | $regex .= $_; | 922 | $regex .= $_; | |
911 | } | 923 | } | |
912 | } | 924 | } | |
913 | 925 | |||
914 | if ($in_alt) { | 926 | return undef if $alternative_depth > 0; | |
915 | # Error | 927 | return '' if $regex eq $glob; # XXX: why? | |
916 | return undef; | 928 | ||
917 | } | 929 | $opt{D} and print "glob2regex: $glob -> $regex\n"; | |
918 | if ($regex eq $glob) { | |||
919 | return (''); | |||
920 | } | |||
921 | if ($opt{D}) { | |||
922 | print "glob2regex: $glob -> $regex\n"; | |||
923 | } | |||
924 | '^' . $regex . '$'; | 930 | '^' . $regex . '$'; | |
925 | } | 931 | } | |
926 | 932 | |||
927 | # Perform some (reasonable) subset of 'pkg_info -e' / glob(3) | 933 | # Perform some (reasonable) subset of 'pkg_info -e' / glob(3) | |
928 | # Returns (sometimes best guess at) package name, | 934 | # Returns (sometimes best guess at) package name, | |
929 | # and either 'problem version' or undef if all OK | 935 | # and either 'problem version' or undef if all OK | |
930 | # | 936 | # | |
931 | sub package_globmatch($) { | 937 | sub package_globmatch($) { | |
932 | my ($pkgmatch) = @_; | 938 | my ($pkgmatch) = @_; | |
933 | my ($matchpkgname, $matchver, $regex); | 939 | my ($matchpkgname, $matchver, $regex); | |
934 | 940 | |||
935 | if ($pkgmatch =~ /^([^*?[]+)(<|>|<=|>=|-)(\d[^*?[{]*)$/) { | 941 | if ($pkgmatch =~ /^([^*?[]+)(<|>|<=|>=|-)(\d[^*?[{]*)$/) { | |
936 | 942 |
@@ -1,56 +1,59 @@ | @@ -1,56 +1,59 @@ | |||
1 | # $NetBSD: glob.t,v 1.5 2022/08/09 18:14:22 rillig Exp $ | 1 | # $NetBSD: glob.t,v 1.6 2022/08/09 18:35:43 rillig Exp $ | |
2 | 2 | |||
3 | use strict; | 3 | use strict; | |
4 | use warnings; | 4 | use warnings; | |
5 | use Test; | 5 | use Test; | |
6 | 6 | |||
7 | BEGIN { plan tests => 12, onfail => sub { die } } | 7 | BEGIN { plan tests => 12, onfail => sub { die } } | |
8 | 8 | |||
9 | require('../lintpkgsrc.pl'); | 9 | require('../lintpkgsrc.pl'); | |
10 | 10 | |||
11 | sub test_glob2regex() { | 11 | sub test_glob2regex() { | |
12 | 12 | |||
13 | ok(glob2regex('*'), '^.*$'); | 13 | ok(glob2regex('*'), '^.*$'); | |
14 | ok(glob2regex('\*'), ''); | |||
14 | 15 | |||
15 | ok(glob2regex('?'), '^.$'); | 16 | ok(glob2regex('?'), '^.$'); | |
17 | ok(glob2regex('\?'), ''); | |||
16 | 18 | |||
17 | ok(glob2regex('[a-z]'), ''); | 19 | # Ordinary characters in glob patterns. | |
20 | ok(glob2regex('+'), '^\+$'); | |||
21 | ok(glob2regex('\+'), ''); | |||
22 | ok(glob2regex('|'), '^\|$'); | |||
23 | ok(glob2regex('\|'), ''); | |||
24 | ||||
25 | ok(glob2regex('\.'), ''); | |||
26 | ok(glob2regex('\n'), '^n$'); | |||
27 | ok(glob2regex('\\\\'), ''); | |||
28 | ok(glob2regex('\['), ''); | |||
29 | ok(glob2regex('\{'), ''); | |||
30 | ok(glob2regex('\-'), ''); | |||
18 | 31 | |||
32 | ok(glob2regex('[a-z]'), ''); | |||
19 | ok(glob2regex('[a-z0-9]'), ''); | 33 | ok(glob2regex('[a-z0-9]'), ''); | |
20 | ||||
21 | ok(glob2regex('[a-z0-9_]'), ''); | 34 | ok(glob2regex('[a-z0-9_]'), ''); | |
22 | 35 | ok(glob2regex('[*]'), ''); | ||
23 | # Outside of braces, the ',' is a regular character. | |||
24 | ok(glob2regex('a,b'), ''); | |||
25 | ||||
26 | # FIXME: Inside brackets, the '*' is a literal '*'. | |||
27 | ok(glob2regex('[*]'), '^[.*]$'); | |||
28 | ||||
29 | ok(glob2regex('\*'), ''); | |||
30 | 36 | |||
31 | ok(glob2regex('*.[ch]'), '^.*\.[ch]$'); | 37 | ok(glob2regex('*.[ch]'), '^.*\.[ch]$'); | |
32 | 38 | |||
39 | # Outside of braces, the ',' is a regular character. | |||
40 | ok(glob2regex('a,b'), ''); | |||
33 | ok(glob2regex('{one,two}'), '^(one|two)$'); | 41 | ok(glob2regex('{one,two}'), '^(one|two)$'); | |
34 | ||||
35 | ok(glob2regex('{{thi,fou}r,fif}teen'), '^((thi|fou)r|fif)teen$'); | 42 | ok(glob2regex('{{thi,fou}r,fif}teen'), '^((thi|fou)r|fif)teen$'); | |
36 | 43 | |||
37 | # There is an unbalanced '}' at the very end. | 44 | # There is an unbalanced '}' at the very end. | |
38 | ok(glob2regex('{{thi,fou}r,fif}teen}'), undef); | 45 | ok(glob2regex('{four,fif}teen}'), undef); | |
39 | ||||
40 | ok(glob2regex('a+b|c'), '^a\+b\|c$'); | |||
41 | 46 | |||
47 | # An escaped '[' does not start a character class. | |||
42 | ok(glob2regex('a\[b*'), '^a\[b.*$'); | 48 | ok(glob2regex('a\[b*'), '^a\[b.*$'); | |
43 | 49 | |||
44 | ok(glob2regex('a\+b'), ''); | 50 | ok(glob2regex('a\n*'), '^an.*$'); | |
45 | ||||
46 | ok(glob2regex('a\?b'), ''); | |||
47 | ||||
48 | # XXX: Depending on the exact implementation, the '\n' may be | |||
49 | # interpreted as a newline, a literal 'n' or a literal '\' 'n'. | |||
50 | ok(glob2regex('a\n*'), '^a\n.*$'); | |||
51 | 51 | |||
52 | # https://gnats.netbsd.org/12996 | 52 | # https://gnats.netbsd.org/12996 | |
53 | ok(glob2regex('libsigc++'), '^libsigc\+\+$'); | 53 | ok(glob2regex('libsigc++'), '^libsigc\+\+$'); | |
54 | ||||
55 | my $re = 'a\nb'; | |||
56 | ok("a\nb" =~ $re, 1); | |||
54 | } | 57 | } | |
55 | 58 | |||
56 | test_glob2regex(); | 59 | test_glob2regex(); |