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