missing NULL terminator in set_config_x
[geda-gaf.git] / utils / gxyrs / gxyrs.pl
blob5957f055e086f1038de61412a0eec96903292663
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();
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: $!");
332 else {
333 *FILE_IN = &stdin();
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;
342 $TITLE_LINE = 0;
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
355 $return_code = 0;
356 if ($DEBUG_RETURN_CODE) {
357 print "Setting global return code to 0.\n";
360 # Parse file
361 $LINE_NUMBER = 0;
362 print "Pass number: $pass.\n" if ($DEBUG);
363 while ( <FILE_IN> ) {
364 $this_line=$_;
365 $_ = $this_line;
367 # Remove end line characters.
368 chomp($this_line);
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);
375 } else {
376 my $this_delimiter = $delimiters[$delimiter_index];
377 @LINE = split ($this_delimiter, $this_line);
379 $LINE_NUMBER += 1;
380 next if /^@/;
381 next if /^q/;
383 # Test if the line is a comment
384 # (which shouldn't be processed).
385 if ( ($this_line =~ /^\s*#/) ||
386 ($this_line =~ /^\s*$/) ) {
387 if ($DEBUG) {
388 print "Line is comment: @LINE\n";
390 $is_comment = 1;
391 } else {
392 $is_comment = 0;
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];
414 $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
425 guess_file_format();
426 # Check if already guessed the file format.
427 $guessing_file_format = ( ($REF_COL == -1) ||
428 ($FOOTPRINT_COL == -1) ||
429 ($X_COL == -1) ||
430 ($Y_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;
439 if ($verbose) {
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) {
450 my $rc ;
452 # If it's parsing the file then keep the number
453 # of columns constant, joining all the fields
454 # in the last one.
455 while ( (@LINE > @lengths) &&
456 (scalar(@lengths) > 0) ) {
457 $LINE[@LINE-2] = $LINE[@LINE-2].
458 $delimiters_char[$delimiter_index].$LINE[@LINE-1];
459 pop @LINE;
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");
470 } else {
471 #print "Eval: $eval_string\n";
472 $rc = eval $eval_string;
474 if ($@) {
475 print STDERR "Error: ".$@."\n";
476 $rc = -10;
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";
488 if ($rc == -1) {
489 $return_code = -1;
491 elsif (($rc == -2) && ($return_code >= 0)) {
492 $return_code = -2;
494 elsif (($rc == 1) && ($return_code == 0)) {
495 $return_code = 1;
497 elsif (($rc == 0) && ($return_code == 1)) {
498 $return_code = 1;
500 elsif (($rc >= 0) && ($return_code < 0)) {
501 # Do nothing
503 else {
504 $return_code = $rc;
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: ".
514 "'$this_line'\n";
516 # Calcule max length array
517 if ($tabulate) {
518 my $j=0;
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") {
523 $j = $i+1;
524 next;
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";
545 elsif (@LINE > 0) {
546 my $delimiter;
547 my $j=0;
549 # Set the output delimiter
550 if (length($output_delimiter) != 0) {
551 $delimiter = $output_delimiter;
553 else {
554 $delimiter = $delimiters_char[$delimiter_index];
557 # Write output
558 for (my $i=0; $i <= scalar(@LINE)-1; $i++) {
559 # Allow adding more lines, and handle
560 # new lines.
561 if ($LINE[$i] eq "\n") {
562 print FILE_OUT "\n";
563 $j = $i+1;
564 next;
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];
570 } else {
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;
579 print FILE_OUT "\n";
585 # Close output file.
586 if ($pass >= $total_passes) {
587 close(FILE_OUT);
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;
616 close(FILE_IN);
617 if ($tempfile) {
618 unlink $tempfile;
621 exit $return_code;