4 # Convert .fvwm2rc from 2.4.x format to 2.6.x format.
6 # Original author: Thomas Adam <thomas.adam22@gmail.com> Dec. 2009
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation; either version 2 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 # Global array for all our converted lines.
28 my @converted_lines = ();
30 # Global softref for addtofunc continuations.
32 my %converted_funcs = ();
34 # Global for additional files...
35 my @additional_files = ();
41 # Convert conditional command syntax correctly.
42 sub __convert_conditionals
45 my( $line ) = $cond->[-1];
47 qr/(all|current|direction|next|none|prev|pick|thiswindow|windowid)/;
49 # Take the last component. We no longer care for "[*]" as conditional
50 # command parameters. But we shouldn't really put another conditional
51 # in its place, so we'll just remove it.
54 # And convert over Next [$1] syntax.
55 $line =~ s/$condition_cmds\s*\[(.*?)\]/\($1\)/io;
57 $line = "$1 ". join( ', ', split( /[^,](\s+)[^,]/, $2 ) ) . " $3" if $line =~
58 /$condition_cmds\s*(\(.*?\))(.*)/io;
63 # Process the files specified and output them to a destination file.
69 foreach my $f ( @$files )
72 my $cwd_path = getcwd();
74 warn "Following: Read $s...\n" if $process_read;
76 if( !defined $d or $d eq '' )
78 my $fbasename = basename( $s );
79 $d = "$cwd_path/$fbasename.converted";
83 die "Destination file: $d exists\n";
86 open( my $in_file, '<', $s ) or die
87 "Unable to open source file: $!\n";
93 # We have to handle continuation lines here, such as:
95 # Style foo !Bar, !Baz, \
106 @converted_lines = ();
107 %converted_funcs = ();
111 # Convert style syntax over where applicable.
117 # At the very least, we can cheat and just negate everything. Whilst it
118 # isn't deprecated yet, it will be -- so it won't hurt to do it here.
120 # Split the line out first of all, between the "Style foo" part, and the
121 # actual styles being applied.
122 my( @style_parts ) = ($line =~ /^(style\s+\"??[\w+*?]\"??)(.*)$/i);
124 # Convert the second part over.
125 foreach( split( /\s*,\s*/, $style_parts[1] ) )
127 # There is no !PPosition style, but there is !UsePPosition
128 s/(?:No)(.*)/\!$1/ unless /nopposition/i;
129 s/nopposition/!UsePPosition/i;
133 push @converted_lines, $style_parts[0] . join(', ',
137 # Buckshot approach at turning fvwmthemes into colorsets. Can't really do
138 # much more than this, but at least gives the user something to go on.
139 sub convert_fvwmtheme
143 $line =~ s/^\*fvwmtheme\s*:?//i;
144 $line = undef if $line =~ /modulesynchronous.*?fvwmtheme/i;
146 push @converted_lines, $line;
149 # Comment out the modulepath line -- grr.
150 sub handle_modulepath
154 push( @converted_lines, "# Commented out by fvwm-convert-2.6: $line" );
157 # This should have happened in the fvwm-2.4 convert script, but handle it
159 sub convert_windowshadesteps
163 $line = "Style * WindowShadeSteps $1" :
164 $line = "Style * " . $line;
166 push( @converted_lines, $line );
169 sub convert_edge_resistance
173 # This gets converted into two parts. One is the EdgeResistance
174 # command, the other is a style line.
176 # We could only ever have had two numbers as arguments to
178 my( $edge_res_arg, $move_res_arg ) =
179 ( $line =~ /edgeresistance\s*(\d+)\s*(\d+)/i );
181 push( @converted_lines,
183 EdgeResistance $edge_res_arg
184 Style * EdgeMoveResistance $move_res_arg| );
187 sub convert_snapattraction
191 push( @converted_lines, "Style * " . $line );
194 sub convert_key_mouse_bindings
197 my @components = split( /(\s+)/, $line, 5 );
199 # Also, conditional commands should now be separated with commas and not
200 # whitespace, so try and fix these up where we can. It's not the
201 # intention we'll catch them all, but at least try and do so based on
202 # where they're likely to be used.
203 __convert_conditionals(\@components);
205 push( @converted_lines, join '', @components );
208 sub handle_continuation
210 no strict "refs"; # Yes, yes...
213 if( !defined $last_func_ref || $last_func_ref eq '' )
215 my @func_parts = split( /(\+\s*\"?(?:i|c|d|h|m)\"?\s*)/i, $line, 2 );
217 __convert_conditionals(\@func_parts);
219 push( @converted_lines, join '', @func_parts );
223 eval { &{$last_func_ref}($line) };
230 my @read_parts = split( /\s+/, $line );
232 push( @converted_lines, $line );
234 # Crudely try and work out if the file is readable, and if it is add it
235 # to the list of further files to convert.
237 # This won't handle having to interpolate out any env vars set via
238 # SetEnv, or worse yet, outside of FVWM's environment. The user will
239 # just have to run this script on that file manually.
240 my $fname = $read_parts[1];
241 return unless defined $fname and $fname ne '';
245 push( @additional_files, [$fname] );
259 # Then we assume FVWM_USERDIR ("$HOME/.fvwm/"), and if that file can't
260 # be found there, try CWD, and if that fails we just give up.
262 # Canonicalise the starting point by removing "$." -- we can guess what
263 # it ought to be replaced with.
264 $fname =~ s/^\$\.\/?//;
266 if( -e "$ENV{FVWM_USERDIR}/$fname" )
268 push( @additional_files,
269 ["$ENV{FVWM_USERDIR}/$fname"] );
273 if( -e "$ENV{HOME}/.fvwm/$fname" )
275 push( @additional_files,
276 ["$ENV{HOME}/.fvwm/$fname"] );
280 my $cwd_path = getcwd();
282 if( -e "$cwd_path/$fname" )
284 push( @additional_files, [$fname] );
288 warn "Unable to follow: $line\n";
291 sub check_func_definition
295 if( $line !~ /^addtofunc\s+(?:start|init|restart)function.*/i )
300 # Then we have a standard function line in the form:
304 # Ensure we run it all through __convert_conditionals()
305 my @func_parts = split( /(\s+)/, $line, 4 );
306 __convert_conditionals( \@func_parts );
308 push( @converted_lines, join '', @func_parts );
315 $last_func_ref = "convert_initfunc";
317 if( $line =~ /addtofunc\s+initfunction\s+\"??[icmhd]{1}\"??\s+.*/i ||
318 $line =~ /addtofunc\s+initfunction\s*/i )
320 $line =~ s/addtofunc\s+initfunction\s*//i;
325 return if !defined $line || $line eq '';
327 # What we need to do now is convert this from:
333 # + I Test (Init) Foo
335 my @func_cmd = split( /\s+/, $line, 3 );
336 unshift( @func_cmd, '' ) unless @func_cmd > 2;
338 # Remove any quotes around the action type --- they're not needed
340 $func_cmd[1] =~ s/\"//g;
341 $func_cmd[1] .= q| Test (Init) |;
343 # Run the command through the conditional function to ensure we
344 # handle those correctly.
345 __convert_conditionals( \@func_cmd );
347 push( @{ $converted_funcs{initfunction} }, join ' ', @func_cmd );
350 sub convert_restartfunc
353 $last_func_ref = "convert_restartfunc";
355 # We treat this exactly like startfunction.
356 if( $line =~ /addtofunc\s+restartfunction\s+\"??[icmhd]{1}\"??\s+.*/i )
358 # Split this string. We can throw away the "AddToFunc" part as this
359 # is irrelevant. But we want the following result:
360 # ( 'I', 'Some Command' )
361 $line =~ s/addtofunc\s+restartfunction\s*//i;
364 $line =~ s/addtofunc\s+restartfunction\s*//i;
366 return if $line eq '';
368 # Remove the continuation prefix as we can add this in when writing out
369 # the function definitions later.
372 my @func_cmd = split( /\s+/, $line, 2 );
373 $func_cmd[1] =~ s/\"//g;
375 # Run the command through the conditional function to ensure we
376 # handle those correctly.
377 __convert_conditionals( \@func_cmd );
379 push( @{ $converted_funcs{startfunction} }, join ' ', @func_cmd );
382 sub convert_startfunc
385 $last_func_ref = "convert_startfunc";
387 # Now, it's possible that we have something like this:
389 # AddToFunc StartFunction I Some Command
391 # Extract the command part, add it to the hash for our functions, and
392 # flag the fact we're dealing with StartFunction at this point for any
393 # continuation lines (+ I Foo) since we can't determine the context of
394 # them without such a thing.
396 if( $line =~ /addtofunc\s+startfunction\s+\"??[icmhd]{1}\"??\s+.*/i )
398 # Split this string. We can throw away the "AddToFunc" part as this
399 # is irrelevant. But we want the following result:
400 # ( 'I', 'Some Command' )
401 $line =~ s/addtofunc\s+startfunction\s*//i;
403 $line =~ s/addtofunc\s+startfunction\s*//i;
405 # Remove the continuation prefix as we can add this in when writing out
406 # the function definitions later.
409 return if !defined $line || $line eq '';
411 my @func_cmd = split( /\s+/, $line, 2 );
412 $func_cmd[1] =~ s/\"//g;
414 # Run the command through the conditional function to ensure we
415 # handle those correctly.
416 __convert_conditionals( \@func_cmd );
418 push( @{ $converted_funcs{startfunction} }, join ' ', @func_cmd );
423 my( $dest_file ) = @_;
424 open( my $f, '>', $dest_file ) or
425 die "Couldn't open $dest_file: $!\n";
427 # If we had any continuation lines, preserve them as best we can.
428 @converted_lines = map {
429 join "\\\n", split /\\/, $_
432 print $f join( "\n", @converted_lines );
434 # Write out the functions.
435 if( defined $converted_funcs{initfunction} or
436 defined $converted_funcs{startfunction} )
438 print $f qq|\n\nDestroyFunc StartFunction\nAddToFunc StartFunction\n|;
440 # Put the Init stuff before anything else.
441 for( @{ $converted_funcs{initfunction} },
442 @{ $converted_funcs{startfunction } } )
455 if( $line =~ /^style/i )
457 convert_styles($line);
458 } elsif( $line =~ /^\s*\*fvwmtheme:??/i ) {
459 convert_fvwmtheme($line);
460 } elsif( $line =~ /^\s*modulepath\s*/i ) {
461 handle_modulepath( $line );
462 } elsif( $line =~ /^\s*windowshadesteps.*/i ) {
463 convert_windowshadesteps($line);
464 } elsif( $line =~ /^\s*module(?:synchronous)?.*?fvwmtheme$/i ) {
465 convert_fvwmtheme($line);
466 } elsif( $line =~ /^\s*edgeresistance\s*\d+\s*\d+/i ) {
467 convert_edge_resistance($line);
468 } elsif( $line =~ /^\s*key|mouse/i ) {
469 convert_key_mouse_bindings($line);
470 } elsif( $line =~ /^\s*snap(?:attraction|grid)/i ) {
471 convert_snapattraction( $line );
472 } elsif( $line =~ /^\s*addtofunc\s+initfunction/i ) {
473 convert_initfunc( $line );
474 } elsif( $line =~ /^\s*addtofunc\s+startfunction.*/i ) {
475 convert_startfunc( $line );
476 } elsif( $line =~ /^\s*addtofunc\s+restartfunction/i ) {
477 convert_restartfunc( $line );
478 } elsif( $line =~ /^\s*addtofunc\s+\w+.*/i ) {
479 check_func_definition( $line );
480 } elsif( $line =~ /^\s*\+\s*\"??[ichmd]{1}\s*\"??\s+.*/i ) {
481 handle_continuation( $line );
482 } elsif( $line =~ /^\s*read\s*[\/\w]+/i ) {
483 handle_read_file( $line );
485 # Could be a comment, or a continuation, or simply something we
486 # don't need to convert. As far as continuation lines are
487 # concerned, these are kept in order just by pushing them onto the
488 # array --- but converting continuation lines is tricky since we'd
489 # need to determine the context of the continuation. I can't be
491 push( @converted_lines, $_ );
497 print "fvwm-convert-2.6 [-f] [-h] source-file destination-file\n";
503 "follow-read|f" => \$follow_read,
506 # But we still require @ARGV to be populated with our filenames.
507 usage() unless( @ARGV > 0 and @ARGV <=2 );
510 process_files( \@files );
512 if( @additional_files && !$follow_read )
514 print "The following files were detected, but not processed:\n\n",
515 join("\n", @$_ ) for @additional_files;
519 # Only do this is we've been asked.
520 if( @additional_files && $follow_read )
523 process_files( \@additional_files );