| 1 | #!/usr/bin/perl |
|---|
| 2 | |
|---|
| 3 | use strict; |
|---|
| 4 | use warnings; |
|---|
| 5 | |
|---|
| 6 | my $reg_key = qr/^([0-9\.-]+\s+){13}(.*)$/o; |
|---|
| 7 | my $reg_isLabel = qr/^(\$)?(to:)?([XY]+:)?/o; |
|---|
| 8 | |
|---|
| 9 | sub defEmpty($) { |
|---|
| 10 | my ($whatever) = @_; |
|---|
| 11 | defined $whatever ? $whatever : ""; |
|---|
| 12 | } |
|---|
| 13 | sub ifdef($) { |
|---|
| 14 | my ($whatever) = @_; |
|---|
| 15 | defined $whatever ? 1 : 0; |
|---|
| 16 | } |
|---|
| 17 | |
|---|
| 18 | my $ASCII1 = chr(1); |
|---|
| 19 | |
|---|
| 20 | sub lessKey($$) { |
|---|
| 21 | my ($a,$b) = @_; |
|---|
| 22 | |
|---|
| 23 | my ($ato,$aIsLab,$aXY,$arest) = ('',0,'',$a); |
|---|
| 24 | my ($bto,$bIsLab,$bXY,$brest) = ('',0,'',$b); |
|---|
| 25 | if ($a =~ $reg_isLabel) { ($ato,$aIsLab,$aXY,$arest) = (defEmpty($2),ifdef($1),$3,$'); } |
|---|
| 26 | if ($b =~ $reg_isLabel) { ($bto,$bIsLab,$bXY,$brest) = (defEmpty($2),ifdef($1),$3,$'); } |
|---|
| 27 | |
|---|
| 28 | $arest =~ s/($ASCII1|\\001)x*$//; |
|---|
| 29 | $brest =~ s/($ASCII1|\\001)x*$//; |
|---|
| 30 | |
|---|
| 31 | my ($atxt,$btxt) = (lc($arest),lc($brest)); |
|---|
| 32 | $atxt =~ s/[^a-z0-9]//go; |
|---|
| 33 | $btxt =~ s/[^a-z0-9]//go; |
|---|
| 34 | |
|---|
| 35 | my $cmp = 0; |
|---|
| 36 | if ($aIsLab != $bIsLab) { |
|---|
| 37 | my $prefixLen = 3; # reduce to make stable |
|---|
| 38 | my ($apre,$bpre) = (substr($atxt,0,$prefixLen),substr($btxt,0,$prefixLen)); |
|---|
| 39 | if ($apre eq $bpre) { |
|---|
| 40 | $cmp = $aIsLab <=> $bIsLab; |
|---|
| 41 | } |
|---|
| 42 | else { |
|---|
| 43 | $cmp = $apre cmp $bpre; |
|---|
| 44 | } |
|---|
| 45 | } |
|---|
| 46 | else { |
|---|
| 47 | $cmp = $atxt cmp $btxt; |
|---|
| 48 | if ($cmp==0) { |
|---|
| 49 | $cmp = $ato cmp $bto; |
|---|
| 50 | if ($cmp==0) { |
|---|
| 51 | $cmp = length($a) <=> length($b); |
|---|
| 52 | if ($cmp==0) { |
|---|
| 53 | $cmp = $a cmp $b; |
|---|
| 54 | } |
|---|
| 55 | } |
|---|
| 56 | } |
|---|
| 57 | } |
|---|
| 58 | if ($cmp==0) { die "no order defined ($a,$b)"; } |
|---|
| 59 | $cmp; |
|---|
| 60 | } |
|---|
| 61 | |
|---|
| 62 | my $formatDetected = undef; |
|---|
| 63 | |
|---|
| 64 | sub sort4(\@) { |
|---|
| 65 | my ($lines_r) = @_; |
|---|
| 66 | |
|---|
| 67 | my %keyedLines = (); |
|---|
| 68 | |
|---|
| 69 | foreach my $line (@$lines_r) { |
|---|
| 70 | if ($line =~ $reg_key) { |
|---|
| 71 | my $key = $2; |
|---|
| 72 | while (exists $keyedLines{$key}) { $key .= 'x'; } # avoid overwriting keys |
|---|
| 73 | $keyedLines{$key} = $line; |
|---|
| 74 | if (not defined $formatDetected) { |
|---|
| 75 | if ($key =~ /$ASCII1/) { $formatDetected = 'old'; } |
|---|
| 76 | if ($key =~ /\\001/o) { $formatDetected = 'new'; } |
|---|
| 77 | } |
|---|
| 78 | } |
|---|
| 79 | else { |
|---|
| 80 | die "unexpected line '$line'"; |
|---|
| 81 | } |
|---|
| 82 | } |
|---|
| 83 | |
|---|
| 84 | my @sortedKeys = sort { lessKey($a,$b); } keys %keyedLines; |
|---|
| 85 | my @sortedLines = map { $keyedLines{$_}; } @sortedKeys; |
|---|
| 86 | |
|---|
| 87 | my $countIn = scalar(@$lines_r); |
|---|
| 88 | my $countOut = scalar(@sortedLines); |
|---|
| 89 | if ($countOut != $countIn) { |
|---|
| 90 | die "size changed in sort4 ($countIn -> $countOut)\n"; |
|---|
| 91 | } |
|---|
| 92 | |
|---|
| 93 | my $differs = 0; |
|---|
| 94 | for (my $i=0; $i<$countIn and $differs==0; ++$i) { |
|---|
| 95 | if ($sortedLines[$i] ne $$lines_r[$i]) { |
|---|
| 96 | $differs = 1; |
|---|
| 97 | } |
|---|
| 98 | } |
|---|
| 99 | |
|---|
| 100 | if ($differs) { |
|---|
| 101 | @$lines_r = @sortedLines; |
|---|
| 102 | } |
|---|
| 103 | return $differs; |
|---|
| 104 | } |
|---|
| 105 | |
|---|
| 106 | sub warnUnwantedItems($$$$) { |
|---|
| 107 | my ($fig,$item_line,$item_count,$description) = @_; |
|---|
| 108 | if ($item_count>0) { |
|---|
| 109 | print "$fig:$item_line: Warning: seen $item_count $description".($item_count>1 ? 's' : '')."\n"; |
|---|
| 110 | } |
|---|
| 111 | } |
|---|
| 112 | |
|---|
| 113 | sub sortFig($) { |
|---|
| 114 | my ($fig) = @_; |
|---|
| 115 | |
|---|
| 116 | $formatDetected = undef; |
|---|
| 117 | |
|---|
| 118 | eval { |
|---|
| 119 | my $changes = 0; |
|---|
| 120 | my $HEADERSIZE = 10; |
|---|
| 121 | |
|---|
| 122 | my @out = (); |
|---|
| 123 | my @in4 = (); |
|---|
| 124 | |
|---|
| 125 | my @item_count = (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); |
|---|
| 126 | my @item_line = @item_count; |
|---|
| 127 | |
|---|
| 128 | open(FIG,'<'.$fig) || die "cannot read '$fig' (Reason: $!)"; |
|---|
| 129 | my $line; |
|---|
| 130 | my $linenr = 0; |
|---|
| 131 | while (defined($line=<FIG>)) { |
|---|
| 132 | ++$linenr; |
|---|
| 133 | my $num = -1; |
|---|
| 134 | if ($linenr>=$HEADERSIZE) { |
|---|
| 135 | if ($line =~ /^([0-9])\s/o) { |
|---|
| 136 | $num = $1; |
|---|
| 137 | } |
|---|
| 138 | elsif ($line =~ /^-([0-9])/o) { |
|---|
| 139 | $num = $1 + 10; |
|---|
| 140 | } |
|---|
| 141 | |
|---|
| 142 | # log which item types are used in xfig: |
|---|
| 143 | ++$item_count[$num]; |
|---|
| 144 | $item_line[$num] = $linenr; |
|---|
| 145 | |
|---|
| 146 | if ($num == 4) { |
|---|
| 147 | push @in4, $line; |
|---|
| 148 | } |
|---|
| 149 | else { |
|---|
| 150 | if (scalar(@in4)) { $changes += sort4(@in4); push @out,@in4; @in4=(); } |
|---|
| 151 | push @out, $line; |
|---|
| 152 | } |
|---|
| 153 | } |
|---|
| 154 | else { |
|---|
| 155 | if ($line =~ /^#FIG\s2/o) { $HEADERSIZE = 2; } # older format -> shorter header |
|---|
| 156 | push @out, $line; |
|---|
| 157 | } |
|---|
| 158 | } |
|---|
| 159 | if (scalar(@in4)) { $changes += sort4(@in4); push @out,@in4; @in4=(); } |
|---|
| 160 | close(FIG); |
|---|
| 161 | |
|---|
| 162 | warnUnwantedItems($fig,$item_line[0],$item_count[0],"useless color definition"); |
|---|
| 163 | warnUnwantedItems($fig,$item_line[1],$item_count[1],"ignored ellipse"); |
|---|
| 164 | warnUnwantedItems($fig,$item_line[3],$item_count[3],"ignored spline"); |
|---|
| 165 | warnUnwantedItems($fig,$item_line[5],$item_count[5],"ignored arc"); |
|---|
| 166 | warnUnwantedItems($fig,$item_line[6],$item_count[6],"(probably) forgotten compound"); |
|---|
| 167 | |
|---|
| 168 | if ($formatDetected eq 'old') { |
|---|
| 169 | print "$fig:0: Warning: ancient fig format detected\n"; |
|---|
| 170 | } |
|---|
| 171 | |
|---|
| 172 | if ($changes>0) { |
|---|
| 173 | print "Sorted changed fig '$fig'\n"; |
|---|
| 174 | open(OUT,'>'.$fig) || die "cannot write to '$fig' (Reason: $!)"; |
|---|
| 175 | foreach (@out) { |
|---|
| 176 | print OUT $_; |
|---|
| 177 | } |
|---|
| 178 | close(OUT); |
|---|
| 179 | } |
|---|
| 180 | }; |
|---|
| 181 | if ($@) { |
|---|
| 182 | die "$@ (while sorting '$fig')"; |
|---|
| 183 | } |
|---|
| 184 | } |
|---|
| 185 | |
|---|
| 186 | sub getModtime($) { |
|---|
| 187 | my ($fileOrDir) = @_; |
|---|
| 188 | my $modtime = (stat($fileOrDir))[9]; |
|---|
| 189 | return $modtime; |
|---|
| 190 | } |
|---|
| 191 | |
|---|
| 192 | sub checkFigsNewerThan($$); |
|---|
| 193 | sub checkFigsNewerThan($$) { |
|---|
| 194 | my ($figdir,$checkAfter) = @_; |
|---|
| 195 | |
|---|
| 196 | my @sub = (); |
|---|
| 197 | |
|---|
| 198 | opendir(DIR, $figdir) || die "cannot read directory '$figdir' (Reason: $!)"; |
|---|
| 199 | foreach (readdir(DIR)) { |
|---|
| 200 | if ($_ ne '.' and $_ ne '..') { |
|---|
| 201 | my $full = $figdir.'/'.$_; |
|---|
| 202 | if (-d $full) { |
|---|
| 203 | push @sub, $full; |
|---|
| 204 | } |
|---|
| 205 | elsif (-f $full) { |
|---|
| 206 | if ($_ =~ /\.fig$/) { |
|---|
| 207 | my $mod = getModtime($full); |
|---|
| 208 | if ($mod>$checkAfter) { |
|---|
| 209 | sortFig($full); |
|---|
| 210 | } |
|---|
| 211 | } |
|---|
| 212 | } |
|---|
| 213 | } |
|---|
| 214 | } |
|---|
| 215 | closedir(DIR); |
|---|
| 216 | |
|---|
| 217 | foreach (@sub) { |
|---|
| 218 | checkFigsNewerThan($_,$checkAfter); |
|---|
| 219 | } |
|---|
| 220 | } |
|---|
| 221 | |
|---|
| 222 | sub main() { |
|---|
| 223 | my $topdir = $ENV{ARBHOME}; |
|---|
| 224 | |
|---|
| 225 | my $srctools = $topdir.'/SOURCE_TOOLS'; |
|---|
| 226 | my $figbase = $topdir.'/lib/pictures'; |
|---|
| 227 | |
|---|
| 228 | my $last_check_stamp = $srctools.'/sortfig.stamp'; |
|---|
| 229 | unlink($last_check_stamp); # uncomment to force resort of all figs |
|---|
| 230 | # `touch $figbase/ad_branch.fig`; |
|---|
| 231 | |
|---|
| 232 | if (not -d $srctools) { |
|---|
| 233 | die "No such directory '$srctools'\n "; |
|---|
| 234 | } |
|---|
| 235 | else { |
|---|
| 236 | my $checkAfter = 0; |
|---|
| 237 | if (-f $last_check_stamp) { |
|---|
| 238 | $checkAfter = getModtime($last_check_stamp)-5; |
|---|
| 239 | } |
|---|
| 240 | |
|---|
| 241 | `touch $last_check_stamp`; |
|---|
| 242 | checkFigsNewerThan($figbase,$checkAfter); |
|---|
| 243 | } |
|---|
| 244 | } |
|---|
| 245 | |
|---|
| 246 | # ---------------------------------------- |
|---|
| 247 | |
|---|
| 248 | my $args = scalar(@ARGV); |
|---|
| 249 | my $done = 0; |
|---|
| 250 | if ($args==1) { |
|---|
| 251 | my $arg = shift @ARGV; |
|---|
| 252 | if ($arg eq 'doit') { |
|---|
| 253 | main(); |
|---|
| 254 | $done = 1; |
|---|
| 255 | } |
|---|
| 256 | } |
|---|
| 257 | if (not $done) { |
|---|
| 258 | print "Usage: sortfig.pl doit\n"; |
|---|
| 259 | print "Sorts some contents of .fig files, avoiding random useless changes between commits.\n"; |
|---|
| 260 | } |
|---|
| 261 | |
|---|