3 # ================================================================
4 # === ==> -------- HISTORY -------- <== ===
5 # ================================================================
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.
41 # ========================
42 # === Little TODO list ===
43 # ========================
47 use Cwd qw(getcwd abs_path);
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
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.
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.
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.
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.
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()
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.
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>
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.
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.
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.
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.
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.
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.
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.
164 ## ( { hunk : the failed hunk for later printing
165 ## msg : a message set by the function that failed
166 ## part : local relative file part
170 # ================================================================
171 # === ==> -------- Function list -------- <== ===
172 # ================================================================
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
208 # ================================================================
209 # === ==> -------- Prechecks -------- <== ==
210 # ================================================================
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.
219 generate_file_list or exit 1; ## Note: @wanted_files is heeded.
221 # ================================================================
222 # === ==> -------- = MAIN PROGRAM = -------- <== ===
223 # ================================================================
225 for my $file_part (@source_files) {
226 printf("$file_fmt: ", $file_part);
228 build_hFile($file_part) or next;
231 # Reset global state helpers
234 $in_insert_block = 0;
236 # Empty the include manipulation hash
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
245 # === 1) Check for elogind masks 1 (normal source code) ===========
246 check_masks and hunk_is_useful and prune_hunk or next;
248 # === 2) Check for elogind masks 2 (symbol files) =================
249 check_sym_lines and hunk_is_useful and prune_hunk or next;
251 # === 3) Check for musl_libc compatibility blocks =================
252 check_musl and hunk_is_useful and prune_hunk or next;
254 # === 4) Check for debug constructs ===============================
255 check_debug and hunk_is_useful and prune_hunk or next;
257 # === 5) Check for elogind extra comments and information =========
258 check_comments and hunk_is_useful and prune_hunk or next;
260 # === 6) Check for useful blank line additions ====================
261 check_blanks and hunk_is_useful and prune_hunk or next;
263 # === 7) Check for 'elogind' => 'systemd' reverts =================
264 check_name_reverts and hunk_is_useful and prune_hunk or next;
266 # === 8) Check for elogind_*() function removals ==================
267 check_func_removes and hunk_is_useful and prune_hunk or next;
269 # ===> IMPORTANT: From here on no more pruning, lines must *NOT* change any more! <===
271 # === 9) Analyze includes and note their appearance in $hIncs =====
272 read_includes; ## Never fails, doesn't change anything.
274 } ## End of first hunk loop
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 };
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
296 hunk_is_useful or next;
298 # === 1) Apply what we learned about changed includes =============
299 check_includes and hunk_is_useful or next;
301 } ## End of second hunk loop
303 # ---------------------------------------------------------------------
304 # --- Splice all include insertions that are marked for splicing ---
305 # ---------------------------------------------------------------------
308 # ---------------------------------------------------------------------
309 # --- Go through all hunks for a last prune and check ---
310 # ---------------------------------------------------------------------
312 for (my $pos = 0; $pos < $hFile{count}; ++$pos) {
313 $hHunk = $hFile{hunks}[$pos]; ## Global shortcut
316 hunk_is_useful or next;
318 prune_hunk and ++$have_hunk;
321 # If we have at least 1 useful hunk, create the output and tell the user what we've got.
323 and build_output # (Always returns 1)
324 and printf("%d Hunk%s\n", $have_hunk, $have_hunk > 1 ? "s" : "")
327 # Shell and meson files must be unprepared. See unprepare_shell()
328 $hFile{pwxfile} and ( unprepare_shell or unprepare_xml );
330 # Now skip the writing if there are no hunks
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";
340 printf("ERROR: %s could not be opened for writing!\n%s\n",
342 die("Please fix this first!");
344 } ## End of main loop
346 # ===========================
347 # === END OF MAIN PROGRAM ===
348 # ===========================
350 # ================================================================
351 # === ==> -------- Cleanup -------- <== ===
352 # ================================================================
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"));
362 printf("\n%d file%s only found in $WORKDIR:\n", $count, $count > 1 ? "s": "");
364 for (my $i = 0; $i < $count; ++$i) {
365 printf($fmt, $i + 1, $only_here[$i]);
369 # -------------------------------------------------------------------------
370 # --- Print out the list of failed hunks -> bug in hunk or program? ---
371 # -------------------------------------------------------------------------
372 if (scalar @lFails) {
373 my $count = scalar @lFails;
375 printf("\n%d file%s %s at least one fishy hunk:\n", $count,
376 $count > 1 ? "s" : "", $count > 1 ? "have" : "has");
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}});
386 $do_stay or length($previous_commit) and checkout_upstream($previous_commit);
390 # ================================================================
391 # === ==> ---- Function Implementations ---- <== ===
392 # ================================================================
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 # -----------------------------------------------------------------------
402 defined($part) and length($part) or print("ERROR\n") and die("build_hfile: part is empty ???");
404 # Is this a new file?
405 my $isNew = defined($hToCreate{$part}) ? 1 : 0;
407 # We only prefixed './' to unify things. Now it is no longer needed:
410 # Pre: erase current $hFile, as that is what is expected.
413 # Check the target file
414 my $tgt = "$upstream_path/$part";
415 $tgt =~ s/elogind/systemd/g;
417 -f $tgt or push(@only_here, $part)
418 and print "only here\n"
421 # Build the patch name
425 # Build the central data structure.
432 patch => "$PROGDIR/patches/${patch}.patch",
434 source => "$WORKDIR/$part",
441 # -----------------------------------------------------------------------
442 # --- Build a new $hHunk instance and add it to $hFile{hunks} ---
443 # -----------------------------------------------------------------------
445 my ($head, @lHunk) = @_;
446 my $pos = $hFile{count}++;
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]} = (
463 # We need to chomp the lines:
464 for my $line (@lHunk) {
465 defined($line) or next;
467 push @{$hFile{hunks}[$pos]{lines}}, $line;
468 $hFile{hunks}[$pos]{count}++;
473 print "Illegal hunk no $hFile{count}\n(Head: \"$head\")\n";
478 # -----------------------------------------------------------------------
479 # --- Writes $hFile{output} from all useful $hFile{hunks}. ---
480 # --- Important: No more checks, just do it! ---
481 # -----------------------------------------------------------------------
484 my $offset = 0; ## Count building up target offsets
486 for (my $pos = 0; $pos < $hFile{count}; ++$pos) {
487 $hHunk = $hFile{hunks}[$pos]; ## Global shortcut
489 # The useless are to be skipped, but we need the hunks masked_end
490 if ($hHunk->{useful}) {
492 # --- Note down the relevant starting mask status ---
493 # ---------------------------------------------------
494 $hFile{pwxfile} and push(@{$hFile{output}}, "# masked_start " . $hHunk->{masked_start});
496 # --- Add the header line ---------------------------
497 # ---------------------------------------------------
498 push(@{$hFile{output}}, get_hunk_head(\$offset));
500 # --- Add the hunk lines ----------------------------
501 # ---------------------------------------------------
502 for my $line (@{$hHunk->{lines}}) {
503 push(@{$hFile{output}}, $line);
507 # --- Note down the relevant ending mask status -----
508 # ---------------------------------------------------
509 $hFile{pwxfile} and push(@{$hFile{output}}, "# masked_end " . $hHunk->{masked_end});
511 } ## End of walking the hunks
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 # -----------------------------------------------------------------------
526 defined($hHunk) or return 0;
527 $hHunk->{useful} or return 0;
529 for (my $i = 0; $i < $hHunk->{count}; ++$i) {
530 my $line = \$hHunk->{lines}[$i]; ## Shortcut
532 if ( ($$line =~ m/^\+\s*$/)
534 && ( (is_mask_start( $hHunk->{lines}[$i-1])
535 || is_insert_start($hHunk->{lines}[$i-1])) ) ) {
536 # Simply swap the lines
538 $$line = $hHunk->{lines}[$i-1];
539 $hHunk->{lines}[$i-1] = $tmp;
547 # -----------------------------------------------------------------------
548 # --- Check comments we added for elogind specific information. ---
549 # --- These are all comments, and can be both single and multi line. ---
550 # -----------------------------------------------------------------------
554 defined($hHunk) or return 0;
555 $hHunk->{useful} or return 0;
557 my $in_comment_block = 0;
559 for (my $i = 0; $i < $hHunk->{count}; ++$i) {
560 my $line = \$hHunk->{lines}[$i]; ## Shortcut
562 # Check for comment block start
563 # -----------------------------
564 if ($$line =~ m,^-\s*(/[*]+|/[/]+).*elogind,) {
568 and return hunk_failed("check_comments: Comment block start found in comment block!");
570 substr($$line, 0, 1) = " ";
572 # Only start the comment block if this is really a multiline comment
573 (!($$line =~ m,\*/[^/]*$,) )
574 and $in_comment_block = 1;
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;
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) = " ";
595 # If none of the above applied, the comment block is over.
596 $in_comment_block = 0;
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 # -----------------------------------------------------------------------
612 defined($hHunk) or return 0;
613 $hHunk->{useful} or return 0;
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.
619 my $is_debug_func = 0; ## Special for log_debug_elogind()
621 for (my $i = 0; $i < $hHunk->{count}; ++$i) {
622 my $line = \$hHunk->{lines}[$i]; ## Shortcut
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.
634 $$line =~ m/^-#if/ and $in_insert_block and ++$regular_ifs;
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.
644 # Ending a debug construct block
645 # ---------------------------------------
646 if ( $$line =~ m,^-#endif\s*///?.*ENABLE_DEBUG_, ) {
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.
656 $$line =~ m/^-#endif/ and $in_insert_block and --$regular_ifs;
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;
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.
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;
677 } ## End of looping lines
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 {
692 defined($hHunk) or return 1;
693 $hHunk->{useful} or return 1;
695 # Needed for multi-line calls
696 my $is_func_call = 0;
698 for (my $i = 0; $i < $hHunk->{count}; ++$i) {
699 my $line = \$hHunk->{lines}[$i]; ## Shortcut
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;
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 '-'
715 # Check for the end of a multiline elogind_*() call
716 # -------------------------------------------------------------------
717 $is_func_call and $$line =~ m/\)\s*;/ and --$is_func_call;
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. ---
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 ---
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 # -----------------------------------------------------------------------
752 defined($hHunk) or return 1;
753 $hHunk->{useful} or return 1;
755 # We must know when "needed by elogind blocks" start
756 my $in_elogind_block = 0;
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.
762 for (my $i = 0; $i < $hHunk->{count}; ++$i) {
763 my $line = \$hHunk->{lines}[$i]; ## Shortcut
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
773 defined($hIncs{$inc}{remove}{hunkid}) and $hIncs{$inc}{remove}{hunkid} > -1
774 or return hunk_failed("check_includes: Unrecorded removal found!");
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);
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};
786 my $direction = $ins_diff > 0 ? 1 : -1;
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) {
793 if ( ( $hHunk->{lines}[$i+$j] =~ m,^-\s*//+\s*#include\s+[<"']([^>"']+)[>"'], )
794 || ( $hHunk->{lines}[$i+$j] =~ m,^\+\s*#include\s+[<"']([^>"']+)[>"'], ) ) {
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}
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;
809 } ## end of insert and remove being in the same hunk
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;
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;
832 } ## End of ruleset 1
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
840 defined($hIncs{$1}{insert}{hunkid}) and $hIncs{$1}{insert}{hunkid} > -1
841 or return hunk_failed("check_includes: Unrecorded insertion found!");
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;
849 # That's it. Cool, eh?
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
861 defined($hIncs{$1}{elogind}{hunkid}) and $hIncs{$1}{elogind}{hunkid} > -1
862 or return hunk_failed("check_includes: Unrecorded elogind include found!");
864 # As 1 and 2 do not apply, simply undo the removal.
865 substr($$line, 0, 1) = " ";
866 $hIncs{$1}{applied} = 1;
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;
876 # Never remove the block start
877 ($$line =~ m,^-,) and substr($$line, 0, 1) = " ";
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) = " ";
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;
892 # === Other 3 : Undo all other removals in elogind include blocks ===
893 # =====================================================================
894 $in_elogind_block and ($$line =~ m,^-,) and substr($$line, 0, 1) = " ";
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.
901 } ## End of looping lines
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) = " ";
911 # -----------------------------------------------------------------------
912 # --- Check $hHunk for elogind preprocessor masks and additions ---
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. ---
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 # -----------------------------------------------------------------------
937 defined($hHunk) or die("check_masks: hHunk is undef");
938 $hHunk->{useful} or die("chec_masks: Nothing done but hHunk is useless?");
940 # Count non-elogind block #ifs. This is needed, so normal
941 # #if/#else/#/endif constructs can be put inside elogind mask blocks.
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;
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;
954 # Note down how this hunk starts before first pruning
955 $hHunk->{masked_start} = $in_mask_block && !$in_else_block;
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)) {
963 and return hunk_failed("check_masks: Mask start found while being in a mask block!");
965 and return hunk_failed("check_masks: Mask start found while being in an insert block!");
966 substr($$line, 0, 1) = " "; ## Remove '-'
968 $else_block_start = -1;
969 $mask_block_start = $i;
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) = " ";
980 # Entering an elogind insert
981 # ---------------------------------------
982 if (is_insert_start($$line)) {
984 and return hunk_failed("check_masks: Insert start found while being in a mask 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;
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) = " ";
1000 $$line =~ m/^-#if/ and $in_mask_block and ++$regular_ifs;
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 '-'
1009 $else_block_start = $i; ## Here we might insert upstream additions
1010 $mask_block_start = -1;
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 '-'
1021 $mask_block_start = -1;
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;
1036 $$line =~ m/^-#endif/ and $in_mask_block and --$regular_ifs;
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 '-'
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;
1049 # Special check for additions that might wreak havoc:
1050 # ---------------------------------------------------
1051 if ( $$line =~ m,^\+, ) {
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);
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);
1070 } ## End of looping lines
1072 # Note down how this hunk ends before first pruning
1073 $hHunk->{masked_end} = $in_mask_block && !$in_else_block;
1079 # -----------------------------------------------------------------------
1080 # --- Check for musl_libc compatibility blocks ---
1082 # --- For musl-libc compatibility, there are some ---
1083 # --- #ifdef __GLIBC__ (...) #else (...) #endif // __GLIBC__ ---
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 # -----------------------------------------------------------------------
1093 defined($hHunk) or return 0;
1094 $hHunk->{useful} or return 0;
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;
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;
1106 $hHunk->{masked_start} and $in_mask_block = 1 or $in_mask_block = 0;
1108 for (my $i = 0; $i < $hHunk->{count}; ++$i) {
1109 my $line = \$hHunk->{lines}[$i]; ## Shortcut
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;
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)) {
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.
1135 $$line =~ m/^-#if/ and $in_mask_block and ++$regular_ifs;
1137 # Switching from __GLIBC__ to else - not needed.
1138 # (done by is_else_block() above)
1139 # ---------------------------------------
1141 # Ending a __GLBC__ block
1142 # ---------------------------------------
1143 if ( $$line =~ m,^-#endif\s*///?.*__GLIBC__, ) {
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.
1153 $$line =~ m/^-#endif/ and $in_mask_block and --$regular_ifs;
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 '-'
1162 } ## End of looping lines
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;
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 {
1181 defined($hHunk) or return 0;
1182 $hHunk->{useful} or return 0;
1184 # Note down what is changed, so we can have inline updates
1187 # Remember entering and ending newly inserted comments.
1188 # We do not rename in them.
1189 my $is_in_comment = 0;
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;
1196 $hHunk->{masked_start} and $in_mask_block = 1 or $in_mask_block = 0;
1198 for (my $i = 0; $i < $hHunk->{count}; ++$i) {
1199 my $line = \$hHunk->{lines}[$i]; ## Shortcut
1202 or return hunk_failed("check_name_reverts: Line "
1203 . ($i + 1) . "/$hHunk->{count} is undef?");
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;
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)) {
1219 # Note down removals
1220 # ---------------------------------
1221 if ($$line =~ m/^-[# ]*\s*(.*elogind.*)\s*$/) {
1222 $hRemovals{$1}{line} = $i;
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;
1233 ($$line =~ m,^\+\s*/[*]+,) and $is_in_comment = 1;
1234 ($$line =~ m,^\+.*\*/[^/]*$,) and $is_in_comment = 0;
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;
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;
1253 # 2) References to the systemd github site must not be changed
1254 $replace_text =~ m,github\.com/systemd, and next;
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;
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 :
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);
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}/;
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;
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; ---
1302 # --- /* sd_foo_func; */ ---
1303 # -----------------------------------------------------------------------
1304 sub check_sym_lines {
1307 defined($hHunk) or return 0;
1308 $hHunk->{useful} or return 0;
1310 # Only .sym files are handled here
1311 $hFile{source} =~ m/\.sym\.pwx$/ or return 1;
1313 # Note down what is changed, so we can have inline updates
1314 my %hAdditions = ();
1317 # We need a sortable line map for possible later splicing
1320 for (my $i = 0; $i < $hHunk->{count}; ++$i) {
1321 my $line = \$hHunk->{lines}[$i]; ## Shortcut
1324 or return hunk_failed("check_sym_files: Line "
1325 . ($i + 1) . "/$hHunk->{count} is undef?");
1327 # Note down removals
1328 # ---------------------------------
1329 if ($$line =~ m,^-\s*/\*\s+(\S.+;)\s+\*/\s*$,) {
1330 $hRemovals{$1}{line} = $i;
1335 # ---------------------------------
1336 if ($$line =~ m,^\+\s*([^ /].+;)\s*$,) {
1337 $hAdditions{$1}{line} = $i;
1338 $hAdditions{$1}{handled} = 0;
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};
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!" );
1352 # New stuff is in order:
1353 defined($hRemovals{$item}) or ++$hAdditions{$item}{handled} and next;
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;
1366 # -----------------------------------------------------------------------
1367 # --- Checkout the given refid on $upstream_path ---
1368 # --- Returns 1 on success, 0 otherwise. ---
1369 # -----------------------------------------------------------------------
1370 sub checkout_upstream {
1373 # It is completely in order to not wanting to checkout a specific commit.
1374 defined($commit) and length($commit) or return 1;
1376 my $new_commit = "";
1377 my $git = Git::Wrapper->new($upstream_path);
1380 # Save the previous commit
1382 @lOut = $git->rev_parse({short => 1}, "HEAD");
1384 print "ERROR: Couldn't rev-parse $upstream_path HEAD\n";
1385 print "Exit Code : " . $_->status . "\n";
1386 print "Message : " . $_->error . "\n";
1389 $previous_commit = $lOut[0];
1391 # Get the shortened commit hash of $commit
1393 @lOut = $git->rev_parse({short => 1}, $commit);
1395 print "ERROR: Couldn't rev-parse $upstream_path \"$commit\"\n";
1396 print "Exit Code : " . $_->status . "\n";
1397 print "Message : " . $_->error . "\n";
1400 $new_commit = $lOut[0];
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...";
1406 $git->checkout($new_commit);
1408 print "\nERROR: Couldn't checkout \"new_commit\" in $upstream_path\n";
1409 print "Exit Code : " . $_->status . "\n";
1410 print "Message : " . $_->error . "\n";
1420 # -----------------------------------------------------------------------
1421 # --- Completely clean up the current %hFile data structure. ---
1422 # -----------------------------------------------------------------------
1424 defined($hFile{count}) or return 1;
1426 for (my $i = $hFile{count} - 1; $i > -1; --$i) {
1427 defined($hFile{hunks}[$i]) and undef(%{$hFile{hunks}[$i]});
1431 $hFile{hunks} = [ ];
1432 $hFile{output} = [ ];
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 # -----------------------------------------------------------------------
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;
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;
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;
1468 # Let's have three shortcuts:
1469 my $src = $hFile{source};
1470 my $tgt = $hFile{target};
1471 my $prt = $hFile{part};
1473 # Now the diff can be built ...
1474 my @lDiff = `diff -N -u "$src" "$tgt"`;
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.
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)
1491 scalar @lDiff and build_hHunk(@lDiff);
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 {
1502 # Do some cleanup first. Just to be sure.
1504 `find -iname '*.orig' -or -iname '*.bak' -or -iname '*.rej' -or -iname '*~' -or -iname '*.gc??' | xargs rm -f`;
1506 # Build wanted files hash
1507 while (my $want = shift @wanted_files) {
1509 $want =~ m,^\./, or $want = "./$want";
1510 $hWanted{$want} = 1;
1511 $want =~ s/elogind/systemd/g;
1512 $hWanted{$want} = 1;
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
1519 for my $xDir ("docs", "factory", "m4", "man", "shell-completion", "src", "tools") {
1521 find(\&wanted, "$xDir");
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");
1534 # All files that shall be created must be added manually now.
1535 scalar keys %hToCreate and push @source_files, keys %hToCreate;
1537 # Just to be sure...
1538 scalar @source_files
1539 or print("ERROR: No source files found? Where the hell are we?\n")
1542 # Get the maximum file length and build $file_fmt
1544 for my $f (@source_files) {
1545 length($f) > $mlen and $mlen = length($f);
1547 $file_fmt = sprintf("%%-%d%s", $mlen, "s");
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 # ------------------------------------------------------------------------
1562 my $lCount = $hHunk->{count};
1563 my $src_start = $hHunk->{src_start};
1564 my $tgt_start = defined($offset) ? $src_start + $$offset : $hHunk->{tgt_start};
1566 for (my $i = 0; $i < $lCount; ++$i) {
1567 if ($hHunk->{lines}[$i] =~ m/^\+/ ) {
1569 } elsif ($hHunk->{lines}[$i] =~ m/^\-/ ) {
1577 # If an offset reference was given, add back the size diff
1579 and $$offset += $tgt_len - $src_len;
1581 return sprintf("@@ -%d,%d +%d,%d @@", $src_start, $src_len, $tgt_start, $tgt_len);
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 ---
1590 # --- Return: Always zero. ---
1591 # -----------------------------------------------------------------------
1594 my $num = scalar @lFails;
1598 hunk => [ get_hunk_head ],
1600 part => $hFile{part}
1603 # Add the hunk itself
1604 for my $line (@{$hHunk->{lines}}) {
1605 push @{$lFails[$num]{hunk}}, $line;
1608 # And terminate the progress line:
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 {
1623 defined($hHunk) or return 0;
1624 $hHunk->{useful} or return 0;
1626 # Go through the lines and see whether we have any changes
1627 $hHunk->{useful} = 0;
1629 for (my $i = 0; $i < $hHunk->{count}; ++$i) {
1630 if ($hHunk->{lines}[$i] =~ m/^[-+]/ ) {
1631 $hHunk->{useful} = 1;
1640 # --------------------------------------------------------------
1641 # --- Return 1 if the argument consists of any insertion end ---
1642 # --------------------------------------------------------------
1646 defined($line) and length($line) or return 0;
1648 if ( ( $line =~ m,^[- ]?#endif\s*/(?:[*/]+)\s*1, )
1649 || ( $line =~ m,//\s+1\s+-->\s*$, )
1650 || ( $line =~ m,\*\s+//\s+1\s+\*\*/\s*$, ) ) {
1659 # ----------------------------------------------------------------
1660 # --- Return 1 if the argument consists of any insertion start ---
1661 # ----------------------------------------------------------------
1662 sub is_insert_start {
1665 defined($line) and length($line) or return 0;
1667 if ( ( $line =~ m/^[- ]?#if\s+1.+elogind/ )
1668 || ( $line =~ m/<!--\s+1.+elogind.+-->\s*$/ ) ) {
1676 # --------------------------------------------------------------
1677 # --- Return 1 if the argument consists of any mask else ---
1678 # --------------------------------------------------------------
1682 defined($line) and length($line) or return 0;
1684 if ( ( $line =~ m/^[- ]?#else/ )
1685 || ( $line =~ m/else\s+-->\s*$/ )
1686 || ( $line =~ m,\*\s+else\s+\*\*/\s*$, ) ) {
1694 # --------------------------------------------------------------
1695 # --- Return 1 if the argument consists of any mask end ---
1696 # --------------------------------------------------------------
1700 defined($line) and length($line) or return 0;
1702 if ( ( $line =~ m,^[- ]?#endif\s*/(?:[*/]+)\s*0, )
1703 || ( $line =~ m,//\s+0\s+-->\s*$, )
1704 || ( $line =~ m,\*\s+//\s+0\s+\*\*/\s*$, ) ) {
1712 # --------------------------------------------------------------
1713 # --- Return 1 if the argument consists of any mask start ---
1714 # --------------------------------------------------------------
1718 defined($line) and length($line) or return 0;
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*$,) ) ) {
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 # -----------------------------------------------------------------------
1741 for (my $i = 0; $i < @args; ++$i) {
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";
1752 $wanted_commit = $args[++$i];
1755 # Check for --create option
1756 # -------------------------------------------------------------------------------
1757 elsif ($args[$i] =~ m/^--create$/) {
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";
1770 push @wanted_files, split(/,/, $args[++$i]);
1773 # Check for -h|--help option
1774 # -------------------------------------------------------------------------------
1775 elsif ($args[$i] =~ m/^-(?:h|-help)$/) {
1779 # Check for --stay option
1780 # -------------------------------------------------------------------------------
1781 elsif ($args[$i] =~ m/^--stay$/) {
1785 # Check for unknown options:
1786 # -------------------------------------------------------------------------------
1787 elsif ($args[$i] =~ m/^-/) {
1788 print "ERROR: Unknown option \"$args[$1]\" encountered!\n\nUsage: $USAGE_SHORT\n";
1792 # Everything else is considered to the path to upstream
1793 # -------------------------------------------------------------------------------
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";
1801 if ( ! -d "$args[$i]") {
1802 print "ERROR: Upstream path \"$args[$i]\" does not exist!\n\nUsage: $USAGE_SHORT\n";
1806 $upstream_path = $args[$i];
1808 } ## End looping arguments
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";
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";
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";
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) {
1833 or $do_create and $hToCreate{$f} = 1
1834 or print "ERROR: $f does not exist!\n" and $result = 0;
1839 } ## parse_srgs() end
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 # -----------------------------------------------------------------------
1852 my $in = $hFile{source};
1853 my $out = $in . ".pwx";
1857 # Leech the source file
1858 if (open(my $fIn, "<", $in)) {
1862 die("$in can not be opened for reading! [$!]");
1865 # Now prepare the output, line by line.
1869 for my $line (@lIn) {
1874 if ( is_mask_start($line) ) {
1876 print "ERROR: $in:$line_no : Mask start in mask!\n";
1877 die("Illegal file");
1880 } elsif ($is_block && is_mask_else($line) ) {
1882 } elsif ( is_mask_end($line) ) {
1884 print "ERROR: $in:$line_no : Mask end outside mask!\n";
1885 die("Illegal file");
1889 } elsif ($is_block && !$is_else) {
1891 $line =~ s,^\s\s\*\s?,,;
1897 # Now write the outfile:
1898 if (open(my $fOut, ">", $out)) {
1899 for my $line (@lOut) {
1900 print $fOut "$line\n";
1904 die("$out can not be opened for writing! [$!]");
1907 # The temporary file is our new source
1908 $hFile{source} = $out;
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 --, which must be reversed---
1919 # --- here or the further processing would go nuts. ---
1920 # -----------------------------------------------------------------------
1922 my $in = $hFile{source};
1923 my $out = $in . ".pwx";
1927 # Leech the source file
1928 if (open(my $fIn, "<", $in)) {
1932 die("$in can not be opened for reading! [$!]");
1935 # Now prepare the output, line by line.
1939 for my $line (@lIn) {
1944 if ( is_mask_start($line) ) {
1946 print "ERROR: $in:$line_no : Mask start in mask!\n";
1947 die("Illegal file");
1950 } elsif ($is_block && is_mask_else($line) ) {
1952 } elsif ( is_mask_end($line) ) {
1954 print "ERROR: $in:$line_no : Mask end outside mask!\n";
1955 die("Illegal file");
1959 } elsif ($is_block && !$is_else) {
1960 $line =~ s/-/-/g;
1966 # Now write the outfile:
1967 if (open(my $fOut, ">", $out)) {
1968 for my $line (@lOut) {
1969 print $fOut "$line\n";
1973 die("$out can not be opened for writing! [$!]");
1976 # The temporary file is our new source
1977 $hFile{source} = $out;
1982 # -----------------------------------------------------------------------
1983 # --- Remove unused prefix and postfix lines. Recalculates offsets. ---
1984 # -----------------------------------------------------------------------
1988 defined($hHunk) or return 0;
1989 $hHunk->{useful} or return 0;
1991 # Go through the lines and see what we've got.
1992 my @mask_info = ($hHunk->{masked_start});
1995 my $changed = 0; ## Set to 1 once the first change was found.
1997 for (my $i = 0; $i < $hHunk->{count}; ++$i) {
1998 my $line = $hHunk->{lines}[$i]; ## Shortcut
1999 if ($line =~ m/^[-+]/ ) {
2003 $changed or ++$prefix;
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
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.
2019 # Now let's prune it:
2022 splice(@{$hHunk->{lines}}, 0, $prefix);
2023 $hHunk->{src_start} += $prefix;
2024 $hHunk->{count} -= $prefix;
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;
2036 splice(@{$hHunk->{lines}}, $hHunk->{count} - $postfix, $postfix);
2037 $hHunk->{count} -= $postfix;
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);
2062 # Do not handle XML files here
2063 $out =~ m/\.xml$/ and return 0;
2065 # policy files are xml, too
2066 $out =~ m/\.policy\.in$/ and return 0;
2068 # Leech the temporary file
2069 if (open(my $fIn, "<", $in)) {
2073 die("$in can not be opened for reading! [$!]");
2076 # Now prepare the output, line by line.
2080 for my $line (@lIn) {
2085 if ( is_mask_start($line) ) {
2087 print "ERROR: $in:$line_no : Mask start in mask!\n";
2088 die("Illegal file");
2091 } elsif ($is_block && is_mask_else($line) ) {
2093 } elsif ( is_mask_end($line) ) {
2095 print "ERROR: $in:$line_no : Mask end outside mask!\n";
2096 die("Illegal file");
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;
2111 # Now write the outfile:
2112 if (open(my $fOut, ">", $out)) {
2113 for my $line (@lOut) {
2114 print $fOut "$line\n";
2118 die("$out can not be opened for writing! [$!]");
2121 # Remove the temporary file
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.
2128 @lIn = splice(@{$hFile{output}});
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
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;
2147 # Now source is the written back original:
2148 $hFile{source} = $out;
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 '--'. ---
2158 # -----------------------------------------------------------------------
2160 my $in = $hFile{source};
2161 my $out = substr($in, 0, -4);
2165 # Leech the temporary file
2166 if (open(my $fIn, "<", $in)) {
2170 die("$in can not be opened for reading! [$!]");
2173 # Now prepare the output, line by line.
2177 for my $line (@lIn) {
2182 if ( is_mask_start($line) ) {
2184 print "ERROR: $in:$line_no : Mask start in mask!\n";
2185 die("Illegal file");
2188 } elsif ($is_block && is_mask_else($line) ) {
2190 } elsif ( is_mask_end($line) ) {
2192 print "ERROR: $in:$line_no : Mask end outside mask!\n";
2193 die("Illegal file");
2197 } elsif ($is_block && !$is_else) {
2198 $line =~ s/--/--/g;
2204 # Now write the outfile:
2205 if (open(my $fOut, ">", $out)) {
2206 for my $line (@lOut) {
2207 print $fOut "$line\n";
2211 die("$out can not be opened for writing! [$!]");
2214 # Remove the temporary file
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.
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
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}--${2}/g;
2236 push @{$hFile{output}}, $line;
2239 # Now source is the written back original:
2240 $hFile{source} = $out;
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 # -----------------------------------------------------------------------
2253 defined($hHunk) or return 1;
2254 $hHunk->{useful} or return 1;
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
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},
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},
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 };
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;
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;
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 {
2312 # First build a tree of the includes to splice:
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};
2320 $hId > -1 or print "splice_includes : Inc $inc has Hunk Id -1!\n" and next;
2322 $hHunk = $hFile{hunks}[$hId];
2323 hunk_failed("splice_includes: $inc has line id -1!");
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}!");
2332 # Record the include line
2333 $incMap{$hId}{$lId} = 1;
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}--;
2349 # Callback function for File::Find
2351 my $f = $File::Find::name;
2354 $f =~ m,^\./, or $f = "./$f";
2356 -f $_ and ( (0 == $have_wanted) or defined($hWanted{$f}) )
2357 and (! ($_ =~ m/\.pwx$/ ) )
2358 and push @source_files, $File::Find::name
2361 $is_wanted and $hWanted{$f} = 2;