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
27 use lib
'GEDADATADIR/perl/lib'; # Where gxyrs package is located
30 use gxyrs
qw($CASE_INSENSITIVE);
32 # for parsing input options
36 $REF_COL $FOOTPRINT_COL $X_COL $Y_COL $ANGLE_COL
37 $LAYER_COL $VALUE_COL $CASE_INSENSITIVE $LINE_NUMBER
44 # Set these for debugging purposes
46 my $DEBUG_GUESSING = 0;
47 my $DEBUG_COL_LENGTH = 0;
48 my $DEBUG_RETURN_CODE = 0;
50 # Initialize global variables
59 #######################################################################
63 #######################################################################
66 #---------------------------------
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);
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";
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$/) ||
127 if ($DEBUG_GUESSING) {
128 print "Found X column: ".($i+1).".\n";
132 if ( ($LINE[$i] =~ /^Mid Y$/) ||
135 if ($DEBUG_GUESSING) {
136 print "Found Y column: ".($i+1).".\n";
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";
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";
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";
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) ||
192 ($FOOTPRINT_COL == -1) ||
195 ($ANGLE_COL == -1) ||
196 ($LAYER_COL == -1) ||
200 # All columns should be defined in the same line.
210 if ($DEBUG_GUESSING == 1) {
211 print "guess_file_format ended processing line $LINE_NUMBER.\n";
216 #---------------------------------
219 # open a temp file, to allow multiple passes to the input file.
220 #---------------------------------
222 $tempfile = 'temp_'.time() . '_' . int(rand(1000000));
223 open(TEMP, "+>$tempfile") or print STDERR $!;
231 #---------------------------------
234 # prints program usage
235 #---------------------------------
238 print " gxyrs [--tabulate] <input_file> --adjust <adjust_file> --output <outputfile> \n";
243 #######################################################################
247 #######################################################################
253 my ($file_in, $adjust_file, $file_out);
254 my ($this_line, $line_out);
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;
264 my $total_passes = scalar(@delimiters) + 1;
265 my $guessing_file_format;
266 my $is_comment = 0; # Mark the current line as a comment.
269 my $tabulate=0; # Output should be tabulated or not.
271 my $process_comments;
275 # Comparisons are case insensitive by default.
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";
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.
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");
309 $file_in = $fname[0];
311 # Print usage string in event user didn't specify input, adjust
313 if (! ($file_in && $file_out && ($adjust_file || $eval_string))) {
314 print STDERR "Nothing to do.\n";
316 print STDERR "Eval string: $eval_string.\n";
319 print STDERR "Commands file: $adjust_file.\n";
325 if ($file_in !~ '^-$') {
326 open(FILE_IN, $file_in) || die ("Can't open input file $file_in: $!");
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;
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
352 if ($DEBUG_RETURN_CODE) {
353 print "Setting global return code to 0.\n";
358 print "Pass number: $pass.\n" if ($DEBUG);
359 while ( <FILE_IN> ) {
363 # Remove end line characters.
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);
372 my $this_delimiter = $delimiters[$delimiter_index];
373 @LINE = split ($this_delimiter, $this_line);
379 # Test if the line is a comment
380 # (which shouldn't be processed).
381 if ( ($this_line =~ /^\s*#/) ||
382 ($this_line =~ /^\s*$/) ) {
384 print "Line is comment: @LINE\n";
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];
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
422 # Check if already guessed the file format.
423 $guessing_file_format = ( ($REF_COL == -1) ||
424 ($FOOTPRINT_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;
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) {
448 # If it's parsing the file then keep the number
449 # of columns constant, joining all the fields
451 while ( (@LINE > @lengths) &&
452 (scalar(@lengths) > 0) ) {
453 $LINE[@LINE-2] = $LINE[@LINE-2].
454 $delimiters_char[$delimiter_index].$LINE[@LINE-1];
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");
467 #print "Eval: $eval_string\n";
468 $rc = eval $eval_string;
471 print STDERR "Error: ".$@."\n";
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";
487 elsif (($rc == -2) && ($return_code >= 0)) {
490 elsif (($rc == 1) && ($return_code == 0)) {
493 elsif (($rc == 0) && ($return_code == 1)) {
496 elsif (($rc >= 0) && ($return_code < 0)) {
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: ".
512 # Calcule max length array
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") {
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";
545 # Set the output delimiter
546 if (length($output_delimiter) != 0) {
547 $delimiter = $output_delimiter;
550 $delimiter = $delimiters_char[$delimiter_index];
554 for (my $i=0; $i <= scalar(@LINE)-1; $i++) {
555 # Allow adding more lines, and handle
557 if ($LINE[$i] eq "\n") {
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];
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;
582 if ($pass >= $total_passes) {
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;