gsch2pcb: Fix broken gnetlist backend.
[geda-gaf/whiteaudio.git] / utils / gxyrs / gxyrs.pl
blob3b7f21d9281002d6bd7b873b76bd02c8e67f17bc
1 #!/usr/bin/perl -w
3 # Copyright (C) 2008 Carlos Nieves Onega
4 # Copyright (C) 2008 other contributors
5 # (see ChangeLog or SCM history for details)
7 # This file is part of gxyrs.
9 # This program is free software; you can redistribute it and/or
10 # modify it under the terms of the GNU General Public License
11 # as published by the Free Software Foundation; either version 2
12 # of the License, or (at your option) any later version.
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 # 02110-1301, USA.
24 use strict;
25 use warnings;
27 use lib 'GEDADATADIR/perl/lib'; # Where gxyrs package is located
29 use gxyrs;
30 use gxyrs qw($CASE_INSENSITIVE);
32 # for parsing input options
33 use Getopt::Long;
35 use vars qw {
36 $REF_COL $FOOTPRINT_COL $X_COL $Y_COL $ANGLE_COL
37 $LAYER_COL $VALUE_COL $CASE_INSENSITIVE $LINE_NUMBER
38 $LINE $TITLE_LINE
41 my $tempfile;
44 # Set these for debugging purposes
45 my $DEBUG = 0;
46 my $DEBUG_GUESSING = 0;
47 my $DEBUG_COL_LENGTH = 0;
48 my $DEBUG_RETURN_CODE = 0;
50 # Initialize global variables
51 $REF_COL = -1;
52 $FOOTPRINT_COL = -1;
53 $X_COL = -1;
54 $Y_COL = -1;
55 $ANGLE_COL = -1;
56 $LAYER_COL = -1;
57 $VALUE_COL = -1;
59 #######################################################################
61 # Subroutines
63 #######################################################################
66 #---------------------------------
67 # guess_file_format()
69 # Check current line and try to guess column numbers by
70 # testing if it is the title line and there are some title keywords.
71 #---------------------------------
72 sub guess_file_format () {
73 my $num_elems = scalar(@LINE);
74 my @positions = ();
75 my $position_repeated=0;
77 if ($DEBUG_GUESSING == 1) {
78 print "guess_file_format processing line $LINE_NUMBER.\n";
79 print "Line: @LINE\n";
80 print "Number of elements in line: $num_elems.\n";
83 # Remove '#' and spacer characters from the beginning of first element
84 while ($LINE[0] =~ /^#/) {
85 $LINE[0] = substr($LINE[0], 1);
87 while ($LINE[0] =~ /^\s+/) {
88 $LINE[0] = substr($LINE[0], 1);
92 # Now try to guess column numbers from column titles.
93 for (my $i = 0; $i <= $num_elems-1; $i++) {
94 if ($DEBUG_GUESSING == 1) {
95 print "Testing element $i: ".$LINE[$i].".\n";
97 if ( ($LINE[$i] =~ /^Designator$/) ||
98 ($LINE[$i] =~ /^RefDesignator$/) ||
99 ($LINE[$i] =~ /^RefDes$/)
101 if ($DEBUG_GUESSING) {
102 print "Found reference column: ".($i+1).".\n";
104 $REF_COL = $i+1;
105 if (grep(/\b$REF_COL\b/, @positions)) {
106 $position_repeated = 1;
108 push(@positions,$REF_COL);
110 if ( ($LINE[$i] =~ /^Footprint$/) ||
111 ($LINE[$i] =~ /^TopCell$/) ||
112 ( ($LINE[$i] =~ /^Description$/) &&
113 (! grep(/\bTopCell\b/,@LINE)) )
115 if ($DEBUG_GUESSING) {
116 print "Found footprint column: ".($i+1).".\n";
118 $FOOTPRINT_COL = $i+1;
119 if (grep(/\b$FOOTPRINT_COL\b/, @positions)) {
120 $position_repeated = 1;
122 push(@positions,$FOOTPRINT_COL);
124 if ( ($LINE[$i] =~ /^Mid X$/) ||
125 ($LINE[$i] =~ /^X$/)
127 if ($DEBUG_GUESSING) {
128 print "Found X column: ".($i+1).".\n";
130 $X_COL = $i+1;
132 if ( ($LINE[$i] =~ /^Mid Y$/) ||
133 ($LINE[$i] =~ /^Y$/)
135 if ($DEBUG_GUESSING) {
136 print "Found Y column: ".($i+1).".\n";
138 $Y_COL = $i+1;
139 if (grep(/\b$Y_COL\b/, @positions)) {
140 $position_repeated = 1;
142 push(@positions,$Y_COL);
144 if ( ($LINE[$i] =~ /^[Rr]otation$/) ||
145 ($LINE[$i] =~ /^Rot$/)
148 if ($DEBUG_GUESSING) {
149 print "Found angle column: ".($i+1).".\n";
151 $ANGLE_COL = $i+1;
152 if (grep(/\b$ANGLE_COL\b/, @positions)) {
153 $position_repeated = 1;
155 push(@positions,$ANGLE_COL);
157 if ( ($LINE[$i] =~ /^TB$/) ||
158 ($LINE[$i] =~ /^Side$/) ||
159 ($LINE[$i] =~ /^top\/bottom$/)
161 if ($DEBUG_GUESSING) {
162 print "Found layer column: ".($i+1).".\n";
164 $LAYER_COL = $i+1;
165 if (grep(/\b$LAYER_COL\b/, @positions)) {
166 $position_repeated = 1;
168 push(@positions,$LAYER_COL);
170 if ( ($LINE[$i] =~ /^Comment$/) ||
171 ($LINE[$i] =~ /^PartNumber$/) ||
172 ($LINE[$i] =~ /^Value$/)
174 if ($DEBUG_GUESSING) {
175 print "Found value column: ".($i+1).".\n";
177 $VALUE_COL = $i+1;
178 if (grep(/\b$VALUE_COL\b/, @positions)) {
179 $position_repeated = 1;
181 push(@positions,$VALUE_COL);
185 # If there is any repeated position, warn the user.
186 if ($position_repeated == 1) {
187 print STDERR "A position was repeated while guessing file format.\n";
189 # If not all columns were guessed, or a position is repeated then reset all.
190 if ( ($position_repeated == 1) ||
191 ($REF_COL == -1) ||
192 ($FOOTPRINT_COL == -1) ||
193 ($X_COL == -1) ||
194 ($Y_COL == -1) ||
195 ($ANGLE_COL == -1) ||
196 ($LAYER_COL == -1) ||
197 ($VALUE_COL == -1)
200 # All columns should be defined in the same line.
201 $REF_COL = -1;
202 $FOOTPRINT_COL = -1;
203 $X_COL = -1;
204 $Y_COL = -1;
205 $ANGLE_COL = -1;
206 $LAYER_COL = -1;
207 $VALUE_COL = -1;
210 if ($DEBUG_GUESSING == 1) {
211 print "guess_file_format ended processing line $LINE_NUMBER.\n";
216 #---------------------------------
217 # stdin()
219 # open a temp file, to allow multiple passes to the input file.
220 #---------------------------------
221 sub stdin () {
222 $tempfile = 'temp_'.time() . '_' . int(rand(1000000));
223 open(TEMP, "+>$tempfile") or print STDERR $!;
224 local $/ = undef;
225 print TEMP <STDIN>;
226 seek(TEMP, 0, 0);
227 \*TEMP;
231 #---------------------------------
232 # usage()
234 # prints program usage
235 #---------------------------------
236 sub usage {
237 print "Usage:\n";
238 print " gxyrs [--tabulate] <input_file> --adjust <adjust_file> --output <outputfile> \n";
239 exit(0);
243 #######################################################################
245 # Main program
247 #######################################################################
252 my @fname=();
253 my ($file_in, $adjust_file, $file_out);
254 my ($this_line, $line_out);
255 my @lengths;
256 my $output_delimiter="";
258 # Delimiters to try when guessing file format.
259 my @delimiters = ('\s+', ',\s*');
260 my @delimiters_char = (' ', ','); # Used when writing the output file
261 my $delimiter_index=-1;
263 my $pass;
264 my $total_passes = scalar(@delimiters) + 1;
265 my $guessing_file_format;
266 my $is_comment = 0; # Mark the current line as a comment.
267 my $eval_string;
269 my $tabulate=0; # Output should be tabulated or not.
270 my $verbose;
271 my $process_comments;
273 my $return_code;
275 # Comparisons are case insensitive by default.
276 $CASE_INSENSITIVE=0;
278 &GetOptions(("help" => \&usage,
279 "verbose" => \$verbose,
280 "process-comments" => \$process_comments,
281 "tabulate!" => \$tabulate,
282 "caseinsensitive!" => \$CASE_INSENSITIVE,
283 "adjust=s" => \$adjust_file,
284 "eval=s" => \$eval_string,
285 "output=s" => \$file_out,
286 "output-delimiter=s" => \$output_delimiter,
289 if ($Getopt::Long::error) {
290 print STDERR "Wrong arguments.\n";
291 usage();
294 # Print usage string in event user didn't call out any args
295 usage() unless @ARGV;
297 # Make sure the input schematic exists and we can read it.
298 # Also count number of files to open.
299 while(@ARGV) {
300 my $i=0;
301 $fname[$i]=shift(@ARGV);
302 print "Checking argument $fname[$i].\n";
303 if (! -r $fname[$i]) {
304 die("Input file $fname[$i] does not exist or can not be read");
306 $i++;
309 $file_in = $fname[0];
311 # Print usage string in event user didn't specify input, adjust
312 # and output files.
313 if (! ($file_in && $file_out && ($adjust_file || $eval_string))) {
314 print STDERR "Nothing to do.\n";
315 if ($eval_string) {
316 print STDERR "Eval string: $eval_string.\n";
318 if ($adjust_file) {
319 print STDERR "Commands file: $adjust_file.\n";
321 usage();
325 if ($file_in !~ '^-$') {
326 open(FILE_IN, $file_in) || die ("Can't open input file $file_in: $!");
328 else {
329 *FILE_IN = &stdin();
332 if (length($output_delimiter) > 1) {
333 die ("Output delimiter $output_delimiter must be a character.");
336 # Don't know yet the file format.
337 $guessing_file_format = 1;
338 $TITLE_LINE = 0;
340 # Last pass is to process the file. Others are for guessing the
341 # file format with several delimiters (one for each element in @delimiters.
342 for ($pass = 1; $pass <= $total_passes; $pass++) {
343 if (seek(FILE_IN, 0, 0) != 1) {
344 print STDERR "Error when seek input file to 0\n";
346 if ($pass >= $total_passes) {
347 open(FILE_OUT, "> ".$file_out) || die ("Can't open output file $file_out: $!");
350 # Set the return code to 0
351 $return_code = 0;
352 if ($DEBUG_RETURN_CODE) {
353 print "Setting global return code to 0.\n";
356 # Parse file
357 $LINE_NUMBER = 0;
358 print "Pass number: $pass.\n" if ($DEBUG);
359 while ( <FILE_IN> ) {
360 $this_line=$_;
361 $_ = $this_line;
363 # Remove end line characters.
364 chomp($this_line);
366 # For the last pass, use the delimiter guessed.
367 # For the others, get the delimiter from @delimiters array.
368 if ($pass < $total_passes) {
369 my $this_delimiter = $delimiters[$pass-1];
370 @LINE = split ($this_delimiter, $this_line);
371 } else {
372 my $this_delimiter = $delimiters[$delimiter_index];
373 @LINE = split ($this_delimiter, $this_line);
375 $LINE_NUMBER += 1;
376 next if /^@/;
377 next if /^q/;
379 # Test if the line is a comment
380 # (which shouldn't be processed).
381 if ( ($this_line =~ /^\s*#/) ||
382 ($this_line =~ /^\s*$/) ) {
383 if ($DEBUG) {
384 print "Line is comment: @LINE\n";
386 $is_comment = 1;
387 } else {
388 $is_comment = 0;
391 # First, some column titles are separated by spaces, so join them.
392 if ($LINE_NUMBER == 1) {
393 my $num_elems = scalar(@LINE);
394 for (my $i = 0; $i <= $num_elems-1; $i++) {
395 if ($i <= $num_elems-2) {
396 # Join titles like "Pad X", "Pad Y", "Mid X", "Mid Y",
397 # "ref X", "ref Y" (Protel output)
398 if ( ( ($LINE[$i] =~ /^Pad$/) &&
399 ($LINE[$i+1] =~/^[XY]$/) ) ||
400 ( ($LINE[$i] =~ /^Mid$/) &&
401 ($LINE[$i+1] =~/^[XY]$/) ) ||
402 ( ($LINE[$i] =~ /^Ref$/) &&
403 ($LINE[$i+1] =~/^[XY]$/) )
405 $LINE[$i] = $LINE[$i]." ".$LINE[$i+1];
406 for (my $j = $i+1; $j <= $num_elems-2; $j++) {
407 $LINE[$j] = $LINE[$j+1];
409 delete $LINE[$num_elems-1];
410 $num_elems -= 1;
417 if ( ($pass != $total_passes) &&
418 $guessing_file_format ) {
419 my $old_guessing_file_format = $guessing_file_format;
420 # Try to guess the file format
421 guess_file_format();
422 # Check if already guessed the file format.
423 $guessing_file_format = ( ($REF_COL == -1) ||
424 ($FOOTPRINT_COL == -1) ||
425 ($X_COL == -1) ||
426 ($Y_COL == -1) ||
427 ($ANGLE_COL == -1) ||
428 ($LAYER_COL == -1) ||
429 ($VALUE_COL == -1) );
431 if (!$guessing_file_format) {
432 if ($old_guessing_file_format == 1) {
433 $TITLE_LINE = $LINE_NUMBER;
435 if ($verbose) {
436 print "Found file format. ";
437 print "Delimiter is $delimiters[$pass-1].\n";
440 $delimiter_index = $pass-1;
444 if (!$guessing_file_format) {
445 if (($is_comment == 0) || $process_comments) {
446 my $rc ;
448 # If it's parsing the file then keep the number
449 # of columns constant, joining all the fields
450 # in the last one.
451 while ( (@LINE > @lengths) &&
452 (scalar(@lengths) > 0) ) {
453 $LINE[@LINE-2] = $LINE[@LINE-2].
454 $delimiters_char[$delimiter_index].$LINE[@LINE-1];
455 pop @LINE;
458 if ($DEBUG_RETURN_CODE) {
459 print "Global return code before evaluation: $return_code.\n";
462 # Run the adjust file.
463 if (defined $adjust_file) {
464 $rc = do $adjust_file ||
465 die("Can't use adjust file: $!\n");
466 } else {
467 #print "Eval: $eval_string\n";
468 $rc = eval $eval_string;
470 if ($@) {
471 print STDERR "Error: ".$@."\n";
472 $rc = -10;
475 # Set $return_code based on priorities.
476 # 1 means the command found a match and was succesful
477 # at least one time in the whole file.
478 # 0 means there was no match.
479 # -1 means there was an error.
480 # -2 means there was a warning.
481 if ($DEBUG_RETURN_CODE) {
482 print "Return code $rc after evaluation.\n";
484 if ($rc == -1) {
485 $return_code = -1;
487 elsif (($rc == -2) && ($return_code >= 0)) {
488 $return_code = -2;
490 elsif (($rc == 1) && ($return_code == 0)) {
491 $return_code = 1;
493 elsif (($rc == 0) && ($return_code == 1)) {
494 $return_code = 1;
496 elsif (($rc >= 0) && ($return_code < 0)) {
497 # Do nothing
499 else {
500 $return_code = $rc;
502 if ($DEBUG_RETURN_CODE) {
503 print "Global return code: $return_code.\n";
506 if ( ($pass != $total_passes) &&
507 ( ($is_comment == 0) || $process_comments) ) {
508 if ($DEBUG_COL_LENGTH == 1) {
509 print "Measuring column length of line: ".
510 "'$this_line'\n";
512 # Calcule max length array
513 if ($tabulate) {
514 my $j=0;
515 for (my $i=0; $i <= scalar(@LINE)-1; $i++) {
516 # Allow adding more lines, and checking
517 # element's length right.
518 if ($LINE[$i] eq "\n") {
519 $j = $i+1;
520 next;
522 if (! exists $lengths[$i-$j]) {
523 $lengths[$i-$j]= length($LINE[$i]);
525 elsif ($lengths[$i-$j] < length($LINE[$i]) ) {
526 $lengths[$i-$j]= length($LINE[$i]);
528 if ($DEBUG_COL_LENGTH == 1) {
529 print "Column ".($i-$j+1).
530 ", length: $lengths[$i].\n";
536 # Print the result after processing
537 if ($pass >= $total_passes) {
538 if (($is_comment == 1) && !$process_comments) {
539 print FILE_OUT "$this_line\n";
541 elsif (@LINE > 0) {
542 my $delimiter;
543 my $j=0;
545 # Set the output delimiter
546 if (length($output_delimiter) != 0) {
547 $delimiter = $output_delimiter;
549 else {
550 $delimiter = $delimiters_char[$delimiter_index];
553 # Write output
554 for (my $i=0; $i <= scalar(@LINE)-1; $i++) {
555 # Allow adding more lines, and handle
556 # new lines.
557 if ($LINE[$i] eq "\n") {
558 print FILE_OUT "\n";
559 $j = $i+1;
560 next;
562 if (($i-$j) > scalar(@lengths)-1) {
563 # If column length array has no number
564 # for this column, print it as is.
565 print FILE_OUT $LINE[$i];
566 } else {
567 printf FILE_OUT "%-$lengths[$i-$j]s",$LINE[$i];
569 # print the last column without delimiter
570 if ( ($i < @LINE-1) &&
571 (!($LINE[$i+1] eq "\n"))) {
572 print FILE_OUT $delimiter;
575 print FILE_OUT "\n";
581 # Close output file.
582 if ($pass >= $total_passes) {
583 close(FILE_OUT);
586 # Print all column numbers (DEBUGGING)
587 if ( ($pass != $total_passes) && ($DEBUG_GUESSING==1) ) {
588 print "Reference column: $REF_COL.\n";
589 print "Footprint column: $FOOTPRINT_COL.\n";
590 print "X column: $X_COL.\n";
591 print "Y column: $Y_COL.\n";
592 print "Rotation column: $ANGLE_COL.\n";
593 print "Layer column: $LAYER_COL.\n";
594 print "Value column: $VALUE_COL.\n";
597 # If last guessing pass is complete, but all columns were not guessed,
598 # exit with an error.
599 if ( $guessing_file_format &&
600 ($pass == $total_passes-1) )
602 die ("Can't guess file format.\n");
605 # If file format was guessed, but next pass is not the last one,
606 # then go to the last one (processing pass).
607 if ( (!$guessing_file_format) &&
608 ($pass < $total_passes-1) ) {
609 $pass = $total_passes-1;
612 close(FILE_IN);
613 if ($tempfile) {
614 unlink $tempfile;
617 exit $return_code;