cvsimport
[fvwm.git] / bin / fvwm-convert-2.6.in
blob2510eb262ceb952d8fe74ae3a48dc5962d48e8d3
1 #!@PERL@
2 # -*-perl-*-
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
22 use strict;
23 use Cwd;
24 use File::Basename;
25 use Getopt::Long;
27 # Global array for all our converted lines.
28 my @converted_lines = ();
30 # Global softref for addtofunc continuations.
31 my $last_func_ref;
32 my %converted_funcs = ();
34 # Global for additional files...
35 my @additional_files = ();
37 # GetOpts
38 my $follow_read = '';
39 my $process_read = 0;
41 # Convert conditional command syntax correctly.
42 sub __convert_conditionals
44     my( $cond ) = @_;
45     my( $line ) = $cond->[-1];
46         my $condition_cmds = 
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.
52     $line =~ s/\[\*\]//;
53     
54     # And convert over Next [$1] syntax.
55     $line =~ s/$condition_cmds\s*\[(.*?)\]/\($1\)/io;
56     
57     $line = "$1 ". join( ', ', split( /[^,](\s+)[^,]/, $2 ) ) . " $3" if $line =~
58     /$condition_cmds\s*(\(.*?\))(.*)/io;
60     $cond->[-1] = $line;
63 # Process the files specified and output them to a destination file.
64 sub process_files
66     my( $files ) = @_;
68     no strict "refs";
69     foreach my $f ( @$files )
70     {
71         my( $s, $d ) = @$f;
72         my $cwd_path = getcwd();
74         warn "Following:  Read $s...\n" if $process_read;
76         if( !defined $d or $d eq '' ) 
77         {
78             my $fbasename = basename( $s );
79             $d = "$cwd_path/$fbasename.converted";
80         }
82         if( -e $d ) {
83             die "Destination file:  $d exists\n";
84         }
86         open( my $in_file, '<', $s ) or die
87             "Unable to open source file:  $!\n";
89         while( <$in_file> )
90         {  
91             chomp;
93             # We have to handle continuation lines here, such as:
94             #
95             # Style foo !Bar, !Baz, \
96             # NoSomethingElse
97             if( /\\\s*$/ )
98             {
99                 $_ .= <$in_file>;
100                 redo;
101             }
102             dispatch_line($_);
103         }
104         
105         write_out_file($d);
106         @converted_lines = ();
107         %converted_funcs = ();
108     }
111 # Convert style syntax over where applicable.
112 sub convert_styles
114     my( $line ) = @_;
115     my @converted;
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] ) )
126     {
127         # There is no !PPosition style, but there is !UsePPosition
128         s/(?:No)(.*)/\!$1/ unless /nopposition/i;
129         s/nopposition/!UsePPosition/i;
130         push @converted, $_;
131     }
133     push @converted_lines, $style_parts[0] . join(', ',
134         @converted);
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
141     my( $line ) = @_;
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
152     my( $line ) = @_;
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
158 # here anyway.
159 sub convert_windowshadesteps
161     my( $line ) = @_;
162     $line =~ /(\d+)p?/ ? 
163         $line = "Style * WindowShadeSteps $1" : 
164         $line = "Style * " . $line;
166     push( @converted_lines, $line );
169 sub convert_edge_resistance
171     my( $line ) = @_;
173     # This gets converted into two parts.  One is the EdgeResistance
174     # command, the other is a style line.
175     #
176     # We could only ever have had two numbers as arguments to
177     # EdgeResistance.
178     my( $edge_res_arg, $move_res_arg ) = 
179         ( $line =~ /edgeresistance\s*(\d+)\s*(\d+)/i );
181     push( @converted_lines,
182         qq|
183 EdgeResistance $edge_res_arg
184 Style * EdgeMoveResistance $move_res_arg| );
187 sub convert_snapattraction
189     my( $line ) = @_;
191     push( @converted_lines, "Style * " . $line );
194 sub convert_key_mouse_bindings
196     my( $line ) = @_;
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...
211     my( $line ) = @_;
213     if( !defined $last_func_ref || $last_func_ref eq '' )
214     {
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 );
220         return;
221     }
223     eval { &{$last_func_ref}($line) };
224     warn "$@\n" if $@;
227 sub handle_read_file
229     my( $line ) = @_;
230     my @read_parts = split( /\s+/, $line );
231     
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.
236     #
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 '';
243     if( -e $fname )
244     {
245         push( @additional_files, [$fname] );
246         
247         # We're done.
248         return;
249     }
251     # If we have this:
252     #
253     # Read foo
254     #
255     # Or this:
256     #
257     # Read $./foo
258     #
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.
261     
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" )
267     {
268         push( @additional_files,
269             ["$ENV{FVWM_USERDIR}/$fname"] );
270         return;
271     }
273     if( -e "$ENV{HOME}/.fvwm/$fname" )
274     {
275         push( @additional_files,
276             ["$ENV{HOME}/.fvwm/$fname"] );
277         return;
278     }
280     my $cwd_path = getcwd();
282     if( -e "$cwd_path/$fname" )
283     {
284         push( @additional_files, [$fname] );
285         return;
286     }
288     warn "Unable to follow:  $line\n";
291 sub check_func_definition
293     my( $line ) = @_;
295     if( $line !~ /^addtofunc\s+(?:start|init|restart)function.*/i )
296     {
297         $last_func_ref = '';
298     }
300     # Then we have a standard function line in the form:
301     #
302     # + I SomeCommand
303     #
304     # Ensure we run it all through __convert_conditionals()
305     my @func_parts = split( /(\s+)/, $line, 4 );
306     __convert_conditionals( \@func_parts );
307     
308     push( @converted_lines, join '', @func_parts );
312 sub convert_initfunc
314     my( $line ) = @_;
315     $last_func_ref = "convert_initfunc";
317     if( $line =~ /addtofunc\s+initfunction\s+\"??[icmhd]{1}\"??\s+.*/i ||
318         $line =~ /addtofunc\s+initfunction\s*/i )
319     {
320         $line =~ s/addtofunc\s+initfunction\s*//i;
321     }
323     $line =~ s/^\s*\+//;
325     return if !defined $line || $line eq '';
327     # What we need to do now is convert this from:
328     #
329     # + I Foo
330     #
331     # to:
332     #
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
339     # anymore.
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
352     my( $line ) = @_;
353     $last_func_ref = "convert_restartfunc";
354     
355     # We treat this exactly like startfunction.
356     if( $line =~ /addtofunc\s+restartfunction\s+\"??[icmhd]{1}\"??\s+.*/i )
357     {
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;
362     }
364     $line =~ s/addtofunc\s+restartfunction\s*//i;
365     
366     return if $line eq '';
368     # Remove the continuation prefix as we can add this in when writing out
369     # the function definitions later. 
370     $line =~ s/^\s*\+//;
371     
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
384     my( $line ) = @_;
385     $last_func_ref = "convert_startfunc";
387     # Now, it's possible that we have something like this:
388     #
389     # AddToFunc StartFunction I Some Command
390     #
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 )
397     {
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;
402     }
403     $line =~ s/addtofunc\s+startfunction\s*//i;
404     
405     # Remove the continuation prefix as we can add this in when writing out
406     # the function definitions later. 
407     $line =~ s/^\s*\+//;
409     return if !defined $line || $line eq '';
410     
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 );
421 sub write_out_file
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 /\\/, $_ 
430     } @converted_lines;
431     
432     print $f join( "\n", @converted_lines );
434     # Write out the functions.
435     if( defined $converted_funcs{initfunction} or
436         defined $converted_funcs{startfunction} )
437     {
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 } } )
443         {
444             print $f "+ $_\n";
445         }
446     }
448     close( $f );
451 sub dispatch_line
453     my( $line ) = @_;
455     if( $line =~ /^style/i )
456     {
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 );
484     } else {
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
490         # bothered.
491         push( @converted_lines, $_ );
492     }
495 sub usage
497     print "fvwm-convert-2.6 [-f] [-h] source-file destination-file\n";
498     exit;
501 GetOptions(
502     "help|h" => \&usage,
503     "follow-read|f" => \$follow_read,
504 ) || usage();
506 # But we still require @ARGV to be populated with our filenames.
507 usage() unless( @ARGV > 0 and @ARGV <=2 );
509 my @files = [@ARGV];
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;
516     print "\n";
519 # Only do this is we've been asked.
520 if( @additional_files && $follow_read )
522     $process_read = 1;
523     process_files( \@additional_files );