xref: /freebsd/crypto/openssl/util/perl/OpenSSL/ParseC.pm (revision e0c4386e7e71d93b0edc0c8fa156263fc4a8b0b6)
1*e0c4386eSCy Schubert#! /usr/bin/env perl
2*e0c4386eSCy Schubert# Copyright 2018-2021 The OpenSSL Project Authors. All Rights Reserved.
3*e0c4386eSCy Schubert#
4*e0c4386eSCy Schubert# Licensed under the Apache License 2.0 (the "License").  You may not use
5*e0c4386eSCy Schubert# this file except in compliance with the License.  You can obtain a copy
6*e0c4386eSCy Schubert# in the file LICENSE in the source distribution or at
7*e0c4386eSCy Schubert# https://www.openssl.org/source/license.html
8*e0c4386eSCy Schubert
9*e0c4386eSCy Schubertpackage OpenSSL::ParseC;
10*e0c4386eSCy Schubert
11*e0c4386eSCy Schubertuse strict;
12*e0c4386eSCy Schubertuse warnings;
13*e0c4386eSCy Schubert
14*e0c4386eSCy Schubertuse Exporter;
15*e0c4386eSCy Schubertuse vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
16*e0c4386eSCy Schubert$VERSION = "0.9";
17*e0c4386eSCy Schubert@ISA = qw(Exporter);
18*e0c4386eSCy Schubert@EXPORT = qw(parse);
19*e0c4386eSCy Schubert
20*e0c4386eSCy Schubert# Global handler data
21*e0c4386eSCy Schubertmy @preprocessor_conds;         # A list of simple preprocessor conditions,
22*e0c4386eSCy Schubert                                # each item being a list of macros defined
23*e0c4386eSCy Schubert                                # or not defined.
24*e0c4386eSCy Schubert
25*e0c4386eSCy Schubert# Handler helpers
26*e0c4386eSCy Schubertsub all_conds {
27*e0c4386eSCy Schubert    return map { ( @$_ ) } @preprocessor_conds;
28*e0c4386eSCy Schubert}
29*e0c4386eSCy Schubert
30*e0c4386eSCy Schubert# A list of handlers that will look at a "complete" string and try to
31*e0c4386eSCy Schubert# figure out what to make of it.
32*e0c4386eSCy Schubert# Each handler is a hash with the following keys:
33*e0c4386eSCy Schubert#
34*e0c4386eSCy Schubert# regexp                a regexp to compare the "complete" string with.
35*e0c4386eSCy Schubert# checker               a function that does a more complex comparison.
36*e0c4386eSCy Schubert#                       Use this instead of regexp if that isn't enough.
37*e0c4386eSCy Schubert# massager              massages the "complete" string into an array with
38*e0c4386eSCy Schubert#                       the following elements:
39*e0c4386eSCy Schubert#
40*e0c4386eSCy Schubert#                       [0]     String that needs further processing (this
41*e0c4386eSCy Schubert#                               applies to typedefs of structs), or empty.
42*e0c4386eSCy Schubert#                       [1]     The name of what was found.
43*e0c4386eSCy Schubert#                       [2]     A character that denotes what type of thing
44*e0c4386eSCy Schubert#                               this is: 'F' for function, 'S' for struct,
45*e0c4386eSCy Schubert#                               'T' for typedef, 'M' for macro, 'V' for
46*e0c4386eSCy Schubert#                               variable.
47*e0c4386eSCy Schubert#                       [3]     Return type (only for type 'F' and 'V')
48*e0c4386eSCy Schubert#                       [4]     Value (for type 'M') or signature (for type 'F',
49*e0c4386eSCy Schubert#                               'V', 'T' or 'S')
50*e0c4386eSCy Schubert#                       [5...]  The list of preprocessor conditions this is
51*e0c4386eSCy Schubert#                               found in, as in checks for macro definitions
52*e0c4386eSCy Schubert#                               (stored as the macro's name) or the absence
53*e0c4386eSCy Schubert#                               of definition (stored as the macro's name
54*e0c4386eSCy Schubert#                               prefixed with a '!'
55*e0c4386eSCy Schubert#
56*e0c4386eSCy Schubert#                       If the massager returns an empty list, it means the
57*e0c4386eSCy Schubert#                       "complete" string has side effects but should otherwise
58*e0c4386eSCy Schubert#                       be ignored.
59*e0c4386eSCy Schubert#                       If the massager is undefined, the "complete" string
60*e0c4386eSCy Schubert#                       should be ignored.
61*e0c4386eSCy Schubertmy @opensslcpphandlers = (
62*e0c4386eSCy Schubert    ##################################################################
63*e0c4386eSCy Schubert    # OpenSSL CPP specials
64*e0c4386eSCy Schubert    #
65*e0c4386eSCy Schubert    # These are used to convert certain pre-precessor expressions into
66*e0c4386eSCy Schubert    # others that @cpphandlers have a better chance to understand.
67*e0c4386eSCy Schubert
68*e0c4386eSCy Schubert    # This changes any OPENSSL_NO_DEPRECATED_x_y[_z] check to a check of
69*e0c4386eSCy Schubert    # OPENSSL_NO_DEPRECATEDIN_x_y[_z].  That's due to <openssl/macros.h>
70*e0c4386eSCy Schubert    # creating OPENSSL_NO_DEPRECATED_x_y[_z], but the ordinals files using
71*e0c4386eSCy Schubert    # DEPRECATEDIN_x_y[_z].
72*e0c4386eSCy Schubert    { regexp   => qr/#if(def|ndef) OPENSSL_NO_DEPRECATED_(\d+_\d+(?:_\d+)?)$/,
73*e0c4386eSCy Schubert      massager => sub {
74*e0c4386eSCy Schubert          return (<<"EOF");
75*e0c4386eSCy Schubert#if$1 OPENSSL_NO_DEPRECATEDIN_$2
76*e0c4386eSCy SchubertEOF
77*e0c4386eSCy Schubert      }
78*e0c4386eSCy Schubert    }
79*e0c4386eSCy Schubert);
80*e0c4386eSCy Schubertmy @cpphandlers = (
81*e0c4386eSCy Schubert    ##################################################################
82*e0c4386eSCy Schubert    # CPP stuff
83*e0c4386eSCy Schubert
84*e0c4386eSCy Schubert    { regexp   => qr/#ifdef ?(.*)/,
85*e0c4386eSCy Schubert      massager => sub {
86*e0c4386eSCy Schubert          my %opts;
87*e0c4386eSCy Schubert          if (ref($_[$#_]) eq "HASH") {
88*e0c4386eSCy Schubert              %opts = %{$_[$#_]};
89*e0c4386eSCy Schubert              pop @_;
90*e0c4386eSCy Schubert          }
91*e0c4386eSCy Schubert          push @preprocessor_conds, [ $1 ];
92*e0c4386eSCy Schubert          print STDERR "DEBUG[",$opts{debug_type},"]: preprocessor level: ", scalar(@preprocessor_conds), "\n"
93*e0c4386eSCy Schubert              if $opts{debug};
94*e0c4386eSCy Schubert          return ();
95*e0c4386eSCy Schubert      },
96*e0c4386eSCy Schubert    },
97*e0c4386eSCy Schubert    { regexp   => qr/#ifndef ?(.*)/,
98*e0c4386eSCy Schubert      massager => sub {
99*e0c4386eSCy Schubert          my %opts;
100*e0c4386eSCy Schubert          if (ref($_[$#_]) eq "HASH") {
101*e0c4386eSCy Schubert              %opts = %{$_[$#_]};
102*e0c4386eSCy Schubert              pop @_;
103*e0c4386eSCy Schubert          }
104*e0c4386eSCy Schubert          push @preprocessor_conds, [ '!'.$1 ];
105*e0c4386eSCy Schubert          print STDERR "DEBUG[",$opts{debug_type},"]: preprocessor level: ", scalar(@preprocessor_conds), "\n"
106*e0c4386eSCy Schubert              if $opts{debug};
107*e0c4386eSCy Schubert          return ();
108*e0c4386eSCy Schubert      },
109*e0c4386eSCy Schubert    },
110*e0c4386eSCy Schubert    { regexp   => qr/#if (0|1)/,
111*e0c4386eSCy Schubert      massager => sub {
112*e0c4386eSCy Schubert          my %opts;
113*e0c4386eSCy Schubert          if (ref($_[$#_]) eq "HASH") {
114*e0c4386eSCy Schubert              %opts = %{$_[$#_]};
115*e0c4386eSCy Schubert              pop @_;
116*e0c4386eSCy Schubert          }
117*e0c4386eSCy Schubert          if ($1 eq "1") {
118*e0c4386eSCy Schubert              push @preprocessor_conds, [ "TRUE" ];
119*e0c4386eSCy Schubert          } else {
120*e0c4386eSCy Schubert              push @preprocessor_conds, [ "!TRUE" ];
121*e0c4386eSCy Schubert          }
122*e0c4386eSCy Schubert          print STDERR "DEBUG[",$opts{debug_type},"]: preprocessor level: ", scalar(@preprocessor_conds), "\n"
123*e0c4386eSCy Schubert              if $opts{debug};
124*e0c4386eSCy Schubert          return ();
125*e0c4386eSCy Schubert      },
126*e0c4386eSCy Schubert    },
127*e0c4386eSCy Schubert    { regexp   => qr/#if ?(.*)/,
128*e0c4386eSCy Schubert      massager => sub {
129*e0c4386eSCy Schubert          my %opts;
130*e0c4386eSCy Schubert          if (ref($_[$#_]) eq "HASH") {
131*e0c4386eSCy Schubert              %opts = %{$_[$#_]};
132*e0c4386eSCy Schubert              pop @_;
133*e0c4386eSCy Schubert          }
134*e0c4386eSCy Schubert          my @results = ();
135*e0c4386eSCy Schubert          my $conds = $1;
136*e0c4386eSCy Schubert          if ($conds =~ m|^defined<<<\(([^\)]*)\)>>>(.*)$|) {
137*e0c4386eSCy Schubert              push @results, $1; # Handle the simple case
138*e0c4386eSCy Schubert              my $rest = $2;
139*e0c4386eSCy Schubert              my $re = qr/^(?:\|\|defined<<<\([^\)]*\)>>>)*$/;
140*e0c4386eSCy Schubert              print STDERR "DEBUG[",$opts{debug_type},"]: Matching '$rest' with '$re'\n"
141*e0c4386eSCy Schubert                  if $opts{debug};
142*e0c4386eSCy Schubert              if ($rest =~ m/$re/) {
143*e0c4386eSCy Schubert                  my @rest = split /\|\|/, $rest;
144*e0c4386eSCy Schubert                  shift @rest;
145*e0c4386eSCy Schubert                  foreach (@rest) {
146*e0c4386eSCy Schubert                      m|^defined<<<\(([^\)]*)\)>>>$|;
147*e0c4386eSCy Schubert                      die "Something wrong...$opts{PLACE}" if $1 eq "";
148*e0c4386eSCy Schubert                      push @results, $1;
149*e0c4386eSCy Schubert                  }
150*e0c4386eSCy Schubert              } else {
151*e0c4386eSCy Schubert                  $conds =~ s/<<<|>>>//g;
152*e0c4386eSCy Schubert                  warn "Warning: complicated #if expression(1): $conds$opts{PLACE}"
153*e0c4386eSCy Schubert                      if $opts{warnings};
154*e0c4386eSCy Schubert              }
155*e0c4386eSCy Schubert          } elsif ($conds =~ m|^!defined<<<\(([^\)]*)\)>>>(.*)$|) {
156*e0c4386eSCy Schubert              push @results, '!'.$1; # Handle the simple case
157*e0c4386eSCy Schubert              my $rest = $2;
158*e0c4386eSCy Schubert              my $re = qr/^(?:\&\&!defined<<<\([^\)]*\)>>>)*$/;
159*e0c4386eSCy Schubert              print STDERR "DEBUG[",$opts{debug_type},"]: Matching '$rest' with '$re'\n"
160*e0c4386eSCy Schubert                  if $opts{debug};
161*e0c4386eSCy Schubert              if ($rest =~ m/$re/) {
162*e0c4386eSCy Schubert                  my @rest = split /\&\&/, $rest;
163*e0c4386eSCy Schubert                  shift @rest;
164*e0c4386eSCy Schubert                  foreach (@rest) {
165*e0c4386eSCy Schubert                      m|^!defined<<<\(([^\)]*)\)>>>$|;
166*e0c4386eSCy Schubert                      die "Something wrong...$opts{PLACE}" if $1 eq "";
167*e0c4386eSCy Schubert                      push @results, '!'.$1;
168*e0c4386eSCy Schubert                  }
169*e0c4386eSCy Schubert              } else {
170*e0c4386eSCy Schubert                  $conds =~ s/<<<|>>>//g;
171*e0c4386eSCy Schubert                  warn "Warning: complicated #if expression(2): $conds$opts{PLACE}"
172*e0c4386eSCy Schubert                      if $opts{warnings};
173*e0c4386eSCy Schubert              }
174*e0c4386eSCy Schubert          } else {
175*e0c4386eSCy Schubert              $conds =~ s/<<<|>>>//g;
176*e0c4386eSCy Schubert              warn "Warning: complicated #if expression(3): $conds$opts{PLACE}"
177*e0c4386eSCy Schubert                  if $opts{warnings};
178*e0c4386eSCy Schubert          }
179*e0c4386eSCy Schubert          print STDERR "DEBUG[",$opts{debug_type},"]: Added preprocessor conds: '", join("', '", @results), "'\n"
180*e0c4386eSCy Schubert              if $opts{debug};
181*e0c4386eSCy Schubert          push @preprocessor_conds, [ @results ];
182*e0c4386eSCy Schubert          print STDERR "DEBUG[",$opts{debug_type},"]: preprocessor level: ", scalar(@preprocessor_conds), "\n"
183*e0c4386eSCy Schubert              if $opts{debug};
184*e0c4386eSCy Schubert          return ();
185*e0c4386eSCy Schubert      },
186*e0c4386eSCy Schubert    },
187*e0c4386eSCy Schubert    { regexp   => qr/#elif (.*)/,
188*e0c4386eSCy Schubert      massager => sub {
189*e0c4386eSCy Schubert          my %opts;
190*e0c4386eSCy Schubert          if (ref($_[$#_]) eq "HASH") {
191*e0c4386eSCy Schubert              %opts = %{$_[$#_]};
192*e0c4386eSCy Schubert              pop @_;
193*e0c4386eSCy Schubert          }
194*e0c4386eSCy Schubert          die "An #elif without corresponding condition$opts{PLACE}"
195*e0c4386eSCy Schubert              if !@preprocessor_conds;
196*e0c4386eSCy Schubert          pop @preprocessor_conds;
197*e0c4386eSCy Schubert          print STDERR "DEBUG[",$opts{debug_type},"]: preprocessor level: ", scalar(@preprocessor_conds), "\n"
198*e0c4386eSCy Schubert              if $opts{debug};
199*e0c4386eSCy Schubert          return (<<"EOF");
200*e0c4386eSCy Schubert#if $1
201*e0c4386eSCy SchubertEOF
202*e0c4386eSCy Schubert      },
203*e0c4386eSCy Schubert    },
204*e0c4386eSCy Schubert    { regexp   => qr/#else/,
205*e0c4386eSCy Schubert      massager => sub {
206*e0c4386eSCy Schubert          my %opts;
207*e0c4386eSCy Schubert          if (ref($_[$#_]) eq "HASH") {
208*e0c4386eSCy Schubert              %opts = %{$_[$#_]};
209*e0c4386eSCy Schubert              pop @_;
210*e0c4386eSCy Schubert          }
211*e0c4386eSCy Schubert          die "An #else without corresponding condition$opts{PLACE}"
212*e0c4386eSCy Schubert              if !@preprocessor_conds;
213*e0c4386eSCy Schubert          # Invert all conditions on the last level
214*e0c4386eSCy Schubert          my $stuff = pop @preprocessor_conds;
215*e0c4386eSCy Schubert          push @preprocessor_conds, [
216*e0c4386eSCy Schubert              map { m|^!(.*)$| ? $1 : '!'.$_ } @$stuff
217*e0c4386eSCy Schubert          ];
218*e0c4386eSCy Schubert          print STDERR "DEBUG[",$opts{debug_type},"]: preprocessor level: ", scalar(@preprocessor_conds), "\n"
219*e0c4386eSCy Schubert              if $opts{debug};
220*e0c4386eSCy Schubert          return ();
221*e0c4386eSCy Schubert      },
222*e0c4386eSCy Schubert    },
223*e0c4386eSCy Schubert    { regexp   => qr/#endif ?/,
224*e0c4386eSCy Schubert      massager => sub {
225*e0c4386eSCy Schubert          my %opts;
226*e0c4386eSCy Schubert          if (ref($_[$#_]) eq "HASH") {
227*e0c4386eSCy Schubert              %opts = %{$_[$#_]};
228*e0c4386eSCy Schubert              pop @_;
229*e0c4386eSCy Schubert          }
230*e0c4386eSCy Schubert          die "An #endif without corresponding condition$opts{PLACE}"
231*e0c4386eSCy Schubert              if !@preprocessor_conds;
232*e0c4386eSCy Schubert          pop @preprocessor_conds;
233*e0c4386eSCy Schubert          print STDERR "DEBUG[",$opts{debug_type},"]: preprocessor level: ", scalar(@preprocessor_conds), "\n"
234*e0c4386eSCy Schubert              if $opts{debug};
235*e0c4386eSCy Schubert          return ();
236*e0c4386eSCy Schubert      },
237*e0c4386eSCy Schubert    },
238*e0c4386eSCy Schubert    { regexp   => qr/#define ([[:alpha:]_]\w*)(<<<\(.*?\)>>>)?( (.*))?/,
239*e0c4386eSCy Schubert      massager => sub {
240*e0c4386eSCy Schubert          my $name = $1;
241*e0c4386eSCy Schubert          my $params = $2;
242*e0c4386eSCy Schubert          my $spaceval = $3||"";
243*e0c4386eSCy Schubert          my $val = $4||"";
244*e0c4386eSCy Schubert          return ("",
245*e0c4386eSCy Schubert                  $1, 'M', "", $params ? "$name$params$spaceval" : $val,
246*e0c4386eSCy Schubert                  all_conds()); }
247*e0c4386eSCy Schubert    },
248*e0c4386eSCy Schubert    { regexp   => qr/#.*/,
249*e0c4386eSCy Schubert      massager => sub { return (); }
250*e0c4386eSCy Schubert    },
251*e0c4386eSCy Schubert    );
252*e0c4386eSCy Schubert
253*e0c4386eSCy Schubertmy @opensslchandlers = (
254*e0c4386eSCy Schubert    ##################################################################
255*e0c4386eSCy Schubert    # OpenSSL C specials
256*e0c4386eSCy Schubert    #
257*e0c4386eSCy Schubert    # They are really preprocessor stuff, but they look like C stuff
258*e0c4386eSCy Schubert    # to this parser.  All of these do replacements, anything else is
259*e0c4386eSCy Schubert    # an error.
260*e0c4386eSCy Schubert
261*e0c4386eSCy Schubert    #####
262*e0c4386eSCy Schubert    # Deprecated stuff, by OpenSSL release.
263*e0c4386eSCy Schubert
264*e0c4386eSCy Schubert    # OSSL_DEPRECATEDIN_x_y[_z] is simply ignored.  Such declarations are
265*e0c4386eSCy Schubert    # supposed to be guarded with an '#ifdef OPENSSL_NO_DEPRECATED_x_y[_z]'
266*e0c4386eSCy Schubert    { regexp   => qr/OSSL_DEPRECATEDIN_\d+_\d+(?:_\d+)?\s+(.*)/,
267*e0c4386eSCy Schubert      massager => sub { return $1; },
268*e0c4386eSCy Schubert    },
269*e0c4386eSCy Schubert    { regexp   => qr/(.*?)\s+OSSL_DEPRECATEDIN_\d+_\d+(?:_\d+)?\s+(.*)/,
270*e0c4386eSCy Schubert      massager => sub { return "$1 $2"; },
271*e0c4386eSCy Schubert    },
272*e0c4386eSCy Schubert
273*e0c4386eSCy Schubert    #####
274*e0c4386eSCy Schubert    # Core stuff
275*e0c4386eSCy Schubert
276*e0c4386eSCy Schubert    # OSSL_CORE_MAKE_FUNC is a macro to create the necessary data and inline
277*e0c4386eSCy Schubert    # function the libcrypto<->provider interface
278*e0c4386eSCy Schubert    { regexp   => qr/OSSL_CORE_MAKE_FUNC<<<\((.*?),(.*?),(.*?)\)>>>/,
279*e0c4386eSCy Schubert      massager => sub {
280*e0c4386eSCy Schubert          return (<<"EOF");
281*e0c4386eSCy Schuberttypedef $1 OSSL_FUNC_$2_fn$3;
282*e0c4386eSCy Schubertstatic ossl_inline OSSL_FUNC_$2_fn *OSSL_FUNC_$2(const OSSL_DISPATCH *opf);
283*e0c4386eSCy SchubertEOF
284*e0c4386eSCy Schubert      },
285*e0c4386eSCy Schubert    },
286*e0c4386eSCy Schubert
287*e0c4386eSCy Schubert    #####
288*e0c4386eSCy Schubert    # LHASH stuff
289*e0c4386eSCy Schubert
290*e0c4386eSCy Schubert    # LHASH_OF(foo) is used as a type, but the chandlers won't take it
291*e0c4386eSCy Schubert    # gracefully, so we expand it here.
292*e0c4386eSCy Schubert    { regexp   => qr/(.*)\bLHASH_OF<<<\((.*?)\)>>>(.*)/,
293*e0c4386eSCy Schubert      massager => sub { return ("$1struct lhash_st_$2$3"); }
294*e0c4386eSCy Schubert    },
295*e0c4386eSCy Schubert    { regexp   => qr/DEFINE_LHASH_OF(?:_INTERNAL)?<<<\((.*)\)>>>/,
296*e0c4386eSCy Schubert      massager => sub {
297*e0c4386eSCy Schubert          return (<<"EOF");
298*e0c4386eSCy Schubertstatic ossl_inline LHASH_OF($1) * lh_$1_new(unsigned long (*hfn)(const $1 *),
299*e0c4386eSCy Schubert                                            int (*cfn)(const $1 *, const $1 *));
300*e0c4386eSCy Schubertstatic ossl_inline void lh_$1_free(LHASH_OF($1) *lh);
301*e0c4386eSCy Schubertstatic ossl_inline $1 *lh_$1_insert(LHASH_OF($1) *lh, $1 *d);
302*e0c4386eSCy Schubertstatic ossl_inline $1 *lh_$1_delete(LHASH_OF($1) *lh, const $1 *d);
303*e0c4386eSCy Schubertstatic ossl_inline $1 *lh_$1_retrieve(LHASH_OF($1) *lh, const $1 *d);
304*e0c4386eSCy Schubertstatic ossl_inline int lh_$1_error(LHASH_OF($1) *lh);
305*e0c4386eSCy Schubertstatic ossl_inline unsigned long lh_$1_num_items(LHASH_OF($1) *lh);
306*e0c4386eSCy Schubertstatic ossl_inline void lh_$1_node_stats_bio(const LHASH_OF($1) *lh, BIO *out);
307*e0c4386eSCy Schubertstatic ossl_inline void lh_$1_node_usage_stats_bio(const LHASH_OF($1) *lh,
308*e0c4386eSCy Schubert                                                   BIO *out);
309*e0c4386eSCy Schubertstatic ossl_inline void lh_$1_stats_bio(const LHASH_OF($1) *lh, BIO *out);
310*e0c4386eSCy Schubertstatic ossl_inline unsigned long lh_$1_get_down_load(LHASH_OF($1) *lh);
311*e0c4386eSCy Schubertstatic ossl_inline void lh_$1_set_down_load(LHASH_OF($1) *lh, unsigned long dl);
312*e0c4386eSCy Schubertstatic ossl_inline void lh_$1_doall(LHASH_OF($1) *lh, void (*doall)($1 *));
313*e0c4386eSCy SchubertLHASH_OF($1)
314*e0c4386eSCy SchubertEOF
315*e0c4386eSCy Schubert      }
316*e0c4386eSCy Schubert     },
317*e0c4386eSCy Schubert
318*e0c4386eSCy Schubert    #####
319*e0c4386eSCy Schubert    # STACK stuff
320*e0c4386eSCy Schubert
321*e0c4386eSCy Schubert    # STACK_OF(foo) is used as a type, but the chandlers won't take it
322*e0c4386eSCy Schubert    # gracefully, so we expand it here.
323*e0c4386eSCy Schubert    { regexp   => qr/(.*)\bSTACK_OF<<<\((.*?)\)>>>(.*)/,
324*e0c4386eSCy Schubert      massager => sub { return ("$1struct stack_st_$2$3"); }
325*e0c4386eSCy Schubert    },
326*e0c4386eSCy Schubert#    { regexp   => qr/(.*)\bSTACK_OF\((.*?)\)(.*)/,
327*e0c4386eSCy Schubert#      massager => sub {
328*e0c4386eSCy Schubert#          my $before = $1;
329*e0c4386eSCy Schubert#          my $stack_of = "struct stack_st_$2";
330*e0c4386eSCy Schubert#          my $after = $3;
331*e0c4386eSCy Schubert#          if ($after =~ m|^\w|) { $after = " ".$after; }
332*e0c4386eSCy Schubert#          return ("$before$stack_of$after");
333*e0c4386eSCy Schubert#      }
334*e0c4386eSCy Schubert#    },
335*e0c4386eSCy Schubert    { regexp   => qr/SKM_DEFINE_STACK_OF<<<\((.*),\s*(.*),\s*(.*)\)>>>/,
336*e0c4386eSCy Schubert      massager => sub {
337*e0c4386eSCy Schubert          return (<<"EOF");
338*e0c4386eSCy SchubertSTACK_OF($1);
339*e0c4386eSCy Schuberttypedef int (*sk_$1_compfunc)(const $3 * const *a, const $3 *const *b);
340*e0c4386eSCy Schuberttypedef void (*sk_$1_freefunc)($3 *a);
341*e0c4386eSCy Schuberttypedef $3 * (*sk_$1_copyfunc)(const $3 *a);
342*e0c4386eSCy Schubertstatic ossl_inline int sk_$1_num(const STACK_OF($1) *sk);
343*e0c4386eSCy Schubertstatic ossl_inline $2 *sk_$1_value(const STACK_OF($1) *sk, int idx);
344*e0c4386eSCy Schubertstatic ossl_inline STACK_OF($1) *sk_$1_new(sk_$1_compfunc compare);
345*e0c4386eSCy Schubertstatic ossl_inline STACK_OF($1) *sk_$1_new_null(void);
346*e0c4386eSCy Schubertstatic ossl_inline STACK_OF($1) *sk_$1_new_reserve(sk_$1_compfunc compare,
347*e0c4386eSCy Schubert                                                   int n);
348*e0c4386eSCy Schubertstatic ossl_inline int sk_$1_reserve(STACK_OF($1) *sk, int n);
349*e0c4386eSCy Schubertstatic ossl_inline void sk_$1_free(STACK_OF($1) *sk);
350*e0c4386eSCy Schubertstatic ossl_inline void sk_$1_zero(STACK_OF($1) *sk);
351*e0c4386eSCy Schubertstatic ossl_inline $2 *sk_$1_delete(STACK_OF($1) *sk, int i);
352*e0c4386eSCy Schubertstatic ossl_inline $2 *sk_$1_delete_ptr(STACK_OF($1) *sk, $2 *ptr);
353*e0c4386eSCy Schubertstatic ossl_inline int sk_$1_push(STACK_OF($1) *sk, $2 *ptr);
354*e0c4386eSCy Schubertstatic ossl_inline int sk_$1_unshift(STACK_OF($1) *sk, $2 *ptr);
355*e0c4386eSCy Schubertstatic ossl_inline $2 *sk_$1_pop(STACK_OF($1) *sk);
356*e0c4386eSCy Schubertstatic ossl_inline $2 *sk_$1_shift(STACK_OF($1) *sk);
357*e0c4386eSCy Schubertstatic ossl_inline void sk_$1_pop_free(STACK_OF($1) *sk,
358*e0c4386eSCy Schubert                                       sk_$1_freefunc freefunc);
359*e0c4386eSCy Schubertstatic ossl_inline int sk_$1_insert(STACK_OF($1) *sk, $2 *ptr, int idx);
360*e0c4386eSCy Schubertstatic ossl_inline $2 *sk_$1_set(STACK_OF($1) *sk, int idx, $2 *ptr);
361*e0c4386eSCy Schubertstatic ossl_inline int sk_$1_find(STACK_OF($1) *sk, $2 *ptr);
362*e0c4386eSCy Schubertstatic ossl_inline int sk_$1_find_ex(STACK_OF($1) *sk, $2 *ptr);
363*e0c4386eSCy Schubertstatic ossl_inline void sk_$1_sort(STACK_OF($1) *sk);
364*e0c4386eSCy Schubertstatic ossl_inline int sk_$1_is_sorted(const STACK_OF($1) *sk);
365*e0c4386eSCy Schubertstatic ossl_inline STACK_OF($1) * sk_$1_dup(const STACK_OF($1) *sk);
366*e0c4386eSCy Schubertstatic ossl_inline STACK_OF($1) *sk_$1_deep_copy(const STACK_OF($1) *sk,
367*e0c4386eSCy Schubert                                                 sk_$1_copyfunc copyfunc,
368*e0c4386eSCy Schubert                                                 sk_$1_freefunc freefunc);
369*e0c4386eSCy Schubertstatic ossl_inline sk_$1_compfunc sk_$1_set_cmp_func(STACK_OF($1) *sk,
370*e0c4386eSCy Schubert                                                     sk_$1_compfunc compare);
371*e0c4386eSCy SchubertEOF
372*e0c4386eSCy Schubert      }
373*e0c4386eSCy Schubert    },
374*e0c4386eSCy Schubert    { regexp   => qr/SKM_DEFINE_STACK_OF_INTERNAL<<<\((.*),\s*(.*),\s*(.*)\)>>>/,
375*e0c4386eSCy Schubert      massager => sub {
376*e0c4386eSCy Schubert          return (<<"EOF");
377*e0c4386eSCy SchubertSTACK_OF($1);
378*e0c4386eSCy Schuberttypedef int (*sk_$1_compfunc)(const $3 * const *a, const $3 *const *b);
379*e0c4386eSCy Schuberttypedef void (*sk_$1_freefunc)($3 *a);
380*e0c4386eSCy Schuberttypedef $3 * (*sk_$1_copyfunc)(const $3 *a);
381*e0c4386eSCy Schubertstatic ossl_unused ossl_inline $2 *ossl_check_$1_type($2 *ptr);
382*e0c4386eSCy Schubertstatic ossl_unused ossl_inline const OPENSSL_STACK *ossl_check_const_$1_sk_type(const STACK_OF($1) *sk);
383*e0c4386eSCy Schubertstatic ossl_unused ossl_inline OPENSSL_sk_compfunc ossl_check_$1_compfunc_type(sk_$1_compfunc cmp);
384*e0c4386eSCy Schubertstatic ossl_unused ossl_inline OPENSSL_sk_copyfunc ossl_check_$1_copyfunc_type(sk_$1_copyfunc cpy);
385*e0c4386eSCy Schubertstatic ossl_unused ossl_inline OPENSSL_sk_freefunc ossl_check_$1_freefunc_type(sk_$1_freefunc fr);
386*e0c4386eSCy SchubertEOF
387*e0c4386eSCy Schubert      }
388*e0c4386eSCy Schubert    },
389*e0c4386eSCy Schubert    { regexp   => qr/DEFINE_SPECIAL_STACK_OF<<<\((.*),\s*(.*)\)>>>/,
390*e0c4386eSCy Schubert      massager => sub { return ("SKM_DEFINE_STACK_OF($1,$2,$2)"); },
391*e0c4386eSCy Schubert    },
392*e0c4386eSCy Schubert    { regexp   => qr/DEFINE_STACK_OF<<<\((.*)\)>>>/,
393*e0c4386eSCy Schubert      massager => sub { return ("SKM_DEFINE_STACK_OF($1,$1,$1)"); },
394*e0c4386eSCy Schubert    },
395*e0c4386eSCy Schubert    { regexp   => qr/DEFINE_SPECIAL_STACK_OF_CONST<<<\((.*),\s*(.*)\)>>>/,
396*e0c4386eSCy Schubert      massager => sub { return ("SKM_DEFINE_STACK_OF($1,const $2,$2)"); },
397*e0c4386eSCy Schubert    },
398*e0c4386eSCy Schubert    { regexp   => qr/DEFINE_STACK_OF_CONST<<<\((.*)\)>>>/,
399*e0c4386eSCy Schubert      massager => sub { return ("SKM_DEFINE_STACK_OF($1,const $1,$1)"); },
400*e0c4386eSCy Schubert    },
401*e0c4386eSCy Schubert
402*e0c4386eSCy Schubert    #####
403*e0c4386eSCy Schubert    # ASN1 stuff
404*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_ITEM<<<\((.*)\)>>>/,
405*e0c4386eSCy Schubert      massager => sub {
406*e0c4386eSCy Schubert          return (<<"EOF");
407*e0c4386eSCy Schubertconst ASN1_ITEM *$1_it(void);
408*e0c4386eSCy SchubertEOF
409*e0c4386eSCy Schubert      },
410*e0c4386eSCy Schubert    },
411*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_ENCODE_FUNCTIONS_only<<<\((.*),\s*(.*)\)>>>/,
412*e0c4386eSCy Schubert      massager => sub {
413*e0c4386eSCy Schubert          return (<<"EOF");
414*e0c4386eSCy Schubertint d2i_$2(void);
415*e0c4386eSCy Schubertint i2d_$2(void);
416*e0c4386eSCy SchubertEOF
417*e0c4386eSCy Schubert      },
418*e0c4386eSCy Schubert    },
419*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_ENCODE_FUNCTIONS<<<\((.*),\s*(.*),\s*(.*)\)>>>/,
420*e0c4386eSCy Schubert      massager => sub {
421*e0c4386eSCy Schubert          return (<<"EOF");
422*e0c4386eSCy Schubertint d2i_$3(void);
423*e0c4386eSCy Schubertint i2d_$3(void);
424*e0c4386eSCy SchubertDECLARE_ASN1_ITEM($2)
425*e0c4386eSCy SchubertEOF
426*e0c4386eSCy Schubert      },
427*e0c4386eSCy Schubert    },
428*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_ENCODE_FUNCTIONS_name<<<\((.*),\s*(.*)\)>>>/,
429*e0c4386eSCy Schubert      massager => sub {
430*e0c4386eSCy Schubert          return (<<"EOF");
431*e0c4386eSCy Schubertint d2i_$2(void);
432*e0c4386eSCy Schubertint i2d_$2(void);
433*e0c4386eSCy SchubertDECLARE_ASN1_ITEM($2)
434*e0c4386eSCy SchubertEOF
435*e0c4386eSCy Schubert      },
436*e0c4386eSCy Schubert    },
437*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_ALLOC_FUNCTIONS_name<<<\((.*),\s*(.*)\)>>>/,
438*e0c4386eSCy Schubert      massager => sub {
439*e0c4386eSCy Schubert          return (<<"EOF");
440*e0c4386eSCy Schubertint $2_free(void);
441*e0c4386eSCy Schubertint $2_new(void);
442*e0c4386eSCy SchubertEOF
443*e0c4386eSCy Schubert      },
444*e0c4386eSCy Schubert    },
445*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_ALLOC_FUNCTIONS<<<\((.*)\)>>>/,
446*e0c4386eSCy Schubert      massager => sub {
447*e0c4386eSCy Schubert          return (<<"EOF");
448*e0c4386eSCy Schubertint $1_free(void);
449*e0c4386eSCy Schubertint $1_new(void);
450*e0c4386eSCy SchubertEOF
451*e0c4386eSCy Schubert      },
452*e0c4386eSCy Schubert    },
453*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_FUNCTIONS_name<<<\((.*),\s*(.*)\)>>>/,
454*e0c4386eSCy Schubert      massager => sub {
455*e0c4386eSCy Schubert          return (<<"EOF");
456*e0c4386eSCy Schubertint d2i_$2(void);
457*e0c4386eSCy Schubertint i2d_$2(void);
458*e0c4386eSCy Schubertint $2_free(void);
459*e0c4386eSCy Schubertint $2_new(void);
460*e0c4386eSCy SchubertDECLARE_ASN1_ITEM($2)
461*e0c4386eSCy SchubertEOF
462*e0c4386eSCy Schubert      },
463*e0c4386eSCy Schubert    },
464*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_FUNCTIONS<<<\((.*)\)>>>/,
465*e0c4386eSCy Schubert      massager => sub { return (<<"EOF");
466*e0c4386eSCy Schubertint d2i_$1(void);
467*e0c4386eSCy Schubertint i2d_$1(void);
468*e0c4386eSCy Schubertint $1_free(void);
469*e0c4386eSCy Schubertint $1_new(void);
470*e0c4386eSCy SchubertDECLARE_ASN1_ITEM($1)
471*e0c4386eSCy SchubertEOF
472*e0c4386eSCy Schubert      }
473*e0c4386eSCy Schubert    },
474*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_NDEF_FUNCTION<<<\((.*)\)>>>/,
475*e0c4386eSCy Schubert      massager => sub {
476*e0c4386eSCy Schubert          return (<<"EOF");
477*e0c4386eSCy Schubertint i2d_$1_NDEF(void);
478*e0c4386eSCy SchubertEOF
479*e0c4386eSCy Schubert      }
480*e0c4386eSCy Schubert    },
481*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_PRINT_FUNCTION<<<\((.*)\)>>>/,
482*e0c4386eSCy Schubert      massager => sub {
483*e0c4386eSCy Schubert          return (<<"EOF");
484*e0c4386eSCy Schubertint $1_print_ctx(void);
485*e0c4386eSCy SchubertEOF
486*e0c4386eSCy Schubert      }
487*e0c4386eSCy Schubert    },
488*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_PRINT_FUNCTION_name<<<\((.*),\s*(.*)\)>>>/,
489*e0c4386eSCy Schubert      massager => sub {
490*e0c4386eSCy Schubert          return (<<"EOF");
491*e0c4386eSCy Schubertint $2_print_ctx(void);
492*e0c4386eSCy SchubertEOF
493*e0c4386eSCy Schubert      }
494*e0c4386eSCy Schubert    },
495*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_SET_OF<<<\((.*)\)>>>/,
496*e0c4386eSCy Schubert      massager => sub { return (); }
497*e0c4386eSCy Schubert    },
498*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_DUP_FUNCTION<<<\((.*)\)>>>/,
499*e0c4386eSCy Schubert      massager => sub {
500*e0c4386eSCy Schubert          return (<<"EOF");
501*e0c4386eSCy Schubertint $1_dup(void);
502*e0c4386eSCy SchubertEOF
503*e0c4386eSCy Schubert      }
504*e0c4386eSCy Schubert    },
505*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_ASN1_DUP_FUNCTION_name<<<\((.*),\s*(.*)\)>>>/,
506*e0c4386eSCy Schubert      massager => sub {
507*e0c4386eSCy Schubert          return (<<"EOF");
508*e0c4386eSCy Schubertint $2_dup(void);
509*e0c4386eSCy SchubertEOF
510*e0c4386eSCy Schubert      }
511*e0c4386eSCy Schubert    },
512*e0c4386eSCy Schubert    # Universal translator of attributed PEM declarators
513*e0c4386eSCy Schubert    { regexp   => qr/
514*e0c4386eSCy Schubert          DECLARE_ASN1
515*e0c4386eSCy Schubert          (_ENCODE_FUNCTIONS_only|_ENCODE_FUNCTIONS|_ENCODE_FUNCTIONS_name
516*e0c4386eSCy Schubert           |_ALLOC_FUNCTIONS_name|_ALLOC_FUNCTIONS|_FUNCTIONS_name|_FUNCTIONS
517*e0c4386eSCy Schubert           |_NDEF_FUNCTION|_PRINT_FUNCTION|_PRINT_FUNCTION_name
518*e0c4386eSCy Schubert           |_DUP_FUNCTION|_DUP_FUNCTION_name)
519*e0c4386eSCy Schubert          _attr
520*e0c4386eSCy Schubert          <<<\(\s*OSSL_DEPRECATEDIN_(.*?)\s*,(.*?)\)>>>
521*e0c4386eSCy Schubert      /x,
522*e0c4386eSCy Schubert      massager => sub { return (<<"EOF");
523*e0c4386eSCy SchubertDECLARE_ASN1$1($3)
524*e0c4386eSCy SchubertEOF
525*e0c4386eSCy Schubert      },
526*e0c4386eSCy Schubert    },
527*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_PKCS12_SET_OF<<<\((.*)\)>>>/,
528*e0c4386eSCy Schubert      massager => sub { return (); }
529*e0c4386eSCy Schubert    },
530*e0c4386eSCy Schubert
531*e0c4386eSCy Schubert    #####
532*e0c4386eSCy Schubert    # PEM stuff
533*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_PEM(?|_rw|_rw_cb|_rw_const)<<<\((.*?),.*\)>>>/,
534*e0c4386eSCy Schubert      massager => sub { return (<<"EOF");
535*e0c4386eSCy Schubert#ifndef OPENSSL_NO_STDIO
536*e0c4386eSCy Schubertint PEM_read_$1(void);
537*e0c4386eSCy Schubertint PEM_write_$1(void);
538*e0c4386eSCy Schubert#endif
539*e0c4386eSCy Schubertint PEM_read_bio_$1(void);
540*e0c4386eSCy Schubertint PEM_write_bio_$1(void);
541*e0c4386eSCy SchubertEOF
542*e0c4386eSCy Schubert      },
543*e0c4386eSCy Schubert    },
544*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_PEM(?|_rw|_rw_cb|_rw_const)_ex<<<\((.*?),.*\)>>>/,
545*e0c4386eSCy Schubert      massager => sub { return (<<"EOF");
546*e0c4386eSCy Schubert#ifndef OPENSSL_NO_STDIO
547*e0c4386eSCy Schubertint PEM_read_$1(void);
548*e0c4386eSCy Schubertint PEM_write_$1(void);
549*e0c4386eSCy Schubertint PEM_read_$1_ex(void);
550*e0c4386eSCy Schubertint PEM_write_$1_ex(void);
551*e0c4386eSCy Schubert#endif
552*e0c4386eSCy Schubertint PEM_read_bio_$1(void);
553*e0c4386eSCy Schubertint PEM_write_bio_$1(void);
554*e0c4386eSCy Schubertint PEM_read_bio_$1_ex(void);
555*e0c4386eSCy Schubertint PEM_write_bio_$1_ex(void);
556*e0c4386eSCy SchubertEOF
557*e0c4386eSCy Schubert      },
558*e0c4386eSCy Schubert    },
559*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_PEM(?|_write|_write_cb|_write_const)<<<\((.*?),.*\)>>>/,
560*e0c4386eSCy Schubert      massager => sub { return (<<"EOF");
561*e0c4386eSCy Schubert#ifndef OPENSSL_NO_STDIO
562*e0c4386eSCy Schubertint PEM_write_$1(void);
563*e0c4386eSCy Schubert#endif
564*e0c4386eSCy Schubertint PEM_write_bio_$1(void);
565*e0c4386eSCy SchubertEOF
566*e0c4386eSCy Schubert      },
567*e0c4386eSCy Schubert    },
568*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_PEM(?|_write|_write_cb|_write_const)_ex<<<\((.*?),.*\)>>>/,
569*e0c4386eSCy Schubert      massager => sub { return (<<"EOF");
570*e0c4386eSCy Schubert#ifndef OPENSSL_NO_STDIO
571*e0c4386eSCy Schubertint PEM_write_$1(void);
572*e0c4386eSCy Schubertint PEM_write_$1_ex(void);
573*e0c4386eSCy Schubert#endif
574*e0c4386eSCy Schubertint PEM_write_bio_$1(void);
575*e0c4386eSCy Schubertint PEM_write_bio_$1_ex(void);
576*e0c4386eSCy SchubertEOF
577*e0c4386eSCy Schubert      },
578*e0c4386eSCy Schubert    },
579*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_PEM(?|_read|_read_cb)<<<\((.*?),.*\)>>>/,
580*e0c4386eSCy Schubert      massager => sub { return (<<"EOF");
581*e0c4386eSCy Schubert#ifndef OPENSSL_NO_STDIO
582*e0c4386eSCy Schubertint PEM_read_$1(void);
583*e0c4386eSCy Schubert#endif
584*e0c4386eSCy Schubertint PEM_read_bio_$1(void);
585*e0c4386eSCy SchubertEOF
586*e0c4386eSCy Schubert      },
587*e0c4386eSCy Schubert    },
588*e0c4386eSCy Schubert    { regexp   => qr/DECLARE_PEM(?|_read|_read_cb)_ex<<<\((.*?),.*\)>>>/,
589*e0c4386eSCy Schubert      massager => sub { return (<<"EOF");
590*e0c4386eSCy Schubert#ifndef OPENSSL_NO_STDIO
591*e0c4386eSCy Schubertint PEM_read_$1(void);
592*e0c4386eSCy Schubertint PEM_read_$1_ex(void);
593*e0c4386eSCy Schubert#endif
594*e0c4386eSCy Schubertint PEM_read_bio_$1(void);
595*e0c4386eSCy Schubertint PEM_read_bio_$1_ex(void);
596*e0c4386eSCy SchubertEOF
597*e0c4386eSCy Schubert      },
598*e0c4386eSCy Schubert    },
599*e0c4386eSCy Schubert    # Universal translator of attributed PEM declarators
600*e0c4386eSCy Schubert    { regexp   => qr/
601*e0c4386eSCy Schubert          DECLARE_PEM
602*e0c4386eSCy Schubert          ((?:_rw|_rw_cb|_rw_const|_write|_write_cb|_write_const|_read|_read_cb)
603*e0c4386eSCy Schubert           (?:_ex)?)
604*e0c4386eSCy Schubert          _attr
605*e0c4386eSCy Schubert          <<<\(\s*OSSL_DEPRECATEDIN_(.*?)\s*,(.*?)\)>>>
606*e0c4386eSCy Schubert      /x,
607*e0c4386eSCy Schubert      massager => sub { return (<<"EOF");
608*e0c4386eSCy SchubertDECLARE_PEM$1($3)
609*e0c4386eSCy SchubertEOF
610*e0c4386eSCy Schubert      },
611*e0c4386eSCy Schubert    },
612*e0c4386eSCy Schubert
613*e0c4386eSCy Schubert    # OpenSSL's declaration of externs with possible export linkage
614*e0c4386eSCy Schubert    # (really only relevant on Windows)
615*e0c4386eSCy Schubert    { regexp   => qr/OPENSSL_(?:EXPORT|EXTERN)/,
616*e0c4386eSCy Schubert      massager => sub { return ("extern"); }
617*e0c4386eSCy Schubert    },
618*e0c4386eSCy Schubert
619*e0c4386eSCy Schubert    # Spurious stuff found in the OpenSSL headers
620*e0c4386eSCy Schubert    # Usually, these are just macros that expand to, well, something
621*e0c4386eSCy Schubert    { regexp   => qr/__NDK_FPABI__/,
622*e0c4386eSCy Schubert      massager => sub { return (); }
623*e0c4386eSCy Schubert    },
624*e0c4386eSCy Schubert    );
625*e0c4386eSCy Schubert
626*e0c4386eSCy Schubertmy $anoncnt = 0;
627*e0c4386eSCy Schubert
628*e0c4386eSCy Schubertmy @chandlers = (
629*e0c4386eSCy Schubert    ##################################################################
630*e0c4386eSCy Schubert    # C stuff
631*e0c4386eSCy Schubert
632*e0c4386eSCy Schubert    # extern "C" of individual items
633*e0c4386eSCy Schubert    # Note that the main parse function has a special hack for 'extern "C" {'
634*e0c4386eSCy Schubert    # which can't be done in handlers
635*e0c4386eSCy Schubert    # We simply ignore it.
636*e0c4386eSCy Schubert    { regexp   => qr/^extern "C" (.*(?:;|>>>))/,
637*e0c4386eSCy Schubert      massager => sub { return ($1); },
638*e0c4386eSCy Schubert    },
639*e0c4386eSCy Schubert    # any other extern is just ignored
640*e0c4386eSCy Schubert    { regexp   => qr/^\s*                       # Any spaces before
641*e0c4386eSCy Schubert                     extern                     # The keyword we look for
642*e0c4386eSCy Schubert                     \b                         # word to non-word boundary
643*e0c4386eSCy Schubert                     .*                         # Anything after
644*e0c4386eSCy Schubert                     ;
645*e0c4386eSCy Schubert                    /x,
646*e0c4386eSCy Schubert      massager => sub { return (); },
647*e0c4386eSCy Schubert    },
648*e0c4386eSCy Schubert    # union, struct and enum definitions
649*e0c4386eSCy Schubert    # Because this one might appear a little everywhere within type
650*e0c4386eSCy Schubert    # definitions, we take it out and replace it with just
651*e0c4386eSCy Schubert    # 'union|struct|enum name' while registering it.
652*e0c4386eSCy Schubert    # This makes use of the parser trick to surround the outer braces
653*e0c4386eSCy Schubert    # with <<< and >>>
654*e0c4386eSCy Schubert    { regexp   => qr/(.*)                       # Anything before       ($1)
655*e0c4386eSCy Schubert                     \b                         # word to non-word boundary
656*e0c4386eSCy Schubert                     (union|struct|enum)        # The word used         ($2)
657*e0c4386eSCy Schubert                     (?:\s([[:alpha:]_]\w*))?   # Struct or enum name   ($3)
658*e0c4386eSCy Schubert                     <<<(\{.*?\})>>>            # Struct or enum definition ($4)
659*e0c4386eSCy Schubert                     (.*)                       # Anything after        ($5)
660*e0c4386eSCy Schubert                     ;
661*e0c4386eSCy Schubert                    /x,
662*e0c4386eSCy Schubert      massager => sub {
663*e0c4386eSCy Schubert          my $before = $1;
664*e0c4386eSCy Schubert          my $word = $2;
665*e0c4386eSCy Schubert          my $name = $3
666*e0c4386eSCy Schubert              || sprintf("__anon%03d", ++$anoncnt); # Anonymous struct
667*e0c4386eSCy Schubert          my $definition = $4;
668*e0c4386eSCy Schubert          my $after = $5;
669*e0c4386eSCy Schubert          my $type = $word eq "struct" ? 'S' : 'E';
670*e0c4386eSCy Schubert          if ($before ne "" || $after ne ";") {
671*e0c4386eSCy Schubert              if ($after =~ m|^\w|) { $after = " ".$after; }
672*e0c4386eSCy Schubert              return ("$before$word $name$after;",
673*e0c4386eSCy Schubert                      "$word $name", $type, "", "$word$definition", all_conds());
674*e0c4386eSCy Schubert          }
675*e0c4386eSCy Schubert          # If there was no before nor after, make the return much simple
676*e0c4386eSCy Schubert          return ("", "$word $name", $type, "", "$word$definition", all_conds());
677*e0c4386eSCy Schubert      }
678*e0c4386eSCy Schubert    },
679*e0c4386eSCy Schubert    # Named struct and enum forward declarations
680*e0c4386eSCy Schubert    # We really just ignore them, but we need to parse them or the variable
681*e0c4386eSCy Schubert    # declaration handler further down will think it's a variable declaration.
682*e0c4386eSCy Schubert    { regexp   => qr/^(union|struct|enum) ([[:alpha:]_]\w*);/,
683*e0c4386eSCy Schubert      massager => sub { return (); }
684*e0c4386eSCy Schubert    },
685*e0c4386eSCy Schubert    # Function returning function pointer declaration
686*e0c4386eSCy Schubert    # This sort of declaration may have a body (inline functions, for example)
687*e0c4386eSCy Schubert    { regexp   => qr/(?:(typedef)\s?)?          # Possible typedef      ($1)
688*e0c4386eSCy Schubert                     ((?:\w|\*|\s)*?)           # Return type           ($2)
689*e0c4386eSCy Schubert                     \s?                        # Possible space
690*e0c4386eSCy Schubert                     <<<\(\*
691*e0c4386eSCy Schubert                     ([[:alpha:]_]\w*)          # Function name         ($3)
692*e0c4386eSCy Schubert                     (\(.*\))                   # Parameters            ($4)
693*e0c4386eSCy Schubert                     \)>>>
694*e0c4386eSCy Schubert                     <<<(\(.*\))>>>             # F.p. parameters       ($5)
695*e0c4386eSCy Schubert                     (?:<<<\{.*\}>>>|;)         # Body or semicolon
696*e0c4386eSCy Schubert                    /x,
697*e0c4386eSCy Schubert      massager => sub {
698*e0c4386eSCy Schubert          return ("", $3, 'T', "", "$2(*$4)$5", all_conds())
699*e0c4386eSCy Schubert              if defined $1;
700*e0c4386eSCy Schubert          return ("", $3, 'F', "$2(*)$5", "$2(*$4)$5", all_conds()); }
701*e0c4386eSCy Schubert    },
702*e0c4386eSCy Schubert    # Function pointer declaration, or typedef thereof
703*e0c4386eSCy Schubert    # This sort of declaration never has a function body
704*e0c4386eSCy Schubert    { regexp   => qr/(?:(typedef)\s?)?          # Possible typedef      ($1)
705*e0c4386eSCy Schubert                     ((?:\w|\*|\s)*?)           # Return type           ($2)
706*e0c4386eSCy Schubert                     <<<\(\*([[:alpha:]_]\w*)\)>>> # T.d. or var name   ($3)
707*e0c4386eSCy Schubert                     <<<(\(.*\))>>>             # F.p. parameters       ($4)
708*e0c4386eSCy Schubert                     ;
709*e0c4386eSCy Schubert                    /x,
710*e0c4386eSCy Schubert      massager => sub {
711*e0c4386eSCy Schubert          return ("", $3, 'T', "", "$2(*)$4", all_conds())
712*e0c4386eSCy Schubert              if defined $1;
713*e0c4386eSCy Schubert          return ("", $3, 'V', "$2(*)$4", "$2(*)$4", all_conds());
714*e0c4386eSCy Schubert      },
715*e0c4386eSCy Schubert    },
716*e0c4386eSCy Schubert    # Function declaration, or typedef thereof
717*e0c4386eSCy Schubert    # This sort of declaration may have a body (inline functions, for example)
718*e0c4386eSCy Schubert    { regexp   => qr/(?:(typedef)\s?)?          # Possible typedef      ($1)
719*e0c4386eSCy Schubert                     ((?:\w|\*|\s)*?)           # Return type           ($2)
720*e0c4386eSCy Schubert                     \s?                        # Possible space
721*e0c4386eSCy Schubert                     ([[:alpha:]_]\w*)          # Function name         ($3)
722*e0c4386eSCy Schubert                     <<<(\(.*\))>>>             # Parameters            ($4)
723*e0c4386eSCy Schubert                     (?:<<<\{.*\}>>>|;)         # Body or semicolon
724*e0c4386eSCy Schubert                    /x,
725*e0c4386eSCy Schubert      massager => sub {
726*e0c4386eSCy Schubert          return ("", $3, 'T', "", "$2$4", all_conds())
727*e0c4386eSCy Schubert              if defined $1;
728*e0c4386eSCy Schubert          return ("", $3, 'F', $2, "$2$4", all_conds());
729*e0c4386eSCy Schubert      },
730*e0c4386eSCy Schubert    },
731*e0c4386eSCy Schubert    # Variable declaration, including arrays, or typedef thereof
732*e0c4386eSCy Schubert    { regexp   => qr/(?:(typedef)\s?)?          # Possible typedef      ($1)
733*e0c4386eSCy Schubert                     ((?:\w|\*|\s)*?)           # Type                  ($2)
734*e0c4386eSCy Schubert                     \s?                        # Possible space
735*e0c4386eSCy Schubert                     ([[:alpha:]_]\w*)          # Variable name         ($3)
736*e0c4386eSCy Schubert                     ((?:<<<\[[^\]]*\]>>>)*)    # Possible array declaration ($4)
737*e0c4386eSCy Schubert                     ;
738*e0c4386eSCy Schubert                    /x,
739*e0c4386eSCy Schubert      massager => sub {
740*e0c4386eSCy Schubert          return ("", $3, 'T', "", $2.($4||""), all_conds())
741*e0c4386eSCy Schubert              if defined $1;
742*e0c4386eSCy Schubert          return ("", $3, 'V', $2.($4||""), $2.($4||""), all_conds());
743*e0c4386eSCy Schubert      },
744*e0c4386eSCy Schubert    },
745*e0c4386eSCy Schubert);
746*e0c4386eSCy Schubert
747*e0c4386eSCy Schubert# End handlers are almost the same as handlers, except they are run through
748*e0c4386eSCy Schubert# ONCE when the input has been parsed through.  These are used to check for
749*e0c4386eSCy Schubert# remaining stuff, such as an unfinished #ifdef and stuff like that that the
750*e0c4386eSCy Schubert# main parser can't check on its own.
751*e0c4386eSCy Schubertmy @endhandlers = (
752*e0c4386eSCy Schubert    { massager => sub {
753*e0c4386eSCy Schubert        my %opts = %{$_[0]};
754*e0c4386eSCy Schubert
755*e0c4386eSCy Schubert        die "Unfinished preprocessor conditions levels: ",scalar(@preprocessor_conds),($opts{filename} ? " in file ".$opts{filename}: ""),$opts{PLACE}
756*e0c4386eSCy Schubert            if @preprocessor_conds;
757*e0c4386eSCy Schubert      }
758*e0c4386eSCy Schubert    }
759*e0c4386eSCy Schubert    );
760*e0c4386eSCy Schubert
761*e0c4386eSCy Schubert# takes a list of strings that can each contain one or several lines of code
762*e0c4386eSCy Schubert# also takes a hash of options as last argument.
763*e0c4386eSCy Schubert#
764*e0c4386eSCy Schubert# returns a list of hashes with information:
765*e0c4386eSCy Schubert#
766*e0c4386eSCy Schubert#       name            name of the thing
767*e0c4386eSCy Schubert#       type            type, see the massage handler function
768*e0c4386eSCy Schubert#       returntype      return type of functions and variables
769*e0c4386eSCy Schubert#       value           value for macros, signature for functions, variables
770*e0c4386eSCy Schubert#                       and structs
771*e0c4386eSCy Schubert#       conds           preprocessor conditions (array ref)
772*e0c4386eSCy Schubert
773*e0c4386eSCy Schubertsub parse {
774*e0c4386eSCy Schubert    my %opts;
775*e0c4386eSCy Schubert    if (ref($_[$#_]) eq "HASH") {
776*e0c4386eSCy Schubert        %opts = %{$_[$#_]};
777*e0c4386eSCy Schubert        pop @_;
778*e0c4386eSCy Schubert    }
779*e0c4386eSCy Schubert    my %state = (
780*e0c4386eSCy Schubert        in_extern_C => 0,       # An exception to parenthesis processing.
781*e0c4386eSCy Schubert        cpp_parens => [],       # A list of ending parens and braces found in
782*e0c4386eSCy Schubert                                # preprocessor directives
783*e0c4386eSCy Schubert        c_parens => [],         # A list of ending parens and braces found in
784*e0c4386eSCy Schubert                                # C statements
785*e0c4386eSCy Schubert        in_string => "",        # empty string when outside a string, otherwise
786*e0c4386eSCy Schubert                                # "'" or '"' depending on the starting quote.
787*e0c4386eSCy Schubert        in_comment => "",       # empty string when outside a comment, otherwise
788*e0c4386eSCy Schubert                                # "/*" or "//" depending on the type of comment
789*e0c4386eSCy Schubert                                # found.  The latter will never be multiline
790*e0c4386eSCy Schubert                                # NOTE: in_string and in_comment will never be
791*e0c4386eSCy Schubert                                # true (in perl semantics) at the same time.
792*e0c4386eSCy Schubert        current_line => 0,
793*e0c4386eSCy Schubert        );
794*e0c4386eSCy Schubert    my @result = ();
795*e0c4386eSCy Schubert    my $normalized_line = "";   # $input_line, but normalized.  In essence, this
796*e0c4386eSCy Schubert                                # means that ALL whitespace is removed unless
797*e0c4386eSCy Schubert                                # it absolutely has to be present, and in that
798*e0c4386eSCy Schubert                                # case, there's only one space.
799*e0c4386eSCy Schubert                                # The cases where a space needs to stay present
800*e0c4386eSCy Schubert                                # are:
801*e0c4386eSCy Schubert                                # 1. between words
802*e0c4386eSCy Schubert                                # 2. between words and number
803*e0c4386eSCy Schubert                                # 3. after the first word of a preprocessor
804*e0c4386eSCy Schubert                                #    directive.
805*e0c4386eSCy Schubert                                # 4. for the #define directive, between the macro
806*e0c4386eSCy Schubert                                #    name/args and its value, so we end up with:
807*e0c4386eSCy Schubert                                #       #define FOO val
808*e0c4386eSCy Schubert                                #       #define BAR(x) something(x)
809*e0c4386eSCy Schubert    my $collected_stmt = "";    # Where we're building up a C line until it's a
810*e0c4386eSCy Schubert                                # complete definition/declaration, as determined
811*e0c4386eSCy Schubert                                # by any handler being capable of matching it.
812*e0c4386eSCy Schubert
813*e0c4386eSCy Schubert    # We use $_ shamelessly when looking through @lines.
814*e0c4386eSCy Schubert    # In case we find a \ at the end, we keep filling it up with more lines.
815*e0c4386eSCy Schubert    $_ = undef;
816*e0c4386eSCy Schubert
817*e0c4386eSCy Schubert    foreach my $line (@_) {
818*e0c4386eSCy Schubert        # split tries to be smart when a string ends with the thing we split on
819*e0c4386eSCy Schubert        $line .= "\n" unless $line =~ m|\R$|;
820*e0c4386eSCy Schubert        $line .= "#";
821*e0c4386eSCy Schubert
822*e0c4386eSCy Schubert        # We use ¦undef¦ as a marker for a new line from the file.
823*e0c4386eSCy Schubert        # Since we convert one line to several and unshift that into @lines,
824*e0c4386eSCy Schubert        # that's the only safe way we have to track the original lines
825*e0c4386eSCy Schubert        my @lines = map { ( undef, $_ ) } split m|\R|, $line;
826*e0c4386eSCy Schubert
827*e0c4386eSCy Schubert        # Remember that extra # we added above?  Now we remove it
828*e0c4386eSCy Schubert        pop @lines;
829*e0c4386eSCy Schubert        pop @lines;             # Don't forget the undef
830*e0c4386eSCy Schubert
831*e0c4386eSCy Schubert        while (@lines) {
832*e0c4386eSCy Schubert            if (!defined($lines[0])) {
833*e0c4386eSCy Schubert                shift @lines;
834*e0c4386eSCy Schubert                $state{current_line}++;
835*e0c4386eSCy Schubert                if (!defined($_)) {
836*e0c4386eSCy Schubert                    $opts{PLACE} = " at ".$opts{filename}." line ".$state{current_line}."\n";
837*e0c4386eSCy Schubert                    $opts{PLACE2} = $opts{filename}.":".$state{current_line};
838*e0c4386eSCy Schubert                }
839*e0c4386eSCy Schubert                next;
840*e0c4386eSCy Schubert            }
841*e0c4386eSCy Schubert
842*e0c4386eSCy Schubert            $_ = "" unless defined $_;
843*e0c4386eSCy Schubert            $_ .= shift @lines;
844*e0c4386eSCy Schubert
845*e0c4386eSCy Schubert            if (m|\\$|) {
846*e0c4386eSCy Schubert                $_ = $`;
847*e0c4386eSCy Schubert                next;
848*e0c4386eSCy Schubert            }
849*e0c4386eSCy Schubert
850*e0c4386eSCy Schubert            if ($opts{debug}) {
851*e0c4386eSCy Schubert                print STDERR "DEBUG:----------------------------\n";
852*e0c4386eSCy Schubert                print STDERR "DEBUG: \$_      = '$_'\n";
853*e0c4386eSCy Schubert            }
854*e0c4386eSCy Schubert
855*e0c4386eSCy Schubert            ##########################################################
856*e0c4386eSCy Schubert            # Now that we have a full line, let's process through it
857*e0c4386eSCy Schubert            while(1) {
858*e0c4386eSCy Schubert                unless ($state{in_comment}) {
859*e0c4386eSCy Schubert                    # Begin with checking if the current $normalized_line
860*e0c4386eSCy Schubert                    # contains a preprocessor directive
861*e0c4386eSCy Schubert                    # This is only done if we're not inside a comment and
862*e0c4386eSCy Schubert                    # if it's a preprocessor directive and it's finished.
863*e0c4386eSCy Schubert                    if ($normalized_line =~ m|^#| && $_ eq "") {
864*e0c4386eSCy Schubert                        print STDERR "DEBUG[OPENSSL CPP]: \$normalized_line = '$normalized_line'\n"
865*e0c4386eSCy Schubert                            if $opts{debug};
866*e0c4386eSCy Schubert                        $opts{debug_type} = "OPENSSL CPP";
867*e0c4386eSCy Schubert                        my @r = ( _run_handlers($normalized_line,
868*e0c4386eSCy Schubert                                                @opensslcpphandlers,
869*e0c4386eSCy Schubert                                                \%opts) );
870*e0c4386eSCy Schubert                        if (shift @r) {
871*e0c4386eSCy Schubert                            # Checking if there are lines to inject.
872*e0c4386eSCy Schubert                            if (@r) {
873*e0c4386eSCy Schubert                                @r = split $/, (pop @r).$_;
874*e0c4386eSCy Schubert                                print STDERR "DEBUG[OPENSSL CPP]: injecting '", join("', '", @r),"'\n"
875*e0c4386eSCy Schubert                                    if $opts{debug} && @r;
876*e0c4386eSCy Schubert                                @lines = ( @r, @lines );
877*e0c4386eSCy Schubert
878*e0c4386eSCy Schubert                                $_ = "";
879*e0c4386eSCy Schubert                            }
880*e0c4386eSCy Schubert                        } else {
881*e0c4386eSCy Schubert                            print STDERR "DEBUG[CPP]: \$normalized_line = '$normalized_line'\n"
882*e0c4386eSCy Schubert                                if $opts{debug};
883*e0c4386eSCy Schubert                            $opts{debug_type} = "CPP";
884*e0c4386eSCy Schubert                            my @r = ( _run_handlers($normalized_line,
885*e0c4386eSCy Schubert                                                    @cpphandlers,
886*e0c4386eSCy Schubert                                                    \%opts) );
887*e0c4386eSCy Schubert                            if (shift @r) {
888*e0c4386eSCy Schubert                                if (ref($r[0]) eq "HASH") {
889*e0c4386eSCy Schubert                                    push @result, shift @r;
890*e0c4386eSCy Schubert                                }
891*e0c4386eSCy Schubert
892*e0c4386eSCy Schubert                                # Now, check if there are lines to inject.
893*e0c4386eSCy Schubert                                # Really, this should never happen, it IS a
894*e0c4386eSCy Schubert                                # preprocessor directive after all...
895*e0c4386eSCy Schubert                                if (@r) {
896*e0c4386eSCy Schubert                                    @r = split $/, pop @r;
897*e0c4386eSCy Schubert                                    print STDERR "DEBUG[CPP]: injecting '", join("', '", @r),"'\n"
898*e0c4386eSCy Schubert                                    if $opts{debug} && @r;
899*e0c4386eSCy Schubert                                    @lines = ( @r, @lines );
900*e0c4386eSCy Schubert                                    $_ = "";
901*e0c4386eSCy Schubert                                }
902*e0c4386eSCy Schubert                            }
903*e0c4386eSCy Schubert                        }
904*e0c4386eSCy Schubert
905*e0c4386eSCy Schubert                        # Note: we simply ignore all directives that no
906*e0c4386eSCy Schubert                        # handler matches
907*e0c4386eSCy Schubert                        $normalized_line = "";
908*e0c4386eSCy Schubert                    }
909*e0c4386eSCy Schubert
910*e0c4386eSCy Schubert                    # If the two strings end and start with a character that
911*e0c4386eSCy Schubert                    # shouldn't get concatenated, add a space
912*e0c4386eSCy Schubert                    my $space =
913*e0c4386eSCy Schubert                        ($collected_stmt =~ m/(?:"|')$/
914*e0c4386eSCy Schubert                         || ($collected_stmt =~ m/(?:\w|\d)$/
915*e0c4386eSCy Schubert                             && $normalized_line =~ m/^(?:\w|\d)/)) ? " " : "";
916*e0c4386eSCy Schubert
917*e0c4386eSCy Schubert                    # Now, unless we're building up a preprocessor directive or
918*e0c4386eSCy Schubert                    # are in the middle of a string, or the parens et al aren't
919*e0c4386eSCy Schubert                    # balanced up yet, let's try and see if there's a OpenSSL
920*e0c4386eSCy Schubert                    # or C handler that can make sense of what we have so far.
921*e0c4386eSCy Schubert                    if ( $normalized_line !~ m|^#|
922*e0c4386eSCy Schubert                         && ($collected_stmt ne "" || $normalized_line ne "")
923*e0c4386eSCy Schubert                         && ! @{$state{c_parens}}
924*e0c4386eSCy Schubert                         && ! $state{in_string} ) {
925*e0c4386eSCy Schubert                        if ($opts{debug}) {
926*e0c4386eSCy Schubert                            print STDERR "DEBUG[OPENSSL C]: \$collected_stmt  = '$collected_stmt'\n";
927*e0c4386eSCy Schubert                            print STDERR "DEBUG[OPENSSL C]: \$normalized_line = '$normalized_line'\n";
928*e0c4386eSCy Schubert                        }
929*e0c4386eSCy Schubert                        $opts{debug_type} = "OPENSSL C";
930*e0c4386eSCy Schubert                        my @r = ( _run_handlers($collected_stmt
931*e0c4386eSCy Schubert                                                    .$space
932*e0c4386eSCy Schubert                                                    .$normalized_line,
933*e0c4386eSCy Schubert                                                @opensslchandlers,
934*e0c4386eSCy Schubert                                                \%opts) );
935*e0c4386eSCy Schubert                        if (shift @r) {
936*e0c4386eSCy Schubert                            # Checking if there are lines to inject.
937*e0c4386eSCy Schubert                            if (@r) {
938*e0c4386eSCy Schubert                                @r = split $/, (pop @r).$_;
939*e0c4386eSCy Schubert                                print STDERR "DEBUG[OPENSSL]: injecting '", join("', '", @r),"'\n"
940*e0c4386eSCy Schubert                                    if $opts{debug} && @r;
941*e0c4386eSCy Schubert                                @lines = ( @r, @lines );
942*e0c4386eSCy Schubert
943*e0c4386eSCy Schubert                                $_ = "";
944*e0c4386eSCy Schubert                            }
945*e0c4386eSCy Schubert                            $normalized_line = "";
946*e0c4386eSCy Schubert                            $collected_stmt = "";
947*e0c4386eSCy Schubert                        } else {
948*e0c4386eSCy Schubert                            if ($opts{debug}) {
949*e0c4386eSCy Schubert                                print STDERR "DEBUG[C]: \$collected_stmt  = '$collected_stmt'\n";
950*e0c4386eSCy Schubert                                print STDERR "DEBUG[C]: \$normalized_line = '$normalized_line'\n";
951*e0c4386eSCy Schubert                            }
952*e0c4386eSCy Schubert                            $opts{debug_type} = "C";
953*e0c4386eSCy Schubert                            my @r = ( _run_handlers($collected_stmt
954*e0c4386eSCy Schubert                                                        .$space
955*e0c4386eSCy Schubert                                                        .$normalized_line,
956*e0c4386eSCy Schubert                                                    @chandlers,
957*e0c4386eSCy Schubert                                                    \%opts) );
958*e0c4386eSCy Schubert                            if (shift @r) {
959*e0c4386eSCy Schubert                                if (ref($r[0]) eq "HASH") {
960*e0c4386eSCy Schubert                                    push @result, shift @r;
961*e0c4386eSCy Schubert                                }
962*e0c4386eSCy Schubert
963*e0c4386eSCy Schubert                                # Checking if there are lines to inject.
964*e0c4386eSCy Schubert                                if (@r) {
965*e0c4386eSCy Schubert                                    @r = split $/, (pop @r).$_;
966*e0c4386eSCy Schubert                                    print STDERR "DEBUG[C]: injecting '", join("', '", @r),"'\n"
967*e0c4386eSCy Schubert                                        if $opts{debug} && @r;
968*e0c4386eSCy Schubert                                    @lines = ( @r, @lines );
969*e0c4386eSCy Schubert
970*e0c4386eSCy Schubert                                    $_ = "";
971*e0c4386eSCy Schubert                                }
972*e0c4386eSCy Schubert                                $normalized_line = "";
973*e0c4386eSCy Schubert                                $collected_stmt = "";
974*e0c4386eSCy Schubert                            }
975*e0c4386eSCy Schubert                        }
976*e0c4386eSCy Schubert                    }
977*e0c4386eSCy Schubert                    if ($_ eq "") {
978*e0c4386eSCy Schubert                        $collected_stmt .= $space.$normalized_line;
979*e0c4386eSCy Schubert                        $normalized_line = "";
980*e0c4386eSCy Schubert                    }
981*e0c4386eSCy Schubert                }
982*e0c4386eSCy Schubert
983*e0c4386eSCy Schubert                if ($_ eq "") {
984*e0c4386eSCy Schubert                    $_ = undef;
985*e0c4386eSCy Schubert                    last;
986*e0c4386eSCy Schubert                }
987*e0c4386eSCy Schubert
988*e0c4386eSCy Schubert                # Take care of inside string first.
989*e0c4386eSCy Schubert                if ($state{in_string}) {
990*e0c4386eSCy Schubert                    if (m/ (?:^|(?<!\\))        # Make sure it's not escaped
991*e0c4386eSCy Schubert                           $state{in_string}    # Look for matching quote
992*e0c4386eSCy Schubert                         /x) {
993*e0c4386eSCy Schubert                        $normalized_line .= $`.$&;
994*e0c4386eSCy Schubert                        $state{in_string} = "";
995*e0c4386eSCy Schubert                        $_ = $';
996*e0c4386eSCy Schubert                        next;
997*e0c4386eSCy Schubert                    } else {
998*e0c4386eSCy Schubert                        die "Unfinished string without continuation found$opts{PLACE}\n";
999*e0c4386eSCy Schubert                    }
1000*e0c4386eSCy Schubert                }
1001*e0c4386eSCy Schubert                # ... or inside comments, whichever happens to apply
1002*e0c4386eSCy Schubert                elsif ($state{in_comment}) {
1003*e0c4386eSCy Schubert
1004*e0c4386eSCy Schubert                    # This should never happen
1005*e0c4386eSCy Schubert                    die "Something went seriously wrong, multiline //???$opts{PLACE}\n"
1006*e0c4386eSCy Schubert                        if ($state{in_comment} eq "//");
1007*e0c4386eSCy Schubert
1008*e0c4386eSCy Schubert                    # A note: comments are simply discarded.
1009*e0c4386eSCy Schubert
1010*e0c4386eSCy Schubert                    if (m/ (?:^|(?<!\\))        # Make sure it's not escaped
1011*e0c4386eSCy Schubert                           \*\/                 # Look for C comment end
1012*e0c4386eSCy Schubert                         /x) {
1013*e0c4386eSCy Schubert                        $state{in_comment} = "";
1014*e0c4386eSCy Schubert                        $_ = $';
1015*e0c4386eSCy Schubert                        print STDERR "DEBUG: Found end of comment, followed by '$_'\n"
1016*e0c4386eSCy Schubert                            if $opts{debug};
1017*e0c4386eSCy Schubert                        next;
1018*e0c4386eSCy Schubert                    } else {
1019*e0c4386eSCy Schubert                        $_ = "";
1020*e0c4386eSCy Schubert                        next;
1021*e0c4386eSCy Schubert                    }
1022*e0c4386eSCy Schubert                }
1023*e0c4386eSCy Schubert
1024*e0c4386eSCy Schubert                # At this point, it's safe to remove leading whites, but
1025*e0c4386eSCy Schubert                # we need to be careful with some preprocessor lines
1026*e0c4386eSCy Schubert                if (m|^\s+|) {
1027*e0c4386eSCy Schubert                    my $rest = $';
1028*e0c4386eSCy Schubert                    my $space = "";
1029*e0c4386eSCy Schubert                    $space = " "
1030*e0c4386eSCy Schubert                        if ($normalized_line =~ m/^
1031*e0c4386eSCy Schubert                                                  \#define\s\w(?:\w|\d)*(?:<<<\([^\)]*\)>>>)?
1032*e0c4386eSCy Schubert                                                  | \#[a-z]+
1033*e0c4386eSCy Schubert                                                  $/x);
1034*e0c4386eSCy Schubert                    print STDERR "DEBUG: Processing leading spaces: \$normalized_line = '$normalized_line', \$space = '$space', \$rest = '$rest'\n"
1035*e0c4386eSCy Schubert                        if $opts{debug};
1036*e0c4386eSCy Schubert                    $_ = $space.$rest;
1037*e0c4386eSCy Schubert                }
1038*e0c4386eSCy Schubert
1039*e0c4386eSCy Schubert                my $parens =
1040*e0c4386eSCy Schubert                    $normalized_line =~ m|^#| ? 'cpp_parens' : 'c_parens';
1041*e0c4386eSCy Schubert                (my $paren_singular = $parens) =~ s|s$||;
1042*e0c4386eSCy Schubert
1043*e0c4386eSCy Schubert                # Now check for specific tokens, and if they are parens,
1044*e0c4386eSCy Schubert                # check them against $state{$parens}.  Note that we surround
1045*e0c4386eSCy Schubert                # the outermost parens with extra "<<<" and ">>>".  Those
1046*e0c4386eSCy Schubert                # are for the benefit of handlers who to need to detect
1047*e0c4386eSCy Schubert                # them, and they will be removed from the final output.
1048*e0c4386eSCy Schubert                if (m|^[\{\[\(]|) {
1049*e0c4386eSCy Schubert                    my $body = $&;
1050*e0c4386eSCy Schubert                    $_ = $';
1051*e0c4386eSCy Schubert                    if (!@{$state{$parens}}) {
1052*e0c4386eSCy Schubert                        if ("$normalized_line$body" =~ m|^extern "C"\{$|) {
1053*e0c4386eSCy Schubert                            $state{in_extern_C} = 1;
1054*e0c4386eSCy Schubert                            print STDERR "DEBUG: found start of 'extern \"C\"' ($normalized_line$body)\n"
1055*e0c4386eSCy Schubert                                if $opts{debug};
1056*e0c4386eSCy Schubert                            $normalized_line = "";
1057*e0c4386eSCy Schubert                        } else {
1058*e0c4386eSCy Schubert                            $normalized_line .= "<<<".$body;
1059*e0c4386eSCy Schubert                        }
1060*e0c4386eSCy Schubert                    } else {
1061*e0c4386eSCy Schubert                        $normalized_line .= $body;
1062*e0c4386eSCy Schubert                    }
1063*e0c4386eSCy Schubert
1064*e0c4386eSCy Schubert                    if ($normalized_line ne "") {
1065*e0c4386eSCy Schubert                        print STDERR "DEBUG: found $paren_singular start '$body'\n"
1066*e0c4386eSCy Schubert                            if $opts{debug};
1067*e0c4386eSCy Schubert                        $body =~ tr|\{\[\(|\}\]\)|;
1068*e0c4386eSCy Schubert                        print STDERR "DEBUG: pushing $paren_singular end '$body'\n"
1069*e0c4386eSCy Schubert                            if $opts{debug};
1070*e0c4386eSCy Schubert                        push @{$state{$parens}}, $body;
1071*e0c4386eSCy Schubert                    }
1072*e0c4386eSCy Schubert                } elsif (m|^[\}\]\)]|) {
1073*e0c4386eSCy Schubert                    $_ = $';
1074*e0c4386eSCy Schubert
1075*e0c4386eSCy Schubert                    if (!@{$state{$parens}}
1076*e0c4386eSCy Schubert                        && $& eq '}' && $state{in_extern_C}) {
1077*e0c4386eSCy Schubert                        print STDERR "DEBUG: found end of 'extern \"C\"'\n"
1078*e0c4386eSCy Schubert                            if $opts{debug};
1079*e0c4386eSCy Schubert                        $state{in_extern_C} = 0;
1080*e0c4386eSCy Schubert                    } else {
1081*e0c4386eSCy Schubert                        print STDERR "DEBUG: Trying to match '$&' against '"
1082*e0c4386eSCy Schubert                            ,join("', '", @{$state{$parens}})
1083*e0c4386eSCy Schubert                            ,"'\n"
1084*e0c4386eSCy Schubert                            if $opts{debug};
1085*e0c4386eSCy Schubert                        die "Unmatched parentheses$opts{PLACE}\n"
1086*e0c4386eSCy Schubert                            unless (@{$state{$parens}}
1087*e0c4386eSCy Schubert                                    && pop @{$state{$parens}} eq $&);
1088*e0c4386eSCy Schubert                        if (!@{$state{$parens}}) {
1089*e0c4386eSCy Schubert                            $normalized_line .= $&.">>>";
1090*e0c4386eSCy Schubert                        } else {
1091*e0c4386eSCy Schubert                            $normalized_line .= $&;
1092*e0c4386eSCy Schubert                        }
1093*e0c4386eSCy Schubert                    }
1094*e0c4386eSCy Schubert                } elsif (m|^["']|) { # string start
1095*e0c4386eSCy Schubert                    my $body = $&;
1096*e0c4386eSCy Schubert                    $_ = $';
1097*e0c4386eSCy Schubert
1098*e0c4386eSCy Schubert                    # We want to separate strings from \w and \d with one space.
1099*e0c4386eSCy Schubert                    $normalized_line .= " " if $normalized_line =~ m/(\w|\d)$/;
1100*e0c4386eSCy Schubert                    $normalized_line .= $body;
1101*e0c4386eSCy Schubert                    $state{in_string} = $body;
1102*e0c4386eSCy Schubert                } elsif (m|^\/\*|) { # C style comment
1103*e0c4386eSCy Schubert                    print STDERR "DEBUG: found start of C style comment\n"
1104*e0c4386eSCy Schubert                        if $opts{debug};
1105*e0c4386eSCy Schubert                    $state{in_comment} = $&;
1106*e0c4386eSCy Schubert                    $_ = $';
1107*e0c4386eSCy Schubert                } elsif (m|^\/\/|) { # C++ style comment
1108*e0c4386eSCy Schubert                    print STDERR "DEBUG: found C++ style comment\n"
1109*e0c4386eSCy Schubert                        if $opts{debug};
1110*e0c4386eSCy Schubert                    $_ = "";    # (just discard it entirely)
1111*e0c4386eSCy Schubert                } elsif (m/^ (?| (?: 0[xX][[:xdigit:]]+ | 0[bB][01]+ | [0-9]+ )
1112*e0c4386eSCy Schubert                                 (?i: U | L | UL | LL | ULL )?
1113*e0c4386eSCy Schubert                               | [0-9]+\.[0-9]+(?:[eE][\-\+]\d+)? (?i: F | L)?
1114*e0c4386eSCy Schubert                               ) /x) {
1115*e0c4386eSCy Schubert                    print STDERR "DEBUG: Processing numbers: \$normalized_line = '$normalized_line', \$& = '$&', \$' = '$''\n"
1116*e0c4386eSCy Schubert                        if $opts{debug};
1117*e0c4386eSCy Schubert                    $normalized_line .= $&;
1118*e0c4386eSCy Schubert                    $_ = $';
1119*e0c4386eSCy Schubert                } elsif (m/^[[:alpha:]_]\w*/) {
1120*e0c4386eSCy Schubert                    my $body = $&;
1121*e0c4386eSCy Schubert                    my $rest = $';
1122*e0c4386eSCy Schubert                    my $space = "";
1123*e0c4386eSCy Schubert
1124*e0c4386eSCy Schubert                    # Now, only add a space if it's needed to separate
1125*e0c4386eSCy Schubert                    # two \w characters, and we also surround strings with
1126*e0c4386eSCy Schubert                    # a space.  In this case, that's if $normalized_line ends
1127*e0c4386eSCy Schubert                    # with a \w, \d, " or '.
1128*e0c4386eSCy Schubert                    $space = " "
1129*e0c4386eSCy Schubert                        if ($normalized_line =~ m/("|')$/
1130*e0c4386eSCy Schubert                            || ($normalized_line =~ m/(\w|\d)$/
1131*e0c4386eSCy Schubert                                && $body =~ m/^(\w|\d)/));
1132*e0c4386eSCy Schubert
1133*e0c4386eSCy Schubert                    print STDERR "DEBUG: Processing words: \$normalized_line = '$normalized_line', \$space = '$space', \$body = '$body', \$rest = '$rest'\n"
1134*e0c4386eSCy Schubert                        if $opts{debug};
1135*e0c4386eSCy Schubert                    $normalized_line .= $space.$body;
1136*e0c4386eSCy Schubert                    $_ = $rest;
1137*e0c4386eSCy Schubert                } elsif (m|^(?:\\)?.|) { # Catch-all
1138*e0c4386eSCy Schubert                    $normalized_line .= $&;
1139*e0c4386eSCy Schubert                    $_ = $';
1140*e0c4386eSCy Schubert                }
1141*e0c4386eSCy Schubert            }
1142*e0c4386eSCy Schubert        }
1143*e0c4386eSCy Schubert    }
1144*e0c4386eSCy Schubert    foreach my $handler (@endhandlers) {
1145*e0c4386eSCy Schubert        if ($handler->{massager}) {
1146*e0c4386eSCy Schubert            $handler->{massager}->(\%opts);
1147*e0c4386eSCy Schubert        }
1148*e0c4386eSCy Schubert    }
1149*e0c4386eSCy Schubert    return @result;
1150*e0c4386eSCy Schubert}
1151*e0c4386eSCy Schubert
1152*e0c4386eSCy Schubert# arg1:    line to check
1153*e0c4386eSCy Schubert# arg2...: handlers to check
1154*e0c4386eSCy Schubert# return undef when no handler matched
1155*e0c4386eSCy Schubertsub _run_handlers {
1156*e0c4386eSCy Schubert    my %opts;
1157*e0c4386eSCy Schubert    if (ref($_[$#_]) eq "HASH") {
1158*e0c4386eSCy Schubert        %opts = %{$_[$#_]};
1159*e0c4386eSCy Schubert        pop @_;
1160*e0c4386eSCy Schubert    }
1161*e0c4386eSCy Schubert    my $line = shift;
1162*e0c4386eSCy Schubert    my @handlers = @_;
1163*e0c4386eSCy Schubert
1164*e0c4386eSCy Schubert    foreach my $handler (@handlers) {
1165*e0c4386eSCy Schubert        if ($handler->{regexp}
1166*e0c4386eSCy Schubert            && $line =~ m|^$handler->{regexp}$|) {
1167*e0c4386eSCy Schubert            if ($handler->{massager}) {
1168*e0c4386eSCy Schubert                if ($opts{debug}) {
1169*e0c4386eSCy Schubert                    print STDERR "DEBUG[",$opts{debug_type},"]: Trying to handle '$line'\n";
1170*e0c4386eSCy Schubert                    print STDERR "DEBUG[",$opts{debug_type},"]: (matches /\^",$handler->{regexp},"\$/)\n";
1171*e0c4386eSCy Schubert                }
1172*e0c4386eSCy Schubert                my $saved_line = $line;
1173*e0c4386eSCy Schubert                my @massaged =
1174*e0c4386eSCy Schubert                    map { s/(<<<|>>>)//g; $_ }
1175*e0c4386eSCy Schubert                    $handler->{massager}->($saved_line, \%opts);
1176*e0c4386eSCy Schubert                print STDERR "DEBUG[",$opts{debug_type},"]: Got back '"
1177*e0c4386eSCy Schubert                    , join("', '", @massaged), "'\n"
1178*e0c4386eSCy Schubert                    if $opts{debug};
1179*e0c4386eSCy Schubert
1180*e0c4386eSCy Schubert                # Because we may get back new lines to be
1181*e0c4386eSCy Schubert                # injected before whatever else that follows,
1182*e0c4386eSCy Schubert                # and the injected stuff might include
1183*e0c4386eSCy Schubert                # preprocessor lines, we need to inject them
1184*e0c4386eSCy Schubert                # in @lines and set $_ to the empty string to
1185*e0c4386eSCy Schubert                # break out from the inner loops
1186*e0c4386eSCy Schubert                my $injected_lines = shift @massaged || "";
1187*e0c4386eSCy Schubert
1188*e0c4386eSCy Schubert                if (@massaged) {
1189*e0c4386eSCy Schubert                    return (1,
1190*e0c4386eSCy Schubert                            {
1191*e0c4386eSCy Schubert                                name    => shift @massaged,
1192*e0c4386eSCy Schubert                                type    => shift @massaged,
1193*e0c4386eSCy Schubert                                returntype => shift @massaged,
1194*e0c4386eSCy Schubert                                value   => shift @massaged,
1195*e0c4386eSCy Schubert                                conds   => [ @massaged ]
1196*e0c4386eSCy Schubert                            },
1197*e0c4386eSCy Schubert                            $injected_lines
1198*e0c4386eSCy Schubert                        );
1199*e0c4386eSCy Schubert                } else {
1200*e0c4386eSCy Schubert                    print STDERR "DEBUG[",$opts{debug_type},"]:   (ignore, possible side effects)\n"
1201*e0c4386eSCy Schubert                        if $opts{debug} && $injected_lines eq "";
1202*e0c4386eSCy Schubert                    return (1, $injected_lines);
1203*e0c4386eSCy Schubert                }
1204*e0c4386eSCy Schubert            }
1205*e0c4386eSCy Schubert            return (1);
1206*e0c4386eSCy Schubert        }
1207*e0c4386eSCy Schubert    }
1208*e0c4386eSCy Schubert    return (0);
1209*e0c4386eSCy Schubert}
1210