refdes_renum: warn of possible number clash with non-conforming values
[geda-gaf/whiteaudio.git] / utils / scripts / refdes_renum
blob2c56d6a54b8291b019ec212ff97033e2253aa4dc
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 "debug" => \$debug,
45 "version" => \&version,
46 "clear" => \$clear,
47 "gentle" => \$gentle,
48 "force" => \$force,
49 ));
51 usage() if $Getopt::Long::error;
54 # By default pgskip is empty. But if the user gives
55 # the --pgskip option with no value, then assign pgskip
56 # to 100.
57 if (defined($pgskip) && ($pgskip == 0)) {
58 $pgskip = 100;
62 # This makes --gentle the default behavior. To
63 # force refdes renumbering, use --force.
64 $gentle = 1;
65 if ( defined($force) || defined($clear) ) {
66 undef($gentle);
70 # Option --debug implies --verbose
71 if ( defined($debug) ) {
72 $verbose=1;
76 # Print usage string in event user didn't call out any args
77 usage() unless @ARGV;
79 # Make sure the input schematic exists and we can read it.
80 # Also count number of files to open.
81 $i=0;
82 while(@ARGV) {
83 $fname[$i]=shift(@ARGV);
84 die_sdb(1, "Schematic file $fname[$i] does not exist or can not be read")
85 unless -r $fname[$i];
86 $i++;
88 $filecnt = $i; # number of files to open
89 if($debug) {print "Found $filecnt files to process\n";}
91 # To refdes an entire design, we normally just loop through all files
92 # and process each one in order. However if --gentle option is set,
93 # we need to make two passes through all files. First to create a hash
94 # of the highest refdeses used, and second to update unnumbered
95 # refdeses. The hash is used to know where to start the refdes numbering.
97 # It gets more complicated. If --pgskip is not set, then we only need
98 # to find the highest refdes overall. But if --pgskip is set, then
99 # we need to know the highest refdes on each *page*. This is accomplished
100 # on the first scan by creating the variable $highdev{$i, $pre} when --pgskip
101 # is defined, but using the variable $highdev{$pre} when it is not.
103 if (defined($gentle)) { # First scan through design to build hash
104 for($i=0; $i < $filecnt; $i++) { # $i is the file index.
105 print "Scanning input file #".($i+1).": $fname[$i]\n";
106 open(NETLIST,"$fname[$i]") or die_sdb(1, "Can't open $fname[$i]: $!\n");
108 while($line = <NETLIST>) { # Iterate over lines found in .sch file.
109 unless( $line =~ /^refdes/) {
110 next;
112 # Found refdes line. Eat the newline.
113 $line =~ s/\n//;
115 # Now extract component prefix, number, and suffix.
116 if($debug) {print ("Matching $line ....\n");}
117 $line =~ /refdes=([a-zA-Z_]+)(\d+|\??)(.*)/i;
118 my $pre = $1;
119 #old behavior: my $num = $2 ? $2 : "?";
120 # DJW: if (neither number nor '?' defined) or there is a suffix,
121 # keep it so, this refdes is "non-conforming", assume the user wants this.
122 # e.g. multiple-symbol parts have the same refdes on purpose
123 my $num = $2;
124 my $suf = $3;
125 if($debug) {print "Refdes line \"$line\" has pre=$pre num=$num suf=$suf\n";}
127 # Now put highest refdes found into $highdev hash.
128 if (defined($pgskip)) {
129 if (!($num =~ /\d+/)) {
130 # Part has no pre-existing refdes.
131 # Skip to next part.
132 next;
133 } elsif (!defined($highdev{$i, $pre})) {
134 # refdes is not cached, but refdes exists on part.
135 # Create new cache entry
136 if ($num < $pgskip) {
137 # if pgskip=100, on pg 1, and suf = 23, set highdev=123.
138 $highdev{$i, $pre} = ($i+1)*$pgskip + $num;
139 } elsif ( ( ($i+1)*$pgskip < $num) &&
140 ( ($i+2)*$pgskip > $num) ) {
141 # if pgskip=100, we're on pg 2, and suf = 204, set highdev=204.
142 $highdev{$i, $pre} = $num;
143 } else {
144 # I don't know what to do!
145 die_sdb(3, "Encountered refdes incompatable with --pgskip setting. Refdes=$pre$num$suf.\n");
148 } elsif ($highdev{$i, $pre} < $num) {
149 # part has refdes and refdes is higher than cached
150 # value. Store this new value as highest value.
151 $highdev{$i, $pre} = $num;
152 } elsif ($highdev{$i, $pre} >= $num) {
153 # part has refdes, but refdes is lower than cached
154 # high value. Leave it alone.
155 next;
156 } else {
157 # Should never get here
158 die_sdb(4, "Invalid refdes with --pgskip set! Exiting....\n");
161 if($debug) {
162 print "On page ".($i+1).", caching highest refdes $pre$highdev{$i, $pre}\n";
164 } else {
165 if (!($num =~ /\d+/)) {
166 # Part has no pre-existing refdes.
167 next;
168 } elsif (!defined($highdev{$pre}) ) {
169 # refdes is not cached, and refdes exists on part. Create new
170 # cache entry
171 $highdev{$pre} = $num;
172 } elsif ($highdev{$pre} < $num) {
173 # part has refdes and refdes is higher than cached
174 # value. Store this new value as highest value.
175 $highdev{$pre} = $num;
176 } elsif ($highdev{$pre} >= $num) {
177 # part has refdes, but refdes is lower than cached
178 # high value. Leave it alone.
179 next;
180 } else {
181 # Should never get here
182 die_sdb(4, "Invalid refdes! Exiting....\n");
184 if($debug) {
185 print "Caching highest refdes $pre$highdev{$pre}\n";
189 } # while($line = <NETLIST>)
190 close(NETLIST);
191 } # for($i=0; $i < $filecnt; $i++)
192 } # if (defined($gentle))
195 # OK, now we can read through the netlist file again, assign refdeses,
196 # and write the output file.
197 for($i=0; $i < $filecnt; $i++) { # $i is the file index.
198 print "Now processing input file #".($i+1).": $fname[$i]\n";
199 open(NETLIST,"$fname[$i]") or die_sdb(1, "Can't open $fname[$i]: $!\n");
201 # open output netlist
202 $outfname="$fname[$i].renum";
203 open(OUTNET,">$outfname") or die_sdb(2, "Can't open $outfname: $!\n");
205 # Iterate over lines found in .sch file.
206 while($line = <NETLIST>) {
207 unless( $line =~ /^refdes/) {
208 print OUTNET $line;
209 next;
212 # Found refdes line. Eat the newline.
213 $line =~ s/\n//;
215 # Now extract component prefix, number, and suffix.
216 if($debug) {print ("Processing $line ....\n");}
217 $line =~ /refdes=([a-zA-Z_]+)(\d+|\??)(.*)/i;
218 my $pre = $1;
219 #old behavior: my $num = $2 ? $2 : "?";
220 # DJW: if neither number nor '?' defined, keep it so, this refdes
221 # is "non-conforming", assume the user wants this.
222 # e.g. multiple-symbol parts have the same refdes on purpose
223 my $num = $2;
224 my $suf = $3;
225 if($debug) {print "Refdes line \"$line\" has pre=$pre num=$num suf=$suf\n";}
227 # Now finally update refdes
228 if ($clear) {
229 # Just overwrite all refdeses upon clear
230 if($verbose) {print ("Clearing refdes=$pre$num$suf\n");}
231 print OUTNET "refdes=$pre?\n";
232 } elsif (defined($pgskip) && defined($gentle)) {
233 # If highdev exists, then start devcnt there, otherwise, start
234 # devcnt at correct value for each page.
235 if (!defined($devcnt{$i, $pre}) && defined($highdev{$i, $pre})) {
236 $devcnt{$i, $pre} = $highdev{$i, $pre};
237 } elsif (!defined($devcnt{$i, $pre}) && !defined($highdev{$i, $pre})) {
238 $devcnt{$i, $pre} = ($i+1)*$pgskip ;
241 if ($num eq "?" and $suf eq "") {
242 $devcnt{$i, $pre}++;
243 if ($devcnt{$i, $pre} >= ($i+2)*$pgskip) {
244 print STDERR "ERROR: You are numbering more than $pgskip elements with\n";
245 print STDERR "prefix $pre on this sheet. You will need to either\n";
246 print STDERR "reduce this number or specify a larger step with the\n";
247 print STDERR "--pgskip argument.\n";
248 die_sdb(3, "");
250 print "Renumbering $line to $pre$devcnt{$i, $pre}\n" if($verbose);
251 print OUTNET "refdes=$pre$devcnt{$i, $pre}\n";
252 } else {
253 if ($num eq "?") {
254 print STDERR "*** WARNING: Leaving line=$line alone in --gentle mode\n";
255 print "*** WARNING: Leaving line=$line alone in --gentle mode\n";
257 print "Leaving line=$line alone\n" if($debug);
258 print OUTNET "refdes=$pre$num$suf\n";
261 } elsif (defined($pgskip) && !defined($gentle)) {
262 if (!defined($devcnt{$i, $pre})) {
263 $devcnt{$i, $pre} = ($i+1)*$pgskip ;
265 $devcnt{$i, $pre}++;
266 if ($devcnt{$i, $pre} >= ($i+2)*$pgskip) {
267 print STDERR "ERROR: You are numbering more than $pgskip elements with\n";
268 print STDERR "prefix $pre on this sheet. You will need to either\n";
269 print STDERR "reduce this number or specify a larger step with the\n";
270 print STDERR "--pgskip argument.\n";
271 die_sdb(3, "");
273 # DJW: "non-conforming" refdes, do not touch
274 if ($num eq "" or $suf ne "") {
275 if ($num ne "" and $num <= $devcnt{$i, $pre}) {
276 print STDERR "WARNING: number $num is already used, possible conflict with $pre$num$suf\n";
277 print "WARNING: number $num is already used, possible conflict with $pre$num$suf\n";
279 print "Leaving non-conforming line=$line alone\n" if($verbose);
280 print OUTNET "refdes=$pre$num$suf\n";
281 } else {
282 print "Renumbering $line to $pre$devcnt{$i, $pre}\n" if($verbose);
283 print OUTNET "refdes=$pre$devcnt{$i, $pre}\n";
286 } elsif (!defined($pgskip) && defined($gentle)) {
287 if (!defined($devcnt{$pre}) && defined($highdev{$pre})) {
288 $devcnt{$pre} = $highdev{$pre};
289 } elsif (!defined($devcnt{$pre}) && !defined($highdev{$pre})) {
290 $devcnt{$pre} = 0;
292 if ($num eq "?" and $suf eq "") {
293 $devcnt{$pre}++;
294 print "Renumbering $line to $pre$devcnt{$pre}\n" if($verbose);
295 print OUTNET "refdes=$pre$devcnt{$pre}\n";
296 } else {
297 if ($num eq "?") {
298 print STDERR "*** WARNING: Leaving line=$line alone in --gentle mode\n";
299 print "*** WARNING: Leaving line=$line alone in --gentle mode\n";
301 print "Leaving line=$line alone\n" if($debug);
302 print OUTNET "refdes=$pre$num$suf\n";
304 } elsif (!defined($pgskip) && !defined($gentle)) {
305 if (!defined($devcnt{$pre})) {
306 $devcnt{$pre} = 0 ;
308 $devcnt{$pre}++;
309 # DJW: "non-conforming" refdes, do not touch
310 if ($num eq "" or $suf ne "") {
311 if ($num ne "" and $num <= $devcnt{$i, $pre}) {
312 print STDERR "WARNING: number $num is already used, possible conflict with $pre$num$suf\n";
313 print "WARNING: number $num is already used, possible conflict with $pre$num$suf\n";
315 print "Leaving non-conforming line=$line alone\n" if($verbose);
316 print OUTNET "refdes=$pre$num$suf\n";
317 } else {
318 print "Renumbering $line to $pre$devcnt{$pre}\n" if($verbose);
319 print OUTNET "refdes=$pre$devcnt{$pre}\n";
322 } else {
323 die_sdb(4, "Invalid state when trying to update refdes! Exiting.....\n");
325 } # while($line = <NETLIST>)
326 close(NETLIST);
327 close(OUTNET);
330 # make this an option to not overwrite the original
331 if( $nocopy ) {
332 print "Leaving page #",$i+1," output in $outfname\n";
334 else {
335 system("mv $outfname $fname[$i]");
339 exit(0);
342 #######################################################################
344 # Subroutines
346 #######################################################################
348 #---------------------------------
349 # usage()
351 # prints program usage
352 #---------------------------------
354 sub usage {
355 my $pname = $0;
356 $pname =~ s/.*\///g;
358 print "Usage:\n\n";
359 print "\t$pname [--nocopy] [--pgskip [number] ] file1 [file2 [file3 ... ] ]\n";
360 print "\t$pname --help\n";
361 print "\t$pname --version\n";
362 print "\n";
363 print "$pname reads a gschem schematic file or files and renumbers all reference\n";
364 print "designators matching the regex ^([a-zA-Z_]+)(\\d+|\\??)\$. Values with a\n";
365 print "suffix after the number or placeholder are detected but left untouched in\n";
366 print "all modes. The reference designators are numbered starting with 1 and the old\n";
367 print "schematic file is replaced by the modified schematic file.\n";
368 print "\n";
369 print "$pname accepts the following options:\n";
370 print "\n";
371 print " --help Displays this help message.\n";
372 print "\n";
373 print " --nocopy If given, this flag leaves the modified files in new files\n";
374 print " whose names are generated by appending a \".renum\" to the\n";
375 print " original file names. The default is to overwrite the original.\n";
376 print "\n";
377 print " --pgskip When this flag is used, components on the first schematic sheet\n";
378 print " are numbered starting with 101. On the second sheet, they start\n";
379 print " with 201, etc Specifying a value gives the step between pages.\n";
380 print " For example --pgskip 10 will start with 11, 21, 31, etc.\n";
381 print "\n";
382 print " --gentle This flag tells refdes_renum to leave any refdeses\n";
383 print " alone if they already have numbers. Use this option to number\n";
384 print " new components in a schematic which has already been numbered.\n";
385 print " Note that --gentle is set by default!\n";
386 print "\n";
387 print " --force Set this flag to renumber all conforming refdeses, whether they\n";
388 print " are already numbered or not.\n";
389 print "\n";
390 print " --verbose Enables verbose output.\n";
391 print "\n";
392 print " --debug Enables extra-verbose output.\n";
393 print "\n";
394 print " --version Shows the version of this program.\n";
395 print "\n\n";
396 print "Return codes:\n\n";
397 print "\t$pname returns the following codes to the shell upon completion:\n";
398 print "\n";
399 print " 0 Program ran successfully.\n";
400 print " 1 Error opening or reading input file.\n";
401 print " 2 Error opening or writing output file.\n";
402 print " 3 Too many components for --pgskip setting.\n";
403 print " 4 Internal error (program bug encountered).\n";
404 print "\n";
405 print "Usage examples:\n\n";
406 print "\tTo renumber the reference designators in a single page schematic\n";
407 print "\tmysch.sch:\n";
408 print "\n";
409 print "\t\t$pname mysch.sch\n";
410 print "\n\n";
411 print "\tTo renumber the reference designators in schematic pages pg1.sch,\n";
412 print "\tpg2.sch and pg3.sch, with the reference designators on pg1.sch\n";
413 print "\tbeginning at 101, the designators on pg2.sch beginning at 201,\n";
414 print "\tand the designators on pg3.sch beginning at 301:\n";
415 print "\n";
416 print "\t\t$pname --pgskip pg1.sch pg2.sch pg3.sch\n";
417 print "\n\n";
418 print "$pname was written by Dan McMahill <dmcmahill\@netbsd.org>\n";
419 print "\n\n";
420 exit(0);
423 #---------------------------------
424 # version()
426 # prints program version
427 #---------------------------------
429 sub version {
430 open(PROG,"$0") or die_sdb(4, "Could not open \"$0\" to find version\n\n");
431 my $pname = $0;
432 $pname =~ s/.*\///g;
434 while($line = <PROG>) {
435 if( $line =~ /^#\s*\$Id.*\$/) {
436 @words = split ' ',,$line;
437 $version = $words[3];
438 $date = $words[4];
439 print "$pname ($0): Version $version, $date\n";
440 exit(0);
443 die_sdb(4, "Could not determine version of \"$0\"\n\n");
446 #-------------------------------------------
447 # die_sdb(rc, string)
449 # Local version of die. Sets return code
450 # for shell, then calls die(string)
451 #--------------------------------------------
452 sub die_sdb {
453 my $rc = $_[0];
454 my $string = $_[1];
455 $! = $rc;
456 die($string);
459 # ----------------------------------------------
461 # Change Log
463 # 2012-01-28 Dan White
464 # When '--force'ing renumbering, only do so for "conforming" refdes values.
465 # A refdes not matching the regex, nominally ending with a number or "?",
466 # remain unchanged. This keeps the script from mangling explicitly-named
467 # refdes' for e.g. multiple-symbol components (each having the same refdes).
469 # $Log$
470 # Revision 1.8 2007-04-17 20:19:23 pcjc2
471 # Merge changes from noscreen branch
473 # Revision 1.2.6.2 2007/04/17 16:19:01 pcjc2
474 # Sync with trunk
476 # Revision 1.7 2007/04/15 23:20:31 sdb
477 # Made --gentle the default behavior of refdes_renum.
479 # Revision 1.7 2007/04/15 SDB
480 # Made --gentle default behavior. Now user must use --force
481 # to get old behavior, which renumbers all refdeses whether
482 # already numbered or not.
484 # Revision 1.6 2007/04/15 00:42:22 sdb
485 # Added --gentle flag to refdes_renum, which won't renumber refdeses
486 # which already have a number.
488 # Revision 1.5 4.9.2007 sdb.
489 # Added --gentle flag, and include logic to handle case
490 # where user mixes --gentle with --pgskip.
492 # Revision 1.4 2007/02/24 18:43:17 pcjc2
493 # Merge changes to date from noscreen branch.
495 # Revision 1.2.6.1 2007/02/11 23:59:10 pcjc2
496 # Sync with trunc
498 # Revision 1.3 2007/02/10 20:46:17 sdb
499 # Added --clear option to clear refdeses. (* jcl *)
501 # Revision 1.2 2005/12/21 00:09:56 danmc
502 # - Fix a bug where when using the --pgskip option, components which were
503 # present on page n, but not on pages n+1 through n+j, and present again
504 # on page n+j+1 got numbered in a strange way. For example, J101 on page
505 # 1, no connectors on page 2, J201 on page 3 instead of J301. Noted by
506 # Stuart Brorson.
508 # - While here allow the user to change the default increment from 100 to whatever
509 # they want.
511 # - Also fix a bug where exactly 101 components with the same refdes prefix
512 # would cause a duplicate refdes on the next page.
514 # Revision 1.1 2003/02/21 03:21:12 ahvezda
515 # Added scripts/refdes_renum written by Dan McMahill