chiark / gitweb /
pwx/last_mutual_commits.csv: Add last mutual before branching out v238-stable
[elogind.git] / pwx_local / check_tree.pl
1 #!/usr/bin/perl -w
2
3 # ================================================================
4 # ===        ==> --------     HISTORY      -------- <==        ===
5 # ================================================================
6 #
7 # Version  Date        Maintainer      Changes, Additions, Fixes
8 # 0.0.1    2017-04-08  sed, PrydeWorX  First basic design
9 # ... a lot of undocumented and partly lost development ...
10 # 0.8.0    2018-02-23  sed, PrydeWorX  2nd gen rewritten due to loss of the original thanks to stupidity.
11 # 0.8.1    2018-03-05  sed, PrydeWorX  Fixed all outstanding issues and optimized rewriting of shell files.
12 # 0.8.2    2018-03-06  sed, PrydeWorX  Added checks for elogind_*() function call removals.
13 # 0.8.3    2018-03-08  sed, PrydeWorX  Handle systemd-logind <=> elogind renames. Do not allow moving of
14 #                                        commented out includes under out elogind block.
15 # 0.8.4    2018-03-09  sed, PrydeWorX  Added handling of .gperf and .xml files.
16 # 0.8.5    2018-03-13  sed, PrydeWorX  Added possibility to (manualy) check root files and enhanced the
17 #                                        handling of shell masks and unmasks.
18 # 0.8.6    2018-03-16  sed, PrydeWorX  Enhanced mask block handling and added handling of .sym files.
19 # 0.8.7    2018-04-20  sed, PrydeWorX  Add [un]preparation for XML files.
20 # 0.8.8    2018-05-08  sed, PrydeWorX  Use Git::Wrapper to checkout the wanted commit in the upstream tree.
21 # 0.8.9    2018-05-09  sed, PrydeWorX  Add new option --create to create non-existing files. Needs --file.
22 #                                      + Add new option --stay to to not reset back from --commit.
23 # 0.9.0    2018-05-15  sed, PrydeWorX  Do not prefix mask block content in XML file patch hunks with a '# '.
24 # 0.9.1    2018-05-17  sed, PrydeWorX  Replace the source in creation patches with /dev/null.
25 #                                      + Remember mask starts and elses in hunks, so the resulting patches
26 #                                        can be reworked without ignoring changes in useless hunks.
27 # 0.9.2    2018-05-24  sed, PrydeWorX  Enhance the final processing of shell and xml files and their patches
28 #                                        by remembering mask changes that get pruned from the hunks.
29 # 0.9.3    2018-05-25  sed, PrydeWorX  Made check_musl() and check_name_reverts() safer. Further policy.in
30 #                                        consist of XML code, and are now handled by (un)prepare_xml().
31 # 0.9.4    2018-05-29  sed, PrydeWorX  Fixed a bug that caused #else to not be unremoved in __GLIBC__ blocks.
32 #                                      + The word "systemd" is no longer changed to "elogind", if it was
33 #                                        found in a comment block that is added by the patch.
34 #                                      + Added missing detection of mask else switches in prune_hunk().
35 #                                      + Move move upstream appends from after our mask blocks up before
36 #                                        found #else switches.
37 # 0.9.5    2018-05-30  sed, PrydeWorX  Do not allow diff to move name reverts into a mask block.
38 #                                      + Do not replace double dashes in XML comments, that are either the
39 #                                        comment start or end.
40 #
41 # ========================
42 # === Little TODO list ===
43 # ========================
44 #
45 use strict;
46 use warnings;
47 use Cwd qw(getcwd abs_path);
48 use File::Basename;
49 use File::Find;
50 use Git::Wrapper;
51 use Readonly;
52 use Try::Tiny;
53
54 # ================================================================
55 # ===        ==> ------ Help Text and Version ----- <==        ===
56 # ================================================================
57 Readonly my $VERSION     => "0.9.5"; ## Please keep this current!
58 Readonly my $VERSMIN     => "-" x length($VERSION);
59 Readonly my $PROGDIR     => dirname($0);
60 Readonly my $PROGNAME    => basename($0);
61 Readonly my $WORKDIR     => getcwd();
62 Readonly my $USAGE_SHORT => "$PROGNAME <--help|[OPTIONS] <path to upstream tree>>";
63 Readonly my $USAGE_LONG  => qq#
64 elogind git tree checker V$VERSION
65 --------------------------$VERSMIN
66
67     Check the current tree, from where this program is called, against an
68     upstream tree for changes, and generate a patchset out of the differences,
69     that does not interfere with elogind development markings.
70
71 Usage :
72 -------
73   $USAGE_SHORT
74
75   The path to the upstream tree should have the same layout as the place from
76   where this program is called. It is best to call this program from the
77   elogind root path and use a systemd upstream root path for comparison.
78
79 Options :
80 ---------
81     -c|--commit <refid> | Check out <refid> in the upstream tree.
82        --create         | Needs --file. If the file does not exist, create it.
83     -f|--file   <path>  | Do not search recursively, check only <path>.
84                         | For the check of multiple files, you can either
85                         | specify -f multiple times, or concatenate paths with
86                         | a comma, or mix both methods.
87     -h|--help           | Print this help text and exit.
88        --stay           | Needs --commit. Do not reset to the current commit,
89                         | stay with the wanted commit.
90 #;
91
92 # ================================================================
93 # ===        ==> -------- Global variables -------- <==        ===
94 # ================================================================
95 my $do_create       = 0;  ## If set to 1, a non-existing file is created.
96 my $do_stay         = 0;  ## If set to 1, the previous commit isn't restored on exit.
97 my $file_fmt        = ""; ## Format string built from the longest file name in generate_file_list().
98 my $have_wanted     = 0;  ## Helper for finding relevant files (see wanted())
99 my %hToCreate       = (); ## The keys are files that do not exist and shall be created.
100 my %hWanted         = (); ## Helper hash for finding relevant files (see wanted())
101 my $in_else_block   = 0;  ## Set to 1 if we switched from mask/unmask to 'else'.
102 my $in_mask_block   = 0;  ## Set to 1 if we entered an elogind mask block
103 my $in_insert_block = 0;  ## Set to 1 if we entered an elogind addition block
104 my $main_result     = 1;  ## Used for parse_args() only, as simple $result is local everywhere.
105 my @only_here       = (); ## List of files that do not exist in $upstream_path.
106 my $previous_commit = ""; ## Store current upstream state, so we can revert afterwards.
107 my $show_help       = 0;
108 my @source_files    = (); ## Final file list to process, generated in in generate_file_list().
109 my $upstream_path   = "";
110 my $wanted_commit   = "";
111 my @wanted_files    = (); ## User given file list (if any) to limit generate_file_list()
112
113 # ================================================================
114 # ===        ==> ------- MAIN DATA STRUCTURES ------ <==       ===
115 # ================================================================
116 my %hFile = (); ## Main data structure to manage a complete compare of two files. (See: build_hFile() )
117                 ## Note: %hFile is used globaly for each file that is processed.
118                 ## The structure is:
119                 ## ( count  : Number of hunks stored
120                 ##   create : Set to 1 if this is a new file to be created, 0 otherwise.
121                 ##   hunks  : Arrayref with the Hunks, stored in %hHunk instances
122                 ##   output : Arrayref with the lines of the final patch
123                 ##   part   : local relative file path
124                 ##   patch  : PROGDIR/patches/<part>.patch (With / replaced by _ in <part>)
125                 ##   pwxfile: Set to 1 if it is a prepared shell file (See prepare_shell())
126                 ##   source : WORKDIR/<part>
127                 ##   target : UPSTREAM/<part>
128                 ## )
129 my $hHunk = {}; ## Secondary data structure to describe one diff hunk.            (See: build_hHunk() )
130                 ## Note: $hHunk is used globally for each Hunk that is processed and points to the
131                 ##       current $hFile{hunks}[] instance.
132                 ## The structure is:
133                 ## { count        : Number of lines in {lines}
134                 ##   idx          : Index of this hunk in %hFile{hunks}
135                 ##   lines        : list of the lines themselves.
136                 ##   masked_end   : 1 if this hunk ends in a masked block.
137                 ##   masked_start : 1 if the previous hunk ends in a masked block.
138                 ##   src_start    : line number this hunk starts in the source file.
139                 ##   tgt_start    : line number this hunk becomes in the target file.
140                 ##   useful       : 1 if the hunk is transported, 0 if it is to be omitted.
141                 ## }
142 my %hIncs  = ();## Hash for remembered includes over different hunks.
143                 ## Note: Only counted in the first step, actions are only in the second step.
144                 ## The structure is:
145                 ## { include => {
146                 ##     applied : Set to 1 once check_includes() has applied any rules on it.
147                 ##     elogind = { hunkid : Hunk id ; Position where a "needed by elogin" include is
148                 ##                 lineid : Line id ; wanted to be removed by the patch.
149                 ##     }
150                 ##     insert  = { elogind : Set to 1 if the insert was found under elogind special includes.
151                 ##                 hunkid  : Hunk id ; Position where a commented out include is
152                 ##                 lineid  : Line id ; wanted to be removed by the patch.
153                 ##                 spliceme: Set to 1 if this insert is later to be spliced.
154                 ##                 sysinc  : Set to 1 if it is <include>, 0 otherwise.
155                 ##     }
156                 ##     remove  = { hunkid : Hunk id ; Position where a new include is wanted to be
157                 ##                 lineid : Line id ; added by the patch.
158                 ##                 sysinc : Set to 1 if it is <include>, 0 otherwise.
159                 ##     }
160                 ## } }
161 my @lFails = ();## List of failed hunks. These are worth noting down in an extra structure, as these
162                 ## mean that something is fishy about a file, like elogind mask starts within masks.
163                 ## The structure is:
164                 ## ( { hunk : the failed hunk for later printing
165                 ##     msg  : a message set by the function that failed
166                 ##     part : local relative file part
167                 ##   }
168                 ## )
169
170 # ================================================================
171 # ===        ==> --------  Function list   -------- <==        ===
172 # ================================================================
173
174 sub build_hFile;        ## Inititializes and fills %hFile.
175 sub build_hHunk;        ## Adds a new $hFile{hunks}[] instance.
176 sub build_output;       ## Writes $hFile{output} from all useful $hFile{hunks}.
177 sub check_blanks;       ## Check that useful blank line additions aren't misplaced.
178 sub check_comments;     ## Check comments we added for elogind specific information.
179 sub check_debug;        ## Check for debug constructs
180 sub check_func_removes; ## Check for attempts to remove elogind_*() special function calls.
181 sub check_includes;     ## performe necessary actions with the help of %hIncs (built by read_includes())
182 sub check_masks;        ## Check for elogind preprocessor masks
183 sub check_musl;         ## Check for musl_libc compatibility blocks
184 sub check_name_reverts; ## Check for attempts to revert 'elogind' to 'systemd'
185 sub check_sym_lines;    ## Check for attempts to uncomment unsupported API functions in .sym files.
186 sub checkout_upstream;  ## Checkout the given refid on $upstream_path
187 sub clean_hFile;        ## Completely clean up the current %hFile data structure.
188 sub diff_hFile;         ## Generates the diff and builds $hFile{hunks} if needed.
189 sub generate_file_list; ## Find all relevant files and store them in @wanted_files
190 sub get_hunk_head;      ## Generate the "@@ -xx,n +yy,m @@" hunk header line out of $hHunk.
191 sub hunk_failed;        ## Generates a new @lFails entry and terminates the progress line.
192 sub hunk_is_useful;     ## Prunes the hunk and checks whether it stil does anything
193 sub is_insert_end;      ## Return 1 if the argument consists of any insertion end
194 sub is_insert_start;    ## Return 1 if the argument consists of any insertion start
195 sub is_mask_else;       ## Return 1 if the argument consists of any mask else
196 sub is_mask_end;        ## Return 1 if the argument consists of any mask end
197 sub is_mask_start;      ## Return 1 if the argument consists of any mask start
198 sub parse_args;         ## Parse ARGV for the options we support
199 sub prepare_shell;      ## Prepare shell (and meson) files for our processing
200 sub prepare_xml;        ## Prepare XML files for our processing (Unmask double dashes in comments)
201 sub prune_hunk;         ## remove unneeded prefix and postfix lines.
202 sub read_includes;      ## map include changes
203 sub splice_includes;    ## Splice all includes that were marked for splicing
204 sub unprepare_shell;    ## Unprepare shell (and meson) files after our processing
205 sub unprepare_xml;      ## Unprepare XML files after our processing (Mask double dashes in comments)
206 sub wanted;             ## Callback function for File::Find
207
208 # ================================================================
209 # ===        ==> --------    Prechecks     -------- <==        ==
210 # ================================================================
211
212 $main_result = parse_args(@ARGV);
213 ( (!$main_result)                 ## Note: Error or --help given, then exit.
214         or ( $show_help and print "$USAGE_LONG" ) )
215         and exit(!$main_result);
216 length($wanted_commit)
217         and (checkout_upstream($wanted_commit) ## Note: Does nothing if $wanted_commit is already checked out.
218                 or exit 1);
219 generate_file_list or exit 1;     ## Note: @wanted_files is heeded.
220
221 # ================================================================
222 # ===        ==> -------- = MAIN PROGRAM = -------- <==        ===
223 # ================================================================
224
225 for my $file_part (@source_files) {
226         printf("$file_fmt: ", $file_part);
227
228         build_hFile($file_part) or next;
229         diff_hFile or next;
230
231         # Reset global state helpers
232         $in_else_block   = 0;
233         $in_mask_block   = 0;
234         $in_insert_block = 0;
235
236         # Empty the include manipulation hash
237         %hIncs = ();
238
239         # ---------------------------------------------------------------------
240         # --- Go through all hunks and check them against our various rules ---
241         # ---------------------------------------------------------------------
242         for (my $pos = 0; $pos < $hFile{count}; ++$pos) {
243                 $hHunk = $hFile{hunks}[$pos]; ## Global shortcut
244
245                 # === 1) Check for elogind masks 1 (normal source code) ===========
246                 check_masks and hunk_is_useful and prune_hunk or next;
247
248                 # === 2) Check for elogind masks 2 (symbol files) =================
249                 check_sym_lines and hunk_is_useful and prune_hunk or next;
250
251                 # === 3) Check for musl_libc compatibility blocks =================
252                 check_musl and hunk_is_useful and prune_hunk or next;
253
254                 # === 4) Check for debug constructs ===============================
255                 check_debug and hunk_is_useful and prune_hunk or next;
256
257                 # === 5) Check for elogind extra comments and information =========
258                 check_comments and hunk_is_useful and prune_hunk or next;
259
260                 # === 6) Check for useful blank line additions ====================
261                 check_blanks and hunk_is_useful and prune_hunk or next;
262
263                 # === 7) Check for 'elogind' => 'systemd' reverts =================
264                 check_name_reverts and hunk_is_useful and prune_hunk or next;
265
266                 # === 8) Check for elogind_*() function removals ==================
267                 check_func_removes and hunk_is_useful and prune_hunk or next;
268
269                 #  ===> IMPORTANT: From here on no more pruning, lines must *NOT* change any more! <===
270
271                 # === 9) Analyze includes and note their appearance in $hIncs =====
272                 read_includes; ## Never fails, doesn't change anything.
273
274         } ## End of first hunk loop
275
276         # ---------------------------------------------------------------------
277         # --- Make sure saved include data is sane                          ---
278         # ---------------------------------------------------------------------
279         for my $inc (keys %hIncs) {
280                 $hIncs{$inc}{applied} = 0;
281                 defined ($hIncs{$inc}{elogind})
282                         or $hIncs{$inc}{elogind} = {               hunkid => -1, lineid => -1 };
283                 defined ($hIncs{$inc}{insert})
284                         or $hIncs{$inc}{insert}  = { elogind => 0, hunkid => -1, lineid => -1, spliceme => 0, sysinc => 0 };
285                 defined ($hIncs{$inc}{remove})
286                         or $hIncs{$inc}{remove}  = {               hunkid => -1, lineid => -1,                sysinc => 0 };
287         }
288
289         # ---------------------------------------------------------------------
290         # --- Go through all hunks and apply remaining changes and checks   ---
291         # ---------------------------------------------------------------------
292         for (my $pos = 0; $pos < $hFile{count}; ++$pos) {
293                 $hHunk = $hFile{hunks}[$pos]; ## Global shortcut
294
295                 # (pre -> early out)
296                 hunk_is_useful or next;
297
298                 # === 1) Apply what we learned about changed includes =============
299                 check_includes and hunk_is_useful or next;
300
301         } ## End of second hunk loop
302
303         # ---------------------------------------------------------------------
304         # --- Splice all include insertions that are marked for splicing    ---
305         # ---------------------------------------------------------------------
306         splice_includes;
307
308         # ---------------------------------------------------------------------
309         # --- Go through all hunks for a last prune and check               ---
310         # ---------------------------------------------------------------------
311         my $have_hunk = 0;
312         for (my $pos = 0; $pos < $hFile{count}; ++$pos) {
313                 $hHunk = $hFile{hunks}[$pos]; ## Global shortcut
314
315                 # (pre -> early out)
316                 hunk_is_useful or next;
317
318                 prune_hunk and ++$have_hunk;
319         }
320
321         # If we have at least 1 useful hunk, create the output and tell the user what we've got.
322         $have_hunk
323                 and build_output # (Always returns 1)
324                 and printf("%d Hunk%s\n", $have_hunk, $have_hunk > 1 ? "s" : "")
325                  or print("clean\n");
326
327         # Shell and meson files must be unprepared. See unprepare_shell()
328         $hFile{pwxfile} and ( unprepare_shell or unprepare_xml );
329
330         # Now skip the writing if there are no hunks
331         $have_hunk or next;
332
333         # That's it, write the file and be done!
334         if (open(my $fOut, ">", $hFile{patch})) {
335                 for my $line (@{$hFile{output}}) {
336                         print $fOut "$line\n";
337                 }
338                 close($fOut);
339         } else {
340                 printf("ERROR: %s could not be opened for writing!\n%s\n",
341                         $hFile{patch}, $!);
342                 die("Please fix this first!");
343         }
344 } ## End of main loop
345
346 # ===========================
347 # === END OF MAIN PROGRAM ===
348 # ===========================
349
350 # ================================================================
351 # ===        ==> --------     Cleanup      -------- <==        ===
352 # ================================================================
353
354 END {
355         # -------------------------------------------------------------------------
356         # --- Print out the list of files that only exist here and not upstream ---
357         # -------------------------------------------------------------------------
358         if (scalar @only_here) {
359                 my $count = scalar @only_here;
360                 my $fmt   = sprintf("%%d %d: %%s\n", length("$count"));
361         
362                 printf("\n%d file%s only found in $WORKDIR:\n", $count, $count > 1 ? "s": "");
363         
364                 for (my $i = 0; $i < $count; ++$i) {
365                         printf($fmt, $i + 1, $only_here[$i]);
366                 }
367         }
368         
369         # -------------------------------------------------------------------------
370         # --- Print out the list of failed hunks -> bug in hunk or program?     ---
371         # -------------------------------------------------------------------------
372         if (scalar @lFails) {
373                 my $count = scalar @lFails;
374         
375                 printf("\n%d file%s %s at least one fishy hunk:\n", $count,
376                        $count > 1 ? "s" : "", $count > 1 ? "have" : "has");
377         
378                 for (my $i = 0; $i < $count; ++$i) {
379                         print "=== $lFails[$i]{part} ===\n";
380                         print " => $lFails[$i]{msg} <=\n";
381                         print "---------------------------\n";
382                         print "$_\n" foreach(@{$lFails[$i]{hunk}});
383                 }
384         }
385
386         $do_stay or length($previous_commit) and checkout_upstream($previous_commit);
387 }
388
389
390 # ================================================================
391 # ===        ==> ---- Function Implementations ---- <==        ===
392 # ================================================================
393
394 # -----------------------------------------------------------------------
395 # --- Inititializes and fills %hFile. Old values are discarded.       ---
396 # --- Adds files, that do not exist upstream, to @only_here.          ---
397 # --- Returns 1 on success, 0 otherwise.                              ---
398 # -----------------------------------------------------------------------
399 sub build_hFile {
400         my ($part) = @_;
401
402         defined($part) and length($part) or print("ERROR\n") and die("build_hfile: part is empty ???");
403
404         # Is this a new file?
405         my $isNew = defined($hToCreate{$part}) ? 1 : 0;
406
407         # We only prefixed './' to unify things. Now it is no longer needed:
408         $part =~ s,^\./,,;
409
410         # Pre: erase current $hFile, as that is what is expected.
411         clean_hFile();
412
413         # Check the target file
414         my $tgt = "$upstream_path/$part";
415         $tgt =~ s/elogind/systemd/g;
416         $tgt =~ s/\.pwx$//;
417         -f $tgt or push(@only_here, $part)
418                 and print "only here\n"
419                 and return 0;
420
421         # Build the patch name
422         my $patch = $part;
423         $patch =~ s/\//_/g;
424         
425         # Build the central data structure.
426         %hFile = (
427                 count  => 0,
428                 create => $isNew,
429                 hunks  => [ ],
430                 output => [ ],
431                 part   => "$part",
432                 patch  => "$PROGDIR/patches/${patch}.patch",
433                 pwxfile=> 0,
434                 source => "$WORKDIR/$part",
435                 target => "$tgt"
436         );
437
438         return 1;
439 }
440
441 # -----------------------------------------------------------------------
442 # --- Build a new $hHunk instance and add it to $hFile{hunks}         ---
443 # -----------------------------------------------------------------------
444 sub build_hHunk {
445         my ($head, @lHunk) = @_;
446         my $pos = $hFile{count}++;
447
448         # The first line must be the hunk positional and size data.
449         # Example: @@ -136,6 +136,8 @@
450         # That is @@ -<source line>,<source length> +<target line>,<target length> @@
451         if ( $head =~ m/^@@ -(\d+),\d+ \+(\d+),\d+ @@/ ) {
452                 %{$hFile{hunks}[$pos]} = (
453                         count        => 0,
454                         idx          => $pos,
455                         masked_end   => 0,
456                         masked_start => 0,
457                         offset       => 0,
458                         src_start    => $1,
459                         tgt_start    => $2,
460                         useful       => 1
461                 );
462
463                 # We need to chomp the lines:
464                 for my $line (@lHunk) {
465                         defined($line) or next;
466                         chomp $line;
467                         push @{$hFile{hunks}[$pos]{lines}}, $line;
468                         $hFile{hunks}[$pos]{count}++;
469                 }
470                 return 1;
471         }
472
473         print "Illegal hunk no $hFile{count}\n(Head: \"$head\")\n";
474
475         return 0;
476 }
477
478 # -----------------------------------------------------------------------
479 # --- Writes $hFile{output} from all useful $hFile{hunks}.            ---
480 # --- Important: No more checks, just do it!                          ---
481 # -----------------------------------------------------------------------
482 sub build_output {
483
484         my $offset = 0; ## Count building up target offsets
485
486         for (my $pos = 0; $pos < $hFile{count}; ++$pos) {
487                 $hHunk = $hFile{hunks}[$pos]; ## Global shortcut
488
489                 # The useless are to be skipped, but we need the hunks masked_end
490                 if ($hHunk->{useful}) {
491
492                         # --- Note down the relevant starting mask status ---
493                         # ---------------------------------------------------
494                         $hFile{pwxfile} and push(@{$hFile{output}}, "# masked_start " . $hHunk->{masked_start});
495
496                         # --- Add the header line ---------------------------
497                         # ---------------------------------------------------
498                         push(@{$hFile{output}}, get_hunk_head(\$offset));
499
500                         # --- Add the hunk lines ----------------------------
501                         # ---------------------------------------------------
502                         for my $line (@{$hHunk->{lines}}) {
503                                 push(@{$hFile{output}}, $line);
504                         }
505                 }
506
507                 # --- Note down the relevant ending mask status -----
508                 # ---------------------------------------------------
509                 $hFile{pwxfile} and push(@{$hFile{output}}, "# masked_end " . $hHunk->{masked_end});
510
511         } ## End of walking the hunks
512
513         return 1;
514 }
515
516 # -----------------------------------------------------------------------
517 # --- Check that useful blank line additions aren't misplaced.        ---
518 # ---- Note: Sometimes the masks aren't placed correctly, and the diff---
519 # ----       wants to add a missing blank line. As it tried to remove ---
520 # ----       our mask first, it'll be added after. That's fine for    ---
521 # ----       #endif, but not for #if 0.                               ---
522 # -----------------------------------------------------------------------
523 sub check_blanks {
524
525         # Early exits:
526         defined($hHunk) or return 0;
527         $hHunk->{useful} or return 0;
528
529         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
530                 my $line = \$hHunk->{lines}[$i]; ## Shortcut
531
532                 if ( ($$line =~ m/^\+\s*$/)
533                   && ($i > 0)
534                   && ( (is_mask_start(  $hHunk->{lines}[$i-1])
535                      || is_insert_start($hHunk->{lines}[$i-1])) ) ) {
536                         # Simply swap the lines
537                         my $tmp = $$line;
538                         $$line = $hHunk->{lines}[$i-1];
539                         $hHunk->{lines}[$i-1] = $tmp;
540                         next;
541                 }
542         }
543
544         return 1;
545 }
546
547 # -----------------------------------------------------------------------
548 # --- Check comments we added for elogind specific information.       ---
549 # --- These are all comments, and can be both single and multi line.  ---
550 # -----------------------------------------------------------------------
551 sub check_comments {
552
553         # Early exits:
554         defined($hHunk) or return 0;
555         $hHunk->{useful} or return 0;
556
557         my $in_comment_block = 0;
558
559         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
560                 my $line = \$hHunk->{lines}[$i]; ## Shortcut
561
562                 # Check for comment block start
563                 # -----------------------------
564                 if ($$line =~ m,^-\s*(/[*]+|/[/]+).*elogind,) {
565
566                         # Sanity check:
567                         $in_comment_block
568                                 and return hunk_failed("check_comments: Comment block start found in comment block!");
569
570                         substr($$line, 0, 1) = " ";
571
572                         # Only start the comment block if this is really a multiline comment
573                         (!($$line =~ m,\*/[^/]*$,) )
574                                 and $in_comment_block = 1;
575
576                         next;
577                 }
578
579                 # Check for comment block end
580                 # -----------------------------
581                 if ($in_comment_block && ($$line =~ m,^-.*\*/\s*$,)) {
582                         substr($$line, 0, 1) = " ";
583                         $in_comment_block = 0;
584                         next;
585                 }
586
587                 # Check for comment block line
588                 # -----------------------------
589                 if ($in_comment_block && ($$line =~ m,^-,)) {
590                         # Note: We do not check for anything else, as empty lines must be allowed.
591                         substr($$line, 0, 1) = " ";
592                         next;
593                 }
594
595                 # If none of the above applied, the comment block is over.
596                 $in_comment_block = 0;
597         }
598
599         return 1;
600 }
601
602 # -----------------------------------------------------------------------
603 # --- Check for debug constructs                                      ---
604 # --- Rules: ENABLE_DEBUG_ELOGIND must be taken like #if 1, *but*     ---
605 # ---        here an #else is valid and must stay fully.              ---
606 # ---        Further there might be multiline calls to                ---
607 # ---        log_debug_elogind() that must not be removed either.     ---
608 # -----------------------------------------------------------------------
609 sub check_debug {
610
611         # Early exits:
612         defined($hHunk) or return 0;
613         $hHunk->{useful} or return 0;
614
615         # Count non-elogind block #ifs. This is needed, so normal
616         # #if/#else/#/endif constructs can be put inside both the
617         # debug and the release block.
618         my $regular_ifs   = 0;
619         my $is_debug_func = 0; ## Special for log_debug_elogind()
620
621         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
622                 my $line = \$hHunk->{lines}[$i]; ## Shortcut
623
624                 # Entering a debug construct block
625                 # ---------------------------------------
626                 if ( $$line =~ m/^-#if.+ENABLE_DEBUG_ELOGIND/ ) {
627                         ## Note: Here it is perfectly fine to be in an elogind mask or insert block.
628                         substr($$line, 0, 1) = " "; ## Remove '-'
629                         $in_insert_block++; ## Increase instead of setting this to 1.
630                         next;
631                 }
632
633                 # Count regular #if
634                 $$line =~ m/^-#if/ and $in_insert_block and ++$regular_ifs;
635
636                 # Switching to the release variant.
637                 # ---------------------------------------
638                 if ( ($$line =~ m/^-#else/ ) && $in_insert_block && !$regular_ifs) {
639                         substr($$line, 0, 1) = " "; ## Remove '-'
640                         $in_else_block++; ## Increase instead of setting this to 1.
641                         next;
642                 }
643
644                 # Ending a debug construct block
645                 # ---------------------------------------
646                 if ( $$line =~ m,^-#endif\s*///?.*ENABLE_DEBUG_, ) {
647                         (!$in_insert_block)
648                                 and return hunk_failed("check_debug: #endif // ENABLE_DEBUG_* found outside any debug construct");
649                         substr($$line, 0, 1) = " "; ## Remove '-'
650                         $in_insert_block--; ## Decrease instead of setting to 0. This allows such
651                         $in_else_block--;   ## blocks to reside in regular elogind mask/insert blocks.
652                         next;
653                 }
654
655                 # End regular #if
656                 $$line =~ m/^-#endif/ and $in_insert_block and --$regular_ifs;
657
658                 # Check for log_debug_elogind()
659                 # ---------------------------------------
660                 if ($$line =~ m/^-.*log_debug_elogind\s*\(/ ) {
661                         substr($$line, 0, 1) = " "; ## Remove '-'
662                         $$line =~ m/\)\s*;/ or ++$is_debug_func;
663                         next;
664                 }
665
666                 # Remove '-' prefixes in all lines within the debug construct block
667                 # -------------------------------------------------------------------
668                 if ( ($$line =~ m,^-,) && ($in_insert_block || $is_debug_func) ) {
669                         substr($$line, 0, 1) = " "; ## Remove '-'
670                         # Note: Everything in *any* insert block must not be removed anyway.
671                 }
672
673                 # Check for the end of a multiline log_debug_elogind() call
674                 # ---------------------------------------------------------
675                 $is_debug_func and $$line =~ m/\)\s*;/ and --$is_debug_func;
676
677         } ## End of looping lines
678
679         return 1;
680 }
681
682 # -----------------------------------------------------------------------
683 # --- Check for attempts to remove elogind_*() special function calls. --
684 # --- We have some special functions, needed only by elogind.         ---
685 # --- One of the most important ones is elogind_set_program_name(),   ---
686 # --- which has an important role in musl_libc compatibility.         ---
687 # --- These calls must not be removed of course.                      ---
688 # -----------------------------------------------------------------------
689 sub check_func_removes  {
690
691         # Early exits:
692         defined($hHunk) or return 1;
693         $hHunk->{useful} or return 1;
694
695         # Needed for multi-line calls
696         my $is_func_call = 0;
697
698         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
699                 my $line = \$hHunk->{lines}[$i]; ## Shortcut
700
701                 # Check for elogind_*() call
702                 # -------------------------------------------------------------------
703                 if ($$line =~ m/^-.*elogind_\S+\s*\(/ ) {
704                         substr($$line, 0, 1) = " "; ## Remove '-'
705                         $$line =~ m/\)\s*;/ or ++$is_func_call;
706                         next;
707                 }
708
709                 # Remove '-' prefixes in all lines that belong to an elogind_*() call
710                 # -------------------------------------------------------------------
711                 if ( ($$line =~ m,^-,) && $is_func_call ) {
712                         substr($$line, 0, 1) = " "; ## Remove '-'
713                 }
714
715                 # Check for the end of a multiline elogind_*() call
716                 # -------------------------------------------------------------------
717                 $is_func_call and $$line =~ m/\)\s*;/ and --$is_func_call;
718         }
719
720         return 1;
721 }
722
723 # -----------------------------------------------------------------------
724 # --- Check hunk for include manipulations we must step in            ---
725 # --- This is basically read_include(), but this time we actually act ---
726 # --- on what we found.                                               ---
727 # --- Rules:                                                          ---
728 # --- 1) Removals of includes that we commented out:                  ---
729 # ---    a) If there is no insertion of this include, let it be       ---
730 # ---       removed, it seems to be obsolete.                         ---
731 # ---    b) If there is an insertion of the very same include in one  ---
732 # ---       of the surrounding lines, mark the insert for splicing    ---
733 # ---       and undo the removal.                                     ---
734 # ---    c) If there is an insertion somewhere else, let it be        ---
735 # ---       removed here, and handle the insertion:                   ---
736 # ---       - Outside a "needed by elogind" block: comment out the    ---
737 # ---         inserted include.                                       ---
738 # ---       - Inside a "needed by elogind" block: undo the removal    ---
739 # ---         there.                                                  ---
740 # --- 2) Insertions of new includes, where 1) does not apply:         ---
741 # ---    a) If the include is new, comment it out to force a later    ---
742 # ---       check. The compiler will tell us if it is needed.         ---
743 # ---    b) If the include is part of the "needed by elogind" block   ---
744 # ---       already, allow the removal there and accept the regular   ---
745 # ---       insertion here.                                           ---
746 # --- 3) Removals of includes in "needed by elogind" blocks:          ---
747 # ---    As 1) and 2) do not apply, simply undo any removal here.     ---
748 # -----------------------------------------------------------------------
749 sub check_includes {
750
751         # Early exits:
752         defined($hHunk) or return 1;
753         $hHunk->{useful} or return 1;
754
755         # We must know when "needed by elogind blocks" start
756         my $in_elogind_block = 0;
757
758         # The simple undo check will fail, if we do at least one at once.
759         # Delay the undoing of the removals until after the hunk was checked.
760         my %undos = ();
761
762         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
763                 my $line = \$hHunk->{lines}[$i]; ## Shortcut
764
765                 # === Ruleset 1 : Handling of removals of includes we commented out ===
766                 # =====================================================================
767                 if ( $$line =~ m,^-\s*//+\s*#include\s+[<"']([^>"']+)[>"'], ) {
768                         $hIncs{$1}{applied} and next; ## No double handling
769
770                         my $inc = $1;
771
772                         # Pre: Sanity check:
773                         defined($hIncs{$inc}{remove}{hunkid}) and $hIncs{$inc}{remove}{hunkid} > -1
774                          or return hunk_failed("check_includes: Unrecorded removal found!");
775
776                         # a) Check for removals of obsolete includes.
777                         $hIncs{$inc}{elogind}{hunkid} > -1        ## If no insert was found, then the include was
778                                  or $hIncs{$inc}{insert}{hunkid} > -1 ## removed by systemd devs for good.
779                                  or ( ++$hIncs{$inc}{applied} and next);
780
781                         # b) Check for a simple undo of our commenting out
782                         if ( ($hIncs{$inc}{insert}{hunkid} == $hIncs{$inc}{remove}{hunkid} )
783                           && ($hIncs{$inc}{insert}{sysinc} == $hIncs{$inc}{remove}{sysinc} ) ) {
784                                 my $ins_diff  = $hIncs{$inc}{insert}{lineid} - $hIncs{$inc}{remove}{lineid};
785                                 my $all_same  = 1;
786                                 my $direction = $ins_diff > 0 ? 1 : -1;
787
788                                 # Let's see whether there are undos between this and its addition
789                                 # in the same order, meaning there has been no resorting.
790                                 for (my $j = $direction; $all_same && (abs($j) < abs($ins_diff)); $j += $direction) {
791                                         $all_same = 0;
792
793                                         if ( ( $hHunk->{lines}[$i+$j] =~ m,^-\s*//+\s*#include\s+[<"']([^>"']+)[>"'], )
794                                           || ( $hHunk->{lines}[$i+$j] =~ m,^\+\s*#include\s+[<"']([^>"']+)[>"'], ) ) {
795
796                                                 $hIncs{$1}{insert}{hunkid} == $hIncs{$1}{remove}{hunkid}
797                                                         and $ins_diff == ($hIncs{$1}{insert}{lineid} - $hIncs{$1}{remove}{lineid})
798                                                         and $hIncs{$inc}{insert}{sysinc} == $hIncs{$inc}{remove}{sysinc}
799                                                         and $all_same = 1;
800                                         }
801                                 }
802                                 if ($all_same) {
803                                         # The insertion is right before or after the removal. That's pointless.
804                                         $undos{$hIncs{$inc}{remove}{lineid}} = 1;
805                                         $hIncs{$inc}{applied}  = 1;
806                                         $hIncs{$inc}{insert}{spliceme} = 1;
807                                         next;
808                                 }
809                         } ## end of insert and remove being in the same hunk
810
811                         # c) Move somewhere else, or change include type. Can't be anything else here.
812                         if ($hIncs{$inc}{elogind}{hunkid} > -1) {
813                                 # Just undo the removal of the elogind insert.
814                                 my $hId = $hIncs{$inc}{elogind}{hunkid};
815                                 my $lId = $hIncs{$inc}{elogind}{lineid};
816                                 substr($hFile{hunks}[$hId]{lines}[$lId], 0, 1) = " ";
817                                 $hIncs{$inc}{applied}  = 1;
818                         } elsif ( $hIncs{$inc}{insert}{elogind} ) {
819                                 # Do not move masked includes under our block.
820                                 $undos{$hIncs{$inc}{remove}{lineid}} = 1;
821                                 $hIncs{$inc}{applied}  = 1;
822                                 $hIncs{$inc}{insert}{spliceme} = 1;
823                         } else {
824                                 # Just comment out the insert.
825                                 my $hId = $hIncs{$inc}{insert}{hunkid};
826                                 my $lId = $hIncs{$inc}{insert}{lineid};
827                                 substr($hFile{hunks}[$hId]{lines}[$lId], 0, 1) = "+//";
828                                 $hIncs{$inc}{applied}  = 1;
829                         }
830
831                         next;
832                 } ## End of ruleset 1
833
834                 # === Ruleset 2 : Handling of insertions, not handled by 1          ===
835                 # =====================================================================
836                 if ( $$line =~ m,^\+\s*#include\s+[<"']([^>"']+)[>"'], ) {
837                         $hIncs{$1}{applied} and next; ## No double handling
838
839                         # Pre: Sanity check:
840                         defined($hIncs{$1}{insert}{hunkid}) and $hIncs{$1}{insert}{hunkid} > -1
841                          or return hunk_failed("check_includes: Unrecorded insertion found!");
842
843                         # Nicely enough we only have to check whether this is removed from
844                         # any elogind includes block or not.
845                         (-1 == $hIncs{$1}{elogind}{hunkid})
846                                 and substr($$line, 0, 1) = "+//"; ## comment out for later check
847                         $hIncs{$1}{applied}  = 1;
848
849                         # That's it. Cool, eh?
850
851                         next;
852                 }
853
854                 # === Ruleset 3 : Handling of "needed by elogind" blocks            ===
855                 # =====================================================================
856                 if ( $in_elogind_block
857                   && ($$line =~ m,^-\s*#include\s+[<"']([^>"']+)[>"'], ) ) {
858                         $hIncs{$1}{applied} and next; ## No double handling
859
860                         # Pre: Sanity check:
861                         defined($hIncs{$1}{elogind}{hunkid}) and $hIncs{$1}{elogind}{hunkid} > -1
862                          or return hunk_failed("check_includes: Unrecorded elogind include found!");
863
864                         # As 1 and 2 do not apply, simply undo the removal.
865                         substr($$line, 0, 1) = " ";
866                         $hIncs{$1}{applied} = 1;
867
868                         next;
869                 }
870
871                 # === Other 1 : Look for "needed by elogind" block starts           ===
872                 # =====================================================================
873                 if ($$line =~ m,^[- ]\s*//+.*needed by elogind.*,i) {
874                         $in_elogind_block = 1;
875
876                         # Never remove the block start
877                         ($$line =~ m,^-,) and substr($$line, 0, 1) = " ";
878
879                         # While we are here, we can see to it, that the additional empty
880                         # line above our marker does not get removed:
881                         ($i > 0) and ($hHunk->{lines}[$i-1] =~ m/^-\s*$/)
882                            and substr($hHunk->{lines}[$i-1], 0, 1) = " ";
883
884                         next;
885                 }
886
887                 # === Other 2 : elogind include blocks end, when the first not      ===
888                 # ===           removed EMPTY line is found                         ===
889                 # =====================================================================
890                 $in_elogind_block and ($$line =~ m,^[ +]\s*$,) and $in_elogind_block = 0;
891
892                 # === Other 3 : Undo all other removals in elogind include blocks   ===
893                 # =====================================================================
894                 $in_elogind_block and ($$line =~ m,^-,) and substr($$line, 0, 1) = " ";
895
896                 # Note: Although it looks like all rules are out the window here, all
897                 #       elogind includes that are handled above, end in a 'next', so
898                 #       those won't reach here. Actually 'Other 3' would be never
899                 #       reached with an #include line.
900
901         } ## End of looping lines
902
903         # Before we can leave, we have to neutralize the %undo lines:
904         for my $lId (keys %undos) {
905                 substr($hHunk->{lines}[$lId], 0, 1) = " ";
906         }
907
908         return 1;
909 }
910
911 # -----------------------------------------------------------------------
912 # --- Check $hHunk for elogind preprocessor masks and additions       ---
913 # --- Rules:                                                          ---
914 # --- 1) If we need an alternative for an existing systemd code block,---
915 # ---    we do it like this: (*)                                      ---
916 # ---      #if 0 /// <some comment with the word "elogind" in it>     ---
917 # ---        (... systemd code block, unaltered ...)                  ---
918 # ---        -> Any change in this block is okay.                     ---
919 # ---      #else                                                      ---
920 # ---        (... elogind alternative code ...)                       ---
921 # ---        -> Any change in this block is *NOT* okay.               ---
922 # ---      #endif // 0                                                ---
923 # --- 2) If we need an addition for elogind, we do it like this: (*)  ---
924 # ---      #if 1 /// <some comment with the word "elogind" in it>     ---
925 # ---        (... elogind additional code ...)                        ---
926 # ---        -> Any change in this block is *NOT* okay.               ---
927 # ---      #endif // 1                                                ---
928 # --- (*) : To be able to handle XML content, masking and unmasking   ---
929 # ---       can also be done with :                                   ---
930 # ---       Masking : "<!-- 0 /// <comment>" and "// 0 -->"           ---
931 # ---       Adding  : "<!-- 1 /// <comment> --> and "<!-- // 1 -->"   ---
932 # ---       Else    : "<!-- else -->"                                 ---
933 # -----------------------------------------------------------------------
934 sub check_masks {
935
936         # Early exits:
937         defined($hHunk) or die("check_masks: hHunk is undef");
938         $hHunk->{useful} or die("chec_masks: Nothing done but hHunk is useless?");
939
940         # Count non-elogind block #ifs. This is needed, so normal
941         # #if/#else/#/endif constructs can be put inside elogind mask blocks.
942         my $regular_ifs = 0;
943
944         # We have to know where an #else block ends. If upstream appends something
945         # to a block we commented out, diff adds it after the removal of our
946         # #else block. With this we can move the stuff up before the else.
947         my $else_block_start = -1;
948
949         # We have to note down mask starts. If a name revert takes place right in
950         # front of a mask start, diff will put it under the mask start, which
951         # would place it at the wrong position.
952         my $mask_block_start = -1;
953
954         # Note down how this hunk starts before first pruning
955         $hHunk->{masked_start} = $in_mask_block && !$in_else_block;
956
957         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
958                 my $line = \$hHunk->{lines}[$i]; ## Shortcut
959                 # Entering an elogind mask
960                 # ---------------------------------------
961                 if (is_mask_start($$line)) {
962                         $in_mask_block
963                                 and return hunk_failed("check_masks: Mask start found while being in a mask block!");
964                         $in_insert_block
965                                 and return hunk_failed("check_masks: Mask start found while being in an insert block!");
966                         substr($$line, 0, 1) = " "; ## Remove '-'
967                         $in_mask_block    = 1;
968                         $else_block_start = -1;
969                         $mask_block_start = $i;
970
971                         # While we are here we can check the previous line.
972                         # All masks shall be preceded by an empty line to enhance readability.
973                         # So any attempt to remove them must be stopped.
974                         ($i > 0) and ($hHunk->{lines}[$i-1] =~ m/^-\s*$/)
975                                 and substr($hHunk->{lines}[$i-1], 0, 1) = " ";
976
977                         next;
978                 }
979
980                 # Entering an elogind insert
981                 # ---------------------------------------
982                 if (is_insert_start($$line)) {
983                         $in_mask_block
984                                 and return hunk_failed("check_masks: Insert start found while being in a mask block!");
985                         $in_insert_block
986                                 and return hunk_failed("check_masks: Insert start found while being in an insert block!");
987                         substr($$line, 0, 1) = " "; ## Remove '-'
988                         $in_insert_block  = 1;
989
990                         # While we are here we can check the previous line.
991                         # All inserts shall be preceded by an empty line to enhance readability.
992                         # So any attempt to remove them must be stopped.
993                         ($i > 0) and ($hHunk->{lines}[$i-1] =~ m/^-\s*$/)
994                                 and substr($hHunk->{lines}[$i-1], 0, 1) = " ";
995
996                         next;
997                 }
998
999                 # Count regular #if
1000                 $$line =~ m/^-#if/ and $in_mask_block and ++$regular_ifs;
1001
1002                 # Switching from Mask to else.
1003                 # Note: Inserts have no #else, they make no sense.
1004                 # ---------------------------------------
1005                 if ( $in_mask_block && !$regular_ifs
1006                   && is_mask_else($$line) ) {
1007                         substr($$line, 0, 1) = " "; ## Remove '-'
1008                         $in_else_block    = 1;
1009                         $else_block_start = $i; ## Here we might insert upstream additions
1010                         $mask_block_start = -1;
1011                         next;
1012                 }
1013
1014                 # Ending a Mask block
1015                 # ---------------------------------------
1016                 if (is_mask_end($$line)) {
1017                         $in_mask_block or return hunk_failed("check_masks: #endif // 0 found outside any mask block");
1018                         substr($$line, 0, 1) = " "; ## Remove '-'
1019                         $in_mask_block    = 0;
1020                         $in_else_block    = 0;
1021                         $mask_block_start = -1;
1022                         next;
1023                 }
1024
1025                 # Ending an insert block
1026                 # ---------------------------------------
1027                 if (is_insert_end($$line)) {
1028                         $in_insert_block or return hunk_failed("check_masks: #endif // 1 found outside any insert block");
1029                         substr($$line, 0, 1) = " "; ## Remove '-'
1030                         $in_insert_block  = 0;
1031                         $in_else_block    = 0;
1032                         next;
1033                 }
1034
1035                 # End regular #if
1036                 $$line =~ m/^-#endif/ and $in_mask_block and --$regular_ifs;
1037
1038                 # Remove '-' prefixes in all lines within insert and mask-else blocks
1039                 # -------------------------------------------------------------------
1040                 if ( ($$line =~ m,^-,)
1041                   && ( $in_insert_block || ($in_mask_block && $in_else_block) ) ) {
1042                         substr($$line, 0, 1) = " "; ## Remove '-'
1043                 }
1044
1045                 # End our else block awareness at the first empty line after a mask block.
1046                 # ------------------------------------------------------------------------
1047                 $$line =~ m,^\s+$, and ($else_block_start > -1) and (!$in_mask_block) and $else_block_start = -1;
1048
1049                 # Special check for additions that might wreak havoc:
1050                 # ---------------------------------------------------
1051                 if ( $$line =~ m,^\+, ) {
1052
1053                         # If upstream adds something after a mask #else block, we move it up before the #else.
1054                         # ------------------------------------------------------------------------------------
1055                         if ( ($else_block_start > -1) && !$in_mask_block) {
1056                                 my $moved_line = $$line;
1057                                 splice(@{$hHunk->{lines}}, $i, 1); ## Order matters here.
1058                                 splice(@{$hHunk->{lines}}, $else_block_start++, 0, $moved_line);
1059                                 next;
1060                         }
1061
1062                         # If a name reverts pulls a line under a mask start, push it back up.
1063                         # -------------------------------------------------------------------
1064                         if ( ($mask_block_start > -1) && $in_mask_block && (1 ==($i - $mask_block_start))) {
1065                                 my $moved_line = $$line;
1066                                 splice(@{$hHunk->{lines}}, $i, 1); ## Order matters here, too.
1067                                 splice(@{$hHunk->{lines}}, $mask_block_start++, 0, $moved_line);
1068                         }
1069                 }
1070         } ## End of looping lines
1071
1072         # Note down how this hunk ends before first pruning
1073         $hHunk->{masked_end} = $in_mask_block && !$in_else_block;
1074
1075         return 1;
1076 }
1077
1078
1079 # -----------------------------------------------------------------------
1080 # --- Check for musl_libc compatibility blocks                        ---
1081 # --- Rules:                                                          ---
1082 # --- For musl-libc compatibility, there are some                     ---
1083 # ---   #ifdef __GLIBC__ (...) #else (...) #endif // __GLIBC__        ---
1084 # --- helpers.                                                        ---
1085 # --- These can also be "#if defined(__GLIBC__)"                      ---
1086 # --- Note: We handle them like regular mask blocks, because the      ---
1087 # ---       __GLIBC__ block is considered to be the original, while   ---
1088 # ---       the musl_libc compat block is the #else block.            ---
1089 # -----------------------------------------------------------------------
1090 sub check_musl {
1091
1092         # Early exits:
1093         defined($hHunk) or return 0;
1094         $hHunk->{useful} or return 0;
1095
1096         # Count non-elogind block #ifs. This is needed, so normal
1097         # #if/#else/#/endif constructs can be put inside both the original
1098         # and the alternative block.
1099         my $regular_ifs = 0;
1100
1101         # Remember the final mask state for later reversal
1102         # ------------------------------------------------
1103         my $hunk_ends_in_mask = $in_mask_block;
1104         my $hunk_ends_in_else = $in_else_block;
1105         $in_else_block = 0;
1106         $hHunk->{masked_start} and $in_mask_block = 1 or $in_mask_block = 0;
1107
1108         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
1109                 my $line = \$hHunk->{lines}[$i]; ## Shortcut
1110
1111                 # The increment/decrement variant can cause negative values:
1112                 $in_mask_block < 0 and $in_mask_block = 0;
1113                 $in_else_block < 0 and $in_else_block = 0;
1114
1115                 # Quick mask checks, we must have the intermediate states
1116                 # -------------------------------------------------------
1117                 is_mask_start($$line) and ++$in_mask_block and next;
1118                 is_mask_else($$line)  and ++$in_else_block and substr($$line, 0, 1) = " " and next;
1119                 if (is_mask_end($$line)) {
1120                         $in_mask_block--;
1121                         $in_else_block--;
1122                         next;
1123                 }
1124
1125                 # Entering a __GLIBC__ block
1126                 # ---------------------------------------
1127                 if ( $$line =~ m/^-#if.+__GLIBC__/ ) {
1128                         ## Note: Here it is perfectly fine to be in an elogind mask block.
1129                         substr($$line, 0, 1) = " "; ## Remove '-'
1130                         $in_mask_block++; ## Increase instead of setting this to 1.
1131                         next;
1132                 }
1133
1134                 # Count regular #if
1135                 $$line =~ m/^-#if/ and $in_mask_block and ++$regular_ifs;
1136
1137                 # Switching from __GLIBC__ to else - not needed.
1138                 # (done by is_else_block() above)
1139                 # ---------------------------------------
1140
1141                 # Ending a __GLBC__ block
1142                 # ---------------------------------------
1143                 if ( $$line =~ m,^-#endif\s*///?.*__GLIBC__, ) {
1144                         (!$in_mask_block)
1145                                 and return hunk_failed("check_musl: #endif // __GLIBC__ found outside any __GLIBC__ block");
1146                         substr($$line, 0, 1) = " "; ## Remove '-'
1147                         $in_mask_block--; ## Decrease instead of setting to 0. This allows such
1148                         $in_else_block--; ## blocks to reside in regular elogind mask/insert blocks.
1149                         next;
1150                 }
1151
1152                 # End regular #if
1153                 $$line =~ m/^-#endif/ and $in_mask_block and --$regular_ifs;
1154
1155                 # Remove '-' prefixes in all lines within the musl (#else) blocks
1156                 # -------------------------------------------------------------------
1157                 if ( ($$line =~ m,^-,)
1158                   && ($in_mask_block > 0)
1159                   && ($in_else_block > 0 ) ) {
1160                         substr($$line, 0, 1) = " "; ## Remove '-'
1161                 }
1162         } ## End of looping lines
1163
1164         # Revert the final mask state remembered above
1165         # ------------------------------------------------
1166         $in_mask_block = $hunk_ends_in_mask;
1167         $in_else_block = $hunk_ends_in_else;
1168
1169         return 1;
1170 }
1171
1172 # -----------------------------------------------------------------------
1173 # --- Check for attempts to revert 'elogind' to 'systemd'             ---
1174 # --- Note: We only check for single line reverts like:               ---
1175 # --- |-  // This is how elogind does it                              ---
1176 # --- |+  // This is how systemd does it                              ---
1177 # -----------------------------------------------------------------------
1178 sub check_name_reverts {
1179
1180         # Early exits:
1181         defined($hHunk) or return 0;
1182         $hHunk->{useful} or return 0;
1183
1184         # Note down what is changed, so we can have inline updates
1185         my %hRemovals = ();
1186
1187         # Remember entering and ending newly inserted comments.
1188         # We do not rename in them.
1189         my $is_in_comment = 0;
1190
1191         # Remember the final mask state for later reversal
1192         # ------------------------------------------------
1193         my $hunk_ends_in_mask = $in_mask_block;
1194         my $hunk_ends_in_else = $in_else_block;
1195         $in_else_block = 0;
1196         $hHunk->{masked_start} and $in_mask_block = 1 or $in_mask_block = 0;
1197
1198         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
1199                 my $line = \$hHunk->{lines}[$i]; ## Shortcut
1200
1201                 defined($$line)
1202                         or return hunk_failed("check_name_reverts: Line "
1203                                 . ($i + 1) . "/$hHunk->{count} is undef?");
1204
1205                 # The increment/decrement variant can cause negative values:
1206                 $in_mask_block < 0 and $in_mask_block = 0;
1207                 $in_else_block < 0 and $in_else_block = 0;
1208
1209                 # Quick mask checks, we must have the intermediate states
1210                 # -------------------------------------------------------
1211                 is_mask_start($$line) and ++$in_mask_block and next;
1212                 is_mask_else($$line)  and ++$in_else_block and next;
1213                 if (is_mask_end($$line)) {
1214                         $in_mask_block = 0;
1215                         $in_else_block = 0;
1216                         next;
1217                 }
1218
1219                 # Note down removals
1220                 # ---------------------------------
1221                 if ($$line =~ m/^-[# ]*\s*(.*elogind.*)\s*$/) {
1222                         $hRemovals{$1}{line} = $i;
1223                         next;
1224                 }
1225
1226                 # Check for comments that get added
1227                 # ---------------------------------
1228                 if ($hFile{pwxfile}) {
1229                         $$line =~ m,^\+#\s+,
1230                                 and $is_in_comment = 1
1231                                  or $is_in_comment = 0;
1232                 } else {
1233                         ($$line =~ m,^\+\s*/[*]+,)    and $is_in_comment = 1;
1234                         ($$line =~ m,^\+.*\*/[^/]*$,) and $is_in_comment = 0;
1235                 }
1236
1237                 # Check Additions
1238                 # ---------------------------------
1239                 if ($$line =~ m/^\+[# ]*\s*(.*systemd.*)\s*$/) {
1240                         my $replace_text   = $1;
1241                         my $our_text_long  = $replace_text;
1242                         my $our_text_short = $our_text_long;
1243                         $our_text_long  =~ s/systemd-logind/elogind/g;
1244                         $our_text_short =~ s/systemd/elogind/g;
1245
1246                         # There is some specialities:
1247                         # =============================================================
1248                         # 1) In some meson files, we need the variable "systemd_headers".
1249                         # This refers to the systemd API headers that get installed,
1250                         # and must therefore not be renamed to elogind_headers.
1251                         $our_text_short =~ s/elogind_headers/systemd_headers/g;
1252
1253                         # 2) References to the systemd github site must not be changed
1254                         $replace_text =~ m,github\.com/systemd, and next;
1255
1256                         # 3) /run/systemd/ must not be changed, as other applications
1257                         #    rely on that naming.
1258                         # Note: The /run/elogind.pid file is not touched by that, as
1259                         #       systemd does not have something like that.
1260                         $replace_text =~ m,/run/systemd, and next;
1261
1262                         # Make the following easier with a simple shortcut:
1263                         my $o_txt = defined($hRemovals{$our_text_long }) ? $our_text_long  :
1264                                     defined($hRemovals{$our_text_short}) ? $our_text_short :
1265                                     "";
1266
1267                         # --- Case A) If this is a simple switch, undo it. ---
1268                         # ----------------------------------------------------
1269                         if ( length($o_txt) ) {
1270                                 substr($hHunk->{lines}[$hRemovals{$o_txt}{line}], 0, 1) = " ";
1271                                 splice(@{$hHunk->{lines}}, $i--, 1);
1272                                 $hHunk->{count}--;
1273                                 next;
1274                         }
1275
1276                         # --- Case B) Otherwise replace the addition with our text. ---
1277                         # ---         Unless we are in a mask block or comment.     ---
1278                         # -------------------------------------------------------------
1279                         $in_mask_block > 0 and (0 == $in_else_block) and next;
1280                         $is_in_comment and next;
1281                         $our_text_long eq $replace_text
1282                                 and $$line =~ s/^\+([# ]*\s*).*systemd.*(\s*)$/+${1}${our_text_short}${2}/
1283                                  or $$line =~ s/^\+([# ]*\s*).*systemd.*(\s*)$/+${1}${our_text_long }${2}/;
1284                 }
1285         }
1286
1287         # Revert the final mask state remembered above
1288         # ------------------------------------------------
1289         $in_mask_block = $hunk_ends_in_mask;
1290         $in_else_block = $hunk_ends_in_else;
1291
1292         return 1;
1293 }
1294
1295
1296 # -----------------------------------------------------------------------
1297 # --- Check for attempts to uncomment unsupported API functions       ---
1298 # --- in .sym files.                                                  ---
1299 # --- In here we change unsupported function calls from               ---
1300 # ---        sd_foo_func;                                             ---
1301 # --- to                                                              ---
1302 # ---        /* sd_foo_func; */                                       ---
1303 # -----------------------------------------------------------------------
1304 sub check_sym_lines {
1305
1306         # Early exits:
1307         defined($hHunk) or return 0;
1308         $hHunk->{useful} or return 0;
1309
1310         # Only .sym files are handled here
1311         $hFile{source} =~ m/\.sym\.pwx$/ or return 1;
1312
1313         # Note down what is changed, so we can have inline updates
1314         my %hAdditions = ();
1315         my %hRemovals  = ();
1316         
1317         # We need a sortable line map for possible later splicing
1318         my %hAddMap     = ();
1319
1320         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
1321                 my $line = \$hHunk->{lines}[$i]; ## Shortcut
1322
1323                 defined($$line)
1324                         or return hunk_failed("check_sym_files: Line "
1325                                 . ($i + 1) . "/$hHunk->{count} is undef?");
1326
1327                 # Note down removals
1328                 # ---------------------------------
1329                 if ($$line =~ m,^-\s*/\*\s+(\S.+;)\s+\*/\s*$,) {
1330                         $hRemovals{$1}{line}    = $i;
1331                         next;
1332                 }
1333
1334                 # Check Additions
1335                 # ---------------------------------
1336                 if ($$line =~ m,^\+\s*([^ /].+;)\s*$,) {
1337                         $hAdditions{$1}{line}    = $i;
1338                         $hAdditions{$1}{handled} = 0;
1339                         $hAddMap{$i} = $1;
1340                 }
1341         }
1342         
1343         # Now we go backwards through the lines that got added and revert the reversals.
1344         for my $i (sort { $b <=> $a } keys %hAddMap) {
1345                 my $item = $hAddMap{$i};
1346                 
1347                 # First a sanity check against double insertions.
1348                 $hAdditions{$item}{handled}
1349                         and return hunk_failed("check_sym_files: Line"
1350                                 . ($i + 1) . ": Double addition of \"$item\" found!" );
1351                 
1352                 # New stuff is in order:
1353                 defined($hRemovals{$item}) or ++$hAdditions{$item}{handled} and next;
1354                 
1355                 # Here we simply undo the removal and splice the addition:
1356                 substr($hHunk->{lines}[$hRemovals{$item}{line}], 0, 1) = " ";
1357                 splice(@{$hHunk->{lines}}, $i, 1);
1358                 $hAdditions{$item}{handled} = 1;
1359                 --$hHunk->{count};
1360         }
1361
1362         return 1;
1363 }
1364
1365
1366 # -----------------------------------------------------------------------
1367 # --- Checkout the given refid on $upstream_path                      ---
1368 # --- Returns 1 on success, 0 otherwise.                              ---
1369 # -----------------------------------------------------------------------
1370 sub checkout_upstream {
1371         my ($commit)   = @_;
1372
1373         # It is completely in order to not wanting to checkout a specific commit.
1374         defined($commit) and length($commit) or return 1;
1375
1376         my $new_commit = "";
1377         my $git        = Git::Wrapper->new($upstream_path);
1378         my @lOut       = ();
1379
1380         # Save the previous commit
1381         try {
1382                 @lOut = $git->rev_parse({short => 1}, "HEAD");
1383         } catch {
1384                 print "ERROR: Couldn't rev-parse $upstream_path HEAD\n";
1385                 print "Exit Code : " . $_->status . "\n";
1386                 print "Message   : " . $_->error  . "\n";
1387                 return 0;
1388         };
1389         $previous_commit = $lOut[0];
1390
1391         # Get the shortened commit hash of $commit
1392         try {
1393                 @lOut = $git->rev_parse({short => 1}, $commit);
1394         } catch {
1395                 print "ERROR: Couldn't rev-parse $upstream_path \"$commit\"\n";
1396                 print "Exit Code : " . $_->status . "\n";
1397                 print "Message   : " . $_->error  . "\n";
1398                 return 0;
1399         };
1400         $new_commit = $lOut[0];
1401
1402         # Now check it out, unless we are already there:
1403         if ($previous_commit ne $new_commit) {
1404                 print "Checking out $new_commit in upstream tree...";
1405                 try {
1406                         $git->checkout($new_commit);
1407                 } catch {
1408                         print "\nERROR: Couldn't checkout \"new_commit\" in $upstream_path\n";
1409                         print "Exit Code : " . $_->status . "\n";
1410                         print "Message   : " . $_->error  . "\n";
1411                         return 0;
1412                 };
1413                 print " done\n";
1414         }
1415
1416         return 1;
1417 }
1418
1419
1420 # -----------------------------------------------------------------------
1421 # --- Completely clean up the current %hFile data structure.          ---
1422 # -----------------------------------------------------------------------
1423 sub clean_hFile {
1424         defined($hFile{count}) or return 1;
1425
1426         for (my $i = $hFile{count} - 1; $i > -1; --$i) {
1427                 defined($hFile{hunks}[$i]) and undef(%{$hFile{hunks}[$i]});
1428         }
1429
1430         $hFile{count}  = 0;
1431         $hFile{hunks}  = [ ];
1432         $hFile{output} = [ ];
1433
1434         return 1;
1435 }
1436
1437 # -----------------------------------------------------------------------
1438 # --- Builds the diff between source and target file, and stores all  ---
1439 # --- hunks in $hFile{hunks} - if any.                                ---
1440 # --- Returns 1 on success and 0 if the files are the same.           ---
1441 # -----------------------------------------------------------------------
1442 sub diff_hFile {
1443
1444         # If this is not an attempt to create a new file, a few preparations
1445         # and checks can be made beforehand. They make no sense on new files.
1446         if (0 == $hFile{create}) {
1447                 # Do they differ at all?
1448                 `diff -qu "$hFile{source}" "$hFile{target}" 1>/dev/null 2>&1`;
1449                 $? or print "same\n" and return 0;
1450
1451                 # Shell and meson files must be prepared. See prepare_meson()
1452                 ( $hFile{source} =~ m/meson/ or
1453                   $hFile{source} =~ m/\.gperf$/ or
1454                  ($hFile{source} =~ m/\.in$/ and (!($hFile{source} =~ m/\.policy\.in$/)) ) or
1455                   $hFile{source} =~ m/\.pl$/ or
1456                   $hFile{source} =~ m/\.po$/ or
1457                   $hFile{source} =~ m/\.sh$/ or
1458                   $hFile{source} =~ m/\.sym$/
1459                 ) and $hFile{pwxfile} = 1 and prepare_shell;
1460
1461                 # We mask double dashes in XML comments using XML hex entities. These
1462                 # must be unmasked for processing.
1463                 ( $hFile{source} =~ m/\.xml$/ or
1464                   $hFile{source} =~ m/\.policy\.in$/
1465                 ) and $hFile{pwxfile} = 1 and prepare_xml;
1466         }
1467
1468         # Let's have three shortcuts:
1469         my $src = $hFile{source};
1470         my $tgt = $hFile{target};
1471         my $prt = $hFile{part};
1472
1473         # Now the diff can be built ...
1474         my @lDiff = `diff -N -u "$src" "$tgt"`;
1475
1476         # ... the head of the output can be generated ...
1477         @{$hFile{output}} = splice(@lDiff, 0, 2);
1478         chomp $hFile{output}[0]; # These now have absolute paths, and source meson files have a
1479         chomp $hFile{output}[1]; # .pwx extensions. That is not what the result shall look like.
1480         $hFile{create}           # But we have $hFile{part}, which is already the
1481                 and $hFile{output}[0] =~ s,$src,/dev/null, # relative file name of the file we are 
1482                  or $hFile{output}[0] =~ s,$src,a/$prt,;   # processing, and we know if a file is
1483         $hFile{output}[1] =~ s,$tgt,b/$prt,;           # to be created.
1484
1485         # ... and the raw hunks can be stored.
1486         for (my $line_no  = 1; $line_no < scalar @lDiff; ++$line_no) {
1487                 ("@@" eq substr($lDiff[$line_no], 0, 2))
1488                         and (build_hHunk(splice(@lDiff, 0, $line_no)) or return 0)
1489                         and $line_no = 0;
1490         }
1491         scalar @lDiff and build_hHunk(@lDiff);
1492
1493         return 1;
1494 }
1495
1496 # -----------------------------------------------------------------------
1497 # --- Finds all relevant files and store them in @wanted_files        ---
1498 # --- Returns 1 on success, 0 otherwise.                              ---
1499 # -----------------------------------------------------------------------
1500 sub generate_file_list {
1501
1502         # Do some cleanup first. Just to be sure.
1503         `rm -rf build`;
1504         `find -iname '*.orig' -or -iname '*.bak' -or -iname '*.rej' -or -iname '*~' -or -iname '*.gc??' | xargs rm -f`;
1505
1506         # Build wanted files hash
1507         while (my $want = shift @wanted_files) {
1508                 $have_wanted    = 1;
1509                 $want =~ m,^\./, or $want = "./$want"; 
1510                 $hWanted{$want} = 1;
1511                 $want           =~ s/elogind/systemd/g;
1512                 $hWanted{$want} = 1;
1513         }
1514
1515         # The idea now is, that we use File::Find to search for files
1516         # in all legal directories this program allows. Checking against
1517         # the built %hWanted ensures that a user provided list of files
1518         # is heeded.
1519         for my $xDir ("docs", "factory", "m4", "man", "shell-completion", "src", "tools") {
1520                 if ( -d "$xDir" ) {
1521                         find(\&wanted, "$xDir");
1522                 }
1523         }
1524
1525         # If the user wants to check files, that do not show up when
1526         # searching our predefined set of directories, then let them.
1527         for my $xFile (keys %hWanted) {
1528                 # Files with systemd<->elogind alternatives might not be there
1529                 -f "$xFile" or $hWanted{$xFile} = 2;
1530                 $hWanted{$xFile} == 2 and next; ## Already handled or unavailable
1531                 find(\&wanted, "$xFile");
1532         }
1533         
1534         # All files that shall be created must be added manually now.
1535         scalar keys %hToCreate and push @source_files, keys %hToCreate;
1536
1537         # Just to be sure...
1538         scalar @source_files
1539                  or print("ERROR: No source files found? Where the hell are we?\n")
1540                 and return 0;
1541
1542         # Get the maximum file length and build $file_fmt
1543         my $mlen = 0;
1544         for my $f (@source_files) {
1545                 length($f) > $mlen and $mlen = length($f);
1546         }
1547         $file_fmt = sprintf("%%-%d%s", $mlen, "s");
1548
1549         return 1;
1550 }
1551
1552 # ------------------------------------------------------------------------
1553 # --- Generate the "@@ -xx,n +yy,m @@" hunk header line out of $hHunk. ---
1554 # --- Returns the generated string, with 0 values if $hHunk is undef.  ---
1555 # --- IMPORTANT: This function does *NOT* prune $hHunk->{lines} !      ---
1556 # ------------------------------------------------------------------------
1557 sub get_hunk_head {
1558         my ($offset) = @_;
1559
1560         my $src_len   = 0;
1561         my $tgt_len   = 0;
1562         my $lCount    = $hHunk->{count};
1563         my $src_start = $hHunk->{src_start};
1564         my $tgt_start = defined($offset) ? $src_start + $$offset : $hHunk->{tgt_start};
1565
1566         for (my $i = 0; $i < $lCount; ++$i) {
1567                 if ($hHunk->{lines}[$i] =~ m/^\+/ ) {
1568                         $tgt_len++;
1569                 } elsif ($hHunk->{lines}[$i] =~ m/^\-/ ) {
1570                         $src_len++;
1571                 } else {
1572                         $src_len++;
1573                         $tgt_len++;
1574                 }
1575         }
1576
1577         # If an offset reference was given, add back the size diff
1578         defined($offset)
1579                 and $$offset += $tgt_len - $src_len;
1580
1581         return sprintf("@@ -%d,%d +%d,%d @@", $src_start, $src_len, $tgt_start, $tgt_len);
1582 }
1583
1584 # -----------------------------------------------------------------------
1585 # --- Whenever a check finds an illegal situation, it has to call     ---
1586 # --- this subroutine which terminates the progress line and creaes   ---
1587 # --- an entry in @lFails.                                            ---
1588 # --- Param: An error message, preferably with the name of the failed ---
1589 # ---        check.                                                   ---
1590 # --- Return: Always zero.                                            ---
1591 # -----------------------------------------------------------------------
1592 sub hunk_failed {
1593         my ($msg) = @_;
1594         my $num   = scalar @lFails;
1595
1596         # Generate entry:
1597         push @lFails, {
1598                 hunk => [ get_hunk_head ],
1599                 msg  => $msg,
1600                 part => $hFile{part}
1601         };
1602
1603         # Add the hunk itself
1604         for my $line (@{$hHunk->{lines}}) {
1605                 push @{$lFails[$num]{hunk}}, $line;
1606         }
1607
1608         # And terminate the progress line:
1609         print "$msg\n";
1610
1611         return 0;
1612 }
1613
1614 # -----------------------------------------------------------------------
1615 # --- Check the current $hHunk whether it still does anything.        ---
1616 # --- While being at it, prune it to what a diff needs:               ---
1617 # ---   3 lines before the first and 3 lines after the last change.   ---
1618 # --- Returns 1 if at least one change was found, 0 otherwise.        ---
1619 # -----------------------------------------------------------------------
1620 sub hunk_is_useful {
1621
1622         # Early exits:
1623         defined($hHunk) or return 0;
1624         $hHunk->{useful} or return 0;
1625
1626         # Go through the lines and see whether we have any changes
1627         $hHunk->{useful} = 0;
1628
1629         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
1630                 if ($hHunk->{lines}[$i] =~ m/^[-+]/ ) {
1631                         $hHunk->{useful} = 1;
1632                         return 1;
1633                 }
1634         }
1635
1636         return 0;
1637 }
1638
1639
1640 # --------------------------------------------------------------
1641 # --- Return 1 if the argument consists of any insertion end ---
1642 # --------------------------------------------------------------
1643 sub is_insert_end {
1644         my ($line) = @_;
1645
1646         defined($line) and length($line) or return 0;
1647
1648         if ( ( $line =~ m,^[- ]?#endif\s*/(?:[*/]+)\s*1, )
1649           || ( $line =~ m,//\s+1\s+-->\s*$, )
1650           || ( $line =~ m,\*\s+//\s+1\s+\*\*/\s*$, ) ) {
1651                 return 1;
1652         }
1653
1654
1655         return 0;
1656 }
1657
1658
1659 # ----------------------------------------------------------------
1660 # --- Return 1 if the argument consists of any insertion start ---
1661 # ----------------------------------------------------------------
1662 sub is_insert_start {
1663         my ($line) = @_;
1664
1665         defined($line) and length($line) or return 0;
1666
1667         if ( ( $line =~ m/^[- ]?#if\s+1.+elogind/ )
1668           || ( $line =~ m/<!--\s+1.+elogind.+-->\s*$/  ) ) {
1669                 return 1;
1670           }
1671
1672         return 0;
1673 }
1674
1675
1676 # --------------------------------------------------------------
1677 # --- Return 1 if the argument consists of any mask else     ---
1678 # --------------------------------------------------------------
1679 sub is_mask_else {
1680         my ($line) = @_;
1681
1682         defined($line) and length($line) or return 0;
1683
1684         if ( ( $line =~ m/^[- ]?#else/ )
1685           || ( $line =~ m/else\s+-->\s*$/ ) 
1686           || ( $line =~ m,\*\s+else\s+\*\*/\s*$, ) ) {
1687                 return 1;
1688         }
1689
1690         return 0;
1691 }
1692
1693
1694 # --------------------------------------------------------------
1695 # --- Return 1 if the argument consists of any mask end      ---
1696 # --------------------------------------------------------------
1697 sub is_mask_end {
1698         my ($line) = @_;
1699
1700         defined($line) and length($line) or return 0;
1701
1702         if ( ( $line =~ m,^[- ]?#endif\s*/(?:[*/]+)\s*0, )
1703           || ( $line =~ m,//\s+0\s+-->\s*$, )
1704           || ( $line =~ m,\*\s+//\s+0\s+\*\*/\s*$, ) ) {
1705                 return 1;
1706         }
1707
1708         return 0;
1709 }
1710
1711
1712 # --------------------------------------------------------------
1713 # --- Return 1 if the argument consists of any mask start    ---
1714 # --------------------------------------------------------------
1715 sub is_mask_start {
1716         my ($line) = @_;
1717
1718         defined($line) and length($line) or return 0;
1719
1720         if ( ($line =~ m/^[- ]?#if\s+0.+elogind/ )
1721           || (  ($line =~ m/<!--\s+0.+elogind/  )
1722             && !($line =~ m/-->\s*$/) )
1723           || (  ($line =~ m,/\*\*\s+0.+elogind,)
1724             && !($line =~ m,\*\*/\s*$,) ) ) {
1725                 return 1;
1726         }
1727
1728         return 0;
1729 }
1730
1731
1732 # -----------------------------------------------------------------------
1733 # --- parse the given list for arguments.                             ---
1734 # --- returns 1 on success, 0 otherwise.                              ---
1735 # --- sets global $show_help to 1 if the long help should be printed. ---
1736 # -----------------------------------------------------------------------
1737 sub parse_args {
1738         my @args      = @_;
1739         my $result    = 1;
1740
1741         for (my $i = 0; $i < @args; ++$i) {
1742
1743                 # Check for -c|--commit option
1744                 # -------------------------------------------------------------------------------
1745                 if ($args[$i] =~ m/^-(?:c|-commit)$/) {
1746                         if ( ( ($i + 1) >= @args )
1747                           || ( $args[$i+1] =~ m,^[-/.], ) ) {
1748                                 print "ERROR: Option $args[$i] needs a refid as argument!\n\nUsage: $USAGE_SHORT\n";
1749                                 $result = 0;
1750                                 next;
1751                         }
1752                         $wanted_commit = $args[++$i];
1753                 }
1754
1755                 # Check for --create option
1756                 # -------------------------------------------------------------------------------
1757                 elsif ($args[$i] =~ m/^--create$/) {
1758                         $do_create = 1;
1759                 }
1760
1761                 # Check for -f|--file option
1762                 # -------------------------------------------------------------------------------
1763                 elsif ($args[$i] =~ m/^-(?:f|-file)$/) {
1764                         if ( ( ($i + 1) >= @args )
1765                           || ( $args[$i+1] =~ m,^[-], ) ) {
1766                                 print "ERROR: Option $args[$i] needs a path as argument!\n\nUsage: $USAGE_SHORT\n";
1767                                 $result = 0;
1768                                 next;
1769                         }
1770                         push @wanted_files, split(/,/, $args[++$i]);
1771                 }
1772
1773                 # Check for -h|--help option
1774                 # -------------------------------------------------------------------------------
1775                 elsif ($args[$i] =~ m/^-(?:h|-help)$/) {
1776                         $show_help = 1;
1777                 }
1778
1779                 # Check for --stay option
1780                 # -------------------------------------------------------------------------------
1781                 elsif ($args[$i] =~ m/^--stay$/) {
1782                         $do_stay = 1;
1783                 }
1784
1785                 # Check for unknown options:
1786                 # -------------------------------------------------------------------------------
1787                 elsif ($args[$i] =~ m/^-/) {
1788                         print "ERROR: Unknown option \"$args[$1]\" encountered!\n\nUsage: $USAGE_SHORT\n";
1789                         $result = 0;
1790                 }
1791
1792                 # Everything else is considered to the path to upstream
1793                 # -------------------------------------------------------------------------------
1794                 else {
1795                         # But only if it is not set, yet:
1796                         if (length($upstream_path)) {
1797                                 print "ERROR: Superfluous upstream path \"$args[$i]\" found!\n\nUsage: $USAGE_SHORT\n";
1798                                 $result = 0;
1799                                 next;
1800                         }
1801                         if ( ! -d "$args[$i]") {
1802                                 print "ERROR: Upstream path \"$args[$i]\" does not exist!\n\nUsage: $USAGE_SHORT\n";
1803                                 $result = 0;
1804                                 next;
1805                         }
1806                         $upstream_path = $args[$i];
1807                 }
1808         } ## End looping arguments
1809
1810         # If we have no upstream path now, show short help.
1811         if ($result && !$show_help && !length($upstream_path)) {
1812                 print "ERROR: Please provide a path to upstream!\n\nUsage: $USAGE_SHORT\n";
1813                 $result = 0;
1814         }
1815         
1816         # If --create was given, @wanted_files must not be empty
1817         if ($result && !$show_help && $do_create && (0 == scalar @wanted_files)) {
1818                 print "ERROR: --create must not be used on the full tree!\n";
1819                 print "       Add at least one file using the --file option.\n";
1820                 $result = 0;
1821         }
1822
1823         # If --stay was given, $wanted_commit must not be empty
1824         if ($result && !$show_help && $do_stay && (0 == length($wanted_commit))) {
1825                 print "ERROR: --stay makes only sense with the -c|--commit option!\n";
1826                 $result = 0;
1827         }
1828
1829         # If any of the wanted files do not exist, error out unless --create was used.
1830         if ($result && !$show_help && defined($wanted_files[0])) {
1831                 foreach my $f (@wanted_files) {
1832                         -f $f 
1833                                 or $do_create and $hToCreate{$f} = 1
1834                                 or print "ERROR: $f does not exist!\n" and $result = 0;
1835                 }
1836         }
1837
1838         return $result;
1839 } ## parse_srgs() end
1840
1841 # -----------------------------------------------------------------------
1842 # --- Prepare shell and meson files for our processing.               ---
1843 # --- If this is a shell or meson file, we have to adapt it first:    ---
1844 # --- To be able to use our patch building system, the files use the  ---
1845 # --- same masking technology as the C files. But as these are not    ---
1846 # --- handled by any preprocessor, it is necessary to comment out all ---
1847 # --- masked blocks.                                                  ---
1848 # --- If we do not do this, diff creates patches which move all       ---
1849 # --- commented blocks behind the #endif and uncomment them.          ---
1850 # -----------------------------------------------------------------------
1851 sub prepare_shell {
1852         my $in   = $hFile{source};
1853         my $out  = $in . ".pwx";
1854         my @lIn  = ();
1855         my @lOut = ();
1856
1857         # Leech the source file
1858         if (open(my $fIn, "<", $in)) {
1859                 @lIn = <$fIn>;
1860                 close($fIn);
1861         } else {
1862                 die("$in can not be opened for reading! [$!]");
1863         }
1864
1865         # Now prepare the output, line by line.
1866         my $is_block = 0;
1867         my $is_else  = 0;
1868         my $line_no  = 0;
1869         for my $line (@lIn) {
1870
1871                 chomp $line;
1872                 ++$line_no;
1873
1874                 if ( is_mask_start($line) ) {
1875                         if ($is_block) {
1876                                 print "ERROR: $in:$line_no : Mask start in mask!\n";
1877                                 die("Illegal file");
1878                         }
1879                         $is_block = 1;
1880                 } elsif ($is_block && is_mask_else($line) ) {
1881                         $is_else = 1;
1882                 } elsif ( is_mask_end($line) ) {
1883                         if (!$is_block) {
1884                                 print "ERROR: $in:$line_no : Mask end outside mask!\n";
1885                                 die("Illegal file");
1886                         }
1887                         $is_block = 0;
1888                         $is_else  = 0;
1889                 } elsif ($is_block && !$is_else) {
1890                         $line =~ s,^#\s?,,;
1891                         $line =~ s,^\s\s\*\s?,,;
1892                 }
1893
1894                 push @lOut, $line;
1895         }
1896
1897         # Now write the outfile:
1898         if (open(my $fOut, ">", $out)) {
1899                 for my $line (@lOut) {
1900                         print $fOut "$line\n";
1901                 }
1902                 close($fOut);
1903         } else {
1904                 die("$out can not be opened for writing! [$!]");
1905         }
1906
1907         # The temporary file is our new source
1908         $hFile{source} = $out;
1909
1910         return 1;
1911 }
1912
1913 # -----------------------------------------------------------------------
1914 # --- The masking of unneeded blocks in XML files is done using a     ---
1915 # --- comment scheme. Unfortunately the standard forbids double dashes---
1916 # --- in comments. To be able to process XML files nevertheless, they ---
1917 # --- are updated by unprepare_xml() so that all double dashes in     ---
1918 # --- comments are substituted by &#x2D;&#x2D;, which must be reversed---
1919 # --- here or the further processing would go nuts.                   ---
1920 # -----------------------------------------------------------------------
1921 sub prepare_xml {
1922         my $in   = $hFile{source};
1923         my $out  = $in . ".pwx";
1924         my @lIn  = ();
1925         my @lOut = ();
1926
1927         # Leech the source file
1928         if (open(my $fIn, "<", $in)) {
1929                 @lIn = <$fIn>;
1930                 close($fIn);
1931         } else {
1932                 die("$in can not be opened for reading! [$!]");
1933         }
1934
1935         # Now prepare the output, line by line.
1936         my $is_block = 0;
1937         my $is_else  = 0;
1938         my $line_no  = 0;
1939         for my $line (@lIn) {
1940
1941                 chomp $line;
1942                 ++$line_no;
1943
1944                 if ( is_mask_start($line) ) {
1945                         if ($is_block) {
1946                                 print "ERROR: $in:$line_no : Mask start in mask!\n";
1947                                 die("Illegal file");
1948                         }
1949                         $is_block = 1;
1950                 } elsif ($is_block && is_mask_else($line) ) {
1951                         $is_else = 1;
1952                 } elsif ( is_mask_end($line) ) {
1953                         if (!$is_block) {
1954                                 print "ERROR: $in:$line_no : Mask end outside mask!\n";
1955                                 die("Illegal file");
1956                         }
1957                         $is_block = 0;
1958                         $is_else  = 0;
1959                 } elsif ($is_block && !$is_else) {
1960                         $line =~ s/&#x2D;/-/g;
1961                 }
1962
1963                 push @lOut, $line;
1964         }
1965
1966         # Now write the outfile:
1967         if (open(my $fOut, ">", $out)) {
1968                 for my $line (@lOut) {
1969                         print $fOut "$line\n";
1970                 }
1971                 close($fOut);
1972         } else {
1973                 die("$out can not be opened for writing! [$!]");
1974         }
1975
1976         # The temporary file is our new source
1977         $hFile{source} = $out;
1978
1979         return 1;
1980 }
1981
1982 # -----------------------------------------------------------------------
1983 # --- Remove unused prefix and postfix lines. Recalculates offsets.   ---
1984 # -----------------------------------------------------------------------
1985 sub prune_hunk {
1986
1987         # Early exits:
1988         defined($hHunk) or return 0;
1989         $hHunk->{useful} or return 0;
1990
1991         # Go through the lines and see what we've got.
1992         my @mask_info = ($hHunk->{masked_start});
1993         my $prefix    = 0;
1994         my $postfix   = 0;
1995         my $changed   = 0; ## Set to 1 once the first change was found.
1996
1997         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
1998                 my $line = $hHunk->{lines}[$i]; ## Shortcut
1999                 if ($line =~ m/^[-+]/ ) {
2000                         $changed = 1;
2001                         $postfix = 0;
2002                 } else {
2003                         $changed or ++$prefix;
2004                         ++$postfix;
2005                 }
2006
2007                 # We have to note down mask changes, that might get pruned.
2008                 # If any is found, the hunks masked_start must be set to it.
2009                 if ( 0 == $changed) {
2010                         $mask_info[$i+1] = is_mask_end($line)   ? -1
2011                                          : is_mask_else($line)  ? -1
2012                                          : is_mask_start($line) ?  1
2013                                          : 0;
2014                 }
2015                 # Note: The last action still stands, no matter whether it gets pruned
2016                 #       or not, as it is only relevant for the next hunk.
2017         } ## End of analyzing the hunks lines.
2018
2019         # Now let's prune it:
2020         if ($prefix > 3) {
2021                 $prefix -= 3;
2022                 splice(@{$hHunk->{lines}}, 0, $prefix);
2023                 $hHunk->{src_start} += $prefix;
2024                 $hHunk->{count}     -= $prefix;
2025
2026                 # If any mask state change gets pruned, we have to remember the last one:
2027                 for (my $i = $prefix; $i >= 0; --$i) {
2028                         if ($mask_info[$i]) {
2029                                 $hHunk->{masked_start} = $mask_info[$i] > 0 ? 1 : 0;
2030                                 last;
2031                         }
2032                 }
2033         }
2034         if ($postfix > 3) {
2035                 $postfix -= 3;
2036                 splice(@{$hHunk->{lines}}, $hHunk->{count} - $postfix, $postfix);
2037                 $hHunk->{count}     -= $postfix;
2038         }
2039
2040         return 1;
2041 }
2042
2043 # -----------------------------------------------------------------------
2044 # --- Unprepare shell (and meson) files after our processing          ---
2045 # --- In prepare_shell() we have commented in all content between our ---
2046 # --- elogind markers, to help diff not to juggle our blocks around.  ---
2047 # --- Now these blocks must be commented out again.                   ---
2048 # --- We have an advantage and a disadvantage here. On one hand, we   ---
2049 # --- can do the changes within $hFile{output}, as that is the final  ---
2050 # --- patch. On the other hand, we do not know whether really all     ---
2051 # --- lines in the source where commented out. The latter means, that ---
2052 # --- if we blindly comment out all block lines, the resulting patch  ---
2053 # --- may fail. We therefore write the temporary .pwx file back, and  ---
2054 # --- and ensure that all lines are commented out.                    ---
2055 # -----------------------------------------------------------------------
2056 sub unprepare_shell {
2057         my $in   = $hFile{source};
2058         my $out  = substr($in, 0, -4);
2059         my @lIn  = ();
2060         my @lOut = ();
2061
2062         # Do not handle XML files here
2063         $out =~ m/\.xml$/ and return 0;
2064
2065         # policy files are xml, too
2066         $out =~ m/\.policy\.in$/ and return 0;
2067
2068         # Leech the temporary file
2069         if (open(my $fIn, "<", $in)) {
2070                 @lIn = <$fIn>;
2071                 close($fIn);
2072         } else {
2073                 die("$in can not be opened for reading! [$!]");
2074         }
2075
2076         # Now prepare the output, line by line.
2077         my $is_block = 0;
2078         my $is_else  = 0;
2079         my $line_no  = 0;
2080         for my $line (@lIn) {
2081
2082                 chomp $line;
2083                 ++$line_no;
2084
2085                 if ( is_mask_start($line) ) {
2086                         if ($is_block) {
2087                                 print "ERROR: $in:$line_no : Mask start in mask!\n";
2088                                 die("Illegal file");
2089                         }
2090                         $is_block = 1;
2091                 } elsif ($is_block && is_mask_else($line) ) {
2092                         $is_else = 1;
2093                 } elsif ( is_mask_end($line) ) {
2094                         if (!$is_block) {
2095                                 print "ERROR: $in:$line_no : Mask end outside mask!\n";
2096                                 die("Illegal file");
2097                         }
2098                         $is_block = 0;
2099                         $is_else  = 0;
2100                 } elsif ($is_block && !$is_else
2101                      && ("# #"   ne substr($line, 0, 3))
2102                      && ("  * " ne substr($line, 0, 4)) ) {
2103                         $hFile{source} =~ m/\.sym\.pwx$/
2104                                 and $line = "  * " . $line
2105                                  or $line = "# "   . $line;
2106                 }
2107
2108                 push @lOut, $line;
2109         }
2110
2111         # Now write the outfile:
2112         if (open(my $fOut, ">", $out)) {
2113                 for my $line (@lOut) {
2114                         print $fOut "$line\n";
2115                 }
2116                 close($fOut);
2117         } else {
2118                 die("$out can not be opened for writing! [$!]");
2119         }
2120
2121         # Remove the temporary file
2122         unlink($in);
2123
2124         # Now prepare the patch. It is like above, but with less checks.
2125         # We have to move out the lines first, and then write them back.
2126         $is_block = 0;
2127         $is_else  = 0;
2128         @lIn = splice(@{$hFile{output}});
2129
2130         for my $line (@lIn) {
2131                 if ( $line =~ m/#\s+masked_(?:start|end)\s+([01])$/ ) {
2132                         $1 and $is_block = 1 or $is_block = 0;
2133                         $is_block and $is_else = 0; ## can't be.
2134                         next; ## do not transport this line
2135                 }
2136                 is_mask_end($line)   and $is_block = 0;
2137                 is_mask_start($line) and $is_block = 1;
2138                 $is_block  or $is_else = 0;
2139                 $is_block and is_mask_else($line) and $is_else = 1;
2140                 $is_block and (!$is_else)
2141                         and "@@" ne substr($line, 0, 2)
2142                         and (! ($line =~ m/^[ ]+#(?:if|else|endif)/) )
2143                         and substr($line, 1, 0) = "# ";
2144                 push @{$hFile{output}}, $line;
2145         }
2146
2147         # Now source is the written back original:
2148         $hFile{source} = $out;
2149
2150         return 1;
2151 }
2152
2153 # -----------------------------------------------------------------------
2154 # --- Before we can allow an XML file to live, all double dashes that ---
2155 # --- happen to reside in one of our mask blocks must be masked.      ---
2156 # --- The standard forbids double dashes inside comments, so we solve ---
2157 # --- this by substituting '--' with '&#x2D;&#x2D;'.                  ---
2158 # -----------------------------------------------------------------------
2159 sub unprepare_xml {
2160         my $in   = $hFile{source};
2161         my $out  = substr($in, 0, -4);
2162         my @lIn  = ();
2163         my @lOut = ();
2164
2165         # Leech the temporary file
2166         if (open(my $fIn, "<", $in)) {
2167                 @lIn = <$fIn>;
2168                 close($fIn);
2169         } else {
2170                 die("$in can not be opened for reading! [$!]");
2171         }
2172
2173         # Now prepare the output, line by line.
2174         my $is_block = 0;
2175         my $is_else  = 0;
2176         my $line_no  = 0;
2177         for my $line (@lIn) {
2178
2179                 chomp $line;
2180                 ++$line_no;
2181
2182                 if ( is_mask_start($line) ) {
2183                         if ($is_block) {
2184                                 print "ERROR: $in:$line_no : Mask start in mask!\n";
2185                                 die("Illegal file");
2186                         }
2187                         $is_block = 1;
2188                 } elsif ($is_block && is_mask_else($line) ) {
2189                         $is_else = 1;
2190                 } elsif ( is_mask_end($line) ) {
2191                         if (!$is_block) {
2192                                 print "ERROR: $in:$line_no : Mask end outside mask!\n";
2193                                 die("Illegal file");
2194                         }
2195                         $is_block = 0;
2196                         $is_else  = 0;
2197                 } elsif ($is_block && !$is_else) {
2198                         $line =~ s/--/&#x2D;&#x2D;/g;
2199                 }
2200
2201                 push @lOut, $line;
2202         }
2203
2204         # Now write the outfile:
2205         if (open(my $fOut, ">", $out)) {
2206                 for my $line (@lOut) {
2207                         print $fOut "$line\n";
2208                 }
2209                 close($fOut);
2210         } else {
2211                 die("$out can not be opened for writing! [$!]");
2212         }
2213
2214         # Remove the temporary file
2215         unlink($in);
2216
2217         # Now prepare the patch. It is like above, but with less checks.
2218         # We have to move out the lines first, and then write them back.
2219         @lIn = ();
2220         $is_block = 0;
2221         $is_else  = 0;
2222         @lIn = splice(@{$hFile{output}});
2223         for my $line (@lIn) {
2224                 if ( $line =~ m/#\s+masked_(?:start|end)\s+([01])$/ ) {
2225                         $1 and $is_block = 1 or $is_block = 0;
2226                         $is_block and $is_else = 0; ## can't be.
2227                         next; ## do not transport this line
2228                 }
2229                 is_mask_end($line)   and $is_block = 0;
2230                 is_mask_start($line) and $is_block = 1;
2231                 $is_block  or $is_else = 0;
2232                 $is_block and is_mask_else($line) and $is_else = 1;
2233                 $is_block and (!$is_else)
2234                         and $line =~ s/([^<!]+)--([^>]+)/${1}&#x2D;&#x2D;${2}/g;
2235
2236                 push @{$hFile{output}}, $line;
2237         }
2238
2239         # Now source is the written back original:
2240         $hFile{source} = $out;
2241
2242         return 1;
2243 }
2244
2245 # -----------------------------------------------------------------------
2246 # --- Analyze the hunk and map all include changes                    ---
2247 # --- The gathered knowledge is used in check_includes(), see there   ---
2248 # --- for the rules applied.                                          ---
2249 # -----------------------------------------------------------------------
2250 sub read_includes {
2251
2252         # Early exits:
2253         defined($hHunk) or return 1;
2254         $hHunk->{useful} or return 1;
2255
2256         # We must know when "needed by elogind blocks" start
2257         my $in_elogind_block = 0;
2258         for (my $i = 0; $i < $hHunk->{count}; ++$i) {
2259                 my $line = \$hHunk->{lines}[$i]; ## Shortcut
2260
2261                 # Note down removals of includes we commented out
2262                 if ( $$line =~ m,^-\s*//+\s*#include\s+([<"'])([^>"']+)[>"'], ) {
2263                         $hIncs{$2}{remove} = {
2264                                 hunkid => $hHunk->{idx},
2265                                 lineid => $i,
2266                                 sysinc => $1 eq "<"
2267                         };
2268                         next;
2269                 }
2270
2271                 # Note down inserts of possibly new includes we might want commented out
2272                 if ( $$line =~ m,^\+\s*#include\s+([<"'])([^>"']+)[>"'], ) {
2273                         $hIncs{$2}{insert} = {
2274                                 elogind  => $in_elogind_block,
2275                                 hunkid   => $hHunk->{idx},
2276                                 lineid   => $i,
2277                                 spliceme => 0,
2278                                 sysinc   => $1 eq "<"
2279                         };
2280                         next;
2281                 }
2282
2283                 # Note down removals of includes we explicitly added for elogind
2284                 if ( $in_elogind_block
2285                   && ($$line =~ m,^-\s*#include\s+([<"'])([^>"']+)[>"'], ) ) {
2286                         $hIncs{$2}{elogind} = { hunkid => $hHunk->{idx}, lineid => $i };
2287                         next;
2288                 }
2289
2290                 # elogind include blocks are started by a comment featuring "needed by elogind"
2291                 if ($$line =~ m,^[ -]\s*/+.*needed by elogind.*,i) {
2292                         $in_elogind_block = 1;
2293                         next;
2294                 }
2295
2296                 # elogind include blocks end, when the first not removed *EMPTY* line is found
2297                 $in_elogind_block and ($$line =~ m,^[ ]\s*$,) and $in_elogind_block = 0;
2298         }
2299
2300         return 1;
2301 }
2302
2303 # -----------------------------------------------------------------------
2304 # --- Splice all includes that were marked for splicing.              ---
2305 # --- This is not as easy as it seems. It can be, that if we just go  ---
2306 # --- through the %hIncs keys, that we splice one include that is     ---
2307 # --- before another. That leads to the wrong line to be spliced, or  ---
2308 # --- worse, the splicing being attempted out of bounds.              ---
2309 # -----------------------------------------------------------------------
2310 sub splice_includes {
2311
2312         # First build a tree of the includes to splice:
2313         my %incMap = ();
2314         for my $inc (keys %hIncs) {
2315                 if ($hIncs{$inc}{insert}{spliceme}) {
2316                         my $hId = $hIncs{$inc}{insert}{hunkid};
2317                         my $lId = $hIncs{$inc}{insert}{lineid};
2318
2319                         # Sanity checks:
2320                         $hId > -1 or print "splice_includes : Inc $inc has Hunk Id -1!\n" and next;
2321                         if ( -1 == $lId ) {
2322                                 $hHunk = $hFile{hunks}[$hId];
2323                                 hunk_failed("splice_includes: $inc has line id -1!");
2324                                 next;
2325                         }
2326                         if ( $lId >= $hFile{hunks}[$hId]{count} ) {
2327                                 $hHunk = $hFile{hunks}[$hId];
2328                                 hunk_failed("splice_includes: $inc line id $lId/$hFile{hunks}[$hId]{count}!");
2329                                 next;
2330                         }
2331
2332                         # Record the include line
2333                         $incMap{$hId}{$lId} = 1;
2334                 }
2335         }
2336
2337         # Now we can do the splicing in an ordered way:
2338         for my $hId (sort { $a <=> $b } keys %incMap) {
2339                 # Go through the lines in reverse, that should be save:
2340                 for my $lId (sort { $b <=> $a } keys %{$incMap{$hId}}) {
2341                         splice(@{$hFile{hunks}[$hId]{lines}}, $lId, 1);
2342                         $hFile{hunks}[$hId]{count}--;
2343                 }
2344         }
2345
2346         return 1;
2347 }
2348
2349 # Callback function for File::Find
2350 sub wanted {
2351         my $f = $File::Find::name;
2352         my $is_wanted = 0;
2353
2354         $f =~ m,^\./, or $f = "./$f"; 
2355
2356         -f $_ and ( (0 == $have_wanted) or defined($hWanted{$f}) )
2357               and (! ($_ =~ m/\.pwx$/ ) )
2358               and push @source_files, $File::Find::name
2359               and $is_wanted = 1;
2360
2361         $is_wanted and $hWanted{$f} = 2;
2362
2363         return 1;
2364 }