wmaker: remove useless null pointer check (Coverity #109612)
[wmaker-crm.git] / script / nested-func-to-macro.sh
blob416400fdd79ab9a2aad96173256a2160a07d215d
1 #!/bin/sh
2 ###########################################################################
4 # Window Maker window manager
6 # Copyright (c) 2014 Christophe CURIS
7 # Copyright (c) 2014 Window Maker Team
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License along
20 # with this program; if not, write to the Free Software Foundation, Inc.,
21 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 ###########################################################################
25 # nested-func-to-macro.sh:
26 # from a C source file specified with "-i", convert the functions
27 # specified with "-f" into preprocessor macros ("#define") and save the
28 # result as the file specified with "-o"
30 # The goal is to process nested functions (functions defined inside other
31 # functions), because some compilers do not support that, despite the
32 # advantages against macros:
33 # - there is no side effect on arguments, like what macros does;
34 # - the compiler can check the type used for arguments;
35 # - the compiler can decide wether it is best to inline them or not;
36 # - they are better handled by text editors, mainly for indentation but
37 # also because there is no more '\' at end of lines;
38 # - generaly, error message from the compiler are a lot clearer.
40 # As opposed to simple static functions, there is a strong benefit because
41 # they can access the local variables of the function in which they are
42 # defined without needing extra arguments that may get complicated.
44 # These added values are important for developpement because they help keep
45 # code simple (and so maintainable and with lower bug risk).
47 # Because this script convert them to macros (only if 'configure' detected
48 # that the compiler does not support nested functions, see the macro
49 # WM_PROG_CC_NESTEDFUNC), there are a few constraints when writing such
50 # nested functions in the code:
52 # - you cannot use the function's address (example: callback), but that
53 # would be a bad idea anyway (in best case there's a penalty issue, in
54 # worst case it can crash the program);
56 # - you should be careful on what you're doing with the arguments of the
57 # function, otherwise the macro's side effects will re-appear;
59 # - you may need some extra '{}' when calling the function in an
60 # if/for/while/... construct, because of the difference between a function
61 # call and the macro that will be replaced (the macro will contain its own
62 # pair of '{}' which will be followed by the ';' you use for the function
63 # call;
65 # - the prototype of the function must be on a single line, it must not
66 # spread across multiple lines or replace will fail;
68 # - you should follow the project's coding style, as hacky stuff may
69 # make the script generate crap. And you don't want that to happen.
71 ###########################################################################
73 # Please note that this script is writen in sh+awk on purpose: this script
74 # is gonna be run on the machine of the person who is trying to compile
75 # WindowMaker, and as such we cannot be sure to find any scripting language
76 # in a known version and that works (python/ruby/tcl/perl/php/you-name-it).
78 # So for portability, we stick to the same sh+awk constraint as Autotools
79 # to limit the problem, see for example:
80 # http://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.69/html_node/Portable-Shell.html
82 ###########################################################################
84 # Report an error on stderr and exit with status 1 to tell make could not work
85 arg_error() {
86 echo "$0: $@" >&2
87 exit 1
90 # print help and exit with success status
91 print_help() {
92 echo "$0: convert nested functions into macros in C source"
93 echo "Usage: $0 [options...] input_file"
94 echo "valid options are:"
95 echo " -f name : add 'name' to the list of function to process"
96 echo " -o file : set output file"
97 exit 0
100 # Extract command line arguments
101 while [ $# -gt 0 ]; do
102 case $1 in
104 shift
105 echo "$1" | grep -q '^[A-Z_a-z][A-Z_a-z0-9]*$' || arg_error "function name \"$1\" is not valid"
106 function_list="$function_list $1"
109 -h|-help|--help) print_help ;;
110 -o) shift ; output_file="$1" ;;
111 -*) arg_error "unknow option '$1'" ;;
114 [ "x$input_file" = "x" ] || arg_error "only 1 input file can be specified, not \"$input_file\" and \"$1\""
115 input_file="$1"
117 esac
118 shift
119 done
121 # Check consistency of command-line
122 [ "x$input_file" = "x" ] && arg_error "no source file given"
123 [ "x$function_list" = "x" ] && arg_error "no function name were given, nothing to do"
124 [ "x$output_file" = "x" ] && arg_error "no output file name specified"
126 [ -r "$input_file" ] || arg_error "source file \"$input_file\" is not readable"
128 # Declare a function that takes care of converting the function code into a
129 # macro definition. All the code is considered part of the C function's body
130 # until we have matched the right number of {} pairs
131 awk_function_handler='
132 function replace_definition(func_name)
134 # Isolate the list of arguments from the rest of the line
135 # This code could be updated to handle arg list over multiple lines, but
136 # it would add unnecessary complexity because a function that big should
137 # probably be global static
138 arg_start = index($0, "(");
139 arg_end = index($0, ")");
140 argsubstr = substr($0, arg_start + 1, arg_end - arg_start - 1);
141 remain = substr($0, arg_end);
143 $0 = "#define " func_name "("
145 # Remove the types from the list of arguments
146 split(argsubstr, arglist, /,/);
147 separator = "";
148 for (i = 1; i <= length(arglist); i++) {
149 argname = substr(arglist[i], match(arglist[i], /[A-Z_a-z][A-Z_a-z0-9]*$/));
150 $0 = $0 separator argname;
151 separator = ", ";
153 delete arglist;
154 $0 = $0 remain;
156 # Count the number of pairs of {} and take next line until we get our matching count
157 is_first_line = 1;
158 nb_pair = 0;
159 while (1) {
160 # Count the opening braces
161 split($0, dummy, /\{/);
162 nb_pair = nb_pair + (length(dummy) - 1);
163 delete dummy;
165 # Count the closing braces
166 split($0, dummy, /\}/);
167 nb_pair = nb_pair - (length(dummy) - 1);
168 delete dummy;
170 # If we found the end of the function, stop now
171 if (nb_pair <= 0 && !is_first_line) {
172 # Note that we count on awk that is always executing the match-all
173 # pattern to print the current line in the $0 pattern
174 break;
177 # Otherwise, print current line with the macro continuation mark and grab
178 # next line to process it
179 $0 = $0 " \\";
180 print;
181 getline;
182 is_first_line = 0;
185 # We mark the current macro as defined so it can be undefined at the end
186 func_defined[func_name] = 1;
189 # Build the list of awk pattern matching for each function:
190 # The regular expression matches function definition by the name of the function
191 # that must be preceeded by at least one keyword (likely the return type), but
192 # nothing like a math operator or other esoteric sign
193 for function in $function_list ; do
194 awk_function_handler="$awk_function_handler
195 /^[\\t ]*([A-Za-z][A-Za-z_0-9]*[\\t ])+${function}[\\t ]*\\(/ {
196 replace_definition(\"${function}\");
198 done
200 # Finishing, undefine the macro at the most appropriate place we can easily
201 # guess
202 awk_function_handler="$awk_function_handler
203 /^\\}/ {
204 # If we are at the end of a function definition, undefine all macros that
205 # have been defined so far
206 for (func_name in func_defined) {
207 print \"#undef \" func_name;
208 delete func_defined[func_name];
211 # Print all other lines as-is
212 { print }
215 # Find the specified functions and transform them into macros
216 awk "$awk_function_handler" < "$input_file" > "$output_file"