12f923dce3b5969b4a37f5fde14f08f40a64ef05
[xscreensaver] / hacks / munge-ad.pl
1 #!/usr/bin/perl -w
2 # Copyright © 2008 Jamie Zawinski <jwz@jwz.org>
3 #
4 # Permission to use, copy, modify, distribute, and sell this software and its
5 # documentation for any purpose is hereby granted without fee, provided that
6 # the above copyright notice appear in all copies and that both that
7 # copyright notice and this permission notice appear in supporting
8 # documentation.  No representations are made about the suitability of this
9 # software for any purpose.  It is provided "as is" without express or 
10 # implied warranty.
11 #
12 # This updates driver/XScreenSaver.ad.in with the current list of savers.
13 #
14 # Created:  3-Aug-2008.
15
16 require 5;
17 use diagnostics;
18 use strict;
19
20 my $progname = $0; $progname =~ s@.*/@@g;
21 my $version = q{ $Revision: 1.3 $ }; $version =~ s/^[^\d]+([\d.]+).*/$1/;
22
23 my $verbose = 0;
24
25 # 1 means disabled: marked with "-" by default in the .ad file.
26 # 2 means retired: not mentioned in .ad at all.
27 #
28 my %disable = ( 
29    'abstractile'        => 1,
30    'ant'                => 1,
31    'antinspect'         => 1,
32    'antmaze'            => 1,
33    'antspotlight'       => 1,
34    'braid'              => 1,
35    'critical'           => 1,
36    'crystal'            => 1,
37    'demon'              => 1,
38    'dnalogo'            => 1,
39    'fadeplot'           => 1,
40    'glblur'             => 1,
41    'glforestfire'       => 1,
42    'glplanet'           => 1,
43    'glslideshow'        => 1,
44    'hyperball'          => 1,
45    'hypercube'          => 1,
46    'jigglypuff'         => 1,
47    'juggle'             => 1,
48    'kaleidescope'       => 1,
49    'laser'              => 1,
50    'lcdscrub'           => 1,
51    'lightning'          => 1,
52    'lisa'               => 1,
53    'lissie'             => 1,
54    'lmorph'             => 1,
55    'loop'               => 1,
56    'nerverot'           => 1,
57    'noseguy'            => 1,
58    'polyominoes'        => 1,
59    'providence'         => 1,
60    'pyro'               => 1,
61    'rdbomb'             => 2,  # alternate name
62    'rocks'              => 1,
63    'rotor'              => 1,
64    'sballs'             => 1,
65    'sierpinski'         => 1,
66    'sphere'             => 1,
67    'spiral'             => 1,
68    'thornbird'          => 1,
69    'vidwhacker'         => 1,
70    'vines'              => 1,
71    'webcollage'         => 1,
72    'worm'               => 1,
73    'xsublim'            => 2,
74   );
75
76
77 # Parse the RETIRED_EXES variable from the Makefiles to populate %disable.
78 #
79 sub parse_makefiles() {
80   foreach my $mf ( "Makefile.in", "glx/Makefile.in" ) {
81     my $body = '';
82     local *IN;
83     open (IN, "<$mf") || error ("$mf: $!");
84     while (<IN>) { $body .= $_; }
85     close IN;
86
87     $body =~ s/\\\n//gs;
88     my ($var) = ($body =~ m/^RETIRED_EXES\s*=\s*(.*)$/mi);
89     error ("no RETIRED_EXES in $mf") unless $var;
90     foreach my $hack (split (/\s+/, $var)) {
91       $disable{$hack} = 2;
92     }
93   }
94 }
95
96
97 sub munge_ad($) {
98   my ($file) = @_;
99
100   parse_makefiles();
101
102   my $body = '';
103   local *IN;
104   open (IN, "<$file") || error ("$file: $!");
105   while (<IN>) { $body .= $_; }
106   close IN;
107   my $obody = $body;
108
109   my ($top, $mid, $bot) = ($body =~ m/^(.*?\n)(\*hacks\..*?\n)(\n.*)$/s);
110
111   my $mid2 = '';
112
113   my %hacks;
114
115   # Update the "*hacks.foo.name" section of the file based on the contents
116   # of config/*.xml.
117   #
118   my $dir = $file;
119   $dir =~ s@/[^/]*$@@s;
120   my @counts = (0,0,0,0,0,0,0,0,0,0);
121   foreach my $xml (sort (glob ("$dir/../hacks/config/*.xml"))) {
122     my $b = '';
123     open (IN, "<$xml") || error ("$xml: $!");
124     while (<IN>) { $b .= $_; }
125     close IN;
126     my ($name) = ($b =~ m@<screensaver[^<>]*\b_label=\"([^<>\"]+)\">@s);
127     error ("$xml: no name") unless $name;
128
129     my $name2 = lc($name);
130     $name2 =~ s/^((x|gl)?[a-z])/\U$1/s;  # what prefs.c (make_hack_name) does
131
132     $xml =~ s@^.*/([^/]+)\.xml$@$1@s;
133     if ($name ne $name2) {
134       my $s = sprintf("*hacks.%s.name:", $xml);
135       $mid2 .= sprintf ("%-28s%s\n", $s, $name);
136       $counts[9]++;
137     }
138
139     # Grab the year.
140     my ($year) =
141       ($b =~ m/<_description>.*Written by.*?;\s+(19[6-9]\d|20\d\d)\b/si);
142     error ("no year in $xml.xml") unless $year;
143     $hacks{$xml} = $year;
144   }
145
146   # Splice in new names.
147   $body = $top . $mid2 . $bot;
148
149
150   # Replace the "programs" section.
151   # Sort hacks by creation date, but put the OpenGL ones at the end.
152   #
153   my $segregate_p = 0;  # whether to put the GL hacks at the end.
154   my $xhacks = '';
155   my $ghacks = '';
156   foreach my $hack (sort { $hacks{$a} == $hacks{$b}
157                            ? $a cmp $b 
158                            : $hacks{$a} <=> $hacks{$b}}
159                     (keys(%hacks))) {
160     my $cmd = "$hack -root";
161     my $ts = (length($cmd) / 8) * 8;
162     while ($ts < 40) { $cmd .= "\t"; $ts += 8; }
163
164     my $dis = $disable{$hack} || 0;
165
166     my $glp;
167     my $glep = ($hack eq 'extrusion');
168     if (-f "$hack.c" || -f "$hack") { $glp = 0; }
169     elsif (-f "glx/$hack.c") { $glp = 1; }
170     elsif ($dis != 2) { error ("is $hack X or GL?"); }
171
172     $counts[($disable{$hack} || 0)]++;
173     if ($glp) {
174       $counts[6+($disable{$hack} || 0)]++;
175     } else {
176       $counts[3+($disable{$hack} || 0)]++;
177     }
178
179     next if ($dis == 2);
180
181     $dis = ($dis ? '-' : '');
182     my $vis = ($glp
183                ? (($dis ? '' : $glep ? '@GLE_KLUDGE@' : '@GL_KLUDGE@') .
184                   ' GL: ')
185                : '');
186     $cmd = "$dis$vis\t\t\t\t$cmd    \\n\\\n";
187
188     if ($glp) {
189       ($segregate_p ? $ghacks : $xhacks) .= $cmd;
190     } else {
191       $xhacks .= $cmd;
192     }
193   }
194
195   # Splice in new programs list.
196   #
197   $mid2 = ($xhacks .
198            ($segregate_p ? "\t\t\t\t\t\t\t\t\t      \\\n" : "") .
199            $ghacks);
200   $mid2 =~ s@\\$@@s;
201   ($top, $mid, $bot) = 
202     ($body =~ m/^(.*?\n\*programs:\s+\\\n)(.*?\n)(\n.*)$/s);
203   error ("unparsable") unless $mid;
204   $body = $top . $mid2 . $bot;
205
206   print STDERR "$progname: " .
207     "Total: $counts[0]+$counts[1]+$counts[2]; " .
208       "X11: $counts[3]+$counts[4]+$counts[5]; " .
209        "GL: $counts[6]+$counts[7]+$counts[8]; " .
210     "Names: $counts[9]\n"
211         if ($verbose);
212
213   # Write file if changed.
214   #
215   if ($body ne $obody) {
216     local *OUT;
217     open (OUT, ">$file") || error ("$file: $!");
218     print OUT $body;
219     close OUT;
220     print STDERR "$progname: wrote $file\n";
221   } elsif ($verbose) {
222     print STDERR "$progname: $file unchanged\n";
223   }
224 }
225
226
227 sub error($) {
228   my ($err) = @_;
229   print STDERR "$progname: $err\n";
230   exit 1;
231 }
232
233 sub usage() {
234   print STDERR "usage: $progname [--verbose] ad-file\n";
235   exit 1;
236 }
237
238 sub main() {
239   my $file;
240   while ($#ARGV >= 0) {
241     $_ = shift @ARGV;
242     if (m/^--?verbose$/) { $verbose++; }
243     elsif (m/^-v+$/) { $verbose += length($_)-1; }
244     elsif (m/^-./) { usage; }
245     elsif (!$file) { $file = $_; }
246     else { usage; }
247   }
248
249   usage unless ($file);
250   munge_ad ($file);
251 }
252
253 main();
254 exit 0;