Default LOG_PRIMASK for Sys-Syslog in cases where it is not
[p5sagit/p5-mst-13.2.git] / ext / Devel / PPPort / parts / ppptools.pl
CommitLineData
adfe19db 1################################################################################
2#
3# ppptools.pl -- various utility functions
4#
5################################################################################
6#
af36fda7 7# $Revision: 19 $
adfe19db 8# $Author: mhx $
af36fda7 9# $Date: 2007/08/13 22:59:58 +0200 $
adfe19db 10#
11################################################################################
12#
d2dacc4f 13# Version 3.x, Copyright (C) 2004-2007, Marcus Holland-Moritz.
adfe19db 14# Version 2.x, Copyright (C) 2001, Paul Marquess.
15# Version 1.x, Copyright (C) 1999, Kenneth Albanowski.
16#
17# This program is free software; you can redistribute it and/or
18# modify it under the same terms as Perl itself.
19#
20################################################################################
21
1d088ed8 22sub cat_file
23{
24 eval { require File::Spec };
25 return $@ ? join('/', @_) : File::Spec->catfile(@_);
26}
27
28sub all_files_in_dir
29{
30 my $dir = shift;
31 local *DIR;
32
33 opendir DIR, $dir or die "cannot open directory $dir: $!\n";
34 my @files = grep { !-d && !/^\./ } readdir DIR; # no dirs or hidden files
35 closedir DIR;
36
37 return map { cat_file($dir, $_) } @files;
38}
39
adfe19db 40sub parse_todo
41{
42 my $dir = shift || 'parts/todo';
43 local *TODO;
44 my %todo;
45 my $todo;
46
1d088ed8 47 for $todo (all_files_in_dir($dir)) {
adfe19db 48 open TODO, $todo or die "cannot open $todo: $!\n";
49 my $perl = <TODO>;
50 chomp $perl;
51 while (<TODO>) {
52 chomp;
53 s/#.*//;
54 s/^\s+//; s/\s+$//;
55 /^\s*$/ and next;
56 /^\w+$/ or die "invalid identifier: $_\n";
57 exists $todo{$_} and die "duplicate identifier: $_ ($todo{$_} <=> $perl)\n";
58 $todo{$_} = $perl;
59 }
60 close TODO;
61 }
62
63 return \%todo;
64}
65
96ad942f 66sub expand_version
67{
68 my($op, $ver) = @_;
69 my($r, $v, $s) = parse_version($ver);
70 $r == 5 or die "only Perl revision 5 is supported\n";
71 $op eq '==' and return "((PERL_VERSION == $v) && (PERL_SUBVERSION == $s))";
72 $op eq '!=' and return "((PERL_VERSION != $v) || (PERL_SUBVERSION != $s))";
73 $op =~ /([<>])/ and return "((PERL_VERSION $1 $v) || ((PERL_VERSION == $v) && (PERL_SUBVERSION $op $s)))";
74 die "cannot expand version expression ($op $ver)\n";
75}
76
adfe19db 77sub parse_partspec
78{
79 my $file = shift;
80 my $section = 'implementation';
81 my $vsec = join '|', qw( provides dontwarn implementation
82 xsubs xsinit xsmisc xshead xsboot tests );
83 my(%data, %options);
84 local *F;
85
86 open F, $file or die "$file: $!\n";
87 while (<F>) {
88 /^##/ and next;
89 if (/^=($vsec)(?:\s+(.*))?/) {
90 $section = $1;
91 if (defined $2) {
92 my $opt = $2;
93 $options{$section} = eval "{ $opt }";
94 $@ and die "Invalid options ($opt) in section $section of $file: $@\n";
95 }
96 next;
97 }
98 push @{$data{$section}}, $_;
99 }
100 close F;
101
102 for (keys %data) {
103 my @v = @{$data{$_}};
104 shift @v while @v && $v[0] =~ /^\s*$/;
105 pop @v while @v && $v[-1] =~ /^\s*$/;
106 $data{$_} = join '', @v;
107 }
108
109 unless (exists $data{provides}) {
679ad62d 110 $data{provides} = ($file =~ /(\w+)$/)[0];
adfe19db 111 }
112 $data{provides} = [$data{provides} =~ /(\S+)/g];
113
114 if (exists $data{dontwarn}) {
115 $data{dontwarn} = [$data{dontwarn} =~ /(\S+)/g];
116 }
117
118 my @prov;
119 my %proto;
120
121 if (exists $data{tests} && (!exists $data{implementation} || $data{implementation} !~ /\S/)) {
122 $data{implementation} = '';
123 }
124 else {
125 $data{implementation} =~ /\S/ or die "Empty implementation in $file\n";
126
127 my $p;
128
129 for $p (@{$data{provides}}) {
130 if ($p =~ m#^/.*/\w*$#) {
131 my @tmp = eval "\$data{implementation} =~ ${p}gm";
132 $@ and die "invalid regex $p in $file\n";
133 @tmp or warn "no matches for regex $p in $file\n";
134 push @prov, do { my %h; grep !$h{$_}++, @tmp };
135 }
136 elsif ($p eq '__UNDEFINED__') {
137 my @tmp = $data{implementation} =~ /^\s*__UNDEFINED__[^\r\n\S]+(\w+)/gm;
138 @tmp or warn "no __UNDEFINED__ macros in $file\n";
139 push @prov, @tmp;
140 }
141 else {
142 push @prov, $p;
143 }
144 }
145
146 for (@prov) {
147 if ($data{implementation} !~ /\b\Q$_\E\b/) {
148 warn "$file claims to provide $_, but doesn't seem to do so\n";
149 next;
150 }
151
152 # scan for prototypes
153 my($proto) = $data{implementation} =~ /
154 ( ^ (?:[\w*]|[^\S\r\n])+
155 [\r\n]*?
156 ^ \b$_\b \s*
157 \( [^{]* \)
158 )
159 \s* \{
160 /xm or next;
161
162 $proto =~ s/^\s+//;
163 $proto =~ s/\s+$//;
164 $proto =~ s/\s+/ /g;
165
166 exists $proto{$_} and warn "$file: duplicate prototype for $_\n";
167 $proto{$_} = $proto;
168 }
169 }
170
96ad942f 171 for $section (qw( implementation xsubs xsinit xsmisc xshead xsboot )) {
172 if (exists $data{$section}) {
173 $data{$section} =~ s/\{\s*version\s*(<|>|==|!=|>=|<=)\s*([\d._]+)\s*\}/expand_version($1, $2)/gei;
174 }
175 }
176
adfe19db 177 $data{provides} = \@prov;
178 $data{prototypes} = \%proto;
179 $data{OPTIONS} = \%options;
180
181 my %prov = map { ($_ => 1) } @prov;
182 my %dontwarn = exists $data{dontwarn} ? map { ($_ => 1) } @{$data{dontwarn}} : ();
183 my @maybeprov = do { my %h;
184 grep {
185 my($nop) = /^Perl_(.*)/;
186 not exists $prov{$_} ||
187 exists $dontwarn{$_} ||
188 (defined $nop && exists $prov{$nop} ) ||
189 (defined $nop && exists $dontwarn{$nop}) ||
190 $h{$_}++;
191 }
af36fda7 192 $data{implementation} =~ /^\s*#\s*define\s+(\w+)/gm };
adfe19db 193
194 if (@maybeprov) {
195 warn "$file seems to provide these macros, but doesn't list them:\n "
196 . join("\n ", @maybeprov) . "\n";
197 }
198
199 return \%data;
200}
201
202sub compare_prototypes
203{
204 my($p1, $p2) = @_;
205 for ($p1, $p2) {
206 s/^\s+//;
207 s/\s+$//;
208 s/\s+/ /g;
209 s/(\w)\s(\W)/$1$2/g;
210 s/(\W)\s(\w)/$1$2/g;
211 }
212 return $p1 cmp $p2;
213}
214
215sub ppcond
216{
217 my $s = shift;
218 my @c;
219 my $p;
220
221 for $p (@$s) {
222 push @c, map "!($_)", @{$p->{pre}};
223 defined $p->{cur} and push @c, "($p->{cur})";
224 }
225
226 join " && ", @c;
227}
228
229sub trim_arg
230{
231 my $in = shift;
4a582685 232 my $remove = join '|', qw( NN NULLOK );
adfe19db 233
234 $in eq '...' and return ($in);
235
236 local $_ = $in;
237 my $id;
4a582685 238
adfe19db 239 s/[*()]/ /g;
240 s/\[[^\]]*\]/ /g;
241 s/\b(?:auto|const|extern|inline|register|static|volatile|restrict)\b//g;
4a582685 242 s/\b(?:$remove)\b//;
adfe19db 243 s/^\s*//; s/\s*$//;
244
245 if( /^\b(?:struct|union|enum)\s+\w+(?:\s+(\w+))?$/ ) {
246 defined $1 and $id = $1;
247 }
248 else {
249 if( s/\b(?:char|double|float|int|long|short|signed|unsigned|void)\b//g ) {
250 /^\s*(\w+)\s*$/ and $id = $1;
251 }
252 else {
253 /^\s*\w+\s+(\w+)\s*$/ and $id = $1;
254 }
255 }
256
257 $_ = $in;
258
259 defined $id and s/\b$id\b//;
260
261 # these don't matter at all
262 s/\b(?:auto|extern|inline|register|static|volatile|restrict)\b//g;
4a582685 263 s/\b(?:$remove)\b//;
adfe19db 264
265 s/(?=<\*)\s+(?=\*)//g;
266 s/\s*(\*+)\s*/ $1 /g;
267 s/^\s*//; s/\s*$//;
268 s/\s+/ /g;
269
270 return ($_, $id);
271}
272
273sub parse_embed
274{
275 my @files = @_;
276 my @func;
277 my @pps;
278 my $file;
279 local *FILE;
280
281 for $file (@files) {
282 open FILE, $file or die "$file: $!\n";
283 my($line, $l);
284
285 while (defined($line = <FILE>)) {
286 while ($line =~ /\\$/ && defined($l = <FILE>)) {
287 $line =~ s/\\\s*//;
288 $line .= $l;
289 }
290 next if $line =~ /^\s*:/;
291 $line =~ s/^\s+|\s+$//gs;
292 my($dir, $args) = ($line =~ /^\s*#\s*(\w+)(?:\s*(.*?)\s*)?$/);
293 if (defined $dir and defined $args) {
294 for ($dir) {
295 /^ifdef$/ and do { push @pps, { pre => [], cur => "defined($args)" } ; last };
296 /^ifndef$/ and do { push @pps, { pre => [], cur => "!defined($args)" } ; last };
297 /^if$/ and do { push @pps, { pre => [], cur => $args } ; last };
298 /^elif$/ and do { push @{$pps[-1]{pre}}, $pps[-1]{cur}; $pps[-1]{cur} = $args; last };
299 /^else$/ and do { push @{$pps[-1]{pre}}, $pps[-1]{cur}; $pps[-1]{cur} = undef; last };
300 /^endif$/ and do { pop @pps ; last };
301 /^include$/ and last;
302 /^define$/ and last;
303 /^undef$/ and last;
304 warn "unhandled preprocessor directive: $dir\n";
305 }
306 }
307 else {
308 my @e = split /\s*\|\s*/, $line;
309 if( @e >= 3 ) {
310 my($flags, $ret, $name, @args) = @e;
311 for (@args) {
312 $_ = [trim_arg($_)];
313 }
314 ($ret) = trim_arg($ret);
315 push @func, {
316 name => $name,
317 flags => { map { $_, 1 } $flags =~ /./g },
318 ret => $ret,
319 args => \@args,
320 cond => ppcond(\@pps),
321 };
322 }
323 }
324 }
325
326 close FILE;
327 }
328
329 return @func;
330}
331
332sub make_prototype
333{
334 my $f = shift;
335 my @args = map { "@$_" } @{$f->{args}};
336 my $proto;
337 my $pTHX_ = exists $f->{flags}{n} ? "" : "pTHX_ ";
338 $proto = "$f->{ret} $f->{name}" . "($pTHX_" . join(', ', @args) . ')';
339 return $proto;
340}
341
342sub format_version
343{
344 my $ver = shift;
345
346 $ver =~ s/$/000000/;
347 my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
348
349 $v = int $v;
350 $s = int $s;
351
352 if ($r < 5 || ($r == 5 && $v < 6)) {
353 if ($s % 10) {
354 die "invalid version '$ver'\n";
355 }
356 $s /= 10;
357
358 $ver = sprintf "%d.%03d", $r, $v;
359 $s > 0 and $ver .= sprintf "_%02d", $s;
360
361 return $ver;
362 }
363
364 return sprintf "%d.%d.%d", $r, $v, $s;
365}
366
367sub parse_version
368{
369 my $ver = shift;
370
371 if ($ver =~ /^(\d+)\.(\d+)\.(\d+)$/) {
372 return ($1, $2, $3);
373 }
374 elsif ($ver !~ /^\d+\.[\d_]+$/) {
375 die "cannot parse version '$ver'\n";
376 }
377
378 $ver =~ s/_//g;
379 $ver =~ s/$/000000/;
380
381 my($r,$v,$s) = $ver =~ /(\d+)\.(\d{3})(\d{3})/;
382
383 $v = int $v;
384 $s = int $s;
385
386 if ($r < 5 || ($r == 5 && $v < 6)) {
387 if ($s % 10) {
388 die "cannot parse version '$ver'\n";
389 }
390 $s /= 10;
391 }
392
393 return ($r, $v, $s);
394}
395
3961;