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";
324 if (defined $adjust_file && substr($adjust_file, 0, 1) ne "/") {
325 $adjust_file = "./" . $adjust_file;
329 if ($file_in !~ '^-$') {
330 open(FILE_IN, $file_in) || die ("Can't open input file $file_in: $!");
336 if (length($output_delimiter) > 1) {
337 die ("Output delimiter $output_delimiter must be a character.");
340 # Don't know yet the file format.
341 $guessing_file_format = 1;
344 # Last pass is to process the file. Others are for guessing the
345 # file format with several delimiters (one for each element in @delimiters.
346 for ($pass = 1; $pass <= $total_passes; $pass++) {
347 if (seek(FILE_IN, 0, 0) != 1) {
348 print STDERR "Error when seek input file to 0\n";
350 if ($pass >= $total_passes) {
351 open(FILE_OUT, "> ".$file_out) || die ("Can't open output file $file_out: $!");
354 # Set the return code to 0
356 if ($DEBUG_RETURN_CODE) {
357 print "Setting global return code to 0.\n";
362 print "Pass number: $pass.\n" if ($DEBUG);
363 while ( <FILE_IN> ) {
367 # Remove end line characters.
370 # For the last pass, use the delimiter guessed.
371 # For the others, get the delimiter from @delimiters array.
372 if ($pass < $total_passes) {
373 my $this_delimiter = $delimiters[$pass-1];
374 @LINE = split ($this_delimiter, $this_line);
376 my $this_delimiter = $delimiters[$delimiter_index];
377 @LINE = split ($this_delimiter, $this_line);
383 # Test if the line is a comment
384 # (which shouldn't be processed).
385 if ( ($this_line =~ /^\s*#/) ||
386 ($this_line =~ /^\s*$/) ) {
388 print "Line is comment: @LINE\n";
395 # First, some column titles are separated by spaces, so join them.
396 if ($LINE_NUMBER == 1) {
397 my $num_elems = scalar(@LINE);
398 for (my $i = 0; $i <= $num_elems-1; $i++) {
399 if ($i <= $num_elems-2) {
400 # Join titles like "Pad X", "Pad Y", "Mid X", "Mid Y",
401 # "ref X", "ref Y" (Protel output)
402 if ( ( ($LINE[$i] =~ /^Pad$/) &&
403 ($LINE[$i+1] =~/^[XY]$/) ) ||
404 ( ($LINE[$i] =~ /^Mid$/) &&
405 ($LINE[$i+1] =~/^[XY]$/) ) ||
406 ( ($LINE[$i] =~ /^Ref$/) &&
407 ($LINE[$i+1] =~/^[XY]$/) )
409 $LINE[$i] = $LINE[$i]." ".$LINE[$i+1];
410 for (my $j = $i+1; $j <= $num_elems-2; $j++) {
411 $LINE[$j] = $LINE[$j+1];
413 delete $LINE[$num_elems-1];
421 if ( ($pass != $total_passes) &&
422 $guessing_file_format ) {
423 my $old_guessing_file_format = $guessing_file_format;
424 # Try to guess the file format
426 # Check if already guessed the file format.
427 $guessing_file_format = ( ($REF_COL == -1) ||
428 ($FOOTPRINT_COL == -1) ||
431 ($ANGLE_COL == -1) ||
432 ($LAYER_COL == -1) ||
433 ($VALUE_COL == -1) );
435 if (!$guessing_file_format) {
436 if ($old_guessing_file_format == 1) {
437 $TITLE_LINE = $LINE_NUMBER;
440 print "Found file format. ";
441 print "Delimiter is $delimiters[$pass-1].\n";
444 $delimiter_index = $pass-1;
448 if (!$guessing_file_format) {
449 if (($is_comment == 0) || $process_comments) {
452 # If it's parsing the file then keep the number
453 # of columns constant, joining all the fields
455 while ( (@LINE > @lengths) &&
456 (scalar(@lengths) > 0) ) {
457 $LINE[@LINE-2] = $LINE[@LINE-2].
458 $delimiters_char[$delimiter_index].$LINE[@LINE-1];
462 if ($DEBUG_RETURN_CODE) {
463 print "Global return code before evaluation: $return_code.\n";
466 # Run the adjust file.
467 if (defined $adjust_file) {
468 $rc = do $adjust_file ||
469 die("Can't use adjust file: $!\n");
471 #print "Eval: $eval_string\n";
472 $rc = eval $eval_string;
475 print STDERR "Error: ".$@."\n";
479 # Set $return_code based on priorities.
480 # 1 means the command found a match and was succesful
481 # at least one time in the whole file.
482 # 0 means there was no match.
483 # -1 means there was an error.
484 # -2 means there was a warning.
485 if ($DEBUG_RETURN_CODE) {
486 print "Return code $rc after evaluation.\n";
491 elsif (($rc == -2) && ($return_code >= 0)) {
494 elsif (($rc == 1) && ($return_code == 0)) {
497 elsif (($rc == 0) && ($return_code == 1)) {
500 elsif (($rc >= 0) && ($return_code < 0)) {
506 if ($DEBUG_RETURN_CODE) {
507 print "Global return code: $return_code.\n";
510 if ( ($pass != $total_passes) &&
511 ( ($is_comment == 0) || $process_comments) ) {
512 if ($DEBUG_COL_LENGTH == 1) {
513 print "Measuring column length of line: ".
516 # Calcule max length array
519 for (my $i=0; $i <= scalar(@LINE)-1; $i++) {
520 # Allow adding more lines, and checking
521 # element's length right.
522 if ($LINE[$i] eq "\n") {
526 if (! exists $lengths[$i-$j]) {
527 $lengths[$i-$j]= length($LINE[$i]);
529 elsif ($lengths[$i-$j] < length($LINE[$i]) ) {
530 $lengths[$i-$j]= length($LINE[$i]);
532 if ($DEBUG_COL_LENGTH == 1) {
533 print "Column ".($i-$j+1).
534 ", length: $lengths[$i].\n";
540 # Print the result after processing
541 if ($pass >= $total_passes) {
542 if (($is_comment == 1) && !$process_comments) {
543 print FILE_OUT "$this_line\n";
549 # Set the output delimiter
550 if (length($output_delimiter) != 0) {
551 $delimiter = $output_delimiter;
554 $delimiter = $delimiters_char[$delimiter_index];
558 for (my $i=0; $i <= scalar(@LINE)-1; $i++) {
559 # Allow adding more lines, and handle
561 if ($LINE[$i] eq "\n") {
566 if (($i-$j) > scalar(@lengths)-1) {
567 # If column length array has no number
568 # for this column, print it as is.
569 print FILE_OUT $LINE[$i];
571 printf FILE_OUT "%-$lengths[$i-$j]s",$LINE[$i];
573 # print the last column without delimiter
574 if ( ($i < @LINE-1) &&
575 (!($LINE[$i+1] eq "\n"))) {
576 print FILE_OUT $delimiter;
586 if ($pass >= $total_passes) {
590 # Print all column numbers (DEBUGGING)
591 if ( ($pass != $total_passes) && ($DEBUG_GUESSING==1) ) {
592 print "Reference column: $REF_COL.\n";
593 print "Footprint column: $FOOTPRINT_COL.\n";
594 print "X column: $X_COL.\n";
595 print "Y column: $Y_COL.\n";
596 print "Rotation column: $ANGLE_COL.\n";
597 print "Layer column: $LAYER_COL.\n";
598 print "Value column: $VALUE_COL.\n";
601 # If last guessing pass is complete, but all columns were not guessed,
602 # exit with an error.
603 if ( $guessing_file_format &&
604 ($pass == $total_passes-1) )
606 die ("Can't guess file format.\n");
609 # If file format was guessed, but next pass is not the last one,
610 # then go to the last one (processing pass).
611 if ( (!$guessing_file_format) &&
612 ($pass < $total_passes-1) ) {
613 $pass = $total_passes-1;