refdes_renum: Only change conforming refdes values
[geda-gaf/whiteaudio.git] / utils / scripts / refdes_renum
blobbfea4c3df2d4bc7def21b3e4bd42ea0ed3c4bc60
1 #!/usr/bin/perl -w
3 # $Id$
5 # Copyright (C) 2003 Dan McMahill
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 # This script is used to renumber devices in a gschem (part of gEDA)
23 # schematic. This program works on single as well as multi-sheet
24 # schematics.
26 # Usage:
27 # renum.pl file1.sch [file2.sch [file3.sch ...]]
30 # for parsing input options
31 use Getopt::Long;
33 # for ceil function
34 use POSIX;
36 # don't allow -he to be interpreted as --help
37 $Getopt::Long::autoabbrev=0;
39 # my $clear; # reset all refdes
40 &GetOptions(("help" => \&usage,
41 "nocopy" => \$nocopy,
42 "pgskip:100" => \$pgskip,
43 "verbose" => \$verbose,
44 "version" => \&version,
45 "clear" => \$clear,
46 "gentle" => \$gentle,
47 "force" => \$force,
48 ));
50 usage() if $Getopt::Long::error;
53 # By default pgskip is empty. But if the user gives
54 # the --pgskip option with no value, then assign pgskip
55 # to 100.
56 if (defined($pgskip) && ($pgskip == 0)) {
57 $pgskip = 100;
61 # This makes --gentle the default behavior. To
62 # force refdes renumbering, use --force.
63 $gentle = 1;
64 if ( defined($force) || defined($clear) ) {
65 undef($gentle);
68 # Print usage string in event user didn't call out any args
69 usage() unless @ARGV;
71 # Make sure the input schematic exists and we can read it.
72 # Also count number of files to open.
73 $i=0;
74 while(@ARGV) {
75 $fname[$i]=shift(@ARGV);
76 die_sdb(1, "Schematic file $fname[$i] does not exist or can not be read")
77 unless -r $fname[$i];
78 $i++;
80 $filecnt = $i; # number of files to open
81 if($verbose) {print "Found $filecnt files to process\n";}
83 # To refdes an entire design, we normally just loop through all files
84 # and process each one in order. However if --gentle option is set,
85 # we need to make two passes through all files. First to create a hash
86 # of the highest refdeses used, and second to update unnumbered
87 # refdeses. The hash is used to know where to start the refdes numbering.
89 # It gets more complicated. If --pgskip is not set, then we only need
90 # to find the highest refdes overall. But if --pgskip is set, then
91 # we need to know the highest refdes on each *page*. This is accomplished
92 # on the first scan by creating the variable $highdev{$i, $pre} when --pgskip
93 # is defined, but using the variable $highdev{$pre} when it is not.
95 if (defined($gentle)) { # First scan through design to build hash
96 for($i=0; $i < $filecnt; $i++) { # $i is the file index.
97 print "Scanning input file #".($i+1).": $fname[$i]\n";
98 open(NETLIST,"$fname[$i]") or die_sdb(1, "Can't open $fname[$i]: $!\n");
100 while($line = <NETLIST>) { # Iterate over lines found in .sch file.
101 unless( $line =~ /^refdes/) {
102 next;
104 # Found refdes line. Eat the newline.
105 $line =~ s/\n//;
107 # Now extract component prefix and suffix.
108 if($verbose) {print ("Matching $line ....\n");}
109 $line =~ /refdes=([a-zA-Z_]+)(\d+|\??)/i;
110 my $pre = $1;
111 #old behavior: my $suf = $2 ? $2 : "?";
112 # DJW: if neither number nor '?' defined, keep it so, this refdes
113 # is "non-conforming", assume the user wants this.
114 # e.g. multiple-symbol parts have the same refdes on purpose
115 my $suf = $2;
116 if($verbose) {print "Refdes line \"$line\" has pre=$pre suf=$suf\n";}
118 # Now put highest refdes found into $highdev hash.
119 if (defined($pgskip)) {
120 if ($suf eq "?") {
121 # Part has no pre-existing refdes.
122 # Skip to next part.
123 next;
124 } elsif (!defined($highdev{$i, $pre})) {
125 # refdes is not cached, but refdes exists on part.
126 # Create new cache entry
127 if ($suf < $pgskip) {
128 # if pgskip=100, on pg 1, and suf = 23, set highdev=123.
129 $highdev{$i, $pre} = ($i+1)*$pgskip + $suf;
130 } elsif ( ( ($i+1)*$pgskip < $suf) &&
131 ( ($i+2)*$pgskip > $suf) ) {
132 # if pgskip=100, we're on pg 2, and suf = 204, set highdev=204.
133 $highdev{$i, $pre} = $suf;
134 } else {
135 # I don't know what to do!
136 die_sdb(3, "Encountered refdes incompatable with --pgskip setting. Refdes=$pre$suf.\n");
139 } elsif ($highdev{$i, $pre} < $suf) {
140 # part has refdes and refdes is higher than cached
141 # value. Store this new value as highest value.
142 $highdev{$i, $pre} = $suf;
143 } elsif ($highdev{$i, $pre} >= $suf) {
144 # part has refdes, but refdes is lower than cached
145 # high value. Leave it alone.
146 next;
147 } else {
148 # Should never get here
149 die_sdb(4, "Invalid refdes with --pgskip set! Exiting....\n");
152 if($verbose) {
153 print "On page ".($i+1).", caching highest refdes $pre$highdev{$i, $pre}\n";
155 } else {
156 if ($suf eq "?") {
157 # Part has no pre-existing refdes.
158 next;
159 } elsif (!defined($highdev{$pre}) ) {
160 # refdes is not cached, and refdes exists on part. Create new
161 # cache entry
162 $highdev{$pre} = $suf;
163 } elsif ($highdev{$pre} < $suf) {
164 # part has refdes and refdes is higher than cached
165 # value. Store this new value as highest value.
166 $highdev{$pre} = $suf;
167 } elsif ($highdev{$pre} >= $suf) {
168 # part has refdes, but refdes is lower than cached
169 # high value. Leave it alone.
170 next;
171 } else {
172 # Should never get here
173 die_sdb(4, "Invalid refdes! Exiting....\n");
175 if($verbose) {
176 print "Caching highest refdes $pre$highdev{$pre}\n";
180 } # while($line = <NETLIST>)
181 close(NETLIST);
182 } # for($i=0; $i < $filecnt; $i++)
183 } # if (defined($gentle))
186 # OK, now we can read through the netlist file again, assign refdeses,
187 # and write the output file.
188 for($i=0; $i < $filecnt; $i++) { # $i is the file index.
189 print "Now processing input file #".($i+1).": $fname[$i]\n";
190 open(NETLIST,"$fname[$i]") or die_sdb(1, "Can't open $fname[$i]: $!\n");
192 # open output netlist
193 $outfname="$fname[$i].renum";
194 open(OUTNET,">$outfname") or die_sdb(2, "Can't open $outfname: $!\n");
196 # Iterate over lines found in .sch file.
197 while($line = <NETLIST>) {
198 unless( $line =~ /^refdes/) {
199 print OUTNET $line;
200 next;
203 # Found refdes line. Eat the newline.
204 $line =~ s/\n//;
206 # Now extract component prefix and suffix.
207 if($verbose) {print ("Processing $line ....\n");}
208 $line =~ /refdes=([a-zA-Z_]+)(\d+|\??)/i;
209 my $pre = $1;
210 #old behavior: my $suf = $2 ? $2 : "?";
211 # DJW: if neither number nor '?' defined, keep it so, this refdes
212 # is "non-conforming", assume the user wants this.
213 # e.g. multiple-symbol parts have the same refdes on purpose
214 my $suf = $2;
215 if($verbose) {print "Refdes line \"$line\" has pre=$pre suf=$suf\n";}
217 # Now finally update refdes
218 if ($clear) {
219 # Just overwrite all refdeses upon clear
220 if($verbose) {print ("Clearing refdes=$pre$suf\n");}
221 print OUTNET "refdes=$pre?\n";
222 } elsif (defined($pgskip) && defined($gentle)) {
223 # If highdev exists, then start devcnt there, otherwise, start
224 # devcnt at correct value for each page.
225 if (!defined($devcnt{$i, $pre}) && defined($highdev{$i, $pre})) {
226 $devcnt{$i, $pre} = $highdev{$i, $pre};
227 } elsif (!defined($devcnt{$i, $pre}) && !defined($highdev{$i, $pre})) {
228 $devcnt{$i, $pre} = ($i+1)*$pgskip ;
231 if ($suf eq "?") {
232 $devcnt{$i, $pre}++;
233 if ($devcnt{$i, $pre} >= ($i+2)*$pgskip) {
234 print STDERR "ERROR: You are numbering more than $pgskip elements with\n";
235 print STDERR "prefix $pre on this sheet. You will need to either\n";
236 print STDERR "reduce this number or specify a larger step with the\n";
237 print STDERR "--pgskip argument.\n";
238 die_sdb(3, "");
240 print "Renumbering $line to $pre$devcnt{$i, $pre}\n" if($verbose);
241 print OUTNET "refdes=$pre$devcnt{$i, $pre}\n";
242 } else {
243 print "Leaving line=$line alone\n" if($verbose);
244 print OUTNET "refdes=$pre$suf\n";
247 } elsif (defined($pgskip) && !defined($gentle)) {
248 if (!defined($devcnt{$i, $pre})) {
249 $devcnt{$i, $pre} = ($i+1)*$pgskip ;
251 $devcnt{$i, $pre}++;
252 if ($devcnt{$i, $pre} >= ($i+2)*$pgskip) {
253 print STDERR "ERROR: You are numbering more than $pgskip elements with\n";
254 print STDERR "prefix $pre on this sheet. You will need to either\n";
255 print STDERR "reduce this number or specify a larger step with the\n";
256 print STDERR "--pgskip argument.\n";
257 die_sdb(3, "");
259 # DJW: "non-conforming" refdes, do not touch
260 if ($suf eq "") {
261 print "Leaving non-conforming line=$line alone\n" if($verbose);
262 print OUTNET "refdes=$pre$suf\n";
263 } else {
264 print "Renumbering $line to $pre$devcnt{$i, $pre}\n" if($verbose);
265 print OUTNET "refdes=$pre$devcnt{$i, $pre}\n";
268 } elsif (!defined($pgskip) && defined($gentle)) {
269 if (!defined($devcnt{$pre}) && defined($highdev{$pre})) {
270 $devcnt{$pre} = $highdev{$pre};
271 } elsif (!defined($devcnt{$pre}) && !defined($highdev{$pre})) {
272 $devcnt{$pre} = 0;
274 if ($suf eq "?") {
275 $devcnt{$pre}++;
276 print "Renumbering $line to $pre$devcnt{$pre}\n" if($verbose);
277 print OUTNET "refdes=$pre$devcnt{$pre}\n";
278 } else {
279 print "Leaving line=$line alone\n" if($verbose);
280 print OUTNET "refdes=$pre$suf\n";
282 } elsif (!defined($pgskip) && !defined($gentle)) {
283 if (!defined($devcnt{$pre})) {
284 $devcnt{$pre} = 0 ;
286 $devcnt{$pre}++;
287 # DJW: "non-conforming" refdes, do not touch
288 if ($suf eq "") {
289 print "Leaving non-conforming line=$line alone\n" if($verbose);
290 print OUTNET "refdes=$pre$suf\n";
291 } else {
292 print "Renumbering $line to $pre$devcnt{$pre}\n" if($verbose);
293 print OUTNET "refdes=$pre$devcnt{$pre}\n";
296 } else {
297 die_sdb(4, "Invalid state when trying to update refdes! Exiting.....\n");
299 } # while($line = <NETLIST>)
300 close(NETLIST);
301 close(OUTNET);
304 # make this an option to not overwrite the original
305 if( $nocopy ) {
306 print "Leaving page #",$i+1," output in $outfname\n";
308 else {
309 system("mv $outfname $fname[$i]");
313 exit(0);
316 #######################################################################
318 # Subroutines
320 #######################################################################
322 #---------------------------------
323 # usage()
325 # prints program usage
326 #---------------------------------
328 sub usage {
329 my $pname = $0;
330 $pname =~ s/.*\///g;
332 print "Usage:\n\n";
333 print "\t$pname [--nocopy] [--pgskip [number] ] file1 [file2 [file3 ... ] ]\n";
334 print "\t$pname --help\n";
335 print "\t$pname --version\n";
336 print "\n";
337 print "$pname reads a gschem schematic file or files and renumbers all reference\n";
338 print "designators matching the regex ^([a-zA-Z_]+)(\\d+|\\??). The reference\n";
339 print "designators are numbered starting with 1 and the old schematic file is replaced\n";
340 print "by the modified schematic file.\n";
341 print "\n";
342 print "$pname accepts the following options:\n";
343 print "\n";
344 print " --help Displays this help message.\n";
345 print "\n";
346 print " --nocopy If given, this flag leaves the modified files in new files\n";
347 print " whose names are generated by appending a \".renum\" to the\n";
348 print " original file names. The default is to overwrite the original.\n";
349 print "\n";
350 print " --pgskip When this flag is used, components on the first schematic sheet\n";
351 print " are numbered starting with 101. On the second sheet, they start\n";
352 print " with 201, etc Specifying a value gives the step between pages.\n";
353 print " For example --pgskip 10 will start with 11, 21, 31, etc.\n";
354 print "\n";
355 print " --gentle This flag tells refdes_renum to leave any refdeses\n";
356 print " alone if they already have numbers. Use this option to number\n";
357 print " new components in a schematic which has already been numbered.\n";
358 print " Note that --gentle is set by default!\n";
359 print "\n";
360 print " --force Set this flag to renumber all conforming refdeses, whether they\n";
361 print " are already numbered or not.\n";
362 print "\n";
363 print " --verbose Enables verbose output.\n";
364 print "\n";
365 print " --version Shows the version of this program.\n";
366 print "\n\n";
367 print "Return codes:\n\n";
368 print "\t$pname returns the following codes to the shell upon completion:\n";
369 print "\n";
370 print " 0 Program ran successfully.\n";
371 print " 1 Error opening or reading input file.\n";
372 print " 2 Error opening or writing output file.\n";
373 print " 3 Too many components for --pgskip setting.\n";
374 print " 4 Internal error (program bug encountered).\n";
375 print "\n";
376 print "Usage examples:\n\n";
377 print "\tTo renumber the reference designators in a single page schematic\n";
378 print "\tmysch.sch:\n";
379 print "\n";
380 print "\t\t$pname mysch.sch\n";
381 print "\n\n";
382 print "\tTo renumber the reference designators in schematic pages pg1.sch,\n";
383 print "\tpg2.sch and pg3.sch, with the reference designators on pg1.sch\n";
384 print "\tbeginning at 101, the designators on pg2.sch beginning at 201,\n";
385 print "\tand the designators on pg3.sch beginning at 301:\n";
386 print "\n";
387 print "\t\t$pname --pgskip pg1.sch pg2.sch pg3.sch\n";
388 print "\n\n";
389 print "$pname was written by Dan McMahill <dmcmahill\@netbsd.org>\n";
390 print "\n\n";
391 exit(0);
394 #---------------------------------
395 # version()
397 # prints program version
398 #---------------------------------
400 sub version {
401 open(PROG,"$0") or die_sdb(4, "Could not open \"$0\" to find version\n\n");
402 my $pname = $0;
403 $pname =~ s/.*\///g;
405 while($line = <PROG>) {
406 if( $line =~ /^#\s*\$Id.*\$/) {
407 @words = split ' ',,$line;
408 $version = $words[3];
409 $date = $words[4];
410 print "$pname ($0): Version $version, $date\n";
411 exit(0);
414 die_sdb(4, "Could not determine version of \"$0\"\n\n");
417 #-------------------------------------------
418 # die_sdb(rc, string)
420 # Local version of die. Sets return code
421 # for shell, then calls die(string)
422 #--------------------------------------------
423 sub die_sdb {
424 my $rc = $_[0];
425 my $string = $_[1];
426 $! = $rc;
427 die($string);
430 # ----------------------------------------------
432 # Change Log
434 # 2012-01-28 Dan White
435 # When '--force'ing renumbering, only do so for "conforming" refdes values.
436 # A refdes not matching the regex, nominally ending with a number or "?",
437 # remain unchanged. This keeps the script from mangling explicitly-named
438 # refdes' for e.g. multiple-symbol components (each having the same refdes).
440 # $Log$
441 # Revision 1.8 2007-04-17 20:19:23 pcjc2
442 # Merge changes from noscreen branch
444 # Revision 1.2.6.2 2007/04/17 16:19:01 pcjc2
445 # Sync with trunk
447 # Revision 1.7 2007/04/15 23:20:31 sdb
448 # Made --gentle the default behavior of refdes_renum.
450 # Revision 1.7 2007/04/15 SDB
451 # Made --gentle default behavior. Now user must use --force
452 # to get old behavior, which renumbers all refdeses whether
453 # already numbered or not.
455 # Revision 1.6 2007/04/15 00:42:22 sdb
456 # Added --gentle flag to refdes_renum, which won't renumber refdeses
457 # which already have a number.
459 # Revision 1.5 4.9.2007 sdb.
460 # Added --gentle flag, and include logic to handle case
461 # where user mixes --gentle with --pgskip.
463 # Revision 1.4 2007/02/24 18:43:17 pcjc2
464 # Merge changes to date from noscreen branch.
466 # Revision 1.2.6.1 2007/02/11 23:59:10 pcjc2
467 # Sync with trunc
469 # Revision 1.3 2007/02/10 20:46:17 sdb
470 # Added --clear option to clear refdeses. (* jcl *)
472 # Revision 1.2 2005/12/21 00:09:56 danmc
473 # - Fix a bug where when using the --pgskip option, components which were
474 # present on page n, but not on pages n+1 through n+j, and present again
475 # on page n+j+1 got numbered in a strange way. For example, J101 on page
476 # 1, no connectors on page 2, J201 on page 3 instead of J301. Noted by
477 # Stuart Brorson.
479 # - While here allow the user to change the default increment from 100 to whatever
480 # they want.
482 # - Also fix a bug where exactly 101 components with the same refdes prefix
483 # would cause a duplicate refdes on the next page.
485 # Revision 1.1 2003/02/21 03:21:12 ahvezda
486 # Added scripts/refdes_renum written by Dan McMahill